Skip to content

Commit

Permalink
feat(mls): navigate to migrated conversation [WPB-4705] (#2317)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitorhugods authored Oct 12, 2023
1 parent 080ef10 commit 36b5ef6
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 3 deletions.
2 changes: 1 addition & 1 deletion app/src/main/kotlin/com/wire/android/WireApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class WireApplication : Application(), Configuration.Provider {
.detectDiskReads()
.detectDiskWrites()
.penaltyLog()
.penaltyDeath()
// .penaltyDeath()
.build()
)
StrictMode.setVmPolicy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,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 @@ -75,6 +76,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 @@ -95,6 +97,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 @@ -151,6 +154,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 @@ -177,6 +181,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()
}
}
2 changes: 1 addition & 1 deletion kalium
Submodule kalium updated 61 files
+59 βˆ’14 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/connection/ConnectionRepository.kt
+1 βˆ’1 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationGroupRepository.kt
+2 βˆ’1 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt
+1 βˆ’1 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt
+2 βˆ’2 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/Event.kt
+2 βˆ’4 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt
+1 βˆ’3 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventRepository.kt
+23 βˆ’10 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/team/TeamRepository.kt
+61 βˆ’14 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserMapper.kt
+13 βˆ’14 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt
+2 βˆ’3 logic/src/commonMain/kotlin/com/wire/kalium/logic/di/MapperProvider.kt
+6 βˆ’4 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt
+2 βˆ’4 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/connection/ConnectionScope.kt
+12 βˆ’16 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/connection/SendConnectionRequestUseCase.kt
+1 βˆ’1 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/RenameConversationUseCase.kt
+5 βˆ’4 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandler.kt
+23 βˆ’28 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt
+9 βˆ’12 logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt
+49 βˆ’22 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/connection/ConnectionRepositoryTest.kt
+1 βˆ’2 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/EventRepositoryTest.kt
+1 βˆ’1 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/reaction/ReactionRepositoryTest.kt
+2 βˆ’2 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/team/TeamRepositoryTest.kt
+51 βˆ’0 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/user/UserMapperTest.kt
+47 βˆ’19 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/user/UserRepositoryTest.kt
+1 βˆ’43 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/connection/SendConnectionRequestUseCaseTest.kt
+7 βˆ’62 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt
+4 βˆ’2 logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestConversation.kt
+1 βˆ’1 logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestEvent.kt
+0 βˆ’4 logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt
+2 βˆ’2 ...src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/conversation/message/ReceiptMessageHandlerTest.kt
+0 βˆ’9 logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/UserRepositoryArrangement.kt
+13 βˆ’8 logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/dao/MemberDAOArrangement.kt
+21 βˆ’1 network/src/commonMain/kotlin/com/wire/kalium/network/api/v4/authenticated/SelfApiV4.kt
+1 βˆ’19 network/src/commonMain/kotlin/com/wire/kalium/network/api/v5/authenticated/SelfApiV5.kt
+4 βˆ’4 network/src/commonTest/kotlin/com/wire/kalium/api/v4/user/self/SelfApiV4Test.kt
+1 βˆ’1 ...istence/src/androidInstrumentedTest/kotlin/com/wire/kalium/persistence/dao/message/MessageExtensionsTest.kt
+4 βˆ’0 persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq
+19 βˆ’18 persistence/src/commonMain/db_user/com/wire/kalium/persistence/Users.sq
+109 βˆ’0 persistence/src/commonMain/db_user/migrations/63.sqm
+25 βˆ’36 persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAO.kt
+121 βˆ’28 persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAOImpl.kt
+2 βˆ’1 persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationMapper.kt
+3 βˆ’1 persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationViewEntity.kt
+6 βˆ’2 persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/member/MemberDAO.kt
+3 βˆ’3 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/backup/DatabaseExporterTest.kt
+1 βˆ’1 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/backup/DatabaseImporterTest.kt
+5 βˆ’5 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/backup/UserDatabaseDataGenerator.kt
+29 βˆ’28 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/ConversationDAOTest.kt
+1 βˆ’1 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/LastMessageListTest.kt
+68 βˆ’19 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/MemberDAOTest.kt
+1 βˆ’1 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/UserClientDAOIntegrationTest.kt
+26 βˆ’3 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/UserConversationDAOIntegrationTest.kt
+39 βˆ’37 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/UserDAOTest.kt
+22 βˆ’22 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/client/ClientDAOTest.kt
+3 βˆ’3 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/message/CompositeMessageTest.kt
+2 βˆ’2 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/message/MessageDAOBenchmarkTest.kt
+20 βˆ’20 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/message/MessageDAOTest.kt
+1 βˆ’1 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/message/MessageInsertExtensionTest.kt
+1 βˆ’1 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/message/MessageMetadataDAOTest.kt
+2 βˆ’2 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/reaction/ReactionDAOTest.kt
+2 βˆ’2 persistence/src/commonTest/kotlin/com/wire/kalium/persistence/dao/receipt/ReceiptDAOTest.kt

0 comments on commit 36b5ef6

Please sign in to comment.