diff --git a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt index 3913981531..65cb1a988a 100644 --- a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt +++ b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt @@ -84,6 +84,9 @@ import com.wire.android.ui.home.E2EIRequiredDialog import com.wire.android.ui.home.E2EISnoozeDialog import com.wire.android.ui.home.appLock.LockCodeTimeManager import com.wire.android.ui.home.sync.FeatureFlagNotificationViewModel +import com.wire.android.ui.legalhold.dialog.deactivated.LegalHoldDeactivatedDialog +import com.wire.android.ui.legalhold.dialog.deactivated.LegalHoldDeactivatedState +import com.wire.android.ui.legalhold.dialog.deactivated.LegalHoldDeactivatedViewModel import com.wire.android.ui.legalhold.dialog.requested.LegalHoldRequestedDialog import com.wire.android.ui.legalhold.dialog.requested.LegalHoldRequestedState import com.wire.android.ui.legalhold.dialog.requested.LegalHoldRequestedViewModel @@ -125,6 +128,7 @@ class WireActivity : AppCompatActivity() { private val commonTopAppBarViewModel: CommonTopAppBarViewModel by viewModels() private val legalHoldRequestedViewModel: LegalHoldRequestedViewModel by viewModels() + private val legalHoldDeactivatedViewModel: LegalHoldDeactivatedViewModel by viewModels() val navigationCommands: MutableSharedFlow = MutableSharedFlow() @@ -306,6 +310,11 @@ class WireActivity : AppCompatActivity() { acceptClicked = legalHoldRequestedViewModel::acceptClicked, ) } + if (legalHoldDeactivatedViewModel.state is LegalHoldDeactivatedState.Visible) { + LegalHoldDeactivatedDialog( + dialogDismissed = legalHoldDeactivatedViewModel::dismiss, + ) + } if (showFileSharingDialog) { FileRestrictionDialog( isFileSharingEnabled = isFileSharingEnabledState, diff --git a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedState.kt b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedState.kt new file mode 100644 index 0000000000..6fab1721a4 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedState.kt @@ -0,0 +1,25 @@ +/* + * 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.legalhold.dialog.deactivated + +import com.wire.kalium.logic.data.user.UserId + +sealed class LegalHoldDeactivatedState { + data object Hidden : LegalHoldDeactivatedState() + data class Visible(val userId: UserId) : LegalHoldDeactivatedState() +} diff --git a/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedViewModel.kt new file mode 100644 index 0000000000..b100290c44 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedViewModel.kt @@ -0,0 +1,106 @@ +/* + * 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.legalhold.dialog.deactivated + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wire.android.appLogger +import com.wire.android.di.KaliumCoreLogic +import com.wire.kalium.logic.CoreLogic +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.UserSessionScope +import com.wire.kalium.logic.feature.legalhold.LegalHoldState +import com.wire.kalium.logic.feature.legalhold.MarkLegalHoldChangeAsNotifiedForSelfUseCase +import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldChangeNotifiedForSelfUseCase +import com.wire.kalium.logic.feature.session.CurrentSessionResult +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class LegalHoldDeactivatedViewModel @Inject constructor( + @KaliumCoreLogic private val coreLogic: CoreLogic, +) : ViewModel() { + + var state: LegalHoldDeactivatedState by mutableStateOf(LegalHoldDeactivatedState.Hidden) + private set + + private fun currentSessionFlow(noSession: T, session: suspend UserSessionScope.(UserId) -> Flow): Flow = + coreLogic.getGlobalScope().session.currentSessionFlow() + .flatMapLatest { currentSessionResult -> + when (currentSessionResult) { + is CurrentSessionResult.Failure.Generic -> { + appLogger.e("$TAG: Failed to get current session") + flowOf(noSession) + } + + CurrentSessionResult.Failure.SessionNotFound -> flowOf(noSession) + is CurrentSessionResult.Success -> + currentSessionResult.accountInfo.userId.let { coreLogic.getSessionScope(it).session(it) } + } + } + + init { + viewModelScope.launch { + currentSessionFlow(noSession = LegalHoldDeactivatedState.Hidden) { userId -> + observeLegalHoldChangeNotifiedForSelf() + .mapLatest { + when (it) { + is ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.Failure -> { + appLogger.e("$TAG: Failed to get legal hold change notified data: ${it.failure}") + LegalHoldDeactivatedState.Hidden + } + ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.AlreadyNotified -> LegalHoldDeactivatedState.Hidden + is ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.ShouldNotify -> + when (it.legalHoldState) { + is LegalHoldState.Disabled -> LegalHoldDeactivatedState.Visible(userId) + is LegalHoldState.Enabled -> { // for enabled we don't show the dialog, just mark as already notified + coreLogic.getSessionScope(userId).markLegalHoldChangeAsNotifiedForSelf() + LegalHoldDeactivatedState.Hidden + } + } + } + } + }.collectLatest { state = it } + } + } + + fun dismiss() { + viewModelScope.launch { + (state as? LegalHoldDeactivatedState.Visible)?.let { + coreLogic.getSessionScope(it.userId).markLegalHoldChangeAsNotifiedForSelf().let { + if (it is MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result.Success) { + state = LegalHoldDeactivatedState.Hidden + } + } + } + } + } + + companion object { + private const val TAG = "LegalHoldDeactivatedViewModel" + } +} diff --git a/app/src/test/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedViewModelTest.kt new file mode 100644 index 0000000000..ed7fd50760 --- /dev/null +++ b/app/src/test/kotlin/com/wire/android/ui/legalhold/dialog/deactivated/LegalHoldDeactivatedViewModelTest.kt @@ -0,0 +1,137 @@ +/* + * 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.legalhold.dialog.deactivated + +import com.wire.android.config.CoroutineTestExtension +import com.wire.android.ui.legalhold.dialog.deactivated.LegalHoldDeactivatedViewModelTest.Arrangement.Companion.UNKNOWN_ERROR +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.CoreLogic +import com.wire.kalium.logic.data.auth.AccountInfo +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.legalhold.LegalHoldState +import com.wire.kalium.logic.feature.legalhold.MarkLegalHoldChangeAsNotifiedForSelfUseCase +import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldChangeNotifiedForSelfUseCase +import com.wire.kalium.logic.feature.session.CurrentSessionResult +import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBeInstanceOf +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@OptIn(ExperimentalCoroutinesApi::class) +@ExtendWith(CoroutineTestExtension::class) +class LegalHoldDeactivatedViewModelTest { + + @Test + fun givenNoSession_whenGettingState_thenStateShouldBeHidden() = runTest { + val (_, viewModel) = Arrangement() + .withNotCurrentSession() + .arrange() + advanceUntilIdle() + viewModel.state shouldBeInstanceOf LegalHoldDeactivatedState.Hidden::class + } + @Test + fun givenSessionReturnsFailure_whenGettingState_thenStateShouldBeHidden() = runTest { + val (_, viewModel) = Arrangement() + .withCurrentSessionFailure() + .arrange() + advanceUntilIdle() + viewModel.state shouldBeInstanceOf LegalHoldDeactivatedState.Hidden::class + } + @Test + fun givenLegalHoldRequestReturnsFailure_whenGettingState_thenStateShouldBeHidden() = runTest { + val (_, viewModel) = Arrangement() + .withCurrentSessionExists() + .withLegalHoldChangeNotifiedResult(ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.Failure(UNKNOWN_ERROR)) + .arrange() + advanceUntilIdle() + viewModel.state shouldBeInstanceOf LegalHoldDeactivatedState.Hidden::class + } + @Test + fun givenAlreadyNotified_whenGettingState_thenStateShouldBeHidden() = runTest { + val (_, viewModel) = Arrangement() + .withCurrentSessionExists() + .withLegalHoldChangeNotifiedResult(ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.AlreadyNotified) + .arrange() + advanceUntilIdle() + viewModel.state shouldBeInstanceOf LegalHoldDeactivatedState.Hidden::class + } + @Test + fun givenShouldNotify_whenGettingState_thenStateShouldBeVisible() = runTest { + val legalHoldState = LegalHoldState.Disabled + val (_, viewModel) = Arrangement() + .withCurrentSessionExists() + .withLegalHoldChangeNotifiedResult(ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.ShouldNotify(legalHoldState)) + .arrange() + advanceUntilIdle() + viewModel.state shouldBeInstanceOf LegalHoldDeactivatedState.Visible::class + } + @Test + fun givenShouldNotify_whenDismissing_thenStateShouldBeChangedToHidden() = runTest { + val legalHoldState = LegalHoldState.Disabled + val (arrangement, viewModel) = Arrangement() + .withCurrentSessionExists() + .withLegalHoldChangeNotifiedResult(ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.ShouldNotify(legalHoldState)) + .withMarkLegalHoldChangeAsNotifiedResult(MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result.Success) + .arrange() + advanceUntilIdle() + viewModel.dismiss() + advanceUntilIdle() + viewModel.state shouldBeInstanceOf LegalHoldDeactivatedState.Hidden::class + coVerify { arrangement.coreLogic.getSessionScope(any()).markLegalHoldChangeAsNotifiedForSelf() } + } + + private class Arrangement { + + @MockK + lateinit var coreLogic: CoreLogic + val viewModel by lazy { LegalHoldDeactivatedViewModel(coreLogic) } + + init { MockKAnnotations.init(this) } + fun withNotCurrentSession() = apply { + every { coreLogic.globalScope { session.currentSessionFlow() } } returns + flowOf(CurrentSessionResult.Failure.SessionNotFound) + } + fun withCurrentSessionFailure() = apply { + every { coreLogic.globalScope { session.currentSessionFlow() } } returns + flowOf(CurrentSessionResult.Failure.Generic(UNKNOWN_ERROR)) + } + fun withCurrentSessionExists() = apply { + every { coreLogic.globalScope { session.currentSessionFlow() } } returns + flowOf(CurrentSessionResult.Success(AccountInfo.Valid(UserId("userId", "domain")))) + } + fun withLegalHoldChangeNotifiedResult(result: ObserveLegalHoldChangeNotifiedForSelfUseCase.Result) = apply { + coEvery { coreLogic.getSessionScope(any()).observeLegalHoldChangeNotifiedForSelf() } returns flowOf(result) + } + fun withMarkLegalHoldChangeAsNotifiedResult(result: MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result) = apply { + coEvery { coreLogic.getSessionScope(any()).markLegalHoldChangeAsNotifiedForSelf() } returns result + } + fun arrange() = this to viewModel + + companion object { + val UNKNOWN_ERROR = CoreFailure.Unknown(RuntimeException("error")) + } + } +} diff --git a/kalium b/kalium index d37b5cba05..568fde8909 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit d37b5cba05f4c77cbf556a3b6ad1261efc9e0562 +Subproject commit 568fde89096d9a18935bf173e00aec661a41b095