Boilerplate for inviting new member to chat
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
package at.torch.compose.ui.composable
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Mic
|
||||
import androidx.compose.material.icons.filled.PersonAdd
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.material3.BottomAppBarDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.database.model.ChatRoom
|
||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||
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.composable.widgets.profile.ProfileColor
|
||||
import at.torch.compose.ui.theme.TorchTheme
|
||||
import at.torch.compose.ui.view.model.AddMemberToChatRoomConfirmationViewModel
|
||||
import at.torch.compose.ui.view.state.AddMemberToChatRoomConfirmationUIState
|
||||
import at.torch.compose.ui.view.state.ChatRoomDetailUIState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AddMemberToChatRoomConfirmationScreen(
|
||||
activeUserPublicKey: HexKey,
|
||||
chatRoomId: String,
|
||||
relayHint: String?,
|
||||
initialChatRoomDetailUIState: AddMemberToChatRoomConfirmationUIState = AddMemberToChatRoomConfirmationUIState.Loading,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository,
|
||||
onNavigateToChat: (Route) -> Unit,
|
||||
) {
|
||||
val addMemberToChatRoomConfirmationViewModel: AddMemberToChatRoomConfirmationViewModel = viewModel(
|
||||
factory = AddMemberToChatRoomConfirmationViewModel.factory(
|
||||
chatRoomId = chatRoomId,
|
||||
relayHint = relayHint,
|
||||
initialAddMemberToChatRoomConfirmationUIState = initialChatRoomDetailUIState,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository,
|
||||
activeUserPublicKey = activeUserPublicKey
|
||||
)
|
||||
)
|
||||
|
||||
when (val addMemberToChatRoomConfirmationUIState = addMemberToChatRoomConfirmationViewModel.addMemberToChatRoomConfirmationUIState) {
|
||||
is AddMemberToChatRoomConfirmationUIState.Error -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.height(50.dp)
|
||||
)
|
||||
Text(
|
||||
text = addMemberToChatRoomConfirmationUIState.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AddMemberToChatRoomConfirmationUIState.Loaded -> {
|
||||
val textFieldState = rememberTextFieldState()
|
||||
|
||||
val chatRoomDetailMessageListViewModel: at.torch.compose.ui.view.model.ChatRoomDetailMessageListViewModel = viewModel(
|
||||
factory = at.torch.compose.ui.view.model.ChatRoomDetailMessageListViewModel.factory(
|
||||
localChatRoom = addMemberToChatRoomConfirmationUIState.localChatRoom,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
val title = if (addMemberToChatRoomConfirmationUIState.localChatRoom.chatRoom.subject != null) {
|
||||
"Invite new member to ${addMemberToChatRoomConfirmationUIState.localChatRoom.chatRoom.subject}"
|
||||
} else {
|
||||
"Invite new member to chat"
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding).fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth()
|
||||
) {
|
||||
Text("Render contact List")
|
||||
}
|
||||
|
||||
key(true) {
|
||||
chatRoomDetailMessageListViewModel.initiate()
|
||||
}
|
||||
|
||||
// TODO: Check that we have direct message relays for this user...
|
||||
}
|
||||
}
|
||||
}
|
||||
AddMemberToChatRoomConfirmationUIState.Loading -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(
|
||||
20.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
) {
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = "Direct Message Detail",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Direct message functionality will be here.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
LoadingDataIndicator(
|
||||
fillScreen = false
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(2f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(true) {
|
||||
if (initialChatRoomDetailUIState == AddMemberToChatRoomConfirmationUIState.Loading) {
|
||||
addMemberToChatRoomConfirmationViewModel.initiateChatRoomDetail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ChatRoomDetailScreenPreview() {
|
||||
TorchTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
AddMemberToChatRoomConfirmationScreen(
|
||||
activeUserPublicKey = "",
|
||||
chatRoomId = "publicKey",
|
||||
relayHint = null,
|
||||
initialChatRoomDetailUIState = AddMemberToChatRoomConfirmationUIState.Loaded(
|
||||
localChatRoom = LocalChatRoom(
|
||||
chatRoom = ChatRoom(
|
||||
id = "",
|
||||
userPublicKey = "",
|
||||
subject = "Message title",
|
||||
initialGiftWrapPayloadId = "sdfaer",
|
||||
mlsGroupState = null
|
||||
),
|
||||
)
|
||||
),
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
||||
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY,
|
||||
onNavigateToChat = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||
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.navigation.routes.SearchMemberToAddToChatRoomRoute
|
||||
import at.torch.compose.ui.composable.widgets.LoadingDataIndicator
|
||||
import at.torch.compose.ui.theme.TorchTheme
|
||||
import at.torch.compose.ui.view.model.ChatRoomDetailViewModel
|
||||
@@ -57,7 +58,8 @@ fun ChatRoomDetailScreen(
|
||||
initialChatRoomDetailUIState: ChatRoomDetailUIState = ChatRoomDetailUIState.Loading,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository,
|
||||
onNavigateToChat: (Route) -> Unit,
|
||||
onNavigateToRouteAndPopUpInclusive: (Route) -> Unit,
|
||||
onNavigateToRoute: (Route) -> Unit,
|
||||
) {
|
||||
val chatRoomDetailViewModel: ChatRoomDetailViewModel = viewModel(
|
||||
factory = ChatRoomDetailViewModel.factory(
|
||||
@@ -104,7 +106,14 @@ fun ChatRoomDetailScreen(
|
||||
actions = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
// TODO: Navigate to the invite user screen...
|
||||
// Navigate to the invite user screen...
|
||||
onNavigateToRoute.invoke(
|
||||
SearchMemberToAddToChatRoomRoute(
|
||||
chatRoomId = chatRoomId,
|
||||
activeUserPublicKey = activeUserPublicKey,
|
||||
relayHint = relayHint
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
@@ -298,7 +307,7 @@ fun ChatRoomDetailScreen(
|
||||
|
||||
LaunchedEffect(true) {
|
||||
chatRoomDetailViewModel.initiateNewChat(
|
||||
onNavigateToChat
|
||||
onNavigateToRouteAndPopUpInclusive
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -336,7 +345,8 @@ private fun ChatRoomDetailScreenPreview() {
|
||||
),
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
||||
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY,
|
||||
onNavigateToChat = {}
|
||||
onNavigateToRouteAndPopUpInclusive = {},
|
||||
onNavigateToRoute = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package at.torch.compose.ui.composable
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Mic
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.material3.BottomAppBarDefaults
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.database.model.ChatRoom
|
||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||
import at.torch.compose.repository.ChatRepository
|
||||
import at.torch.compose.repository.NostrRepository
|
||||
import at.torch.compose.ui.composable.navigation.routes.AddMemberToChatRoomConfirmationRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.Route
|
||||
import at.torch.compose.ui.composable.widgets.LoadingDataIndicator
|
||||
import at.torch.compose.ui.theme.TorchTheme
|
||||
import at.torch.compose.ui.view.model.ChatRoomDetailMessageListViewModel
|
||||
import at.torch.compose.ui.view.model.SearchMemberToAddToChatRoomViewModel
|
||||
import at.torch.compose.ui.view.state.ChatRoomDetailUIState
|
||||
import at.torch.compose.ui.view.state.SearchMemberToAddToChatRoomUIState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SearchMemberToAddToChatRoomScreen(
|
||||
activeUserPublicKey: HexKey,
|
||||
chatRoomId: String,
|
||||
relayHint: String?,
|
||||
initialSearchMemberToAddToChatRoomUIState: SearchMemberToAddToChatRoomUIState = SearchMemberToAddToChatRoomUIState.Loading,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository,
|
||||
onNavigateToRoute: (Route) -> Unit,
|
||||
) {
|
||||
val searchMemberToAddToChatRoomViewModel: SearchMemberToAddToChatRoomViewModel = viewModel(
|
||||
factory = SearchMemberToAddToChatRoomViewModel.factory(
|
||||
chatRoomId = chatRoomId,
|
||||
relayHint = relayHint,
|
||||
initialSearchMemberToAddToChatRoomUIState = initialSearchMemberToAddToChatRoomUIState,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository,
|
||||
activeUserPublicKey = activeUserPublicKey
|
||||
)
|
||||
)
|
||||
|
||||
when (val searchMemberToAddToChatRoomUIState = searchMemberToAddToChatRoomViewModel.searchMemberToAddToChatRoomUIState) {
|
||||
is SearchMemberToAddToChatRoomUIState.Error -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.height(50.dp)
|
||||
)
|
||||
Text(
|
||||
text = searchMemberToAddToChatRoomUIState.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
is SearchMemberToAddToChatRoomUIState.Loaded -> {
|
||||
val textFieldState = rememberTextFieldState()
|
||||
|
||||
val chatRoomDetailMessageListViewModel: ChatRoomDetailMessageListViewModel = viewModel(
|
||||
factory = ChatRoomDetailMessageListViewModel.factory(
|
||||
localChatRoom = searchMemberToAddToChatRoomUIState.localChatRoom,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
val title = if (searchMemberToAddToChatRoomUIState.localChatRoom.chatRoom.subject != null) {
|
||||
"Invite new member to ${searchMemberToAddToChatRoomUIState.localChatRoom.chatRoom.subject}"
|
||||
} else {
|
||||
"Invite new member to chat"
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding).fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth()
|
||||
) {
|
||||
key(true) {
|
||||
Button(
|
||||
onClick = {
|
||||
onNavigateToRoute.invoke(
|
||||
AddMemberToChatRoomConfirmationRoute(
|
||||
activeUserPublicKey = activeUserPublicKey,
|
||||
chatRoomId = chatRoomId,
|
||||
relayHint = relayHint
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text("Contact List TODO")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key(true) {
|
||||
chatRoomDetailMessageListViewModel.initiate()
|
||||
}
|
||||
|
||||
// TODO: Check that we have direct message relays for this user...
|
||||
}
|
||||
}
|
||||
}
|
||||
SearchMemberToAddToChatRoomUIState.Loading -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(
|
||||
20.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
) {
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = "Search member functionality",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Search message functionality will be here.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
LoadingDataIndicator(
|
||||
fillScreen = false
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.weight(2f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(true) {
|
||||
if (initialSearchMemberToAddToChatRoomUIState == SearchMemberToAddToChatRoomUIState.Loading) {
|
||||
searchMemberToAddToChatRoomViewModel.initiateChatRoomDetail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ChatRoomDetailScreenPreview() {
|
||||
TorchTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
SearchMemberToAddToChatRoomScreen(
|
||||
activeUserPublicKey = "",
|
||||
chatRoomId = "publicKey",
|
||||
relayHint = null,
|
||||
initialSearchMemberToAddToChatRoomUIState = SearchMemberToAddToChatRoomUIState.Loaded(
|
||||
localChatRoom = LocalChatRoom(
|
||||
chatRoom = ChatRoom(
|
||||
id = "",
|
||||
userPublicKey = "",
|
||||
subject = "Message title",
|
||||
initialGiftWrapPayloadId = "sdfaer",
|
||||
mlsGroupState = null
|
||||
),
|
||||
)
|
||||
),
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
||||
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY,
|
||||
onNavigateToRoute = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,14 @@ import at.torch.compose.database.repository.DatabaseMarmotRepository
|
||||
import at.torch.compose.database.repository.DatabaseNostrRepository
|
||||
import at.torch.compose.database.repository.DatabaseSearchRepository
|
||||
import at.torch.compose.ui.composable.ActiveProfileScreen
|
||||
import at.torch.compose.ui.composable.AddMemberToChatRoomConfirmationScreen
|
||||
import at.torch.compose.ui.composable.ChatRoomCreationScreen
|
||||
import at.torch.compose.ui.composable.ChatRoomDetailScreen
|
||||
import at.torch.compose.ui.composable.HomeScreen
|
||||
import at.torch.compose.ui.composable.ImplementationPendingScreen
|
||||
import at.torch.compose.ui.composable.KeyPackageManagementScreen
|
||||
import at.torch.compose.ui.composable.NostrEventDetailScreen
|
||||
import at.torch.compose.ui.composable.SearchMemberToAddToChatRoomScreen
|
||||
import at.torch.compose.ui.composable.SearchResultScreen
|
||||
import at.torch.compose.ui.composable.SearchScreen
|
||||
import at.torch.compose.ui.composable.ShareProfileScreen
|
||||
@@ -37,6 +39,7 @@ import at.torch.compose.ui.composable.UnsignedProfileScreen
|
||||
import at.torch.compose.ui.composable.UnsyncedProfileScreen
|
||||
import at.torch.compose.ui.composable.WriteNewNoteScreen
|
||||
import at.torch.compose.ui.composable.navigation.routes.ActiveProfileRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.AddMemberToChatRoomConfirmationRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.BlankRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.ChatRoomCreationRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute
|
||||
@@ -47,6 +50,7 @@ import at.torch.compose.ui.composable.navigation.routes.KeyPackageManagementRout
|
||||
import at.torch.compose.ui.composable.navigation.routes.LandingRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.LoadingRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.NostrEventDetailRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.SearchMemberToAddToChatRoomRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.SearchResultRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.SearchRoute
|
||||
import at.torch.compose.ui.composable.navigation.routes.ShareProfileRoute
|
||||
@@ -549,6 +553,51 @@ fun TorchNavHost(
|
||||
val route = backStackEntry.toRoute<ChatRoomDetailRoute>()
|
||||
|
||||
ChatRoomDetailScreen(
|
||||
activeUserPublicKey = route.activeUserPublicKey,
|
||||
chatRoomId = route.chatRoomId,
|
||||
relayHint = route.relayHint,
|
||||
nostrRepository = databaseNostrRepository,
|
||||
chatRepository = databaseChatRepository,
|
||||
onNavigateToRouteAndPopUpInclusive = { chatRoomDetailRoute ->
|
||||
navController.navigate(
|
||||
route = chatRoomDetailRoute
|
||||
) {
|
||||
popUpTo(route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
},
|
||||
onNavigateToRoute = { actionRoute ->
|
||||
navController.navigate(
|
||||
route = actionRoute
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
composable<SearchMemberToAddToChatRoomRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<SearchMemberToAddToChatRoomRoute>()
|
||||
|
||||
SearchMemberToAddToChatRoomScreen(
|
||||
activeUserPublicKey = route.activeUserPublicKey,
|
||||
chatRoomId = route.chatRoomId,
|
||||
relayHint = route.relayHint,
|
||||
nostrRepository = databaseNostrRepository,
|
||||
chatRepository = databaseChatRepository,
|
||||
onNavigateToRoute = { chatRoomDetailRoute ->
|
||||
navController.navigate(
|
||||
route = chatRoomDetailRoute
|
||||
) {
|
||||
popUpTo(route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
composable<AddMemberToChatRoomConfirmationRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<AddMemberToChatRoomConfirmationRoute>()
|
||||
|
||||
AddMemberToChatRoomConfirmationScreen(
|
||||
activeUserPublicKey = route.activeUserPublicKey,
|
||||
chatRoomId = route.chatRoomId,
|
||||
relayHint = route.relayHint,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package at.torch.compose.ui.composable.navigation.routes
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class AddMemberToChatRoomConfirmationRoute(
|
||||
val activeUserPublicKey: String,
|
||||
val chatRoomId: String, // TODO: have this as a publicKey
|
||||
val relayHint: String?
|
||||
): Route()
|
||||
@@ -0,0 +1,10 @@
|
||||
package at.torch.compose.ui.composable.navigation.routes
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SearchMemberToAddToChatRoomRoute(
|
||||
val activeUserPublicKey: String,
|
||||
val chatRoomId: String, // TODO: have this as a publicKey
|
||||
val relayHint: String?
|
||||
): Route()
|
||||
@@ -0,0 +1,166 @@
|
||||
package at.torch.compose.ui.view.model
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
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.repository.NostrRepository
|
||||
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.AddMemberToChatRoomConfirmationUIState
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AddMemberToChatRoomConfirmationViewModel(
|
||||
val chatRoomId: String,
|
||||
val activeUserPublicKey: HexKey,
|
||||
val relayHint: String?,
|
||||
initialAddMemberToChatRoomConfirmationUIState: AddMemberToChatRoomConfirmationUIState,
|
||||
val nostrRepository: NostrRepository,
|
||||
val chatRepository: ChatRepository
|
||||
): ViewModel() {
|
||||
|
||||
var addMemberToChatRoomConfirmationUIState: AddMemberToChatRoomConfirmationUIState by mutableStateOf(initialAddMemberToChatRoomConfirmationUIState)
|
||||
private set
|
||||
|
||||
|
||||
|
||||
private val logger = Logger.withTag(TAG)
|
||||
|
||||
val isActionPending: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
fun initiateChatRoomDetail() {
|
||||
logger.d("compressed (most likely chat room): $chatRoomId")
|
||||
// Get ChatRoomById
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val localChatRoom = chatRepository.getChatRoomByIdentifier(chatRoomId)
|
||||
|
||||
addMemberToChatRoomConfirmationUIState = if (localChatRoom == null) {
|
||||
// Initiate new chat... and navigate to new chat...
|
||||
AddMemberToChatRoomConfirmationUIState.Error("Couldn't load chat")
|
||||
} else {
|
||||
AddMemberToChatRoomConfirmationUIState.Loaded(
|
||||
localChatRoom = localChatRoom
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val profile = chatRepository.getProfileWithPublicKey(chatRoomId)
|
||||
|
||||
if (profile != null) {
|
||||
logger.d("Create chat with: $profile")
|
||||
// Check if we have a keyPackageEvent...
|
||||
val keyPackageEvent = chatRepository.getMarmotKeyPackageForPublicKey(chatRoomId)
|
||||
|
||||
if (keyPackageEvent == null) {
|
||||
addMemberToChatRoomConfirmationUIState = AddMemberToChatRoomConfirmationUIState.Error("Missing Key Package") // TODO: Introduce missing key package UI State...
|
||||
} else {
|
||||
// Create new chat and invite local
|
||||
try {
|
||||
val chatRoomId = chatRepository.createMlsDirectMessage(
|
||||
userPublicKey = activeUserPublicKey,
|
||||
peerPublicKey = chatRoomId,
|
||||
peerKeyPackage = keyPackageEvent,
|
||||
)
|
||||
|
||||
onNavigateToChat.invoke(
|
||||
ChatRoomDetailRoute(
|
||||
activeUserPublicKey = activeUserPublicKey,
|
||||
chatRoomId = chatRoomId,
|
||||
relayHint = relayHint
|
||||
)
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
addMemberToChatRoomConfirmationUIState = AddMemberToChatRoomConfirmationUIState.Error("Failed to create chat")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
addMemberToChatRoomConfirmationUIState = AddMemberToChatRoomConfirmationUIState.Error("Couldn't find profile...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TAG = "AddMemberToChatRoomConfirmationViewModel"
|
||||
|
||||
fun factory(
|
||||
activeUserPublicKey: HexKey,
|
||||
chatRoomId: String,
|
||||
relayHint: String?,
|
||||
initialAddMemberToChatRoomConfirmationUIState: AddMemberToChatRoomConfirmationUIState = AddMemberToChatRoomConfirmationUIState.Loading,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
AddMemberToChatRoomConfirmationViewModel(
|
||||
activeUserPublicKey = activeUserPublicKey,
|
||||
chatRoomId = chatRoomId,
|
||||
relayHint = relayHint,
|
||||
initialAddMemberToChatRoomConfirmationUIState = initialAddMemberToChatRoomConfirmationUIState,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package at.torch.compose.ui.view.model
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
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.repository.NostrRepository
|
||||
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.SearchMemberToAddToChatRoomUIState
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SearchMemberToAddToChatRoomViewModel(
|
||||
val chatRoomId: String,
|
||||
val activeUserPublicKey: HexKey,
|
||||
val relayHint: String?,
|
||||
initialSearchMemberToAddToChatRoomUIState: SearchMemberToAddToChatRoomUIState,
|
||||
val nostrRepository: NostrRepository,
|
||||
val chatRepository: ChatRepository
|
||||
): ViewModel() {
|
||||
|
||||
var searchMemberToAddToChatRoomUIState: SearchMemberToAddToChatRoomUIState by mutableStateOf(initialSearchMemberToAddToChatRoomUIState)
|
||||
private set
|
||||
|
||||
|
||||
|
||||
private val logger = Logger.withTag(TAG)
|
||||
|
||||
val isActionPending: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
fun initiateChatRoomDetail() {
|
||||
logger.d("compressed (most likely chat room): $chatRoomId")
|
||||
// Get ChatRoomById
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val localChatRoom = chatRepository.getChatRoomByIdentifier(chatRoomId)
|
||||
|
||||
searchMemberToAddToChatRoomUIState = if (localChatRoom == null) {
|
||||
// Initiate new chat... and navigate to new chat...
|
||||
SearchMemberToAddToChatRoomUIState.Error("Failed to load chat")
|
||||
} else {
|
||||
SearchMemberToAddToChatRoomUIState.Loaded(
|
||||
localChatRoom = localChatRoom
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val profile = chatRepository.getProfileWithPublicKey(chatRoomId)
|
||||
|
||||
if (profile != null) {
|
||||
logger.d("Create chat with: $profile")
|
||||
// Check if we have a keyPackageEvent...
|
||||
val keyPackageEvent = chatRepository.getMarmotKeyPackageForPublicKey(chatRoomId)
|
||||
|
||||
if (keyPackageEvent == null) {
|
||||
searchMemberToAddToChatRoomUIState = SearchMemberToAddToChatRoomUIState.Error("Missing Key Package") // TODO: Introduce missing key package UI State...
|
||||
} else {
|
||||
// Create new chat and invite local
|
||||
try {
|
||||
val chatRoomId = chatRepository.createMlsDirectMessage(
|
||||
userPublicKey = activeUserPublicKey,
|
||||
peerPublicKey = chatRoomId,
|
||||
peerKeyPackage = keyPackageEvent,
|
||||
)
|
||||
|
||||
onNavigateToChat.invoke(
|
||||
ChatRoomDetailRoute(
|
||||
activeUserPublicKey = activeUserPublicKey,
|
||||
chatRoomId = chatRoomId,
|
||||
relayHint = relayHint
|
||||
)
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
searchMemberToAddToChatRoomUIState = SearchMemberToAddToChatRoomUIState.Error("Failed to create chat")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
searchMemberToAddToChatRoomUIState = SearchMemberToAddToChatRoomUIState.Error("Couldn't find profile...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SearchMemberToAddToChatRoomViewModel"
|
||||
|
||||
fun factory(
|
||||
activeUserPublicKey: HexKey,
|
||||
chatRoomId: String,
|
||||
relayHint: String?,
|
||||
initialSearchMemberToAddToChatRoomUIState: SearchMemberToAddToChatRoomUIState = SearchMemberToAddToChatRoomUIState.Loading,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
SearchMemberToAddToChatRoomViewModel(
|
||||
activeUserPublicKey = activeUserPublicKey,
|
||||
chatRoomId = chatRoomId,
|
||||
relayHint = relayHint,
|
||||
initialSearchMemberToAddToChatRoomUIState = initialSearchMemberToAddToChatRoomUIState,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package at.torch.compose.ui.view.state
|
||||
|
||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||
|
||||
sealed interface AddMemberToChatRoomConfirmationUIState {
|
||||
data class Loaded(
|
||||
val localChatRoom: LocalChatRoom
|
||||
): AddMemberToChatRoomConfirmationUIState
|
||||
|
||||
data class Error(
|
||||
val message: String
|
||||
): AddMemberToChatRoomConfirmationUIState
|
||||
data object Loading: AddMemberToChatRoomConfirmationUIState
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package at.torch.compose.ui.view.state
|
||||
|
||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||
|
||||
sealed interface SearchMemberToAddToChatRoomUIState {
|
||||
data class Loaded(
|
||||
val localChatRoom: LocalChatRoom
|
||||
): SearchMemberToAddToChatRoomUIState
|
||||
|
||||
data class Error(
|
||||
val message: String
|
||||
): SearchMemberToAddToChatRoomUIState
|
||||
data object Loading: SearchMemberToAddToChatRoomUIState
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user