From 15d56715b9474ab568d8022f097ca4160e572154 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 27 Jul 2026 02:51:19 +0200 Subject: [PATCH] cleanup --- .../ui/component/button/BitChatBrandButton.kt | 6 +- .../core/ui/component/button/CloseButton.kt | 23 ++- .../BackgroundLocationPermissionScreen.kt | 6 +- .../onboarding/BatteryOptimizationScreen.kt | 6 +- .../onboarding/BluetoothCheckScreen.kt | 4 +- .../android/onboarding/LocationCheckScreen.kt | 4 +- .../onboarding/PermissionExplanationScreen.kt | 8 +- .../com/bitchat/android/ui/AboutSections.kt | 56 +++--- .../java/com/bitchat/android/ui/AboutSheet.kt | 39 ++-- .../com/bitchat/android/ui/AnimatedCount.kt | 112 +++++++++++ .../java/com/bitchat/android/ui/ChatHeader.kt | 182 +++++++++-------- .../java/com/bitchat/android/ui/ChatScreen.kt | 5 +- .../bitchat/android/ui/GeohashPeopleList.kt | 16 +- .../android/ui/GeohashPickerActivity.kt | 8 +- .../bitchat/android/ui/GeohashViewModel.kt | 21 +- .../com/bitchat/android/ui/InputComponents.kt | 14 +- .../com/bitchat/android/ui/LinkPreviewPill.kt | 4 +- .../android/ui/LocationChannelsSheet.kt | 32 +-- .../bitchat/android/ui/LocationNotesButton.kt | 8 +- .../bitchat/android/ui/LocationNotesSheet.kt | 4 +- .../android/ui/MatrixEncryptionAnimation.kt | 183 ------------------ .../bitchat/android/ui/MeshPeerListSheet.kt | 103 ++++++---- .../bitchat/android/ui/MessageComponents.kt | 23 +-- .../bitchat/android/ui/PoWStatusIndicator.kt | 133 ------------- .../android/ui/SecurityVerificationSheet.kt | 14 +- .../bitchat/android/ui/VerificationSheet.kt | 4 +- .../android/ui/VoiceInputComponents.kt | 6 +- .../android/ui/debug/DebugSettingsSheet.kt | 16 +- .../com/bitchat/android/ui/debug/MeshGraph.kt | 6 +- .../android/ui/media/AudioMessageItem.kt | 4 +- .../android/ui/media/FileMessageItem.kt | 4 +- .../android/ui/media/FilePickerButton.kt | 4 +- .../android/ui/media/FileSendingAnimation.kt | 4 +- .../android/ui/media/FullScreenImageViewer.kt | 6 +- .../android/ui/media/ImageMessageItem.kt | 4 +- .../android/ui/media/ImagePickerButton.kt | 14 +- .../android/ui/media/MediaPickerOptions.kt | 6 +- .../android/ui/media/VoiceNotePlayer.kt | 6 +- .../android/ui/theme/BitchatPalette.kt | 9 +- app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-bn/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-fa/strings.xml | 1 - app/src/main/res/values-fil/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-hi/strings.xml | 1 - app/src/main/res/values-id/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-ka/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-mg/strings.xml | 1 - app/src/main/res/values-ne/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-pa-rPK/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-th/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-ur/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh/strings.xml | 1 - app/src/main/res/values/strings.xml | 5 +- 66 files changed, 476 insertions(+), 662 deletions(-) create mode 100644 app/src/main/java/com/bitchat/android/ui/AnimatedCount.kt delete mode 100644 app/src/main/java/com/bitchat/android/ui/MatrixEncryptionAnimation.kt delete mode 100644 app/src/main/java/com/bitchat/android/ui/PoWStatusIndicator.kt diff --git a/app/src/main/java/com/bitchat/android/core/ui/component/button/BitChatBrandButton.kt b/app/src/main/java/com/bitchat/android/core/ui/component/button/BitChatBrandButton.kt index 15b2b40e..c1361364 100644 --- a/app/src/main/java/com/bitchat/android/core/ui/component/button/BitChatBrandButton.kt +++ b/app/src/main/java/com/bitchat/android/core/ui/component/button/BitChatBrandButton.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.bitchat.android.core.ui.icon.BitChatIcon import kotlinx.coroutines.Job @@ -30,6 +31,7 @@ fun BitChatBrandButton( contentDescription: String, modifier: Modifier = Modifier, tint: Color = MaterialTheme.colorScheme.primary, + iconSize: Dp = 22.dp, ) { var tapCount by remember { mutableIntStateOf(0) } var resetJob by remember { mutableStateOf(null) } @@ -63,9 +65,7 @@ fun BitChatBrandButton( imageVector = BitChatIcon, contentDescription = contentDescription, tint = tint, - // 20.dp rather than 16.dp: the pixel-art bubble has heavy internal negative space, - // so it reads noticeably smaller than a solid glyph at the same nominal size. - modifier = Modifier.size(20.dp), + modifier = Modifier.size(iconSize), ) } } diff --git a/app/src/main/java/com/bitchat/android/core/ui/component/button/CloseButton.kt b/app/src/main/java/com/bitchat/android/core/ui/component/button/CloseButton.kt index 96f408bc..36ad7526 100644 --- a/app/src/main/java/com/bitchat/android/core/ui/component/button/CloseButton.kt +++ b/app/src/main/java/com/bitchat/android/core/ui/component/button/CloseButton.kt @@ -1,34 +1,37 @@ package com.bitchat.android.core.ui.component.button -import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close +import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.bitchat.android.R +import com.bitchat.android.ui.theme.LocalBitchatPalette @Composable fun CloseButton( onClick: () -> Unit, - modifier: Modifier = Modifier.Companion + modifier: Modifier = Modifier ) { + val palette = LocalBitchatPalette.current IconButton( onClick = onClick, - modifier = modifier - .size(32.dp), + // 44.dp to match every other tap target in the app's chrome. + modifier = modifier.size(44.dp), colors = IconButtonDefaults.iconButtonColors( - contentColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f), - containerColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.1f) + contentColor = palette.textSecondary, + containerColor = palette.surfaceVariant ) ) { Icon( imageVector = Icons.Default.Close, - contentDescription = "Close", - modifier = Modifier.Companion.size(18.dp) + contentDescription = stringResource(R.string.close_plain), + modifier = Modifier.size(18.dp) ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bitchat/android/onboarding/BackgroundLocationPermissionScreen.kt b/app/src/main/java/com/bitchat/android/onboarding/BackgroundLocationPermissionScreen.kt index 1346cb23..93101abf 100644 --- a/app/src/main/java/com/bitchat/android/onboarding/BackgroundLocationPermissionScreen.kt +++ b/app/src/main/java/com/bitchat/android/onboarding/BackgroundLocationPermissionScreen.kt @@ -1,5 +1,8 @@ package com.bitchat.android.onboarding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.LocationOn +import androidx.compose.material.icons.filled.Security import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -13,9 +16,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.LocationOn -import androidx.compose.material.icons.filled.Security import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ColorScheme diff --git a/app/src/main/java/com/bitchat/android/onboarding/BatteryOptimizationScreen.kt b/app/src/main/java/com/bitchat/android/onboarding/BatteryOptimizationScreen.kt index 3fe392d6..0b3036aa 100644 --- a/app/src/main/java/com/bitchat/android/onboarding/BatteryOptimizationScreen.kt +++ b/app/src/main/java/com/bitchat/android/onboarding/BatteryOptimizationScreen.kt @@ -1,13 +1,13 @@ package com.bitchat.android.onboarding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.* import androidx.compose.animation.core.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment diff --git a/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt b/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt index 60c6e5e4..933656bd 100644 --- a/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt +++ b/app/src/main/java/com/bitchat/android/onboarding/BluetoothCheckScreen.kt @@ -1,9 +1,9 @@ package com.bitchat.android.onboarding -import androidx.compose.animation.core.* -import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* +import androidx.compose.animation.core.* +import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment diff --git a/app/src/main/java/com/bitchat/android/onboarding/LocationCheckScreen.kt b/app/src/main/java/com/bitchat/android/onboarding/LocationCheckScreen.kt index c5bb8c9b..32c86c0b 100644 --- a/app/src/main/java/com/bitchat/android/onboarding/LocationCheckScreen.kt +++ b/app/src/main/java/com/bitchat/android/onboarding/LocationCheckScreen.kt @@ -1,10 +1,10 @@ package com.bitchat.android.onboarding -import androidx.compose.animation.core.* -import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.* +import androidx.compose.animation.core.* +import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment diff --git a/app/src/main/java/com/bitchat/android/onboarding/PermissionExplanationScreen.kt b/app/src/main/java/com/bitchat/android/onboarding/PermissionExplanationScreen.kt index 48138d66..a9b61476 100644 --- a/app/src/main/java/com/bitchat/android/onboarding/PermissionExplanationScreen.kt +++ b/app/src/main/java/com/bitchat/android/onboarding/PermissionExplanationScreen.kt @@ -1,9 +1,5 @@ package com.bitchat.android.onboarding -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Bluetooth import androidx.compose.material.icons.filled.LocationOn @@ -13,6 +9,10 @@ import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.Security import androidx.compose.material.icons.filled.Wifi import androidx.compose.material.icons.filled.Settings +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment diff --git a/app/src/main/java/com/bitchat/android/ui/AboutSections.kt b/app/src/main/java/com/bitchat/android/ui/AboutSections.kt index ae1b79a0..3df4fe5d 100644 --- a/app/src/main/java/com/bitchat/android/ui/AboutSections.kt +++ b/app/src/main/java/com/bitchat/android/ui/AboutSections.kt @@ -1,5 +1,17 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AlternateEmail +import androidx.compose.material.icons.filled.DeleteForever +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Shuffle +import androidx.compose.material.icons.filled.Terminal +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material.icons.filled.WifiOff +import androidx.compose.material.icons.outlined.BookmarkBorder +import androidx.compose.material.icons.outlined.Lock +import androidx.compose.material.icons.outlined.Public +import androidx.compose.material.icons.outlined.VisibilityOff import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.animateColorAsState @@ -19,18 +31,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AlternateEmail -import androidx.compose.material.icons.filled.DeleteForever -import androidx.compose.material.icons.filled.Groups -import androidx.compose.material.icons.filled.Shuffle -import androidx.compose.material.icons.filled.Terminal -import androidx.compose.material.icons.filled.Warning -import androidx.compose.material.icons.filled.WifiOff -import androidx.compose.material.icons.outlined.BookmarkBorder -import androidx.compose.material.icons.outlined.Lock -import androidx.compose.material.icons.outlined.Public -import androidx.compose.material.icons.outlined.VisibilityOff import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -70,10 +70,12 @@ internal val AboutHorizontalPadding = 20.dp /** Card corner radius for grouped rows. */ internal val AboutCardShape = RoundedCornerShape(16.dp) -/** Two top-level views of the sheet. */ +/** + * Two top-level views of the sheet: what the app is and how to drive it, versus the knobs. + */ enum class AboutTab { - HowToUse, - BasicInfo, + Info, + Settings, } /** @@ -175,7 +177,7 @@ internal fun AboutTabBar( var rowWidth by remember { mutableStateOf(0.dp) } val tabWidth = rowWidth / 2 val indicatorOffset by animateDpAsState( - targetValue = if (selected == AboutTab.HowToUse) 0.dp else tabWidth, + targetValue = if (selected == AboutTab.Info) 0.dp else tabWidth, animationSpec = tween(BitchatMotion.STANDARD_MS, easing = FastOutSlowInEasing), label = "aboutTabIndicator" ) @@ -190,15 +192,15 @@ internal fun AboutTabBar( ) { Row(modifier = Modifier.fillMaxWidth()) { AboutTabLabel( - text = stringResource(R.string.about_tab_how_to_use), - isSelected = selected == AboutTab.HowToUse, - onClick = { onSelect(AboutTab.HowToUse) }, + text = stringResource(R.string.about_tab_info), + isSelected = selected == AboutTab.Info, + onClick = { onSelect(AboutTab.Info) }, modifier = Modifier.weight(1f) ) AboutTabLabel( - text = stringResource(R.string.about_tab_basic_info), - isSelected = selected == AboutTab.BasicInfo, - onClick = { onSelect(AboutTab.BasicInfo) }, + text = stringResource(R.string.about_tab_settings), + isSelected = selected == AboutTab.Settings, + onClick = { onSelect(AboutTab.Settings) }, modifier = Modifier.weight(1f) ) } @@ -272,8 +274,8 @@ private fun AboutInstructionRow( contentDescription = null, tint = colorScheme.primary, modifier = Modifier - .padding(top = 2.dp) - .size(18.dp) + .padding(top = 1.dp) + .size(22.dp) ) Text( text = text, @@ -315,7 +317,7 @@ internal fun AboutHowToUseSection(modifier: Modifier = Modifier) { text = stringResource(R.string.about_howto_channels) ) AboutInstructionRow( - icon = Icons.Filled.Groups, + icon = Icons.Filled.Person, text = stringResource(R.string.about_howto_people) ) AboutInstructionRow( @@ -428,7 +430,7 @@ private fun AboutFeatureRow( tint = colorScheme.primary, modifier = Modifier .padding(top = 1.dp) - .size(22.dp) + .size(24.dp) ) Spacer(modifier = Modifier.width(16.dp)) Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { @@ -580,7 +582,7 @@ internal fun SheetHeaderBadge( imageVector = icon, contentDescription = null, tint = colorScheme.primary, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(26.dp) ) } } diff --git a/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt b/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt index 2049b518..9b3d6836 100644 --- a/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt @@ -1,5 +1,12 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bluetooth +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Public +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material.icons.filled.Security +import androidx.compose.material.icons.filled.Speed import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateFloatAsState @@ -10,13 +17,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Bluetooth -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material.icons.filled.Public -import androidx.compose.material.icons.filled.Warning -import androidx.compose.material.icons.filled.Security -import androidx.compose.material.icons.filled.Speed import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -246,7 +246,7 @@ fun AboutSheet( val colorScheme = MaterialTheme.colorScheme val palette = LocalBitchatPalette.current - var selectedTab by remember { mutableStateOf(AboutTab.HowToUse) } + var selectedTab by remember { mutableStateOf(AboutTab.Info) } if (isPresented) { BitchatBottomSheet( @@ -273,21 +273,22 @@ fun AboutSheet( ) } - if (selectedTab == AboutTab.HowToUse) { + if (selectedTab == AboutTab.Info) { + // What the app is, then how to drive it. Both are reference material a + // new user reads once, so they belong on the same tab. + item(key = "features") { + Column { + AboutSectionLabel(text = stringResource(R.string.about_section_about)) + AboutFeatureCard() + } + } + item(key = "how_to_use") { AboutHowToUseSection() } } - if (selectedTab == AboutTab.BasicInfo) { - // Features Section - Grouped Card - item(key = "features") { - Column { - AboutSectionLabel(text = stringResource(R.string.about_section_about)) - AboutFeatureCard() - } - } - + if (selectedTab == AboutTab.Settings) { // Appearance Section item(key = "appearance") { Column { @@ -569,7 +570,7 @@ fun AboutSheet( item(key = "warning") { AboutWarningCard(modifier = Modifier.padding(top = 24.dp)) } - } // end BasicInfo tab + } // end Settings tab // Footer item(key = "footer") { diff --git a/app/src/main/java/com/bitchat/android/ui/AnimatedCount.kt b/app/src/main/java/com/bitchat/android/ui/AnimatedCount.kt new file mode 100644 index 00000000..41650b25 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/ui/AnimatedCount.kt @@ -0,0 +1,112 @@ +package com.bitchat.android.ui + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.TextUnit +import com.bitchat.android.ui.theme.BitchatMotion + +/** + * Counts that roll to their new value instead of snapping. + * + * Peer counts change on their own, without the user doing anything, so a hard digit swap is easy + * to miss and looks like a rendering fault when it is noticed. Sliding the digits in the + * direction the number moved — up when someone joins, down when someone leaves — conveys the + * change without needing a separate indicator. + */ +@Composable +fun AnimatedCount( + count: Int, + modifier: Modifier = Modifier, + style: TextStyle = LocalTextStyle.current, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + prefix: String = "", +) { + AnimatedContent( + targetState = count, + transitionSpec = { + val goingUp = targetState > initialState + ( + slideInVertically( + animationSpec = tween(BitchatMotion.STANDARD_MS, easing = FastOutSlowInEasing), + initialOffsetY = { height -> if (goingUp) height else -height } + ) + fadeIn(tween(BitchatMotion.STANDARD_MS)) + ).togetherWith( + slideOutVertically( + animationSpec = tween(BitchatMotion.STANDARD_MS, easing = FastOutSlowInEasing), + targetOffsetY = { height -> if (goingUp) -height else height } + ) + fadeOut(tween(BitchatMotion.QUICK_MS)) + // Clip so the outgoing digit cannot bleed past the text bounds mid-transition. + ) using SizeTransform(clip = true) + }, + modifier = modifier, + label = "animatedCount" + ) { value -> + Text( + text = "$prefix$value", + style = style, + color = color, + fontSize = fontSize, + fontWeight = fontWeight, + fontFamily = fontFamily, + maxLines = 1 + ) + } +} + +/** + * Cross-fades a label whose text embeds a count, e.g. `People (7)` or `3 people`. + * + * Used where the number is not isolated in its own composable and cannot be rolled on its own. + * The transition is keyed on [count] rather than on [text] so that a label changing for some + * other reason (a locale switch, say) does not animate. + */ +@Composable +fun AnimatedCountLabel( + count: Int, + text: String, + modifier: Modifier = Modifier, + style: TextStyle = LocalTextStyle.current, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, +) { + AnimatedContent( + targetState = count, + transitionSpec = { + fadeIn(tween(BitchatMotion.STANDARD_MS, easing = FastOutSlowInEasing)) + .togetherWith(fadeOut(tween(BitchatMotion.QUICK_MS))) + .using(SizeTransform(clip = false)) + }, + modifier = modifier, + label = "animatedCountLabel" + ) { _ -> + Text( + text = text, + style = style, + color = color, + fontSize = fontSize, + fontWeight = fontWeight, + fontFamily = fontFamily, + maxLines = 1 + ) + } +} diff --git a/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt b/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt index adda2378..6576a712 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt @@ -1,10 +1,20 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.* import android.util.Log +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.animation.togetherWith import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.* @@ -14,10 +24,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.* -import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -45,14 +51,29 @@ import com.bitchat.android.ui.theme.LocalBitchatPalette * Extracted from ChatScreen.kt for better organization */ -/** Height of the chat top bar. Taller than the old 42.dp so 40.dp tap targets fit properly. */ -val ChatHeaderHeight = 48.dp +/** Height of the chat top bar. Taller than the old 42.dp so 44.dp tap targets fit properly. */ +val ChatHeaderHeight = 52.dp -/** Standard icon size in the header. */ -private val HeaderIconSize = 18.dp +/** + * The single icon size used by every glyph in the top bar. + * + * Previously the bar mixed 12/14/16/18.dp icons, which is why it read as a collection of + * unrelated glyphs rather than one control strip. One size for everything, and large enough to + * actually see. + */ +internal val HeaderIconSize = 22.dp + +/** + * Text size for the top bar's labels: nickname, channel name, peer count. + * + * A step up from the 15.sp body scale. The bar is the app's primary status readout and was + * noticeably harder to read than the messages below it; the extra point costs nothing because + * the bar's height is driven by [HeaderTapTarget], not by the text. + */ +private val HeaderTextSize = 17.sp /** Minimum tap target for every interactive element in the header. */ -private val HeaderTapTarget = 40.dp +private val HeaderTapTarget = 44.dp /** Corner radius for the header's tappable label+icon clusters. */ private val HeaderClusterShape = RoundedCornerShape(8.dp) @@ -81,6 +102,14 @@ private fun HeaderIconButton( } } +/** + * Connection status indicator. + * + * Only visible while the connection is still coming up (or has failed). A steady green "all + * good" light is noise: the absence of the indicator already means everything is fine, and the + * eye stops registering an always-on dot anyway. It fades and scales in/out so appearing and + * disappearing reads as a state change rather than a glitch. + */ @Composable fun TorStatusDot( modifier: Modifier = Modifier @@ -88,27 +117,35 @@ fun TorStatusDot( val palette = LocalBitchatPalette.current val torProvider = remember { com.bitchat.android.net.ArtiTorManager.getInstance() } val torStatus by torProvider.statusFlow.collectAsState() - - if (torStatus.mode != com.bitchat.android.net.TorMode.OFF) { - val targetColor = when { - torStatus.running && torStatus.bootstrapPercent < 100 -> palette.accentOrange - torStatus.running && torStatus.bootstrapPercent >= 100 -> palette.accentGreen - else -> palette.accentRed - } - // Cross-fade rather than snap: Tor flips through bootstrapping states frequently and a - // hard colour cut in the corner of the screen reads as a glitch. + + val isEnabled = torStatus.mode != com.bitchat.android.net.TorMode.OFF + val isEstablished = torStatus.running && torStatus.bootstrapPercent >= 100 + val isVisible = isEnabled && !isEstablished + + AnimatedVisibility( + visible = isVisible, + enter = fadeIn(tween(BitchatMotion.STANDARD_MS)) + + scaleIn( + initialScale = 0.4f, + animationSpec = tween(BitchatMotion.STANDARD_MS, easing = FastOutSlowInEasing) + ), + exit = fadeOut(tween(BitchatMotion.QUICK_MS)) + + scaleOut( + targetScale = 0.4f, + animationSpec = tween(BitchatMotion.QUICK_MS, easing = FastOutSlowInEasing) + ), + ) { + // Bootstrapping vs. failed. Cross-faded, because Tor flips between these frequently and + // a hard colour cut in the corner of the screen reads as a rendering fault. val dotColor by animateColorAsState( - targetValue = targetColor, + targetValue = if (torStatus.running) palette.accentOrange else palette.accentRed, animationSpec = tween(BitchatMotion.STANDARD_MS, easing = FastOutSlowInEasing), label = "torDotColor" ) - Canvas( - modifier = modifier - ) { - val radius = size.minDimension / 2 + Canvas(modifier = modifier) { drawCircle( color = dotColor, - radius = radius, + radius = size.minDimension / 2, center = Offset(size.width / 2, size.height / 2) ) } @@ -179,15 +216,17 @@ fun NicknameEditor( Text( text = stringResource(R.string.at_symbol), style = MaterialTheme.typography.bodyMedium, + fontSize = HeaderTextSize, color = colorScheme.primary.copy(alpha = 0.7f) ) - + BasicTextField( value = value, onValueChange = onValueChange, textStyle = MaterialTheme.typography.bodyMedium.copy( color = colorScheme.primary, - fontFamily = FontFamily.Monospace + fontFamily = FontFamily.Monospace, + fontSize = HeaderTextSize ), cursorBrush = SolidColor(colorScheme.primary), singleLine = true, @@ -198,7 +237,7 @@ fun NicknameEditor( } ), modifier = Modifier - .widthIn(max = 140.dp) + .widthIn(max = 150.dp) .horizontalScroll(scrollState) ) } @@ -250,9 +289,9 @@ fun PeerCounter( .padding(horizontal = 6.dp) ) { Icon( - // `Groups` reads as a crowd at 18.dp, where `Group`'s two-person silhouette turns - // into an indistinct blob. - imageVector = Icons.Filled.Groups, + // A single silhouette rather than a crowd: at 22.dp a multi-person glyph collapses + // into an indistinct blob, and the number beside it already conveys "how many". + imageVector = Icons.Filled.Person, contentDescription = when (selectedLocationChannel) { is com.bitchat.android.geohash.ChannelID.Location -> stringResource(R.string.cd_geohash_participants) else -> stringResource(R.string.cd_connected_peers) @@ -261,17 +300,20 @@ fun PeerCounter( tint = animatedCountColor ) - Text( - text = "$peopleCount", + AnimatedCount( + count = peopleCount, style = MaterialTheme.typography.bodyMedium, + fontSize = HeaderTextSize, color = animatedCountColor, fontWeight = FontWeight.Medium ) - + if (joinedChannels.isNotEmpty()) { - Text( - text = stringResource(R.string.channel_count_prefix) + "${joinedChannels.size}", + AnimatedCount( + count = joinedChannels.size, + prefix = stringResource(R.string.channel_count_prefix), style = MaterialTheme.typography.bodyMedium, + fontSize = HeaderTextSize, color = if (isConnected) palette.accentGreen else palette.accentRed, fontWeight = FontWeight.Medium ) @@ -343,7 +385,7 @@ private fun ChannelHeader( Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back), - modifier = Modifier.size(20.dp), + modifier = Modifier.size(HeaderIconSize), tint = colorScheme.primary ) } @@ -352,6 +394,7 @@ private fun ChannelHeader( Text( text = "#$channel", style = MaterialTheme.typography.titleMedium, + fontSize = HeaderTextSize, color = colorScheme.primary, modifier = Modifier .align(Alignment.Center) @@ -368,6 +411,7 @@ private fun ChannelHeader( Text( text = stringResource(R.string.chat_leave), style = MaterialTheme.typography.labelMedium, + fontSize = 15.sp, color = palette.accentRed ) } @@ -395,11 +439,6 @@ private fun MainHeader( val selectedLocationChannel by viewModel.selectedLocationChannel.collectAsStateWithLifecycle() val geohashPeople by viewModel.geohashPeople.collectAsStateWithLifecycle() - // Bookmarks store for current geohash toggle (iOS parity) - val context = androidx.compose.ui.platform.LocalContext.current - val bookmarksStore = remember { com.bitchat.android.geohash.GeohashBookmarksStore.getInstance(context) } - val bookmarks by bookmarksStore.bookmarks.collectAsStateWithLifecycle() - Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically @@ -423,6 +462,7 @@ private fun MainHeader( Text( text = "/", style = MaterialTheme.typography.bodyMedium, + fontSize = HeaderTextSize, // Dimmed: the slash is a separator, not content. At full brightness it competed // with the nickname beside it. color = colorScheme.primary.copy(alpha = 0.45f), @@ -435,12 +475,26 @@ private fun MainHeader( ) } - // MARK: - Status cluster. Order matches the design: bookmark, channel, people. + // MARK: - Status cluster. + // + // Order, left to right: connection state, unread DMs, notes, channel, people. + // The connection indicator leads because it is the only item that can invalidate + // everything to its right, and because it appears and disappears on its own — a fixed + // leftmost slot means the rest of the cluster never shifts when it does. Row( verticalAlignment = Alignment.CenterVertically, - // Tight, because every child below is its own >=40.dp tap target. + // Tight, because every child below is its own >=44.dp tap target. horizontalArrangement = Arrangement.spacedBy(2.dp) ) { + // Connection status. The Box holds the slot open at a constant width so the icons to + // its right stay put whether or not the dot is currently showing. + Box( + modifier = Modifier.size(HeaderIconSize), + contentAlignment = Alignment.Center + ) { + TorStatusDot(modifier = Modifier.size(8.dp)) + } + // Unread private messages badge (click to open most recent DM) if (hasUnreadPrivateMessages.isNotEmpty()) { HeaderIconButton( @@ -462,37 +516,9 @@ private fun MainHeader( onClick = onLocationNotesClick ) - // Tor status dot. Wrapped in a fixed box so its optical position no longer depends - // on ad-hoc asymmetric padding. - Box(modifier = Modifier.size(16.dp), contentAlignment = Alignment.Center) { - TorStatusDot(modifier = Modifier.size(6.dp)) - } - - // PoW status indicator - PoWStatusIndicator( - modifier = Modifier, - style = PoWIndicatorStyle.COMPACT - ) - - // Bookmark toggle for current geohash (not shown for mesh) - val currentGeohash: String? = when (val sc = selectedLocationChannel) { - is com.bitchat.android.geohash.ChannelID.Location -> sc.channel.geohash - else -> null - } - if (currentGeohash != null) { - val isBookmarked = bookmarks.contains(currentGeohash) - HeaderIconButton( - onClick = { bookmarksStore.toggle(currentGeohash) }, - contentDescription = stringResource(R.string.cd_toggle_bookmark) - ) { - Icon( - imageVector = if (isBookmarked) Icons.Filled.Bookmark else Icons.Outlined.BookmarkBorder, - contentDescription = stringResource(R.string.cd_toggle_bookmark), - tint = if (isBookmarked) palette.accentGreen else palette.textSecondary, - modifier = Modifier.size(HeaderIconSize) - ) - } - } + // Bookmarking lives in the Location Channels sheet, one tap away via the channel + // button below. Duplicating it here bought a shortcut for a rare action at the cost + // of a slot in the app's most crowded row. LocationChannelsButton( viewModel = viewModel, @@ -532,8 +558,11 @@ private fun LocationChannelsButton( val isLocation = selectedChannel is com.bitchat.android.geohash.ChannelID.Location val badgeText = when (val channel = selectedChannel) { + // Geohashes keep the '#' because that is how they are written and typed everywhere else. is com.bitchat.android.geohash.ChannelID.Location -> "#${channel.channel.geohash}" - else -> "#mesh" + // The local mesh is not a hashtag channel, and the Bluetooth glyph already says what it + // is, so it is plain "mesh". + else -> stringResource(R.string.mesh_label) } val badgeColor = if (isLocation) palette.accentGreen else palette.accentBlue val badgeIcon = if (isLocation) Icons.Outlined.Public else Icons.Filled.Bluetooth @@ -557,6 +586,7 @@ private fun LocationChannelsButton( Text( text = badgeText, style = MaterialTheme.typography.bodyMedium, + fontSize = HeaderTextSize, fontWeight = FontWeight.Medium, color = badgeColor, maxLines = 1 @@ -567,7 +597,7 @@ private fun LocationChannelsButton( Icon( imageVector = Icons.Default.PinDrop, contentDescription = stringResource(R.string.cd_teleported), - modifier = Modifier.size(12.dp), + modifier = Modifier.size(HeaderIconSize), tint = badgeColor ) } diff --git a/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt b/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt index 340f180c..6f4ff8a3 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt @@ -4,6 +4,8 @@ package com.bitchat.android.ui // [Goose] Installing FileShareDispatcher handler in ChatScreen to forward file sends to ViewModel +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDownward import androidx.compose.animation.* import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.tween @@ -14,8 +16,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.Alignment -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDownward import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.IconButton @@ -303,6 +303,7 @@ fun ChatScreen(viewModel: ChatViewModel) { Icon( imageVector = Icons.Filled.ArrowDownward, contentDescription = stringResource(com.bitchat.android.R.string.cd_scroll_to_bottom), + modifier = Modifier.size(22.dp), tint = palette.accentGreen ) } diff --git a/app/src/main/java/com/bitchat/android/ui/GeohashPeopleList.kt b/app/src/main/java/com/bitchat/android/ui/GeohashPeopleList.kt index 1eccc152..cb3cc476 100644 --- a/app/src/main/java/com/bitchat/android/ui/GeohashPeopleList.kt +++ b/app/src/main/java/com/bitchat/android/ui/GeohashPeopleList.kt @@ -1,13 +1,13 @@ package com.bitchat.android.ui -import android.util.Log -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.Explore import androidx.compose.material.icons.outlined.LocationOn +import android.util.Log +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -193,7 +193,7 @@ private fun GeohashPersonItem( Icon( imageVector = Icons.Filled.Email, contentDescription = stringResource(R.string.cd_unread_message), - modifier = Modifier.size(16.dp), + modifier = Modifier.size(18.dp), tint = palette.accentOrange ) } else { @@ -205,7 +205,7 @@ private fun GeohashPersonItem( else -> "face.smiling" to palette.textSecondary } - // Use appropriate Material icon (closest match to iOS SF Symbols) + // Teleported users get the pin (they chose this location); locals get the person. val icon = when (iconName) { "face.dashed" -> Icons.Outlined.Explore else -> Icons.Outlined.LocationOn @@ -214,8 +214,8 @@ private fun GeohashPersonItem( Icon( imageVector = icon, contentDescription = if (isTeleported || isMyTeleported) "Teleported user" else "User", - modifier = Modifier.size(16.dp), - tint = iconColor.copy(alpha = if (iconName == "face.dashed") 0.6f else 1.0f) // Make dashed faces slightly transparent + modifier = Modifier.size(18.dp), + tint = iconColor.copy(alpha = if (iconName == "face.dashed") 0.6f else 1.0f) ) } diff --git a/app/src/main/java/com/bitchat/android/ui/GeohashPickerActivity.kt b/app/src/main/java/com/bitchat/android/ui/GeohashPickerActivity.kt index e15f9930..0f2f53bb 100644 --- a/app/src/main/java/com/bitchat/android/ui/GeohashPickerActivity.kt +++ b/app/src/main/java/com/bitchat/android/ui/GeohashPickerActivity.kt @@ -1,5 +1,9 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Remove import android.annotation.SuppressLint import android.app.Activity import android.content.Intent @@ -15,10 +19,6 @@ import android.webkit.WebViewClient import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Remove import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api diff --git a/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt b/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt index 0504aa11..4cae30dd 100644 --- a/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt @@ -210,22 +210,11 @@ class GeohashViewModel( powDifficulty = if (pow.enabled) pow.difficulty else null ) messageManager.addChannelMessage("geo:${channel.geohash}", localMsg) - val startedMining = pow.enabled && pow.difficulty > 0 - if (startedMining) { - com.bitchat.android.ui.PoWMiningTracker.startMiningMessage(tempId) - } - try { - val identity = NostrIdentityBridge.deriveIdentity(forGeohash = channel.geohash, context = getApplication()) - val teleported = state.isTeleported.value - val event = NostrProtocol.createEphemeralGeohashEvent(content, channel.geohash, identity, nickname, teleported) - val relayManager = NostrRelayManager.getInstance(getApplication()) - relayManager.sendEventToGeohash(event, channel.geohash, includeDefaults = false, nRelays = 5) - } finally { - // Ensure we stop the per-message mining animation regardless of success/failure - if (startedMining) { - com.bitchat.android.ui.PoWMiningTracker.stopMiningMessage(tempId) - } - } + val identity = NostrIdentityBridge.deriveIdentity(forGeohash = channel.geohash, context = getApplication()) + val teleported = state.isTeleported.value + val event = NostrProtocol.createEphemeralGeohashEvent(content, channel.geohash, identity, nickname, teleported) + val relayManager = NostrRelayManager.getInstance(getApplication()) + relayManager.sendEventToGeohash(event, channel.geohash, includeDefaults = false, nRelays = 5) } catch (e: Exception) { Log.e(TAG, "Failed to send geohash message: ${e.message}") } diff --git a/app/src/main/java/com/bitchat/android/ui/InputComponents.kt b/app/src/main/java/com/bitchat/android/ui/InputComponents.kt index 3fac4761..938729ce 100644 --- a/app/src/main/java/com/bitchat/android/ui/InputComponents.kt +++ b/app/src/main/java/com/bitchat/android/ui/InputComponents.kt @@ -2,6 +2,8 @@ package com.bitchat.android.ui // [Goose] TODO: Replace inline file attachment stub with FilePickerButton abstraction that dispatches via FileShareDispatcher +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.tween @@ -12,8 +14,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -187,10 +187,10 @@ private val ComposerMinHeight = 44.dp private val ComposerShape = RoundedCornerShape(22.dp) /** Diameter of the circular send affordance nested inside the pill. */ -private val SendButtonSize = 32.dp +private val SendButtonSize = 36.dp -/** Tap target for the media affordances sitting outside the pill. */ -private val MediaButtonSize = 40.dp +/** Icon size shared by the composer's glyphs, matching the top bar. */ +internal val ComposerIconSize = 22.dp @Composable fun MessageInput( @@ -325,7 +325,7 @@ fun MessageInput( hasText = hasText, isAccented = selectedPrivatePeer != null || currentChannel != null, onSend = onSend, - modifier = Modifier.padding(end = 6.dp, bottom = 6.dp) + modifier = Modifier.padding(end = 4.dp, bottom = 4.dp) ) } @@ -430,7 +430,7 @@ private fun SendButton( Icon( imageVector = Icons.Filled.ArrowUpward, contentDescription = stringResource(id = R.string.send_message), - modifier = Modifier.size(18.dp), + modifier = Modifier.size(ComposerIconSize), tint = tint ) } diff --git a/app/src/main/java/com/bitchat/android/ui/LinkPreviewPill.kt b/app/src/main/java/com/bitchat/android/ui/LinkPreviewPill.kt index 4896e345..cc0d3026 100644 --- a/app/src/main/java/com/bitchat/android/ui/LinkPreviewPill.kt +++ b/app/src/main/java/com/bitchat/android/ui/LinkPreviewPill.kt @@ -1,5 +1,7 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Link import android.content.Intent import android.net.Uri import androidx.compose.foundation.background @@ -7,8 +9,6 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Link import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment diff --git a/app/src/main/java/com/bitchat/android/ui/LocationChannelsSheet.kt b/app/src/main/java/com/bitchat/android/ui/LocationChannelsSheet.kt index 98e1fb40..3f3e59cf 100644 --- a/app/src/main/java/com/bitchat/android/ui/LocationChannelsSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/LocationChannelsSheet.kt @@ -1,5 +1,12 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bookmark +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Map +import androidx.compose.material.icons.filled.PinDrop +import androidx.compose.material.icons.outlined.BookmarkBorder +import androidx.compose.material.icons.outlined.Public import android.content.Intent import android.net.Uri import android.provider.Settings @@ -15,13 +22,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Bookmark -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Map -import androidx.compose.material.icons.filled.PinDrop -import androidx.compose.material.icons.outlined.BookmarkBorder -import androidx.compose.material.icons.outlined.Public import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -218,6 +218,7 @@ fun LocationChannelsSheet( title = meshTitleWithCount(viewModel), subtitle = stringResource(R.string.location_bluetooth_subtitle, bluetoothRangeString()), isSelected = selectedChannel is ChannelID.Mesh, + participantCount = meshCount(viewModel), titleColor = standardBlue, titleBold = meshCount(viewModel) > 0, trailingContent = null, @@ -251,6 +252,7 @@ fun LocationChannelsSheet( title = geohashTitleWithCount(channel, participantCount), subtitle = subtitlePrefix + (namePart?.let { " • $it" } ?: ""), isSelected = isChannelSelected(channel, selectedChannel), + participantCount = participantCount, titleColor = standardGreen, titleBold = highlight, trailingContent = { @@ -259,7 +261,7 @@ fun LocationChannelsSheet( imageVector = if (isBookmarked) Icons.Filled.Bookmark else Icons.Outlined.BookmarkBorder, contentDescription = if (isBookmarked) stringResource(R.string.cd_remove_bookmark) else stringResource(R.string.cd_add_bookmark), tint = if (isBookmarked) palette.accentGreen else palette.textSecondary, - modifier = Modifier.size(18.dp) + modifier = Modifier.size(22.dp) ) } }, @@ -314,6 +316,7 @@ fun LocationChannelsSheet( title = title, subtitle = subtitle, isSelected = isChannelSelected(channel, selectedChannel), + participantCount = participantCount, titleColor = null, titleBold = participantCount > 0, trailingContent = { @@ -322,7 +325,7 @@ fun LocationChannelsSheet( imageVector = Icons.Filled.Bookmark, contentDescription = stringResource(R.string.cd_remove_bookmark), tint = palette.accentGreen, - modifier = Modifier.size(18.dp) + modifier = Modifier.size(22.dp) ) } }, @@ -432,7 +435,7 @@ fun LocationChannelsSheet( imageVector = Icons.Filled.Map, contentDescription = stringResource(R.string.cd_open_map), tint = palette.textSecondary, - modifier = Modifier.size(18.dp) + modifier = Modifier.size(22.dp) ) } @@ -468,7 +471,7 @@ fun LocationChannelsSheet( Icon( imageVector = Icons.Filled.PinDrop, contentDescription = stringResource(R.string.cd_teleport), - modifier = Modifier.size(12.dp), + modifier = Modifier.size(14.dp), tint = teleportColor ) Text( @@ -641,6 +644,8 @@ private fun ChannelRow( title: String, subtitle: String, isSelected: Boolean, + /** Drives the count animation; the number itself is already baked into [title]. */ + participantCount: Int, titleColor: Color? = null, titleBold: Boolean = false, trailingContent: (@Composable (() -> Unit))? = null, @@ -701,7 +706,10 @@ private fun ChannelRow( ) countSuffix?.let { count -> - Text( + AnimatedCountLabel( + // Participant counts are polled from the relays, so they change + // while the sheet is open. + count = participantCount, text = count, fontSize = 11.sp, fontFamily = FontFamily.Monospace, diff --git a/app/src/main/java/com/bitchat/android/ui/LocationNotesButton.kt b/app/src/main/java/com/bitchat/android/ui/LocationNotesButton.kt index af1198bc..1ec6417e 100644 --- a/app/src/main/java/com/bitchat/android/ui/LocationNotesButton.kt +++ b/app/src/main/java/com/bitchat/android/ui/LocationNotesButton.kt @@ -1,8 +1,8 @@ package com.bitchat.android.ui -import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Description +import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -55,12 +55,12 @@ fun LocationNotesButton( val hasNotes = notesCount > 0 IconButton( onClick = onClick, - modifier = modifier.size(40.dp) + modifier = modifier.size(44.dp) ) { Icon( - imageVector = Icons.Outlined.Description, // "long.text.page.and.pencil" equivalent + imageVector = Icons.Outlined.Description, contentDescription = stringResource(R.string.cd_location_notes), - modifier = Modifier.size(18.dp), + modifier = Modifier.size(HeaderIconSize), tint = if (hasNotes) colorScheme.primary else LocalBitchatPalette.current.textSecondary ) } diff --git a/app/src/main/java/com/bitchat/android/ui/LocationNotesSheet.kt b/app/src/main/java/com/bitchat/android/ui/LocationNotesSheet.kt index 29ead6a6..4543d2af 100644 --- a/app/src/main/java/com/bitchat/android/ui/LocationNotesSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/LocationNotesSheet.kt @@ -1,5 +1,7 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowUpward import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -9,8 +11,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowUpward import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.getValue diff --git a/app/src/main/java/com/bitchat/android/ui/MatrixEncryptionAnimation.kt b/app/src/main/java/com/bitchat/android/ui/MatrixEncryptionAnimation.kt deleted file mode 100644 index 52632f93..00000000 --- a/app/src/main/java/com/bitchat/android/ui/MatrixEncryptionAnimation.kt +++ /dev/null @@ -1,183 +0,0 @@ -package com.bitchat.android.ui - -import androidx.compose.material3.ColorScheme -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.bitchat.android.mesh.MeshService -import com.bitchat.android.model.BitchatMessage -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch -import java.text.SimpleDateFormat -import kotlin.random.Random - -/** - * Animation state for individual characters - */ -private enum class CharacterAnimationState { - ENCRYPTED, // Showing random encrypted characters - FINAL // Showing final decrypted character -} - -/** - * Check if a message should be animated based on its mining state - */ -@Composable -fun shouldAnimateMessage(messageId: String): Boolean { - val miningMessages by PoWMiningTracker.miningMessages.collectAsStateWithLifecycle() - return miningMessages.contains(messageId) -} - -/** - * Tracks which messages are currently being mined for PoW - * Provides reactive state for UI animations - */ -object PoWMiningTracker { - private val _miningMessages = MutableStateFlow>(emptySet()) - val miningMessages: StateFlow> = _miningMessages.asStateFlow() - - /** - * Start tracking a message as mining - */ - fun startMiningMessage(messageId: String) { - _miningMessages.value = _miningMessages.value + messageId - } - - /** - * Stop tracking a message as mining - */ - fun stopMiningMessage(messageId: String) { - _miningMessages.value = _miningMessages.value - messageId - } - - /** - * Check if a message is currently mining - */ - fun isMiningMessage(messageId: String): Boolean { - return _miningMessages.value.contains(messageId) - } - - /** - * Clear all mining messages (for cleanup) - */ - fun clearAllMining() { - _miningMessages.value = emptySet() - } -} - -/** - * Shows the active PoW animation inside the same two-row layout used by static text messages. - */ -@Composable -fun MessageWithMatrixAnimation( - message: BitchatMessage, - currentUserNickname: String, - meshService: MeshService, - colorScheme: ColorScheme, - timeFormatter: SimpleDateFormat, - onNicknameClick: ((String) -> Unit)?, - onMessageLongPress: ((BitchatMessage) -> Unit)?, - modifier: Modifier = Modifier, - showSender: Boolean = true, -) { - AnimatedMessageDisplay( - message = message, - currentUserNickname = currentUserNickname, - meshService = meshService, - colorScheme = colorScheme, - timeFormatter = timeFormatter, - onNicknameClick = onNicknameClick, - onMessageLongPress = onMessageLongPress, - modifier = modifier, - showSender = showSender, - ) -} - -/** - * Animates only the body content; sender, metadata, gestures, and spacing remain stable. - */ -@Composable -private fun AnimatedMessageDisplay( - message: BitchatMessage, - currentUserNickname: String, - meshService: MeshService, - colorScheme: ColorScheme, - timeFormatter: SimpleDateFormat, - onNicknameClick: ((String) -> Unit)?, - onMessageLongPress: ((BitchatMessage) -> Unit)?, - modifier: Modifier = Modifier, - showSender: Boolean = true, -) { - var animatedContent by remember(message.id, message.content) { - mutableStateOf(message.content) - } - - // Character-by-character animation state like the JavaScript version - var characterStates by remember(message.id, message.content) { - mutableStateOf(message.content.map { char -> - if (char == ' ') CharacterAnimationState.FINAL else CharacterAnimationState.ENCRYPTED - }) - } - - LaunchedEffect(message.id, message.content) { - if (message.content.isEmpty()) return@LaunchedEffect - - val encryptedChars = "!@$%^&*()_+-=[]{}|;:,<>?".toCharArray() - - // Start character animations with staggered delays (like JS version). - message.content.forEachIndexed { index, targetChar -> - if (targetChar != ' ') { - launch { - delay(index * 50L) - - while (true) { - while (characterStates.getOrNull(index) == CharacterAnimationState.ENCRYPTED) { - val newContent = animatedContent.toCharArray() - if (index < newContent.size) { - newContent[index] = encryptedChars[Random.nextInt(encryptedChars.size)] - animatedContent = String(newContent) - } - - delay(100L) - - if (Random.nextFloat() < 0.1f) { - val finalContent = animatedContent.toCharArray() - if (index < finalContent.size) { - finalContent[index] = targetChar - animatedContent = String(finalContent) - } - - val finalStates = characterStates.toMutableList() - finalStates[index] = CharacterAnimationState.FINAL - characterStates = finalStates - break - } - } - - delay(2000L) - - val resetStates = characterStates.toMutableList() - resetStates[index] = CharacterAnimationState.ENCRYPTED - characterStates = resetStates - } - } - } - } - } - - TextMessageLayout( - message = message, - currentUserNickname = currentUserNickname, - meshService = meshService, - colorScheme = colorScheme, - timeFormatter = timeFormatter, - onNicknameClick = onNicknameClick, - onMessageLongPress = onMessageLongPress, - modifier = modifier, - showSender = showSender, - bodyContent = animatedContent, - ) -} diff --git a/app/src/main/java/com/bitchat/android/ui/MeshPeerListSheet.kt b/app/src/main/java/com/bitchat/android/ui/MeshPeerListSheet.kt index a6c0d63d..5aae35a3 100644 --- a/app/src/main/java/com/bitchat/android/ui/MeshPeerListSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/MeshPeerListSheet.kt @@ -1,19 +1,25 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.* import com.bitchat.android.R import android.util.Log +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Groups -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.* -import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -37,6 +43,7 @@ import com.bitchat.android.favorites.FavoritesPersistenceService import com.bitchat.android.geohash.ChannelID import com.bitchat.android.identity.SecureIdentityStateManager import com.bitchat.android.ui.theme.BASE_FONT_SIZE +import com.bitchat.android.ui.theme.BitchatMotion import com.bitchat.android.ui.theme.LocalBitchatPalette import com.bitchat.android.nostr.GeohashAliasRegistry import com.bitchat.android.nostr.GeohashConversationRegistry @@ -116,8 +123,9 @@ fun MeshPeerListSheet( .padding(horizontal = SheetHorizontalPadding + 14.dp), verticalArrangement = Arrangement.spacedBy(10.dp) ) { - SheetHeaderBadge(icon = Icons.Filled.Groups) - Text( + SheetHeaderBadge(icon = Icons.Filled.Person) + AnimatedCountLabel( + count = peopleCount, text = stringResource(R.string.people_count_title, peopleCount), fontSize = 20.sp, fontFamily = FontFamily.Monospace, @@ -212,13 +220,13 @@ fun MeshPeerListSheet( if (selectedLocationChannel !is ChannelID.Location) { IconButton( onClick = onShowVerification, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(44.dp) ) { Icon( imageVector = Icons.Outlined.QrCode, contentDescription = stringResource(R.string.verify_title), - tint = colorScheme.onSurface.copy(alpha = 0.8f), - modifier = Modifier.size(18.dp) + tint = LocalBitchatPalette.current.textSecondary, + modifier = Modifier.size(22.dp) ) } } @@ -231,6 +239,9 @@ fun MeshPeerListSheet( } } +/** Icon size for rows inside the sheets: one step down from the top bar. */ +private val PeerRowIconSize = 18.dp + @Composable private fun ChannelRow( channel: String, @@ -580,28 +591,26 @@ private fun PeerItem( ) { // Connection/status indicator if (hasUnreadDM) { - // Show mail icon for unread DMs (iOS orange) Icon( imageVector = Icons.Filled.Email, contentDescription = stringResource(R.string.cd_unread_message), - modifier = Modifier.size(16.dp), - tint = Color(0xFFFF9500) // iOS orange + modifier = Modifier.size(PeerRowIconSize), + tint = palette.accentOrange ) } else if (showNostrGlobe) { - // Purple globe to indicate Nostr availability Icon( imageVector = Icons.Filled.Public, contentDescription = stringResource(R.string.cd_reachable_via_nostr), - modifier = Modifier.size(16.dp), - tint = Color(0xFF9C27B0) // Purple + modifier = Modifier.size(PeerRowIconSize), + tint = palette.accentPurple ) } else if (!isDirect && isFavorite) { - // Offline favorited user: show outlined circle icon + // Offline favourite: routed glyph, dimmed to read as unavailable. Icon( imageVector = Icons.Outlined.Circle, contentDescription = stringResource(R.string.cd_offline_favorite), - modifier = Modifier.size(16.dp), - tint = Color.Gray + modifier = Modifier.size(PeerRowIconSize), + tint = palette.textTertiary ) } else { Icon( @@ -615,8 +624,8 @@ private fun PeerItem( isDirect -> "Direct Bluetooth" else -> "Routed" }, - modifier = Modifier.size(16.dp), - tint = colorScheme.onSurface.copy(alpha = 0.6f) + modifier = Modifier.size(PeerRowIconSize), + tint = palette.textSecondary ) } @@ -649,8 +658,8 @@ private fun PeerItem( Icon( imageVector = Icons.Filled.Wifi, contentDescription = "Direct Wi-Fi Aware", - modifier = Modifier.size(13.dp), - tint = colorScheme.onSurface.copy(alpha = 0.8f) + modifier = Modifier.size(PeerRowIconSize), + tint = palette.textSecondary ) } } @@ -660,9 +669,9 @@ private fun PeerItem( Spacer(modifier = Modifier.width(4.dp)) Icon( imageVector = Icons.Filled.Verified, - contentDescription = null, - modifier = Modifier.size(14.dp), - tint = Color(0xFF32D74B) // iOS Green + contentDescription = stringResource(R.string.verify_title), + modifier = Modifier.size(PeerRowIconSize), + tint = palette.accentGreen ) } @@ -673,13 +682,13 @@ private fun PeerItem( // Favorite star with proper filled/outlined states IconButton( onClick = onToggleFavorite, - modifier = Modifier.size(32.dp) + modifier = Modifier.size(44.dp) ) { Icon( imageVector = if (isFavorite) Icons.Filled.Star else Icons.Outlined.Star, contentDescription = if (isFavorite) "Remove from favorites" else "Add to favorites", - modifier = Modifier.size(16.dp), - tint = if (isFavorite) Color(0xFFFFD700) else Color(0xFF4CAF50) + modifier = Modifier.size(PeerRowIconSize), + tint = if (isFavorite) palette.accentOrange else palette.textTertiary ) } } @@ -696,25 +705,41 @@ private fun UnreadBadge( colorScheme: ColorScheme, modifier: Modifier = Modifier ) { - if (count > 0) { + val palette = LocalBitchatPalette.current + // Scale/fade in and out so a badge appearing while the sheet is open is noticed, and one + // clearing does not just blink away. + AnimatedVisibility( + visible = count > 0, + enter = fadeIn(tween(BitchatMotion.STANDARD_MS)) + + scaleIn( + initialScale = 0.5f, + animationSpec = tween(BitchatMotion.STANDARD_MS, easing = FastOutSlowInEasing) + ), + exit = fadeOut(tween(BitchatMotion.QUICK_MS)) + + scaleOut( + targetScale = 0.5f, + animationSpec = tween(BitchatMotion.QUICK_MS, easing = FastOutSlowInEasing) + ), + modifier = modifier + ) { Box( - modifier = modifier + modifier = Modifier .background( - color = Color(0xFFFFD700), // Yellow color + color = palette.accentOrange, shape = RoundedCornerShape(10.dp) ) .padding(horizontal = 6.dp, vertical = 2.dp) .defaultMinSize(minWidth = 18.dp, minHeight = 18.dp), contentAlignment = Alignment.Center ) { - Text( + AnimatedCountLabel( + count = count, text = if (count > 99) "99+" else count.toString(), - style = MaterialTheme.typography.labelSmall.copy( - fontSize = 10.sp, - fontWeight = FontWeight.Bold, - fontFamily = FontFamily.Monospace - ), - color = Color.Black // Black text on yellow background + style = MaterialTheme.typography.labelSmall, + fontSize = 10.sp, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace, + color = Color.Black ) } } diff --git a/app/src/main/java/com/bitchat/android/ui/MessageComponents.kt b/app/src/main/java/com/bitchat/android/ui/MessageComponents.kt index 7457c681..83db678a 100644 --- a/app/src/main/java/com/bitchat/android/ui/MessageComponents.kt +++ b/app/src/main/java/com/bitchat/android/ui/MessageComponents.kt @@ -1,6 +1,8 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -17,8 +19,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close import androidx.compose.material3.ColorScheme import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -393,24 +393,7 @@ fun MessageItem( return } - // Check if this message should be animated during PoW mining - val shouldAnimate = shouldAnimateMessage(message.id) - - // If animation is needed, use the matrix animation component for content only - if (shouldAnimate) { - // Display message with matrix animation for content - MessageWithMatrixAnimation( - message = message, - currentUserNickname = currentUserNickname, - meshService = meshService, - colorScheme = colorScheme, - timeFormatter = timeFormatter, - showSender = showSender, - onNicknameClick = onNicknameClick, - onMessageLongPress = onMessageLongPress, - modifier = modifier - ) - } else if (message.sender == "system") { + if (message.sender == "system") { // Background narration: `// Tor started. Routing all chats…` val annotatedText = remember(message, palette) { formatSystemMessage( diff --git a/app/src/main/java/com/bitchat/android/ui/PoWStatusIndicator.kt b/app/src/main/java/com/bitchat/android/ui/PoWStatusIndicator.kt deleted file mode 100644 index 883b2b45..00000000 --- a/app/src/main/java/com/bitchat/android/ui/PoWStatusIndicator.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.bitchat.android.ui - -import androidx.compose.animation.core.* -import androidx.compose.foundation.layout.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Security -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.bitchat.android.nostr.NostrProofOfWork -import androidx.compose.ui.res.stringResource -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.bitchat.android.R -import com.bitchat.android.nostr.PoWPreferenceManager - -/** - * Shows the current Proof of Work status and settings - */ -@Composable -fun PoWStatusIndicator( - modifier: Modifier = Modifier, - style: PoWIndicatorStyle = PoWIndicatorStyle.COMPACT -) { - val powEnabled by PoWPreferenceManager.powEnabled.collectAsStateWithLifecycle() - val powDifficulty by PoWPreferenceManager.powDifficulty.collectAsStateWithLifecycle() - val isMining by PoWPreferenceManager.isMining.collectAsStateWithLifecycle() - val colorScheme = MaterialTheme.colorScheme - val isDark = colorScheme.background.red + colorScheme.background.green + colorScheme.background.blue < 1.5f - - if (!powEnabled) return - - when (style) { - PoWIndicatorStyle.COMPACT -> { - Row( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - // PoW icon with animation if mining - if (isMining) { - val rotation by rememberInfiniteTransition(label = "pow-rotation").animateFloat( - initialValue = 0f, - targetValue = 360f, - animationSpec = infiniteRepeatable( - animation = tween(1000, easing = LinearEasing), - repeatMode = RepeatMode.Restart - ), - label = "pow-icon-rotation" - ) - - Icon( - imageVector = Icons.Filled.Security, - contentDescription = stringResource(R.string.cd_mining_pow), - tint = Color(0xFFFF9500), // Orange for mining - modifier = Modifier - .size(12.dp) - .graphicsLayer { rotationZ = rotation } - ) - } else { - Icon( - imageVector = Icons.Filled.Security, - contentDescription = stringResource(R.string.cd_pow_enabled), - tint = if (isDark) Color(0xFF32D74B) else Color(0xFF248A3D), // Green when ready - modifier = Modifier.size(12.dp) - ) - } - } - } - - PoWIndicatorStyle.DETAILED -> { - Surface( - modifier = modifier, - color = colorScheme.surfaceVariant.copy(alpha = 0.3f), - shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp) - ) { - Row( - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), - horizontalArrangement = Arrangement.spacedBy(6.dp), - verticalAlignment = Alignment.CenterVertically - ) { - // PoW icon - Icon( - imageVector = Icons.Filled.Security, - contentDescription = stringResource(R.string.cd_proof_of_work), - tint = if (isMining) Color(0xFFFF9500) else { - if (isDark) Color(0xFF32D74B) else Color(0xFF248A3D) - }, - modifier = Modifier.size(14.dp) - ) - - // Status text - Text( - text = if (isMining) { - stringResource(R.string.pow_mining_ellipsis) - } else { - stringResource(R.string.pow_label_format, powDifficulty) - }, - fontSize = 11.sp, - fontFamily = FontFamily.Monospace, - color = if (isMining) Color(0xFFFF9500) else { - colorScheme.onSurface.copy(alpha = 0.7f) - } - ) - - // Time estimate - if (!isMining && powDifficulty > 0) { - Text( - text = stringResource(R.string.pow_time_estimate, NostrProofOfWork.estimateMiningTime(powDifficulty)), - fontSize = 9.sp, - fontFamily = FontFamily.Monospace, - color = colorScheme.onSurface.copy(alpha = 0.5f) - ) - } - } - } - } - } -} - -/** - * Style options for the PoW status indicator - */ -enum class PoWIndicatorStyle { - COMPACT, // Small icon + difficulty number - DETAILED // Icon + status text + time estimate -} diff --git a/app/src/main/java/com/bitchat/android/ui/SecurityVerificationSheet.kt b/app/src/main/java/com/bitchat/android/ui/SecurityVerificationSheet.kt index 2633f888..5549f43f 100644 --- a/app/src/main/java/com/bitchat/android/ui/SecurityVerificationSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/SecurityVerificationSheet.kt @@ -1,5 +1,12 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Verified +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material.icons.outlined.NoEncryption +import androidx.compose.material.icons.outlined.Sync +import androidx.compose.material.icons.outlined.Warning as OutlinedWarning import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -12,13 +19,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material.icons.filled.Verified -import androidx.compose.material.icons.filled.Warning -import androidx.compose.material.icons.outlined.NoEncryption -import androidx.compose.material.icons.outlined.Sync -import androidx.compose.material.icons.outlined.Warning as OutlinedWarning import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenu diff --git a/app/src/main/java/com/bitchat/android/ui/VerificationSheet.kt b/app/src/main/java/com/bitchat/android/ui/VerificationSheet.kt index 97392eae..a81938d5 100644 --- a/app/src/main/java/com/bitchat/android/ui/VerificationSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/VerificationSheet.kt @@ -1,5 +1,7 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.QrCodeScanner import android.graphics.Bitmap import android.os.Handler import android.os.Looper @@ -31,8 +33,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.QrCodeScanner import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api diff --git a/app/src/main/java/com/bitchat/android/ui/VoiceInputComponents.kt b/app/src/main/java/com/bitchat/android/ui/VoiceInputComponents.kt index 1c005b46..cdc40583 100644 --- a/app/src/main/java/com/bitchat/android/ui/VoiceInputComponents.kt +++ b/app/src/main/java/com/bitchat/android/ui/VoiceInputComponents.kt @@ -1,13 +1,13 @@ package com.bitchat.android.ui +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Mic import android.Manifest import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Mic import androidx.compose.material3.Icon import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -132,7 +132,7 @@ fun VoiceRecordButton( imageVector = Icons.Filled.Mic, contentDescription = stringResource(com.bitchat.android.R.string.cd_record_voice), tint = Color.Black, - modifier = Modifier.size(20.dp) + modifier = Modifier.size(22.dp) ) } } diff --git a/app/src/main/java/com/bitchat/android/ui/debug/DebugSettingsSheet.kt b/app/src/main/java/com/bitchat/android/ui/debug/DebugSettingsSheet.kt index be950316..8d38c678 100644 --- a/app/src/main/java/com/bitchat/android/ui/debug/DebugSettingsSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/debug/DebugSettingsSheet.kt @@ -1,5 +1,13 @@ package com.bitchat.android.ui.debug +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bluetooth +import androidx.compose.material.icons.filled.Wifi +import androidx.compose.material.icons.filled.WifiTethering +import androidx.compose.material.icons.filled.BugReport +import androidx.compose.material.icons.filled.Devices +import androidx.compose.material.icons.filled.PowerSettingsNew +import androidx.compose.material.icons.filled.SettingsEthernet import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -9,14 +17,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Bluetooth -import androidx.compose.material.icons.filled.Wifi -import androidx.compose.material.icons.filled.WifiTethering -import androidx.compose.material.icons.filled.BugReport -import androidx.compose.material.icons.filled.Devices -import androidx.compose.material.icons.filled.PowerSettingsNew -import androidx.compose.material.icons.filled.SettingsEthernet import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable diff --git a/app/src/main/java/com/bitchat/android/ui/debug/MeshGraph.kt b/app/src/main/java/com/bitchat/android/ui/debug/MeshGraph.kt index 44c7ef55..3254c565 100644 --- a/app/src/main/java/com/bitchat/android/ui/debug/MeshGraph.kt +++ b/app/src/main/java/com/bitchat/android/ui/debug/MeshGraph.kt @@ -1,14 +1,14 @@ package com.bitchat.android.ui.debug +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Wifi +import androidx.compose.material.icons.outlined.Bluetooth import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Wifi -import androidx.compose.material.icons.outlined.Bluetooth import androidx.compose.material3.Icon import androidx.compose.runtime.* import androidx.compose.ui.Modifier diff --git a/app/src/main/java/com/bitchat/android/ui/media/AudioMessageItem.kt b/app/src/main/java/com/bitchat/android/ui/media/AudioMessageItem.kt index dc52f845..8722ab29 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/AudioMessageItem.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/AudioMessageItem.kt @@ -1,11 +1,11 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment diff --git a/app/src/main/java/com/bitchat/android/ui/media/FileMessageItem.kt b/app/src/main/java/com/bitchat/android/ui/media/FileMessageItem.kt index ca57931b..7a289686 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/FileMessageItem.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/FileMessageItem.kt @@ -1,5 +1,7 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Description import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -12,8 +14,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Description import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon diff --git a/app/src/main/java/com/bitchat/android/ui/media/FilePickerButton.kt b/app/src/main/java/com/bitchat/android/ui/media/FilePickerButton.kt index 16d92d7a..bd5f30e0 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/FilePickerButton.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/FilePickerButton.kt @@ -1,11 +1,11 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Attachment import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Attachment import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/bitchat/android/ui/media/FileSendingAnimation.kt b/app/src/main/java/com/bitchat/android/ui/media/FileSendingAnimation.kt index 2f40dec0..2ec7cd9a 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/FileSendingAnimation.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/FileSendingAnimation.kt @@ -1,5 +1,7 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Description import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween @@ -11,8 +13,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Description import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/bitchat/android/ui/media/FullScreenImageViewer.kt b/app/src/main/java/com/bitchat/android/ui/media/FullScreenImageViewer.kt index 2964f9c0..bebc1293 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/FullScreenImageViewer.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/FullScreenImageViewer.kt @@ -1,5 +1,8 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Download import android.content.ContentValues import android.os.Build import android.provider.MediaStore @@ -10,9 +13,6 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Download import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface diff --git a/app/src/main/java/com/bitchat/android/ui/media/ImageMessageItem.kt b/app/src/main/java/com/bitchat/android/ui/media/ImageMessageItem.kt index 01d119fe..a0961cfc 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/ImageMessageItem.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/ImageMessageItem.kt @@ -1,12 +1,12 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.* diff --git a/app/src/main/java/com/bitchat/android/ui/media/ImagePickerButton.kt b/app/src/main/java/com/bitchat/android/ui/media/ImagePickerButton.kt index 172ad50d..df6f8ea7 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/ImagePickerButton.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/ImagePickerButton.kt @@ -1,5 +1,9 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Camera +import androidx.compose.material.icons.filled.Photo +import androidx.compose.material.icons.filled.PhotoCamera import android.Manifest import android.content.pm.PackageManager import android.net.Uri @@ -10,10 +14,6 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Camera -import androidx.compose.material.icons.filled.Photo -import androidx.compose.material.icons.filled.PhotoCamera import androidx.compose.material3.Icon import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -91,8 +91,8 @@ fun ImagePickerButton( Box( modifier = modifier - // 40.dp to match the header's tap targets and the composer's send button. - .size(40.dp) + // Matches the header's tap targets and the composer's send button. + .size(44.dp) .clip(CircleShape) .combinedClickable( onClick = { imagePicker.launch("image/*") }, @@ -110,7 +110,7 @@ fun ImagePickerButton( imageVector = Icons.Filled.PhotoCamera, contentDescription = stringResource(com.bitchat.android.R.string.pick_image), tint = LocalBitchatPalette.current.textSecondary, - modifier = Modifier.size(20.dp) + modifier = Modifier.size(22.dp) ) } diff --git a/app/src/main/java/com/bitchat/android/ui/media/MediaPickerOptions.kt b/app/src/main/java/com/bitchat/android/ui/media/MediaPickerOptions.kt index 47883499..6909a52d 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/MediaPickerOptions.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/MediaPickerOptions.kt @@ -1,5 +1,8 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Description import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -9,9 +12,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Description import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/bitchat/android/ui/media/VoiceNotePlayer.kt b/app/src/main/java/com/bitchat/android/ui/media/VoiceNotePlayer.kt index 67719d87..5341ec1f 100644 --- a/app/src/main/java/com/bitchat/android/ui/media/VoiceNotePlayer.kt +++ b/app/src/main/java/com/bitchat/android/ui/media/VoiceNotePlayer.kt @@ -1,13 +1,13 @@ package com.bitchat.android.ui.media +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Pause +import androidx.compose.material.icons.filled.PlayArrow import android.media.MediaPlayer import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Pause -import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme diff --git a/app/src/main/java/com/bitchat/android/ui/theme/BitchatPalette.kt b/app/src/main/java/com/bitchat/android/ui/theme/BitchatPalette.kt index bd4280eb..bc5e7b03 100644 --- a/app/src/main/java/com/bitchat/android/ui/theme/BitchatPalette.kt +++ b/app/src/main/java/com/bitchat/android/ui/theme/BitchatPalette.kt @@ -22,7 +22,7 @@ data class BitchatPalette( val isDark: Boolean, // MARK: - Surfaces - /** Screen, header and composer background. Slightly green-tinted near-black. */ + /** Screen, header and composer background. True black in dark mode. */ val background: Color, /** Sheets, cards and list rows: one step above [background]. */ val surface: Color, @@ -56,7 +56,10 @@ data class BitchatPalette( val DarkBitchatPalette = BitchatPalette( isDark = true, - background = Color(0xFF070D07), + // True black. OLED panels switch these pixels off entirely, which is both the terminal + // aesthetic the app is going for and a real battery win on a screen that is mostly + // background. + background = Color(0xFF000000), surface = Color(0xFF0E150E), surfaceVariant = Color(0xFF182118), outline = Color(0xFF2A3A2A), @@ -73,7 +76,7 @@ val DarkBitchatPalette = BitchatPalette( val LightBitchatPalette = BitchatPalette( isDark = false, - background = Color(0xFFFBFDFB), + background = Color(0xFFFFFFFF), surface = Color(0xFFF2F6F2), surfaceVariant = Color(0xFFE7EDE7), outline = Color(0xFFCBD6CB), diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 5ad12ff2..00892344 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -75,7 +75,6 @@ مغادرة يمكن الوصول عبر Nostr رسائل خاصة غير مقروءة - تبديل الإشارة المرجعية تم الانتقال حالة Tor الأقران المتصلون diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 76c13dd2..45750704 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -75,7 +75,6 @@ ছেড়ে দিন Nostr এর মাধ্যমে পৌঁছানো যায় অপঠিত ব্যক্তিগত বার্তা - বুকমার্ক টগল করুন টেলিপোর্ট করা হয়েছে Tor অবস্থা সংযুক্ত পিয়ার diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 23168837..dd2675fe 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -75,7 +75,6 @@ verlassen Über Nostr erreichbar Ungelesene private Nachrichten - Lesezeichen umschalten Teleportiert Tor‑Status Verbundenen Peers diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 836f563a..76871eeb 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -75,7 +75,6 @@ salir Accesible vía Nostr Mensajes privados sin leer - Alternar marcador Teletransportado Estado de Tor Pares conectados diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 551ca8db..0bd3529f 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -75,7 +75,6 @@ خروج قابل دسترس از طریق Nostr پیام‌های خصوصی خوانده‌نشده - تغییر وضعیت نشانک انتقال مکانی وضعیت Tor همتایان متصل diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml index 526cc636..288068bc 100644 --- a/app/src/main/res/values-fil/strings.xml +++ b/app/src/main/res/values-fil/strings.xml @@ -75,7 +75,6 @@ umalis Maabot sa Nostr Hindi nabasang pribadong mensahe - I‑toggle ang bookmark Naiteleport Katayuan ng Tor Nakakonektang peers diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 8da0188f..4d2a0d52 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -75,7 +75,6 @@ quitter Joignable via Nostr Messages privés non lus - Basculer le signet Téléporté Statut Tor Pairs connectés diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 6921ace7..5074fa44 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -75,7 +75,6 @@ छोड़ें Nostr के माध्यम से उपलब्ध अपठित निजी संदेश - बुकमार्क टॉगल करें टेलीपोर्ट हुआ Tor स्थिति कनेक्टेड पीयर्स diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index c5eb612c..cae8b84a 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -75,7 +75,6 @@ keluar Dapat dijangkau melalui Nostr Pesan pribadi yang belum dibaca - Alihkan bookmark Diteleportasi Status Tor Peer yang terhubung diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 8c157300..78cc8b84 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -75,7 +75,6 @@ esci Raggiungibile via Nostr Messaggi privati non letti - Attiva/disattiva segnalibro Teletrasportato Stato Tor Peer connessi diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index b272e51f..c5b7a5c6 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -75,7 +75,6 @@ 退出 Nostr 経由で到達可能 未読のプライベートメッセージ - ブックマークを切り替え テレポート済み Tor ステータス 接続中のピア diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 65f27848..2c682575 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -75,7 +75,6 @@ გასვლა მისაწვდომია Nostr-ით წაუკითხავი პირადი შეტყობინებები - სანიშნეს გადართვა ტელეპორტირებული Tor-ის სტატუსი დაკავშირებული პირები diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index ee2838b6..4cda1af6 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -75,7 +75,6 @@ 나가기 Nostr로 연결 가능 읽지 않은 개인 메시지 - 북마크 전환 텔레포트됨 Tor 상태 연결된 피어 diff --git a/app/src/main/res/values-mg/strings.xml b/app/src/main/res/values-mg/strings.xml index ffec5957..6cff0fe3 100644 --- a/app/src/main/res/values-mg/strings.xml +++ b/app/src/main/res/values-mg/strings.xml @@ -75,7 +75,6 @@ hiala Azo tratrarina amin\'ny alalan\'ny Nostr Hafatra manokana tsy voavaky - Hanova bookmark Nafindra toerana Toe-javatra Tor Mpiara-miasa mifandray diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index 7c2bbf26..f80354db 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -75,7 +75,6 @@ छोड्नुहोस् Nostr मार्फत पुग्न सकिने नपढिएका निजी सन्देश - बुकमार्क टगल टेलिपोर्ट भयो Tor स्थिति जडान भएका साथीहरू diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index d9de3a6d..7ce6dbc9 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -75,7 +75,6 @@ verlaten Bereikbaar via Nostr Ongelezen privéberichten - Bladwijzer omschakelen Geteleporteerd Tor-status Verbonden peers diff --git a/app/src/main/res/values-pa-rPK/strings.xml b/app/src/main/res/values-pa-rPK/strings.xml index f7423ce5..46024ab5 100644 --- a/app/src/main/res/values-pa-rPK/strings.xml +++ b/app/src/main/res/values-pa-rPK/strings.xml @@ -75,7 +75,6 @@ باہر آؤ Nostr راہین پہنچ ممکن اے انہ پڑھیا ذاتی پیغام - بُک مارک ٹوگل کرو ٹیلپورٹ کیتا گیا Tor حالت جُڑے پیئر diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a350d3f7..96043e4f 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -75,7 +75,6 @@ sair Acessível via Nostr Mensagens privadas não lidas - Alternar favorito Teletransportado Status do Tor Peers conectados diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 9751b733..184e4b2a 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -75,7 +75,6 @@ sair Alcançável via Nostr Mensagens privadas não lidas - Alternar marcador Teletransportado Estado do Tor Pares ligados diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5672cd00..4a60c472 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -67,7 +67,6 @@ выйти Доступен через Nostr Непрочитанные личные - Переключить закладку Телепортировано Статус Tor Подключённые узлы diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index a6df7a8e..9eabab8a 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -67,7 +67,6 @@ lämna Nås via Nostr Olästa privata meddelanden - Växla bokmärke Teleporterad Tor‑status Anslutna noder diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index af9111fa..ddb21b5e 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -75,7 +75,6 @@ ออก ติดต่อได้ผ่าน Nostr ข้อความส่วนตัวยังไม่ได้อ่าน - สลับที่คั่นหน้า เทเลพอร์ตแล้ว สถานะ Tor เพียร์ที่เชื่อมต่อ diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 721b50c1..7d64afdf 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -67,7 +67,6 @@ ayrıl Nostr üzerinden ulaşılabilir Okunmamış özel mesaj - Yer imini değiştir Teleport edildi Tor durumu Bağlı eşler diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 0f11e124..60fc378f 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -75,7 +75,6 @@ چھوڑیں Nostr کے ذریعے رسائی غیر پڑھے ہوئے نجی پیغامات - بک مارک تبدیل کریں ٹیلی پورٹ Tor کی کیفیت منسلک پیئرز diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 083a930a..7858dd49 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -75,7 +75,6 @@ rời khỏi Có thể tiếp cận qua Nostr Tin nhắn riêng chưa đọc - Bật/tắt bookmark Đã dịch chuyển Trạng thái Tor Peers đã kết nối diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 75d92b58..21ce5d51 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -75,7 +75,6 @@ 离开 可通过 Nostr 联系 未读私信 - 切换书签 已传送 Tor 状态 已连接节点 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 594d6879..0818a9f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,7 +76,6 @@ leave Nostr reachable Unread private messages - Toggle bookmark Location notes Teleported Tor status @@ -111,8 +110,8 @@ The sidegroup chat - How to use - Basic info + Info + Settings How To Use