Merge pull request #5449 from element-hq/feature/fga/room_list_space_invite

Room list space invite
This commit is contained in:
ganfra 2025-10-03 13:17:28 +02:00 committed by GitHub
commit d577f02126
17 changed files with 92 additions and 41 deletions

View file

@ -189,10 +189,14 @@ private fun RoomSummaryScaffoldRow(
) { ) {
Avatar( Avatar(
avatarData = room.avatarData, avatarData = room.avatarData,
avatarType = AvatarType.Room( avatarType = if (room.isSpace) {
heroes = room.heroes, AvatarType.Space(isTombstoned = room.isTombstoned)
isTombstoned = room.isTombstoned, } else {
), AvatarType.Room(
heroes = room.heroes,
isTombstoned = room.isTombstoned,
)
},
hideImage = hideAvatarImage, hideImage = hideAvatarImage,
) )
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))

View file

@ -69,6 +69,7 @@ class RoomListRoomSummaryFactory(
user.getAvatarData(size = AvatarSize.RoomListItem) user.getAvatarData(size = AvatarSize.RoomListItem)
}.toImmutableList(), }.toImmutableList(),
isTombstoned = roomInfo.successorRoom != null, isTombstoned = roomInfo.successorRoom != null,
isSpace = roomInfo.isSpace,
) )
} }
} }

View file

@ -38,6 +38,7 @@ data class RoomListRoomSummary(
val inviteSender: InviteSender?, val inviteSender: InviteSender?,
val isTombstoned: Boolean, val isTombstoned: Boolean,
val heroes: ImmutableList<AvatarData>, val heroes: ImmutableList<AvatarData>,
val isSpace: Boolean,
) { ) {
val isHighlighted = userDefinedNotificationMode != RoomNotificationMode.MUTE && val isHighlighted = userDefinedNotificationMode != RoomNotificationMode.MUTE &&
(numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0) || (numberOfUnreadNotifications > 0 || numberOfUnreadMentions > 0) ||

View file

@ -102,6 +102,15 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSu
displayName = "Bob", displayName = "Bob",
), ),
), ),
aRoomListRoomSummary(
name = "A space invite",
displayType = RoomSummaryDisplayType.INVITE,
inviteSender = anInviteSender(
userId = UserId("@bob:matrix.org"),
displayName = "Bob",
),
isSpace = true
),
aRoomListRoomSummary( aRoomListRoomSummary(
name = "A knocked room", name = "A knocked room",
displayType = RoomSummaryDisplayType.KNOCKED, displayType = RoomSummaryDisplayType.KNOCKED,
@ -151,6 +160,7 @@ internal fun aRoomListRoomSummary(
canonicalAlias: RoomAlias? = null, canonicalAlias: RoomAlias? = null,
heroes: List<AvatarData> = emptyList(), heroes: List<AvatarData> = emptyList(),
isTombstoned: Boolean = false, isTombstoned: Boolean = false,
isSpace: Boolean = false,
) = RoomListRoomSummary( ) = RoomListRoomSummary(
id = id, id = id,
roomId = RoomId(id), roomId = RoomId(id),
@ -172,4 +182,5 @@ internal fun aRoomListRoomSummary(
canonicalAlias = canonicalAlias, canonicalAlias = canonicalAlias,
heroes = heroes.toImmutableList(), heroes = heroes.toImmutableList(),
isTombstoned = isTombstoned, isTombstoned = isTombstoned,
isSpace = isSpace
) )

View file

@ -85,6 +85,7 @@ internal fun createRoomListRoomSummary(
heroes: List<AvatarData> = emptyList(), heroes: List<AvatarData> = emptyList(),
timestamp: String? = null, timestamp: String? = null,
isTombstoned: Boolean = false, isTombstoned: Boolean = false,
isSpace: Boolean = false,
) = RoomListRoomSummary( ) = RoomListRoomSummary(
id = A_ROOM_ID.value, id = A_ROOM_ID.value,
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
@ -106,4 +107,5 @@ internal fun createRoomListRoomSummary(
isDm = false, isDm = false,
heroes = heroes.toPersistentList(), heroes = heroes.toPersistentList(),
isTombstoned = isTombstoned, isTombstoned = isTombstoned,
isSpace = isSpace
) )

View file

@ -58,11 +58,12 @@ sealed interface RoomListFilter {
data object Invite : RoomListFilter data object Invite : RoomListFilter
/** /**
* A filter that matches either Group or People rooms. * A filter that matches either Group,People rooms or Space.
*/ */
sealed interface Category : RoomListFilter { sealed interface Category : RoomListFilter {
data object Group : Category data object Group : Category
data object People : Category data object People : Category
data object Space : Category
} }
/** /**

View file

@ -29,7 +29,6 @@ import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
private val ROOM_LIST_RUST_FILTERS = listOf( private val ROOM_LIST_RUST_FILTERS = listOf(
RoomListEntriesDynamicFilterKind.NonLeft, RoomListEntriesDynamicFilterKind.NonLeft,
RoomListEntriesDynamicFilterKind.NonSpace,
RoomListEntriesDynamicFilterKind.DeduplicateVersions RoomListEntriesDynamicFilterKind.DeduplicateVersions
) )

View file

@ -15,41 +15,57 @@ import io.element.android.libraries.matrix.api.roomlist.RoomSummary
val RoomListFilter.predicate val RoomListFilter.predicate
get() = when (this) { get() = when (this) {
is RoomListFilter.All -> { _: RoomSummary -> true } is RoomListFilter.All -> { roomSummary -> NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary) }
is RoomListFilter.Any -> { _: RoomSummary -> true } is RoomListFilter.Any -> { roomSummary -> NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary) }
RoomListFilter.None -> { _: RoomSummary -> false } RoomListFilter.None -> { _ -> false }
RoomListFilter.Category.Group -> { roomSummary: RoomSummary -> RoomListFilter.Category.Group -> { roomSummary: RoomSummary ->
!roomSummary.info.isDm && !roomSummary.isInvited() !roomSummary.info.isDm && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary)
} }
RoomListFilter.Category.People -> { roomSummary: RoomSummary -> RoomListFilter.Category.People -> { roomSummary: RoomSummary ->
roomSummary.info.isDm && !roomSummary.isInvited() roomSummary.info.isDm && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary)
} }
RoomListFilter.Category.Space -> IsSpacePredicate
RoomListFilter.Favorite -> { roomSummary: RoomSummary -> RoomListFilter.Favorite -> { roomSummary: RoomSummary ->
roomSummary.info.isFavorite && !roomSummary.isInvited() roomSummary.info.isFavorite && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary)
} }
RoomListFilter.Unread -> { roomSummary: RoomSummary -> RoomListFilter.Unread -> { roomSummary: RoomSummary ->
!roomSummary.isInvited() && (roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread) NonInvitedPredicate(roomSummary) &&
NonSpacePredicate(roomSummary) &&
(roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread)
} }
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary -> is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
roomSummary.info.name?.withoutAccents().orEmpty().contains(normalizedPattern, ignoreCase = true) roomSummary.info.name?.withoutAccents().orEmpty().contains(normalizedPattern, ignoreCase = true) &&
} (NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary))
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
roomSummary.isInvited()
} }
RoomListFilter.Invite -> IsInvitedPredicate
} }
fun List<RoomSummary>.filter(filter: RoomListFilter): List<RoomSummary> { fun List<RoomSummary>.filter(filter: RoomListFilter): List<RoomSummary> {
return when (filter) { return when (filter) {
is RoomListFilter.All -> { is RoomListFilter.All -> {
val predicates = filter.filters.map { it.predicate } val predicates = if (filter.filters.isNotEmpty()) {
filter.filters.map { it.predicate }
} else {
listOf(filter.predicate)
}
filter { roomSummary -> predicates.all { it(roomSummary) } } filter { roomSummary -> predicates.all { it(roomSummary) } }
} }
is RoomListFilter.Any -> { is RoomListFilter.Any -> {
val predicates = filter.filters.map { it.predicate } val predicates = if (filter.filters.isNotEmpty()) {
filter.filters.map { it.predicate }
} else {
listOf(filter.predicate)
}
filter { roomSummary -> predicates.any { it(roomSummary) } } filter { roomSummary -> predicates.any { it(roomSummary) } }
} }
else -> filter(filter.predicate) else -> filter(filter.predicate)
} }
} }
private fun RoomSummary.isInvited() = info.currentUserMembership == CurrentUserMembership.INVITED private val IsSpacePredicate = { roomSummary: RoomSummary -> roomSummary.info.isSpace }
private val NonSpacePredicate = { roomSummary: RoomSummary -> !IsSpacePredicate(roomSummary) }
private val IsInvitedPredicate = { roomSummary: RoomSummary -> roomSummary.info.currentUserMembership == CurrentUserMembership.INVITED }
private val NonInvitedPredicate = { roomSummary: RoomSummary -> !IsInvitedPredicate(roomSummary) }

View file

@ -41,6 +41,14 @@ class RoomListFilterTest {
currentUserMembership = CurrentUserMembership.INVITED currentUserMembership = CurrentUserMembership.INVITED
) )
private val space = aRoomSummary(
isSpace = true
)
private val invitedSpace = aRoomSummary(
isSpace = true,
currentUserMembership = CurrentUserMembership.INVITED
)
private val roomSummaries = listOf( private val roomSummaries = listOf(
regularRoom, regularRoom,
dmRoom, dmRoom,
@ -49,13 +57,15 @@ class RoomListFilterTest {
unreadNotificationRoom, unreadNotificationRoom,
roomToSearch, roomToSearch,
roomWithAccent, roomWithAccent,
invitedRoom invitedRoom,
space,
invitedSpace,
) )
@Test @Test
fun `Room list filter all empty`() = runTest { fun `Room list filter all empty`() = runTest {
val filter = RoomListFilter.all() val filter = RoomListFilter.all()
assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries) assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries - space)
} }
@Test @Test
@ -83,6 +93,12 @@ class RoomListFilterTest {
) )
} }
@Test
fun `Room list filter space`() = runTest {
val filter = RoomListFilter.Category.Space
assertThat(roomSummaries.filter(filter)).containsExactly(space, invitedSpace)
}
@Test @Test
fun `Room list filter favorite`() = runTest { fun `Room list filter favorite`() = runTest {
val filter = RoomListFilter.Favorite val filter = RoomListFilter.Favorite
@ -98,7 +114,7 @@ class RoomListFilterTest {
@Test @Test
fun `Room list filter invites`() = runTest { fun `Room list filter invites`() = runTest {
val filter = RoomListFilter.Invite val filter = RoomListFilter.Invite
assertThat(roomSummaries.filter(filter)).containsExactly(invitedRoom) assertThat(roomSummaries.filter(filter)).containsExactly(invitedRoom, invitedSpace)
} }
@Test @Test
@ -136,10 +152,4 @@ class RoomListFilterTest {
) )
assertThat(roomSummaries.filter(filter)).isEmpty() assertThat(roomSummaries.filter(filter)).isEmpty()
} }
@Test
fun `Room list filter all with empty list`() = runTest {
val filter = RoomListFilter.all()
assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries)
}
} }

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1ad2d62e2ce270b8548ddf611c3b192e756be0896627ccad28fff84c4ce5e2a1 oid sha256:98d4e9152dee861cc7afdb3efe57b086c3d9d58d38eeb1345749d31debbf6017
size 11938 size 21266

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:541b4210ff0acaf6451a0ce9c6cdc4f120c460086e036fc27e319daa23c7082b oid sha256:1ad2d62e2ce270b8548ddf611c3b192e756be0896627ccad28fff84c4ce5e2a1
size 17393 size 11938

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:697513adb4fa43d68e8a4477f248e68f64ba305f0ecb6a2e681b8a8b04ceee2b oid sha256:541b4210ff0acaf6451a0ce9c6cdc4f120c460086e036fc27e319daa23c7082b
size 14823 size 17393

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:697513adb4fa43d68e8a4477f248e68f64ba305f0ecb6a2e681b8a8b04ceee2b
size 14823

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f0c73cab66c885e528bd8723b14cfe28356f0843fd360164c2d6163833a9576b oid sha256:c24a3b08afe224e9919dd3818c72bd2ff8dd3f38663a6465a10b301ad91e8787
size 11999 size 20377

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:71e4c4f995456a5502fb057bbb37cebcb9b92af6edf315cff63f92b3541a30a2 oid sha256:f0c73cab66c885e528bd8723b14cfe28356f0843fd360164c2d6163833a9576b
size 17234 size 11999

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:466ff5436718dd1871635330108c592ff28727c1628368c11387a9b1f5c73642 oid sha256:71e4c4f995456a5502fb057bbb37cebcb9b92af6edf315cff63f92b3541a30a2
size 14204 size 17234

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:466ff5436718dd1871635330108c592ff28727c1628368c11387a9b1f5c73642
size 14204