Prevent verification while initial sync is in progress (#1138)
* Prevent verification while initial sync is in progress * Add `canVerifySessionFlow` to simplify the check
This commit is contained in:
parent
f5fd505bc2
commit
39ef780fe7
8 changed files with 38 additions and 26 deletions
1
changelog.d/1131.bugfix
Normal file
1
changelog.d/1131.bugfix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Only display verification prompt after initial sync is done.
|
||||||
|
|
@ -20,7 +20,6 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -34,7 +33,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -62,11 +60,8 @@ class PreferencesRootPresenter @Inject constructor(
|
||||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||||
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
|
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
|
||||||
|
|
||||||
// Session verification status (unknown, not verified, verified)
|
// We should display the 'complete verification' option if the current session can be verified
|
||||||
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
|
val showCompleteVerification by sessionVerificationService.canVerifySessionFlow.collectAsState(false)
|
||||||
val sessionIsNotVerified by remember {
|
|
||||||
derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified }
|
|
||||||
}
|
|
||||||
|
|
||||||
val accountManagementUrl: MutableState<String?> = remember {
|
val accountManagementUrl: MutableState<String?> = remember {
|
||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
|
|
@ -82,7 +77,7 @@ class PreferencesRootPresenter @Inject constructor(
|
||||||
logoutState = logoutState,
|
logoutState = logoutState,
|
||||||
myUser = matrixUser.value,
|
myUser = matrixUser.value,
|
||||||
version = versionFormatter.get(),
|
version = versionFormatter.get(),
|
||||||
showCompleteVerification = sessionIsNotVerified,
|
showCompleteVerification = showCompleteVerification,
|
||||||
accountManagementUrl = accountManagementUrl.value,
|
accountManagementUrl = accountManagementUrl.value,
|
||||||
showAnalyticsSettings = hasAnalyticsProviders,
|
showAnalyticsSettings = hasAnalyticsProviders,
|
||||||
showDeveloperSettings = showDeveloperSettings,
|
showDeveloperSettings = showDeveloperSettings,
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
import io.element.android.libraries.matrix.api.user.getCurrentUser
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -73,11 +72,11 @@ class RoomListPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session verification status (unknown, not verified, verified)
|
// Session verification status (unknown, not verified, verified)
|
||||||
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState()
|
val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false)
|
||||||
var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) }
|
var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) }
|
||||||
// We combine both values to only display the prompt if the session is not verified and it wasn't dismissed
|
// We combine both values to only display the prompt if the session is not verified and it wasn't dismissed
|
||||||
val displayVerificationPrompt by remember {
|
val displayVerificationPrompt by remember {
|
||||||
derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified && !verificationPromptDismissed }
|
derivedStateOf { canVerifySession && !verificationPromptDismissed }
|
||||||
}
|
}
|
||||||
|
|
||||||
var displaySearchResults by rememberSaveable { mutableStateOf(false) }
|
var displaySearchResults by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ class RoomListPresenterTests {
|
||||||
fun `present - handle DismissRequestVerificationPrompt`() = runTest {
|
fun `present - handle DismissRequestVerificationPrompt`() = runTest {
|
||||||
val roomListService = FakeRoomListService()
|
val roomListService = FakeRoomListService()
|
||||||
val matrixClient = FakeMatrixClient(
|
val matrixClient = FakeMatrixClient(
|
||||||
roomListService = roomListService
|
roomListService = roomListService,
|
||||||
)
|
)
|
||||||
val presenter = createRoomListPresenter(
|
val presenter = createRoomListPresenter(
|
||||||
client = matrixClient,
|
client = matrixClient,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.api.verification
|
package io.element.android.libraries.matrix.api.verification
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface SessionVerificationService {
|
interface SessionVerificationService {
|
||||||
|
|
@ -37,6 +38,11 @@ interface SessionVerificationService {
|
||||||
*/
|
*/
|
||||||
val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus>
|
val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current session needs to be verified and the SDK is ready to start the verification.
|
||||||
|
*/
|
||||||
|
val canVerifySessionFlow: Flow<Boolean>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request verification of the current session.
|
* Request verification of the current session.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||||
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
|
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
|
|
@ -93,8 +92,8 @@ class RustMatrixClient constructor(
|
||||||
private val innerRoomListService = syncService.roomListService()
|
private val innerRoomListService = syncService.roomListService()
|
||||||
private val sessionDispatcher = dispatchers.io.limitedParallelism(64)
|
private val sessionDispatcher = dispatchers.io.limitedParallelism(64)
|
||||||
private val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-${sessionId}")
|
private val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-${sessionId}")
|
||||||
private val verificationService = RustSessionVerificationService()
|
|
||||||
private val rustSyncService = RustSyncService(syncService, sessionCoroutineScope)
|
private val rustSyncService = RustSyncService(syncService, sessionCoroutineScope)
|
||||||
|
private val verificationService = RustSessionVerificationService(rustSyncService)
|
||||||
private val pushersService = RustPushersService(
|
private val pushersService = RustPushersService(
|
||||||
client = client,
|
client = client,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
|
|
@ -149,13 +148,11 @@ class RustMatrixClient constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
client.setDelegate(clientDelegate)
|
client.setDelegate(clientDelegate)
|
||||||
rustSyncService.syncState
|
roomListService.state.onEach { state ->
|
||||||
.onEach { syncState ->
|
if (state == RoomListService.State.Running) {
|
||||||
if (syncState == SyncState.Running) {
|
setupVerificationControllerIfNeeded()
|
||||||
onSlidingSyncUpdate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.launchIn(sessionCoroutineScope)
|
}.launchIn(sessionCoroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) {
|
override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) {
|
||||||
|
|
@ -338,8 +335,8 @@ class RustMatrixClient constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSlidingSyncUpdate() {
|
private fun setupVerificationControllerIfNeeded() {
|
||||||
if (!verificationService.isReady.value) {
|
if (verificationService.verificationController == null) {
|
||||||
try {
|
try {
|
||||||
verificationService.verificationController = client.getSessionVerificationController()
|
verificationService.verificationController = client.getSessionVerificationController()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,25 @@
|
||||||
package io.element.android.libraries.matrix.impl.verification
|
package io.element.android.libraries.matrix.impl.verification
|
||||||
|
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
|
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||||
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
|
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
|
||||||
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
||||||
|
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
import org.matrix.rustcomponents.sdk.SessionVerificationController
|
import org.matrix.rustcomponents.sdk.SessionVerificationController
|
||||||
import org.matrix.rustcomponents.sdk.SessionVerificationControllerDelegate
|
import org.matrix.rustcomponents.sdk.SessionVerificationControllerDelegate
|
||||||
import org.matrix.rustcomponents.sdk.SessionVerificationControllerInterface
|
import org.matrix.rustcomponents.sdk.SessionVerificationControllerInterface
|
||||||
import org.matrix.rustcomponents.sdk.SessionVerificationEmoji
|
import org.matrix.rustcomponents.sdk.SessionVerificationEmoji
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RustSessionVerificationService @Inject constructor() : SessionVerificationService, SessionVerificationControllerDelegate {
|
class RustSessionVerificationService @Inject constructor(
|
||||||
|
private val syncService: RustSyncService,
|
||||||
|
) : SessionVerificationService, SessionVerificationControllerDelegate {
|
||||||
|
|
||||||
var verificationController: SessionVerificationControllerInterface? = null
|
var verificationController: SessionVerificationControllerInterface? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|
@ -52,6 +57,10 @@ class RustSessionVerificationService @Inject constructor() : SessionVerification
|
||||||
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
|
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
|
||||||
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus.asStateFlow()
|
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus.asStateFlow()
|
||||||
|
|
||||||
|
override val canVerifySessionFlow = combine(sessionVerifiedStatus, syncService.syncState) { verificationStatus, syncState ->
|
||||||
|
syncState == SyncState.Running && verificationStatus == SessionVerifiedStatus.NotVerified
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun requestVerification() = tryOrFail {
|
override suspend fun requestVerification() = tryOrFail {
|
||||||
verificationController?.requestVerification()
|
verificationController?.requestVerification()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS
|
||||||
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||||
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
|
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
|
@ -27,13 +28,13 @@ class FakeSessionVerificationService : SessionVerificationService {
|
||||||
private val _isReady = MutableStateFlow(false)
|
private val _isReady = MutableStateFlow(false)
|
||||||
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
|
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
|
||||||
private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
|
private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
|
||||||
|
private var _canVerifySessionFlow = MutableStateFlow(true)
|
||||||
private var emojiList = emptyList<VerificationEmoji>()
|
private var emojiList = emptyList<VerificationEmoji>()
|
||||||
var shouldFail = false
|
var shouldFail = false
|
||||||
|
|
||||||
override val verificationFlowState: StateFlow<VerificationFlowState>
|
override val verificationFlowState: StateFlow<VerificationFlowState> =_verificationFlowState
|
||||||
get() = _verificationFlowState
|
|
||||||
|
|
||||||
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus
|
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus
|
||||||
|
override val canVerifySessionFlow: Flow<Boolean> = _canVerifySessionFlow
|
||||||
|
|
||||||
override val isReady: StateFlow<Boolean> = _isReady
|
override val isReady: StateFlow<Boolean> = _isReady
|
||||||
|
|
||||||
|
|
@ -77,6 +78,10 @@ class FakeSessionVerificationService : SessionVerificationService {
|
||||||
_verificationFlowState.value = state
|
_verificationFlowState.value = state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenCanVerifySession(canVerify: Boolean) {
|
||||||
|
_canVerifySessionFlow.value = canVerify
|
||||||
|
}
|
||||||
|
|
||||||
fun givenIsReady(value: Boolean) {
|
fun givenIsReady(value: Boolean) {
|
||||||
_isReady.value = value
|
_isReady.value = value
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue