Merge pull request #3631 from element-hq/feature/fga/rework_room_summary
Rework room summary
This commit is contained in:
@@ -45,6 +45,8 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.getRoomInfoFlow
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -120,12 +122,9 @@ class RoomFlowNode @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private fun subscribeToRoomInfoFlow(roomId: RoomId, serverNames: List<String>) {
|
||||
val roomInfoFlow = client.getRoomInfoFlow(
|
||||
roomId = roomId
|
||||
).map { it.getOrNull() }
|
||||
|
||||
val isSpaceFlow = roomInfoFlow.map { it?.isSpace.orFalse() }.distinctUntilChanged()
|
||||
val currentMembershipFlow = roomInfoFlow.map { it?.currentUserMembership }.distinctUntilChanged()
|
||||
val roomInfoFlow = client.getRoomInfoFlow(roomIdOrAlias = roomId.toRoomIdOrAlias())
|
||||
val isSpaceFlow = roomInfoFlow.map { it.getOrNull()?.isSpace.orFalse() }.distinctUntilChanged()
|
||||
val currentMembershipFlow = roomInfoFlow.map { it.getOrNull()?.currentUserMembership }.distinctUntilChanged()
|
||||
combine(currentMembershipFlow, isSpaceFlow) { membership, isSpace ->
|
||||
Timber.d("Room membership: $membership")
|
||||
when (membership) {
|
||||
|
||||
@@ -33,6 +33,8 @@ import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.getRoomInfoFlow
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
@@ -70,7 +72,7 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||
override fun present(): JoinRoomState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var retryCount by remember { mutableIntStateOf(0) }
|
||||
val roomInfo by matrixClient.getRoomInfoFlow(roomId).collectAsState(initial = Optional.empty())
|
||||
val roomInfo by matrixClient.getRoomInfoFlow(roomId.toRoomIdOrAlias()).collectAsState(initial = Optional.empty())
|
||||
val joinAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val knockAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val contentState by produceState<ContentState>(
|
||||
@@ -204,7 +206,7 @@ internal fun MatrixRoomInfo.toContentState(): ContentState {
|
||||
name = name,
|
||||
topic = topic,
|
||||
alias = canonicalAlias,
|
||||
numberOfMembers = activeMembersCount,
|
||||
numberOfMembers = activeMembersCount.toLong(),
|
||||
isDm = isDm,
|
||||
roomType = if (isSpace) RoomType.Space else RoomType.Room,
|
||||
roomAvatarUrl = avatarUrl,
|
||||
|
||||
@@ -19,7 +19,6 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||
|
||||
open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
||||
|
||||
@@ -32,8 +32,8 @@ import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_SERVER_LIST
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
@@ -67,10 +67,10 @@ class JoinRoomPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - when room is joined then content state is filled with his data`() = runTest {
|
||||
val roomInfo = aRoomInfo()
|
||||
val roomSummary = aRoomSummary()
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
@@ -81,22 +81,22 @@ class JoinRoomPresenterTest {
|
||||
awaitItem().also { state ->
|
||||
val contentState = state.contentState as ContentState.Loaded
|
||||
assertThat(contentState.roomId).isEqualTo(A_ROOM_ID)
|
||||
assertThat(contentState.name).isEqualTo(roomInfo.name)
|
||||
assertThat(contentState.topic).isEqualTo(roomInfo.topic)
|
||||
assertThat(contentState.alias).isEqualTo(roomInfo.canonicalAlias)
|
||||
assertThat(contentState.numberOfMembers).isEqualTo(roomInfo.activeMembersCount)
|
||||
assertThat(contentState.isDm).isEqualTo(roomInfo.isDirect)
|
||||
assertThat(contentState.roomAvatarUrl).isEqualTo(roomInfo.avatarUrl)
|
||||
assertThat(contentState.name).isEqualTo(roomSummary.info.name)
|
||||
assertThat(contentState.topic).isEqualTo(roomSummary.info.topic)
|
||||
assertThat(contentState.alias).isEqualTo(roomSummary.info.canonicalAlias)
|
||||
assertThat(contentState.numberOfMembers).isEqualTo(roomSummary.info.activeMembersCount)
|
||||
assertThat(contentState.isDm).isEqualTo(roomSummary.info.isDirect)
|
||||
assertThat(contentState.roomAvatarUrl).isEqualTo(roomSummary.info.avatarUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is invited then join authorization is equal to invited`() = runTest {
|
||||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED)
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.INVITED)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
@@ -114,13 +114,13 @@ class JoinRoomPresenterTest {
|
||||
fun `present - when room is invited then join authorization is equal to invited, an inviter is provided`() = runTest {
|
||||
val inviter = aRoomMember(userId = UserId("@bob:example.com"), displayName = "Bob")
|
||||
val expectedInviteSender = inviter.toInviteSender()
|
||||
val roomInfo = aRoomInfo(
|
||||
val roomSummary = aRoomSummary(
|
||||
currentUserMembership = CurrentUserMembership.INVITED,
|
||||
inviter = inviter,
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
@@ -140,10 +140,10 @@ class JoinRoomPresenterTest {
|
||||
val acceptDeclinePresenter = Presenter {
|
||||
anAcceptDeclineInviteState(eventSink = eventSinkRecorder)
|
||||
}
|
||||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED)
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.INVITED)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
@@ -224,10 +224,10 @@ class JoinRoomPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - when room is left and public then join authorization is equal to canJoin`() = runTest {
|
||||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.LEFT, isPublic = true)
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, isPublic = true)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
@@ -243,10 +243,10 @@ class JoinRoomPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - when room is left and not public then join authorization is equal to unknown`() = runTest {
|
||||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.LEFT, isPublic = false)
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, isPublic = false)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
|
||||
@@ -31,6 +31,7 @@ import im.vector.app.features.analytics.plan.Interaction
|
||||
import io.element.android.features.messages.impl.attachments.Attachment
|
||||
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
|
||||
import io.element.android.features.messages.impl.draft.ComposerDraftService
|
||||
import io.element.android.features.messages.impl.messagecomposer.suggestions.RoomAliasSuggestionsDataSource
|
||||
import io.element.android.features.messages.impl.messagecomposer.suggestions.SuggestionsProcessor
|
||||
import io.element.android.features.messages.impl.timeline.TimelineController
|
||||
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
||||
|
||||
@@ -5,20 +5,22 @@
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.messagecomposer
|
||||
package io.element.android.features.messages.impl.messagecomposer.suggestions
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
data class RoomAliasSuggestion(
|
||||
val roomAlias: RoomAlias,
|
||||
val roomSummary: RoomSummary,
|
||||
val roomId: RoomId,
|
||||
val roomName: String?,
|
||||
val roomAvatarUrl: String?,
|
||||
)
|
||||
|
||||
interface RoomAliasSuggestionsDataSource {
|
||||
@@ -32,14 +34,16 @@ class DefaultRoomAliasSuggestionsDataSource @Inject constructor(
|
||||
override fun getAllRoomAliasSuggestions(): Flow<List<RoomAliasSuggestion>> {
|
||||
return roomListService
|
||||
.allRooms
|
||||
.filteredSummaries
|
||||
.summaries
|
||||
.map { roomSummaries ->
|
||||
roomSummaries
|
||||
.mapNotNull { roomSummary ->
|
||||
roomSummary.canonicalAlias?.let { roomAlias ->
|
||||
roomSummary.info.canonicalAlias?.let { roomAlias ->
|
||||
RoomAliasSuggestion(
|
||||
roomAlias = roomAlias,
|
||||
roomSummary = roomSummary,
|
||||
roomId = roomSummary.roomId,
|
||||
roomName = roomSummary.info.name,
|
||||
roomAvatarUrl = roomSummary.info.avatarUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.ui.components.aRoomSummaryDetails
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -60,7 +59,7 @@ fun SuggestionsPickerView(
|
||||
when (suggestion) {
|
||||
is ResolvedSuggestion.AtRoom -> "@room"
|
||||
is ResolvedSuggestion.Member -> suggestion.roomMember.userId.value
|
||||
is ResolvedSuggestion.Alias -> suggestion.roomSummary.roomId.value
|
||||
is ResolvedSuggestion.Alias -> suggestion.roomId.value
|
||||
}
|
||||
}
|
||||
) {
|
||||
@@ -96,12 +95,12 @@ private fun SuggestionItemView(
|
||||
val avatarData = when (suggestion) {
|
||||
is ResolvedSuggestion.AtRoom -> roomAvatar?.copy(size = avatarSize) ?: AvatarData(roomId, roomName, null, avatarSize)
|
||||
is ResolvedSuggestion.Member -> suggestion.roomMember.getAvatarData(avatarSize)
|
||||
is ResolvedSuggestion.Alias -> suggestion.roomSummary.getAvatarData(avatarSize)
|
||||
is ResolvedSuggestion.Alias -> suggestion.getAvatarData(avatarSize)
|
||||
}
|
||||
val title = when (suggestion) {
|
||||
is ResolvedSuggestion.AtRoom -> stringResource(R.string.screen_room_mentions_at_room_title)
|
||||
is ResolvedSuggestion.Member -> suggestion.roomMember.displayName
|
||||
is ResolvedSuggestion.Alias -> suggestion.roomSummary.name
|
||||
is ResolvedSuggestion.Alias -> suggestion.roomName
|
||||
}
|
||||
val subtitle = when (suggestion) {
|
||||
is ResolvedSuggestion.AtRoom -> "@room"
|
||||
@@ -152,11 +151,6 @@ internal fun SuggestionsPickerViewPreview() {
|
||||
role = RoomMember.Role.USER,
|
||||
)
|
||||
val anAlias = remember { RoomAlias("#room:domain.org") }
|
||||
val roomSummaryDetails = remember {
|
||||
aRoomSummaryDetails(
|
||||
name = "My room",
|
||||
)
|
||||
}
|
||||
SuggestionsPickerView(
|
||||
roomId = RoomId("!room:matrix.org"),
|
||||
roomName = "Room",
|
||||
@@ -166,8 +160,10 @@ internal fun SuggestionsPickerViewPreview() {
|
||||
ResolvedSuggestion.Member(roomMember),
|
||||
ResolvedSuggestion.Member(roomMember.copy(userId = UserId("@bob:server.org"), displayName = "Bob")),
|
||||
ResolvedSuggestion.Alias(
|
||||
anAlias,
|
||||
roomSummaryDetails,
|
||||
roomAlias = anAlias,
|
||||
roomId = RoomId("!room:matrix.org"),
|
||||
roomName = "My room",
|
||||
roomAvatarUrl = null,
|
||||
)
|
||||
),
|
||||
onSelectSuggestion = {}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
package io.element.android.features.messages.impl.messagecomposer.suggestions
|
||||
|
||||
import io.element.android.features.messages.impl.messagecomposer.RoomAliasSuggestion
|
||||
import io.element.android.libraries.core.data.filterUpTo
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
@@ -55,7 +54,14 @@ class SuggestionsProcessor @Inject constructor() {
|
||||
SuggestionType.Room -> {
|
||||
roomAliasSuggestions
|
||||
.filter { it.roomAlias.value.contains(suggestion.text, ignoreCase = true) }
|
||||
.map { ResolvedSuggestion.Alias(it.roomAlias, it.roomSummary) }
|
||||
.map {
|
||||
ResolvedSuggestion.Alias(
|
||||
roomAlias = it.roomAlias,
|
||||
roomId = it.roomId,
|
||||
roomName = it.roomName,
|
||||
roomAvatarUrl = it.roomAvatarUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
SuggestionType.Command,
|
||||
is SuggestionType.Custom -> {
|
||||
|
||||
@@ -9,6 +9,8 @@ package io.element.android.features.messages.impl.messagecomposer
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.messagecomposer.suggestions.DefaultRoomAliasSuggestionsDataSource
|
||||
import io.element.android.features.messages.impl.messagecomposer.suggestions.RoomAliasSuggestion
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
@@ -38,7 +40,9 @@ class DefaultRoomAliasSuggestionsDataSourceTest {
|
||||
listOf(
|
||||
RoomAliasSuggestion(
|
||||
roomAlias = A_ROOM_ALIAS,
|
||||
roomSummary = aRoomSummaryWithAnAlias
|
||||
roomId = aRoomSummaryWithAnAlias.roomId,
|
||||
roomName = aRoomSummaryWithAnAlias.info.name,
|
||||
roomAvatarUrl = aRoomSummaryWithAnAlias.info.avatarUrl
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
package io.element.android.features.messages.impl.messagecomposer
|
||||
|
||||
import io.element.android.features.messages.impl.messagecomposer.suggestions.RoomAliasSuggestion
|
||||
import io.element.android.features.messages.impl.messagecomposer.suggestions.RoomAliasSuggestionsDataSource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package io.element.android.features.messages.impl.messagecomposer.suggestions
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.messagecomposer.RoomAliasSuggestion
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
@@ -110,13 +109,25 @@ class SuggestionsProcessorTest {
|
||||
val result = suggestionsProcessor.process(
|
||||
suggestion = aRoomSuggestion("ALI"),
|
||||
roomMembersState = MatrixRoomMembersState.Ready(persistentListOf()),
|
||||
roomAliasSuggestions = listOf(RoomAliasSuggestion(A_ROOM_ALIAS, aRoomSummary)),
|
||||
roomAliasSuggestions = listOf(
|
||||
RoomAliasSuggestion(
|
||||
roomAlias = A_ROOM_ALIAS,
|
||||
roomId = aRoomSummary.roomId,
|
||||
roomName = aRoomSummary.info.name,
|
||||
roomAvatarUrl = aRoomSummary.info.avatarUrl,
|
||||
)
|
||||
),
|
||||
currentUserId = A_USER_ID,
|
||||
canSendRoomMention = { true },
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary)
|
||||
ResolvedSuggestion.Alias(
|
||||
roomAlias = A_ROOM_ALIAS,
|
||||
roomId = aRoomSummary.roomId,
|
||||
roomName = aRoomSummary.info.name,
|
||||
roomAvatarUrl = aRoomSummary.info.avatarUrl,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -127,13 +138,25 @@ class SuggestionsProcessorTest {
|
||||
val result = suggestionsProcessor.process(
|
||||
suggestion = aRoomSuggestion("ali"),
|
||||
roomMembersState = MatrixRoomMembersState.Ready(persistentListOf()),
|
||||
roomAliasSuggestions = listOf(RoomAliasSuggestion(A_ROOM_ALIAS, aRoomSummary)),
|
||||
roomAliasSuggestions = listOf(
|
||||
RoomAliasSuggestion(
|
||||
roomAlias = A_ROOM_ALIAS,
|
||||
roomId = aRoomSummary.roomId,
|
||||
roomName = aRoomSummary.info.name,
|
||||
roomAvatarUrl = aRoomSummary.info.avatarUrl,
|
||||
)
|
||||
),
|
||||
currentUserId = A_USER_ID,
|
||||
canSendRoomMention = { true },
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary)
|
||||
ResolvedSuggestion.Alias(
|
||||
roomAlias = A_ROOM_ALIAS,
|
||||
roomId = aRoomSummary.roomId,
|
||||
roomName = aRoomSummary.info.name,
|
||||
roomAvatarUrl = aRoomSummary.info.avatarUrl,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -144,7 +167,14 @@ class SuggestionsProcessorTest {
|
||||
val result = suggestionsProcessor.process(
|
||||
suggestion = aRoomSuggestion("tot"),
|
||||
roomMembersState = MatrixRoomMembersState.Ready(persistentListOf()),
|
||||
roomAliasSuggestions = listOf(RoomAliasSuggestion(A_ROOM_ALIAS, aRoomSummary)),
|
||||
roomAliasSuggestions = listOf(
|
||||
RoomAliasSuggestion(
|
||||
roomAlias = A_ROOM_ALIAS,
|
||||
roomId = aRoomSummary.roomId,
|
||||
roomName = aRoomSummary.info.name,
|
||||
roomAvatarUrl = aRoomSummary.info.avatarUrl,
|
||||
)
|
||||
),
|
||||
currentUserId = A_USER_ID,
|
||||
canSendRoomMention = { true },
|
||||
)
|
||||
|
||||
@@ -21,11 +21,12 @@ import dagger.assisted.AssistedInject
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runUpdatingStateNoSuccess
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
@@ -40,7 +41,6 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||
private val notificationSettingsService: NotificationSettingsService,
|
||||
@Assisted private val isOneToOne: Boolean,
|
||||
private val roomListService: RoomListService,
|
||||
private val matrixClient: MatrixClient,
|
||||
) : Presenter<EditDefaultNotificationSettingState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
@@ -57,8 +57,8 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||
|
||||
val changeNotificationSettingAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
||||
val roomsWithUserDefinedMode: MutableState<List<RoomSummary>> = remember {
|
||||
mutableStateOf(listOf())
|
||||
val roomsWithUserDefinedMode: MutableState<List<EditNotificationSettingRoomInfo>> = remember {
|
||||
mutableStateOf(emptyList())
|
||||
}
|
||||
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
@@ -106,31 +106,37 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState<List<RoomSummary>>) {
|
||||
private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState<List<EditNotificationSettingRoomInfo>>) {
|
||||
roomListService.allRooms
|
||||
.summaries
|
||||
.onEach {
|
||||
updateRoomsWithUserDefinedMode(it, roomsWithUserDefinedMode)
|
||||
.onEach { roomSummaries ->
|
||||
updateRoomsWithUserDefinedMode(roomSummaries, roomsWithUserDefinedMode)
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.updateRoomsWithUserDefinedMode(
|
||||
private suspend fun updateRoomsWithUserDefinedMode(
|
||||
summaries: List<RoomSummary>,
|
||||
roomsWithUserDefinedMode: MutableState<List<RoomSummary>>
|
||||
) = launch {
|
||||
val roomWithUserDefinedRules: Set<String> = notificationSettingsService.getRoomsWithUserDefinedRules().getOrThrow().toSet()
|
||||
|
||||
val sortedSummaries = summaries
|
||||
.filterIsInstance<RoomSummary>()
|
||||
.filter {
|
||||
val room = matrixClient.getRoom(it.roomId) ?: return@filter false
|
||||
roomWithUserDefinedRules.contains(it.roomId.value) && isOneToOne == room.isOneToOne
|
||||
roomsWithUserDefinedMode: MutableState<List<EditNotificationSettingRoomInfo>>
|
||||
) {
|
||||
val roomWithUserDefinedRules: Set<String> = notificationSettingsService.getRoomsWithUserDefinedRules().getOrDefault(emptyList()).toSet()
|
||||
roomsWithUserDefinedMode.value = summaries
|
||||
.filter { roomSummary ->
|
||||
roomWithUserDefinedRules.contains(roomSummary.roomId.value) && roomSummary.isOneToOne == isOneToOne
|
||||
}
|
||||
.map { roomSummary ->
|
||||
EditNotificationSettingRoomInfo(
|
||||
roomId = roomSummary.roomId,
|
||||
name = roomSummary.info.name,
|
||||
heroesAvatar = roomSummary.info.heroes.map { hero ->
|
||||
hero.getAvatarData(AvatarSize.CustomRoomNotificationSetting)
|
||||
}.toImmutableList(),
|
||||
avatarData = roomSummary.info.getAvatarData(AvatarSize.CustomRoomNotificationSetting),
|
||||
notificationMode = roomSummary.info.userDefinedNotificationMode,
|
||||
)
|
||||
}
|
||||
// locale sensitive sorting
|
||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||
|
||||
roomsWithUserDefinedMode.value = sortedSummaries
|
||||
.sortedWith(compareBy(Collator.getInstance()) { roomSummary -> roomSummary.name })
|
||||
}
|
||||
|
||||
private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode, action: MutableState<AsyncAction<Unit>>) = launch {
|
||||
|
||||
@@ -9,13 +9,12 @@ package io.element.android.features.preferences.impl.notifications.edit
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class EditDefaultNotificationSettingState(
|
||||
val isOneToOne: Boolean,
|
||||
val mode: RoomNotificationMode?,
|
||||
val roomsWithUserDefinedMode: ImmutableList<RoomSummary>,
|
||||
val roomsWithUserDefinedMode: ImmutableList<EditNotificationSettingRoomInfo>,
|
||||
val changeNotificationSettingAction: AsyncAction<Unit>,
|
||||
val displayMentionsOnlyDisclaimer: Boolean,
|
||||
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
|
||||
|
||||
@@ -9,9 +9,10 @@ package io.element.android.features.preferences.impl.notifications.edit
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.ui.components.aRoomSummaryDetails
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
open class EditDefaultNotificationSettingStateProvider : PreviewParameterProvider<EditDefaultNotificationSettingState> {
|
||||
@@ -33,21 +34,25 @@ private fun anEditDefaultNotificationSettingsState(
|
||||
isOneToOne = isOneToOne,
|
||||
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||
roomsWithUserDefinedMode = persistentListOf(
|
||||
aRoomSummary("Room"),
|
||||
aRoomSummary(null),
|
||||
anEditNotificationSettingRoomInfo("Room"),
|
||||
anEditNotificationSettingRoomInfo(null),
|
||||
),
|
||||
changeNotificationSettingAction = changeNotificationSettingAction,
|
||||
displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer,
|
||||
eventSink = {}
|
||||
)
|
||||
|
||||
private fun aRoomSummary(
|
||||
private fun anEditNotificationSettingRoomInfo(
|
||||
name: String?,
|
||||
) = aRoomSummaryDetails(
|
||||
) = EditNotificationSettingRoomInfo(
|
||||
roomId = RoomId("!roomId:domain"),
|
||||
name = name,
|
||||
avatarUrl = null,
|
||||
isDirect = false,
|
||||
lastMessage = null,
|
||||
avatarData = AvatarData(
|
||||
id = "!roomId:domain",
|
||||
name = name,
|
||||
url = null,
|
||||
size = AvatarSize.CustomRoomNotificationSetting,
|
||||
),
|
||||
heroesAvatar = persistentListOf(),
|
||||
notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||
)
|
||||
|
||||
@@ -16,7 +16,6 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.avatar.CompositeAvatar
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
@@ -27,9 +26,7 @@ import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
/**
|
||||
* A view that allows a user to edit the default notification setting for rooms. This can be set separately
|
||||
@@ -80,7 +77,7 @@ fun EditDefaultNotificationSettingView(
|
||||
if (state.roomsWithUserDefinedMode.isNotEmpty()) {
|
||||
PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_edit_custom_settings_section_title)) {
|
||||
state.roomsWithUserDefinedMode.forEach { summary ->
|
||||
val subtitle = when (summary.userDefinedNotificationMode) {
|
||||
val subtitle = when (summary.notificationMode) {
|
||||
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages)
|
||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> {
|
||||
stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords)
|
||||
@@ -101,10 +98,8 @@ fun EditDefaultNotificationSettingView(
|
||||
},
|
||||
leadingContent = ListItemContent.Custom {
|
||||
CompositeAvatar(
|
||||
avatarData = summary.getAvatarData(size = AvatarSize.CustomRoomNotificationSetting),
|
||||
heroes = summary.heroes.map { user ->
|
||||
user.getAvatarData(size = AvatarSize.CustomRoomNotificationSetting)
|
||||
}.toPersistentList()
|
||||
avatarData = summary.avatarData,
|
||||
heroes = summary.heroesAvatar,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.preferences.impl.notifications.edit
|
||||
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class EditNotificationSettingRoomInfo(
|
||||
val roomId: RoomId,
|
||||
val name: String?,
|
||||
val heroesAvatar: ImmutableList<AvatarData>,
|
||||
val avatarData: AvatarData,
|
||||
val notificationMode: RoomNotificationMode?
|
||||
)
|
||||
@@ -14,11 +14,8 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter
|
||||
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import io.element.android.tests.testutils.awaitLastSequentialItem
|
||||
@@ -49,24 +46,20 @@ class EditDefaultNotificationSettingsPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - ensure list of rooms with user defined mode`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val notificationSettingsService = FakeNotificationSettingsService(
|
||||
initialRoomMode = RoomNotificationMode.ALL_MESSAGES,
|
||||
initialRoomModeIsDefault = false
|
||||
)
|
||||
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService).apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
val roomListService = FakeRoomListService()
|
||||
val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService, roomListService, matrixClient)
|
||||
val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService, roomListService)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
roomListService.postAllRooms(listOf(aRoomSummary(notificationMode = RoomNotificationMode.ALL_MESSAGES)))
|
||||
roomListService.postAllRooms(listOf(aRoomSummary(userDefinedNotificationMode = RoomNotificationMode.ALL_MESSAGES)))
|
||||
val loadedState = consumeItemsUntilPredicate { state ->
|
||||
state.roomsWithUserDefinedMode.any { it.userDefinedNotificationMode == RoomNotificationMode.ALL_MESSAGES }
|
||||
state.roomsWithUserDefinedMode.any { it.notificationMode == RoomNotificationMode.ALL_MESSAGES }
|
||||
}.last()
|
||||
assertThat(loadedState.roomsWithUserDefinedMode.any { it.userDefinedNotificationMode == RoomNotificationMode.ALL_MESSAGES }).isTrue()
|
||||
assertThat(loadedState.roomsWithUserDefinedMode.any { it.notificationMode == RoomNotificationMode.ALL_MESSAGES }).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,13 +114,11 @@ class EditDefaultNotificationSettingsPresenterTest {
|
||||
private fun createEditDefaultNotificationSettingPresenter(
|
||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
roomListService: FakeRoomListService = FakeRoomListService(),
|
||||
matrixClient: FakeMatrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
|
||||
): EditDefaultNotificationSettingPresenter {
|
||||
return EditDefaultNotificationSettingPresenter(
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
isOneToOne = false,
|
||||
roomListService = roomListService,
|
||||
matrixClient = matrixClient
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormat
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
@@ -24,34 +25,35 @@ class RoomListRoomSummaryFactory @Inject constructor(
|
||||
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
|
||||
private val roomLastMessageFormatter: RoomLastMessageFormatter,
|
||||
) {
|
||||
fun create(details: RoomSummary): RoomListRoomSummary {
|
||||
val avatarData = details.getAvatarData(size = AvatarSize.RoomListItem)
|
||||
fun create(roomSummary: RoomSummary): RoomListRoomSummary {
|
||||
val roomInfo = roomSummary.info
|
||||
val avatarData = roomInfo.getAvatarData(size = AvatarSize.RoomListItem)
|
||||
return RoomListRoomSummary(
|
||||
id = details.roomId.value,
|
||||
roomId = details.roomId,
|
||||
name = details.name,
|
||||
numberOfUnreadMessages = details.numUnreadMessages,
|
||||
numberOfUnreadMentions = details.numUnreadMentions,
|
||||
numberOfUnreadNotifications = details.numUnreadNotifications,
|
||||
isMarkedUnread = details.isMarkedUnread,
|
||||
timestamp = lastMessageTimestampFormatter.format(details.lastMessageTimestamp),
|
||||
lastMessage = details.lastMessage?.let { message ->
|
||||
roomLastMessageFormatter.format(message.event, details.isDm)
|
||||
id = roomSummary.roomId.value,
|
||||
roomId = roomSummary.roomId,
|
||||
name = roomInfo.name,
|
||||
numberOfUnreadMessages = roomInfo.numUnreadMessages,
|
||||
numberOfUnreadMentions = roomInfo.numUnreadMentions,
|
||||
numberOfUnreadNotifications = roomInfo.numUnreadNotifications,
|
||||
isMarkedUnread = roomInfo.isMarkedUnread,
|
||||
timestamp = lastMessageTimestampFormatter.format(roomSummary.lastMessageTimestamp),
|
||||
lastMessage = roomSummary.lastMessage?.let { message ->
|
||||
roomLastMessageFormatter.format(message.event, roomInfo.isDm)
|
||||
}.orEmpty(),
|
||||
avatarData = avatarData,
|
||||
userDefinedNotificationMode = details.userDefinedNotificationMode,
|
||||
hasRoomCall = details.hasRoomCall,
|
||||
isDirect = details.isDirect,
|
||||
isFavorite = details.isFavorite,
|
||||
inviteSender = details.inviter?.toInviteSender(),
|
||||
isDm = details.isDm,
|
||||
canonicalAlias = details.canonicalAlias,
|
||||
displayType = if (details.currentUserMembership == CurrentUserMembership.INVITED) {
|
||||
userDefinedNotificationMode = roomInfo.userDefinedNotificationMode,
|
||||
hasRoomCall = roomInfo.hasRoomCall,
|
||||
isDirect = roomInfo.isDirect,
|
||||
isFavorite = roomInfo.isFavorite,
|
||||
inviteSender = roomInfo.inviter?.toInviteSender(),
|
||||
isDm = roomInfo.isDm,
|
||||
canonicalAlias = roomInfo.canonicalAlias,
|
||||
displayType = if (roomInfo.currentUserMembership == CurrentUserMembership.INVITED) {
|
||||
RoomSummaryDisplayType.INVITE
|
||||
} else {
|
||||
RoomSummaryDisplayType.ROOM
|
||||
},
|
||||
heroes = details.heroes.map { user ->
|
||||
heroes = roomInfo.heroes.map { user ->
|
||||
user.getAvatarData(size = AvatarSize.RoomListItem)
|
||||
}.toImmutableList(),
|
||||
)
|
||||
|
||||
@@ -22,9 +22,9 @@ data class RoomListRoomSummary(
|
||||
val roomId: RoomId,
|
||||
val name: String?,
|
||||
val canonicalAlias: RoomAlias?,
|
||||
val numberOfUnreadMessages: Int,
|
||||
val numberOfUnreadMentions: Int,
|
||||
val numberOfUnreadNotifications: Int,
|
||||
val numberOfUnreadMessages: Long,
|
||||
val numberOfUnreadMentions: Long,
|
||||
val numberOfUnreadNotifications: Long,
|
||||
val isMarkedUnread: Boolean,
|
||||
val timestamp: String?,
|
||||
val lastMessage: CharSequence?,
|
||||
|
||||
@@ -119,9 +119,9 @@ internal fun anInviteSender(
|
||||
internal fun aRoomListRoomSummary(
|
||||
id: String = "!roomId:domain",
|
||||
name: String? = "Room name",
|
||||
numberOfUnreadMessages: Int = 0,
|
||||
numberOfUnreadMentions: Int = 0,
|
||||
numberOfUnreadNotifications: Int = 0,
|
||||
numberOfUnreadMessages: Long = 0,
|
||||
numberOfUnreadMentions: Long = 0,
|
||||
numberOfUnreadNotifications: Long = 0,
|
||||
isMarkedUnread: Boolean = false,
|
||||
lastMessage: String? = "Last message",
|
||||
timestamp: String? = lastMessage?.let { "88:88" },
|
||||
|
||||
@@ -396,7 +396,7 @@ class RoomListPresenterTest {
|
||||
val notificationSettingsService = FakeNotificationSettingsService()
|
||||
val roomListService = FakeRoomListService()
|
||||
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
||||
roomListService.postAllRooms(listOf(aRoomSummary(notificationMode = userDefinedMode)))
|
||||
roomListService.postAllRooms(listOf(aRoomSummary(userDefinedNotificationMode = userDefinedMode)))
|
||||
val matrixClient = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
notificationSettingsService = notificationSettingsService
|
||||
|
||||
@@ -76,9 +76,9 @@ class RoomListRoomSummaryTest {
|
||||
}
|
||||
|
||||
internal fun createRoomListRoomSummary(
|
||||
numberOfUnreadMentions: Int = 0,
|
||||
numberOfUnreadMessages: Int = 0,
|
||||
numberOfUnreadNotifications: Int = 0,
|
||||
numberOfUnreadMentions: Long = 0,
|
||||
numberOfUnreadMessages: Long = 0,
|
||||
numberOfUnreadNotifications: Long = 0,
|
||||
isMarkedUnread: Boolean = false,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
isFavorite: Boolean = false,
|
||||
|
||||
@@ -38,6 +38,8 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import java.io.Closeable
|
||||
import java.util.Optional
|
||||
|
||||
@@ -94,7 +96,13 @@ interface MatrixClient : Closeable {
|
||||
suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?>
|
||||
suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String>
|
||||
fun roomMembershipObserver(): RoomMembershipObserver
|
||||
fun getRoomInfoFlow(roomId: RoomId): Flow<Optional<MatrixRoomInfo>>
|
||||
|
||||
/**
|
||||
* Get a room summary flow for a given room ID or alias.
|
||||
* The flow will emit a new value whenever the room summary is updated.
|
||||
* The flow will emit Optional.empty item if the room is not found.
|
||||
*/
|
||||
fun getRoomSummaryFlow(roomIdOrAlias: RoomIdOrAlias): Flow<Optional<RoomSummary>>
|
||||
|
||||
fun isMe(userId: UserId?) = userId == sessionId
|
||||
|
||||
@@ -142,3 +150,14 @@ interface MatrixClient : Closeable {
|
||||
fun canDeactivateAccount(): Boolean
|
||||
suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit>
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a room info flow for a given room ID or alias.
|
||||
* The flow will emit a new value whenever the room info is updated.
|
||||
* The flow will emit Optional.empty item if the room is not found.
|
||||
*/
|
||||
fun MatrixClient.getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow<Optional<MatrixRoomInfo>> {
|
||||
return getRoomSummaryFlow(roomIdOrAlias)
|
||||
.map { roomSummary -> roomSummary.map { it.info } }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
@@ -33,6 +33,13 @@ data class MatrixRoomInfo(
|
||||
val canonicalAlias: RoomAlias?,
|
||||
val alternativeAliases: ImmutableList<RoomAlias>,
|
||||
val currentUserMembership: CurrentUserMembership,
|
||||
/**
|
||||
* Member who invited the current user to a room that's in the invited
|
||||
* state.
|
||||
*
|
||||
* Can be missing if the room membership invite event is missing from the
|
||||
* store.
|
||||
*/
|
||||
val inviter: RoomMember?,
|
||||
val activeMembersCount: Long,
|
||||
val invitedMembersCount: Long,
|
||||
@@ -43,7 +50,26 @@ data class MatrixRoomInfo(
|
||||
val userDefinedNotificationMode: RoomNotificationMode?,
|
||||
val hasRoomCall: Boolean,
|
||||
val activeRoomCallParticipants: ImmutableList<UserId>,
|
||||
val isMarkedUnread: Boolean,
|
||||
/**
|
||||
* "Interesting" messages received in that room, independently of the
|
||||
* notification settings.
|
||||
*/
|
||||
val numUnreadMessages: Long,
|
||||
/**
|
||||
* Events that will notify the user, according to their
|
||||
* notification settings.
|
||||
*/
|
||||
val numUnreadNotifications: Long,
|
||||
/**
|
||||
* Events causing mentions/highlights for the user, according to their
|
||||
* notification settings.
|
||||
*/
|
||||
val numUnreadMentions: Long,
|
||||
val heroes: ImmutableList<MatrixUser>,
|
||||
val pinnedEventIds: ImmutableList<EventId>,
|
||||
val creator: UserId?,
|
||||
)
|
||||
) {
|
||||
val aliases: List<RoomAlias>
|
||||
get() = listOfNotNull(canonicalAlias) + alternativeAliases
|
||||
}
|
||||
|
||||
@@ -7,35 +7,14 @@
|
||||
|
||||
package io.element.android.libraries.matrix.api.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
data class RoomSummary(
|
||||
val roomId: RoomId,
|
||||
val name: String?,
|
||||
val canonicalAlias: RoomAlias?,
|
||||
val alternativeAliases: List<RoomAlias>,
|
||||
val isDirect: Boolean,
|
||||
val avatarUrl: String?,
|
||||
val info: MatrixRoomInfo,
|
||||
val lastMessage: RoomMessage?,
|
||||
val numUnreadMessages: Int,
|
||||
val numUnreadMentions: Int,
|
||||
val numUnreadNotifications: Int,
|
||||
val isMarkedUnread: Boolean,
|
||||
val inviter: RoomMember?,
|
||||
val userDefinedNotificationMode: RoomNotificationMode?,
|
||||
val hasRoomCall: Boolean,
|
||||
val isDm: Boolean,
|
||||
val isFavorite: Boolean,
|
||||
val currentUserMembership: CurrentUserMembership,
|
||||
val heroes: List<MatrixUser>,
|
||||
) {
|
||||
val roomId = info.id
|
||||
val lastMessageTimestamp = lastMessage?.originServerTs
|
||||
val aliases: List<RoomAlias>
|
||||
get() = listOfNotNull(canonicalAlias) + alternativeAliases
|
||||
val isOneToOne get() = info.activeMembersCount == 2L
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.InvitedRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
||||
@@ -84,8 +83,8 @@ import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -105,8 +104,8 @@ import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.INFINITE
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import org.matrix.rustcomponents.sdk.CreateRoomParameters as RustCreateRoomParameters
|
||||
import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset
|
||||
@@ -262,21 +261,14 @@ class RustMatrixClient(
|
||||
* @param timeout the timeout to wait for the room to be available
|
||||
* @throws TimeoutCancellationException if the room is not available after the timeout
|
||||
*/
|
||||
private suspend fun awaitJoinedRoom(roomIdOrAlias: RoomIdOrAlias, timeout: Duration): RoomSummary {
|
||||
val predicate: (List<RoomSummary>) -> Boolean = when (roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> { roomSummaries: List<RoomSummary> ->
|
||||
val found = roomSummaries.find { it.aliases.contains(roomIdOrAlias.roomAlias) }
|
||||
found != null && found.currentUserMembership == CurrentUserMembership.JOINED
|
||||
}
|
||||
is RoomIdOrAlias.Id -> { roomSummaries: List<RoomSummary> ->
|
||||
val found = roomSummaries.find { it.roomId == roomIdOrAlias.roomId }
|
||||
found != null && found.currentUserMembership == CurrentUserMembership.JOINED
|
||||
}
|
||||
}
|
||||
private suspend fun awaitJoinedRoom(
|
||||
roomIdOrAlias: RoomIdOrAlias,
|
||||
timeout: Duration
|
||||
): RoomSummary {
|
||||
return withTimeout(timeout) {
|
||||
roomListService.allRooms.summaries
|
||||
.filter(predicate)
|
||||
.first()
|
||||
getRoomSummaryFlow(roomIdOrAlias)
|
||||
.mapNotNull { optionalRoomSummary -> optionalRoomSummary.getOrNull() }
|
||||
.filter { roomSummary -> roomSummary.info.currentUserMembership == CurrentUserMembership.JOINED }
|
||||
.first()
|
||||
// Ensure that the room is ready
|
||||
.also { client.awaitRoomRemoteEcho(it.roomId.value) }
|
||||
@@ -568,20 +560,21 @@ class RustMatrixClient(
|
||||
|
||||
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
|
||||
|
||||
override fun getRoomInfoFlow(roomId: RoomId): Flow<Optional<MatrixRoomInfo>> {
|
||||
return flow {
|
||||
var room = getRoom(roomId)
|
||||
if (room == null) {
|
||||
emit(Optional.empty())
|
||||
awaitJoinedRoom(roomId.toRoomIdOrAlias(), INFINITE)
|
||||
room = getRoom(roomId)
|
||||
override fun getRoomSummaryFlow(roomIdOrAlias: RoomIdOrAlias): Flow<Optional<RoomSummary>> {
|
||||
val predicate: (RoomSummary) -> Boolean = when (roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> { roomSummary ->
|
||||
roomSummary.info.aliases.contains(roomIdOrAlias.roomAlias)
|
||||
}
|
||||
room?.use {
|
||||
room.roomInfoFlow
|
||||
.map { roomInfo -> Optional.of(roomInfo) }
|
||||
.collect(this)
|
||||
is RoomIdOrAlias.Id -> { roomSummary ->
|
||||
roomSummary.roomId == roomIdOrAlias.roomId
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
return roomListService.allRooms.summaries
|
||||
.map { roomSummaries ->
|
||||
val roomSummary = roomSummaries.firstOrNull(predicate)
|
||||
Optional.ofNullable(roomSummary)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
override suspend fun setAllSendQueuesEnabled(enabled: Boolean) = withContext(sessionDispatcher) {
|
||||
|
||||
@@ -53,6 +53,10 @@ class MatrixRoomInfoMapper {
|
||||
activeRoomCallParticipants = it.activeRoomCallParticipants.map(::UserId).toImmutableList(),
|
||||
heroes = it.elementHeroes().toImmutableList(),
|
||||
pinnedEventIds = it.pinnedEventIds.map(::EventId).toImmutableList(),
|
||||
isMarkedUnread = it.isMarkedUnread,
|
||||
numUnreadMessages = it.numUnreadMessages.toLong(),
|
||||
numUnreadMentions = it.numUnreadMentions.toLong(),
|
||||
numUnreadNotifications = it.numUnreadNotifications.toLong(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ internal class RoomListFactory(
|
||||
private val innerRoomListService: RoomListService,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
) {
|
||||
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory()
|
||||
private val roomSummaryDetailsFactory: RoomSummaryFactory = RoomSummaryFactory()
|
||||
|
||||
/**
|
||||
* Creates a room list that can be used to load more rooms and filter them dynamically.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
|
||||
@@ -17,19 +18,19 @@ val RoomListFilter.predicate
|
||||
is RoomListFilter.Any -> { _: RoomSummary -> true }
|
||||
RoomListFilter.None -> { _: RoomSummary -> false }
|
||||
RoomListFilter.Category.Group -> { roomSummary: RoomSummary ->
|
||||
!roomSummary.isDm && !roomSummary.isInvited()
|
||||
!roomSummary.info.isDm && !roomSummary.isInvited()
|
||||
}
|
||||
RoomListFilter.Category.People -> { roomSummary: RoomSummary ->
|
||||
roomSummary.isDm && !roomSummary.isInvited()
|
||||
roomSummary.info.isDm && !roomSummary.isInvited()
|
||||
}
|
||||
RoomListFilter.Favorite -> { roomSummary: RoomSummary ->
|
||||
roomSummary.isFavorite && !roomSummary.isInvited()
|
||||
roomSummary.info.isFavorite && !roomSummary.isInvited()
|
||||
}
|
||||
RoomListFilter.Unread -> { roomSummary: RoomSummary ->
|
||||
!roomSummary.isInvited() && (roomSummary.numUnreadNotifications > 0 || roomSummary.isMarkedUnread)
|
||||
!roomSummary.isInvited() && (roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread)
|
||||
}
|
||||
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
||||
roomSummary.name.orEmpty().contains(pattern, ignoreCase = true)
|
||||
roomSummary.info.name.orEmpty().contains(pattern, ignoreCase = true)
|
||||
}
|
||||
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
|
||||
roomSummary.isInvited()
|
||||
@@ -50,4 +51,4 @@ fun List<RoomSummary>.filter(filter: RoomListFilter): List<RoomSummary> {
|
||||
}
|
||||
}
|
||||
|
||||
private fun RoomSummary.isInvited() = currentUserMembership == CurrentUserMembership.INVITED
|
||||
private fun RoomSummary.isInvited() = info.currentUserMembership == CurrentUserMembership.INVITED
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.impl.notificationsettings.RoomNotificationSettingsMapper
|
||||
import io.element.android.libraries.matrix.impl.room.elementHeroes
|
||||
import io.element.android.libraries.matrix.impl.room.map
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
||||
import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
|
||||
class RoomSummaryDetailsFactory(
|
||||
private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory(),
|
||||
) {
|
||||
suspend fun create(roomListItem: RoomListItem): RoomSummary {
|
||||
val roomInfo = roomListItem.roomInfo()
|
||||
val latestRoomMessage = roomListItem.latestEvent().use { event ->
|
||||
roomMessageFactory.create(event)
|
||||
}
|
||||
return RoomSummary(
|
||||
roomId = RoomId(roomInfo.id),
|
||||
name = roomInfo.displayName,
|
||||
canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias),
|
||||
alternativeAliases = roomInfo.alternativeAliases.map(::RoomAlias),
|
||||
isDirect = roomInfo.isDirect,
|
||||
avatarUrl = roomInfo.avatarUrl,
|
||||
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.cachedUserDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode),
|
||||
hasRoomCall = roomInfo.hasRoomCall,
|
||||
isDm = isDm(isDirect = roomInfo.isDirect, activeMembersCount = roomInfo.activeMembersCount.toInt()),
|
||||
isFavorite = roomInfo.isFavourite,
|
||||
currentUserMembership = roomInfo.membership.map(),
|
||||
heroes = roomInfo.elementHeroes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.impl.room.MatrixRoomInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
|
||||
class RoomSummaryFactory(
|
||||
private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory(),
|
||||
private val roomInfoMapper: MatrixRoomInfoMapper = MatrixRoomInfoMapper(),
|
||||
) {
|
||||
suspend fun create(roomListItem: RoomListItem): RoomSummary {
|
||||
val roomInfo = roomListItem.roomInfo().let(roomInfoMapper::map)
|
||||
val latestRoomMessage = roomListItem.latestEvent().use { event ->
|
||||
roomMessageFactory.create(event)
|
||||
}
|
||||
return RoomSummary(
|
||||
info = roomInfo,
|
||||
lastMessage = latestRoomMessage,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class RoomSummaryListProcessor(
|
||||
private val roomSummaries: MutableSharedFlow<List<RoomSummary>>,
|
||||
private val roomListService: RoomListServiceInterface,
|
||||
private val coroutineContext: CoroutineContext,
|
||||
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||
private val roomSummaryDetailsFactory: RoomSummaryFactory = RoomSummaryFactory(),
|
||||
) {
|
||||
private val roomSummariesByIdentifier = HashMap<String, RoomSummary>()
|
||||
private val mutex = Mutex()
|
||||
|
||||
@@ -105,6 +105,10 @@ class MatrixRoomInfoMapperTest {
|
||||
).toImmutableList(),
|
||||
pinnedEventIds = listOf(AN_EVENT_ID).toPersistentList(),
|
||||
creator = A_USER_ID,
|
||||
isMarkedUnread = false,
|
||||
numUnreadMessages = 12L,
|
||||
numUnreadNotifications = 13L,
|
||||
numUnreadMentions = 14L,
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -174,6 +178,10 @@ class MatrixRoomInfoMapperTest {
|
||||
heroes = emptyList<MatrixUser>().toImmutableList(),
|
||||
pinnedEventIds = emptyList<EventId>().toPersistentList(),
|
||||
creator = null,
|
||||
isMarkedUnread = true,
|
||||
numUnreadMessages = 12L,
|
||||
numUnreadNotifications = 13L,
|
||||
numUnreadMentions = 14L,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SERVER_LIST
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
@@ -29,7 +29,7 @@ import org.junit.Test
|
||||
class DefaultJoinRoomTest {
|
||||
@Test
|
||||
fun `when using roomId and there is no server names, the classic join room API is used`() = runTest {
|
||||
val roomSummary = aRoomSummaryFilled()
|
||||
val roomSummary = aRoomSummary()
|
||||
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
||||
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
||||
val roomResult = FakeMatrixRoom()
|
||||
@@ -64,7 +64,7 @@ class DefaultJoinRoomTest {
|
||||
|
||||
@Test
|
||||
fun `when using roomId and server names are available, joinRoomByIdOrAlias API is used`() = runTest {
|
||||
val roomSummary = aRoomSummaryFilled()
|
||||
val roomSummary = aRoomSummary()
|
||||
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
||||
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
||||
val roomResult = FakeMatrixRoom()
|
||||
@@ -100,7 +100,7 @@ class DefaultJoinRoomTest {
|
||||
|
||||
@Test
|
||||
fun `when using roomAlias, joinRoomByIdOrAlias API is used`() = runTest {
|
||||
val roomSummary = aRoomSummaryFilled()
|
||||
val roomSummary = aRoomSummary()
|
||||
val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) }
|
||||
val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List<String> -> Result.success(roomSummary) }
|
||||
val roomResult = FakeMatrixRoom()
|
||||
|
||||
@@ -16,10 +16,11 @@ import org.junit.Test
|
||||
|
||||
class RoomListFilterTest {
|
||||
private val regularRoom = aRoomSummary(
|
||||
isDm = false
|
||||
isDirect = false,
|
||||
)
|
||||
private val dmRoom = aRoomSummary(
|
||||
isDm = true
|
||||
isDirect = true,
|
||||
activeMembersCount = 2
|
||||
)
|
||||
private val favoriteRoom = aRoomSummary(
|
||||
isFavorite = true
|
||||
|
||||
@@ -15,7 +15,6 @@ 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.matrix.test.A_ROOM_ID_3
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
@@ -40,7 +39,7 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled())
|
||||
summaries.value = listOf(aRoomSummary())
|
||||
val processor = createProcessor()
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(FakeRustRoomListItem(A_ROOM_ID_2))))
|
||||
|
||||
@@ -50,7 +49,7 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled())
|
||||
summaries.value = listOf(aRoomSummary())
|
||||
val processor = createProcessor()
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(FakeRustRoomListItem(A_ROOM_ID_2))))
|
||||
|
||||
@@ -60,7 +59,7 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `Set replaces an entry at some index`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled())
|
||||
summaries.value = listOf(aRoomSummary())
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
@@ -72,7 +71,7 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `Insert inserts a new entry at the provided index`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled())
|
||||
summaries.value = listOf(aRoomSummary())
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
@@ -84,7 +83,10 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `Remove removes an entry at some index`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||
summaries.value = listOf(
|
||||
aRoomSummary(roomId = A_ROOM_ID),
|
||||
aRoomSummary(A_ROOM_ID_2)
|
||||
)
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
@@ -96,7 +98,10 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `PopBack removes an entry at the end of the list`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||
summaries.value = listOf(
|
||||
aRoomSummary(roomId = A_ROOM_ID),
|
||||
aRoomSummary(A_ROOM_ID_2)
|
||||
)
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
@@ -108,7 +113,10 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `PopFront removes an entry at the start of the list`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||
summaries.value = listOf(
|
||||
aRoomSummary(roomId = A_ROOM_ID),
|
||||
aRoomSummary(A_ROOM_ID_2)
|
||||
)
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
@@ -120,7 +128,10 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `Clear removes all the entries`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||
summaries.value = listOf(
|
||||
aRoomSummary(roomId = A_ROOM_ID),
|
||||
aRoomSummary(A_ROOM_ID_2)
|
||||
)
|
||||
val processor = createProcessor()
|
||||
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Clear))
|
||||
@@ -130,7 +141,10 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `Truncate removes all entries after the provided length`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||
summaries.value = listOf(
|
||||
aRoomSummary(roomId = A_ROOM_ID),
|
||||
aRoomSummary(A_ROOM_ID_2)
|
||||
)
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
@@ -142,7 +156,10 @@ class RoomSummaryListProcessorTest {
|
||||
|
||||
@Test
|
||||
fun `Reset removes all entries and add the provided ones`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||
summaries.value = listOf(
|
||||
aRoomSummary(roomId = A_ROOM_ID),
|
||||
aRoomSummary(A_ROOM_ID_2)
|
||||
)
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
@@ -156,6 +173,6 @@ class RoomSummaryListProcessorTest {
|
||||
summaries,
|
||||
FakeRustRoomListService(),
|
||||
coroutineContext = StandardTestDispatcher(testScheduler),
|
||||
roomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||
roomSummaryDetailsFactory = RoomSummaryFactory(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.InvitedRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
||||
@@ -118,8 +117,8 @@ class FakeMatrixClient(
|
||||
var knockRoomLambda: (RoomId) -> Result<Unit> = {
|
||||
Result.success(Unit)
|
||||
}
|
||||
var getRoomInfoFlowLambda = { _: RoomId ->
|
||||
flowOf<Optional<MatrixRoomInfo>>(Optional.empty())
|
||||
var getRoomSummaryFlowLambda = { _: RoomIdOrAlias ->
|
||||
flowOf<Optional<RoomSummary>>(Optional.empty())
|
||||
}
|
||||
var logoutLambda: (Boolean, Boolean) -> String? = { _, _ ->
|
||||
null
|
||||
@@ -316,7 +315,7 @@ class FakeMatrixClient(
|
||||
return Result.success(visitedRoomsId)
|
||||
}
|
||||
|
||||
override fun getRoomInfoFlow(roomId: RoomId) = getRoomInfoFlowLambda(roomId)
|
||||
override fun getRoomSummaryFlow(roomIdOrAlias: RoomIdOrAlias) = getRoomSummaryFlowLambda(roomIdOrAlias)
|
||||
|
||||
var setAllSendQueuesEnabledLambda = lambdaRecorder(ensureNeverCalled = true) { _: Boolean ->
|
||||
// no-op
|
||||
|
||||
@@ -24,7 +24,6 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
@@ -32,7 +31,6 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
@@ -40,21 +38,15 @@ import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerL
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
@@ -527,62 +519,6 @@ class FakeMatrixRoom(
|
||||
}
|
||||
}
|
||||
|
||||
fun aRoomInfo(
|
||||
id: RoomId = A_ROOM_ID,
|
||||
name: String? = A_ROOM_NAME,
|
||||
rawName: String? = name,
|
||||
topic: String? = "A topic",
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = true,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 1,
|
||||
invitedMembersCount: Long = 0,
|
||||
joinedMembersCount: Long = 1,
|
||||
highlightCount: Long = 0,
|
||||
notificationCount: Long = 0,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
userPowerLevels: ImmutableMap<UserId, Long> = persistentMapOf(),
|
||||
activeRoomCallParticipants: List<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreator: UserId? = null,
|
||||
) = MatrixRoomInfo(
|
||||
id = id,
|
||||
name = name,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
isFavorite = isFavorite,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases.toImmutableList(),
|
||||
currentUserMembership = currentUserMembership,
|
||||
inviter = inviter,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
userDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
userPowerLevels = userPowerLevels,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
|
||||
heroes = heroes.toImmutableList(),
|
||||
pinnedEventIds = pinnedEventIds.toImmutableList(),
|
||||
creator = roomCreator,
|
||||
)
|
||||
|
||||
fun defaultRoomPowerLevels() = MatrixRoomPowerLevels(
|
||||
ban = 50,
|
||||
invite = 0,
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_RAW_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
fun aRoomInfo(
|
||||
id: RoomId = A_ROOM_ID,
|
||||
name: String? = A_ROOM_NAME,
|
||||
rawName: String? = A_ROOM_RAW_NAME,
|
||||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = true,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 1,
|
||||
invitedMembersCount: Long = 0,
|
||||
joinedMembersCount: Long = 1,
|
||||
highlightCount: Long = 0,
|
||||
notificationCount: Long = 0,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
userPowerLevels: ImmutableMap<UserId, Long> = persistentMapOf(),
|
||||
activeRoomCallParticipants: List<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreator: UserId? = null,
|
||||
isMarkedUnread: Boolean = false,
|
||||
numUnreadMessages: Long = 0,
|
||||
numUnreadNotifications: Long = 0,
|
||||
numUnreadMentions: Long = 0,
|
||||
) = MatrixRoomInfo(
|
||||
id = id,
|
||||
name = name,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
isFavorite = isFavorite,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases.toImmutableList(),
|
||||
currentUserMembership = currentUserMembership,
|
||||
inviter = inviter,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
userDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
userPowerLevels = userPowerLevels,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
|
||||
heroes = heroes.toImmutableList(),
|
||||
pinnedEventIds = pinnedEventIds.toImmutableList(),
|
||||
creator = roomCreator,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
)
|
||||
@@ -12,6 +12,7 @@ import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
||||
@@ -21,69 +22,88 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_RAW_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
fun aRoomSummaryFilled(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String = A_ROOM_NAME,
|
||||
isDirect: Boolean = false,
|
||||
avatarUrl: String? = null,
|
||||
fun aRoomSummary(
|
||||
info: MatrixRoomInfo = aRoomInfo(),
|
||||
lastMessage: RoomMessage? = aRoomMessage(),
|
||||
numUnreadMentions: Int = 0,
|
||||
numUnreadMessages: Int = 0,
|
||||
notificationMode: RoomNotificationMode? = null,
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
) = aRoomSummary(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
isDirect = isDirect,
|
||||
avatarUrl = avatarUrl,
|
||||
) = RoomSummary(
|
||||
info = info,
|
||||
lastMessage = lastMessage,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
notificationMode = notificationMode,
|
||||
currentUserMembership = currentUserMembership,
|
||||
)
|
||||
|
||||
fun aRoomSummary(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String? = A_ROOM_NAME,
|
||||
isDirect: Boolean = false,
|
||||
rawName: String? = A_ROOM_RAW_NAME,
|
||||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = null,
|
||||
lastMessage: RoomMessage? = aRoomMessage(),
|
||||
numUnreadMentions: Int = 0,
|
||||
numUnreadMessages: Int = 0,
|
||||
numUnreadNotifications: Int = 0,
|
||||
isMarkedUnread: Boolean = false,
|
||||
notificationMode: RoomNotificationMode? = null,
|
||||
inviter: RoomMember? = null,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = true,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
hasRoomCall: Boolean = false,
|
||||
isDm: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 1,
|
||||
invitedMembersCount: Long = 0,
|
||||
joinedMembersCount: Long = 1,
|
||||
highlightCount: Long = 0,
|
||||
notificationCount: Long = 0,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
userPowerLevels: ImmutableMap<UserId, Long> = persistentMapOf(),
|
||||
activeRoomCallParticipants: List<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreator: UserId? = null,
|
||||
isMarkedUnread: Boolean = false,
|
||||
numUnreadMessages: Long = 0,
|
||||
numUnreadNotifications: Long = 0,
|
||||
numUnreadMentions: Long = 0,
|
||||
lastMessage: RoomMessage? = aRoomMessage(),
|
||||
) = RoomSummary(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
isDirect = isDirect,
|
||||
avatarUrl = avatarUrl,
|
||||
info = MatrixRoomInfo(
|
||||
id = roomId,
|
||||
name = name,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
isFavorite = isFavorite,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases.toPersistentList(),
|
||||
currentUserMembership = currentUserMembership,
|
||||
inviter = inviter,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
userPowerLevels = userPowerLevels,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
userDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants.toPersistentList(),
|
||||
heroes = heroes.toPersistentList(),
|
||||
pinnedEventIds = pinnedEventIds.toPersistentList(),
|
||||
creator = roomCreator,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
),
|
||||
lastMessage = lastMessage,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
userDefinedNotificationMode = notificationMode,
|
||||
inviter = inviter,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases,
|
||||
hasRoomCall = hasRoomCall,
|
||||
isDm = isDm,
|
||||
isFavorite = isFavorite,
|
||||
currentUserMembership = currentUserMembership,
|
||||
heroes = heroes,
|
||||
)
|
||||
|
||||
fun aRoomMessage(
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.ui.components
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
open class RoomSummaryDetailsProvider : PreviewParameterProvider<RoomSummary> {
|
||||
override val values: Sequence<RoomSummary>
|
||||
get() = sequenceOf(
|
||||
aRoomSummaryDetails(),
|
||||
aRoomSummaryDetails(name = null),
|
||||
)
|
||||
}
|
||||
|
||||
fun aRoomSummaryDetails(
|
||||
roomId: RoomId = RoomId("!room:domain"),
|
||||
name: String? = "roomName",
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
isDirect: Boolean = true,
|
||||
avatarUrl: String? = null,
|
||||
lastMessage: RoomMessage? = null,
|
||||
inviter: RoomMember? = null,
|
||||
notificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
isDm: Boolean = false,
|
||||
numUnreadMentions: Int = 0,
|
||||
numUnreadMessages: Int = 0,
|
||||
numUnreadNotifications: Int = 0,
|
||||
isMarkedUnread: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
) = RoomSummary(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases,
|
||||
isDirect = isDirect,
|
||||
avatarUrl = avatarUrl,
|
||||
lastMessage = lastMessage,
|
||||
inviter = inviter,
|
||||
userDefinedNotificationMode = notificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
isDm = isDm,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
isFavorite = isFavorite,
|
||||
currentUserMembership = currentUserMembership,
|
||||
heroes = heroes,
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.ui.components
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
class SelectRoomInfoProvider : PreviewParameterProvider<SelectRoomInfo> {
|
||||
override val values: Sequence<SelectRoomInfo>
|
||||
get() = sequenceOf(
|
||||
aSelectRoomInfo(roomId = RoomId("!room1:domain")),
|
||||
aSelectRoomInfo(roomId = RoomId("!room2:domain"), name = "Room with a name"),
|
||||
aSelectRoomInfo(roomId = RoomId("!room3:domain"), name = "Room with a name and alias", canonicalAlias = RoomAlias("#alias:domain")),
|
||||
)
|
||||
}
|
||||
|
||||
fun aSelectRoomInfo(
|
||||
roomId: RoomId,
|
||||
name: String? = null,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
avatarUrl: String? = null,
|
||||
heroes: ImmutableList<MatrixUser> = persistentListOf(),
|
||||
) = SelectRoomInfo(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
canonicalAlias = canonicalAlias,
|
||||
avatarUrl = avatarUrl,
|
||||
heroes = heroes,
|
||||
)
|
||||
@@ -34,15 +34,15 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
fun SelectedRoom(
|
||||
roomSummary: RoomSummary,
|
||||
onRemoveRoom: (RoomSummary) -> Unit,
|
||||
roomInfo: SelectRoomInfo,
|
||||
onRemoveRoom: (SelectRoomInfo) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
@@ -53,14 +53,12 @@ fun SelectedRoom(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CompositeAvatar(
|
||||
avatarData = roomSummary.getAvatarData(size = AvatarSize.SelectedRoom),
|
||||
heroes = roomSummary.heroes.map { user ->
|
||||
user.getAvatarData(size = AvatarSize.SelectedRoom)
|
||||
}.toImmutableList()
|
||||
avatarData = roomInfo.getAvatarData(AvatarSize.SelectedRoom),
|
||||
heroes = roomInfo.heroes.map { it.getAvatarData(AvatarSize.SelectedRoom) }.toImmutableList(),
|
||||
)
|
||||
Text(
|
||||
// If name is null, we do not have space to render "No room name", so just use `#` here.
|
||||
text = roomSummary.name ?: "#",
|
||||
text = roomInfo.name ?: "#",
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
@@ -69,14 +67,14 @@ fun SelectedRoom(
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(20.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onRemoveRoom(roomSummary) }
|
||||
),
|
||||
.clip(CircleShape)
|
||||
.size(20.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onRemoveRoom(roomInfo) }
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Close(),
|
||||
@@ -91,10 +89,10 @@ fun SelectedRoom(
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun SelectedRoomPreview(
|
||||
@PreviewParameter(RoomSummaryDetailsProvider::class) roomSummary: RoomSummary
|
||||
@PreviewParameter(SelectRoomInfoProvider::class) roomInfo: SelectRoomInfo
|
||||
) = ElementPreview {
|
||||
SelectedRoom(
|
||||
roomSummary = roomSummary,
|
||||
roomInfo = roomInfo,
|
||||
onRemoveRoom = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ package io.element.android.libraries.matrix.ui.model
|
||||
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
|
||||
fun RoomSummary.getAvatarData(size: AvatarSize) = AvatarData(
|
||||
id = roomId.value,
|
||||
fun MatrixRoomInfo.getAvatarData(size: AvatarSize) = AvatarData(
|
||||
id = id.value,
|
||||
name = name,
|
||||
url = avatarUrl,
|
||||
size = size,
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.ui.model
|
||||
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class SelectRoomInfo(
|
||||
val roomId: RoomId,
|
||||
val name: String?,
|
||||
val canonicalAlias: RoomAlias?,
|
||||
val avatarUrl: String?,
|
||||
val heroes: ImmutableList<MatrixUser>,
|
||||
) {
|
||||
fun getAvatarData(size: AvatarSize) = AvatarData(
|
||||
id = roomId.value,
|
||||
name = name,
|
||||
url = avatarUrl,
|
||||
size = size,
|
||||
)
|
||||
}
|
||||
|
||||
fun RoomSummary.toSelectRoomInfo() = SelectRoomInfo(
|
||||
roomId = roomId,
|
||||
name = info.name,
|
||||
avatarUrl = info.avatarUrl,
|
||||
heroes = info.heroes,
|
||||
canonicalAlias = info.canonicalAlias,
|
||||
)
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
package io.element.android.libraries.roomselect.impl
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
|
||||
sealed interface RoomSelectEvents {
|
||||
data class SetSelectedRoom(val room: RoomSummary) : RoomSelectEvents
|
||||
data class SetSelectedRoom(val room: SelectRoomInfo) : RoomSelectEvents
|
||||
|
||||
// TODO remove to restore multi-selection
|
||||
data object RemoveSelectedRoom : RoomSelectEvents
|
||||
|
||||
@@ -20,7 +20,7 @@ import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -36,7 +36,7 @@ class RoomSelectPresenter @AssistedInject constructor(
|
||||
|
||||
@Composable
|
||||
override fun present(): RoomSelectState {
|
||||
var selectedRooms by remember { mutableStateOf(persistentListOf<RoomSummary>()) }
|
||||
var selectedRooms by remember { mutableStateOf(persistentListOf<SelectRoomInfo>()) }
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
var isSearchActive by remember { mutableStateOf(false) }
|
||||
|
||||
@@ -48,7 +48,7 @@ class RoomSelectPresenter @AssistedInject constructor(
|
||||
dataSource.setSearchQuery(searchQuery)
|
||||
}
|
||||
|
||||
val roomSummaryDetailsList by dataSource.roomSummaries.collectAsState(initial = persistentListOf())
|
||||
val roomSummaryDetailsList by dataSource.roomInfoList.collectAsState(initial = persistentListOf())
|
||||
|
||||
val searchResults by remember {
|
||||
derivedStateOf {
|
||||
|
||||
@@ -12,8 +12,9 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
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.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import io.element.android.libraries.matrix.ui.model.toSelectRoomInfo
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
@@ -38,11 +39,12 @@ class RoomSelectSearchDataSource @Inject constructor(
|
||||
source = RoomList.Source.All,
|
||||
)
|
||||
|
||||
val roomSummaries: Flow<PersistentList<RoomSummary>> = roomList.filteredSummaries
|
||||
val roomInfoList: Flow<PersistentList<SelectRoomInfo>> = roomList.filteredSummaries
|
||||
.map { roomSummaries ->
|
||||
roomSummaries
|
||||
.filter { it.currentUserMembership == CurrentUserMembership.JOINED }
|
||||
.filter { it.info.currentUserMembership == CurrentUserMembership.JOINED }
|
||||
.distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received
|
||||
.map { roomSummary -> roomSummary.toSelectRoomInfo() }
|
||||
.toPersistentList()
|
||||
}
|
||||
.flowOn(coroutineDispatchers.computation)
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
package io.element.android.libraries.roomselect.impl
|
||||
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class RoomSelectState(
|
||||
val mode: RoomSelectMode,
|
||||
val resultState: SearchBarResultState<ImmutableList<RoomSummary>>,
|
||||
val resultState: SearchBarResultState<ImmutableList<SelectRoomInfo>>,
|
||||
val query: String,
|
||||
val isSearchActive: Boolean,
|
||||
val selectedRooms: ImmutableList<RoomSummary>,
|
||||
val selectedRooms: ImmutableList<SelectRoomInfo>,
|
||||
val eventSink: (RoomSelectEvents) -> Unit
|
||||
)
|
||||
|
||||
@@ -11,8 +11,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.components.aRoomSummaryDetails
|
||||
import io.element.android.libraries.matrix.ui.components.aSelectRoomInfo
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -32,7 +32,7 @@ open class RoomSelectStateProvider : PreviewParameterProvider<RoomSelectState> {
|
||||
resultState = SearchBarResultState.Results(aRoomSelectRoomList()),
|
||||
query = "Test",
|
||||
isSearchActive = true,
|
||||
selectedRooms = persistentListOf(aRoomSummaryDetails(roomId = RoomId("!room2:domain")))
|
||||
selectedRooms = aRoomSelectRoomList().subList(0, 1),
|
||||
),
|
||||
aRoomSelectState(
|
||||
mode = RoomSelectMode.Share,
|
||||
@@ -43,10 +43,10 @@ open class RoomSelectStateProvider : PreviewParameterProvider<RoomSelectState> {
|
||||
|
||||
private fun aRoomSelectState(
|
||||
mode: RoomSelectMode = RoomSelectMode.Forward,
|
||||
resultState: SearchBarResultState<ImmutableList<RoomSummary>> = SearchBarResultState.Initial(),
|
||||
resultState: SearchBarResultState<ImmutableList<SelectRoomInfo>> = SearchBarResultState.Initial(),
|
||||
query: String = "",
|
||||
isSearchActive: Boolean = false,
|
||||
selectedRooms: ImmutableList<RoomSummary> = persistentListOf(),
|
||||
selectedRooms: ImmutableList<SelectRoomInfo> = persistentListOf(),
|
||||
) = RoomSelectState(
|
||||
mode = mode,
|
||||
resultState = resultState,
|
||||
@@ -57,14 +57,16 @@ private fun aRoomSelectState(
|
||||
)
|
||||
|
||||
private fun aRoomSelectRoomList() = persistentListOf(
|
||||
aRoomSummaryDetails(),
|
||||
aRoomSummaryDetails(
|
||||
aSelectRoomInfo(
|
||||
roomId = RoomId("!room1:domain"),
|
||||
name = "Room with name",
|
||||
),
|
||||
aSelectRoomInfo(
|
||||
roomId = RoomId("!room2:domain"),
|
||||
name = "Room with alias",
|
||||
canonicalAlias = RoomAlias("#alias:example.org"),
|
||||
),
|
||||
aRoomSummaryDetails(
|
||||
aSelectRoomInfo(
|
||||
roomId = RoomId("!room3:domain"),
|
||||
name = null,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -47,8 +47,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.ui.components.SelectedRoom
|
||||
import io.element.android.libraries.matrix.ui.model.SelectRoomInfo
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
@@ -65,13 +65,13 @@ fun RoomSelectView(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun onRoomRemoved(roomSummary: RoomSummary) {
|
||||
fun onRoomRemoved(roomInfo: SelectRoomInfo) {
|
||||
// TODO toggle selection when multi-selection is enabled
|
||||
state.eventSink(RoomSelectEvents.RemoveSelectedRoom)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList<RoomSummary>) {
|
||||
fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList<SelectRoomInfo>) {
|
||||
if (isForwarding) return
|
||||
SelectedRooms(
|
||||
selectedRooms = selectedRooms,
|
||||
@@ -185,8 +185,8 @@ fun RoomSelectView(
|
||||
|
||||
@Composable
|
||||
private fun SelectedRooms(
|
||||
selectedRooms: ImmutableList<RoomSummary>,
|
||||
onRemoveRoom: (RoomSummary) -> Unit,
|
||||
selectedRooms: ImmutableList<SelectRoomInfo>,
|
||||
onRemoveRoom: (SelectRoomInfo) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyRow(
|
||||
@@ -194,29 +194,29 @@ private fun SelectedRooms(
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
items(selectedRooms, key = { it.roomId.value }) { roomSummary ->
|
||||
SelectedRoom(roomSummary = roomSummary, onRemoveRoom = onRemoveRoom)
|
||||
items(selectedRooms, key = { it.roomId.value }) { selectRoomInfo ->
|
||||
SelectedRoom(roomInfo = selectRoomInfo, onRemoveRoom = onRemoveRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoomSummaryView(
|
||||
summary: RoomSummary,
|
||||
roomInfo: SelectRoomInfo,
|
||||
isSelected: Boolean,
|
||||
onSelection: (RoomSummary) -> Unit,
|
||||
onSelection: (SelectRoomInfo) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable { onSelection(summary) }
|
||||
.clickable { onSelection(roomInfo) }
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 4.dp)
|
||||
.heightIn(56.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CompositeAvatar(
|
||||
avatarData = summary.getAvatarData(size = AvatarSize.RoomSelectRoomListItem),
|
||||
heroes = summary.heroes.map { user ->
|
||||
avatarData = roomInfo.getAvatarData(size = AvatarSize.RoomSelectRoomListItem),
|
||||
heroes = roomInfo.heroes.map { user ->
|
||||
user.getAvatarData(size = AvatarSize.RoomSelectRoomListItem)
|
||||
}.toPersistentList()
|
||||
)
|
||||
@@ -228,14 +228,14 @@ private fun RoomSummaryView(
|
||||
// Name
|
||||
Text(
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
text = summary.name ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||
fontStyle = FontStyle.Italic.takeIf { summary.name == null },
|
||||
text = roomInfo.name ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||
fontStyle = FontStyle.Italic.takeIf { roomInfo.name == null },
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
// Alias
|
||||
summary.canonicalAlias?.let { alias ->
|
||||
roomInfo.canonicalAlias?.let { alias ->
|
||||
Text(
|
||||
text = alias.value,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
@@ -245,7 +245,7 @@ private fun RoomSummaryView(
|
||||
)
|
||||
}
|
||||
}
|
||||
RadioButton(selected = isSelected, onClick = { onSelection(summary) })
|
||||
RadioButton(selected = isSelected, onClick = { onSelection(roomInfo) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import io.element.android.libraries.matrix.ui.model.toSelectRoomInfo
|
||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
@@ -58,8 +59,9 @@ class RoomSelectPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - update query`() = runTest {
|
||||
val roomSummary = aRoomSummary()
|
||||
val roomListService = FakeRoomListService().apply {
|
||||
postAllRooms(listOf(aRoomSummary()))
|
||||
postAllRooms(listOf(roomSummary))
|
||||
}
|
||||
val presenter = createRoomSelectPresenter(
|
||||
roomListService = roomListService
|
||||
@@ -68,19 +70,10 @@ class RoomSelectPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val expectedRoomSummary = aRoomSummary()
|
||||
val expectedRoomInfo = roomSummary.toSelectRoomInfo()
|
||||
// Do not compare the lambda because they will be different. So copy the lambda from expectedRoomSummary to result
|
||||
val result = (awaitItem().resultState as SearchBarResultState.Results).results.map { roomSummary ->
|
||||
roomSummary.copy(
|
||||
lastMessage = roomSummary.lastMessage!!.copy(
|
||||
event = roomSummary.lastMessage!!.event.copy(
|
||||
debugInfoProvider = expectedRoomSummary.lastMessage!!.event.debugInfoProvider,
|
||||
messageShieldProvider = expectedRoomSummary.lastMessage!!.event.messageShieldProvider,
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
assertThat(result).isEqualTo(listOf(expectedRoomSummary))
|
||||
val result = (awaitItem().resultState as SearchBarResultState.Results).results
|
||||
assertThat(result).isEqualTo(listOf(expectedRoomInfo))
|
||||
initialState.eventSink(RoomSelectEvents.ToggleSearchActive)
|
||||
skipItems(1)
|
||||
initialState.eventSink(RoomSelectEvents.UpdateQuery("string not contained"))
|
||||
@@ -99,8 +92,9 @@ class RoomSelectPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - select and remove a room`() = runTest {
|
||||
val roomSummary = aRoomSummary()
|
||||
val roomListService = FakeRoomListService().apply {
|
||||
postAllRooms(listOf(aRoomSummary()))
|
||||
postAllRooms(listOf(roomSummary))
|
||||
}
|
||||
val presenter = createRoomSelectPresenter(
|
||||
roomListService = roomListService,
|
||||
@@ -109,9 +103,9 @@ class RoomSelectPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
val summary = aRoomSummary()
|
||||
initialState.eventSink(RoomSelectEvents.SetSelectedRoom(summary))
|
||||
assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(summary))
|
||||
val roomInfo = roomSummary.toSelectRoomInfo()
|
||||
initialState.eventSink(RoomSelectEvents.SetSelectedRoom(roomInfo))
|
||||
assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(roomInfo))
|
||||
initialState.eventSink(RoomSelectEvents.RemoveSelectedRoom)
|
||||
assertThat(awaitItem().selectedRooms).isEmpty()
|
||||
cancel()
|
||||
|
||||
@@ -8,13 +8,27 @@
|
||||
package io.element.android.libraries.textcomposer.mentions
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
|
||||
@Immutable
|
||||
sealed interface ResolvedSuggestion {
|
||||
data object AtRoom : ResolvedSuggestion
|
||||
data class Member(val roomMember: RoomMember) : ResolvedSuggestion
|
||||
data class Alias(val roomAlias: RoomAlias, val roomSummary: RoomSummary) : ResolvedSuggestion
|
||||
data class Alias(
|
||||
val roomAlias: RoomAlias,
|
||||
val roomId: RoomId,
|
||||
val roomName: String?,
|
||||
val roomAvatarUrl: String?,
|
||||
) : ResolvedSuggestion {
|
||||
fun getAvatarData(size: AvatarSize) = AvatarData(
|
||||
id = roomId.value,
|
||||
name = roomName,
|
||||
url = roomAvatarUrl,
|
||||
size = size,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
|
||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpan
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion
|
||||
@@ -34,7 +34,7 @@ class MarkdownTextEditorStateTest {
|
||||
@Test
|
||||
fun `insertMention - room alias - getMentions return empty list`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true)
|
||||
val suggestion = ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary(canonicalAlias = A_ROOM_ALIAS))
|
||||
val suggestion = aRoomAliasSuggestion()
|
||||
val permalinkBuilder = FakePermalinkBuilder()
|
||||
val mentionSpanProvider = aMentionSpanProvider()
|
||||
state.insertSuggestion(suggestion, mentionSpanProvider, permalinkBuilder)
|
||||
@@ -46,7 +46,7 @@ class MarkdownTextEditorStateTest {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply {
|
||||
currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Room, text = "")
|
||||
}
|
||||
val suggestion = ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary(canonicalAlias = A_ROOM_ALIAS))
|
||||
val suggestion = aRoomAliasSuggestion()
|
||||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.RoomLink(A_ROOM_ALIAS.toRoomIdOrAlias()) })
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForRoomAliasLambda = { Result.failure(IllegalStateException("Failed")) })
|
||||
val mentionSpanProvider = aMentionSpanProvider(permalinkParser = permalinkParser)
|
||||
@@ -58,7 +58,7 @@ class MarkdownTextEditorStateTest {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply {
|
||||
currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Room, text = "")
|
||||
}
|
||||
val suggestion = ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary(canonicalAlias = A_ROOM_ALIAS))
|
||||
val suggestion = aRoomAliasSuggestion()
|
||||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.RoomLink(A_ROOM_ALIAS.toRoomIdOrAlias()) })
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForRoomAliasLambda = { Result.success("https://matrix.to/#/${A_ROOM_ALIAS.value}") })
|
||||
val mentionSpanProvider = aMentionSpanProvider(permalinkParser = permalinkParser)
|
||||
@@ -202,4 +202,13 @@ class MarkdownTextEditorStateTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun aRoomAliasSuggestion(): ResolvedSuggestion.Alias {
|
||||
return ResolvedSuggestion.Alias(
|
||||
roomAlias = A_ROOM_ALIAS,
|
||||
roomId = A_ROOM_ID,
|
||||
roomName = null,
|
||||
roomAvatarUrl = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user