Listen verification + refresh devices use cases

This commit is contained in:
Maxime NATUREL 2022-09-05 16:44:10 +02:00
parent 39a0b3b1ba
commit 0eae1bd505
3 changed files with 151 additions and 1 deletions

View file

@ -21,19 +21,30 @@ import com.airbnb.mvrx.Success
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.utils.PublishDataSource
import im.vector.lib.core.utils.flow.throttleFirst
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import kotlin.time.Duration.Companion.seconds
// TODO add unit tests
class DevicesViewModel @AssistedInject constructor(
@Assisted initialState: DevicesViewState,
private val activeSessionHolder: ActiveSessionHolder,
private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase,
private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase,
) : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvent>(initialState) {
private val refreshDevicesUseCase: RefreshDevicesUseCase,
private val refreshDevicesOnCryptoDevicesChangeUseCase: RefreshDevicesOnCryptoDevicesChangeUseCase,
) : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvent>(initialState), VerificationService.Listener {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<DevicesViewModel, DevicesViewState> {
@ -42,9 +53,35 @@ class DevicesViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<DevicesViewModel, DevicesViewState> by hiltMavericksViewModelFactory()
private val refreshSource = PublishDataSource<Unit>()
private val refreshThrottleDelayMs = 4.seconds.inWholeMilliseconds
init {
addVerificationListener()
observeCurrentSessionCrossSigningInfo()
observeDevices()
observeRefreshSource()
refreshDevicesOnCryptoDevicesChange()
queryRefreshDevicesList()
}
override fun onCleared() {
removeVerificationListener()
super.onCleared()
}
private fun addVerificationListener() {
activeSessionHolder.getSafeActiveSession()
?.cryptoService()
?.verificationService()
?.addListener(this)
}
private fun removeVerificationListener() {
activeSessionHolder.getSafeActiveSession()
?.cryptoService()
?.verificationService()
?.removeListener(this)
}
private fun observeCurrentSessionCrossSigningInfo() {
@ -77,6 +114,34 @@ class DevicesViewModel @AssistedInject constructor(
}
}
private fun refreshDevicesOnCryptoDevicesChange() {
viewModelScope.launch {
refreshDevicesOnCryptoDevicesChangeUseCase.execute()
}
}
private fun observeRefreshSource() {
refreshSource.stream()
.throttleFirst(refreshThrottleDelayMs)
.onEach { refreshDevicesUseCase.execute() }
.launchIn(viewModelScope)
}
override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.state == VerificationTxState.Verified) {
queryRefreshDevicesList()
}
}
/**
* Force the refresh of the devices list.
* The devices list is the list of the devices where the user is logged in.
* It can be any mobile devices, and any browsers.
*/
private fun queryRefreshDevicesList() {
refreshSource.post(Unit)
}
override fun handle(action: DevicesAction) {
when (action) {
is DevicesAction.MarkAsManuallyVerified -> handleMarkAsManuallyVerifiedAction()

View file

@ -0,0 +1,52 @@
/*
* 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
import im.vector.app.core.di.ActiveSessionHolder
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.flow.flow
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
// TODO add unit tests
class RefreshDevicesOnCryptoDevicesChangeUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
private val samplingPeriodMs = 5.seconds.inWholeMilliseconds
suspend fun execute() {
activeSessionHolder.getSafeActiveSession()
?.let { session ->
session.flow().liveUserCryptoDevices(session.myUserId)
.map { it.size }
.distinctUntilChanged()
.sample(samplingPeriodMs)
.onEach {
// If we have a new crypto device change, we might want to trigger refresh of device info
activeSessionHolder.getSafeActiveSession()
?.cryptoService()
?.fetchDevicesList(NoOpMatrixCallback())
}
.collect()
}
}
}

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
import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.NoOpMatrixCallback
import javax.inject.Inject
// TODO add unit tests
class RefreshDevicesUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
fun execute() {
activeSessionHolder.getSafeActiveSession()?.let { session ->
session.cryptoService().fetchDevicesList(NoOpMatrixCallback())
session.cryptoService().downloadKeys(listOf(session.myUserId), true, NoOpMatrixCallback())
}
}
}