chore: fix app crash when create new room via QR #3

Merged
reya merged 2 commits from fix/crash-on-new-chat into master 2026-05-24 01:30:48 +00:00
4 changed files with 45 additions and 22 deletions

View File

@@ -68,8 +68,19 @@ fun ChatScreen(
val snackbarHostState = LocalSnackbarHostState.current val snackbarHostState = LocalSnackbarHostState.current
val viewModel = LocalNostrViewModel.current val viewModel = LocalNostrViewModel.current
val room = viewModel.getChatRoom(id)
val listState = rememberLazyListState() val listState = rememberLazyListState()
val chatRooms by viewModel.chatRooms.collectAsState()
val room = remember(chatRooms, id) { chatRooms.firstOrNull { it.id == id } }
if (room == null) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
LoadingIndicator()
}
return
}
val displayName by remember(room) { room.displayNameFlow(viewModel) }.collectAsState("Loading...") val displayName by remember(room) { room.displayNameFlow(viewModel) }.collectAsState("Loading...")
val picture by remember(room) { room.pictureFlow(viewModel) }.collectAsState(null) val picture by remember(room) { room.pictureFlow(viewModel) }.collectAsState(null)

View File

@@ -58,7 +58,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coop.composeapp.generated.resources.Res import coop.composeapp.generated.resources.Res
@@ -85,7 +84,6 @@ fun HomeScreen(
onOpenChat: (Long) -> Unit, onOpenChat: (Long) -> Unit,
onNewChat: () -> Unit, onNewChat: () -> Unit,
) { ) {
val clipboard = LocalClipboard.current
val navController = LocalNavController.current val navController = LocalNavController.current
val snackbarHostState = LocalSnackbarHostState.current val snackbarHostState = LocalSnackbarHostState.current
val viewModel = LocalNostrViewModel.current val viewModel = LocalNostrViewModel.current
@@ -112,15 +110,21 @@ fun HomeScreen(
?: remember { mutableStateOf(null) } ?: remember { mutableStateOf(null) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.getChatRooms() if (qrResult == null) {
viewModel.getChatRooms()
}
} }
LaunchedEffect(qrResult) { LaunchedEffect(qrResult) {
qrResult?.let { result -> qrResult?.let { result ->
runCatching { PublicKey.parse(result) } runCatching { PublicKey.parse(result) }
.onSuccess { pubkey -> .onSuccess { pubkey ->
val roomId = viewModel.createChatRoom(listOf(pubkey)) try {
navController.navigate(Screen.Chat(roomId)) val roomId = viewModel.createChatRoom(listOf(pubkey))
navController.navigate(Screen.Chat(roomId))
} catch (e: Exception) {
e.message?.let { snackbarHostState.showSnackbar(it) }
}
} }
.onFailure { e -> println("Failed to parse QR: ${e.message}") } .onFailure { e -> println("Failed to parse QR: ${e.message}") }

View File

@@ -76,7 +76,6 @@ fun ScanScreen(
ScannerWithPermissions( ScannerWithPermissions(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
onScanned = { onScanned = {
println("Scanned: $it");
onResult(it) onResult(it)
true true
}, },

View File

@@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
@@ -406,20 +407,26 @@ class NostrViewModel(
} }
fun createChatRoom(to: List<PublicKey>): Long { fun createChatRoom(to: List<PublicKey>): Long {
if (nostr.signer.currentUser == null) throw IllegalStateException("User not signed in") try {
if (to.isEmpty()) throw IllegalArgumentException("At least one recipient is required") if (nostr.signer.currentUser == null) throw IllegalStateException("User not signed in")
if (to.isEmpty()) throw IllegalArgumentException("At least one recipient is required")
// Construct the rumor event // Construct the rumor event
val rumor = EventBuilder val rumor = EventBuilder
.privateMsgRumor(to.first(), "") .privateMsgRumor(to.first(), "")
.tags(to.map { Tag.publicKey(it) }) .tags(to.map { Tag.publicKey(it) })
.build(nostr.signer.currentUser!!) .build(nostr.signer.currentUser!!)
// Create a room from the rumor event // Create a room from the rumor event
val room = Room.new(rumor, nostr.signer.currentUser!!) val room = Room.new(rumor, nostr.signer.currentUser!!)
_chatRooms.value += room _chatRooms.update { currentRooms ->
currentRooms + room
}
return room.id return room.id
} catch (e: Exception) {
throw IllegalArgumentException("Failed to create room: ${e.message}")
}
} }
fun getChatRoom(id: Long): Room { fun getChatRoom(id: Long): Room {
@@ -429,10 +436,12 @@ class NostrViewModel(
fun getChatRooms() { fun getChatRooms() {
viewModelScope.launch { viewModelScope.launch {
try { val rooms = nostr.getChatRooms() ?: emptySet()
_chatRooms.value = nostr.getChatRooms() ?: emptySet() _chatRooms.update { currentRooms ->
} catch (e: Exception) { val virtualRooms = currentRooms.filter { local ->
showError("Error: ${e.message}") rooms.none { db -> db.id == local.id }
}
rooms + virtualRooms
} }
} }
} }