diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt index 1da9ef6..3c7a671 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt @@ -73,16 +73,20 @@ fun ChatScreen(id: Long) { val navigator = LocalNavigator.current val viewModel = LocalNostrViewModel.current - val listState = rememberLazyListState() - val chatRooms by viewModel.chatRooms.collectAsState() - val room = remember(chatRooms, id) { chatRooms.firstOrNull { it.id == id } } + // Get chat room by ID + val room = viewModel.getChatRoom(id) + // Show empty screen if (room == null) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - LoadingIndicator() + Text( + text = "Chat room not found", + style = MaterialTheme.typography.titleMediumEmphasized, + color = MaterialTheme.colorScheme.onSurface + ) } return } @@ -94,7 +98,9 @@ fun ChatScreen(id: Long) { var loading by remember { mutableStateOf(true) } var newOtherMessages by remember { mutableIntStateOf(0) } + val listState = rememberLazyListState() val messages = remember { mutableStateListOf() } + val groupedMessages = remember(messages.toList()) { messages.groupBy { it.createdAt().formatAsGroupHeader() } } 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 1204079..3c85ade 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt @@ -76,6 +76,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.compose.LifecycleResumeEffect +import androidx.lifecycle.compose.collectAsStateWithLifecycle import coop.composeapp.generated.resources.Res import coop.composeapp.generated.resources.ic_new_chat import coop.composeapp.generated.resources.ic_qr @@ -108,8 +109,8 @@ fun HomeScreen() { val currentUser = viewModel.currentUser() ?: return val currentUserProfile = viewModel.getMetadata(currentUser) ?: return - val userProfile by currentUserProfile.collectAsState(initial = null) - val chatRooms by viewModel.chatRooms.collectAsState(initial = emptyList()) + val userProfile by currentUserProfile.collectAsStateWithLifecycle() + val chatRooms by viewModel.chatRooms.collectAsStateWithLifecycle() val isPartialProcessedGiftWrap by viewModel.isPartialProcessedGiftWrap.collectAsState(initial = false) val isBannerDismissed by viewModel.isNotificationBannerDismissed.collectAsState() diff --git a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt index 6a6afd1..e2b850a 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.viewModelScope import io.ktor.client.HttpClient import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.serialization.kotlinx.json.json -import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow @@ -16,7 +15,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull import kotlinx.serialization.json.Json import rust.nostr.sdk.AsyncNostrSigner @@ -102,12 +100,7 @@ class NostrViewModel( override fun onCleared() { super.onCleared() - // Ensure all relays are disconnect - viewModelScope.launch { - withContext(NonCancellable) { - nostr.disconnect() - } - } + // TODO: optimize relay connection } private fun showError(message: String) { @@ -516,9 +509,8 @@ class NostrViewModel( } } - fun getChatRoom(id: Long): Room { + fun getChatRoom(id: Long): Room? { return chatRooms.value.firstOrNull { it.id == id } - ?: throw IllegalArgumentException("Room not found") } private fun mergeChatRooms(rooms: Set) { @@ -560,14 +552,19 @@ class NostrViewModel( } suspend fun chatRoomConnect(roomId: Long): Map> { - val room = getChatRoom(roomId) - val members = room.members + try { + val room = getChatRoom(roomId) ?: throw IllegalArgumentException("Room not found") + val members = room.members - return runCatching { - nostr.chatRoomConnect(members.toList()) - }.getOrElse { e -> + return runCatching { + nostr.chatRoomConnect(members.toList()) + }.getOrElse { e -> + showError("Error: ${e.message}") + members.associateWith { emptyList() } + } + } catch (e: Exception) { showError("Error: ${e.message}") - members.associateWith { emptyList() } + return emptyMap() } } @@ -577,7 +574,7 @@ class NostrViewModel( } viewModelScope.launch { try { - val room = getChatRoom(roomId) + val room = getChatRoom(roomId) ?: throw IllegalArgumentException("Room not found") nostr.sendMessage( to = room.members, content = message,