From 26520991cfb6f499d7ccb16518011f11ffea9a92 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sat, 23 Aug 2025 12:04:21 +0200 Subject: [PATCH] New about sheet (#294) * about sheet * version * about page --- .../java/com/bitchat/android/ui/AboutSheet.kt | 368 ++++++++++++++++++ .../java/com/bitchat/android/ui/ChatScreen.kt | 8 +- .../bitchat/android/ui/DialogComponents.kt | 126 ------ 3 files changed, 372 insertions(+), 130 deletions(-) create mode 100644 app/src/main/java/com/bitchat/android/ui/AboutSheet.kt delete mode 100644 app/src/main/java/com/bitchat/android/ui/DialogComponents.kt diff --git a/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt b/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt new file mode 100644 index 00000000..00102727 --- /dev/null +++ b/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt @@ -0,0 +1,368 @@ +package com.bitchat.android.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +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.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.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.BaselineShift +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +/** + * About Sheet for bitchat app information + * Matches the design language of LocationChannelsSheet + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AboutSheet( + isPresented: Boolean, + onDismiss: () -> Unit, + modifier: Modifier = Modifier +) { + val context = LocalContext.current + + // Get version name from package info + val versionName = remember { + try { + context.packageManager.getPackageInfo(context.packageName, 0).versionName + } catch (e: Exception) { + "1.0.0" // fallback version + } + } + + // Bottom sheet state + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = false + ) + + // Color scheme matching LocationChannelsSheet + val colorScheme = MaterialTheme.colorScheme + val isDark = colorScheme.background.red + colorScheme.background.green + colorScheme.background.blue < 1.5f + val standardBlue = Color(0xFF007AFF) // iOS blue + val standardGreen = if (isDark) Color(0xFF32D74B) else Color(0xFF248A3D) // iOS green + + if (isPresented) { + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + modifier = modifier + ) { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + .padding(bottom = 24.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Header + item { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.Bottom + ) { + Text( + text = "bitchat", + fontSize = 18.sp, + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + color = colorScheme.onSurface + ) + + Text( + text = "v$versionName", + fontSize = 11.sp, + fontFamily = FontFamily.Monospace, + color = colorScheme.onSurface.copy(alpha = 0.5f), + style = MaterialTheme.typography.bodySmall.copy( + baselineShift = BaselineShift(0.1f) + ) + ) + } + + Text( + text = "decentralized mesh messaging with end-to-end encryption", + fontSize = 12.sp, + fontFamily = FontFamily.Monospace, + color = colorScheme.onSurface.copy(alpha = 0.7f) + ) + } + } + + // Features section + item { + Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + FeatureCard( + icon = Icons.Filled.Bluetooth, + iconColor = standardBlue, + title = "offline mesh chat", + description = "communicate directly via bluetooth le without internet or servers. messages relay through nearby devices to extend range.", + modifier = Modifier.fillMaxWidth() + ) + + FeatureCard( + icon = Icons.Filled.Public, + iconColor = standardGreen, + title = "geohash channels", + description = "internet-based location channels using coarse geohash coordinates. connect with people in your area while preserving privacy.", + modifier = Modifier.fillMaxWidth() + ) + + FeatureCard( + icon = Icons.Filled.Lock, + iconColor = if (isDark) Color(0xFFFFD60A) else Color(0xFFF5A623), + title = "end-to-end encryption", + description = "all direct messages use noise protocol encryption. channel messages are protected with optional passwords.", + modifier = Modifier.fillMaxWidth() + ) + } + } + + // Additional features + item { + Surface( + modifier = Modifier.fillMaxWidth(), + color = colorScheme.surfaceVariant.copy(alpha = 0.3f), + shape = RoundedCornerShape(12.dp) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "additional features", + fontSize = 12.sp, + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + color = colorScheme.onSurface.copy(alpha = 0.8f) + ) + + FeatureItem("store-and-forward messaging for offline peers") + FeatureItem("ephemeral messaging with automatic cleanup") + FeatureItem("peer discovery and identity verification") + FeatureItem("minimal metadata leakage") + } + } + } + + // Emergency warning + item { + Surface( + modifier = Modifier.fillMaxWidth(), + color = Color.Red.copy(alpha = 0.08f), + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier.padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.Top + ) { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = "Warning", + tint = Color(0xFFBF1A1A), + modifier = Modifier.size(16.dp) + ) + + Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { + Text( + text = "emergency data deletion", + fontSize = 12.sp, + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + color = Color(0xFFBF1A1A) + ) + + Text( + text = "tip: triple-click the app title to emergency delete all stored data including messages, keys, and settings.", + fontSize = 11.sp, + fontFamily = FontFamily.Monospace, + color = colorScheme.onSurface.copy(alpha = 0.7f) + ) + } + } + } + } + + // Version and footer space + item { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "open source • privacy first • decentralized", + fontSize = 10.sp, + fontFamily = FontFamily.Monospace, + color = colorScheme.onSurface.copy(alpha = 0.5f) + ) + + // Add extra space at bottom for gesture area + Spacer(modifier = Modifier.height(16.dp)) + } + } + } + } + } +} + +@Composable +private fun FeatureCard( + icon: ImageVector, + iconColor: Color, + title: String, + description: String, + modifier: Modifier = Modifier +) { + Surface( + modifier = modifier, + color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.2f), + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier.padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.Top + ) { + Icon( + imageVector = icon, + contentDescription = title, + tint = iconColor, + modifier = Modifier.size(20.dp) + ) + + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = title, + fontSize = 13.sp, + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface + ) + + Text( + text = description, + fontSize = 11.sp, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f), + lineHeight = 15.sp + ) + } + } + } +} + +@Composable +private fun FeatureItem(text: String) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.Top + ) { + Text( + text = "•", + fontSize = 11.sp, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) + ) + + Text( + text = text, + fontSize = 11.sp, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f), + modifier = Modifier.weight(1f) + ) + } +} + +/** + * Password prompt dialog for password-protected channels + * Kept as dialog since it requires user input + */ +@Composable +fun PasswordPromptDialog( + show: Boolean, + channelName: String?, + passwordInput: String, + onPasswordChange: (String) -> Unit, + onConfirm: () -> Unit, + onDismiss: () -> Unit +) { + if (show && channelName != null) { + val colorScheme = MaterialTheme.colorScheme + + AlertDialog( + onDismissRequest = onDismiss, + title = { + Text( + text = "Enter Channel Password", + style = MaterialTheme.typography.titleMedium, + color = colorScheme.onSurface + ) + }, + text = { + Column { + Text( + text = "Channel $channelName is password protected. Enter the password to join.", + style = MaterialTheme.typography.bodyMedium, + color = colorScheme.onSurface + ) + Spacer(modifier = Modifier.height(8.dp)) + + OutlinedTextField( + value = passwordInput, + onValueChange = onPasswordChange, + label = { Text("Password", style = MaterialTheme.typography.bodyMedium) }, + textStyle = MaterialTheme.typography.bodyMedium.copy( + fontFamily = FontFamily.Monospace + ), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = colorScheme.primary, + unfocusedBorderColor = colorScheme.outline + ) + ) + } + }, + confirmButton = { + TextButton(onClick = onConfirm) { + Text( + text = "Join", + style = MaterialTheme.typography.bodyMedium, + color = colorScheme.primary + ) + } + }, + dismissButton = { + TextButton(onClick = onDismiss) { + Text( + text = "Cancel", + style = MaterialTheme.typography.bodyMedium, + color = colorScheme.onSurface + ) + } + }, + containerColor = colorScheme.surface, + tonalElevation = 8.dp + ) + } +} 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 76d6f17d..7d30da1f 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.zIndex * - MessageComponents: Message display and formatting * - InputComponents: Message input and command suggestions * - SidebarComponents: Navigation drawer with channels and people - * - DialogComponents: Password prompts and modals + * - AboutSheet: App info and password prompts * - ChatUIUtils: Utility functions for formatting and colors */ @Composable @@ -399,9 +399,9 @@ private fun ChatDialogs( onDismiss = onPasswordDismiss ) - // App info dialog - AppInfoDialog( - show = showAppInfo, + // About sheet + AboutSheet( + isPresented = showAppInfo, onDismiss = onAppInfoDismiss ) diff --git a/app/src/main/java/com/bitchat/android/ui/DialogComponents.kt b/app/src/main/java/com/bitchat/android/ui/DialogComponents.kt deleted file mode 100644 index 05309d20..00000000 --- a/app/src/main/java/com/bitchat/android/ui/DialogComponents.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.bitchat.android.ui - -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.unit.dp - -/** - * Dialog components for ChatScreen - * Extracted from ChatScreen.kt for better organization - */ - -@Composable -fun PasswordPromptDialog( - show: Boolean, - channelName: String?, - passwordInput: String, - onPasswordChange: (String) -> Unit, - onConfirm: () -> Unit, - onDismiss: () -> Unit -) { - if (show && channelName != null) { - val colorScheme = MaterialTheme.colorScheme - - AlertDialog( - onDismissRequest = onDismiss, - title = { - Text( - text = "Enter Channel Password", - style = MaterialTheme.typography.titleMedium, - color = colorScheme.onSurface - ) - }, - text = { - Column { - Text( - text = "Channel $channelName is password protected. Enter the password to join.", - style = MaterialTheme.typography.bodyMedium, - color = colorScheme.onSurface - ) - Spacer(modifier = Modifier.height(8.dp)) - - OutlinedTextField( - value = passwordInput, - onValueChange = onPasswordChange, - label = { Text("Password", style = MaterialTheme.typography.bodyMedium) }, - textStyle = MaterialTheme.typography.bodyMedium.copy( - fontFamily = FontFamily.Monospace - ), - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = colorScheme.primary, - unfocusedBorderColor = colorScheme.outline - ) - ) - } - }, - confirmButton = { - TextButton(onClick = onConfirm) { - Text( - text = "Join", - style = MaterialTheme.typography.bodyMedium, - color = colorScheme.primary - ) - } - }, - dismissButton = { - TextButton(onClick = onDismiss) { - Text( - text = "Cancel", - style = MaterialTheme.typography.bodyMedium, - color = colorScheme.onSurface - ) - } - }, - containerColor = colorScheme.surface, - tonalElevation = 8.dp - ) - } -} - -@Composable -fun AppInfoDialog( - show: Boolean, - onDismiss: () -> Unit -) { - if (show) { - val colorScheme = MaterialTheme.colorScheme - - AlertDialog( - onDismissRequest = onDismiss, - title = { - Text( - text = "About bitchat", - style = MaterialTheme.typography.titleMedium, - color = colorScheme.onSurface - ) - }, - text = { - Text( - text = "Decentralized mesh messaging over Bluetooth LE\n\n" + - "• No servers or internet required\n" + - "• End-to-end encrypted private messages\n" + - "• Password-protected channels\n" + - "• Store-and-forward for offline peers\n\n" + - "Triple-click title to emergency clear all data", - style = MaterialTheme.typography.bodyMedium, - color = colorScheme.onSurface - ) - }, - confirmButton = { - TextButton(onClick = onDismiss) { - Text( - text = "OK", - style = MaterialTheme.typography.bodyMedium, - color = colorScheme.primary - ) - } - }, - containerColor = colorScheme.surface, - tonalElevation = 8.dp - ) - } -}