Make isLastSession "live"
This commit is contained in:
committed by
Benoit Marty
parent
fb368f058b
commit
06caf35ff4
@@ -24,7 +24,6 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
@@ -54,11 +53,7 @@ class LogoutPresenter @Inject constructor(
|
||||
}
|
||||
.collectAsState(initial = BackupUploadState.Unknown)
|
||||
|
||||
var isLastDevice by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
isLastDevice = encryptionService.isLastDevice().getOrNull() ?: false
|
||||
}
|
||||
|
||||
val isLastDevice by encryptionService.isLastDevice.collectAsState()
|
||||
val backupState by encryptionService.backupStateStateFlow.collectAsState()
|
||||
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
|
||||
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
package io.element.android.features.logout.impl.direct
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvents
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutPresenter
|
||||
@@ -58,10 +56,7 @@ class DefaultDirectLogoutPresenter @Inject constructor(
|
||||
}
|
||||
.collectAsState(initial = BackupUploadState.Unknown)
|
||||
|
||||
var isLastDevice by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
isLastDevice = encryptionService.isLastDevice().getOrNull() ?: false
|
||||
}
|
||||
val isLastDevice by encryptionService.isLastDevice.collectAsState()
|
||||
|
||||
fun handleEvents(event: DirectLogoutEvents) {
|
||||
when (event) {
|
||||
|
||||
@@ -61,13 +61,13 @@ class LogoutPresenterTest {
|
||||
fun `present - initial state - last session`() = runTest {
|
||||
val presenter = createLogoutPresenter(
|
||||
encryptionService = FakeEncryptionService().apply {
|
||||
givenIsLastDevice(true)
|
||||
emitIsLastDevice(true)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(3)
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.isLastDevice).isTrue()
|
||||
assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown)
|
||||
|
||||
@@ -55,13 +55,12 @@ class DefaultDirectLogoutPresenterTest {
|
||||
fun `present - initial state - last session`() = runTest {
|
||||
val presenter = createDefaultDirectLogoutPresenter(
|
||||
encryptionService = FakeEncryptionService().apply {
|
||||
givenIsLastDevice(true)
|
||||
emitIsLastDevice(true)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.canDoDirectSignOut).isFalse()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
|
||||
@@ -113,10 +113,7 @@ class RoomListPresenter @Inject constructor(
|
||||
|
||||
var securityBannerDismissed by rememberSaveable { mutableStateOf(false) }
|
||||
val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false)
|
||||
var isLastDevice by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
isLastDevice = encryptionService.isLastDevice().getOrNull() ?: false
|
||||
}
|
||||
val isLastDevice by encryptionService.isLastDevice.collectAsState()
|
||||
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
|
||||
val syncState by syncService.syncState.collectAsState()
|
||||
val securityBannerState by remember {
|
||||
|
||||
@@ -243,14 +243,14 @@ class RoomListPresenterTests {
|
||||
coroutineScope = scope,
|
||||
client = FakeMatrixClient(
|
||||
encryptionService = FakeEncryptionService().apply {
|
||||
givenIsLastDevice(true)
|
||||
emitIsLastDevice(true)
|
||||
}
|
||||
),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(2)
|
||||
skipItems(1)
|
||||
val eventSink = awaitItem().eventSink
|
||||
// For the last session, the state is not SessionVerification, but RecoveryKeyConfirmation
|
||||
assertThat(awaitItem().securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation)
|
||||
|
||||
@@ -23,11 +23,10 @@ interface EncryptionService {
|
||||
val backupStateStateFlow: StateFlow<BackupState>
|
||||
val recoveryStateStateFlow: StateFlow<RecoveryState>
|
||||
val enableRecoveryProgressStateFlow: StateFlow<EnableRecoveryProgress>
|
||||
val isLastDevice: StateFlow<Boolean>
|
||||
|
||||
suspend fun enableBackups(): Result<Unit>
|
||||
|
||||
suspend fun isLastDevice(): Result<Boolean>
|
||||
|
||||
/**
|
||||
* Enable recovery. Observe enableProgressStateFlow to get progress and recovery key.
|
||||
*/
|
||||
|
||||
@@ -27,12 +27,17 @@ import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.BackupStateListener
|
||||
import org.matrix.rustcomponents.sdk.BackupSteadyStateListener
|
||||
@@ -88,6 +93,20 @@ internal class RustEncryptionService(
|
||||
|
||||
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
|
||||
|
||||
/**
|
||||
* Check if the session is the last session every 5 seconds.
|
||||
* TODO This is a temporary workaround, when we will have a way to observe
|
||||
* the sessions, this code will have to be updated.
|
||||
*/
|
||||
override val isLastDevice: StateFlow<Boolean> = flow {
|
||||
while (currentCoroutineContext().isActive) {
|
||||
val result = isLastDevice().getOrDefault(false)
|
||||
emit(result)
|
||||
delay(5_000)
|
||||
}
|
||||
}
|
||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
|
||||
|
||||
fun start() {
|
||||
service.backupStateListener(object : BackupStateListener {
|
||||
override fun onUpdate(status: RustBackupState) {
|
||||
@@ -173,7 +192,7 @@ internal class RustEncryptionService(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun isLastDevice(): Result<Boolean> = withContext(dispatchers.io) {
|
||||
private suspend fun isLastDevice(): Result<Boolean> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
service.isLastDevice()
|
||||
}.mapFailure {
|
||||
|
||||
@@ -31,6 +31,7 @@ class FakeEncryptionService : EncryptionService {
|
||||
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(BackupState.UNKNOWN)
|
||||
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(RecoveryState.UNKNOWN)
|
||||
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
|
||||
override val isLastDevice: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf()
|
||||
|
||||
private var recoverFailure: Exception? = null
|
||||
@@ -73,14 +74,8 @@ class FakeEncryptionService : EncryptionService {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
private var isLastDevice = false
|
||||
|
||||
fun givenIsLastDevice(isLastDevice: Boolean) {
|
||||
this.isLastDevice = isLastDevice
|
||||
}
|
||||
|
||||
override suspend fun isLastDevice(): Result<Boolean> = simulateLongTask {
|
||||
return Result.success(isLastDevice)
|
||||
fun emitIsLastDevice(isLastDevice: Boolean) {
|
||||
this.isLastDevice.value = isLastDevice
|
||||
}
|
||||
|
||||
override suspend fun resetRecoveryKey(): Result<String> = simulateLongTask {
|
||||
|
||||
Reference in New Issue
Block a user