diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ParticipantDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ParticipantDao.kt index cc6e8b0d..7d86ee67 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ParticipantDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ParticipantDao.kt @@ -3,16 +3,17 @@ package at.torch.compose.database.dao import androidx.room3.Dao import androidx.room3.Query import androidx.room3.Upsert +import at.torch.compose.database.model.Participant @Dao interface ParticipantDao { @Query("SELECT * FROM Participant WHERE participantPublicKey = :participantPublicKey") - fun findParticipantByPublicKey(participantPublicKey: String): at.torch.compose.database.model.Participant? + fun findParticipantByPublicKey(participantPublicKey: String): List @Query("SELECT * FROM Participant WHERE chatRoomId = :chatRoomId") - fun findParticipantsByChatRoomId(chatRoomId: String): List + fun findParticipantsByChatRoomId(chatRoomId: String): List @Upsert - suspend fun upsert(participants: List) + suspend fun upsert(participants: List) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt index 91c109f3..ecf41b55 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt @@ -3,6 +3,7 @@ package at.torch.compose.database.repository import at.torch.compose.database.model.ChatMessage import at.torch.compose.database.model.GiftWrapPayload import at.torch.compose.database.model.MarmotInnerEvent +import at.torch.compose.database.model.Participant import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.marmot.mls.group.MlsGroup import com.vitorpamplona.quartz.marmot.mls.group.MlsGroupState @@ -38,10 +39,13 @@ class DatabaseChatRepository( return database.chatRoomDao().getChatRoomListByUserPublicKey(publicKey) } - override suspend fun getChatRoomByIdentifier(id: String): at.torch.compose.database.model.intermdiate.LocalChatRoom? { + override suspend fun getChatRoomByIdentifier(id: HexKey): at.torch.compose.database.model.intermdiate.LocalChatRoom? { return database.chatRoomDao().findChatRoomById(id) } + override suspend fun getAllParticipantsWithPubKey(publicKey: HexKey): List { + return database.participantDao().findParticipantByPublicKey(publicKey) + } override suspend fun getChatMessageListByChatRoomId(chatRoomId: String): List { return database.chatMessageDao().getChatMessagesByChatRoomId(chatRoomId) } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt index 5a51d46b..83b948f3 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt @@ -1,5 +1,8 @@ package at.torch.compose.repository +import at.torch.compose.database.model.Participant +import at.torch.compose.database.model.intermdiate.LocalChatMessage +import at.torch.compose.database.model.intermdiate.LocalChatRoom import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync @@ -9,15 +12,17 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow interface ChatRepository { - suspend fun observeChatRoomListByPublicKey(publicKey: String): Flow> + suspend fun observeChatRoomListByPublicKey(publicKey: String): Flow> - suspend fun getChatRoomListByPublicKey(publicKey: String): List + suspend fun getChatRoomListByPublicKey(publicKey: String): List - suspend fun getChatRoomByIdentifier(id: String): at.torch.compose.database.model.intermdiate.LocalChatRoom? + suspend fun getChatRoomByIdentifier(id: HexKey): LocalChatRoom? - suspend fun getChatMessageListByChatRoomId(chatRoomId: String): List + suspend fun getAllParticipantsWithPubKey(publicKey: HexKey): List - suspend fun observeChatMessageListByChatRoomId(chatRoomId: String): Flow> + suspend fun getChatMessageListByChatRoomId(chatRoomId: String): List + + suspend fun observeChatMessageListByChatRoomId(chatRoomId: String): Flow> suspend fun getOrCreateChatRoom( chatRoomId: String, @@ -25,13 +30,13 @@ interface ChatRepository { relayHint: String?, defaultSubject: String?, mlsGroupState: String? - ): at.torch.compose.database.model.intermdiate.LocalChatRoom? + ): LocalChatRoom? suspend fun getChatMessageRelayForPublicKey(publicKey: HexKey): ChatMessageRelayListEvent? suspend fun sendChatMessage( text: String, - localChatRoom: at.torch.compose.database.model.intermdiate.LocalChatRoom, + localChatRoom: LocalChatRoom, messageType: Kind = ChatMessageEvent.KIND ) @@ -43,23 +48,27 @@ interface ChatRepository { companion object { val NO_OP_CHAT_REPOSITORY = object: ChatRepository { - override suspend fun observeChatRoomListByPublicKey(publicKey: String): Flow> { + override suspend fun observeChatRoomListByPublicKey(publicKey: String): Flow> { return flow { } } - override suspend fun getChatRoomListByPublicKey(publicKey: String): List { + override suspend fun getChatRoomListByPublicKey(publicKey: String): List { return emptyList() } - override suspend fun getChatRoomByIdentifier(id: String): at.torch.compose.database.model.intermdiate.LocalChatRoom? { + override suspend fun getChatRoomByIdentifier(id: HexKey): LocalChatRoom? { return null } - override suspend fun getChatMessageListByChatRoomId(chatRoomId: String): List { + override suspend fun getAllParticipantsWithPubKey(publicKey: HexKey): List { + TODO("Not yet implemented") + } + + override suspend fun getChatMessageListByChatRoomId(chatRoomId: String): List { return emptyList() } - override suspend fun observeChatMessageListByChatRoomId(chatRoomId: String): Flow> { + override suspend fun observeChatMessageListByChatRoomId(chatRoomId: String): Flow> { return flow { } } @@ -70,7 +79,7 @@ interface ChatRepository { relayHint: String?, defaultSubject: String?, mlsGroupState: String? - ): at.torch.compose.database.model.intermdiate.LocalChatRoom? { + ): LocalChatRoom? { return null } @@ -80,7 +89,7 @@ interface ChatRepository { override suspend fun sendChatMessage( text: String, - localChatRoom: at.torch.compose.database.model.intermdiate.LocalChatRoom, + localChatRoom: LocalChatRoom, messageType: Kind ) { // TODO("Not yet implemented") diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt index 30d5d031..5bd9816a 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt @@ -37,7 +37,12 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import at.torch.compose.repository.ChatRepository +import at.torch.compose.repository.NostrRepository +import at.torch.compose.ui.composable.navigation.routes.Route +import at.torch.compose.ui.composable.widgets.LoadingDataIndicator import at.torch.compose.ui.view.model.ChatRoomDetailViewModel +import at.torch.compose.ui.view.state.ChatRoomDetailUIState import com.vitorpamplona.quartz.nip01Core.core.HexKey @OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) @@ -46,9 +51,10 @@ fun ChatRoomDetailScreen( activeUserPublicKey: HexKey, chatRoomId: String, relayHint: String?, - initialChatRoomDetailUIState: at.torch.compose.ui.view.state.ChatRoomDetailUIState = at.torch.compose.ui.view.state.ChatRoomDetailUIState.Loading, - nostrRepository: at.torch.compose.repository.NostrRepository, - chatRepository: at.torch.compose.repository.ChatRepository + initialChatRoomDetailUIState: ChatRoomDetailUIState = ChatRoomDetailUIState.Loading, + nostrRepository: NostrRepository, + chatRepository: ChatRepository, + onNavigateToChat: (Route) -> Unit, ) { val chatRoomDetailViewModel: ChatRoomDetailViewModel = viewModel( factory = ChatRoomDetailViewModel.factory( @@ -62,7 +68,7 @@ fun ChatRoomDetailScreen( ) when (val chatRoomDetailUIState = chatRoomDetailViewModel.chatRoomDetailUIState) { - at.torch.compose.ui.view.state.ChatRoomDetailUIState.Error -> { + ChatRoomDetailUIState.Error -> { Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally @@ -75,7 +81,7 @@ fun ChatRoomDetailScreen( ) } } - is at.torch.compose.ui.view.state.ChatRoomDetailUIState.Loaded -> { + is ChatRoomDetailUIState.Loaded -> { val textFieldState = rememberTextFieldState() val chatRoomDetailMessageListViewModel: at.torch.compose.ui.view.model.ChatRoomDetailMessageListViewModel = viewModel( @@ -228,7 +234,7 @@ fun ChatRoomDetailScreen( } } } - at.torch.compose.ui.view.state.ChatRoomDetailUIState.Loading -> { + ChatRoomDetailUIState.Loading -> { Column( modifier = Modifier.fillMaxWidth().padding( 20.dp @@ -252,7 +258,7 @@ fun ChatRoomDetailScreen( textAlign = TextAlign.Center ) - _root_ide_package_.at.torch.compose.ui.composable.widgets.LoadingDataIndicator( + LoadingDataIndicator( fillScreen = false ) @@ -261,10 +267,43 @@ fun ChatRoomDetailScreen( ) } } + ChatRoomDetailUIState.InitiatingNewChat -> { + Column( + modifier = Modifier.fillMaxWidth().padding( + 20.dp + ), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + + Spacer( + modifier = Modifier.weight(1f) + ) + Text( + text = "Creating new chat.", + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center + ) + + LoadingDataIndicator( + fillScreen = false + ) + + Spacer( + modifier = Modifier.weight(2f) + ) + + LaunchedEffect(true) { + chatRoomDetailViewModel.initiateNewChat( + onNavigateToChat + ) + } + } + } } LaunchedEffect(true) { - if (initialChatRoomDetailUIState == at.torch.compose.ui.view.state.ChatRoomDetailUIState.Loading) { + if (initialChatRoomDetailUIState == ChatRoomDetailUIState.Loading) { chatRoomDetailViewModel.initiateChatRoomDetail() } } @@ -281,7 +320,7 @@ private fun ChatRoomDetailScreenPreview() { activeUserPublicKey = "", chatRoomId = "publicKey", relayHint = null, - initialChatRoomDetailUIState = at.torch.compose.ui.view.state.ChatRoomDetailUIState.Loaded( + initialChatRoomDetailUIState = ChatRoomDetailUIState.Loaded( localChatRoom = _root_ide_package_.at.torch.compose.database.model.intermdiate.LocalChatRoom( chatRoom = _root_ide_package_.at.torch.compose.database.model.ChatRoom( id = "", @@ -292,8 +331,9 @@ private fun ChatRoomDetailScreenPreview() { ), ) ), - nostrRepository = at.torch.compose.repository.NostrRepository.NO_OP_NOSTR_REPOSITORY, - chatRepository = at.torch.compose.repository.ChatRepository.NO_OP_CHAT_REPOSITORY + nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY, + chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY, + onNavigateToChat = {} ) } } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/NostrEventDetailScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/NostrEventDetailScreen.kt index a8856577..add61a59 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/NostrEventDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/NostrEventDetailScreen.kt @@ -15,6 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import at.torch.compose.ui.composable.navigation.routes.Route +import at.torch.compose.ui.composable.widgets.detail.MetadataEventDetail import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent @@ -28,8 +30,8 @@ fun NostrEventDetailScreen( nostrEventId: HexKey, nostrRepository: at.torch.compose.repository.NostrRepository, onNavigateBack: () -> Unit, - onNavigateToEvent: (at.torch.compose.ui.composable.navigation.routes.Route) -> Unit, - onNavigateToDirectMessage: (at.torch.compose.ui.composable.navigation.routes.Route) -> Unit, + onNavigateToEvent: (Route) -> Unit, + onNavigateToDirectMessage: (Route) -> Unit, onNavigateToEditProfile: () -> Unit, onNavigateToWriteAReply: (String) -> Unit, onNavigateToQuoteNostrEvent: (String) -> Unit, @@ -57,7 +59,7 @@ fun NostrEventDetailScreen( when(feedListUIState.localNostrEvent.nostrEvent.kind) { MetadataEvent.KIND -> { - _root_ide_package_.at.torch.compose.ui.composable.widgets.detail.MetadataEventDetail( + MetadataEventDetail( activeUserPublicKey = activeUserPublicKey, localNostrEvent = feedListUIState.localNostrEvent, onNavigateBack = onNavigateBack, @@ -68,7 +70,7 @@ fun NostrEventDetailScreen( ) } TextNoteEvent.KIND -> { - _root_ide_package_.at.torch.compose.ui.composable.widgets.detail.TextNoteEventDetail( + at.torch.compose.ui.composable.widgets.detail.TextNoteEventDetail( feedListUIState.localNostrEvent, onNavigateBack = onNavigateBack, onNavigateToWriteAReply = onNavigateToWriteAReply, @@ -108,7 +110,7 @@ fun NostrEventDetailScreen( } } at.torch.compose.ui.view.state.NostrEventDetailUIState.Loading -> { - _root_ide_package_.at.torch.compose.ui.composable.widgets.LoadingDataIndicator() + at.torch.compose.ui.composable.widgets.LoadingDataIndicator() LaunchedEffect(true) { nostrEventDetailViewModel.loadNostrFeed() @@ -129,14 +131,14 @@ fun NostrEventDetailScreen( @Preview @Composable private fun NostrEventDetailScreenPreview() { - _root_ide_package_.at.torch.compose.ui.theme.TorchTheme { + at.torch.compose.ui.theme.TorchTheme { Surface( modifier = Modifier.padding(20.dp) ) { NostrEventDetailScreen( initialNostrEventDetailUIState = at.torch.compose.ui.view.state.NostrEventDetailUIState.Loaded( - localNostrEvent = _root_ide_package_.at.torch.compose.database.model.intermdiate.LocalNostrEvent( - nostrEvent = _root_ide_package_.at.torch.compose.database.model.NostrEvent( + localNostrEvent = at.torch.compose.database.model.intermdiate.LocalNostrEvent( + nostrEvent = at.torch.compose.database.model.NostrEvent( id = "eventId", pubKey = "publicKey", content = """Lorem Ipsum is simply dummy text of the printing and typesetting industry. @@ -148,7 +150,7 @@ It has survived not only five centuries, but also the leap into electronic types sig = "", kind = TextNoteEvent.KIND ), - profile = _root_ide_package_.at.torch.compose.database.model.Profile( + profile = at.torch.compose.database.model.Profile( publicKey = "pubKey", displayName = "John Doe", nostrEventId = "nostrEventId" diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/navigation/TorchNavHost.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/navigation/TorchNavHost.kt index 09f92592..2d0c30c8 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/navigation/TorchNavHost.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/navigation/TorchNavHost.kt @@ -23,6 +23,7 @@ import at.torch.compose.ui.composable.ShareProfileScreen import at.torch.compose.ui.composable.UnsyncedProfileScreen import at.torch.compose.ui.composable.navigation.routes.ActiveProfileRoute import at.torch.compose.ui.composable.navigation.routes.ChatRoomCreationRoute +import at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute import at.torch.compose.ui.composable.navigation.routes.KeyPackageManagementRoute import at.torch.compose.ui.composable.navigation.routes.LoadingRoute import at.torch.compose.ui.composable.navigation.routes.NostrEventDetailRoute @@ -510,15 +511,24 @@ fun TorchNavHost( } ) } - composable { backStackEntry -> - val route = backStackEntry.toRoute() + composable { backStackEntry -> + val route = backStackEntry.toRoute() ChatRoomDetailScreen( activeUserPublicKey = route.activeUserPublicKey, chatRoomId = route.chatRoomId, relayHint = route.relayHint, nostrRepository = databaseNostrRepository, - chatRepository = databaseChatRepository + chatRepository = databaseChatRepository, + onNavigateToChat = { chatRoomDetailRoute -> + navController.navigate( + route = chatRoomDetailRoute + ) { + popUpTo(route) { + inclusive = true + } + } + }, ) } composable { backStackEntry -> @@ -598,10 +608,14 @@ fun TorchNavHost( ) ) }, - onNavigateToDirectMessage = { route -> + onNavigateToDirectMessage = { chatRoom -> navController.navigate( - route = route - ) + route = chatRoom + ) { + popUpTo(route) { + inclusive = true + } + } }, ) } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/widgets/detail/MetadataEventDetail.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/widgets/detail/MetadataEventDetail.kt index d6ed1aca..0bb8c63c 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/widgets/detail/MetadataEventDetail.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/widgets/detail/MetadataEventDetail.kt @@ -8,13 +8,16 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBackIosNew +import androidx.compose.material.icons.filled.Block import androidx.compose.material.icons.filled.Bolt import androidx.compose.material.icons.filled.Error +import androidx.compose.material.icons.filled.GroupAdd import androidx.compose.material.icons.filled.Mail import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api @@ -29,6 +32,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable @@ -42,6 +46,8 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute +import at.torch.compose.ui.composable.navigation.routes.ImplementationPendingRoute import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import kotlinx.coroutines.launch @@ -125,7 +131,9 @@ fun MetadataEventDetail( ) { LazyColumn( - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(10.dp) ) { item { Column( @@ -207,24 +215,6 @@ fun MetadataEventDetail( LoadingIndicator() } } - - IconButton( - onClick = { - onNavigateToChatRoom.invoke( - _root_ide_package_.at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute( - activeUserPublicKey = activeUserPublicKey, - chatRoomId = localNostrEvent.nostrEvent.pubKey, - relayHint = localNostrEvent.nostrEvent.relayUrl, - ) - ) - } - ) { - Icon( - Icons.Default.Mail, - contentDescription = "Direct Message" - ) - } - } Text( modifier = Modifier.fillMaxWidth().padding( @@ -238,117 +228,69 @@ fun MetadataEventDetail( } } - stickyHeader { - PrimaryScrollableTabRow( - modifier = Modifier.padding( - horizontal = 10.dp - ), - edgePadding = 10.dp, - selectedTabIndex = metadataEventDetailViewModel.pagerState.currentPage, - ) { - at.torch.compose.ui.view.model.MetadataEventDetailType.entries.forEachIndexed { index, destination -> - Tab( - selected = metadataEventDetailViewModel.pagerState.currentPage == index, - onClick = { - scope.launch { - metadataEventDetailViewModel.pagerState.animateScrollToPage(destination.ordinal) - } - }, - text = { - Text( - text = destination.name, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } + item { + TextButton( + onClick = { + onNavigateToEvent.invoke( + ImplementationPendingRoute("Add to Group") ) } + ) { + Icon( + Icons.Default.GroupAdd, + contentDescription = "Add to Group" + ) + + Spacer( + modifier = Modifier.width(10.dp) + ) + + Text("Add To Group") } } item { - Column( - modifier = Modifier.fillParentMaxSize() - ) { - HorizontalPager( - state = metadataEventDetailViewModel.pagerState, - ) { pageIndex -> - val metadataEventDetailType = at.torch.compose.ui.view.model.MetadataEventDetailType.entries[pageIndex] - - when (metadataEventDetailType) { - at.torch.compose.ui.view.model.MetadataEventDetailType.Posts, at.torch.compose.ui.view.model.MetadataEventDetailType.Replies, - at.torch.compose.ui.view.model.MetadataEventDetailType.Articles, at.torch.compose.ui.view.model.MetadataEventDetailType.Zaps, - at.torch.compose.ui.view.model.MetadataEventDetailType.Photos, at.torch.compose.ui.view.model.MetadataEventDetailType.Shorts, at.torch.compose.ui.view.model.MetadataEventDetailType.Videos, - at.torch.compose.ui.view.model.MetadataEventDetailType.Bookmarks, at.torch.compose.ui.view.model.MetadataEventDetailType.Reports, at.torch.compose.ui.view.model.MetadataEventDetailType.Relays -> { - val feedListViewModel: at.torch.compose.ui.view.model.FeedListViewModel = viewModel( - key = metadataEventDetailType.name, - factory = at.torch.compose.ui.view.model.FeedListViewModel.factory( - initialFeedListUIState = at.torch.compose.ui.view.state.FeedListUIState.Loading, - synchronizationFilter = metadataEventDetailViewModel.getSynchronizationFilter(metadataEventDetailType), - nostrRepository = nostrRepository - ), - ) - - key(feedListViewModel.feedListUIState) { - feedListViewModel.RenderFeed( - activeUserPublicKey = activeUserPublicKey, - onNavigateToEvent = onNavigateToEvent - ) - } - - key(true) { - feedListViewModel.initiate() - } - } - // TODO: Support gallery feeds... - at.torch.compose.ui.view.model.MetadataEventDetailType.Following -> { - val followingListViewModel: at.torch.compose.ui.view.model.FollowingListViewModel = viewModel( - key = metadataEventDetailType.name, - factory = at.torch.compose.ui.view.model.FollowingListViewModel.factory( - initialFeedListUIState = at.torch.compose.ui.view.state.FollowingListUIState.Loading, - publicKey = localNostrEvent.nostrEvent.pubKey, - synchronizationFilter = metadataEventDetailViewModel.getSynchronizationFilter(metadataEventDetailType), - nostrRepository = nostrRepository - ), - ) - - - key(followingListViewModel.followingListUIState) { - followingListViewModel.RenderFeed( - activeUserPublicKey = activeUserPublicKey, - onNavigateToEvent = onNavigateToEvent - ) - } - - key(true) { - followingListViewModel.initiate() - } - } - at.torch.compose.ui.view.model.MetadataEventDetailType.Followers -> { - val followersListViewModel: at.torch.compose.ui.view.model.FollowersListViewModel = viewModel( - key = metadataEventDetailType.name, - factory = at.torch.compose.ui.view.model.FollowersListViewModel.factory( - initialFeedListUIState = at.torch.compose.ui.view.state.FollowersListUIState.Loading, - publicKey = localNostrEvent.nostrEvent.pubKey, - synchronizationFilter = metadataEventDetailViewModel.getSynchronizationFilter(metadataEventDetailType), - nostrRepository = nostrRepository - ), - ) - - - key(followersListViewModel.followersListUIState) { - followersListViewModel.RenderFeed( - activeUserPublicKey = activeUserPublicKey, - onNavigateToEvent = onNavigateToEvent - ) - } - - key(true) { - followersListViewModel.initiate() - } - } - } + TextButton( + onClick = { + onNavigateToEvent.invoke( + ImplementationPendingRoute("Block User") + ) } + ) { + Icon( + Icons.Default.Block, + contentDescription = "Block" + ) + + Spacer( + modifier = Modifier.width(10.dp) + ) + + Text("Block User") + } + } + + item { + Button( + onClick = { + onNavigateToChatRoom.invoke( + ChatRoomDetailRoute( + activeUserPublicKey = activeUserPublicKey, + chatRoomId = localNostrEvent.nostrEvent.pubKey, + relayHint = localNostrEvent.nostrEvent.relayUrl, + ) + ) + } + ) { + Icon( + Icons.Default.Mail, + contentDescription = "Direct Message" + ) + + Spacer( + modifier = Modifier.width(10.dp) + ) + Text("Send Message") } } } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailMessageListViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailMessageListViewModel.kt index 9d8c5c9a..42e73940 100755 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailMessageListViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailMessageListViewModel.kt @@ -43,6 +43,7 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import at.torch.compose.extensions.toFormattedTimeAndDateString +import at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent @@ -52,13 +53,13 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch class ChatRoomDetailMessageListViewModel( - initialChatRoomDetailMessageListUIState: at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState, + initialChatRoomDetailMessageListUIState: ChatRoomDetailMessageListUIState, val localChatRoom: at.torch.compose.database.model.intermdiate.LocalChatRoom, val nostrRepository: at.torch.compose.repository.NostrRepository, val chatRepository: at.torch.compose.repository.ChatRepository ): ViewModel() { val logger = Logger.withTag(TAG) - var chatRoomDetailMessageListUIState: at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState by mutableStateOf(initialChatRoomDetailMessageListUIState) + var chatRoomDetailMessageListUIState: ChatRoomDetailMessageListUIState by mutableStateOf(initialChatRoomDetailMessageListUIState) private set val isReceiverChatMessageRelayListMissing: MutableState = mutableStateOf(false) @@ -76,7 +77,7 @@ class ChatRoomDetailMessageListViewModel( localChatRoom.chatRoom.id ).distinctUntilChanged().collect { chatMessages -> logger.d("getChatMessageListByChatRoomId: $chatMessages") - chatRoomDetailMessageListUIState = at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState.Loaded( + chatRoomDetailMessageListUIState = ChatRoomDetailMessageListUIState.Loaded( chatMessageList = chatMessages ) } @@ -161,7 +162,7 @@ class ChatRoomDetailMessageListViewModel( horizontalAlignment = Alignment.CenterHorizontally ) { when (val chatRoomDetailMessageListUIState = this@ChatRoomDetailMessageListViewModel.chatRoomDetailMessageListUIState) { - at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState.Error -> { + ChatRoomDetailMessageListUIState.Error -> { Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally @@ -174,7 +175,7 @@ class ChatRoomDetailMessageListViewModel( ) } } - is at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState.Loaded -> { + is ChatRoomDetailMessageListUIState.Loaded -> { Spacer( modifier = Modifier.weight(1f) ) @@ -342,7 +343,7 @@ class ChatRoomDetailMessageListViewModel( } } } - at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState.Loading -> { + ChatRoomDetailMessageListUIState.Loading -> { Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally @@ -384,7 +385,7 @@ class ChatRoomDetailMessageListViewModel( const val TAG = "ChatRoomDetailMessageListViewModel" fun factory( - initialChatRoomDetailMessageListUIState: at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState = at.torch.compose.ui.view.state.ChatRoomDetailMessageListUIState.Loading, + initialChatRoomDetailMessageListUIState: ChatRoomDetailMessageListUIState = ChatRoomDetailMessageListUIState.Loading, localChatRoom: at.torch.compose.database.model.intermdiate.LocalChatRoom, nostrRepository: at.torch.compose.repository.NostrRepository, chatRepository: at.torch.compose.repository.ChatRepository diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt index cb1b20f4..6181b7df 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt @@ -9,6 +9,9 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory +import at.torch.compose.repository.ChatRepository +import at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute +import at.torch.compose.ui.composable.navigation.routes.Route import at.torch.compose.ui.view.state.ChatRoomDetailUIState import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -22,7 +25,7 @@ class ChatRoomDetailViewModel( val relayHint: String?, initialChatRoomDetailUIState: ChatRoomDetailUIState, val nostrRepository: at.torch.compose.repository.NostrRepository, - val chatRepository: at.torch.compose.repository.ChatRepository + val chatRepository: ChatRepository ): ViewModel() { var chatRoomDetailUIState: ChatRoomDetailUIState by mutableStateOf(initialChatRoomDetailUIState) @@ -41,7 +44,8 @@ class ChatRoomDetailViewModel( val localChatRoom = chatRepository.getChatRoomByIdentifier(chatRoomId) chatRoomDetailUIState = if (localChatRoom == null) { - ChatRoomDetailUIState.Error + // Initiate new chat... and navigate to new chat... + ChatRoomDetailUIState.InitiatingNewChat } else { ChatRoomDetailUIState.Loaded( localChatRoom = localChatRoom @@ -50,6 +54,58 @@ class ChatRoomDetailViewModel( } } + fun initiateNewChat( + onNavigateToChat: (Route) -> Unit + ) { + logger.d("initiateNewChat: $chatRoomId") + // Get ChatRoomById + viewModelScope.launch(Dispatchers.IO) { + + // Check if we have a chat with activeUserPublicKey and chatRoomId (pubkey) as participants + val participants = chatRepository.getAllParticipantsWithPubKey(chatRoomId) + + participants.forEach { participant -> + chatRepository.getChatRoomByIdentifier(participant.chatRoomId)?.let { localChatRoom -> + // TODO: Check that chatRoom is not a group chat... + if (localChatRoom.localParticipants.size == 1) { + // This is a chat with self... + // TODO: navigateToTheChat... + viewModelScope.launch(Dispatchers.Main) { + onNavigateToChat.invoke( + ChatRoomDetailRoute( + activeUserPublicKey = activeUserPublicKey, + chatRoomId = localChatRoom.chatRoom.id, + relayHint = relayHint + ) + ) + } + return@launch + + } else if (localChatRoom.localParticipants.size == 2) { + val activeUserParticipant = localChatRoom.localParticipants.find { localParticipant -> localParticipant.participant.participantPublicKey == activeUserPublicKey } + + if (activeUserParticipant != null) { + // We have found the chat we are looking for... + viewModelScope.launch(Dispatchers.Main) { + onNavigateToChat.invoke( + ChatRoomDetailRoute( + activeUserPublicKey = activeUserPublicKey, + chatRoomId = localChatRoom.chatRoom.id, + relayHint = relayHint + ) + ) + } + return@launch + } + } + } + } + + logger.d("We should be creating a new chat...") + chatRoomDetailUIState = ChatRoomDetailUIState.Error + } + } + companion object { private const val TAG = "ChatRoomDetailViewModel" @@ -60,7 +116,7 @@ class ChatRoomDetailViewModel( relayHint: String?, initialChatRoomDetailUIState: ChatRoomDetailUIState = ChatRoomDetailUIState.Loading, nostrRepository: at.torch.compose.repository.NostrRepository, - chatRepository: at.torch.compose.repository.ChatRepository + chatRepository: ChatRepository ): ViewModelProvider.Factory = viewModelFactory { initializer { ChatRoomDetailViewModel( diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/state/ChatRoomDetailUIState.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/state/ChatRoomDetailUIState.kt index 75b0955d..604b5499 100755 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/state/ChatRoomDetailUIState.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/state/ChatRoomDetailUIState.kt @@ -7,5 +7,6 @@ sealed interface ChatRoomDetailUIState { data object Error: ChatRoomDetailUIState data object Loading: ChatRoomDetailUIState + data object InitiatingNewChat: ChatRoomDetailUIState }