From 0104cf453db637cc3b924b02c1d993df66a3d62b Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Tue, 19 May 2026 09:53:41 +0700 Subject: [PATCH] update scan qr screen --- .../kotlin/su/reya/coop/screens/HomeScreen.kt | 73 ++++++++++++++----- .../su/reya/coop/screens/NewChatScreen.kt | 12 ++- .../kotlin/su/reya/coop/screens/ScanScreen.kt | 4 +- .../kotlin/su/reya/coop/NostrViewModel.kt | 8 ++ 4 files changed, 72 insertions(+), 25 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt index 4f0ae20..a3ca0f0 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt @@ -15,9 +15,11 @@ 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.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem @@ -61,12 +63,14 @@ import androidx.compose.ui.unit.dp import coop.composeapp.generated.resources.Res import coop.composeapp.generated.resources.ic_new_chat import coop.composeapp.generated.resources.ic_scanner -import coop.composeapp.generated.resources.ic_search import kotlinx.coroutines.launch import org.jetbrains.compose.resources.painterResource +import rust.nostr.sdk.PublicKey +import su.reya.coop.LocalNavController import su.reya.coop.LocalNostrViewModel import su.reya.coop.LocalSnackbarHostState import su.reya.coop.Room +import su.reya.coop.Screen import su.reya.coop.ago import su.reya.coop.shared.Avatar import su.reya.coop.shared.displayNameFlow @@ -80,6 +84,7 @@ fun HomeScreen( onNewChat: () -> Unit, ) { val clipboard = LocalClipboard.current + val navController = LocalNavController.current val snackbarHostState = LocalSnackbarHostState.current val viewModel = LocalNostrViewModel.current @@ -97,10 +102,30 @@ fun HomeScreen( var showBottomSheet by remember { mutableStateOf(false) } var isRefreshing by remember { mutableStateOf(false) } + val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle + val qrResult by savedStateHandle + ?.getStateFlow("qr_result", null) + ?.collectAsState() + ?: remember { mutableStateOf(null) } + LaunchedEffect(Unit) { viewModel.getChatRooms() } + LaunchedEffect(qrResult) { + qrResult?.let { result -> + runCatching { PublicKey.parse(result) } + .onSuccess { pubkey -> + val roomId = viewModel.createChatRoom(listOf(pubkey)) + navController.navigate(Screen.Chat(roomId)) + } + .onFailure { e -> println("Failed to parse QR: ${e.message}") } + + // Clear the nav state + navController.currentBackStackEntry?.savedStateHandle?.remove("qr_result") + } + } + Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) }, containerColor = MaterialTheme.colorScheme.surfaceContainer, @@ -116,15 +141,8 @@ fun HomeScreen( ) }, actions = { - // Search - IconButton(onClick = { /* TODO: Open search */ }) { - Icon( - painter = painterResource(Res.drawable.ic_search), - contentDescription = "Search" - ) - } // QR Scanner - IconButton(onClick = { /* TODO: Open search */ }) { + IconButton(onClick = { navController.navigate(Screen.Scan) }) { Icon( painter = painterResource(Res.drawable.ic_scanner), contentDescription = "Scanner" @@ -353,20 +371,37 @@ val defaultMenuList = listOf( @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun BottomMenuList() { + val viewModel = LocalNostrViewModel.current + Column( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + horizontalAlignment = Alignment.CenterHorizontally, ) { - defaultMenuList.forEachIndexed { index, item -> - SegmentedListItem( - checked = false, - onCheckedChange = { }, - shapes = ListItemDefaults.segmentedShapes( - index = index, - count = defaultMenuList.size - ), - content = { Text(text = item) }, + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { + defaultMenuList.forEachIndexed { index, item -> + SegmentedListItem( + checked = false, + onCheckedChange = { }, + shapes = ListItemDefaults.segmentedShapes( + index = index, + count = defaultMenuList.size + ), + content = { Text(text = item) }, + ) + } + } + Spacer(modifier = Modifier.size(16.dp)) + FilledTonalButton( + onClick = { viewModel.logout() }, + colors = ButtonDefaults.filledTonalButtonColors( + containerColor = MaterialTheme.colorScheme.error, + contentColor = MaterialTheme.colorScheme.onError ) + ) { + Text(text = "Logout") } } } diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewChatScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewChatScreen.kt index bdf3307..f669836 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewChatScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewChatScreen.kt @@ -77,7 +77,9 @@ fun NewChatScreen( var query by remember { mutableStateOf("") } val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle - val qrResult by savedStateHandle?.getStateFlow("qr_result", null)?.collectAsState() + val qrResult by savedStateHandle + ?.getStateFlow("qr_result", null) + ?.collectAsState() ?: remember { mutableStateOf(null) } LaunchedEffect(query) { @@ -88,6 +90,7 @@ fun NewChatScreen( val pubkey = try { PublicKey.parse(query) } catch (e: Exception) { + println("Failed to parse npub: ${e.message}") null } if (pubkey != null) { @@ -109,8 +112,11 @@ fun NewChatScreen( } LaunchedEffect(qrResult) { - qrResult?.let { - println("QR result: $it") + qrResult?.let { result -> + runCatching { PublicKey.parse(result) } + .onSuccess { pubkey -> selectedReceivers.add(pubkey) } + .onFailure { e -> println("Failed to parse QR: ${e.message}") } + // Clear the nav state navController.currentBackStackEntry?.savedStateHandle?.remove("qr_result") } } diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ScanScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ScanScreen.kt index 3aa05ab..1386c22 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ScanScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ScanScreen.kt @@ -42,9 +42,7 @@ fun ScanScreen( val snackbarHostState = LocalSnackbarHostState.current val onResult: (String) -> Unit = { result -> - navController.previousBackStackEntry - ?.savedStateHandle - ?.set("qr_result", result) + navController.previousBackStackEntry?.savedStateHandle?.set("qr_result", result) navController.popBackStack() } diff --git a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt index 0ae1fbc..4dd2ff1 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt @@ -219,6 +219,14 @@ class NostrViewModel( return nostr.signer.currentUser } + fun logout() { + viewModelScope.launch { + secretStore.clear("user_signer") + nostr.signer.switch(Keys.generate()) + _emptySecret.value = true + } + } + private suspend fun getOrInitAppKeys(): Keys { val secret = secretStore.get("app_keys")