diff --git a/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt b/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt index c5c74c61112..808b57dc4c1 100644 --- a/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt @@ -40,12 +40,14 @@ import com.wire.kalium.logic.feature.conversation.ObserveConversationListDetails import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase import com.wire.kalium.logic.feature.conversation.ObserveDegradedConversationNotifiedUseCase import com.wire.kalium.logic.feature.conversation.ObserveIsSelfUserMemberUseCase +import com.wire.kalium.logic.feature.conversation.ObserveConversationUnderLegalHoldNotifiedUseCase import com.wire.kalium.logic.feature.conversation.ObserveUserListByIdUseCase import com.wire.kalium.logic.feature.conversation.ObserveUsersTypingUseCase import com.wire.kalium.logic.feature.conversation.RefreshConversationsWithoutMetadataUseCase import com.wire.kalium.logic.feature.conversation.RemoveMemberFromConversationUseCase import com.wire.kalium.logic.feature.conversation.RenameConversationUseCase import com.wire.kalium.logic.feature.conversation.SendTypingEventUseCase +import com.wire.kalium.logic.feature.conversation.SetNotifiedAboutConversationUnderLegalHoldUseCase import com.wire.kalium.logic.feature.conversation.SetUserInformedAboutVerificationUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationAccessRoleUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationArchivedStatusUseCase @@ -274,4 +276,16 @@ class ConversationModule { conversationScope: ConversationScope ): ObserveDegradedConversationNotifiedUseCase = conversationScope.observeInformAboutVerificationBeforeMessagingFlagUseCase + + @ViewModelScoped + @Provides + fun provideSetUserNotifiedAboutConversationUnderLegalHoldUseCase( + conversationScope: ConversationScope, + ): SetNotifiedAboutConversationUnderLegalHoldUseCase = conversationScope.setNotifiedAboutConversationUnderLegalHold + + @ViewModelScoped + @Provides + fun provideObserveLegalHoldWithChangeNotifiedForConversationUseCase( + conversationScope: ConversationScope, + ): ObserveConversationUnderLegalHoldNotifiedUseCase = conversationScope.observeConversationUnderLegalHoldNotified } diff --git a/app/src/main/kotlin/com/wire/android/ui/common/dialogs/SureAboutMessagingInDegradedConversationDialog.kt b/app/src/main/kotlin/com/wire/android/ui/common/dialogs/SureAboutMessagingInDegradedConversationDialog.kt index 0991d1eab89..bacb00ea59c 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/dialogs/SureAboutMessagingInDegradedConversationDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/dialogs/SureAboutMessagingInDegradedConversationDialog.kt @@ -25,15 +25,14 @@ import com.wire.android.ui.common.WireDialog import com.wire.android.ui.common.WireDialogButtonProperties import com.wire.android.ui.common.WireDialogButtonType import com.wire.android.ui.home.conversations.SureAboutMessagingDialogState -import com.wire.android.ui.home.messagecomposer.state.MessageBundle @Composable fun SureAboutMessagingInDegradedConversationDialog( dialogState: SureAboutMessagingDialogState, - sendAnyway: (MessageBundle) -> Unit, + sendAnyway: () -> Unit, hideDialog: () -> Unit ) { - if (dialogState is SureAboutMessagingDialogState.ConversationVerificationDegraded) { + if (dialogState is SureAboutMessagingDialogState.Visible.ConversationVerificationDegraded) { WireDialog( title = stringResource(R.string.sure_about_messaging_dialog_title), text = stringResource(R.string.sure_about_messaging_dialog_body), @@ -42,11 +41,11 @@ fun SureAboutMessagingInDegradedConversationDialog( optionButton1Properties = WireDialogButtonProperties( text = stringResource(R.string.sure_about_messaging_dialog_positiv_button), type = WireDialogButtonType.Primary, - onClick = { sendAnyway(dialogState.messageBundleToSend) } + onClick = sendAnyway, ), dismissButtonProperties = WireDialogButtonProperties( text = stringResource(R.string.label_cancel), - onClick = hideDialog + onClick = hideDialog, ) ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt index a48e60191a1..0c85782c51e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt @@ -132,6 +132,7 @@ import com.wire.android.ui.home.messagecomposer.MessageComposer import com.wire.android.ui.home.messagecomposer.state.MessageBundle import com.wire.android.ui.home.messagecomposer.state.MessageComposerStateHolder import com.wire.android.ui.home.messagecomposer.state.rememberMessageComposerStateHolder +import com.wire.android.ui.legalhold.dialog.subject.LegalHoldSubjectMessageDialog import com.wire.android.ui.theme.wireColorScheme import com.wire.android.util.extension.openAppInfoScreen import com.wire.android.util.normalizeLink @@ -430,10 +431,18 @@ fun ConversationScreen( SureAboutMessagingInDegradedConversationDialog( dialogState = messageComposerViewModel.sureAboutMessagingDialogState, - sendAnyway = messageComposerViewModel::sureAboutSendingMessage, - hideDialog = messageComposerViewModel::hideSureAboutSendingMessage + sendAnyway = messageComposerViewModel::acceptSureAboutSendingMessage, + hideDialog = messageComposerViewModel::dismissSureAboutSendingMessage ) + (messageComposerViewModel.sureAboutMessagingDialogState as? SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold)?.let { + LegalHoldSubjectMessageDialog( + conversationName = conversationInfoViewModel.conversationInfoViewState.conversationName.asString(), + dialogDismissed = messageComposerViewModel::dismissSureAboutSendingMessage, + sendAnywayClicked = messageComposerViewModel::acceptSureAboutSendingMessage, + ) + } + groupDetailsScreenResultRecipient.onNavResult { result -> when (result) { is Canceled -> { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt index a853a022baf..07020243f00 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt @@ -61,7 +61,9 @@ import com.wire.kalium.logic.feature.conversation.IsInteractionAvailableResult import com.wire.kalium.logic.feature.conversation.MembersToMentionUseCase import com.wire.kalium.logic.feature.conversation.ObserveConversationInteractionAvailabilityUseCase import com.wire.kalium.logic.feature.conversation.ObserveDegradedConversationNotifiedUseCase +import com.wire.kalium.logic.feature.conversation.ObserveConversationUnderLegalHoldNotifiedUseCase import com.wire.kalium.logic.feature.conversation.SendTypingEventUseCase +import com.wire.kalium.logic.feature.conversation.SetNotifiedAboutConversationUnderLegalHoldUseCase import com.wire.kalium.logic.feature.conversation.SetUserInformedAboutVerificationUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationReadDateUseCase import com.wire.kalium.logic.feature.message.DeleteMessageUseCase @@ -77,6 +79,7 @@ import com.wire.kalium.logic.functional.onFailure import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.datetime.Instant @@ -110,7 +113,9 @@ class MessageComposerViewModel @Inject constructor( private val imageUtil: ImageUtil, private val fileManager: FileManager, private val setUserInformedAboutVerification: SetUserInformedAboutVerificationUseCase, - private val observeDegradedConversationNotified: ObserveDegradedConversationNotifiedUseCase + private val observeDegradedConversationNotified: ObserveDegradedConversationNotifiedUseCase, + private val setNotifiedAboutConversationUnderLegalHold: SetNotifiedAboutConversationUnderLegalHoldUseCase, + private val observeConversationUnderLegalHoldNotified: ObserveConversationUnderLegalHoldNotifiedUseCase, ) : SavedStateViewModel(savedStateHandle) { var messageComposerViewState = mutableStateOf(MessageComposerViewState()) @@ -162,10 +167,8 @@ class MessageComposerViewModel @Inject constructor( InvalidLinkDialogState.Hidden ) - private val shouldInformAboutDegradedBeforeSendingMessage = mutableStateOf(false) - var sureAboutMessagingDialogState: SureAboutMessagingDialogState by mutableStateOf( - SureAboutMessagingDialogState.None + SureAboutMessagingDialogState.Hidden ) init { @@ -174,19 +177,12 @@ class MessageComposerViewModel @Inject constructor( observeIsTypingAvailable() observeSelfDeletingMessagesStatus() setFileSharingStatus() - observeInformedAboutDegradedVerification() } private fun onSnackbarMessage(type: SnackBarMessage) = viewModelScope.launch { _infoMessage.emit(type) } - private fun observeInformedAboutDegradedVerification() = viewModelScope.launch { - observeDegradedConversationNotified(conversationId).collect { - shouldInformAboutDegradedBeforeSendingMessage.value = !it - } - } - private fun observeIsTypingAvailable() = viewModelScope.launch { observeConversationInteractionAvailability(conversationId).collect { result -> messageComposerViewState.value = messageComposerViewState.value.copy( @@ -208,58 +204,66 @@ class MessageComposerViewModel @Inject constructor( } } + private suspend fun shouldInformAboutDegradedBeforeSendingMessage(): Boolean = + observeDegradedConversationNotified(conversationId).first().let { !it } + + private suspend fun shouldInformAboutUnderLegalHoldBeforeSendingMessage() = + observeConversationUnderLegalHoldNotified(conversationId).first().let { !it } + fun trySendMessage(messageBundle: MessageBundle) { - if (shouldInformAboutDegradedBeforeSendingMessage.value) { - sureAboutMessagingDialogState = SureAboutMessagingDialogState.ConversationVerificationDegraded(messageBundle) - } else { - sendMessage(messageBundle) + viewModelScope.launch { + when { + shouldInformAboutDegradedBeforeSendingMessage() -> + sureAboutMessagingDialogState = SureAboutMessagingDialogState.Visible.ConversationVerificationDegraded(messageBundle) + shouldInformAboutUnderLegalHoldBeforeSendingMessage() -> + sureAboutMessagingDialogState = SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold(messageBundle) + else -> sendMessage(messageBundle) + } } } - private fun sendMessage(messageBundle: MessageBundle) { - viewModelScope.launch { - when (messageBundle) { - is ComposableMessageBundle.EditMessageBundle -> { - with(messageBundle) { - sendEditTextMessage( - conversationId = conversationId, - originalMessageId = originalMessageId, - text = newContent, - mentions = newMentions.map { it.intoMessageMention() }, - ) - } - sendTypingEvent(conversationId, TypingIndicatorMode.STOPPED) - } - - is ComposableMessageBundle.AttachmentPickedBundle -> { - handleAssetMessageBundle( - attachmentUri = messageBundle.attachmentUri + private suspend fun sendMessage(messageBundle: MessageBundle) { + when (messageBundle) { + is ComposableMessageBundle.EditMessageBundle -> { + with(messageBundle) { + sendEditTextMessage( + conversationId = conversationId, + originalMessageId = originalMessageId, + text = newContent, + mentions = newMentions.map { it.intoMessageMention() }, ) } + sendTypingEvent(conversationId, TypingIndicatorMode.STOPPED) + } - is ComposableMessageBundle.AudioMessageBundle -> { - handleAssetMessageBundle( - attachmentUri = messageBundle.attachmentUri, - audioPath = messageBundle.attachmentUri.uri.path?.toPath() - ) - } + is ComposableMessageBundle.AttachmentPickedBundle -> { + handleAssetMessageBundle( + attachmentUri = messageBundle.attachmentUri + ) + } - is ComposableMessageBundle.SendTextMessageBundle -> { - with(messageBundle) { - sendTextMessage( - conversationId = conversationId, - text = message, - mentions = mentions.map { it.intoMessageMention() }, - quotedMessageId = quotedMessageId - ) - } - sendTypingEvent(conversationId, TypingIndicatorMode.STOPPED) - } + is ComposableMessageBundle.AudioMessageBundle -> { + handleAssetMessageBundle( + attachmentUri = messageBundle.attachmentUri, + audioPath = messageBundle.attachmentUri.uri.path?.toPath() + ) + } - Ping -> { - pingRinger.ping(R.raw.ping_from_me, isReceivingPing = false) - sendKnockUseCase(conversationId = conversationId, hotKnock = false) + is ComposableMessageBundle.SendTextMessageBundle -> { + with(messageBundle) { + sendTextMessage( + conversationId = conversationId, + text = message, + mentions = mentions.map { it.intoMessageMention() }, + quotedMessageId = quotedMessageId + ) } + sendTypingEvent(conversationId, TypingIndicatorMode.STOPPED) + } + + Ping -> { + pingRinger.ping(R.raw.ping_from_me, isReceivingPing = false) + sendKnockUseCase(conversationId = conversationId, hotKnock = false) } } } @@ -473,16 +477,34 @@ class MessageComposerViewModel @Inject constructor( } } - fun sureAboutSendingMessage(messageBundle: MessageBundle) { - hideSureAboutSendingMessage() - sendMessage(messageBundle) + fun acceptSureAboutSendingMessage() { + (sureAboutMessagingDialogState as? SureAboutMessagingDialogState.Visible)?.let { + viewModelScope.launch { + it.markAsNotified() + trySendMessage(it.messageBundleToSend) + } + } } - fun hideSureAboutSendingMessage() { - sureAboutMessagingDialogState = SureAboutMessagingDialogState.None - viewModelScope.launch { - setUserInformedAboutVerification.invoke(conversationId) + fun dismissSureAboutSendingMessage() { + (sureAboutMessagingDialogState as? SureAboutMessagingDialogState.Visible)?.let { + viewModelScope.launch { + it.markAsNotified() + } + } + } + + private suspend fun SureAboutMessagingDialogState.markAsNotified() { + when (this) { + is SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold -> + setNotifiedAboutConversationUnderLegalHold(conversationId) + + is SureAboutMessagingDialogState.Visible.ConversationVerificationDegraded -> + setUserInformedAboutVerification(conversationId) + + SureAboutMessagingDialogState.Hidden -> { /* do nothing */ } } + sureAboutMessagingDialogState = SureAboutMessagingDialogState.Hidden } companion object { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt index 333b373b75b..11cb2ebe1e7 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt @@ -50,6 +50,9 @@ sealed class InvalidLinkDialogState { } sealed class SureAboutMessagingDialogState { - object None : SureAboutMessagingDialogState() - data class ConversationVerificationDegraded(val messageBundleToSend: MessageBundle) : SureAboutMessagingDialogState() + data object Hidden : SureAboutMessagingDialogState() + sealed class Visible(open val messageBundleToSend: MessageBundle) : SureAboutMessagingDialogState() { + data class ConversationVerificationDegraded(override val messageBundleToSend: MessageBundle) : Visible(messageBundleToSend) + data class ConversationUnderLegalHold(override val messageBundleToSend: MessageBundle) : Visible(messageBundleToSend) + } } diff --git a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectBaseDialog.kt b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectBaseDialog.kt index e8fe22f1f96..61e07c704c0 100644 --- a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectBaseDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectBaseDialog.kt @@ -17,12 +17,8 @@ */ package com.wire.android.ui.legalhold.dialog.subject -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -33,22 +29,21 @@ import com.wire.android.ui.common.WireDialogButtonType import com.wire.android.ui.legalhold.dialog.common.LearnMoreAboutLegalHoldButton import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireDimensions -import com.wire.android.ui.theme.wireTypography import com.wire.android.util.ui.PreviewMultipleThemes @Composable fun LegalHoldSubjectBaseDialog( name: String, - isConversation: Boolean, + customInfo: String? = null, + withDefaultInfo: Boolean, cancelText: String, dialogDismissed: () -> Unit, action: Pair Unit>? = null, - bottomDescriptionText: String? = null, ) { - val text = stringResource(id = R.string.legal_hold_subject_dialog_description).let { - if (isConversation) stringResource(id = R.string.legal_hold_subject_dialog_description_group) + "\n\n" + it - else it - } + val text = listOfNotNull( + customInfo, + if (withDefaultInfo) stringResource(id = R.string.legal_hold_subject_dialog_description) else null + ).joinToString("\n\n") WireDialog( title = stringResource(id = R.string.legal_hold_subject_dialog_title, name), text = text, @@ -67,19 +62,9 @@ fun LegalHoldSubjectBaseDialog( ) }, ) { - Column( - verticalArrangement = Arrangement.spacedBy(MaterialTheme.wireDimensions.dialogTextsSpacing), + LearnMoreAboutLegalHoldButton( modifier = Modifier.padding(bottom = MaterialTheme.wireDimensions.dialogTextsSpacing) - ) { - LearnMoreAboutLegalHoldButton() - if (!bottomDescriptionText.isNullOrEmpty()) { - Text( - text = bottomDescriptionText, - style = MaterialTheme.wireTypography.body01, - modifier = Modifier.fillMaxWidth() - ) - } - } + ) } } @@ -87,6 +72,6 @@ fun LegalHoldSubjectBaseDialog( @PreviewMultipleThemes fun PreviewLegalHoldSubjectBaseDialog() { WireTheme { - LegalHoldSubjectBaseDialog("username", true, "cancel", {}, Pair("send anyway", {}), "Send anyway?") + LegalHoldSubjectBaseDialog("username", null, true, "cancel", {}, Pair("send anyway", {})) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConnectionDialog.kt b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConnectionDialog.kt index 38ecafe0701..8c1f49e7765 100644 --- a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConnectionDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConnectionDialog.kt @@ -31,7 +31,7 @@ fun LegalHoldSubjectConnectionDialog( ) { LegalHoldSubjectBaseDialog( name = userName, - isConversation = false, + withDefaultInfo = true, cancelText = stringResource(id = R.string.label_cancel), dialogDismissed = dialogDismissed, action = stringResource(id = R.string.connection_label_connect) to connectClicked, diff --git a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConversationDialog.kt b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConversationDialog.kt index 9b804f13bdf..948cdb04035 100644 --- a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConversationDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectConversationDialog.kt @@ -30,7 +30,8 @@ fun LegalHoldSubjectConversationDialog( ) { LegalHoldSubjectBaseDialog( name = conversationName, - isConversation = true, + customInfo = stringResource(id = R.string.legal_hold_subject_dialog_description_group), + withDefaultInfo = true, cancelText = stringResource(id = R.string.label_close), dialogDismissed = dialogDismissed ) diff --git a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectMessageDialog.kt b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectMessageDialog.kt index e13c22c854c..610b4a8c668 100644 --- a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectMessageDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectMessageDialog.kt @@ -25,13 +25,14 @@ import com.wire.android.util.ui.PreviewMultipleThemes @Composable fun LegalHoldSubjectMessageDialog( - userName: String, + conversationName: String, dialogDismissed: () -> Unit, sendAnywayClicked: () -> Unit, ) { LegalHoldSubjectBaseDialog( - name = userName, - isConversation = true, + name = conversationName, + customInfo = stringResource(id = R.string.legal_hold_subject_dialog_description_message), + withDefaultInfo = false, cancelText = stringResource(id = R.string.label_cancel), dialogDismissed = dialogDismissed, action = stringResource(id = R.string.legal_hold_subject_dialog_send_anyway_button) to sendAnywayClicked, diff --git a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectProfileDialog.kt b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectProfileDialog.kt index f0c6581164b..de2d7e19aaf 100644 --- a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectProfileDialog.kt +++ b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/subject/LegalHoldSubjectProfileDialog.kt @@ -30,7 +30,7 @@ fun LegalHoldSubjectProfileDialog( ) { LegalHoldSubjectBaseDialog( name = userName, - isConversation = false, + withDefaultInfo = true, cancelText = stringResource(id = R.string.label_close), dialogDismissed = dialogDismissed) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9ab59c6ac09..140bc7ce505 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1325,6 +1325,7 @@ %1$s is subject to legal hold All messages, pictures, and documents will be preserved for future access. It includes deleted, edited, and self-deleting messages. At least one person in this conversation is subject to legal hold. + Do you still want to send your message? Send Anyway Subject to %1$s. legal hold diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelArrangement.kt index c96f1df6b39..eabe7690898 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelArrangement.kt @@ -45,11 +45,11 @@ import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.configuration.FileSharingStatus import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.ConversationDetails -import com.wire.kalium.logic.data.user.LegalHoldStatus import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.message.SelfDeletionTimer import com.wire.kalium.logic.data.sync.SyncState import com.wire.kalium.logic.data.user.ConnectionState +import com.wire.kalium.logic.data.user.LegalHoldStatus import com.wire.kalium.logic.data.user.OtherUser import com.wire.kalium.logic.data.user.UserAssetId import com.wire.kalium.logic.data.user.UserAvailabilityStatus @@ -65,8 +65,10 @@ import com.wire.kalium.logic.feature.conversation.InteractionAvailability import com.wire.kalium.logic.feature.conversation.IsInteractionAvailableResult import com.wire.kalium.logic.feature.conversation.MembersToMentionUseCase import com.wire.kalium.logic.feature.conversation.ObserveConversationInteractionAvailabilityUseCase +import com.wire.kalium.logic.feature.conversation.ObserveConversationUnderLegalHoldNotifiedUseCase import com.wire.kalium.logic.feature.conversation.ObserveDegradedConversationNotifiedUseCase import com.wire.kalium.logic.feature.conversation.SendTypingEventUseCase +import com.wire.kalium.logic.feature.conversation.SetNotifiedAboutConversationUnderLegalHoldUseCase import com.wire.kalium.logic.feature.conversation.SetUserInformedAboutVerificationUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationReadDateUseCase import com.wire.kalium.logic.feature.message.DeleteMessageUseCase @@ -111,6 +113,8 @@ internal class MessageComposerViewModelArrangement { coEvery { fileManager.getTempWritableImageUri(any(), any()) } returns Uri.parse("image.jpg") coEvery { setUserInformedAboutVerificationUseCase(any()) } returns Unit coEvery { observeDegradedConversationNotifiedUseCase(any()) } returns flowOf(true) + coEvery { setNotifiedAboutConversationUnderLegalHold(any()) } returns Unit + coEvery { observeConversationUnderLegalHoldNotified(any()) } returns flowOf(true) } @MockK @@ -191,6 +195,12 @@ internal class MessageComposerViewModelArrangement { @MockK lateinit var observeDegradedConversationNotifiedUseCase: ObserveDegradedConversationNotifiedUseCase + @MockK + lateinit var setNotifiedAboutConversationUnderLegalHold: SetNotifiedAboutConversationUnderLegalHoldUseCase + + @MockK + lateinit var observeConversationUnderLegalHoldNotified: ObserveConversationUnderLegalHoldNotifiedUseCase + private val fakeKaliumFileSystem = FakeKaliumFileSystem() private val viewModel by lazy { @@ -218,7 +228,9 @@ internal class MessageComposerViewModelArrangement { retryFailedMessage = retryFailedMessageUseCase, sendTypingEvent = sendTypingEvent, setUserInformedAboutVerification = setUserInformedAboutVerificationUseCase, - observeDegradedConversationNotified = observeDegradedConversationNotifiedUseCase + observeDegradedConversationNotified = observeDegradedConversationNotifiedUseCase, + setNotifiedAboutConversationUnderLegalHold = setNotifiedAboutConversationUnderLegalHold, + observeConversationUnderLegalHoldNotified = observeConversationUnderLegalHoldNotified, ) } @@ -318,6 +330,10 @@ internal class MessageComposerViewModelArrangement { coEvery { observeDegradedConversationNotifiedUseCase(any()) } returns flowOf(flag) } + fun withObserveConversationUnderLegalHoldNotified(flag: Boolean) = apply { + coEvery { observeConversationUnderLegalHoldNotified(any()) } returns flowOf(flag) + } + fun arrange() = this to viewModel } diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelTest.kt index 0ca72bf6c09..362a36dbd4b 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModelTest.kt @@ -37,6 +37,7 @@ import com.wire.kalium.logic.feature.asset.GetAssetSizeLimitUseCaseImpl.Companio import io.mockk.coVerify import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import okio.Path.Companion.toPath import org.amshove.kluent.internal.assertEquals @@ -673,8 +674,66 @@ class MessageComposerViewModelTest { ) } assertEquals( - SureAboutMessagingDialogState.ConversationVerificationDegraded(messageBundle), + SureAboutMessagingDialogState.Visible.ConversationVerificationDegraded(messageBundle), viewModel.sureAboutMessagingDialogState ) } + + @Test + fun `given that user needs to be informed about enabled legal hold, when invoked sending, then message is not sent and dialog shown`() = + runTest { + // given + val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList()) + val (arrangement, viewModel) = MessageComposerViewModelArrangement() + .withSuccessfulViewModelInit() + .withObserveConversationUnderLegalHoldNotified(false) + .arrange() + // when + viewModel.trySendMessage(messageBundle) + // then + coVerify(exactly = 0) { arrangement.sendTextMessage.invoke(any(), any(), any(), any()) } + assertEquals( + SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold(messageBundle), + viewModel.sureAboutMessagingDialogState + ) + } + + @Test + fun `given that user chose to dismiss when enabled legal hold, when invoked sending, then message is not sent and dialog hidden`() = + runTest { + // given + val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList()) + val (arrangement, viewModel) = MessageComposerViewModelArrangement() + .withSuccessfulViewModelInit() + .withObserveConversationUnderLegalHoldNotified(false) + .arrange() + viewModel.trySendMessage(messageBundle) + // when + arrangement.withObserveConversationUnderLegalHoldNotified(true) + viewModel.dismissSureAboutSendingMessage() + advanceUntilIdle() + // then + coVerify(exactly = 0) { arrangement.sendTextMessage.invoke(any(), any(), any(), any()) } + assertEquals(SureAboutMessagingDialogState.Hidden, viewModel.sureAboutMessagingDialogState) + } + + @Test + fun `given that user chose to send anyway when enabled legal hold, when invoked sending, then message is sent and dialog hidden`() = + runTest { + // given + val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList()) + val (arrangement, viewModel) = MessageComposerViewModelArrangement() + .withSuccessfulViewModelInit() + .withObserveConversationUnderLegalHoldNotified(false) + .withSuccessfulSendTextMessage() + .arrange() + viewModel.trySendMessage(messageBundle) + // when + arrangement.withObserveConversationUnderLegalHoldNotified(true) + viewModel.acceptSureAboutSendingMessage() + advanceUntilIdle() + // then + coVerify(exactly = 1) { arrangement.sendTextMessage.invoke(any(), any(), any(), any()) } + assertEquals(SureAboutMessagingDialogState.Hidden, viewModel.sureAboutMessagingDialogState) + } } diff --git a/kalium b/kalium index edccb70dd6a..3a21d0976fc 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit edccb70dd6a8f8ff7d174d148d7dd32307d8ebeb +Subproject commit 3a21d0976fcc39fe6601aba12c531776148add93