chore: merge the develop branch into master #1
@@ -15,9 +15,11 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.ListItem
|
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.Res
|
||||||
import coop.composeapp.generated.resources.ic_new_chat
|
import coop.composeapp.generated.resources.ic_new_chat
|
||||||
import coop.composeapp.generated.resources.ic_scanner
|
import coop.composeapp.generated.resources.ic_scanner
|
||||||
import coop.composeapp.generated.resources.ic_search
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jetbrains.compose.resources.painterResource
|
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.LocalNostrViewModel
|
||||||
import su.reya.coop.LocalSnackbarHostState
|
import su.reya.coop.LocalSnackbarHostState
|
||||||
import su.reya.coop.Room
|
import su.reya.coop.Room
|
||||||
|
import su.reya.coop.Screen
|
||||||
import su.reya.coop.ago
|
import su.reya.coop.ago
|
||||||
import su.reya.coop.shared.Avatar
|
import su.reya.coop.shared.Avatar
|
||||||
import su.reya.coop.shared.displayNameFlow
|
import su.reya.coop.shared.displayNameFlow
|
||||||
@@ -80,6 +84,7 @@ fun HomeScreen(
|
|||||||
onNewChat: () -> Unit,
|
onNewChat: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val clipboard = LocalClipboard.current
|
val clipboard = LocalClipboard.current
|
||||||
|
val navController = LocalNavController.current
|
||||||
val snackbarHostState = LocalSnackbarHostState.current
|
val snackbarHostState = LocalSnackbarHostState.current
|
||||||
val viewModel = LocalNostrViewModel.current
|
val viewModel = LocalNostrViewModel.current
|
||||||
|
|
||||||
@@ -97,10 +102,30 @@ fun HomeScreen(
|
|||||||
var showBottomSheet by remember { mutableStateOf(false) }
|
var showBottomSheet by remember { mutableStateOf(false) }
|
||||||
var isRefreshing by remember { mutableStateOf(false) }
|
var isRefreshing by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle
|
||||||
|
val qrResult by savedStateHandle
|
||||||
|
?.getStateFlow<String?>("qr_result", null)
|
||||||
|
?.collectAsState()
|
||||||
|
?: remember { mutableStateOf(null) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.getChatRooms()
|
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<String>("qr_result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
@@ -116,15 +141,8 @@ fun HomeScreen(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
// Search
|
|
||||||
IconButton(onClick = { /* TODO: Open search */ }) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(Res.drawable.ic_search),
|
|
||||||
contentDescription = "Search"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// QR Scanner
|
// QR Scanner
|
||||||
IconButton(onClick = { /* TODO: Open search */ }) {
|
IconButton(onClick = { navController.navigate(Screen.Scan) }) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(Res.drawable.ic_scanner),
|
painter = painterResource(Res.drawable.ic_scanner),
|
||||||
contentDescription = "Scanner"
|
contentDescription = "Scanner"
|
||||||
@@ -353,6 +371,12 @@ val defaultMenuList = listOf(
|
|||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomMenuList() {
|
fun BottomMenuList() {
|
||||||
|
val viewModel = LocalNostrViewModel.current
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
|
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
|
||||||
@@ -369,4 +393,15 @@ fun BottomMenuList() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = { viewModel.logout() },
|
||||||
|
colors = ButtonDefaults.filledTonalButtonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.error,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onError
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(text = "Logout")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,9 @@ fun NewChatScreen(
|
|||||||
var query by remember { mutableStateOf("") }
|
var query by remember { mutableStateOf("") }
|
||||||
|
|
||||||
val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle
|
val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle
|
||||||
val qrResult by savedStateHandle?.getStateFlow<String?>("qr_result", null)?.collectAsState()
|
val qrResult by savedStateHandle
|
||||||
|
?.getStateFlow<String?>("qr_result", null)
|
||||||
|
?.collectAsState()
|
||||||
?: remember { mutableStateOf(null) }
|
?: remember { mutableStateOf(null) }
|
||||||
|
|
||||||
LaunchedEffect(query) {
|
LaunchedEffect(query) {
|
||||||
@@ -88,6 +90,7 @@ fun NewChatScreen(
|
|||||||
val pubkey = try {
|
val pubkey = try {
|
||||||
PublicKey.parse(query)
|
PublicKey.parse(query)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
println("Failed to parse npub: ${e.message}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
if (pubkey != null) {
|
if (pubkey != null) {
|
||||||
@@ -109,8 +112,11 @@ fun NewChatScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(qrResult) {
|
LaunchedEffect(qrResult) {
|
||||||
qrResult?.let {
|
qrResult?.let { result ->
|
||||||
println("QR result: $it")
|
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<String>("qr_result")
|
navController.currentBackStackEntry?.savedStateHandle?.remove<String>("qr_result")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ fun ScanScreen(
|
|||||||
val snackbarHostState = LocalSnackbarHostState.current
|
val snackbarHostState = LocalSnackbarHostState.current
|
||||||
|
|
||||||
val onResult: (String) -> Unit = { result ->
|
val onResult: (String) -> Unit = { result ->
|
||||||
navController.previousBackStackEntry
|
navController.previousBackStackEntry?.savedStateHandle?.set("qr_result", result)
|
||||||
?.savedStateHandle
|
|
||||||
?.set("qr_result", result)
|
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,6 +219,14 @@ class NostrViewModel(
|
|||||||
return nostr.signer.currentUser
|
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 {
|
private suspend fun getOrInitAppKeys(): Keys {
|
||||||
val secret = secretStore.get("app_keys")
|
val secret = secretStore.get("app_keys")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user