Introducing a NotificationsStatus to render the push notification toggle in session overview screen

This commit is contained in:
Maxime NATUREL 2022-10-27 14:36:06 +02:00
parent 1acb42f61d
commit 62912f891c
12 changed files with 264 additions and 51 deletions

View file

@ -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
}
}

View file

@ -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<NotificationsStatus> {
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<LocalNotificationSettingsContent>()?.isSilenced?.not() }
.map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED }
}
else -> flowOf(NotificationsStatus.NOT_SUPPORTED)
}
}
}

View file

@ -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,
}

View file

@ -14,7 +14,7 @@
* limitations under the License. * 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 im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
@ -24,17 +24,21 @@ import javax.inject.Inject
class TogglePushNotificationUseCase @Inject constructor( class TogglePushNotificationUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase,
private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase,
) { ) {
suspend fun execute(deviceId: String, enabled: Boolean) { suspend fun execute(deviceId: String, enabled: Boolean) {
val session = activeSessionHolder.getSafeActiveSession() ?: return val session = activeSessionHolder.getSafeActiveSession() ?: return
val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
devicePusher?.let { pusher -> if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute()) {
session.pushersService().togglePusher(pusher, enabled) 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 (checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(deviceId)) {
if (accountData != null) {
val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled) val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled)
session.accountDataService().updateUserAccountData( session.accountDataService().updateUserAccountData(
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId, UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId,

View file

@ -24,6 +24,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.fragmentViewModel 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.crypto.recover.SetupMode
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState 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.more.SessionLearnMoreBottomSheet
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
import im.vector.app.features.workers.signout.SignOutUiWorker import im.vector.app.features.workers.signout.SignOutUiWorker
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
@ -177,7 +179,7 @@ class SessionOverviewFragment :
updateEntryDetails(state.deviceId) updateEntryDetails(state.deviceId)
updateSessionInfo(state) updateSessionInfo(state)
updateLoading(state.isLoading) updateLoading(state.isLoading)
updatePushNotificationToggle(state.deviceId, state.notificationsEnabled) updatePushNotificationToggle(state.deviceId, state.notificationsStatus)
} }
private fun updateToolbar(viewState: SessionOverviewViewState) { private fun updateToolbar(viewState: SessionOverviewViewState) {
@ -218,15 +220,19 @@ class SessionOverviewFragment :
} }
} }
private fun updatePushNotificationToggle(deviceId: String, enabled: Boolean) { private fun updatePushNotificationToggle(deviceId: String, notificationsStatus: NotificationsStatus) {
views.sessionOverviewPushNotifications.apply { views.sessionOverviewPushNotifications.isGone = notificationsStatus == NotificationsStatus.NOT_SUPPORTED
setOnCheckedChangeListener(null) when (notificationsStatus) {
setChecked(enabled) NotificationsStatus.ENABLED, NotificationsStatus.DISABLED -> {
post { views.sessionOverviewPushNotifications.setOnCheckedChangeListener(null)
setOnCheckedChangeListener { _, isChecked -> views.sessionOverviewPushNotifications.setChecked(notificationsStatus == NotificationsStatus.ENABLED)
viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked)) views.sessionOverviewPushNotifications.post {
views.sessionOverviewPushNotifications.setOnCheckedChangeListener { _, isChecked ->
viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked))
}
} }
} }
else -> Unit
} }
} }

View file

@ -29,6 +29,9 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.auth.PendingAuthHandler import im.vector.app.features.auth.PendingAuthHandler
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase 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.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.InterceptSignoutFlowResponseUseCase
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase 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.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch 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.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.failure.Failure 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.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.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.unwrap
import timber.log.Timber import timber.log.Timber
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
@ -65,6 +62,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
private val pendingAuthHandler: PendingAuthHandler, private val pendingAuthHandler: PendingAuthHandler,
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val togglePushNotificationUseCase: TogglePushNotificationUseCase, private val togglePushNotificationUseCase: TogglePushNotificationUseCase,
private val getNotificationsStatusUseCase: GetNotificationsStatusUseCase,
refreshDevicesUseCase: RefreshDevicesUseCase, refreshDevicesUseCase: RefreshDevicesUseCase,
) : VectorSessionsListViewModel<SessionOverviewViewState, SessionOverviewAction, SessionOverviewViewEvent>( ) : VectorSessionsListViewModel<SessionOverviewViewState, SessionOverviewAction, SessionOverviewViewEvent>(
initialState, activeSessionHolder, refreshDevicesUseCase initialState, activeSessionHolder, refreshDevicesUseCase
@ -81,7 +79,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
refreshPushers() refreshPushers()
observeSessionInfo(initialState.deviceId) observeSessionInfo(initialState.deviceId)
observeCurrentSessionInfo() observeCurrentSessionInfo()
observePushers(initialState.deviceId) observeNotificationsStatus(initialState.deviceId)
} }
private fun refreshPushers() { private fun refreshPushers() {
@ -107,20 +105,9 @@ class SessionOverviewViewModel @AssistedInject constructor(
} }
} }
private fun observePushers(deviceId: String) { private fun observeNotificationsStatus(deviceId: String) {
val session = activeSessionHolder.getSafeActiveSession() ?: return getNotificationsStatusUseCase.execute(deviceId)
val pusherFlow = session.flow() .onEach { setState { copy(notificationsStatus = it) } }
.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<LocalNotificationSettingsContent>()?.isSilenced?.not() }
merge(pusherFlow, accountDataFlow)
.onEach { it?.let { setState { copy(notificationsEnabled = it) } } }
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
@ -233,7 +220,9 @@ class SessionOverviewViewModel @AssistedInject constructor(
private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) { private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) {
viewModelScope.launch { viewModelScope.launch {
togglePushNotificationUseCase.execute(action.deviceId, action.enabled) 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) }
} }
} }
} }

View file

@ -20,13 +20,14 @@ import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
data class SessionOverviewViewState( data class SessionOverviewViewState(
val deviceId: String, val deviceId: String,
val isCurrentSessionTrusted: Boolean = false, val isCurrentSessionTrusted: Boolean = false,
val deviceInfo: Async<DeviceFullInfo> = Uninitialized, val deviceInfo: Async<DeviceFullInfo> = Uninitialized,
val isLoading: Boolean = false, val isLoading: Boolean = false,
val notificationsEnabled: Boolean = false, val notificationsStatus: NotificationsStatus = NotificationsStatus.NOT_SUPPORTED,
) : MavericksState { ) : MavericksState {
constructor(args: SessionOverviewArgs) : this( constructor(args: SessionOverviewArgs) : this(
deviceId = args.deviceId deviceId = args.deviceId

View file

@ -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
}
}

View file

@ -14,10 +14,12 @@
* limitations under the License. * 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.fakes.FakeActiveSessionHolder
import im.vector.app.test.fixtures.PusherFixture import im.vector.app.test.fixtures.PusherFixture
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
@ -27,10 +29,21 @@ import org.matrix.android.sdk.api.session.events.model.toContent
class TogglePushNotificationUseCaseTest { class TogglePushNotificationUseCaseTest {
private val activeSessionHolder = FakeActiveSessionHolder() private val activeSessionHolder = FakeActiveSessionHolder()
private val togglePushNotificationUseCase = TogglePushNotificationUseCase(activeSessionHolder.instance) private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase =
mockk<CheckIfCanTogglePushNotificationsViaPusherUseCase>()
private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase =
mockk<CheckIfCanTogglePushNotificationsViaAccountDataUseCase>()
private val togglePushNotificationUseCase =
TogglePushNotificationUseCase(
activeSessionHolder = activeSessionHolder.instance,
checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase,
checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase,
)
@Test @Test
fun `when execute, then toggle enabled for device pushers`() = runTest { fun `when execute, then toggle enabled for device pushers`() = runTest {
// Given
val sessionId = "a_session_id" val sessionId = "a_session_id"
val pushers = listOf( val pushers = listOf(
PusherFixture.aPusher(deviceId = sessionId, enabled = false), PusherFixture.aPusher(deviceId = sessionId, enabled = false),
@ -38,14 +51,19 @@ class TogglePushNotificationUseCaseTest {
) )
activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers) activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers) activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns true
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns false
// When
togglePushNotificationUseCase.execute(sessionId, true) togglePushNotificationUseCase.execute(sessionId, true)
// Then
activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true) activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true)
} }
@Test @Test
fun `when execute, then toggle local notification settings`() = runTest { fun `when execute, then toggle local notification settings`() = runTest {
// Given
val sessionId = "a_session_id" val sessionId = "a_session_id"
val pushers = listOf( val pushers = listOf(
PusherFixture.aPusher(deviceId = sessionId, enabled = false), PusherFixture.aPusher(deviceId = sessionId, enabled = false),
@ -56,9 +74,13 @@ class TogglePushNotificationUseCaseTest {
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
LocalNotificationSettingsContent(isSilenced = true).toContent() LocalNotificationSettingsContent(isSilenced = true).toContent()
) )
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns false
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns true
// When
togglePushNotificationUseCase.execute(sessionId, true) togglePushNotificationUseCase.execute(sessionId, true)
// Then
activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds( activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds(
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
LocalNotificationSettingsContent(isSilenced = false).toContent(), LocalNotificationSettingsContent(isSilenced = false).toContent(),

View file

@ -23,6 +23,8 @@ import com.airbnb.mvrx.test.MavericksTestRule
import im.vector.app.R import im.vector.app.R
import im.vector.app.features.settings.devices.v2.DeviceFullInfo 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.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.InterceptSignoutFlowResponseUseCase
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase 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.FakeStringProvider
import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase
import im.vector.app.test.fakes.FakeVerificationService 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.test
import im.vector.app.test.testDispatcher import im.vector.app.test.testDispatcher
import io.mockk.coEvery import io.mockk.coEvery
@ -87,6 +88,8 @@ class SessionOverviewViewModelTest {
private val fakePendingAuthHandler = FakePendingAuthHandler() private val fakePendingAuthHandler = FakePendingAuthHandler()
private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>() private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>()
private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase() private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase()
private val fakeGetNotificationsStatusUseCase = mockk<GetNotificationsStatusUseCase>()
private val notificationsStatus = NotificationsStatus.ENABLED
private fun createViewModel() = SessionOverviewViewModel( private fun createViewModel() = SessionOverviewViewModel(
initialState = SessionOverviewViewState(args), initialState = SessionOverviewViewState(args),
@ -99,6 +102,7 @@ class SessionOverviewViewModelTest {
activeSessionHolder = fakeActiveSessionHolder.instance, activeSessionHolder = fakeActiveSessionHolder.instance,
refreshDevicesUseCase = refreshDevicesUseCase, refreshDevicesUseCase = refreshDevicesUseCase,
togglePushNotificationUseCase = togglePushNotificationUseCase.instance, togglePushNotificationUseCase = togglePushNotificationUseCase.instance,
getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase,
) )
@Before @Before
@ -108,6 +112,7 @@ class SessionOverviewViewModelTest {
every { SystemClock.elapsedRealtime() } returns 1234 every { SystemClock.elapsedRealtime() } returns 1234
givenVerificationService() givenVerificationService()
every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationsStatus)
} }
@After @After
@ -131,7 +136,7 @@ class SessionOverviewViewModelTest {
deviceId = A_SESSION_ID_1, deviceId = A_SESSION_ID_1,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
val viewModel = createViewModel() val viewModel = createViewModel()
@ -227,7 +232,7 @@ class SessionOverviewViewModelTest {
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isLoading = false, isLoading = false,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
// When // When
@ -264,7 +269,7 @@ class SessionOverviewViewModelTest {
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isLoading = false, isLoading = false,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE) fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE)
@ -299,7 +304,7 @@ class SessionOverviewViewModelTest {
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isLoading = false, isLoading = false,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE) fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE)
@ -466,13 +471,13 @@ class SessionOverviewViewModelTest {
@Test @Test
fun `when viewModel init, then observe pushers and emit to state`() { fun `when viewModel init, then observe pushers and emit to state`() {
val pushers = listOf(aPusher(deviceId = A_SESSION_ID_1)) val notificationStatus = NotificationsStatus.ENABLED
fakeActiveSessionHolder.fakeSession.pushersService().givenPushersLive(pushers) every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationStatus)
val viewModel = createViewModel() val viewModel = createViewModel()
viewModel.test() viewModel.test()
.assertLatestState { state -> state.notificationsEnabled } .assertLatestState { state -> state.notificationsStatus == notificationStatus }
.finish() .finish()
} }
@ -483,6 +488,6 @@ class SessionOverviewViewModelTest {
viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true)) viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true))
togglePushNotificationUseCase.verifyExecute(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()
} }
} }

View file

@ -28,8 +28,8 @@ import org.matrix.android.sdk.api.session.events.model.Content
class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed = true) { class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed = true) {
fun givenGetUserAccountDataEventReturns(type: String, content: Content) { fun givenGetUserAccountDataEventReturns(type: String, content: Content?) {
every { getUserAccountDataEvent(type) } returns UserAccountDataEvent(type, content) every { getUserAccountDataEvent(type) } returns content?.let { UserAccountDataEvent(type, it) }
} }
fun givenUpdateUserAccountDataEventSucceeds() { fun givenUpdateUserAccountDataEventSucceeds() {

View file

@ -16,7 +16,7 @@
package im.vector.app.test.fakes 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.coJustRun
import io.mockk.coVerify import io.mockk.coVerify
import io.mockk.mockk import io.mockk.mockk