Merge branch 'develop' into feature/fga/join_space
This commit is contained in:
commit
c4308e9810
446 changed files with 5669 additions and 2617 deletions
|
|
@ -287,7 +287,6 @@ class RustMatrixClient(
|
|||
}
|
||||
|
||||
override suspend fun getRoom(roomId: RoomId): BaseRoom? = withContext(sessionDispatcher) {
|
||||
innerClient.rooms()
|
||||
roomFactory.getBaseRoom(roomId)
|
||||
}
|
||||
|
||||
|
|
@ -689,12 +688,6 @@ class RustMatrixClient(
|
|||
})
|
||||
}.buffer(Channel.UNLIMITED)
|
||||
|
||||
override suspend fun availableSlidingSyncVersions(): Result<List<SlidingSyncVersion>> = withContext(sessionDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerClient.availableSlidingSyncVersions().map { it.map() }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun currentSlidingSyncVersion(): Result<SlidingSyncVersion> = withContext(sessionDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerClient.session().slidingSyncVersion.map()
|
||||
|
|
|
|||
|
|
@ -33,11 +33,9 @@ import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator
|
|||
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -83,14 +81,6 @@ class RustMatrixAuthenticationService(
|
|||
.also { sessionPaths = it }
|
||||
}
|
||||
|
||||
override fun loggedInStateFlow(): Flow<LoggedInState> {
|
||||
return sessionStore.isLoggedIn()
|
||||
}
|
||||
|
||||
override suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io) {
|
||||
sessionStore.getLatestSession()?.userId?.let { SessionId(it) }
|
||||
}
|
||||
|
||||
override suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient> = withContext(coroutineDispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
val sessionData = sessionStore.getSession(sessionId.value)
|
||||
|
|
@ -158,7 +148,7 @@ class RustMatrixAuthenticationService(
|
|||
)
|
||||
val matrixClient = rustMatrixClientFactory.create(client)
|
||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||
sessionStore.storeData(sessionData)
|
||||
sessionStore.addSession(sessionData)
|
||||
|
||||
// Clean up the strong reference held here since it's no longer necessary
|
||||
currentClient = null
|
||||
|
|
@ -182,7 +172,7 @@ class RustMatrixAuthenticationService(
|
|||
sessionPaths = currentSessionPaths,
|
||||
)
|
||||
clear()
|
||||
sessionStore.storeData(sessionData)
|
||||
sessionStore.addSession(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
}
|
||||
}
|
||||
|
|
@ -250,7 +240,7 @@ class RustMatrixAuthenticationService(
|
|||
|
||||
val matrixClient = rustMatrixClientFactory.create(client)
|
||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||
sessionStore.storeData(sessionData)
|
||||
sessionStore.addSession(sessionData)
|
||||
|
||||
// Clean up the strong reference held here since it's no longer necessary
|
||||
currentClient = null
|
||||
|
|
@ -295,7 +285,7 @@ class RustMatrixAuthenticationService(
|
|||
)
|
||||
val matrixClient = rustMatrixClientFactory.create(client)
|
||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||
sessionStore.storeData(sessionData)
|
||||
sessionStore.addSession(sessionData)
|
||||
|
||||
// Clean up the strong reference held here since it's no longer necessary
|
||||
currentClient = null
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ package io.element.android.libraries.matrix.impl.notification
|
|||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallNotifyType
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationContent
|
||||
import io.element.android.libraries.matrix.api.notification.RtcNotificationType
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper
|
||||
import org.matrix.rustcomponents.sdk.MessageLikeEventContent
|
||||
import org.matrix.rustcomponents.sdk.NotifyType
|
||||
import org.matrix.rustcomponents.sdk.StateEventContent
|
||||
import org.matrix.rustcomponents.sdk.TimelineEvent
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventType
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import org.matrix.rustcomponents.sdk.RtcNotificationType as SdkRtcNotificationType
|
||||
|
||||
class TimelineEventToNotificationContentMapper {
|
||||
fun map(timelineEvent: TimelineEvent): Result<NotificationContent> {
|
||||
|
|
@ -78,7 +78,11 @@ private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationCon
|
|||
MessageLikeEventContent.CallCandidates -> NotificationContent.MessageLike.CallCandidates
|
||||
MessageLikeEventContent.CallHangup -> NotificationContent.MessageLike.CallHangup
|
||||
MessageLikeEventContent.CallInvite -> NotificationContent.MessageLike.CallInvite(senderId)
|
||||
is MessageLikeEventContent.CallNotify -> NotificationContent.MessageLike.CallNotify(senderId, notifyType.map())
|
||||
is MessageLikeEventContent.RtcNotification -> NotificationContent.MessageLike.RtcNotification(
|
||||
senderId = senderId,
|
||||
type = notificationType.map(),
|
||||
expirationTimestampMillis = expirationTs.toLong()
|
||||
)
|
||||
MessageLikeEventContent.KeyVerificationAccept -> NotificationContent.MessageLike.KeyVerificationAccept
|
||||
MessageLikeEventContent.KeyVerificationCancel -> NotificationContent.MessageLike.KeyVerificationCancel
|
||||
MessageLikeEventContent.KeyVerificationDone -> NotificationContent.MessageLike.KeyVerificationDone
|
||||
|
|
@ -101,7 +105,7 @@ private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationCon
|
|||
}
|
||||
}
|
||||
|
||||
private fun NotifyType.map(): CallNotifyType = when (this) {
|
||||
NotifyType.NOTIFY -> CallNotifyType.NOTIFY
|
||||
NotifyType.RING -> CallNotifyType.RING
|
||||
private fun SdkRtcNotificationType.map(): RtcNotificationType = when (this) {
|
||||
SdkRtcNotificationType.NOTIFICATION -> RtcNotificationType.NOTIFY
|
||||
SdkRtcNotificationType.RING -> RtcNotificationType.RING
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ fun MessageEventType.map(): MessageLikeEventType = when (this) {
|
|||
MessageEventType.CALL_INVITE -> MessageLikeEventType.CALL_INVITE
|
||||
MessageEventType.CALL_HANGUP -> MessageLikeEventType.CALL_HANGUP
|
||||
MessageEventType.CALL_CANDIDATES -> MessageLikeEventType.CALL_CANDIDATES
|
||||
MessageEventType.CALL_NOTIFY -> MessageLikeEventType.CALL_NOTIFY
|
||||
MessageEventType.RTC_NOTIFICATION -> MessageLikeEventType.RTC_NOTIFICATION
|
||||
MessageEventType.KEY_VERIFICATION_READY -> MessageLikeEventType.KEY_VERIFICATION_READY
|
||||
MessageEventType.KEY_VERIFICATION_START -> MessageLikeEventType.KEY_VERIFICATION_START
|
||||
MessageEventType.KEY_VERIFICATION_CANCEL -> MessageLikeEventType.KEY_VERIFICATION_CANCEL
|
||||
|
|
@ -41,7 +41,7 @@ fun MessageLikeEventType.map(): MessageEventType = when (this) {
|
|||
MessageLikeEventType.CALL_INVITE -> MessageEventType.CALL_INVITE
|
||||
MessageLikeEventType.CALL_HANGUP -> MessageEventType.CALL_HANGUP
|
||||
MessageLikeEventType.CALL_CANDIDATES -> MessageEventType.CALL_CANDIDATES
|
||||
MessageLikeEventType.CALL_NOTIFY -> MessageEventType.CALL_NOTIFY
|
||||
MessageLikeEventType.RTC_NOTIFICATION -> MessageEventType.RTC_NOTIFICATION
|
||||
MessageLikeEventType.KEY_VERIFICATION_READY -> MessageEventType.KEY_VERIFICATION_READY
|
||||
MessageLikeEventType.KEY_VERIFICATION_START -> MessageEventType.KEY_VERIFICATION_START
|
||||
MessageLikeEventType.KEY_VERIFICATION_CANCEL -> MessageEventType.KEY_VERIFICATION_CANCEL
|
||||
|
|
|
|||
|
|
@ -38,10 +38,12 @@ import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
|
|||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.CallDeclineListener
|
||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
|
|
@ -300,4 +302,20 @@ class RustBaseRoom(
|
|||
innerRoom.reportRoom(reason.orEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun declineCall(notificationEventId: EventId): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerRoom.declineCall(notificationEventId.value)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun subscribeToCallDecline(notificationEventId: EventId): Flow<UserId> = withContext(roomDispatcher) {
|
||||
mxCallbackFlow {
|
||||
innerRoom.subscribeToCallDeclineEvents(notificationEventId.value, object : CallDeclineListener {
|
||||
override fun call(declinerUserId: String) {
|
||||
trySend(UserId(declinerUserId))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ fun RustAllowRule.map(): AllowRule {
|
|||
|
||||
fun AllowRule.map(): RustAllowRule {
|
||||
return when (this) {
|
||||
is AllowRule.RoomMembership -> RustAllowRule.RoomMembership(roomId.toString())
|
||||
is AllowRule.RoomMembership -> RustAllowRule.RoomMembership(roomId.value)
|
||||
is AllowRule.Custom -> RustAllowRule.Custom(json)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ object RoomMemberMapper {
|
|||
membership = mapMembership(roomMember.membership),
|
||||
isNameAmbiguous = roomMember.isNameAmbiguous,
|
||||
powerLevel = powerLevel,
|
||||
normalizedPowerLevel = roomMember.normalizedPowerLevel.into(),
|
||||
isIgnored = roomMember.isIgnored,
|
||||
role = mapRole(roomMember.suggestedRoleForPowerLevel, powerLevel),
|
||||
membershipChangeReason = roomMember.membershipChangeReason
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ class RustTimeline(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = withContext(dispatcher) {
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Boolean> = withContext(dispatcher) {
|
||||
runCatchingExceptions {
|
||||
inner.toggleReaction(
|
||||
key = emoji,
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ class TimelineEventContentMapper(
|
|||
)
|
||||
}
|
||||
is TimelineItemContent.CallInvite -> LegacyCallInviteContent
|
||||
is TimelineItemContent.CallNotify -> CallNotifyContent
|
||||
is TimelineItemContent.RtcNotification -> CallNotifyContent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
|||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.matrix.rustcomponents.sdk.newVirtualElementCallWidget
|
||||
import timber.log.Timber
|
||||
import uniffi.matrix_sdk.EncryptionSystem
|
||||
import uniffi.matrix_sdk.HeaderStyle
|
||||
import uniffi.matrix_sdk.NotificationType
|
||||
import uniffi.matrix_sdk.VirtualElementCallWidgetOptions
|
||||
import uniffi.matrix_sdk.VirtualElementCallWidgetConfig
|
||||
import uniffi.matrix_sdk.VirtualElementCallWidgetProperties
|
||||
import uniffi.matrix_sdk.Intent as CallIntent
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
|
|
@ -31,19 +31,14 @@ class DefaultCallWidgetSettingsProvider(
|
|||
private val callAnalyticsCredentialsProvider: CallAnalyticCredentialsProvider,
|
||||
private val analyticsService: AnalyticsService,
|
||||
) : CallWidgetSettingsProvider {
|
||||
override suspend fun provide(baseUrl: String, widgetId: String, encrypted: Boolean, direct: Boolean): MatrixWidgetSettings {
|
||||
override suspend fun provide(baseUrl: String, widgetId: String, encrypted: Boolean, direct: Boolean, hasActiveCall: Boolean): MatrixWidgetSettings {
|
||||
val isAnalyticsEnabled = analyticsService.userConsentFlow.first()
|
||||
val options = VirtualElementCallWidgetOptions(
|
||||
val properties = VirtualElementCallWidgetProperties(
|
||||
elementCallUrl = baseUrl,
|
||||
widgetId = widgetId,
|
||||
preload = null,
|
||||
fontScale = null,
|
||||
appPrompt = false,
|
||||
confineToRoom = true,
|
||||
font = null,
|
||||
encryption = if (encrypted) EncryptionSystem.PerParticipantKeys else EncryptionSystem.Unencrypted,
|
||||
intent = CallIntent.START_CALL,
|
||||
hideScreensharing = false,
|
||||
posthogUserId = callAnalyticsCredentialsProvider.posthogUserId.takeIf { isAnalyticsEnabled },
|
||||
posthogApiHost = callAnalyticsCredentialsProvider.posthogApiHost.takeIf { isAnalyticsEnabled },
|
||||
posthogApiKey = callAnalyticsCredentialsProvider.posthogApiKey.takeIf { isAnalyticsEnabled },
|
||||
|
|
@ -51,13 +46,25 @@ class DefaultCallWidgetSettingsProvider(
|
|||
sentryDsn = callAnalyticsCredentialsProvider.sentryDsn.takeIf { isAnalyticsEnabled },
|
||||
sentryEnvironment = if (buildMeta.buildType == BuildType.RELEASE) "RELEASE" else "DEBUG",
|
||||
parentUrl = null,
|
||||
// For backwards compatibility, it'll be ignored in recent versions of Element Call
|
||||
hideHeader = true,
|
||||
controlledMediaDevices = true,
|
||||
header = HeaderStyle.APP_BAR,
|
||||
sendNotificationType = if (direct) NotificationType.RING else NotificationType.NOTIFICATION,
|
||||
)
|
||||
val rustWidgetSettings = newVirtualElementCallWidget(options)
|
||||
val config = VirtualElementCallWidgetConfig(
|
||||
// TODO remove this once we have the next EC version
|
||||
preload = false,
|
||||
// TODO remove this once we have the next EC version
|
||||
skipLobby = null,
|
||||
intent = when {
|
||||
direct && hasActiveCall -> CallIntent.JOIN_EXISTING_DM
|
||||
hasActiveCall -> CallIntent.JOIN_EXISTING
|
||||
direct -> CallIntent.START_CALL_DM
|
||||
else -> CallIntent.START_CALL
|
||||
}.also {
|
||||
Timber.d("Starting/joining call with intent: $it")
|
||||
}
|
||||
)
|
||||
val rustWidgetSettings = newVirtualElementCallWidget(
|
||||
props = properties,
|
||||
config = config,
|
||||
)
|
||||
return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ package io.element.android.libraries.matrix.impl
|
|||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClientBuilder
|
||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||
|
||||
class FakeClientBuilderProvider : ClientBuilderProvider {
|
||||
class FakeClientBuilderProvider(
|
||||
private val provideResult: () -> ClientBuilder = { FakeFfiClientBuilder() }
|
||||
) : ClientBuilderProvider {
|
||||
override fun provide(): ClientBuilder {
|
||||
return FakeFfiClientBuilder()
|
||||
return provideResult()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ fun TestScope.createRustMatrixClientFactory(
|
|||
baseDirectory: File = File("/base"),
|
||||
cacheDirectory: File = File("/cache"),
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
|
||||
) = RustMatrixClientFactory(
|
||||
baseDirectory = baseDirectory,
|
||||
cacheDirectory = cacheDirectory,
|
||||
|
|
@ -52,5 +53,5 @@ fun TestScope.createRustMatrixClientFactory(
|
|||
analyticsService = FakeAnalyticsService(),
|
||||
featureFlagService = FakeFeatureFlagService(),
|
||||
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
|
||||
clientBuilderProvider = FakeClientBuilderProvider(),
|
||||
clientBuilderProvider = clientBuilderProvider,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,14 +8,17 @@
|
|||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.impl.ClientBuilderProvider
|
||||
import io.element.android.libraries.matrix.impl.FakeClientBuilderProvider
|
||||
import io.element.android.libraries.matrix.impl.createRustMatrixClientFactory
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClientBuilder
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiHomeserverLoginDetails
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||
import io.element.android.libraries.matrix.test.auth.FakeOidcRedirectUrlProvider
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -24,18 +27,28 @@ import java.io.File
|
|||
|
||||
class RustMatrixAuthenticationServiceTest {
|
||||
@Test
|
||||
fun `getLatestSessionId should return the value from the store`() = runTest {
|
||||
val sessionStore = InMemorySessionStore()
|
||||
fun `setHomeserver is successful`() = runTest {
|
||||
val sut = createRustMatrixAuthenticationService(
|
||||
sessionStore = sessionStore,
|
||||
clientBuilderProvider = FakeClientBuilderProvider(
|
||||
provideResult = {
|
||||
FakeFfiClientBuilder(
|
||||
buildResult = {
|
||||
FakeFfiClient(
|
||||
homeserverLoginDetailsResult = {
|
||||
FakeFfiHomeserverLoginDetails()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
assertThat(sut.getLatestSessionId()).isNull()
|
||||
sessionStore.storeData(aSessionData(sessionId = "@alice:server.org"))
|
||||
assertThat(sut.getLatestSessionId()).isEqualTo(SessionId("@alice:server.org"))
|
||||
assertThat(sut.setHomeserver("matrix.org").isSuccess).isTrue()
|
||||
}
|
||||
|
||||
private fun TestScope.createRustMatrixAuthenticationService(
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
|
||||
): RustMatrixAuthenticationService {
|
||||
val baseDirectory = File("/base")
|
||||
val cacheDirectory = File("/cache")
|
||||
|
|
@ -43,6 +56,7 @@ class RustMatrixAuthenticationServiceTest {
|
|||
baseDirectory = baseDirectory,
|
||||
cacheDirectory = cacheDirectory,
|
||||
sessionStore = sessionStore,
|
||||
clientBuilderProvider = clientBuilderProvider,
|
||||
)
|
||||
return RustMatrixAuthenticationService(
|
||||
sessionPathsFactory = SessionPathsFactory(baseDirectory, cacheDirectory),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ fun aRustRoomMember(
|
|||
membership = membership,
|
||||
isNameAmbiguous = isNameAmbiguous,
|
||||
powerLevel = powerLevel,
|
||||
normalizedPowerLevel = powerLevel,
|
||||
isIgnored = isIgnored,
|
||||
suggestedRoleForPowerLevel = role,
|
||||
membershipChangeReason = membershipChangeReason,
|
||||
|
|
|
|||
|
|
@ -43,4 +43,5 @@ fun aRustSpaceRoom(
|
|||
childrenCount = childrenCount,
|
||||
state = state,
|
||||
heroes = heroes,
|
||||
via = emptyList()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import io.element.android.tests.testutils.simulateLongTask
|
|||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientDelegate
|
||||
import org.matrix.rustcomponents.sdk.Encryption
|
||||
import org.matrix.rustcomponents.sdk.HomeserverLoginDetails
|
||||
import org.matrix.rustcomponents.sdk.IgnoredUsersListener
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.NotificationClient
|
||||
|
|
@ -41,6 +42,7 @@ class FakeFfiClient(
|
|||
private val session: Session = aRustSession(),
|
||||
private val clearCachesResult: () -> Unit = { lambdaError() },
|
||||
private val withUtdHook: (UnableToDecryptDelegate) -> Unit = { lambdaError() },
|
||||
private val homeserverLoginDetailsResult: () -> HomeserverLoginDetails = { lambdaError() },
|
||||
private val closeResult: () -> Unit = {},
|
||||
) : Client(NoPointer) {
|
||||
override fun userId(): String = userId
|
||||
|
|
@ -71,6 +73,7 @@ class FakeFfiClient(
|
|||
override suspend fun ignoredUsers(): List<String> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun subscribeToIgnoredUsers(listener: IgnoredUsersListener): TaskHandle {
|
||||
return FakeFfiTaskHandle()
|
||||
}
|
||||
|
|
@ -78,5 +81,10 @@ class FakeFfiClient(
|
|||
override suspend fun getProfile(userId: String): UserProfile {
|
||||
return UserProfile(userId = userId, displayName = null, avatarUrl = null)
|
||||
}
|
||||
|
||||
override suspend fun homeserverLoginDetails(): HomeserverLoginDetails {
|
||||
return homeserverLoginDetailsResult()
|
||||
}
|
||||
|
||||
override fun close() = closeResult()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ import uniffi.matrix_sdk.BackupDownloadStrategy
|
|||
import uniffi.matrix_sdk_crypto.CollectStrategy
|
||||
import uniffi.matrix_sdk_crypto.DecryptionSettings
|
||||
|
||||
class FakeFfiClientBuilder : ClientBuilder(NoPointer) {
|
||||
class FakeFfiClientBuilder(
|
||||
val buildResult: () -> Client = { FakeFfiClient(withUtdHook = {}) }
|
||||
) : ClientBuilder(NoPointer) {
|
||||
override fun addRootCertificates(certificates: List<ByteArray>) = this
|
||||
override fun autoEnableBackups(autoEnableBackups: Boolean) = this
|
||||
override fun autoEnableCrossSigning(autoEnableCrossSigning: Boolean) = this
|
||||
|
|
@ -41,7 +43,5 @@ class FakeFfiClientBuilder : ClientBuilder(NoPointer) {
|
|||
override fun enableShareHistoryOnInvite(enableShareHistoryOnInvite: Boolean): ClientBuilder = this
|
||||
override fun threadsEnabled(enabled: Boolean, threadSubscriptions: Boolean): ClientBuilder = this
|
||||
|
||||
override suspend fun build(): Client {
|
||||
return FakeFfiClient(withUtdHook = {})
|
||||
}
|
||||
override suspend fun build() = buildResult()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.SpaceListUpdate
|
||||
import org.matrix.rustcomponents.sdk.SpaceRoom
|
||||
import org.matrix.rustcomponents.sdk.SpaceRoomList
|
||||
import org.matrix.rustcomponents.sdk.SpaceRoomListEntriesListener
|
||||
import org.matrix.rustcomponents.sdk.SpaceRoomListPaginationStateListener
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
|
||||
|
||||
class FakeFfiSpaceRoomList(
|
||||
private val paginateResult: () -> Unit = { lambdaError() },
|
||||
private val paginationStateResult: () -> SpaceRoomListPaginationState = { lambdaError() },
|
||||
private val roomsResult: () -> List<SpaceRoom> = { lambdaError() },
|
||||
) : SpaceRoomList(NoPointer) {
|
||||
private var spaceRoomListPaginationStateListener: SpaceRoomListPaginationStateListener? = null
|
||||
private var spaceRoomListEntriesListener: SpaceRoomListEntriesListener? = null
|
||||
|
||||
override suspend fun paginate() = simulateLongTask {
|
||||
paginateResult()
|
||||
}
|
||||
|
||||
override fun paginationState(): SpaceRoomListPaginationState {
|
||||
return paginationStateResult()
|
||||
}
|
||||
|
||||
override fun rooms(): List<SpaceRoom> {
|
||||
return roomsResult()
|
||||
}
|
||||
|
||||
override fun subscribeToPaginationStateUpdates(listener: SpaceRoomListPaginationStateListener): TaskHandle {
|
||||
spaceRoomListPaginationStateListener = listener
|
||||
return FakeFfiTaskHandle()
|
||||
}
|
||||
|
||||
fun triggerPaginationStateUpdate(state: SpaceRoomListPaginationState) {
|
||||
spaceRoomListPaginationStateListener?.onUpdate(state)
|
||||
}
|
||||
|
||||
override fun subscribeToRoomUpdate(listener: SpaceRoomListEntriesListener): TaskHandle {
|
||||
spaceRoomListEntriesListener = listener
|
||||
return FakeFfiTaskHandle()
|
||||
}
|
||||
|
||||
fun triggerRoomListUpdate(rooms: List<SpaceListUpdate>) {
|
||||
spaceRoomListEntriesListener?.onUpdate(rooms)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ class MessageEventTypeKtTest {
|
|||
assertThat(MessageLikeEventType.CALL_INVITE.map()).isEqualTo(MessageEventType.CALL_INVITE)
|
||||
assertThat(MessageLikeEventType.CALL_HANGUP.map()).isEqualTo(MessageEventType.CALL_HANGUP)
|
||||
assertThat(MessageLikeEventType.CALL_CANDIDATES.map()).isEqualTo(MessageEventType.CALL_CANDIDATES)
|
||||
assertThat(MessageLikeEventType.CALL_NOTIFY.map()).isEqualTo(MessageEventType.CALL_NOTIFY)
|
||||
assertThat(MessageLikeEventType.RTC_NOTIFICATION.map()).isEqualTo(MessageEventType.RTC_NOTIFICATION)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_READY.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_READY)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_START.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_START)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_CANCEL.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_CANCEL)
|
||||
|
|
@ -46,7 +46,7 @@ class MessageEventTypeKtTest {
|
|||
assertThat(MessageEventType.CALL_INVITE.map()).isEqualTo(MessageLikeEventType.CALL_INVITE)
|
||||
assertThat(MessageEventType.CALL_HANGUP.map()).isEqualTo(MessageLikeEventType.CALL_HANGUP)
|
||||
assertThat(MessageEventType.CALL_CANDIDATES.map()).isEqualTo(MessageLikeEventType.CALL_CANDIDATES)
|
||||
assertThat(MessageEventType.CALL_NOTIFY.map()).isEqualTo(MessageLikeEventType.CALL_NOTIFY)
|
||||
assertThat(MessageEventType.RTC_NOTIFICATION.map()).isEqualTo(MessageLikeEventType.RTC_NOTIFICATION)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_READY.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_READY)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_START.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_START)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_CANCEL.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_CANCEL)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package io.element.android.libraries.matrix.impl.spaces
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSpaceRoom
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSpaceRoomList
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.SpaceListUpdate
|
||||
import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
|
||||
import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList
|
||||
|
||||
class RustSpaceRoomListTest {
|
||||
@Test
|
||||
fun `paginationStatusFlow emits values`() = runTest {
|
||||
val innerSpaceRoomList = FakeFfiSpaceRoomList(
|
||||
paginationStateResult = { SpaceRoomListPaginationState.Idle(false) }
|
||||
)
|
||||
val sut = createRustSpaceRoomList(
|
||||
innerSpaceRoomList = innerSpaceRoomList,
|
||||
)
|
||||
sut.paginationStatusFlow.test {
|
||||
// First value is the initial one
|
||||
assertThat(awaitItem()).isEqualTo(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false))
|
||||
// First value after the subscription occurs
|
||||
assertThat(awaitItem()).isEqualTo(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = true))
|
||||
innerSpaceRoomList.triggerPaginationStateUpdate(SpaceRoomListPaginationState.Loading)
|
||||
assertThat(awaitItem()).isEqualTo(SpaceRoomList.PaginationStatus.Loading)
|
||||
innerSpaceRoomList.triggerPaginationStateUpdate(SpaceRoomListPaginationState.Idle(true))
|
||||
assertThat(awaitItem()).isEqualTo(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false))
|
||||
innerSpaceRoomList.triggerPaginationStateUpdate(SpaceRoomListPaginationState.Idle(false))
|
||||
assertThat(awaitItem()).isEqualTo(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = true))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `spaceRoomsFlow emits values`() = runTest {
|
||||
val innerSpaceRoomList = FakeFfiSpaceRoomList(
|
||||
paginationStateResult = { SpaceRoomListPaginationState.Idle(false) }
|
||||
)
|
||||
val sut = createRustSpaceRoomList(
|
||||
innerSpaceRoomList = innerSpaceRoomList,
|
||||
)
|
||||
sut.spaceRoomsFlow.test {
|
||||
// Give time for the subscription to be set
|
||||
runCurrent()
|
||||
innerSpaceRoomList.triggerRoomListUpdate(
|
||||
listOf(
|
||||
SpaceListUpdate.PushBack(aRustSpaceRoom(roomId = A_ROOM_ID_2))
|
||||
)
|
||||
)
|
||||
val rooms = awaitItem()
|
||||
assertThat(rooms).hasSize(1)
|
||||
assertThat(rooms[0].roomId).isEqualTo(A_ROOM_ID_2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `paginate invokes paginate on the inner class`() = runTest {
|
||||
val paginateResult = lambdaRecorder<Unit> { }
|
||||
val innerSpaceRoomList = FakeFfiSpaceRoomList(
|
||||
paginateResult = paginateResult,
|
||||
)
|
||||
val sut = createRustSpaceRoomList(
|
||||
innerSpaceRoomList = innerSpaceRoomList,
|
||||
)
|
||||
sut.paginate()
|
||||
paginateResult.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `currentSpaceFlow reads value from the SpaceRoomCache`() = runTest {
|
||||
val spaceRoomCache = SpaceRoomCache()
|
||||
val sut = createRustSpaceRoomList(
|
||||
spaceRoomCache = spaceRoomCache,
|
||||
)
|
||||
sut.currentSpaceFlow().test {
|
||||
assertThat(awaitItem()).isNull()
|
||||
val spaceRoom = aSpaceRoom(roomId = A_ROOM_ID)
|
||||
spaceRoomCache.update(listOf(spaceRoom))
|
||||
assertThat(awaitItem()).isEqualTo(spaceRoom)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createRustSpaceRoomList(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
innerSpaceRoomList: InnerSpaceRoomList = FakeFfiSpaceRoomList(),
|
||||
innerProvider: suspend () -> InnerSpaceRoomList = { innerSpaceRoomList },
|
||||
spaceRoomMapper: SpaceRoomMapper = SpaceRoomMapper(),
|
||||
spaceRoomCache: SpaceRoomCache = SpaceRoomCache(),
|
||||
): RustSpaceRoomList {
|
||||
return RustSpaceRoomList(
|
||||
roomId = roomId,
|
||||
innerProvider = innerProvider,
|
||||
sessionCoroutineScope = backgroundScope,
|
||||
spaceRoomMapper = spaceRoomMapper,
|
||||
spaceRoomCache = spaceRoomCache,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue