From 62912f891cde60bd07fd73735dff126816d85910 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 27 Oct 2022 14:36:06 +0200 Subject: [PATCH] Introducing a NotificationsStatus to render the push notification toggle in session overview screen --- ...ePushNotificationsViaAccountDataUseCase.kt | 33 +++++++++ .../GetNotificationsStatusUseCase.kt | 59 +++++++++++++++ .../v2/notification/NotificationsStatus.kt | 23 ++++++ .../TogglePushNotificationUseCase.kt | 16 +++-- .../v2/overview/SessionOverviewFragment.kt | 22 +++--- .../v2/overview/SessionOverviewViewModel.kt | 33 +++------ .../v2/overview/SessionOverviewViewState.kt | 3 +- ...hNotificationsViaAccountDataUseCaseTest.kt | 71 +++++++++++++++++++ .../TogglePushNotificationUseCaseTest.kt | 26 ++++++- .../overview/SessionOverviewViewModelTest.kt | 23 +++--- .../fakes/FakeSessionAccountDataService.kt | 4 +- .../FakeTogglePushNotificationUseCase.kt | 2 +- 12 files changed, 264 insertions(+), 51 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/NotificationsStatus.kt rename vector/src/main/java/im/vector/app/features/settings/devices/v2/{overview => notification}/TogglePushNotificationUseCase.kt (67%) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt rename vector/src/test/java/im/vector/app/features/settings/devices/v2/{overview => notification}/TogglePushNotificationUseCaseTest.kt (67%) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt new file mode 100644 index 0000000000..dbf9adca14 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2.notification + +import im.vector.app.core.di.ActiveSessionHolder +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import javax.inject.Inject + +class CheckIfCanTogglePushNotificationsViaAccountDataUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, +) { + + fun execute(deviceId: String): Boolean { + return activeSessionHolder + .getSafeActiveSession() + ?.accountDataService() + ?.getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) != null + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt new file mode 100644 index 0000000000..1eb612988a --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2.notification + +import im.vector.app.core.di.ActiveSessionHolder +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap +import javax.inject.Inject + +class GetNotificationsStatusUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, + private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, + private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase, +) { + + // TODO add unit tests + fun execute(deviceId: String): Flow { + val session = activeSessionHolder.getSafeActiveSession() + return when { + session == null -> emptyFlow() + checkIfCanTogglePushNotificationsViaPusherUseCase.execute() -> { + session.flow() + .livePushers() + .map { it.filter { pusher -> pusher.deviceId == deviceId } } + .map { it.takeIf { it.isNotEmpty() }?.any { pusher -> pusher.enabled } } + .map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED } + } + checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(deviceId) -> { + session.flow() + .liveUserAccountData(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) + .unwrap() + .map { it.content.toModel()?.isSilenced?.not() } + .map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED } + } + else -> flowOf(NotificationsStatus.NOT_SUPPORTED) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/NotificationsStatus.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/NotificationsStatus.kt new file mode 100644 index 0000000000..7ff1f04381 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/NotificationsStatus.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2.notification + +enum class NotificationsStatus { + ENABLED, + DISABLED, + NOT_SUPPORTED, +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt similarity index 67% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt index 45c234aaef..be9012e9f1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices.v2.overview +package im.vector.app.features.settings.devices.v2.notification import im.vector.app.core.di.ActiveSessionHolder import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent @@ -24,17 +24,21 @@ import javax.inject.Inject class TogglePushNotificationUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, + private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, + private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase, ) { suspend fun execute(deviceId: String, enabled: Boolean) { val session = activeSessionHolder.getSafeActiveSession() ?: return - val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId } - devicePusher?.let { pusher -> - session.pushersService().togglePusher(pusher, enabled) + + if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute()) { + val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId } + devicePusher?.let { pusher -> + session.pushersService().togglePusher(pusher, enabled) + } } - val accountData = session.accountDataService().getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) - if (accountData != null) { + if (checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(deviceId)) { val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled) session.accountDataService().updateUserAccountData( UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId, diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt index a1cd7ea586..620372f810 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt @@ -24,6 +24,7 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isGone import androidx.core.view.isVisible import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel @@ -43,6 +44,7 @@ import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet +import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus import im.vector.app.features.workers.signout.SignOutUiWorker import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.extensions.orFalse @@ -177,7 +179,7 @@ class SessionOverviewFragment : updateEntryDetails(state.deviceId) updateSessionInfo(state) updateLoading(state.isLoading) - updatePushNotificationToggle(state.deviceId, state.notificationsEnabled) + updatePushNotificationToggle(state.deviceId, state.notificationsStatus) } private fun updateToolbar(viewState: SessionOverviewViewState) { @@ -218,15 +220,19 @@ class SessionOverviewFragment : } } - private fun updatePushNotificationToggle(deviceId: String, enabled: Boolean) { - views.sessionOverviewPushNotifications.apply { - setOnCheckedChangeListener(null) - setChecked(enabled) - post { - setOnCheckedChangeListener { _, isChecked -> - viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked)) + private fun updatePushNotificationToggle(deviceId: String, notificationsStatus: NotificationsStatus) { + views.sessionOverviewPushNotifications.isGone = notificationsStatus == NotificationsStatus.NOT_SUPPORTED + when (notificationsStatus) { + NotificationsStatus.ENABLED, NotificationsStatus.DISABLED -> { + views.sessionOverviewPushNotifications.setOnCheckedChangeListener(null) + views.sessionOverviewPushNotifications.setChecked(notificationsStatus == NotificationsStatus.ENABLED) + views.sessionOverviewPushNotifications.post { + views.sessionOverviewPushNotifications.setOnCheckedChangeListener { _, isChecked -> + viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked)) + } } } + else -> Unit } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt index 21054270f8..1aa5f676cc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt @@ -29,6 +29,9 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.auth.PendingAuthHandler import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel +import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase +import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus +import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase @@ -36,21 +39,15 @@ import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSes import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.failure.Failure -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap import timber.log.Timber import javax.net.ssl.HttpsURLConnection import kotlin.coroutines.Continuation @@ -65,6 +62,7 @@ class SessionOverviewViewModel @AssistedInject constructor( private val pendingAuthHandler: PendingAuthHandler, private val activeSessionHolder: ActiveSessionHolder, private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val getNotificationsStatusUseCase: GetNotificationsStatusUseCase, refreshDevicesUseCase: RefreshDevicesUseCase, ) : VectorSessionsListViewModel( initialState, activeSessionHolder, refreshDevicesUseCase @@ -81,7 +79,7 @@ class SessionOverviewViewModel @AssistedInject constructor( refreshPushers() observeSessionInfo(initialState.deviceId) observeCurrentSessionInfo() - observePushers(initialState.deviceId) + observeNotificationsStatus(initialState.deviceId) } private fun refreshPushers() { @@ -107,20 +105,9 @@ class SessionOverviewViewModel @AssistedInject constructor( } } - private fun observePushers(deviceId: String) { - val session = activeSessionHolder.getSafeActiveSession() ?: return - val pusherFlow = session.flow() - .livePushers() - .map { it.filter { pusher -> pusher.deviceId == deviceId } } - .map { it.takeIf { it.isNotEmpty() }?.any { pusher -> pusher.enabled } } - - val accountDataFlow = session.flow() - .liveUserAccountData(TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) - .unwrap() - .map { it.content.toModel()?.isSilenced?.not() } - - merge(pusherFlow, accountDataFlow) - .onEach { it?.let { setState { copy(notificationsEnabled = it) } } } + private fun observeNotificationsStatus(deviceId: String) { + getNotificationsStatusUseCase.execute(deviceId) + .onEach { setState { copy(notificationsStatus = it) } } .launchIn(viewModelScope) } @@ -233,7 +220,9 @@ class SessionOverviewViewModel @AssistedInject constructor( private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) { viewModelScope.launch { togglePushNotificationUseCase.execute(action.deviceId, action.enabled) - setState { copy(notificationsEnabled = action.enabled) } + // TODO should not be needed => test without + val status = if (action.enabled) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED + setState { copy(notificationsStatus = status) } } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt index 440805bad6..019dd2d724 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt @@ -20,13 +20,14 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus data class SessionOverviewViewState( val deviceId: String, val isCurrentSessionTrusted: Boolean = false, val deviceInfo: Async = Uninitialized, val isLoading: Boolean = false, - val notificationsEnabled: Boolean = false, + val notificationsStatus: NotificationsStatus = NotificationsStatus.NOT_SUPPORTED, ) : MavericksState { constructor(args: SessionOverviewArgs) : this( deviceId = args.deviceId diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt new file mode 100644 index 0000000000..0303444605 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2.notification + +import im.vector.app.test.fakes.FakeActiveSessionHolder +import io.mockk.mockk +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes + +private const val A_DEVICE_ID = "device-id" + +class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + + private val checkIfCanTogglePushNotificationsViaAccountDataUseCase = + CheckIfCanTogglePushNotificationsViaAccountDataUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance, + ) + + @Test + fun `given current session and an account data for the device id when execute then result is true`() { + // Given + fakeActiveSessionHolder + .fakeSession + .accountDataService() + .givenGetUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID, + content = mockk(), + ) + + // When + val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID) + + // Then + result shouldBeEqualTo true + } + + @Test + fun `given current session and NO account data for the device id when execute then result is false`() { + // Given + fakeActiveSessionHolder + .fakeSession + .accountDataService() + .givenGetUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID, + content = null, + ) + + // When + val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID) + + // Then + result shouldBeEqualTo false + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt similarity index 67% rename from vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt index dc64c74836..0a649354f9 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt @@ -14,10 +14,12 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices.v2.overview +package im.vector.app.features.settings.devices.v2.notification import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fixtures.PusherFixture +import io.mockk.every +import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent @@ -27,10 +29,21 @@ import org.matrix.android.sdk.api.session.events.model.toContent class TogglePushNotificationUseCaseTest { private val activeSessionHolder = FakeActiveSessionHolder() - private val togglePushNotificationUseCase = TogglePushNotificationUseCase(activeSessionHolder.instance) + private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase = + mockk() + private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase = + mockk() + + private val togglePushNotificationUseCase = + TogglePushNotificationUseCase( + activeSessionHolder = activeSessionHolder.instance, + checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase, + checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase, + ) @Test fun `when execute, then toggle enabled for device pushers`() = runTest { + // Given val sessionId = "a_session_id" val pushers = listOf( PusherFixture.aPusher(deviceId = sessionId, enabled = false), @@ -38,14 +51,19 @@ class TogglePushNotificationUseCaseTest { ) activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers) activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers) + every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns true + every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns false + // When togglePushNotificationUseCase.execute(sessionId, true) + // Then activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true) } @Test fun `when execute, then toggle local notification settings`() = runTest { + // Given val sessionId = "a_session_id" val pushers = listOf( PusherFixture.aPusher(deviceId = sessionId, enabled = false), @@ -56,9 +74,13 @@ class TogglePushNotificationUseCaseTest { UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, LocalNotificationSettingsContent(isSilenced = true).toContent() ) + every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns false + every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns true + // When togglePushNotificationUseCase.execute(sessionId, true) + // Then activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds( UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, LocalNotificationSettingsContent(isSilenced = false).toContent(), diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt index 544059b77f..c0ba6ce28b 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt @@ -23,6 +23,8 @@ import com.airbnb.mvrx.test.MavericksTestRule import im.vector.app.R import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase +import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase +import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase @@ -32,7 +34,6 @@ import im.vector.app.test.fakes.FakePendingAuthHandler import im.vector.app.test.fakes.FakeStringProvider import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase import im.vector.app.test.fakes.FakeVerificationService -import im.vector.app.test.fixtures.PusherFixture.aPusher import im.vector.app.test.test import im.vector.app.test.testDispatcher import io.mockk.coEvery @@ -87,6 +88,8 @@ class SessionOverviewViewModelTest { private val fakePendingAuthHandler = FakePendingAuthHandler() private val refreshDevicesUseCase = mockk() private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase() + private val fakeGetNotificationsStatusUseCase = mockk() + private val notificationsStatus = NotificationsStatus.ENABLED private fun createViewModel() = SessionOverviewViewModel( initialState = SessionOverviewViewState(args), @@ -99,6 +102,7 @@ class SessionOverviewViewModelTest { activeSessionHolder = fakeActiveSessionHolder.instance, refreshDevicesUseCase = refreshDevicesUseCase, togglePushNotificationUseCase = togglePushNotificationUseCase.instance, + getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase, ) @Before @@ -108,6 +112,7 @@ class SessionOverviewViewModelTest { every { SystemClock.elapsedRealtime() } returns 1234 givenVerificationService() + every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationsStatus) } @After @@ -131,7 +136,7 @@ class SessionOverviewViewModelTest { deviceId = A_SESSION_ID_1, deviceInfo = Success(deviceFullInfo), isCurrentSessionTrusted = true, - notificationsEnabled = true, + notificationsStatus = notificationsStatus, ) val viewModel = createViewModel() @@ -227,7 +232,7 @@ class SessionOverviewViewModelTest { isCurrentSessionTrusted = true, deviceInfo = Success(deviceFullInfo), isLoading = false, - notificationsEnabled = true, + notificationsStatus = notificationsStatus, ) // When @@ -264,7 +269,7 @@ class SessionOverviewViewModelTest { isCurrentSessionTrusted = true, deviceInfo = Success(deviceFullInfo), isLoading = false, - notificationsEnabled = true, + notificationsStatus = notificationsStatus, ) fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE) @@ -299,7 +304,7 @@ class SessionOverviewViewModelTest { isCurrentSessionTrusted = true, deviceInfo = Success(deviceFullInfo), isLoading = false, - notificationsEnabled = true, + notificationsStatus = notificationsStatus, ) fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE) @@ -466,13 +471,13 @@ class SessionOverviewViewModelTest { @Test fun `when viewModel init, then observe pushers and emit to state`() { - val pushers = listOf(aPusher(deviceId = A_SESSION_ID_1)) - fakeActiveSessionHolder.fakeSession.pushersService().givenPushersLive(pushers) + val notificationStatus = NotificationsStatus.ENABLED + every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationStatus) val viewModel = createViewModel() viewModel.test() - .assertLatestState { state -> state.notificationsEnabled } + .assertLatestState { state -> state.notificationsStatus == notificationStatus } .finish() } @@ -483,6 +488,6 @@ class SessionOverviewViewModelTest { viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true)) togglePushNotificationUseCase.verifyExecute(A_SESSION_ID_1, true) - viewModel.test().assertLatestState { state -> state.notificationsEnabled }.finish() + viewModel.test().assertLatestState { state -> state.notificationsStatus == NotificationsStatus.ENABLED }.finish() } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt index 615330463b..c44fc4a497 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt @@ -28,8 +28,8 @@ import org.matrix.android.sdk.api.session.events.model.Content class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed = true) { - fun givenGetUserAccountDataEventReturns(type: String, content: Content) { - every { getUserAccountDataEvent(type) } returns UserAccountDataEvent(type, content) + fun givenGetUserAccountDataEventReturns(type: String, content: Content?) { + every { getUserAccountDataEvent(type) } returns content?.let { UserAccountDataEvent(type, it) } } fun givenUpdateUserAccountDataEventSucceeds() { diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt index 92e311cfb7..bfbbb87705 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt @@ -16,7 +16,7 @@ package im.vector.app.test.fakes -import im.vector.app.features.settings.devices.v2.overview.TogglePushNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase import io.mockk.coJustRun import io.mockk.coVerify import io.mockk.mockk