Skip to content

Commit

Permalink
feat(mls): navigate to migrated conversation [WPB-4705] (#2327)
Browse files Browse the repository at this point in the history
Co-authored-by: Vitor Hugo Schwaab <[email protected]>
  • Loading branch information
typfel and vitorhugods committed Oct 15, 2023
1 parent 73def3a commit 87bc79f
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import com.wire.android.R
import com.wire.android.appLogger
import com.wire.android.media.audiomessage.AudioState
import com.wire.android.model.SnackBarMessage
import com.wire.android.navigation.BackStackMode
import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.Navigator
import com.wire.android.ui.calling.common.MicrophoneBTPermissionsDeniedDialog
Expand All @@ -77,6 +78,7 @@ import com.wire.android.ui.common.dialogs.calling.JoinAnywayDialog
import com.wire.android.ui.common.dialogs.calling.OngoingActiveCallDialog
import com.wire.android.ui.common.error.CoreFailureErrorDialog
import com.wire.android.ui.common.snackbar.LocalSnackbarHostState
import com.wire.android.ui.destinations.ConversationScreenDestination
import com.wire.android.ui.destinations.GroupConversationDetailsScreenDestination
import com.wire.android.ui.destinations.InitiatingCallScreenDestination
import com.wire.android.ui.destinations.MediaGalleryScreenDestination
Expand All @@ -97,6 +99,7 @@ import com.wire.android.ui.home.conversations.info.ConversationInfoViewModel
import com.wire.android.ui.home.conversations.info.ConversationInfoViewState
import com.wire.android.ui.home.conversations.messages.ConversationMessagesViewModel
import com.wire.android.ui.home.conversations.messages.ConversationMessagesViewState
import com.wire.android.ui.home.conversations.migration.ConversationMigrationViewModel
import com.wire.android.ui.home.conversations.model.ExpirationStatus
import com.wire.android.ui.home.conversations.model.UIMessage
import com.wire.android.ui.home.conversations.selfdeletion.SelfDeletionMapper.toSelfDeletionDuration
Expand Down Expand Up @@ -154,6 +157,7 @@ fun ConversationScreen(
conversationCallViewModel: ConversationCallViewModel = hiltViewModel(),
conversationMessagesViewModel: ConversationMessagesViewModel = hiltViewModel(),
messageComposerViewModel: MessageComposerViewModel = hiltViewModel(),
conversationMigrationViewModel: ConversationMigrationViewModel = hiltViewModel(),
groupDetailsScreenResultRecipient: ResultRecipient<GroupConversationDetailsScreenDestination, GroupConversationDetailsNavBackArgs>,
mediaGalleryScreenResultRecipient: ResultRecipient<MediaGalleryScreenDestination, MediaGalleryNavBackArgs>,
resultNavigator: ResultBackNavigator<GroupConversationDetailsNavBackArgs>,
Expand All @@ -180,6 +184,15 @@ fun ConversationScreen(
}
val context = LocalContext.current

conversationMigrationViewModel.migratedConversationId?.let { migratedConversationId ->
navigator.navigate(
NavigationCommand(
ConversationScreenDestination(migratedConversationId),
BackStackMode.REMOVE_CURRENT
)
)
}

with(conversationCallViewModel) {
if (conversationCallViewState.shouldShowJoinAnywayDialog) {
appLogger.i("showing showJoinAnywayDialog..")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import kotlinx.coroutines.launch
import javax.inject.Inject

@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("LongParameterList")
@HiltViewModel
class ConversationBannerViewModel @Inject constructor(
override val savedStateHandle: SavedStateHandle,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.home.conversations.migration

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.wire.android.navigation.SavedStateViewModel
import com.wire.android.ui.home.conversations.ConversationNavArgs
import com.wire.android.ui.navArgs
import com.wire.kalium.logic.data.conversation.ConversationDetails
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class ConversationMigrationViewModel @Inject constructor(
override val savedStateHandle: SavedStateHandle,
private val observeConversationDetails: ObserveConversationDetailsUseCase
) : SavedStateViewModel(savedStateHandle) {

/**
* Represents the target conversation, after a conversation migration.
* The target conversation is the active one-on-one conversation ID if the current conversation
* is migrated to a different conversation.
* If this conversation was not migrated to another one, the target conversation is null.
*/
var migratedConversationId by mutableStateOf<ConversationId?>(null)
private set

private val conversationNavArgs = savedStateHandle.navArgs<ConversationNavArgs>()
private val conversationId: QualifiedID = conversationNavArgs.conversationId

init {
viewModelScope.launch {
observeConversationDetails(conversationId)
.filterIsInstance<ObserveConversationDetailsUseCase.Result.Success>()
.map { it.conversationDetails }
.filterIsInstance<ConversationDetails.OneOne>()
.collectLatest {
val activeOneOnOneConversationId = it.otherUser.activeOneOnOneConversationId
val wasThisConversationMigrated = activeOneOnOneConversationId != conversationId
if (wasThisConversationMigrated) {
migratedConversationId = activeOneOnOneConversationId
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.home.conversations.migration

import androidx.lifecycle.SavedStateHandle
import com.wire.android.config.CoroutineTestExtension
import com.wire.android.config.NavigationTestExtension
import com.wire.android.framework.TestConversation
import com.wire.android.framework.TestUser
import com.wire.android.ui.home.conversations.ConversationNavArgs
import com.wire.android.ui.navArgs
import com.wire.kalium.logic.data.conversation.ConversationDetails
import com.wire.kalium.logic.data.conversation.LegalHoldStatus
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.type.UserType
import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBe
import org.amshove.kluent.shouldBeEqualTo
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith

@ExtendWith(CoroutineTestExtension::class)
@ExtendWith(NavigationTestExtension::class)
class ConversationMigrationViewModelTest {

@Test
fun givenActiveOneOnOneMatchesCurrentConversation_thenMigratedConversationShouldBeNull() = runTest {
val (_, conversationMigrationViewModel) = arrange {
withConversationDetailsReturning(
ConversationDetails.OneOne(
conversation = TestConversation.ONE_ON_ONE,
otherUser = TestUser.OTHER_USER.copy(activeOneOnOneConversationId = conversationId),
legalHoldStatus = LegalHoldStatus.ENABLED,
userType = UserType.NONE,
unreadEventCount = mapOf(),
lastMessage = null
)
)
}

conversationMigrationViewModel.migratedConversationId shouldBe null
}

@Test
fun givenActiveOneOnOneDiffersFromCurrentConversation_thenMigratedConversationShouldBeTheOneInDetails() = runTest {
val expectedActiveOneOnOneId = ConversationId("expectedActiveOneOnOneId", "testDomain")
val (_, conversationMigrationViewModel) = arrange {
withConversationDetailsReturning(
ConversationDetails.OneOne(
conversation = TestConversation.ONE_ON_ONE,
otherUser = TestUser.OTHER_USER.copy(activeOneOnOneConversationId = expectedActiveOneOnOneId),
legalHoldStatus = LegalHoldStatus.ENABLED,
userType = UserType.NONE,
unreadEventCount = mapOf(),
lastMessage = null
)
)
}

conversationMigrationViewModel.migratedConversationId shouldBeEqualTo expectedActiveOneOnOneId
}

private class Arrangement(private val configure: Arrangement.() -> Unit) {

@MockK
lateinit var observeConversationDetailsUseCase: ObserveConversationDetailsUseCase

@MockK
lateinit var savedStateHandle: SavedStateHandle

init {
MockKAnnotations.init(this)
every { savedStateHandle.navArgs<ConversationNavArgs>() } returns ConversationNavArgs(conversationId)
}

fun withConversationDetailsReturning(conversationDetails: ConversationDetails) = apply {
coEvery { observeConversationDetailsUseCase(conversationId) } returns
flowOf(ObserveConversationDetailsUseCase.Result.Success(conversationDetails))
}

fun arrange(): Pair<Arrangement, ConversationMigrationViewModel> = run {
configure()
this@Arrangement to ConversationMigrationViewModel(savedStateHandle, observeConversationDetailsUseCase)
}
}

private companion object {
val conversationId = TestConversation.ID

fun arrange(configure: Arrangement.() -> Unit) = Arrangement(configure).arrange()
}
}

0 comments on commit 87bc79f

Please sign in to comment.