Merge branch 'develop' into feature/fga/space_ui_tweaks

This commit is contained in:
ganfra 2026-02-10 09:31:50 +01:00
commit 0dec3a1cb6
174 changed files with 2905 additions and 1323 deletions

View file

@ -13,6 +13,7 @@ sealed class QrLoginException : Exception() {
data object ConnectionInsecure : QrLoginException()
data object Declined : QrLoginException()
data object Expired : QrLoginException()
data object NotFound : QrLoginException()
data object LinkingNotSupported : QrLoginException()
data object OidcMetadataInvalid : QrLoginException()
data object SlidingSyncNotAvailable : QrLoginException()
@ -20,5 +21,4 @@ sealed class QrLoginException : Exception() {
data object CheckCodeAlreadySent : QrLoginException()
data object CheckCodeCannotBeSent : QrLoginException()
data object Unknown : QrLoginException()
data object NotFound : QrLoginException()
}

View file

@ -8,6 +8,9 @@
package io.element.android.libraries.matrix.api.room.history
import androidx.compose.runtime.Immutable
@Immutable
sealed interface RoomHistoryVisibility {
/**
* Previous events are accessible to newly joined members from the point

View file

@ -28,4 +28,10 @@ sealed interface LatestEventValue {
val senderProfile: ProfileDetails,
val isSending: Boolean,
) : LatestEventValue
data class RoomInvite(
val timestamp: Long,
val inviterId: UserId?,
val invitedProfile: ProfileDetails,
) : LatestEventValue
}

View file

@ -8,6 +8,8 @@
package io.element.android.libraries.matrix.api.roomlist
import io.element.android.libraries.matrix.api.core.RoomId
sealed interface RoomListFilter {
companion object {
/**
@ -41,6 +43,10 @@ sealed interface RoomListFilter {
val filters: List<RoomListFilter>
) : RoomListFilter
data class Identifiers(
val values: List<RoomId>,
) : RoomListFilter
/**
* A filter that matches rooms that are unread.
*/

View file

@ -19,6 +19,7 @@ data class RoomSummary(
is LatestEventValue.None -> null
is LatestEventValue.Local -> latestEvent.timestamp
is LatestEventValue.Remote -> latestEvent.timestamp
is LatestEventValue.RoomInvite -> latestEvent.timestamp
}
val isOneToOne get() = info.activeMembersCount == 2L
}

View file

@ -12,9 +12,8 @@ import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.coroutines.flow.SharedFlow
interface SpaceService {
val spaceRoomsFlow: SharedFlow<List<SpaceRoom>>
suspend fun joinedSpaces(): Result<List<SpaceRoom>>
val topLevelSpacesFlow: SharedFlow<List<SpaceRoom>>
val spaceFiltersFlow: SharedFlow<List<SpaceServiceFilter>>
suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>>
suspend fun getSpaceRoom(spaceId: RoomId): SpaceRoom?

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.api.spaces
import io.element.android.libraries.matrix.api.core.RoomId
/**
* Represents a space filter for filtering rooms by space membership.
*
* @property spaceRoom The space room associated with this filter.
* @property level The nesting level of the space (0 = top level, 1 = first level child, etc.).
* @property descendants The list of room IDs that are descendants of this space.
*/
data class SpaceServiceFilter(
val spaceRoom: SpaceRoom,
val level: Int,
val descendants: List<RoomId>,
)

View file

@ -20,6 +20,7 @@ import org.matrix.rustcomponents.sdk.ClientDelegate
import org.matrix.rustcomponents.sdk.ClientSessionDelegate
import org.matrix.rustcomponents.sdk.Session
import timber.log.Timber
import uniffi.matrix_sdk_common.BackgroundTaskFailureReason
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean
@ -120,6 +121,11 @@ class RustClientSessionDelegate(
}
}
override fun onBackgroundTaskErrorReport(taskName: String, error: BackgroundTaskFailureReason) {
// TODO actually implement the missing logic to report to sentry and crash the app
Timber.tag(loggerTag.value).e("onBackgroundTaskErrorReport(taskName=$taskName, error=$error)")
}
override fun retrieveSessionFromKeychain(userId: String): Session {
// This should never be called, as it's only used for multi-process setups
error("retrieveSessionFromKeychain should never be called for Android")

View file

@ -66,7 +66,7 @@ import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
import io.element.android.libraries.matrix.impl.room.RoomInfoMapper
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
import io.element.android.libraries.matrix.impl.room.RustRoomFactory
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
import io.element.android.libraries.matrix.impl.room.TimelineEventFilterFactory
import io.element.android.libraries.matrix.impl.room.history.map
import io.element.android.libraries.matrix.impl.room.join.map
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
@ -141,7 +141,7 @@ class RustMatrixClient(
dispatchers: CoroutineDispatchers,
baseCacheDirectory: File,
clock: SystemClock,
timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
timelineEventFilterFactory: TimelineEventFilterFactory,
private val featureFlagService: FeatureFlagService,
private val analyticsService: AnalyticsService,
private val workManagerScheduler: WorkManagerScheduler,
@ -225,7 +225,7 @@ class RustMatrixClient(
systemClock = clock,
roomContentForwarder = RoomContentForwarder(innerRoomListService),
roomSyncSubscriber = roomSyncSubscriber,
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
timelineEventFilterFactory = timelineEventFilterFactory,
roomMembershipObserver = roomMembershipObserver,
roomInfoMapper = roomInfoMapper,
featureFlagService = featureFlagService,

View file

@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.impl.certificates.UserCertificatesPro
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
import io.element.android.libraries.matrix.impl.room.TimelineEventFilterFactory
import io.element.android.libraries.matrix.impl.storage.SqliteStoreBuilderProvider
import io.element.android.libraries.matrix.impl.util.anonymizedTokens
import io.element.android.libraries.network.useragent.UserAgentProvider
@ -61,7 +61,7 @@ class RustMatrixClientFactory(
private val clock: SystemClock,
private val analyticsService: AnalyticsService,
private val featureFlagService: FeatureFlagService,
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
private val timelineEventFilterFactory: TimelineEventFilterFactory,
private val clientBuilderProvider: ClientBuilderProvider,
private val sqliteStoreBuilderProvider: SqliteStoreBuilderProvider,
private val workManagerScheduler: WorkManagerScheduler,
@ -115,7 +115,7 @@ class RustMatrixClientFactory(
dispatchers = coroutineDispatchers,
baseCacheDirectory = cacheDirectory,
clock = clock,
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
timelineEventFilterFactory = timelineEventFilterFactory,
featureFlagService = featureFlagService,
analyticsService = analyticsService,
workManagerScheduler = workManagerScheduler,

View file

@ -38,6 +38,7 @@ object QrErrorMapper {
is RustHumanQrLoginException.ConnectionInsecure -> QrLoginException.ConnectionInsecure
is RustHumanQrLoginException.Declined -> QrLoginException.Declined
is RustHumanQrLoginException.Expired -> QrLoginException.Expired
is RustHumanQrLoginException.NotFound -> QrLoginException.NotFound
is RustHumanQrLoginException.OtherDeviceNotSignedIn -> QrLoginException.OtherDeviceNotSignedIn
is RustHumanQrLoginException.LinkingNotSupported -> QrLoginException.LinkingNotSupported
is RustHumanQrLoginException.Unknown -> QrLoginException.Unknown
@ -45,6 +46,5 @@ object QrErrorMapper {
is RustHumanQrLoginException.SlidingSyncNotAvailable -> QrLoginException.SlidingSyncNotAvailable
is RustHumanQrLoginException.CheckCodeAlreadySent -> QrLoginException.CheckCodeAlreadySent
is RustHumanQrLoginException.CheckCodeCannotBeSent -> QrLoginException.CheckCodeCannotBeSent
is RustHumanQrLoginException.NotFound -> QrLoginException.NotFound
}
}

View file

@ -80,6 +80,7 @@ import org.matrix.rustcomponents.sdk.getElementCallRequiredPermissions
import org.matrix.rustcomponents.sdk.use
import timber.log.Timber
import uniffi.matrix_sdk.RoomPowerLevelChanges
import uniffi.matrix_sdk_ui.TimelineEventFocusThreadMode
import uniffi.matrix_sdk_ui.TimelineReadReceiptTracking
import kotlin.coroutines.cancellation.CancellationException
import org.matrix.rustcomponents.sdk.IdentityStatusChange as RustIdentityStateChange
@ -177,21 +178,18 @@ class JoinedRustRoom(
): Result<Timeline> = withContext(roomDispatcher) {
val hideThreadedEvents = featureFlagService.isFeatureEnabled(FeatureFlags.Threads)
val focus = when (createTimelineParams) {
is CreateTimelineParams.PinnedOnly -> TimelineFocus.PinnedEvents(
maxEventsToLoad = 100u,
maxConcurrentRequests = 10u,
)
is CreateTimelineParams.PinnedOnly -> TimelineFocus.PinnedEvents
is CreateTimelineParams.MediaOnly -> TimelineFocus.Live(hideThreadedEvents = hideThreadedEvents)
is CreateTimelineParams.Focused -> TimelineFocus.Event(
eventId = createTimelineParams.focusedEventId.value,
numContextEvents = 50u,
hideThreadedEvents = hideThreadedEvents,
threadMode = TimelineEventFocusThreadMode.Automatic(hideThreadedEvents),
)
is CreateTimelineParams.MediaOnlyFocused -> TimelineFocus.Event(
eventId = createTimelineParams.focusedEventId.value,
numContextEvents = 50u,
// Never hide threaded events in media focused timeline
hideThreadedEvents = false,
threadMode = TimelineEventFocusThreadMode.Automatic(false),
)
is CreateTimelineParams.Threaded -> TimelineFocus.Thread(
rootEventId = createTimelineParams.threadRootEventId.value,

View file

@ -57,7 +57,7 @@ class RustRoomFactory(
private val roomListService: RoomListService,
private val innerRoomListService: InnerRoomListService,
private val roomSyncSubscriber: RoomSyncSubscriber,
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
private val timelineEventFilterFactory: TimelineEventFilterFactory,
private val featureFlagService: FeatureFlagService,
private val roomMembershipObserver: RoomMembershipObserver,
private val roomInfoMapper: RoomInfoMapper,
@ -70,7 +70,7 @@ class RustRoomFactory(
private val eventFilters = TimelineConfig.excludedEvents
.takeIf { it.isNotEmpty() }
?.let { listStateEventType ->
timelineEventTypeFilterFactory.create(listStateEventType)
timelineEventFilterFactory.create(listStateEventType)
}
suspend fun destroy() {
@ -133,7 +133,7 @@ class RustRoomFactory(
sdkRoom.timelineWithConfiguration(
TimelineConfiguration(
focus = TimelineFocus.Live(hideThreadedEvents = hideThreadedEvents),
filter = eventFilters?.let(TimelineFilter::EventTypeFilter) ?: TimelineFilter.All,
filter = eventFilters?.let(TimelineFilter::EventFilter) ?: TimelineFilter.All,
internalIdPrefix = "live",
dateDividerMode = DateDividerMode.DAILY,
trackReadReceipts = TimelineReadReceiptTracking.ALL_EVENTS,

View file

@ -12,16 +12,16 @@ import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.room.StateEventType
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
import org.matrix.rustcomponents.sdk.TimelineEventFilter
interface TimelineEventTypeFilterFactory {
fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter
interface TimelineEventFilterFactory {
fun create(listStateEventType: List<StateEventType>): TimelineEventFilter
}
@ContributesBinding(AppScope::class)
class RustTimelineEventTypeFilterFactory : TimelineEventTypeFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter {
return TimelineEventTypeFilter.exclude(
class RustTimelineEventFilterFactory : TimelineEventFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventFilter {
return TimelineEventFilter.excludeEventTypes(
listStateEventType.map { stateEventType ->
FilterTimelineEventType.State(stateEventType.map())
}

View file

@ -14,6 +14,7 @@ import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Any
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Category
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.DeduplicateVersions
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Favourite
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Identifiers
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.Invite
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonLeft
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind.NonSpace
@ -60,6 +61,7 @@ internal object RoomListFilterMapper {
return when (filter) {
is RoomListFilter.All -> All(filters = filter.filters.map { mapFilter(it) })
is RoomListFilter.Any -> Any(filters = filter.filters.map { mapFilter(it) })
is RoomListFilter.Identifiers -> Identifiers(identifiers = filter.values.map { it.value })
RoomListFilter.None -> None
RoomListFilter.Category.Group -> Category(RoomListFilterCategory.GROUP)
RoomListFilter.Category.People -> Category(RoomListFilterCategory.PEOPLE)

View file

@ -53,6 +53,11 @@ class RoomSummaryFactory(
senderProfile = event.profile.map(),
isOwn = event.isOwn,
)
is RustLatestEventValue.RemoteInvite -> LatestEventValue.RoomInvite(
timestamp = event.timestamp.toLong(),
inviterId = event.inviter?.let(::UserId),
invitedProfile = event.inviterProfile.map(),
)
}
}
return RoomSummary(

View file

@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
import io.element.android.libraries.matrix.api.spaces.SpaceService
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineDispatcher
@ -31,9 +32,11 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.SpaceFilterUpdate
import org.matrix.rustcomponents.sdk.SpaceListUpdate
import org.matrix.rustcomponents.sdk.SpaceServiceInterface
import org.matrix.rustcomponents.sdk.SpaceServiceJoinedSpacesListener
import org.matrix.rustcomponents.sdk.SpaceServiceSpaceFiltersListener
import timber.log.Timber
import org.matrix.rustcomponents.sdk.SpaceService as ClientSpaceService
@ -45,20 +48,20 @@ class RustSpaceService(
private val analyticsService: AnalyticsService,
) : SpaceService {
private val spaceRoomMapper = SpaceRoomMapper()
override val spaceRoomsFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
private val spaceFilterMapper = SpaceServiceFilterMapper(spaceRoomMapper)
override val topLevelSpacesFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
spaceRoomsFlow = spaceRoomsFlow,
spaceRoomsFlow = topLevelSpacesFlow,
mapper = spaceRoomMapper,
analyticsService = analyticsService,
)
override suspend fun joinedSpaces(): Result<List<SpaceRoom>> = withContext(sessionDispatcher) {
runCatchingExceptions {
innerSpaceService
.topLevelJoinedSpaces()
.map(spaceRoomMapper::map)
}
}
override val spaceFiltersFlow = MutableSharedFlow<List<SpaceServiceFilter>>(replay = 1, extraBufferCapacity = 1)
private val spaceFilterUpdateProcessor = SpaceServiceFilterUpdateProcessor(
spaceFiltersFlow = spaceFiltersFlow,
mapper = spaceFilterMapper,
)
override suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>> = withContext(sessionDispatcher) {
runCatchingExceptions {
@ -123,6 +126,13 @@ class RustSpaceService(
spaceListUpdateProcessor.postUpdates(updates)
}
.launchIn(sessionCoroutineScope)
innerSpaceService
.spaceFilterListUpdate()
.onEach { updates ->
spaceFilterUpdateProcessor.postUpdates(updates)
}
.launchIn(sessionCoroutineScope)
}
}
@ -142,3 +152,20 @@ internal fun SpaceServiceInterface.spaceListUpdate(): Flow<List<SpaceListUpdate>
}.catch {
Timber.d(it, "spaceDiffFlow() failed")
}.buffer(Channel.UNLIMITED)
internal fun SpaceServiceInterface.spaceFilterListUpdate(): Flow<List<SpaceFilterUpdate>> =
callbackFlow {
val listener = object : SpaceServiceSpaceFiltersListener {
override fun onUpdate(filterUpdates: List<SpaceFilterUpdate>) {
trySendBlocking(filterUpdates)
}
}
Timber.d("Open spaceFilterDiffFlow for SpaceServiceInterface ${this@spaceFilterListUpdate}")
val taskHandle = subscribeToSpaceFilters(listener)
awaitClose {
Timber.d("Close spaceFilterDiffFlow for SpaceServiceInterface ${this@spaceFilterListUpdate}")
taskHandle.cancelAndDestroy()
}
}.catch {
Timber.d(it, "spaceFilterListUpdate() failed")
}.buffer(Channel.UNLIMITED)

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.spaces
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
import org.matrix.rustcomponents.sdk.SpaceFilter as RustSpaceFilter
class SpaceServiceFilterMapper(
private val spaceRoomMapper: SpaceRoomMapper,
) {
fun map(spaceFilter: RustSpaceFilter): SpaceServiceFilter {
return SpaceServiceFilter(
spaceRoom = spaceRoomMapper.map(spaceFilter.spaceRoom),
level = spaceFilter.level.toInt(),
descendants = spaceFilter.descendants.map { RoomId(it) },
)
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* 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.spaces
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.matrix.rustcomponents.sdk.SpaceFilterUpdate
import timber.log.Timber
internal class SpaceServiceFilterUpdateProcessor(
private val spaceFiltersFlow: MutableSharedFlow<List<SpaceServiceFilter>>,
private val mapper: SpaceServiceFilterMapper,
) {
private val mutex = Mutex()
suspend fun postUpdates(updates: List<SpaceFilterUpdate>) {
Timber.v("Update space filters from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}")
updateSpaceFilters {
updates.forEach { update -> applyUpdate(update) }
}
}
private suspend fun updateSpaceFilters(block: MutableList<SpaceServiceFilter>.() -> Unit) =
mutex.withLock {
val spaceFilters = if (spaceFiltersFlow.replayCache.isNotEmpty()) {
spaceFiltersFlow.first().toMutableList()
} else {
mutableListOf()
}
block(spaceFilters)
spaceFiltersFlow.emit(spaceFilters)
}
private fun MutableList<SpaceServiceFilter>.applyUpdate(update: SpaceFilterUpdate) {
when (update) {
is SpaceFilterUpdate.Append -> {
val newFilters = update.values.map(mapper::map)
addAll(newFilters)
}
SpaceFilterUpdate.Clear -> clear()
is SpaceFilterUpdate.Insert -> {
val newFilter = mapper.map(update.value)
add(update.index.toInt(), newFilter)
}
SpaceFilterUpdate.PopBack -> {
removeAt(lastIndex)
}
SpaceFilterUpdate.PopFront -> {
removeAt(0)
}
is SpaceFilterUpdate.PushBack -> {
val newFilter = mapper.map(update.value)
add(newFilter)
}
is SpaceFilterUpdate.PushFront -> {
val newFilter = mapper.map(update.value)
add(0, newFilter)
}
is SpaceFilterUpdate.Remove -> {
removeAt(update.index.toInt())
}
is SpaceFilterUpdate.Reset -> {
clear()
val newFilters = update.values.map(mapper::map)
addAll(newFilters)
}
is SpaceFilterUpdate.Set -> {
val newFilter = mapper.map(update.value)
this[update.index.toInt()] = newFilter
}
is SpaceFilterUpdate.Truncate -> {
subList(update.length.toInt(), size).clear()
}
}
}
}

View file

@ -13,7 +13,7 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider
import io.element.android.libraries.matrix.impl.auth.FakeUserCertificatesProvider
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilderProvider
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionStore
@ -63,7 +63,7 @@ fun TestScope.createRustMatrixClientFactory(
clock = FakeSystemClock(),
analyticsService = FakeAnalyticsService(),
featureFlagService = FakeFeatureFlagService(),
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
timelineEventFilterFactory = FakeTimelineEventFilterFactory(),
clientBuilderProvider = clientBuilderProvider,
sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(),
workManagerScheduler = workManagerScheduler,

View file

@ -15,7 +15,7 @@ import io.element.android.libraries.core.data.bytes
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSyncService
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.A_DEVICE_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
@ -150,7 +150,7 @@ class RustMatrixClientTest {
dispatchers = testCoroutineDispatchers(),
baseCacheDirectory = File(""),
clock = FakeSystemClock(),
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
timelineEventFilterFactory = FakeTimelineEventFilterFactory(),
featureFlagService = FakeFeatureFlagService(),
analyticsService = FakeAnalyticsService(),
workManagerScheduler = FakeWorkManagerScheduler(submitLambda = {}),

View file

@ -30,6 +30,7 @@ internal fun aRustNotificationItem(
hasMention: Boolean? = false,
threadId: ThreadId? = null,
actions: List<Action>? = null,
rawEvent: String = "",
) = NotificationItem(
event = event,
senderInfo = senderInfo,
@ -38,6 +39,7 @@ internal fun aRustNotificationItem(
hasMention = hasMention,
threadId = threadId?.value,
actions = actions,
rawEvent = rawEvent,
)
internal fun aRustBatchNotificationResultOk(

View file

@ -9,6 +9,6 @@
package io.element.android.libraries.matrix.impl.fixtures.fakes
import org.matrix.rustcomponents.sdk.NoHandle
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
import org.matrix.rustcomponents.sdk.TimelineEventFilter
class FakeFfiTimelineEventTypeFilter : TimelineEventTypeFilter(NoHandle)
class FakeFfiTimelineEventFilter : TimelineEventFilter(NoHandle)

View file

@ -9,11 +9,11 @@
package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineEventTypeFilter
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineEventFilter
import org.matrix.rustcomponents.sdk.TimelineEventFilter
class FakeTimelineEventTypeFilterFactory : TimelineEventTypeFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter {
return FakeFfiTimelineEventTypeFilter()
class FakeTimelineEventFilterFactory : TimelineEventFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventFilter {
return FakeFfiTimelineEventFilter()
}
}

View file

@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
import io.element.android.libraries.matrix.api.spaces.SpaceService
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.flow.MutableSharedFlow
@ -20,7 +21,6 @@ import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
class FakeSpaceService(
private val joinedSpacesResult: () -> Result<List<SpaceRoom>> = { lambdaError() },
private val spaceRoomListResult: (RoomId) -> SpaceRoomList = { lambdaError() },
private val leaveSpaceHandleResult: (RoomId) -> LeaveSpaceHandle = { lambdaError() },
private val removeChildFromSpaceResult: (RoomId, RoomId) -> Result<Unit> = { _, _ -> lambdaError() },
@ -29,16 +29,20 @@ class FakeSpaceService(
private val editableSpacesResult: () -> Result<List<SpaceRoom>> = { lambdaError() },
private val addChildToSpaceResult: (RoomId, RoomId) -> Result<Unit> = { _, _ -> lambdaError() },
) : SpaceService {
private val _spaceRoomsFlow = MutableSharedFlow<List<SpaceRoom>>()
override val spaceRoomsFlow: SharedFlow<List<SpaceRoom>>
get() = _spaceRoomsFlow.asSharedFlow()
private val _topLevelSpacesFlow = MutableSharedFlow<List<SpaceRoom>>()
override val topLevelSpacesFlow: SharedFlow<List<SpaceRoom>>
get() = _topLevelSpacesFlow.asSharedFlow()
suspend fun emitSpaceRoomList(value: List<SpaceRoom>) {
_spaceRoomsFlow.emit(value)
suspend fun emitTopLevelSpaces(value: List<SpaceRoom>) {
_topLevelSpacesFlow.emit(value)
}
override suspend fun joinedSpaces(): Result<List<SpaceRoom>> = simulateLongTask {
return joinedSpacesResult()
private val _spaceFiltersFlow = MutableSharedFlow<List<SpaceServiceFilter>>()
override val spaceFiltersFlow: SharedFlow<List<SpaceServiceFilter>>
get() = _spaceFiltersFlow.asSharedFlow()
suspend fun emitSpaceFilters(value: List<SpaceServiceFilter>) {
_spaceFiltersFlow.emit(value)
}
override suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>> {