Merge branch 'develop' into feature/fga/permalink_timeline

This commit is contained in:
Benoit Marty 2024-04-26 12:50:38 +02:00
commit 2c8abbed0c
1157 changed files with 4307 additions and 1899 deletions

View file

@ -65,7 +65,8 @@ interface MatrixClient : Closeable {
suspend fun setDisplayName(displayName: String): Result<Unit>
suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result<Unit>
suspend fun removeAvatar(): Result<Unit>
suspend fun joinRoom(roomId: RoomId): Result<RoomId>
suspend fun joinRoom(roomId: RoomId): Result<Unit>
suspend fun knockRoom(roomId: RoomId): Result<Unit>
fun syncService(): SyncService
fun sessionVerificationService(): SessionVerificationService
fun pushersService(): PushersService

View file

@ -27,7 +27,7 @@ data class NotificationData(
val roomId: RoomId,
// mxc url
val senderAvatarUrl: String?,
// private, must use `getSenderName`
// private, must use `getDisambiguatedDisplayName`
private val senderDisplayName: String?,
private val senderIsNameAmbiguous: Boolean,
val roomAvatarUrl: String?,
@ -39,7 +39,7 @@ data class NotificationData(
val content: NotificationContent,
val hasMention: Boolean,
) {
fun getSenderName(userId: UserId): String = when {
fun getDisambiguatedDisplayName(userId: UserId): String = when {
senderDisplayName.isNullOrBlank() -> userId.value
senderIsNameAmbiguous -> "$senderDisplayName ($userId)"
else -> senderDisplayName
@ -52,6 +52,7 @@ sealed interface NotificationContent {
data class CallInvite(
val senderId: UserId,
) : MessageLike
data object CallHangup : MessageLike
data object CallCandidates : MessageLike
data object KeyVerificationReady : MessageLike

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2024 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 io.element.android.libraries.matrix.api.room
sealed interface RoomType {
data object Space : RoomType
data object Room : RoomType
data class Other(val type: String) : RoomType
}

View file

@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.api.room.preview
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomType
data class RoomPreview(
/** The room id for this room. */
@ -33,7 +34,7 @@ data class RoomPreview(
/** The number of joined members. */
val numberOfJoinedMembers: Long,
/** The room type (space, custom) or nothing, if it's a regular room. */
val roomType: String?,
val roomType: RoomType,
/** Is the history world-readable for this room? */
val isHistoryWorldReadable: Boolean,
/** Is the room joined by the current user? */

View file

@ -37,7 +37,7 @@ sealed interface RoomSummary {
data class RoomSummaryDetails(
val roomId: RoomId,
val name: String,
val name: String?,
val canonicalAlias: RoomAlias?,
val isDirect: Boolean,
val avatarUrl: String?,

View file

@ -33,8 +33,7 @@ sealed interface InReplyTo {
val eventId: EventId,
val content: EventContent,
val senderId: UserId,
val senderDisplayName: String?,
val senderAvatarUrl: String?,
val senderProfile: ProfileTimelineDetails,
) : InReplyTo
/**

View file

@ -52,3 +52,10 @@ fun ProfileTimelineDetails.getDisambiguatedDisplayName(userId: UserId): String {
else -> userId.value
}
}
fun ProfileTimelineDetails.getAvatarUrl(): String? {
return when (this) {
is ProfileTimelineDetails.Ready -> avatarUrl
else -> null
}
}

View file

@ -21,17 +21,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
interface SessionVerificationService {
/**
* This flow stores the local verification status of the current session.
*
* We should ideally base the verified status in the Rust SDK info, but there are several issues with that approach:
*
* - The SDK takes a while to report this value, resulting in a delay of 1-2s in displaying the UI.
* - We need to add a 'Skip' option for testing purposes, which would not be possible if we relied only on the SDK.
* - The SDK sometimes doesn't report the verification state if there is no network connection when the app boots.
*/
val needsVerificationFlow: StateFlow<Boolean>
/**
* State of the current verification flow ([VerificationFlowState.Initial] if not started).
*/
@ -83,11 +72,6 @@ interface SessionVerificationService {
* Returns the verification service state to the initial step.
*/
suspend fun reset()
/**
* Saves the current session state as [verified].
*/
suspend fun saveVerifiedState(verified: Boolean)
}
/** Verification status of the current session. */

View file

@ -29,7 +29,7 @@ class NotificationDataTest {
senderDisplayName = null,
senderIsNameAmbiguous = false,
)
assertThat(sut.getSenderName(A_USER_ID)).isEqualTo("@alice:server.org")
assertThat(sut.getDisambiguatedDisplayName(A_USER_ID)).isEqualTo("@alice:server.org")
}
@Test
@ -38,7 +38,7 @@ class NotificationDataTest {
senderDisplayName = "Alice",
senderIsNameAmbiguous = false,
)
assertThat(sut.getSenderName(A_USER_ID)).isEqualTo("Alice")
assertThat(sut.getDisambiguatedDisplayName(A_USER_ID)).isEqualTo("Alice")
}
@Test
@ -47,7 +47,7 @@ class NotificationDataTest {
senderDisplayName = "Alice",
senderIsNameAmbiguous = true,
)
assertThat(sut.getSenderName(A_USER_ID)).isEqualTo("Alice (@alice:server.org)")
assertThat(sut.getDisambiguatedDisplayName(A_USER_ID)).isEqualTo("Alice (@alice:server.org)")
}
private fun aNotificationData(

View file

@ -61,6 +61,7 @@ import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
import io.element.android.libraries.matrix.impl.room.map
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper
import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
@ -159,7 +160,6 @@ class RustMatrixClient(
syncService = rustSyncService,
sessionCoroutineScope = sessionCoroutineScope,
dispatchers = dispatchers,
sessionStore = sessionStore,
)
private val roomDirectoryService = RustRoomDirectoryService(
@ -187,7 +187,6 @@ class RustMatrixClient(
isTokenValid = false,
loginType = existingData.loginType,
passphrase = existingData.passphrase,
needsVerification = existingData.needsVerification,
)
sessionStore.updateData(newData)
Timber.d("Removed session data with token: '...$anonymizedToken'.")
@ -215,7 +214,6 @@ class RustMatrixClient(
isTokenValid = true,
loginType = existingData.loginType,
passphrase = existingData.passphrase,
needsVerification = existingData.needsVerification,
)
sessionStore.updateData(newData)
Timber.d("Saved new session data with token: '...$anonymizedToken'.")
@ -241,7 +239,6 @@ class RustMatrixClient(
client = client,
isSyncServiceReady = rustSyncService.syncState.map { it == SyncState.Running },
sessionCoroutineScope = sessionCoroutineScope,
sessionStore = sessionStore,
)
private val eventFilters = TimelineConfig.excludedEvents
@ -439,7 +436,7 @@ class RustMatrixClient(
runCatching { client.removeAvatar() }
}
override suspend fun joinRoom(roomId: RoomId): Result<RoomId> = withContext(sessionDispatcher) {
override suspend fun joinRoom(roomId: RoomId): Result<Unit> = withContext(sessionDispatcher) {
runCatching {
client.joinRoomById(roomId.value).destroy()
try {
@ -447,10 +444,13 @@ class RustMatrixClient(
} catch (e: Exception) {
Timber.e(e, "Timeout waiting for the room to be available in the room list")
}
roomId
}
}
override suspend fun knockRoom(roomId: RoomId): Result<Unit> {
return Result.failure(NotImplementedError("Not yet implemented"))
}
override suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result<Unit> = withContext(sessionDispatcher) {
runCatching {
client.trackRecentlyVisitedRoom(roomId.value)
@ -463,21 +463,15 @@ class RustMatrixClient(
}
}
@Suppress("TooGenericExceptionThrown")
override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result<RoomId> = withContext(sessionDispatcher) {
runCatching {
// TODO Waiting for SDK to be released
throw Exception("Not implemented")
// client.resolveRoomAlias(roomAlias.value).let(::RoomId)
client.resolveRoomAlias(roomAlias.value).let(::RoomId)
}
}
@Suppress("TooGenericExceptionThrown")
override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias): Result<RoomPreview> = withContext(sessionDispatcher) {
runCatching {
// TODO Waiting for SDK to be released
throw Exception("Not implemented")
// client.getRoomPreview(roomIdOrAlias.identifier).let(RoomPreviewMapper::map)
client.getRoomPreview(roomIdOrAlias.identifier).let(RoomPreviewMapper::map)
}
}

View file

@ -138,7 +138,6 @@ class RustMatrixAuthenticationService @Inject constructor(
isTokenValid = true,
loginType = LoginType.PASSWORD,
passphrase = pendingPassphrase,
needsVerification = true,
)
}
sessionStore.storeData(sessionData)
@ -187,7 +186,6 @@ class RustMatrixAuthenticationService @Inject constructor(
isTokenValid = true,
loginType = LoginType.OIDC,
passphrase = pendingPassphrase,
needsVerification = true,
)
}
pendingOidcAuthenticationData?.close()

View file

@ -25,7 +25,6 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import io.element.android.libraries.sessionstorage.api.SessionStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.currentCoroutineContext
@ -49,11 +48,10 @@ import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecover
import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException
internal class RustEncryptionService(
private val client: Client,
client: Client,
syncService: RustSyncService,
sessionCoroutineScope: CoroutineScope,
private val dispatchers: CoroutineDispatchers,
private val sessionStore: SessionStore,
) : EncryptionService {
private val service: Encryption = client.encryption()
@ -188,9 +186,6 @@ internal class RustEncryptionService(
override suspend fun recover(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) {
runCatching {
service.recover(recoveryKey)
val existingSession = sessionStore.getSession(client.userId())
?: error("Failed to save verification state. No session with id ${client.userId()}")
sessionStore.updateData(existingSession.copy(needsVerification = false))
}.mapFailure {
it.mapRecoveryException()
}

View file

@ -25,7 +25,6 @@ internal fun Session.toSessionData(
isTokenValid: Boolean,
loginType: LoginType,
passphrase: String?,
needsVerification: Boolean,
) = SessionData(
userId = userId,
deviceId = deviceId,
@ -38,5 +37,4 @@ internal fun Session.toSessionData(
isTokenValid = isTokenValid,
loginType = loginType,
passphrase = passphrase,
needsVerification = needsVerification,
)

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.matrix.api.room.RoomType
fun String?.toRoomType(): RoomType {
return when (this) {
null -> RoomType.Room
"m.space" -> RoomType.Space
else -> RoomType.Other(this)
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.room.preview
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
import io.element.android.libraries.matrix.impl.room.toRoomType
import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview
object RoomPreviewMapper {
fun map(roomPreview: RustRoomPreview): RoomPreview {
return RoomPreview(
roomId = RoomId(roomPreview.roomId),
canonicalAlias = roomPreview.canonicalAlias?.let(::RoomAlias),
name = roomPreview.name,
topic = roomPreview.topic,
avatarUrl = roomPreview.avatarUrl,
numberOfJoinedMembers = roomPreview.numJoinedMembers.toLong(),
roomType = roomPreview.roomType.toRoomType(),
isHistoryWorldReadable = roomPreview.isHistoryWorldReadable,
isJoined = roomPreview.isJoined,
isInvited = roomPreview.isInvited,
isPublic = roomPreview.isPublic,
canKnock = roomPreview.canKnock
)
}
}

View file

@ -40,7 +40,7 @@ val RoomListFilter.predicate
(roomSummary.details.numUnreadNotifications > 0 || roomSummary.details.isMarkedUnread)
}
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
roomSummary is RoomSummary.Filled && roomSummary.details.name.contains(pattern, ignoreCase = true)
roomSummary is RoomSummary.Filled && roomSummary.details.name.orEmpty().contains(pattern, ignoreCase = true)
}
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
roomSummary.isInvited()

View file

@ -33,7 +33,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto
}
return RoomSummaryDetails(
roomId = RoomId(roomInfo.id),
name = roomInfo.name ?: roomInfo.id,
name = roomInfo.name,
canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias),
isDirect = roomInfo.isDirect,
avatarUrl = roomInfo.avatarUrl,

View file

@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT
import io.element.android.libraries.matrix.impl.media.map
import org.matrix.rustcomponents.sdk.Message
import org.matrix.rustcomponents.sdk.MessageType
import org.matrix.rustcomponents.sdk.ProfileDetails
import org.matrix.rustcomponents.sdk.RepliedToEventDetails
import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.FormattedBody as RustFormattedBody
@ -51,13 +50,11 @@ class EventMessageMapper {
val inReplyToId = EventId(details.eventId)
when (val event = details.event) {
is RepliedToEventDetails.Ready -> {
val senderProfile = event.senderProfile as? ProfileDetails.Ready
InReplyTo.Ready(
eventId = inReplyToId,
content = timelineEventContentMapper.map(event.content),
senderId = UserId(event.sender),
senderDisplayName = senderProfile?.displayName,
senderAvatarUrl = senderProfile?.avatarUrl,
senderProfile = event.senderProfile.map(),
)
}
is RepliedToEventDetails.Error -> InReplyTo.Error

View file

@ -16,7 +16,6 @@
package io.element.android.libraries.matrix.impl.verification
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
@ -24,7 +23,6 @@ import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatu
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
import io.element.android.libraries.sessionstorage.api.SessionStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@ -33,10 +31,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@ -58,7 +53,6 @@ class RustSessionVerificationService(
private val client: Client,
isSyncServiceReady: Flow<Boolean>,
private val sessionCoroutineScope: CoroutineScope,
private val sessionStore: SessionStore,
) : SessionVerificationService, SessionVerificationControllerDelegate {
private val encryptionService: Encryption = client.encryption()
private lateinit var verificationController: SessionVerificationController
@ -80,11 +74,6 @@ class RustSessionVerificationService(
}
})
override val needsVerificationFlow: StateFlow<Boolean> = sessionStore.sessionsFlow()
.map { sessions -> sessions.firstOrNull { it.userId == client.userId() }?.needsVerification.orFalse() }
.distinctUntilChanged()
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
private val _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
override val verificationFlowState = _verificationFlowState.asStateFlow()
@ -98,6 +87,9 @@ class RustSessionVerificationService(
}
init {
// Update initial state in case sliding sync isn't ready
updateVerificationStatus(encryptionService.verificationState())
isReady.onEach { isReady ->
if (isReady) {
Timber.d("Starting verification service")
@ -165,7 +157,6 @@ class RustSessionVerificationService(
}
}
.onSuccess {
saveVerifiedState(true)
updateVerificationStatus(VerificationState.VERIFIED)
_verificationFlowState.value = VerificationFlowState.Finished
}
@ -195,14 +186,6 @@ class RustSessionVerificationService(
_verificationFlowState.value = VerificationFlowState.Initial
}
override suspend fun saveVerifiedState(verified: Boolean) = tryOrFail {
val existingSession = sessionStore.getSession(client.userId())
?: error("Failed to save verification state. No session with id ${client.userId()}")
sessionStore.updateData(existingSession.copy(needsVerification = !verified))
// Wait until the new state is saved
needsVerificationFlow.first { needsVerification -> !needsVerification }
}
fun destroy() {
Timber.d("Destroying RustSessionVerificationService")
verificationStateListenerTaskHandle.cancelAndDestroy()

View file

@ -22,13 +22,20 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.services.analytics.test.FakeAnalyticsService
import org.junit.Test
import org.matrix.rustcomponents.sdk.UnableToDecryptInfo
import uniffi.matrix_sdk_crypto.UtdCause
class UtdTrackerTest {
@Test
fun `when onUtd is called with null timeToDecryptMs, the expected analytics Event is sent`() {
val fakeAnalyticsService = FakeAnalyticsService()
val sut = UtdTracker(fakeAnalyticsService)
sut.onUtd(UnableToDecryptInfo(eventId = AN_EVENT_ID.value, timeToDecryptMs = null))
sut.onUtd(
UnableToDecryptInfo(
eventId = AN_EVENT_ID.value,
timeToDecryptMs = null,
cause = UtdCause.UNKNOWN,
)
)
assertThat(fakeAnalyticsService.capturedEvents).containsExactly(
Error(
context = null,
@ -47,7 +54,13 @@ class UtdTrackerTest {
fun `when onUtd is called with timeToDecryptMs, the expected analytics Event is sent`() {
val fakeAnalyticsService = FakeAnalyticsService()
val sut = UtdTracker(fakeAnalyticsService)
sut.onUtd(UnableToDecryptInfo(eventId = AN_EVENT_ID.value, timeToDecryptMs = 123.toULong()))
sut.onUtd(
UnableToDecryptInfo(
eventId = AN_EVENT_ID.value,
timeToDecryptMs = 123.toULong(),
cause = UtdCause.UNKNOWN,
)
)
assertThat(fakeAnalyticsService.capturedEvents).containsExactly(
Error(
context = null,

View file

@ -103,10 +103,12 @@ class FakeMatrixClient(
private var setDisplayNameResult: Result<Unit> = Result.success(Unit)
private var uploadAvatarResult: Result<Unit> = Result.success(Unit)
private var removeAvatarResult: Result<Unit> = Result.success(Unit)
var joinRoomLambda: (RoomId) -> Result<RoomId> = {
Result.success(it)
var joinRoomLambda: (RoomId) -> Result<Unit> = {
Result.success(Unit)
}
var knockRoomLambda: (RoomId) -> Result<Unit> = {
Result.success(Unit)
}
var getRoomInfoFlowLambda = { _: RoomId ->
flowOf<Optional<MatrixRoomInfo>>(Optional.empty())
}
@ -197,7 +199,9 @@ class FakeMatrixClient(
return removeAvatarResult
}
override suspend fun joinRoom(roomId: RoomId): Result<RoomId> = joinRoomLambda(roomId)
override suspend fun joinRoom(roomId: RoomId): Result<Unit> = joinRoomLambda(roomId)
override suspend fun knockRoom(roomId: RoomId): Result<Unit> = knockRoomLambda(roomId)
override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService

View file

@ -63,7 +63,7 @@ fun aRoomSummaryFilled(
fun aRoomSummaryDetails(
roomId: RoomId = A_ROOM_ID,
name: String = A_ROOM_NAME,
name: String? = A_ROOM_NAME,
isDirect: Boolean = false,
avatarUrl: String? = null,
lastMessage: RoomMessage? = aRoomMessage(),

View file

@ -20,22 +20,17 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationD
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.VerificationFlowState
import io.element.android.tests.testutils.lambda.LambdaOneParamRecorder
import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class FakeSessionVerificationService(
var saveVerifiedStateResult: LambdaOneParamRecorder<Boolean, Unit> = lambdaRecorder<Boolean, Unit> {}
) : SessionVerificationService {
class FakeSessionVerificationService : SessionVerificationService {
private val _isReady = MutableStateFlow(false)
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
private var _canVerifySessionFlow = MutableStateFlow(true)
var shouldFail = false
override val needsVerificationFlow: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val verificationFlowState: StateFlow<VerificationFlowState> = _verificationFlowState
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus
override val canVerifySessionFlow: Flow<Boolean> = _canVerifySessionFlow
@ -94,15 +89,7 @@ class FakeSessionVerificationService(
_isReady.value = value
}
fun givenNeedsVerification(value: Boolean) {
needsVerificationFlow.value = value
}
override suspend fun reset() {
_verificationFlowState.value = VerificationFlowState.Initial
}
override suspend fun saveVerifiedState(verified: Boolean) {
saveVerifiedStateResult(verified)
}
}