diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 8529732b06..d0daa9bcb9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -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 diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/RoomAliasSuggestionsDataSource.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/RoomAliasSuggestionsDataSource.kt similarity index 75% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/RoomAliasSuggestionsDataSource.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/RoomAliasSuggestionsDataSource.kt index 45a1da8e6a..c6eec0cbab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/RoomAliasSuggestionsDataSource.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/RoomAliasSuggestionsDataSource.kt @@ -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> { 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, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt index 7a9c542a1b..a7692fcd31 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt @@ -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 = {} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessor.kt index b1a9f977d1..aab533903b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessor.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessor.kt @@ -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 -> { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/DefaultRoomAliasSuggestionsDataSourceTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/DefaultRoomAliasSuggestionsDataSourceTest.kt index f05cc82aa6..9a443fb174 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/DefaultRoomAliasSuggestionsDataSourceTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/DefaultRoomAliasSuggestionsDataSourceTest.kt @@ -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 ) ) ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/FakeRoomAliasSuggestionsDataSource.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/FakeRoomAliasSuggestionsDataSource.kt index c916d2bc9c..3a9a72799a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/FakeRoomAliasSuggestionsDataSource.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/FakeRoomAliasSuggestionsDataSource.kt @@ -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 diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessorTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessorTest.kt index 1cb7596e89..48000e79b4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessorTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsProcessorTest.kt @@ -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 }, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index dffd1b14a4..50c31cb764 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -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 { @AssistedFactory interface Factory { @@ -57,8 +57,8 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor( val changeNotificationSettingAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - val roomsWithUserDefinedMode: MutableState> = remember { - mutableStateOf(listOf()) + val roomsWithUserDefinedMode: MutableState> = remember { + mutableStateOf(emptyList()) } val localCoroutineScope = rememberCoroutineScope() @@ -106,31 +106,37 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor( .launchIn(this) } - private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState>) { + private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState>) { roomListService.allRooms .summaries - .onEach { - updateRoomsWithUserDefinedMode(it, roomsWithUserDefinedMode) + .onEach { roomSummaries -> + updateRoomsWithUserDefinedMode(roomSummaries, roomsWithUserDefinedMode) } .launchIn(this) } - private fun CoroutineScope.updateRoomsWithUserDefinedMode( + private suspend fun updateRoomsWithUserDefinedMode( summaries: List, - roomsWithUserDefinedMode: MutableState> - ) = launch { - val roomWithUserDefinedRules: Set = notificationSettingsService.getRoomsWithUserDefinedRules().getOrThrow().toSet() - - val sortedSummaries = summaries - .filterIsInstance() - .filter { - val room = matrixClient.getRoom(it.roomId) ?: return@filter false - roomWithUserDefinedRules.contains(it.roomId.value) && isOneToOne == room.isOneToOne + roomsWithUserDefinedMode: MutableState> + ) { + val roomWithUserDefinedRules: Set = 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>) = launch { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt index fc087f4ac7..8e61d5c62c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt @@ -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, + val roomsWithUserDefinedMode: ImmutableList, val changeNotificationSettingAction: AsyncAction, val displayMentionsOnlyDisclaimer: Boolean, val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt index d620ecc01a..b941d738ea 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateProvider.kt @@ -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 { @@ -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, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt index 7ed28dd904..1e6932e1e5 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt @@ -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 = { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditNotificationSettingRoomInfo.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditNotificationSettingRoomInfo.kt new file mode 100644 index 0000000000..5510cb8c55 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditNotificationSettingRoomInfo.kt @@ -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, + val avatarData: AvatarData, + val notificationMode: RoomNotificationMode? +) diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt index d854042717..5916097dc0 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt @@ -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 ) } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt index 1044f58130..004ce9174b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt @@ -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(), ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt index 3494d46348..eaa905eb53 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt @@ -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?, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt index 9ee23d8f3f..894bc46377 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt @@ -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" }, diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt index 91242df095..6dfd1b0a0a 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt @@ -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 diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt index 18c8e2b011..7e91fa59de 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryTest.kt @@ -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, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt index b83c2c98d6..6105a59c38 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt @@ -33,6 +33,13 @@ data class MatrixRoomInfo( val canonicalAlias: RoomAlias?, val alternativeAliases: ImmutableList, 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, + 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, val pinnedEventIds: ImmutableList, val creator: UserId?, -) +) { + val aliases: List + get() = listOfNotNull(canonicalAlias) + alternativeAliases +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt index 0fd1ab4a34..9ba07f50d8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt @@ -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, - 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, ) { + val roomId = info.id val lastMessageTimestamp = lastMessage?.originServerTs - val aliases: List - get() = listOfNotNull(canonicalAlias) + alternativeAliases + val isOneToOne get() = info.activeMembersCount == 2L } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt index c7ed6c1205..c6d60bdca5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt @@ -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(), ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index 77e2de1f94..aa0fcfd285 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -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. diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt index b49c4138e8..88458d56bb 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt @@ -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.filter(filter: RoomListFilter): List { } } -private fun RoomSummary.isInvited() = currentUserMembership == CurrentUserMembership.INVITED +private fun RoomSummary.isInvited() = info.currentUserMembership == CurrentUserMembership.INVITED diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt deleted file mode 100644 index 92dbed82b6..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt +++ /dev/null @@ -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(), - ) - } -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryFactory.kt new file mode 100644 index 0000000000..8a92a6f881 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryFactory.kt @@ -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, + ) + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt index a4addd52f6..a0f5a1f7e2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt @@ -23,7 +23,7 @@ class RoomSummaryListProcessor( private val roomSummaries: MutableSharedFlow>, private val roomListService: RoomListServiceInterface, private val coroutineContext: CoroutineContext, - private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), + private val roomSummaryDetailsFactory: RoomSummaryFactory = RoomSummaryFactory(), ) { private val roomSummariesByIdentifier = HashMap() private val mutex = Mutex() diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt index c9e80f493d..d2fa714880 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt @@ -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().toImmutableList(), pinnedEventIds = emptyList().toPersistentList(), creator = null, + isMarkedUnread = true, + numUnreadMessages = 12L, + numUnreadNotifications = 13L, + numUnreadMentions = 14L, ) ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt index 5eefbefc47..8ab9d2aa73 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt @@ -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 -> 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 -> 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 -> Result.success(roomSummary) } val roomResult = FakeMatrixRoom() diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt index 7c4e76e491..abe7e17bba 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt @@ -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 diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt index 082ab2523d..be63a81bfe 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt @@ -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(), ) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 1d78e87369..d8b246de7e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -23,7 +23,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 @@ -31,7 +30,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 @@ -39,21 +37,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 @@ -508,62 +500,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 = 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 = persistentMapOf(), - activeRoomCallParticipants: List = emptyList(), - heroes: List = emptyList(), - pinnedEventIds: List = 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, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt new file mode 100644 index 0000000000..5aae1a3258 --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt @@ -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 = 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 = persistentMapOf(), + activeRoomCallParticipants: List = emptyList(), + heroes: List = emptyList(), + pinnedEventIds: List = 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, +) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index c0ac4b7bab..39e13cb619 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -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 = 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 = persistentMapOf(), + activeRoomCallParticipants: List = emptyList(), heroes: List = emptyList(), + pinnedEventIds: List = 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( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/RoomSummaryDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/RoomSummaryDetailsProvider.kt deleted file mode 100644 index 6bc8af0591..0000000000 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/RoomSummaryDetailsProvider.kt +++ /dev/null @@ -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 { - override val values: Sequence - get() = sequenceOf( - aRoomSummaryDetails(), - aRoomSummaryDetails(name = null), - ) -} - -fun aRoomSummaryDetails( - roomId: RoomId = RoomId("!room:domain"), - name: String? = "roomName", - canonicalAlias: RoomAlias? = null, - alternativeAliases: List = 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 = 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, -) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoProvider.kt new file mode 100644 index 0000000000..d61dc09b03 --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectRoomInfoProvider.kt @@ -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 { + override val values: Sequence + 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 = persistentListOf(), +) = SelectRoomInfo( + roomId = roomId, + name = name, + canonicalAlias = canonicalAlias, + avatarUrl = avatarUrl, + heroes = heroes, +) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt index eecefd2a45..fb9fab7c97 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt @@ -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 = {}, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomSummaryExtension.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt similarity index 73% rename from libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomSummaryExtension.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt index a6954af232..e876a1da05 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomSummaryExtension.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt @@ -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, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/SelectRoomInfo.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/SelectRoomInfo.kt new file mode 100644 index 0000000000..33376ca766 --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/SelectRoomInfo.kt @@ -0,0 +1,30 @@ +/* + * 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.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, +) { + fun getAvatarData(size: AvatarSize) = AvatarData( + id = roomId.value, + name = name, + url = avatarUrl, + size = size, + ) +} diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt index 35954d4851..8661e2aa75 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectEvents.kt @@ -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 diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt index 74911c3f03..e974fd362b 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -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()) } + var selectedRooms by remember { mutableStateOf(persistentListOf()) } 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 { diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt index 4500456d54..99942953dc 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectSearchDataSource.kt @@ -12,8 +12,8 @@ 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 kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.coroutineScope @@ -38,11 +38,20 @@ class RoomSelectSearchDataSource @Inject constructor( source = RoomList.Source.All, ) - val roomSummaries: Flow> = roomList.filteredSummaries + val roomInfoList: Flow> = 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 -> + SelectRoomInfo( + roomId = roomSummary.roomId, + name = roomSummary.info.name, + avatarUrl = roomSummary.info.avatarUrl, + heroes = roomSummary.info.heroes, + canonicalAlias = roomSummary.info.canonicalAlias, + ) + } .toPersistentList() } .flowOn(coroutineDispatchers.computation) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt index 5037a1ca19..9c2d4d9893 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectState.kt @@ -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>, + val resultState: SearchBarResultState>, val query: String, val isSearchActive: Boolean, - val selectedRooms: ImmutableList, + val selectedRooms: ImmutableList, val eventSink: (RoomSelectEvents) -> Unit ) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt index e9a1bfc158..70d69ac814 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectStateProvider.kt @@ -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 { 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 { private fun aRoomSelectState( mode: RoomSelectMode = RoomSelectMode.Forward, - resultState: SearchBarResultState> = SearchBarResultState.Initial(), + resultState: SearchBarResultState> = SearchBarResultState.Initial(), query: String = "", isSearchActive: Boolean = false, - selectedRooms: ImmutableList = persistentListOf(), + selectedRooms: ImmutableList = 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, ), ) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 643cb00b85..3d1d22c505 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -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) { + fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList) { if (isForwarding) return SelectedRooms( selectedRooms = selectedRooms, @@ -185,8 +185,8 @@ fun RoomSelectView( @Composable private fun SelectedRooms( - selectedRooms: ImmutableList, - onRemoveRoom: (RoomSummary) -> Unit, + selectedRooms: ImmutableList, + 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) }) } } diff --git a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt index ff94e32a5f..40cf8d6330 100644 --- a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt @@ -14,8 +14,10 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.designsystem.theme.components.SearchBarResultState 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.test.room.aRoomSummary import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService +import io.element.android.libraries.matrix.ui.components.aSelectRoomInfo import io.element.android.libraries.roomselect.api.RoomSelectMode import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.testCoroutineDispatchers @@ -58,8 +60,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 +71,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 +93,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 +104,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() @@ -128,4 +123,12 @@ class RoomSelectPresenterTest { coroutineDispatchers = testCoroutineDispatchers(), ), ) + + private fun RoomSummary.toSelectRoomInfo() = aSelectRoomInfo( + roomId = roomId, + name = info.name, + avatarUrl = info.avatarUrl, + heroes = info.heroes, + canonicalAlias = info.canonicalAlias, + ) } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/ResolvedSuggestion.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/ResolvedSuggestion.kt index a0b44fb70c..6763d58ec0 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/ResolvedSuggestion.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/ResolvedSuggestion.kt @@ -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, + ) + } } diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt index 29c0813a22..c0a9612eab 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/model/MarkdownTextEditorStateTest.kt @@ -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 + ) + } }