Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: discover legal hold when sending message [WPB-5999] #2558

Merged
merged 5 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@
import com.wire.android.util.ImageUtil
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.android.util.getAudioLengthInMs
import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.configuration.FileSharingStatus
import com.wire.kalium.logic.data.asset.AttachmentType
import com.wire.kalium.logic.data.asset.KaliumFileSystem
import com.wire.kalium.logic.data.conversation.Conversation.TypingIndicatorMode
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.message.SelfDeletionTimer
import com.wire.kalium.logic.data.user.OtherUser
import com.wire.kalium.logic.failure.LegalHoldEnabledForConversationFailure
import com.wire.kalium.logic.feature.asset.GetAssetSizeLimitUseCase
import com.wire.kalium.logic.feature.asset.ScheduleNewAssetMessageResult
import com.wire.kalium.logic.feature.asset.ScheduleNewAssetMessageUseCase
import com.wire.kalium.logic.feature.conversation.InteractionAvailability
import com.wire.kalium.logic.feature.conversation.IsInteractionAvailableResult
Expand All @@ -75,6 +78,7 @@
import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTimerSettingsForConversationUseCase
import com.wire.kalium.logic.feature.selfDeletingMessages.PersistNewSelfDeletionTimerUseCase
import com.wire.kalium.logic.feature.user.IsFileSharingEnabledUseCase
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.onFailure
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
Expand Down Expand Up @@ -216,7 +220,8 @@
shouldInformAboutDegradedBeforeSendingMessage() ->
sureAboutMessagingDialogState = SureAboutMessagingDialogState.Visible.ConversationVerificationDegraded(messageBundle)
shouldInformAboutUnderLegalHoldBeforeSendingMessage() ->
sureAboutMessagingDialogState = SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold(messageBundle)
sureAboutMessagingDialogState =
SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold.BeforeSending(messageBundle)
else -> sendMessage(messageBundle)
}
}
Expand All @@ -231,7 +236,7 @@
originalMessageId = originalMessageId,
text = newContent,
mentions = newMentions.map { it.intoMessageMention() },
)
).handleLegalHoldFailureAfterSendingMessage()
}
sendTypingEvent(conversationId, TypingIndicatorMode.STOPPED)
}
Expand All @@ -256,7 +261,7 @@
text = message,
mentions = mentions.map { it.intoMessageMention() },
quotedMessageId = quotedMessageId
)
).handleLegalHoldFailureAfterSendingMessage()
}
sendTypingEvent(conversationId, TypingIndicatorMode.STOPPED)
}
Expand Down Expand Up @@ -335,7 +340,7 @@
assetDataSize = dataSize,
assetMimeType = mimeType,
audioLengthInMs = 0L
)
).handleLegalHoldFailureAfterSendingMessage()
}

AttachmentType.VIDEO,
Expand All @@ -354,7 +359,7 @@
dataPath = dataPath,
mimeType = mimeType
)
)
).handleLegalHoldFailureAfterSendingMessage()
} catch (e: OutOfMemoryError) {
appLogger.e("There was an OutOfMemory error while uploading the asset")
onSnackbarMessage(ConversationSnackbarMessages.ErrorSendingAsset)
Expand All @@ -366,6 +371,19 @@
}
}

private fun CoreFailure.handleLegalHoldFailureAfterSendingMessage() = also {
if (this is LegalHoldEnabledForConversationFailure) {
sureAboutMessagingDialogState = SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold.AfterSending(this.messageId)
}
}
private fun Either<CoreFailure, Unit>.handleLegalHoldFailureAfterSendingMessage() =
onFailure { it.handleLegalHoldFailureAfterSendingMessage() }
private fun ScheduleNewAssetMessageResult.handleLegalHoldFailureAfterSendingMessage() = also {
if (it is ScheduleNewAssetMessageResult.Failure) {
it.coreFailure.handleLegalHoldFailureAfterSendingMessage()

Check warning on line 383 in app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt#L383

Added line #L383 was not covered by tests
}
}

fun retrySendingMessage(messageId: String) {
viewModelScope.launch {
retryFailedMessage(messageId = messageId, conversationId = conversationId)
Expand Down Expand Up @@ -481,7 +499,14 @@
(sureAboutMessagingDialogState as? SureAboutMessagingDialogState.Visible)?.let {
viewModelScope.launch {
it.markAsNotified()
trySendMessage(it.messageBundleToSend)
when (it) {
is SureAboutMessagingDialogState.Visible.ConversationVerificationDegraded ->
trySendMessage(it.messageBundleToSend)

Check warning on line 504 in app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewModel.kt#L504

Added line #L504 was not covered by tests
is SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold.BeforeSending ->
trySendMessage(it.messageBundleToSend)
is SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold.AfterSending ->
retrySendingMessage(it.messageId)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package com.wire.android.ui.home.conversations
import com.wire.android.ui.home.messagecomposer.state.MessageBundle
import com.wire.android.ui.home.newconversation.model.Contact
import com.wire.kalium.logic.data.asset.AttachmentType
import com.wire.kalium.logic.data.id.MessageId
import com.wire.kalium.logic.data.message.SelfDeletionTimer
import com.wire.kalium.logic.feature.conversation.InteractionAvailability
import kotlin.time.Duration.Companion.ZERO
Expand Down Expand Up @@ -51,8 +52,11 @@ sealed class InvalidLinkDialogState {

sealed class 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)
sealed class Visible : SureAboutMessagingDialogState() {
data class ConversationVerificationDegraded(val messageBundleToSend: MessageBundle) : Visible()
sealed class ConversationUnderLegalHold : Visible() {
data class BeforeSending(val messageBundleToSend: MessageBundle) : ConversationUnderLegalHold()
data class AfterSending(val messageId: MessageId) : ConversationUnderLegalHold()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@ internal class MessageComposerViewModelArrangement {
)
} returns Either.Right(Unit)
}
fun withFailedSendTextMessage(failure: CoreFailure) = apply {
coEvery {
sendTextMessage(
any(),
any(),
any(),
any()
)
} returns Either.Left(failure)
}

fun withSuccessfulSendEditTextMessage() = apply {
coEvery {
Expand Down Expand Up @@ -334,6 +344,10 @@ internal class MessageComposerViewModelArrangement {
coEvery { observeConversationUnderLegalHoldNotified(any()) } returns flowOf(flag)
}

fun withSuccessfulRetryFailedMessage() = apply {
coEvery { retryFailedMessageUseCase(any(), any()) } returns Either.Right(Unit)
}

fun arrange() = this to viewModel
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.wire.android.ui.home.messagecomposer.state.Ping
import com.wire.kalium.logic.data.asset.AttachmentType
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.message.SelfDeletionTimer
import com.wire.kalium.logic.failure.LegalHoldEnabledForConversationFailure
import com.wire.kalium.logic.feature.asset.GetAssetSizeLimitUseCaseImpl.Companion.ASSET_SIZE_DEFAULT_LIMIT_BYTES
import io.mockk.coVerify
import io.mockk.verify
Expand Down Expand Up @@ -680,7 +681,7 @@ class MessageComposerViewModelTest {
}

@Test
fun `given that user needs to be informed about enabled legal hold, when invoked sending, then message is not sent and dialog shown`() =
fun `given that user needs to be informed about enabled legal hold when sending, then message is not sent and dialog shown`() =
runTest {
// given
val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList())
Expand All @@ -693,13 +694,13 @@ class MessageComposerViewModelTest {
// then
coVerify(exactly = 0) { arrangement.sendTextMessage.invoke(any(), any(), any(), any()) }
assertEquals(
SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold(messageBundle),
SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold.BeforeSending(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`() =
fun `given that user chose to dismiss when enabled legal hold before sending, then message is not sent and dialog hidden`() =
runTest {
// given
val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList())
Expand All @@ -718,7 +719,7 @@ class MessageComposerViewModelTest {
}

@Test
fun `given that user chose to send anyway when enabled legal hold, when invoked sending, then message is sent and dialog hidden`() =
fun `given that user chose to send anyway when enabled legal hold before sending, then message is sent and dialog hidden`() =
runTest {
// given
val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList())
Expand All @@ -736,4 +737,64 @@ class MessageComposerViewModelTest {
coVerify(exactly = 1) { arrangement.sendTextMessage.invoke(any(), any(), any(), any()) }
assertEquals(SureAboutMessagingDialogState.Hidden, viewModel.sureAboutMessagingDialogState)
}

@Test
fun `given that user needs to be informed about enabled legal hold when sending fails, then message is not resent and dialog shown`() =
runTest {
// given
val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList())
val messageId = "messageId"
val (arrangement, viewModel) = MessageComposerViewModelArrangement()
.withSuccessfulViewModelInit()
.withFailedSendTextMessage(LegalHoldEnabledForConversationFailure(messageId))
.arrange()
// when
viewModel.trySendMessage(messageBundle)
// then
coVerify(exactly = 0) { arrangement.retryFailedMessageUseCase.invoke(eq(messageId), any()) }
assertEquals(
SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold.AfterSending(messageId),
viewModel.sureAboutMessagingDialogState
)
}

@Test
fun `given that user chose to dismiss when enabled legal hold when sending fails, then message is not resent and dialog hidden`() =
runTest {
// given
val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList())
val messageId = "messageId"
val (arrangement, viewModel) = MessageComposerViewModelArrangement()
.withSuccessfulViewModelInit()
.withObserveConversationUnderLegalHoldNotified(true)
.withFailedSendTextMessage(LegalHoldEnabledForConversationFailure(messageId))
.arrange()
viewModel.trySendMessage(messageBundle)
// when
viewModel.dismissSureAboutSendingMessage()
advanceUntilIdle()
// then
coVerify(exactly = 0) { arrangement.retryFailedMessageUseCase.invoke(any(), any()) }
assertEquals(SureAboutMessagingDialogState.Hidden, viewModel.sureAboutMessagingDialogState)
}

@Test
fun `given that user chose to send anyway when enabled legal hold when sending fails, then message is resent and dialog hidden`() =
runTest {
// given
val messageBundle = ComposableMessageBundle.SendTextMessageBundle("mocked-text-message", emptyList())
val messageId = "messageId"
val (arrangement, viewModel) = MessageComposerViewModelArrangement()
.withSuccessfulViewModelInit()
.withFailedSendTextMessage(LegalHoldEnabledForConversationFailure(messageId))
.withSuccessfulRetryFailedMessage()
.arrange()
viewModel.trySendMessage(messageBundle)
// when
viewModel.acceptSureAboutSendingMessage()
advanceUntilIdle()
// then
coVerify(exactly = 1) { arrangement.retryFailedMessageUseCase.invoke(eq(messageId), any()) }
assertEquals(SureAboutMessagingDialogState.Hidden, viewModel.sureAboutMessagingDialogState)
}
}
2 changes: 1 addition & 1 deletion kalium
Submodule kalium updated 17 files
+24 −0 logic/src/commonMain/kotlin/com/wire/kalium/logic/failure/LegalHoldEnabledForConversationFailure.kt
+9 −3 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt
+8 −2 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt
+6 −0 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/message/MessageScope.kt
+13 −1 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/message/MessageSendFailureHandler.kt
+31 −7 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/message/MessageSender.kt
+6 −12 logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/conversation/message/NewMessageEventHandler.kt
+147 −2 logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt
+47 −43 ...c/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldSystemMessagesHandler.kt
+58 −0 logic/src/commonMain/kotlin/com/wire/kalium/logic/util/TriggerBuffer.kt
+76 −16 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/prekey/MessageSendFailureHandlerTest.kt
+145 −0 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/message/MessageSenderTest.kt
+28 −29 ...rc/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/conversation/message/NewMessageEventHandlerTest.kt
+451 −8 logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt
+25 −180 ...ommonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldSystemMessageHandlerTest.kt
+97 −0 logic/src/commonTest/kotlin/com/wire/kalium/logic/util/TriggerBufferTest.kt
+15 −0 logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/ClientRepositoryArrangement.kt