Merge branch 'develop' into feature/fga/mark_room_as_favorite

This commit is contained in:
ganfra 2024-02-12 17:08:36 +01:00
commit a8bc0cb4ca
538 changed files with 4465 additions and 1639 deletions

View file

@ -55,10 +55,12 @@ 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.roomlist.RoomListFactory
import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
import io.element.android.libraries.matrix.impl.util.SessionDirectoryNameProvider
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
import io.element.android.libraries.sessionstorage.api.SessionStore
@ -76,12 +78,12 @@ import kotlinx.coroutines.withTimeout
import org.matrix.rustcomponents.sdk.BackupState
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientDelegate
import org.matrix.rustcomponents.sdk.FilterStateEventType
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
import org.matrix.rustcomponents.sdk.NotificationProcessSetup
import org.matrix.rustcomponents.sdk.PowerLevels
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.StateEventType
import org.matrix.rustcomponents.sdk.TaskHandle
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
import org.matrix.rustcomponents.sdk.use
@ -133,6 +135,7 @@ class RustMatrixClient(
sessionCoroutineScope = sessionCoroutineScope,
dispatchers = dispatchers,
).apply { start() }
private val sessionDirectoryNameProvider = SessionDirectoryNameProvider()
private val isLoggingOut = AtomicBoolean(false)
@ -203,20 +206,20 @@ class RustMatrixClient(
private val eventFilters = TimelineEventTypeFilter.exclude(
listOf(
FilterStateEventType.ROOM_ALIASES,
FilterStateEventType.ROOM_CANONICAL_ALIAS,
FilterStateEventType.ROOM_GUEST_ACCESS,
FilterStateEventType.ROOM_HISTORY_VISIBILITY,
FilterStateEventType.ROOM_JOIN_RULES,
FilterStateEventType.ROOM_PINNED_EVENTS,
FilterStateEventType.ROOM_POWER_LEVELS,
FilterStateEventType.ROOM_SERVER_ACL,
FilterStateEventType.ROOM_TOMBSTONE,
FilterStateEventType.SPACE_CHILD,
FilterStateEventType.SPACE_PARENT,
FilterStateEventType.POLICY_RULE_ROOM,
FilterStateEventType.POLICY_RULE_SERVER,
FilterStateEventType.POLICY_RULE_USER,
StateEventType.ROOM_ALIASES,
StateEventType.ROOM_CANONICAL_ALIAS,
StateEventType.ROOM_GUEST_ACCESS,
StateEventType.ROOM_HISTORY_VISIBILITY,
StateEventType.ROOM_JOIN_RULES,
StateEventType.ROOM_PINNED_EVENTS,
StateEventType.ROOM_POWER_LEVELS,
StateEventType.ROOM_SERVER_ACL,
StateEventType.ROOM_TOMBSTONE,
StateEventType.SPACE_CHILD,
StateEventType.SPACE_PARENT,
StateEventType.POLICY_RULE_ROOM,
StateEventType.POLICY_RULE_SERVER,
StateEventType.POLICY_RULE_USER,
).map(FilterTimelineEventType::State)
)
@ -270,12 +273,7 @@ class RustMatrixClient(
private suspend fun pairOfRoom(roomId: RoomId): Pair<RoomListItem, Room>? {
val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value)
val fullRoom = cachedRoomListItem?.let { roomListItem ->
if (!roomListItem.isTimelineInitialized()) {
roomListItem.initTimeline(eventFilters)
}
roomListItem.fullRoom()
}
val fullRoom = cachedRoomListItem?.fullRoomWithTimeline(filter = eventFilters)
return if (cachedRoomListItem == null || fullRoom == null) {
Timber.d("No room cached for $roomId")
null
@ -401,13 +399,12 @@ class RustMatrixClient(
}
override suspend fun getCacheSize(): Long {
// Do not use client.userId since it can throw if client has been closed (during clear cache)
return baseDirectory.getCacheSize(userID = sessionId.value)
return baseDirectory.getCacheSize()
}
override suspend fun clearCache() {
close()
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = false)
baseDirectory.deleteSessionDirectory(deleteCryptoDb = false)
}
override suspend fun logout(ignoreSdkError: Boolean): String? = doLogout(
@ -436,7 +433,7 @@ class RustMatrixClient(
}
}
close()
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = true)
baseDirectory.deleteSessionDirectory(deleteCryptoDb = true)
if (removeSession) {
sessionStore.removeSession(sessionId.value)
}
@ -482,12 +479,10 @@ class RustMatrixClient(
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
private suspend fun File.getCacheSize(
userID: String,
includeCryptoDb: Boolean = false,
): Long = withContext(sessionDispatcher) {
// Rust sanitises the user ID replacing invalid characters with an _
val sanitisedUserID = userID.replace(":", "_")
val sessionDirectory = File(this@getCacheSize, sanitisedUserID)
val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId)
val sessionDirectory = File(this@getCacheSize, sessionDirectoryName)
if (includeCryptoDb) {
sessionDirectory.getSizeOfFiles()
} else {
@ -504,12 +499,10 @@ class RustMatrixClient(
}
private suspend fun File.deleteSessionDirectory(
userID: String,
deleteCryptoDb: Boolean = false,
): Boolean = withContext(sessionDispatcher) {
// Rust sanitises the user ID replacing invalid characters with an _
val sanitisedUserID = userID.replace(":", "_")
val sessionDirectory = File(this@deleteSessionDirectory, sanitisedUserID)
val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId)
val sessionDirectory = File(this@deleteSessionDirectory, sessionDirectoryName)
if (deleteCryptoDb) {
// Delete the folder and all its content
sessionDirectory.deleteRecursively()

View file

@ -31,11 +31,17 @@ fun MessageEventType.map(): MessageLikeEventType = when (this) {
MessageEventType.KEY_VERIFICATION_KEY -> MessageLikeEventType.KEY_VERIFICATION_KEY
MessageEventType.KEY_VERIFICATION_MAC -> MessageLikeEventType.KEY_VERIFICATION_MAC
MessageEventType.KEY_VERIFICATION_DONE -> MessageLikeEventType.KEY_VERIFICATION_DONE
MessageEventType.REACTION_SENT -> MessageLikeEventType.REACTION_SENT
MessageEventType.REACTION -> MessageLikeEventType.REACTION
MessageEventType.ROOM_ENCRYPTED -> MessageLikeEventType.ROOM_ENCRYPTED
MessageEventType.ROOM_MESSAGE -> MessageLikeEventType.ROOM_MESSAGE
MessageEventType.ROOM_REDACTION -> MessageLikeEventType.ROOM_REDACTION
MessageEventType.STICKER -> MessageLikeEventType.STICKER
MessageEventType.POLL_END -> MessageLikeEventType.POLL_END
MessageEventType.POLL_RESPONSE -> MessageLikeEventType.POLL_RESPONSE
MessageEventType.POLL_START -> MessageLikeEventType.POLL_START
MessageEventType.UNSTABLE_POLL_END -> MessageLikeEventType.UNSTABLE_POLL_END
MessageEventType.UNSTABLE_POLL_RESPONSE -> MessageLikeEventType.UNSTABLE_POLL_RESPONSE
MessageEventType.UNSTABLE_POLL_START -> MessageLikeEventType.UNSTABLE_POLL_START
}
fun MessageLikeEventType.map(): MessageEventType = when (this) {
@ -50,9 +56,15 @@ fun MessageLikeEventType.map(): MessageEventType = when (this) {
MessageLikeEventType.KEY_VERIFICATION_KEY -> MessageEventType.KEY_VERIFICATION_KEY
MessageLikeEventType.KEY_VERIFICATION_MAC -> MessageEventType.KEY_VERIFICATION_MAC
MessageLikeEventType.KEY_VERIFICATION_DONE -> MessageEventType.KEY_VERIFICATION_DONE
MessageLikeEventType.REACTION_SENT -> MessageEventType.REACTION_SENT
MessageLikeEventType.REACTION -> MessageEventType.REACTION
MessageLikeEventType.ROOM_ENCRYPTED -> MessageEventType.ROOM_ENCRYPTED
MessageLikeEventType.ROOM_MESSAGE -> MessageEventType.ROOM_MESSAGE
MessageLikeEventType.ROOM_REDACTION -> MessageEventType.ROOM_REDACTION
MessageLikeEventType.STICKER -> MessageEventType.STICKER
MessageLikeEventType.POLL_END -> MessageEventType.POLL_END
MessageLikeEventType.POLL_RESPONSE -> MessageEventType.POLL_RESPONSE
MessageLikeEventType.POLL_START -> MessageEventType.POLL_START
MessageLikeEventType.UNSTABLE_POLL_END -> MessageEventType.UNSTABLE_POLL_END
MessageLikeEventType.UNSTABLE_POLL_RESPONSE -> MessageEventType.UNSTABLE_POLL_RESPONSE
MessageLikeEventType.UNSTABLE_POLL_START -> MessageEventType.UNSTABLE_POLL_START
}

View file

@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.parallelMap
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.ForwardEventException
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered
import kotlinx.coroutines.CancellationException
@ -50,7 +51,9 @@ class RoomContentForwarder(
) {
val content = fromTimeline.getTimelineEventContentByEventId(eventId.value)
val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) }
val targetRooms = targetSlidingSyncRooms.mapNotNull { slidingSyncRoom -> slidingSyncRoom.use { it.fullRoom() } }
val targetRooms = targetSlidingSyncRooms.map { slidingSyncRoom ->
slidingSyncRoom.use { it.fullRoomWithTimeline(null) }
}
val failedForwardingTo = mutableSetOf<RoomId>()
targetRooms.parallelMap { room ->
room.use { targetRoom ->

View file

@ -41,6 +41,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
@ -53,6 +54,7 @@ import io.element.android.libraries.matrix.impl.room.location.toInner
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
import io.element.android.libraries.matrix.impl.room.tags.map
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl
@ -74,6 +76,7 @@ import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
import org.matrix.rustcomponents.sdk.RoomNotableTagsListener
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
import org.matrix.rustcomponents.sdk.TypingNotificationsListener
import org.matrix.rustcomponents.sdk.WidgetCapabilities
import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
@ -115,6 +118,7 @@ class RustMatrixRoom(
})
}
override val notableTagsFlow: Flow<RoomNotableTags> = mxCallbackFlow {
innerRoom.subscribeToNotableTags(object : RoomNotableTagsListener {
override fun call(notableTags: RustRoomNotableTags) {
@ -123,6 +127,22 @@ class RustMatrixRoom(
})
}
override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
launch {
val initial = emptyList<UserId>()
channel.trySend(initial)
}
innerRoom.subscribeToTypingNotifications(object : TypingNotificationsListener {
override fun call(typingUserIds: List<String>) {
channel.trySend(
typingUserIds
.filter { it != sessionData.userId }
.map(::UserId)
)
}
})
}
// Create a dispatcher for all room methods...
private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32)
@ -441,6 +461,22 @@ class RustMatrixRoom(
}
}
override suspend fun markAsRead(receiptType: ReceiptType?): Result<Unit> = withContext(roomDispatcher) {
runCatching {
if (receiptType != null) {
innerRoom.markAsReadAndSendReadReceipt(receiptType.toRustReceiptType())
} else {
innerRoom.markAsRead()
}
}
}
override suspend fun markAsUnread(): Result<Unit> = withContext(roomDispatcher) {
runCatching {
innerRoom.markAsUnread()
}
}
override suspend fun sendLocation(
body: String,
geoUri: String,

View file

@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
import io.element.android.libraries.matrix.api.roomlist.RoomList
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@ -28,7 +29,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
import org.matrix.rustcomponents.sdk.RoomListLoadingState
import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
@ -44,7 +44,7 @@ internal class RoomListFactory(
*/
fun createRoomList(
pageSize: Int,
initialFilter: DynamicRoomList.Filter = DynamicRoomList.Filter.All,
initialFilter: RoomListFilter = RoomListFilter.all(),
innerProvider: suspend () -> InnerRoomList
): DynamicRoomList {
val loadingStateFlow: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
@ -91,7 +91,7 @@ internal class RoomListFactory(
private class RustDynamicRoomList(
override val summaries: MutableStateFlow<List<RoomSummary>>,
override val loadingState: MutableStateFlow<RoomList.LoadingState>,
override val currentFilter: MutableStateFlow<DynamicRoomList.Filter>,
override val currentFilter: MutableStateFlow<RoomListFilter>,
override val loadedPages: MutableStateFlow<Int>,
private val dynamicEvents: MutableSharedFlow<RoomListDynamicEvents>,
private val processor: RoomSummaryListProcessor,
@ -101,7 +101,7 @@ private class RustDynamicRoomList(
processor.rebuildRoomSummaries()
}
override suspend fun updateFilter(filter: DynamicRoomList.Filter) {
override suspend fun updateFilter(filter: RoomListFilter) {
currentFilter.emit(filter)
val filterEvent = RoomListDynamicEvents.SetFilter(filter.toRustFilter())
dynamicEvents.emit(filterEvent)
@ -124,12 +124,3 @@ private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState {
RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded
}
}
private fun DynamicRoomList.Filter.toRustFilter(): RoomListEntriesDynamicFilterKind {
return when (this) {
DynamicRoomList.Filter.All -> RoomListEntriesDynamicFilterKind.All
is DynamicRoomList.Filter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(this.pattern)
DynamicRoomList.Filter.None -> RoomListEntriesDynamicFilterKind.None
DynamicRoomList.Filter.AllNonLeft -> RoomListEntriesDynamicFilterKind.AllNonLeft
}
}

View file

@ -0,0 +1,35 @@
/*
* 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.roomlist
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
import org.matrix.rustcomponents.sdk.RoomListFilterCategory
fun RoomListFilter.toRustFilter(): RoomListEntriesDynamicFilterKind {
return when (this) {
is RoomListFilter.All -> RoomListEntriesDynamicFilterKind.All(filters.map { it.toRustFilter() })
is RoomListFilter.Any -> RoomListEntriesDynamicFilterKind.Any(filters.map { it.toRustFilter() })
RoomListFilter.Category.Group -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.GROUP)
RoomListFilter.Category.People -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.PEOPLE)
is RoomListFilter.FuzzyMatchRoomName -> RoomListEntriesDynamicFilterKind.FuzzyMatchRoomName(pattern)
RoomListFilter.NonLeft -> RoomListEntriesDynamicFilterKind.NonLeft
RoomListFilter.None -> RoomListEntriesDynamicFilterKind.None
is RoomListFilter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(pattern)
RoomListFilter.Unread -> RoomListEntriesDynamicFilterKind.Unread
}
}

View file

@ -0,0 +1,29 @@
/*
* 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.roomlist
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
/** Returns a `Room` with an initialized timeline using the given [filter]. */
suspend fun RoomListItem.fullRoomWithTimeline(filter: TimelineEventTypeFilter? = null): Room {
if (!isTimelineInitialized()) {
initTimeline(filter)
}
return fullRoom()
}

View file

@ -38,6 +38,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto
numUnreadMentions = roomInfo.numUnreadMentions.toInt(),
numUnreadMessages = roomInfo.numUnreadMessages.toInt(),
numUnreadNotifications = roomInfo.numUnreadNotifications.toInt(),
isMarkedUnread = roomInfo.isMarkedUnread,
lastMessage = latestRoomMessage,
inviter = roomInfo.inviter?.let(RoomMemberMapper::map),
userDefinedNotificationMode = roomInfo.userDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode),

View file

@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
import io.element.android.libraries.matrix.api.roomlist.RoomList
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally
import kotlinx.coroutines.CoroutineScope
@ -45,7 +46,7 @@ internal class RustRoomListService(
) : RoomListService {
override val allRooms: DynamicRoomList = roomListFactory.createRoomList(
pageSize = DEFAULT_PAGE_SIZE,
initialFilter = DynamicRoomList.Filter.AllNonLeft,
initialFilter = RoomListFilter.all(RoomListFilter.NonLeft),
) {
innerRoomListService.allRooms()
}

View file

@ -28,7 +28,6 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelin
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
import io.element.android.libraries.matrix.impl.timeline.postprocessor.DmBeginningTimelineProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterHiddenStateEventsProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
@ -84,8 +83,6 @@ class RustMatrixTimeline(
dispatcher = dispatcher,
)
private val filterHiddenStateEventsProcessor = FilterHiddenStateEventsProcessor()
private val dmBeginningTimelineProcessor = DmBeginningTimelineProcessor()
private val timelineItemFactory = MatrixTimelineItemMapper(
@ -109,7 +106,6 @@ class RustMatrixTimeline(
@OptIn(ExperimentalCoroutinesApi::class)
override val timelineItems: Flow<List<MatrixTimelineItem>> = _timelineItems
.mapLatest { items -> encryptedHistoryPostProcessor.process(items) }
.mapLatest { items -> filterHiddenStateEventsProcessor.process(items) }
.mapLatest { items ->
dmBeginningTimelineProcessor.process(
items = items,

View file

@ -1,42 +0,0 @@
/*
* 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.timeline.postprocessor
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
/**
* This class is used to filter out 'hidden' state events from the timeline.
*/
class FilterHiddenStateEventsProcessor {
fun process(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> {
return items.filter { item ->
when (item) {
is MatrixTimelineItem.Event -> {
when (val content = item.event.content) {
// If it's a state event, make sure it's visible
is StateContent -> content.isVisibleInTimeline()
// We can display any other event
else -> true
}
}
is MatrixTimelineItem.Virtual -> true
is MatrixTimelineItem.Other -> true
}
}
}
}

View file

@ -0,0 +1,26 @@
/*
* 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.util
import io.element.android.libraries.matrix.api.core.SessionId
class SessionDirectoryNameProvider {
// Rust sanitises the user ID replacing invalid characters with an _
fun provides(sessionId: SessionId): String {
return sessionId.value.replace(":", "_")
}
}

View file

@ -27,7 +27,7 @@ import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultCallWidgetSettingsProvider @Inject constructor() : CallWidgetSettingsProvider {
override fun provide(baseUrl: String, widgetId: String): MatrixWidgetSettings {
override fun provide(baseUrl: String, widgetId: String, encrypted: Boolean): MatrixWidgetSettings {
val options = VirtualElementCallWidgetOptions(
elementCallUrl = baseUrl,
widgetId = widgetId,
@ -40,7 +40,7 @@ class DefaultCallWidgetSettingsProvider @Inject constructor() : CallWidgetSettin
confineToRoom = true,
font = null,
analyticsId = null,
encryption = EncryptionSystem.PerParticipantKeys,
encryption = if (encrypted) EncryptionSystem.PerParticipantKeys else EncryptionSystem.Unencrypted,
)
val rustWidgetSettings = newVirtualElementCallWidget(options)
return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings)

View file

@ -1,77 +0,0 @@
/*
* 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.timeline.postprocessor
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import org.junit.Test
class FilterHiddenStateEventsProcessorTest {
@Test
fun test() {
val items = listOf(
// These are visible because they're not state events
MatrixTimelineItem.Other,
MatrixTimelineItem.Virtual("virtual", VirtualTimelineItem.ReadMarker),
MatrixTimelineItem.Event("event", anEventTimelineItem()),
// These are visible state events
MatrixTimelineItem.Event("m.room.avatar", anEventTimelineItem(content = StateContent("", OtherState.RoomAvatar("")))),
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(content = StateContent("", OtherState.RoomCreate))),
MatrixTimelineItem.Event("m.room.encrypted", anEventTimelineItem(content = StateContent("", OtherState.RoomEncryption))),
MatrixTimelineItem.Event("m.room.name", anEventTimelineItem(content = StateContent("", OtherState.RoomName("")))),
MatrixTimelineItem.Event("m.room.third_party_invite", anEventTimelineItem(content = StateContent("", OtherState.RoomThirdPartyInvite("")))),
MatrixTimelineItem.Event("m.room.topic", anEventTimelineItem(content = StateContent("", OtherState.RoomTopic("")))),
MatrixTimelineItem.Event("m.room.custom", anEventTimelineItem(content = StateContent("", OtherState.Custom("")))),
// These ones are hidden
MatrixTimelineItem.Event("m.room.aliases", anEventTimelineItem(content = StateContent("", OtherState.RoomAliases))),
MatrixTimelineItem.Event("m.room.canonical_alias", anEventTimelineItem(content = StateContent("", OtherState.RoomCanonicalAlias))),
MatrixTimelineItem.Event("m.room.guest_access", anEventTimelineItem(content = StateContent("", OtherState.RoomGuestAccess))),
MatrixTimelineItem.Event("m.room.history_visibility", anEventTimelineItem(content = StateContent("", OtherState.RoomHistoryVisibility))),
MatrixTimelineItem.Event("m.room.join_rules", anEventTimelineItem(content = StateContent("", OtherState.RoomJoinRules))),
MatrixTimelineItem.Event("m.room.pinned_events", anEventTimelineItem(content = StateContent("", OtherState.RoomPinnedEvents))),
MatrixTimelineItem.Event("m.room.power_levels", anEventTimelineItem(content = StateContent("", OtherState.RoomPowerLevels))),
MatrixTimelineItem.Event("m.room.server_acl", anEventTimelineItem(content = StateContent("", OtherState.RoomServerAcl))),
MatrixTimelineItem.Event("m.room.tombstone", anEventTimelineItem(content = StateContent("", OtherState.RoomTombstone))),
MatrixTimelineItem.Event("m.space.child", anEventTimelineItem(content = StateContent("", OtherState.SpaceChild))),
MatrixTimelineItem.Event("m.space.parent", anEventTimelineItem(content = StateContent("", OtherState.SpaceParent))),
MatrixTimelineItem.Event("m.room.policy.rule.room", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleRoom))),
MatrixTimelineItem.Event("m.room.policy.rule.server", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleServer))),
MatrixTimelineItem.Event("m.room.policy.rule.user", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleUser))),
)
val expected = listOf(
MatrixTimelineItem.Other,
MatrixTimelineItem.Virtual("virtual", VirtualTimelineItem.ReadMarker),
MatrixTimelineItem.Event("event", anEventTimelineItem()),
MatrixTimelineItem.Event("m.room.avatar", anEventTimelineItem(content = StateContent("", OtherState.RoomAvatar("")))),
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(content = StateContent("", OtherState.RoomCreate))),
MatrixTimelineItem.Event("m.room.encrypted", anEventTimelineItem(content = StateContent("", OtherState.RoomEncryption))),
MatrixTimelineItem.Event("m.room.name", anEventTimelineItem(content = StateContent("", OtherState.RoomName("")))),
MatrixTimelineItem.Event("m.room.third_party_invite", anEventTimelineItem(content = StateContent("", OtherState.RoomThirdPartyInvite("")))),
MatrixTimelineItem.Event("m.room.topic", anEventTimelineItem(content = StateContent("", OtherState.RoomTopic("")))),
MatrixTimelineItem.Event("m.room.custom", anEventTimelineItem(content = StateContent("", OtherState.Custom("")))),
)
val processor = FilterHiddenStateEventsProcessor()
assertThat(processor.process(items)).isEqualTo(expected)
}
}