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.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -144,7 +146,7 @@ fun ConfirmSeedPhraseScreen(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
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.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -64,7 +66,7 @@ fun ImportSeedPhraseScreen(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
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.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -65,7 +67,7 @@ fun SeedPhraseScreen(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
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.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -84,9 +86,40 @@ fun SetProfileScreen(
|
||||
var isSaving 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 usernameError = if (usernameTouched) validateUsername(username.trim()) else null
|
||||
val isFormValid = validateName(name.trim()) == null && validateUsername(username.trim()) == null
|
||||
val localUsernameError = if (usernameTouched) validateUsername(username.trim()) else 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 }
|
||||
|
||||
@@ -276,11 +309,42 @@ fun SetProfileScreen(
|
||||
prefix = {
|
||||
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,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(14.dp)),
|
||||
singleLine = true,
|
||||
keyboardOptions = androidx.compose.foundation.text.KeyboardOptions(
|
||||
capitalization = androidx.compose.ui.text.input.KeyboardCapitalization.None,
|
||||
autoCorrect = false
|
||||
),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedTextColor = textColor,
|
||||
unfocusedTextColor = textColor,
|
||||
@@ -300,6 +364,13 @@ fun SetProfileScreen(
|
||||
color = ErrorRed,
|
||||
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 {
|
||||
Text(
|
||||
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.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -81,7 +83,7 @@ fun WelcomeScreen(
|
||||
if (hasExistingAccount) {
|
||||
IconButton(onClick = onBack, modifier = Modifier.statusBarsPadding().padding(4.dp)) {
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
TablerIcons.ChevronLeft,
|
||||
contentDescription = "Back",
|
||||
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.RoundedCornerShape
|
||||
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.Lock
|
||||
import androidx.compose.material.icons.filled.Message
|
||||
@@ -998,7 +999,7 @@ fun GroupInfoScreen(
|
||||
) {
|
||||
IconButton(onClick = onBack, modifier = Modifier.align(Alignment.TopStart)) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription = "Back",
|
||||
tint = Color.White
|
||||
)
|
||||
@@ -1752,7 +1753,7 @@ private fun GroupEncryptionKeyPage(
|
||||
) {
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription = "Back",
|
||||
tint = Color.White
|
||||
)
|
||||
|
||||
@@ -18,7 +18,6 @@ import androidx.compose.foundation.lazy.grid.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -48,6 +47,7 @@ import com.rosetta.messenger.ui.icons.TelegramIcons
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.ChevronDown
|
||||
import compose.icons.tablericons.ChevronLeft
|
||||
import compose.icons.tablericons.PhotoOff
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -730,7 +730,7 @@ internal fun ExpandedHeader(
|
||||
modifier = Modifier.size(40.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription = "Close gallery",
|
||||
tint = textColor
|
||||
)
|
||||
|
||||
@@ -13,7 +13,8 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -438,7 +439,7 @@ private fun FilePickerTopBar(
|
||||
if (showBack) {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription = "Back",
|
||||
tint = textColor
|
||||
)
|
||||
|
||||
@@ -530,7 +530,7 @@ fun ImageViewerScreen(
|
||||
modifier = Modifier.align(Alignment.CenterStart)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription = "Close",
|
||||
tint = Color.White
|
||||
)
|
||||
|
||||
@@ -888,7 +888,7 @@ fun MediaPickerBottomSheet(
|
||||
modifier = Modifier.size(40.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription = "Close gallery",
|
||||
tint = textColor
|
||||
)
|
||||
|
||||
@@ -683,7 +683,8 @@ fun MessageInputBar(
|
||||
} else if (msg.text.isEmpty() && hasImageAttachment) {
|
||||
"Photo"
|
||||
} 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
|
||||
}
|
||||
} else "${panelReplyMessages.size} messages",
|
||||
|
||||
@@ -7,7 +7,8 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
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.ContentCopy
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
@@ -84,7 +85,7 @@ fun CrashLogsScreen(
|
||||
title = { Text("Crash Logs") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(TablerIcons.ChevronLeft, contentDescription = "Back")
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
@@ -293,7 +294,7 @@ private fun CrashDetailScreen(
|
||||
title = { Text("Crash Details") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(TablerIcons.ChevronLeft, contentDescription = "Back")
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
|
||||
@@ -14,7 +14,8 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
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.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
@@ -67,7 +68,7 @@ fun BackupScreen(
|
||||
) {
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription = "Back",
|
||||
tint = textColor
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user