diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt index 0f15864..c46a71d 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt @@ -67,7 +67,7 @@ internal fun MessengerScreen( ) { val state = viewModel.current - viewModel.ObserveEvents(galleryLauncher) + viewModel.ObserveEvents(galleryLauncher, navigator) LifecycleEffect( onStart = { viewModel.dispatch(ComponentLifecycle.Visible) }, onStop = { viewModel.dispatch(ComponentLifecycle.Gone) } @@ -85,6 +85,30 @@ internal fun MessengerScreen( onImageClick = { viewModel.dispatch(ComposerStateChange.ImagePreview.Show(it)) } ) + when (val dialog = state.dialogState) { + null -> { + // do nothing + } + + is DialogState.PositiveNegative -> { + AlertDialog( + onDismissRequest = { viewModel.dispatch(ScreenAction.LeaveRoomConfirmation.Deny) }, + confirmButton = { + Button(onClick = { viewModel.dispatch(ScreenAction.LeaveRoomConfirmation.Confirm) }) { + Text("Leave room") + } + }, + dismissButton = { + Button(onClick = { viewModel.dispatch(ScreenAction.LeaveRoomConfirmation.Deny) }) { + Text("Cancel") + } + }, + title = { Text(dialog.title) }, + text = { Text(dialog.subtitle) } + ) + } + } + Column { Toolbar(onNavigate = { navigator.navigate.upToHome() }, roomTitle, actions = { state.roomState.takeIfContent()?.let { @@ -98,8 +122,10 @@ internal fun MessengerScreen( viewModel.dispatch(ScreenAction.Notifications.Mute) }) } + DropdownMenuItem(text = { Text("Leave room", color = MaterialTheme.colorScheme.onSecondaryContainer) }, onClick = { + viewModel.dispatch(ScreenAction.LeaveRoom) + }) } - } }) @@ -207,7 +233,7 @@ private fun ZoomableImage(viewerState: ViewerState) { } @Composable -private fun MessengerState.ObserveEvents(galleryLauncher: ActivityResultLauncher) { +private fun MessengerState.ObserveEvents(galleryLauncher: ActivityResultLauncher, navigator: Navigator) { val context = LocalContext.current StartObserving { this@ObserveEvents.events.launch { @@ -221,6 +247,8 @@ private fun MessengerState.ObserveEvents(galleryLauncher: ActivityResultLauncher is MessengerEvent.Toast -> { Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show() } + + MessengerEvent.OnLeftRoom -> navigator.navigate.upToHome() } } } diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerAction.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerAction.kt index 07541ca..2cfa295 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerAction.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerAction.kt @@ -10,11 +10,19 @@ sealed interface ScreenAction : Action { data class CopyToClipboard(val model: BubbleModel) : ScreenAction object SendMessage : ScreenAction object OpenGalleryPicker : ScreenAction + object LeaveRoom : ScreenAction sealed interface Notifications : ScreenAction { object Mute : Notifications object Unmute : Notifications } + + sealed interface LeaveRoomConfirmation : ScreenAction { + object Confirm : LeaveRoomConfirmation + object Deny : LeaveRoomConfirmation + } + + data class UpdateDialogState(val dialogState: DialogState?): ScreenAction } sealed interface ComponentLifecycle : Action { diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerReducer.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerReducer.kt index b7e7c8f..a6ae9d7 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerReducer.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerReducer.kt @@ -19,6 +19,7 @@ import app.dapk.st.navigator.MessageAttachment import app.dapk.state.* import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlin.reflect.KClass internal fun messengerReducer( jobBag: JobBag, @@ -36,6 +37,7 @@ internal fun messengerReducer( roomState = Lce.Loading(), composerState = initialComposerState(initialAttachments), viewerState = null, + dialogState = null, ), async(ComponentLifecycle::class) { action -> @@ -159,9 +161,43 @@ internal fun messengerReducer( ) ) }, + + change(ScreenAction.UpdateDialogState::class) { action, state -> + state.copy(dialogState = action.dialogState) + }, + + rewrite(ScreenAction.LeaveRoom::class) { + ScreenAction.UpdateDialogState( + DialogState.PositiveNegative( + title = "Leave room", + subtitle = "Are you sure you want you leave the room? If the room is private you will need to be invited again to rejoin.", + negativeAction = ScreenAction.LeaveRoomConfirmation.Deny, + positiveAction = ScreenAction.LeaveRoomConfirmation.Confirm, + ) + ) + }, + + async(ScreenAction.LeaveRoomConfirmation::class) { action -> + dispatch(ScreenAction.UpdateDialogState(dialogState = null)) + + when (action) { + ScreenAction.LeaveRoomConfirmation.Confirm -> { + runCatching { chatEngine.rejectRoom(getState().roomId) }.fold( + onSuccess = { eventEmitter.invoke(MessengerEvent.OnLeftRoom) }, + onFailure = { eventEmitter.invoke(MessengerEvent.Toast("Failed to leave room")) }, + ) + } + + ScreenAction.LeaveRoomConfirmation.Deny -> { + // do nothing + } + } + }, ) } +private fun rewrite(klass: KClass, mapper: (A) -> Action) = async(klass) { action -> dispatch(mapper(action)) } + private suspend fun ChatEngine.sendTextMessage(content: MessengerPageState, composerState: ComposerState.Text) { val roomState = content.roomState val message = SendMessage.TextMessage( diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerState.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerState.kt index 385767c..095c509 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerState.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/state/MessengerState.kt @@ -1,12 +1,13 @@ package app.dapk.st.messenger.state import app.dapk.st.core.Lce -import app.dapk.st.state.State import app.dapk.st.design.components.BubbleModel import app.dapk.st.engine.MessengerPageState import app.dapk.st.engine.RoomEvent import app.dapk.st.matrix.common.RoomId import app.dapk.st.navigator.MessageAttachment +import app.dapk.st.state.State +import app.dapk.state.Action typealias MessengerState = State @@ -15,15 +16,26 @@ data class MessengerScreenState( val roomState: Lce, val composerState: ComposerState, val viewerState: ViewerState?, + val dialogState: DialogState?, ) data class ViewerState( val event: BubbleModel.Image, ) +sealed interface DialogState { + data class PositiveNegative( + val title: String, + val subtitle: String, + val positiveAction: Action, + val negativeAction: Action, + ) : DialogState +} + sealed interface MessengerEvent { object SelectImageAttachment : MessengerEvent data class Toast(val message: String) : MessengerEvent + object OnLeftRoom : MessengerEvent } sealed interface ComposerState {