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 2989f44d9a9..a4faa42117c 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 @@ -28,9 +28,12 @@ import com.wire.android.BuildConfig import com.wire.android.ui.home.conversations.details.participants.usecase.ObserveParticipantsForConversationUseCase import com.wire.android.ui.navArgs import com.wire.android.util.dispatchers.DispatcherProvider +import com.wire.kalium.common.functional.onSuccess import com.wire.kalium.logic.data.conversation.Conversation +import com.wire.kalium.logic.data.conversation.ConversationDetails import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.data.user.SupportedProtocol +import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase import com.wire.kalium.logic.feature.conversation.SyncConversationCodeUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationAccessRoleUseCase @@ -41,8 +44,8 @@ import com.wire.kalium.logic.feature.conversation.guestroomlink.ObserveGuestRoom import com.wire.kalium.logic.feature.conversation.guestroomlink.RevokeGuestRoomLinkResult import com.wire.kalium.logic.feature.conversation.guestroomlink.RevokeGuestRoomLinkUseCase import com.wire.kalium.logic.feature.user.GetDefaultProtocolUseCase +import com.wire.kalium.logic.feature.user.ObserveSelfUserUseCase import com.wire.kalium.logic.feature.user.guestroomlink.ObserveGuestRoomLinkFeatureFlagUseCase -import com.wire.kalium.common.functional.onSuccess import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -71,6 +74,7 @@ class EditGuestAccessViewModel @Inject constructor( private val canCreatePasswordProtectedLinks: CanCreatePasswordProtectedLinksUseCase, private val syncConversationCode: SyncConversationCodeUseCase, private val getDefaultProtocol: GetDefaultProtocolUseCase, + private val selfUser: ObserveSelfUserUseCase, savedStateHandle: SavedStateHandle ) : ViewModel() { @@ -142,13 +146,19 @@ class EditGuestAccessViewModel @Inject constructor( val isMLSTeam = getDefaultProtocol() == SupportedProtocol.MLS + // TODO(refactor): Move all this logic to a UseCase combine( conversationDetailsFlow, - isSelfAdminFlow - ) { conversationDetails, isSelfAnAdmin -> - isSelfAnAdmin to conversationDetails - }.collect { (isSelfAnAdmin, conversationDetails) -> - + isSelfAdminFlow, + selfUser() + ) { conversationDetails, isSelfAnAdmin, selfUser -> + Triple(isSelfAnAdmin, conversationDetails, selfUser) + }.collect { (isSelfAnAdmin, conversationDetails, selfUser) -> + val isTeamAdmin = selfUser.userType in setOf(UserType.ADMIN, UserType.OWNER) + val isSelfInConversationTeam = selfUser.teamId == conversationDetails.conversation.teamId + val isSelfChannelTeamAdmin = + (conversationDetails is ConversationDetails.Group.Channel && isTeamAdmin && isSelfInConversationTeam) + val canSelfPerformAdminActions = isSelfAnAdmin || isSelfChannelTeamAdmin val isGuestAllowed = conversationDetails.conversation.isGuestAllowed() || conversationDetails.conversation.isNonTeamMemberAllowed() val isMLSConversation = conversationDetails.conversation.protocol is Conversation.ProtocolInfo.MLS @@ -156,7 +166,7 @@ class EditGuestAccessViewModel @Inject constructor( editGuestAccessState = editGuestAccessState.copy( isGuestAccessAllowed = isGuestAllowed, isServicesAccessAllowed = conversationDetails.conversation.isServicesAllowed() && !isMLSTeam && !isMLSConversation, - isUpdatingGuestAccessAllowed = isSelfAnAdmin + isUpdatingGuestAccessAllowed = canSelfPerformAdminActions ) if (isGuestAllowed) updateConversationCodeOnce() diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModel.kt index 2b1d489656a..751e746ea29 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModel.kt @@ -30,9 +30,13 @@ import com.wire.android.ui.home.conversations.selfdeletion.SelfDeletionMapper.to import com.wire.android.ui.home.messagecomposer.SelfDeletionDuration import com.wire.android.ui.navArgs import com.wire.android.util.dispatchers.DispatcherProvider +import com.wire.kalium.logic.data.conversation.ConversationDetails import com.wire.kalium.logic.data.id.QualifiedID +import com.wire.kalium.logic.data.user.type.UserType +import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase import com.wire.kalium.logic.feature.conversation.messagetimer.UpdateMessageTimerUseCase import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTimerSettingsForConversationUseCase +import com.wire.kalium.logic.feature.user.ObserveSelfUserUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -48,6 +52,8 @@ class EditSelfDeletingMessagesViewModel @Inject constructor( private val observeConversationMembers: ObserveParticipantsForConversationUseCase, private val observeSelfDeletionTimerSettingsForConversation: ObserveSelfDeletionTimerSettingsForConversationUseCase, private val updateMessageTimer: UpdateMessageTimerUseCase, + private val selfUser: ObserveSelfUserUseCase, + private val conversationDetails: ObserveConversationDetailsUseCase, savedStateHandle: SavedStateHandle ) : ViewModel() { @@ -64,9 +70,25 @@ class EditSelfDeletingMessagesViewModel @Inject constructor( private fun observeSelfDeletionTimerSettingsForConversation() { viewModelScope.launch { + // TODO(refactor): Move all this logic to a UseCase + val canPerformAdminActionsFlow = combine( + observeConversationMembers(conversationId).map { it.isSelfAnAdmin }, + selfUser(), + conversationDetails(conversationId), + ::Triple + ).map { (isSelfAnAdmin, selfUser, conversationDetailsResult) -> + if (conversationDetailsResult !is ObserveConversationDetailsUseCase.Result.Success) { + return@map false + } + val conversationDetails = conversationDetailsResult.conversationDetails + val isChannel = conversationDetails is ConversationDetails.Group.Channel + val isTeamAdmin = selfUser.userType in setOf(UserType.ADMIN, UserType.OWNER) + val isSelfUserInConversationTeam = selfUser.teamId == conversationDetails.conversation.teamId + isSelfAnAdmin || (isChannel && isTeamAdmin && isSelfUserInConversationTeam) + } combine( observeSelfDeletionTimerSettingsForConversation(conversationId, considerSelfUserSettings = false), - observeConversationMembers(conversationId).map { it.isSelfAnAdmin }, + canPerformAdminActionsFlow, ::Pair ) .distinctUntilChanged() @@ -101,14 +123,18 @@ class EditSelfDeletingMessagesViewModel @Inject constructor( val currentSelectedDuration = state.locallySelected state = when (updateMessageTimer(conversationId, currentSelectedDuration?.value?.inWholeMilliseconds)) { is UpdateMessageTimerUseCase.Result.Failure -> { - appLogger.e("Failed to update self deleting enforced duration for conversation=${conversationId.toLogString()} " + - "with new duration=${currentSelectedDuration?.name}") + appLogger.e( + "Failed to update self deleting enforced duration for conversation=${conversationId.toLogString()} " + + "with new duration=${currentSelectedDuration?.name}" + ) state.copy(isLoading = true) } UpdateMessageTimerUseCase.Result.Success -> { - appLogger.d("Success updating self deleting enforced duration for conversation=${conversationId.toLogString()} " + - "with new duration=${currentSelectedDuration?.name}") + appLogger.d( + "Success updating self deleting enforced duration for conversation=${conversationId.toLogString()} " + + "with new duration=${currentSelectedDuration?.name}" + ) state.copy( isLoading = false, remotelySelected = currentSelectedDuration 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 da7877263e2..07a5794cdc4 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 @@ -26,6 +26,7 @@ import com.wire.android.config.NavigationTestExtension import com.wire.android.config.TestDispatcherProvider import com.wire.android.framework.TestConversation import com.wire.android.framework.TestConversationDetails +import com.wire.android.framework.TestUser 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 @@ -44,6 +45,7 @@ import com.wire.kalium.logic.feature.conversation.guestroomlink.ObserveGuestRoom import com.wire.kalium.logic.feature.conversation.guestroomlink.RevokeGuestRoomLinkResult import com.wire.kalium.logic.feature.conversation.guestroomlink.RevokeGuestRoomLinkUseCase import com.wire.kalium.logic.feature.user.GetDefaultProtocolUseCase +import com.wire.kalium.logic.feature.user.ObserveSelfUserUseCase import com.wire.kalium.logic.feature.user.guestroomlink.ObserveGuestRoomLinkFeatureFlagUseCase import io.mockk.MockKAnnotations import io.mockk.coEvery @@ -287,6 +289,9 @@ class EditGuestAccessViewModelTest { @MockK lateinit var getDefaultProtocolUseCase: GetDefaultProtocolUseCase + @MockK + lateinit var observeSelfUserUseCase: ObserveSelfUserUseCase + val editGuestAccessViewModel: EditGuestAccessViewModel by lazy { EditGuestAccessViewModel( savedStateHandle = savedStateHandle, @@ -300,7 +305,8 @@ class EditGuestAccessViewModelTest { canCreatePasswordProtectedLinks = canCreatePasswordProtectedLinks, dispatcher = dispatcherProvider, syncConversationCode = syncConversationCodeUseCase, - getDefaultProtocol = getDefaultProtocolUseCase + getDefaultProtocol = getDefaultProtocolUseCase, + selfUser = observeSelfUserUseCase ) } @@ -319,6 +325,7 @@ class EditGuestAccessViewModelTest { coEvery { observeGuestRoomLink(any()) } returns flowOf() coEvery { observeGuestRoomLinkFeatureFlag() } returns flowOf() coEvery { canCreatePasswordProtectedLinks() } returns true + coEvery { observeSelfUserUseCase() } returns flowOf(TestUser.SELF_USER) every { getDefaultProtocolUseCase() } returns SupportedProtocol.PROTEUS } diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModelTest.kt index 83b2c807160..a2b60774d73 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editselfdeletingmessages/EditSelfDeletingMessagesViewModelTest.kt @@ -19,16 +19,20 @@ package com.wire.android.ui.home.conversations.details.editselfdeletingmessages 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.framework.TestConversation -import com.wire.android.config.NavigationTestExtension +import com.wire.android.framework.TestConversationDetails +import com.wire.android.framework.TestUser 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.home.messagecomposer.SelfDeletionDuration import com.wire.android.ui.navArgs +import com.wire.kalium.logic.data.message.SelfDeletionTimer +import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase import com.wire.kalium.logic.feature.conversation.messagetimer.UpdateMessageTimerUseCase import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTimerSettingsForConversationUseCase -import com.wire.kalium.logic.data.message.SelfDeletionTimer +import com.wire.kalium.logic.feature.user.ObserveSelfUserUseCase import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.every @@ -49,44 +53,38 @@ import kotlin.time.Duration.Companion.minutes class EditSelfDeletingMessagesViewModelTest { @Test - fun `given self deleting messages option enabled, when disabling it, then it updates proper state`() = - runTest { - // Given - val (arrangement, viewModel) = Arrangement() - .withSelfDeletingMessagesGroupSettings(SelfDeletionTimer.Enabled(5.minutes)) - .withUpdateMessageTimerSuccess() - .arrange() - - // When - viewModel.updateSelfDeletingMessageOption(false) - viewModel.applyNewDuration(arrangement.onCompleted) - - // Then - assertEquals(false, viewModel.state.isEnabled) - assertEquals(null, viewModel.state.locallySelected) - verify { arrangement.onCompleted() } - } + fun `given self deleting messages option enabled, when disabling it, then it updates proper state`() = runTest { + // Given + val (arrangement, viewModel) = Arrangement().withSelfDeletingMessagesGroupSettings(SelfDeletionTimer.Enabled(5.minutes)) + .withUpdateMessageTimerSuccess().arrange() + + // When + viewModel.updateSelfDeletingMessageOption(false) + viewModel.applyNewDuration(arrangement.onCompleted) + + // Then + assertEquals(false, viewModel.state.isEnabled) + assertEquals(null, viewModel.state.locallySelected) + verify { arrangement.onCompleted() } + } @Test - fun `given self deleting messages option disabled, when enabling it, then it updates proper state`() = - runTest { - // Given - val newTimer = SelfDeletionDuration.FiveMinutes - val (arrangement, viewModel) = Arrangement() - .withSelfDeletingMessagesGroupSettings(SelfDeletionTimer.Enabled(Duration.ZERO)) - .withUpdateMessageTimerSuccess() - .arrange() - - // When - viewModel.updateSelfDeletingMessageOption(true) - viewModel.onSelectDuration(newTimer) - viewModel.applyNewDuration(arrangement.onCompleted) - - // Then - assertEquals(newTimer, viewModel.state.remotelySelected) - assertEquals(newTimer, viewModel.state.locallySelected) - verify { arrangement.onCompleted() } - } + fun `given self deleting messages option disabled, when enabling it, then it updates proper state`() = runTest { + // Given + val newTimer = SelfDeletionDuration.FiveMinutes + val (arrangement, viewModel) = Arrangement().withSelfDeletingMessagesGroupSettings(SelfDeletionTimer.Enabled(Duration.ZERO)) + .withUpdateMessageTimerSuccess().arrange() + + // When + viewModel.updateSelfDeletingMessageOption(true) + viewModel.onSelectDuration(newTimer) + viewModel.applyNewDuration(arrangement.onCompleted) + + // Then + assertEquals(newTimer, viewModel.state.remotelySelected) + assertEquals(newTimer, viewModel.state.locallySelected) + verify { arrangement.onCompleted() } + } private class Arrangement { @@ -102,6 +100,12 @@ class EditSelfDeletingMessagesViewModelTest { @MockK private lateinit var updateMessageTimer: UpdateMessageTimerUseCase + @MockK + private lateinit var selfUser: ObserveSelfUserUseCase + + @MockK + private lateinit var conversationDetails: ObserveConversationDetailsUseCase + @MockK(relaxed = true) lateinit var onCompleted: () -> Unit @@ -111,7 +115,9 @@ class EditSelfDeletingMessagesViewModelTest { dispatcher = TestDispatcherProvider(), observeConversationMembers = observerConversationMembers, observeSelfDeletionTimerSettingsForConversation = observeSelfDeletionTimerSettingsForConversation, - updateMessageTimer = updateMessageTimer + updateMessageTimer = updateMessageTimer, + selfUser = selfUser, + conversationDetails = conversationDetails, ) } @@ -121,6 +127,11 @@ class EditSelfDeletingMessagesViewModelTest { conversationId = TestConversation.ID ) + coEvery { selfUser() } returns flowOf(TestUser.SELF_USER) + coEvery { conversationDetails(any()) } returns flowOf( + ObserveConversationDetailsUseCase.Result.Success(TestConversationDetails.GROUP) + ) + coEvery { observerConversationMembers(any()) } returns flowOf(ConversationParticipantsData(isSelfAnAdmin = true)) }