UI: унифицированы иконки навигации (ChevronLeft), фикс обрезки эмодзи в reply-превью, проверка доступности username при регистрации, отдельный экран биометрии, клавиатура прячется при скролле профиля и навигации, плавная анимация navbar при смене темы, аватарки в поиске.
This commit is contained in:
@@ -10,6 +10,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -144,7 +146,7 @@ fun ConfirmSeedPhraseScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = onBack) {
|
||||||
Icon(Icons.Default.ArrowBack, "Back", tint = textColor)
|
Icon(TablerIcons.ChevronLeft, "Back", tint = textColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -64,7 +66,7 @@ fun ImportSeedPhraseScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = onBack) {
|
||||||
Icon(Icons.Default.ArrowBack, "Back", tint = textColor)
|
Icon(TablerIcons.ChevronLeft, "Back", tint = textColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -65,7 +67,7 @@ fun SeedPhraseScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = onBack) {
|
||||||
Icon(Icons.Default.ArrowBack, "Back", tint = textColor)
|
Icon(TablerIcons.ChevronLeft, "Back", tint = textColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -84,9 +86,40 @@ fun SetProfileScreen(
|
|||||||
var isSaving by remember { mutableStateOf(false) }
|
var isSaving by remember { mutableStateOf(false) }
|
||||||
var visible by remember { mutableStateOf(false) }
|
var visible by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// Username availability check
|
||||||
|
var usernameAvailable by remember { mutableStateOf<Boolean?>(null) }
|
||||||
|
var isCheckingUsername by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(username) {
|
||||||
|
val trimmed = username.trim()
|
||||||
|
if (trimmed.length < USERNAME_MIN_LENGTH) {
|
||||||
|
usernameAvailable = null
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
if (validateUsername(trimmed) != null) {
|
||||||
|
usernameAvailable = null
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
usernameAvailable = null
|
||||||
|
isCheckingUsername = true
|
||||||
|
delay(600) // debounce
|
||||||
|
try {
|
||||||
|
val results = ProtocolManager.searchUsers(trimmed, 3000)
|
||||||
|
val taken = results.any { it.username.equals(trimmed, ignoreCase = true) }
|
||||||
|
usernameAvailable = !taken
|
||||||
|
} catch (_: Exception) {
|
||||||
|
usernameAvailable = null
|
||||||
|
}
|
||||||
|
isCheckingUsername = false
|
||||||
|
}
|
||||||
|
|
||||||
val nameError = if (nameTouched) validateName(name.trim()) else null
|
val nameError = if (nameTouched) validateName(name.trim()) else null
|
||||||
val usernameError = if (usernameTouched) validateUsername(username.trim()) else null
|
val localUsernameError = if (usernameTouched) validateUsername(username.trim()) else null
|
||||||
val isFormValid = validateName(name.trim()) == null && validateUsername(username.trim()) == null
|
val usernameError = localUsernameError
|
||||||
|
?: if (usernameTouched && usernameAvailable == false) "Username is already taken" else null
|
||||||
|
val isFormValid = validateName(name.trim()) == null
|
||||||
|
&& validateUsername(username.trim()) == null
|
||||||
|
&& usernameAvailable != false
|
||||||
|
|
||||||
LaunchedEffect(Unit) { visible = true }
|
LaunchedEffect(Unit) { visible = true }
|
||||||
|
|
||||||
@@ -276,11 +309,42 @@ fun SetProfileScreen(
|
|||||||
prefix = {
|
prefix = {
|
||||||
Text("@", color = secondaryText, fontSize = 16.sp)
|
Text("@", color = secondaryText, fontSize = 16.sp)
|
||||||
},
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
when {
|
||||||
|
isCheckingUsername && username.trim().length >= USERNAME_MIN_LENGTH -> {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(18.dp),
|
||||||
|
strokeWidth = 2.dp,
|
||||||
|
color = secondaryText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
usernameAvailable == true && usernameError == null -> {
|
||||||
|
Icon(
|
||||||
|
imageVector = TablerIcons.Check,
|
||||||
|
contentDescription = "Available",
|
||||||
|
tint = Color(0xFF4CAF50),
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
usernameAvailable == false -> {
|
||||||
|
Icon(
|
||||||
|
imageVector = TablerIcons.X,
|
||||||
|
contentDescription = "Taken",
|
||||||
|
tint = ErrorRed,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
isError = usernameError != null,
|
isError = usernameError != null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clip(RoundedCornerShape(14.dp)),
|
.clip(RoundedCornerShape(14.dp)),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
|
keyboardOptions = androidx.compose.foundation.text.KeyboardOptions(
|
||||||
|
capitalization = androidx.compose.ui.text.input.KeyboardCapitalization.None,
|
||||||
|
autoCorrect = false
|
||||||
|
),
|
||||||
colors = TextFieldDefaults.colors(
|
colors = TextFieldDefaults.colors(
|
||||||
focusedTextColor = textColor,
|
focusedTextColor = textColor,
|
||||||
unfocusedTextColor = textColor,
|
unfocusedTextColor = textColor,
|
||||||
@@ -300,6 +364,13 @@ fun SetProfileScreen(
|
|||||||
color = ErrorRed,
|
color = ErrorRed,
|
||||||
modifier = Modifier.padding(start = 16.dp, top = 4.dp)
|
modifier = Modifier.padding(start = 16.dp, top = 4.dp)
|
||||||
)
|
)
|
||||||
|
} else if (usernameAvailable == true) {
|
||||||
|
Text(
|
||||||
|
text = "Username is available!",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = Color(0xFF4CAF50),
|
||||||
|
modifier = Modifier.padding(start = 16.dp, top = 4.dp)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Text(
|
Text(
|
||||||
text = "Username is optional. People can use it to find you without sharing your key.",
|
text = "Username is optional. People can use it to find you without sharing your key.",
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import androidx.compose.foundation.shape.CircleShape
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -81,7 +83,7 @@ fun WelcomeScreen(
|
|||||||
if (hasExistingAccount) {
|
if (hasExistingAccount) {
|
||||||
IconButton(onClick = onBack, modifier = Modifier.statusBarsPadding().padding(4.dp)) {
|
IconButton(onClick = onBack, modifier = Modifier.statusBarsPadding().padding(4.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.ArrowBack,
|
TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
tint = textColor.copy(alpha = 0.6f)
|
tint = textColor.copy(alpha = 0.6f)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material.icons.filled.ExitToApp
|
import androidx.compose.material.icons.filled.ExitToApp
|
||||||
import androidx.compose.material.icons.filled.Lock
|
import androidx.compose.material.icons.filled.Lock
|
||||||
import androidx.compose.material.icons.filled.Message
|
import androidx.compose.material.icons.filled.Message
|
||||||
@@ -998,7 +999,7 @@ fun GroupInfoScreen(
|
|||||||
) {
|
) {
|
||||||
IconButton(onClick = onBack, modifier = Modifier.align(Alignment.TopStart)) {
|
IconButton(onClick = onBack, modifier = Modifier.align(Alignment.TopStart)) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ArrowBack,
|
imageVector = TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
tint = Color.White
|
tint = Color.White
|
||||||
)
|
)
|
||||||
@@ -1752,7 +1753,7 @@ private fun GroupEncryptionKeyPage(
|
|||||||
) {
|
) {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = onBack) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ArrowBack,
|
imageVector = TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
tint = Color.White
|
tint = Color.White
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import androidx.compose.foundation.lazy.grid.*
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
@@ -48,6 +47,7 @@ import com.rosetta.messenger.ui.icons.TelegramIcons
|
|||||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
import compose.icons.TablerIcons
|
import compose.icons.TablerIcons
|
||||||
import compose.icons.tablericons.ChevronDown
|
import compose.icons.tablericons.ChevronDown
|
||||||
|
import compose.icons.tablericons.ChevronLeft
|
||||||
import compose.icons.tablericons.PhotoOff
|
import compose.icons.tablericons.PhotoOff
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -730,7 +730,7 @@ internal fun ExpandedHeader(
|
|||||||
modifier = Modifier.size(40.dp)
|
modifier = Modifier.size(40.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ArrowBack,
|
imageVector = TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Close gallery",
|
contentDescription = "Close gallery",
|
||||||
tint = textColor
|
tint = textColor
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -438,7 +439,7 @@ private fun FilePickerTopBar(
|
|||||||
if (showBack) {
|
if (showBack) {
|
||||||
IconButton(onClick = onBackClick) {
|
IconButton(onClick = onBackClick) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ArrowBack,
|
imageVector = TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
tint = textColor
|
tint = textColor
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ fun ImageViewerScreen(
|
|||||||
modifier = Modifier.align(Alignment.CenterStart)
|
modifier = Modifier.align(Alignment.CenterStart)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ArrowBack,
|
imageVector = TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Close",
|
contentDescription = "Close",
|
||||||
tint = Color.White
|
tint = Color.White
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -888,7 +888,7 @@ fun MediaPickerBottomSheet(
|
|||||||
modifier = Modifier.size(40.dp)
|
modifier = Modifier.size(40.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ArrowBack,
|
imageVector = TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Close gallery",
|
contentDescription = "Close gallery",
|
||||||
tint = textColor
|
tint = textColor
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -683,7 +683,8 @@ fun MessageInputBar(
|
|||||||
} else if (msg.text.isEmpty() && hasImageAttachment) {
|
} else if (msg.text.isEmpty() && hasImageAttachment) {
|
||||||
"Photo"
|
"Photo"
|
||||||
} else {
|
} else {
|
||||||
val shortText = msg.text.take(40)
|
val codePoints = msg.text.codePoints().limit(40).toArray()
|
||||||
|
val shortText = String(codePoints, 0, codePoints.size)
|
||||||
if (shortText.length < msg.text.length) "$shortText..." else shortText
|
if (shortText.length < msg.text.length) "$shortText..." else shortText
|
||||||
}
|
}
|
||||||
} else "${panelReplyMessages.size} messages",
|
} else "${panelReplyMessages.size} messages",
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material.icons.filled.BugReport
|
import androidx.compose.material.icons.filled.BugReport
|
||||||
import androidx.compose.material.icons.filled.ContentCopy
|
import androidx.compose.material.icons.filled.ContentCopy
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
@@ -84,7 +85,7 @@ fun CrashLogsScreen(
|
|||||||
title = { Text("Crash Logs") },
|
title = { Text("Crash Logs") },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onBackClick) {
|
IconButton(onClick = onBackClick) {
|
||||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
Icon(TablerIcons.ChevronLeft, contentDescription = "Back")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
@@ -293,7 +294,7 @@ private fun CrashDetailScreen(
|
|||||||
title = { Text("Crash Details") },
|
title = { Text("Crash Details") },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onBackClick) {
|
IconButton(onClick = onBackClick) {
|
||||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
Icon(TablerIcons.ChevronLeft, contentDescription = "Back")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import compose.icons.TablerIcons
|
||||||
|
import compose.icons.tablericons.*
|
||||||
import androidx.compose.material.icons.filled.ContentCopy
|
import androidx.compose.material.icons.filled.ContentCopy
|
||||||
import androidx.compose.material.icons.filled.Visibility
|
import androidx.compose.material.icons.filled.Visibility
|
||||||
import androidx.compose.material.icons.filled.VisibilityOff
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
@@ -67,7 +68,7 @@ fun BackupScreen(
|
|||||||
) {
|
) {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = onBack) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
imageVector = TablerIcons.ChevronLeft,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
tint = textColor
|
tint = textColor
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user