From 1e259f52ee9410507b56c0bdb12fed3cfa9c2da9 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Wed, 8 Apr 2026 09:22:27 +0500 Subject: [PATCH] =?UTF-8?q?UI:=20=D1=83=D0=BD=D0=B8=D1=84=D0=B8=D1=86?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D1=8B=20=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D0=BA=D0=B8=20=D0=BD=D0=B0=D0=B2=D0=B8=D0=B3=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20(ChevronLeft),=20=D1=84=D0=B8=D0=BA=D1=81=20?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B5=D0=B7=D0=BA=D0=B8=20=D1=8D=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D0=B7=D0=B8=20=D0=B2=20reply-=D0=BF=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D1=8C=D1=8E,=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20username=20=D0=BF=D1=80=D0=B8=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8,=20?= =?UTF-8?q?=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=8D?= =?UTF-8?q?=D0=BA=D1=80=D0=B0=D0=BD=20=D0=B1=D0=B8=D0=BE=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=D0=B8=D0=B8,=20=D0=BA=D0=BB=D0=B0=D0=B2=D0=B8=D0=B0?= =?UTF-8?q?=D1=82=D1=83=D1=80=D0=B0=20=D0=BF=D1=80=D1=8F=D1=87=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=8F=20=D0=BF=D1=80=D0=B8=20=D1=81=D0=BA=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D0=BB=D0=B5=20=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8F?= =?UTF-8?q?=20=D0=B8=20=D0=BD=D0=B0=D0=B2=D0=B8=D0=B3=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8,=20=D0=BF=D0=BB=D0=B0=D0=B2=D0=BD=D0=B0=D1=8F=20=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=BC=D0=B0=D1=86=D0=B8=D1=8F=20navbar=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D1=81=D0=BC=D0=B5=D0=BD=D0=B5=20=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=D1=8B,=20=D0=B0=D0=B2=D0=B0=D1=82=D0=B0=D1=80=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B2=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/auth/ConfirmSeedPhraseScreen.kt | 4 +- .../ui/auth/ImportSeedPhraseScreen.kt | 4 +- .../messenger/ui/auth/SeedPhraseScreen.kt | 4 +- .../messenger/ui/auth/SetProfileScreen.kt | 75 ++++++++++++++++++- .../messenger/ui/auth/WelcomeScreen.kt | 4 +- .../messenger/ui/chats/GroupInfoScreen.kt | 7 +- .../ui/chats/attach/AttachAlertComponents.kt | 4 +- .../ui/chats/attach/AttachAlertFileLayout.kt | 5 +- .../ui/chats/components/ImageViewerScreen.kt | 2 +- .../components/MediaPickerBottomSheet.kt | 2 +- .../ui/chats/input/ChatDetailInput.kt | 3 +- .../messenger/ui/crashlogs/CrashLogsScreen.kt | 7 +- .../messenger/ui/settings/BackupScreen.kt | 5 +- 13 files changed, 105 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt index 78e9138..c5cd0e9 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/ConfirmSeedPhraseScreen.kt @@ -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) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/ImportSeedPhraseScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/ImportSeedPhraseScreen.kt index d9acf42..d5b144f 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/ImportSeedPhraseScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/ImportSeedPhraseScreen.kt @@ -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) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/SeedPhraseScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/SeedPhraseScreen.kt index 2e93ad4..1acad29 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/SeedPhraseScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/SeedPhraseScreen.kt @@ -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) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/SetProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/SetProfileScreen.kt index 2e37f6b..722499f 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/SetProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/SetProfileScreen.kt @@ -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(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.", diff --git a/app/src/main/java/com/rosetta/messenger/ui/auth/WelcomeScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/auth/WelcomeScreen.kt index 901c33f..04a1077 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/auth/WelcomeScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/auth/WelcomeScreen.kt @@ -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) ) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/GroupInfoScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/GroupInfoScreen.kt index dde24d0..2d4fc14 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/GroupInfoScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/GroupInfoScreen.kt @@ -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 ) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertComponents.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertComponents.kt index cce885b..c28c4ba 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertComponents.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertComponents.kt @@ -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 ) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertFileLayout.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertFileLayout.kt index 0f74285..9b9fc97 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertFileLayout.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/attach/AttachAlertFileLayout.kt @@ -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 ) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageViewerScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageViewerScreen.kt index 73bf365..c68511f 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageViewerScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ImageViewerScreen.kt @@ -530,7 +530,7 @@ fun ImageViewerScreen( modifier = Modifier.align(Alignment.CenterStart) ) { Icon( - imageVector = Icons.Default.ArrowBack, + imageVector = TablerIcons.ChevronLeft, contentDescription = "Close", tint = Color.White ) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt index 79ab820..80f4532 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/MediaPickerBottomSheet.kt @@ -888,7 +888,7 @@ fun MediaPickerBottomSheet( modifier = Modifier.size(40.dp) ) { Icon( - imageVector = Icons.Default.ArrowBack, + imageVector = TablerIcons.ChevronLeft, contentDescription = "Close gallery", tint = textColor ) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt index da6df62..7753baa 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt @@ -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", diff --git a/app/src/main/java/com/rosetta/messenger/ui/crashlogs/CrashLogsScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/crashlogs/CrashLogsScreen.kt index 5f2d5c4..f009f30 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/crashlogs/CrashLogsScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/crashlogs/CrashLogsScreen.kt @@ -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 = { diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/BackupScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/BackupScreen.kt index b2e9868..33bf518 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/BackupScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/BackupScreen.kt @@ -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 )