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 42dba9c29a2..15263096e2a 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 @@ -49,6 +49,7 @@ 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.SyncConversationCodeUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationAccessRoleUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationArchivedStatusUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationMemberRoleUseCase @@ -288,4 +289,9 @@ class ConversationModule { fun provideObserveLegalHoldWithChangeNotifiedForConversationUseCase( conversationScope: ConversationScope, ): ObserveConversationUnderLegalHoldNotifiedUseCase = conversationScope.observeConversationUnderLegalHoldNotified + + @ViewModelScoped + @Provides + fun provideSyncConversationCodeUseCase(conversationScope: ConversationScope): SyncConversationCodeUseCase = + conversationScope.syncConversationCode } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModel.kt index 1f1afab46b9..41913c7414d 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModel.kt @@ -31,6 +31,7 @@ import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase +import com.wire.kalium.logic.feature.conversation.SyncConversationCodeUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationAccessRoleUseCase import com.wire.kalium.logic.feature.conversation.guestroomlink.CanCreatePasswordProtectedLinksUseCase import com.wire.kalium.logic.feature.conversation.guestroomlink.GenerateGuestRoomLinkResult @@ -49,6 +50,8 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import javax.inject.Inject @@ -64,6 +67,7 @@ class EditGuestAccessViewModel @Inject constructor( private val observeGuestRoomLink: ObserveGuestRoomLinkUseCase, private val observeGuestRoomLinkFeatureFlag: ObserveGuestRoomLinkFeatureFlagUseCase, private val canCreatePasswordProtectedLinks: CanCreatePasswordProtectedLinksUseCase, + private val syncConversationCode: SyncConversationCodeUseCase, savedStateHandle: SavedStateHandle ) : ViewModel() { @@ -86,6 +90,20 @@ class EditGuestAccessViewModel @Inject constructor( checkIfUserCanCreatePasswordProtectedLinks() } + private val syncCodeMutex = Mutex() + + private var isGuestCodeUpdated = false + private fun updateConversationCodeOnce() { + viewModelScope.launch { + syncCodeMutex.withLock { + if (!isGuestCodeUpdated) { + syncConversationCode(conversationId) + isGuestCodeUpdated = true + } + } + } + } + private fun checkIfUserCanCreatePasswordProtectedLinks() { viewModelScope.launch { val canCreatePasswordProtectedLinks = when { @@ -123,6 +141,8 @@ class EditGuestAccessViewModel @Inject constructor( conversationDetailsFlow, isSelfAdminFlow ) { conversationDetails, isSelfAnAdmin -> + isSelfAnAdmin to conversationDetails + }.collect { (isSelfAnAdmin, conversationDetails) -> val isGuestAllowed = conversationDetails.conversation.isGuestAllowed() || conversationDetails.conversation.isNonTeamMemberAllowed() @@ -132,6 +152,8 @@ class EditGuestAccessViewModel @Inject constructor( isServicesAccessAllowed = conversationDetails.conversation.isServicesAllowed(), isUpdatingGuestAccessAllowed = isSelfAnAdmin ) + + if (isGuestAllowed) updateConversationCodeOnce() } } } diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt index e595c631061..855aabfe7fa 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt @@ -24,12 +24,14 @@ import androidx.lifecycle.SavedStateHandle import com.wire.android.config.CoroutineTestExtension import com.wire.android.config.NavigationTestExtension import com.wire.android.config.TestDispatcherProvider +import com.wire.android.ui.home.conversations.details.participants.model.ConversationParticipantsData import com.wire.android.ui.home.conversations.details.participants.usecase.ObserveParticipantsForConversationUseCase import com.wire.android.ui.navArgs import com.wire.android.ui.userprofile.other.OtherUserProfileScreenViewModelTest import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase +import com.wire.kalium.logic.feature.conversation.SyncConversationCodeUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationAccessRoleUseCase import com.wire.kalium.logic.feature.conversation.guestroomlink.CanCreatePasswordProtectedLinksUseCase import com.wire.kalium.logic.feature.conversation.guestroomlink.GenerateGuestRoomLinkResult @@ -44,7 +46,9 @@ import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.amshove.kluent.internal.assertEquals import org.junit.jupiter.api.Test @@ -62,6 +66,7 @@ class EditGuestAccessViewModelTest { val (arrangement, editGuestAccessViewModel) = Arrangement() .withUpdateConversationAccessRoleResult(UpdateConversationAccessRoleUseCase.Result.Success) .arrange() + advanceUntilIdle() // when editGuestAccessViewModel.updateGuestAccess(true) @@ -72,34 +77,39 @@ class EditGuestAccessViewModelTest { } @Test - fun `given a failure when running updateConversationAccessRole, when trying to enable guest access, then do not enable guest access`() { - // given - val (arrangement, editGuestAccessViewModel) = Arrangement() - .withUpdateConversationAccessRoleResult( - UpdateConversationAccessRoleUseCase.Result.Failure(CoreFailure.MissingClientRegistration) - ).arrange() + fun `given a failure when running updateConversationAccessRole, when trying to enable guest access, then do not enable guest access`() = + runTest { + // given + val (arrangement, editGuestAccessViewModel) = Arrangement() + .withUpdateConversationAccessRoleResult( + UpdateConversationAccessRoleUseCase.Result.Failure(CoreFailure.MissingClientRegistration) + ).arrange() + advanceUntilIdle() - // when - editGuestAccessViewModel.updateGuestAccess(true) + // when + editGuestAccessViewModel.updateGuestAccess(true) - // then - coVerify(exactly = 1) { arrangement.updateConversationAccessRole(any(), any(), any()) } - assertEquals(false, editGuestAccessViewModel.editGuestAccessState.isGuestAccessAllowed) - } + // then + coVerify(exactly = 1) { arrangement.updateConversationAccessRole(any(), any(), any()) } + assertEquals(false, editGuestAccessViewModel.editGuestAccessState.isGuestAccessAllowed) + } @Test - fun `given guest access is activated, when trying to disable guest access, then display dialog before disabling guest access`() { - // given - val (arrangement, editGuestAccessViewModel) = Arrangement() - .withUpdateConversationAccessRoleResult(UpdateConversationAccessRoleUseCase.Result.Success).arrange() + fun `given guest access is activated, when trying to disable guest access, then display dialog before disabling guest access`() = + runTest { + // given + val (arrangement, editGuestAccessViewModel) = Arrangement() + .withUpdateConversationAccessRoleResult(UpdateConversationAccessRoleUseCase.Result.Success) + .arrange() + advanceUntilIdle() - // when - editGuestAccessViewModel.updateGuestAccess(false) + // when + editGuestAccessViewModel.updateGuestAccess(false) - // then - coVerify(inverse = true) { arrangement.updateConversationAccessRoleUseCase(any(), any(), any()) } - assertEquals(true, editGuestAccessViewModel.editGuestAccessState.shouldShowGuestAccessChangeConfirmationDialog) - } + // then + coVerify(inverse = true) { arrangement.updateConversationAccessRoleUseCase(any(), any(), any()) } + assertEquals(true, editGuestAccessViewModel.editGuestAccessState.shouldShowGuestAccessChangeConfirmationDialog) + } @Test fun `given useCase runs with success, when_generating guest link, then invoke it once`() = runTest { @@ -107,6 +117,7 @@ class EditGuestAccessViewModelTest { val (arrangement, editGuestAccessViewModel) = Arrangement() .withGenerateGuestRoomResult(GenerateGuestRoomLinkResult.Success) .arrange() + advanceUntilIdle() // when editGuestAccessViewModel.onRequestGuestRoomLink() @@ -123,6 +134,7 @@ class EditGuestAccessViewModelTest { .withGenerateGuestRoomResult( GenerateGuestRoomLinkResult.Failure(NetworkFailure.NoNetworkConnection(RuntimeException("no network"))) ).arrange() + advanceUntilIdle() // when editGuestAccessViewModel.onRequestGuestRoomLink() @@ -138,6 +150,7 @@ class EditGuestAccessViewModelTest { val (arrangement, editGuestAccessViewModel) = Arrangement() .withRevokeGuestRoomLinkResult(RevokeGuestRoomLinkResult.Success) .arrange() + advanceUntilIdle() // when editGuestAccessViewModel.removeGuestLink() @@ -153,6 +166,7 @@ class EditGuestAccessViewModelTest { val (arrangement, editGuestAccessViewModel) = Arrangement() .withRevokeGuestRoomLinkResult(RevokeGuestRoomLinkResult.Failure(CoreFailure.MissingClientRegistration)) .arrange() + advanceUntilIdle() // when editGuestAccessViewModel.removeGuestLink() @@ -170,6 +184,7 @@ class EditGuestAccessViewModelTest { val (arrangement, editGuestAccessViewModel) = Arrangement() .withUpdateConversationAccessRoleResult(UpdateConversationAccessRoleUseCase.Result.Success) .arrange() + advanceUntilIdle() // when editGuestAccessViewModel.onGuestDialogConfirm() @@ -188,6 +203,7 @@ class EditGuestAccessViewModelTest { UpdateConversationAccessRoleUseCase.Result.Failure(CoreFailure.MissingClientRegistration) ) .arrange() + advanceUntilIdle() // when editGuestAccessViewModel.onGuestDialogConfirm() @@ -228,6 +244,9 @@ class EditGuestAccessViewModelTest { @MockK lateinit var canCreatePasswordProtectedLinks: CanCreatePasswordProtectedLinksUseCase + @MockK + lateinit var syncConversationCodeUseCase: SyncConversationCodeUseCase + val editGuestAccessViewModel: EditGuestAccessViewModel by lazy { EditGuestAccessViewModel( savedStateHandle = savedStateHandle, @@ -239,7 +258,8 @@ class EditGuestAccessViewModelTest { revokeGuestRoomLink = revokeGuestRoomLink, observeGuestRoomLinkFeatureFlag = observeGuestRoomLinkFeatureFlag, canCreatePasswordProtectedLinks = canCreatePasswordProtectedLinks, - dispatcher = TestDispatcherProvider() + dispatcher = TestDispatcherProvider(), + syncConversationCode = syncConversationCodeUseCase ) } @@ -260,6 +280,18 @@ class EditGuestAccessViewModelTest { coEvery { canCreatePasswordProtectedLinks() } returns true } + fun withSyncConversationCodeSuccess() = apply { + coEvery { syncConversationCodeUseCase.invoke(any()) } + } + + fun withConversationMembers(result: Flow) = apply { + coEvery { observeConversationMembers(any()) } returns result + } + + fun withConversationDetails(result: Flow) = apply { + coEvery { observeConversationDetails(any()) } returns result + } + fun withRevokeGuestRoomLinkResult(result: RevokeGuestRoomLinkResult) = apply { coEvery { revokeGuestRoomLink(any()) } returns result } diff --git a/kalium b/kalium index 4be820aa2bb..f9480981c8e 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 4be820aa2bb05ffc40a8ec1709660b16cbe6bfdd +Subproject commit f9480981c8ec5b1c3004c450449b2e2449335ebe