Update dependency org.matrix.rustcomponents:sdk-android to v0.2.31 (#3145)
* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.31 * Use new Rust client side sorting API * Make `RoomListEntriesUpdate.describe()` an extension function * Remove `RoomListSummary.Filled` and `RoomListSummary.Empty` * Fix icon sizes to pass the lint checks * Update screenshots --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín <jorgem@element.io> Co-authored-by: ElementBot <benoitm+elementbot@element.io>
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 4 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails
|
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider
|
import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
|
@ -64,7 +64,7 @@ class ForwardMessagesPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val summary = aRoomSummaryDetails()
|
val summary = aRoomSummary()
|
||||||
presenter.onRoomSelected(listOf(summary.roomId))
|
presenter.onRoomSelected(listOf(summary.roomId))
|
||||||
val forwardingState = awaitItem()
|
val forwardingState = awaitItem()
|
||||||
assertThat(forwardingState.forwardAction.isLoading()).isTrue()
|
assertThat(forwardingState.forwardAction.isLoading()).isTrue()
|
||||||
|
|
@ -88,7 +88,7 @@ class ForwardMessagesPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val summary = aRoomSummaryDetails()
|
val summary = aRoomSummary()
|
||||||
presenter.onRoomSelected(listOf(summary.roomId))
|
presenter.onRoomSelected(listOf(summary.roomId))
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val failedForwardState = awaitItem()
|
val failedForwardState = awaitItem()
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val changeNotificationSettingAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
val changeNotificationSettingAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||||
|
|
||||||
val roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>> = remember {
|
val roomsWithUserDefinedMode: MutableState<List<RoomSummary>> = remember {
|
||||||
mutableStateOf(listOf())
|
mutableStateOf(listOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>) {
|
private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState<List<RoomSummary>>) {
|
||||||
roomListService.allRooms
|
roomListService.allRooms
|
||||||
.summaries
|
.summaries
|
||||||
.onEach {
|
.onEach {
|
||||||
|
|
@ -126,18 +126,18 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private fun CoroutineScope.updateRoomsWithUserDefinedMode(
|
private fun CoroutineScope.updateRoomsWithUserDefinedMode(
|
||||||
summaries: List<RoomSummary>,
|
summaries: List<RoomSummary>,
|
||||||
roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>
|
roomsWithUserDefinedMode: MutableState<List<RoomSummary>>
|
||||||
) = launch {
|
) = launch {
|
||||||
val roomWithUserDefinedRules: Set<String> = notificationSettingsService.getRoomsWithUserDefinedRules().getOrThrow().toSet()
|
val roomWithUserDefinedRules: Set<String> = notificationSettingsService.getRoomsWithUserDefinedRules().getOrThrow().toSet()
|
||||||
|
|
||||||
val sortedSummaries = summaries
|
val sortedSummaries = summaries
|
||||||
.filterIsInstance<RoomSummary.Filled>()
|
.filterIsInstance<RoomSummary>()
|
||||||
.filter {
|
.filter {
|
||||||
val room = matrixClient.getRoom(it.details.roomId) ?: return@filter false
|
val room = matrixClient.getRoom(it.roomId) ?: return@filter false
|
||||||
roomWithUserDefinedRules.contains(it.identifier()) && isOneToOne == room.isOneToOne
|
roomWithUserDefinedRules.contains(it.roomId.value) && isOneToOne == room.isOneToOne
|
||||||
}
|
}
|
||||||
// locale sensitive sorting
|
// locale sensitive sorting
|
||||||
.sortedWith(compareBy(Collator.getInstance()) { it.details.name })
|
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||||
|
|
||||||
roomsWithUserDefinedMode.value = sortedSummaries
|
roomsWithUserDefinedMode.value = sortedSummaries
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
data class EditDefaultNotificationSettingState(
|
data class EditDefaultNotificationSettingState(
|
||||||
val isOneToOne: Boolean,
|
val isOneToOne: Boolean,
|
||||||
val mode: RoomNotificationMode?,
|
val mode: RoomNotificationMode?,
|
||||||
val roomsWithUserDefinedMode: ImmutableList<RoomSummary.Filled>,
|
val roomsWithUserDefinedMode: ImmutableList<RoomSummary>,
|
||||||
val changeNotificationSettingAction: AsyncAction<Unit>,
|
val changeNotificationSettingAction: AsyncAction<Unit>,
|
||||||
val displayMentionsOnlyDisclaimer: Boolean,
|
val displayMentionsOnlyDisclaimer: Boolean,
|
||||||
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
|
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
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.aRoomSummaryDetails
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
|
|
@ -53,13 +52,11 @@ private fun anEditDefaultNotificationSettingsState(
|
||||||
|
|
||||||
private fun aRoomSummary(
|
private fun aRoomSummary(
|
||||||
name: String?,
|
name: String?,
|
||||||
) = RoomSummary.Filled(
|
) = aRoomSummaryDetails(
|
||||||
aRoomSummaryDetails(
|
roomId = RoomId("!roomId:domain"),
|
||||||
roomId = RoomId("!roomId:domain"),
|
name = name,
|
||||||
name = name,
|
avatarUrl = null,
|
||||||
avatarUrl = null,
|
isDirect = false,
|
||||||
isDirect = false,
|
lastMessage = null,
|
||||||
lastMessage = null,
|
notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||||
notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ fun EditDefaultNotificationSettingView(
|
||||||
if (state.roomsWithUserDefinedMode.isNotEmpty()) {
|
if (state.roomsWithUserDefinedMode.isNotEmpty()) {
|
||||||
PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_edit_custom_settings_section_title)) {
|
PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_edit_custom_settings_section_title)) {
|
||||||
state.roomsWithUserDefinedMode.forEach { summary ->
|
state.roomsWithUserDefinedMode.forEach { summary ->
|
||||||
val subtitle = when (summary.details.userDefinedNotificationMode) {
|
val subtitle = when (summary.userDefinedNotificationMode) {
|
||||||
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages)
|
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages)
|
||||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> {
|
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> {
|
||||||
stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords)
|
stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords)
|
||||||
|
|
@ -99,7 +99,7 @@ fun EditDefaultNotificationSettingView(
|
||||||
}
|
}
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
val roomName = summary.details.name
|
val roomName = summary.name
|
||||||
Text(
|
Text(
|
||||||
text = roomName ?: stringResource(id = CommonStrings.common_no_room_name),
|
text = roomName ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||||
fontStyle = FontStyle.Italic.takeIf { roomName == null }
|
fontStyle = FontStyle.Italic.takeIf { roomName == null }
|
||||||
|
|
@ -110,14 +110,14 @@ fun EditDefaultNotificationSettingView(
|
||||||
},
|
},
|
||||||
leadingContent = ListItemContent.Custom {
|
leadingContent = ListItemContent.Custom {
|
||||||
CompositeAvatar(
|
CompositeAvatar(
|
||||||
avatarData = summary.details.getAvatarData(size = AvatarSize.CustomRoomNotificationSetting),
|
avatarData = summary.getAvatarData(size = AvatarSize.CustomRoomNotificationSetting),
|
||||||
heroes = summary.details.heroes.map { user ->
|
heroes = summary.heroes.map { user ->
|
||||||
user.getAvatarData(size = AvatarSize.CustomRoomNotificationSetting)
|
user.getAvatarData(size = AvatarSize.CustomRoomNotificationSetting)
|
||||||
}.toPersistentList()
|
}.toPersistentList()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
openRoomNotificationSettings(summary.details.roomId)
|
openRoomNotificationSettings(summary.roomId)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,12 @@ 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.EditDefaultNotificationSettingPresenter
|
||||||
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents
|
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.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
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.A_THROWABLE
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
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.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails
|
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.tests.testutils.awaitLastSequentialItem
|
import io.element.android.tests.testutils.awaitLastSequentialItem
|
||||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||||
|
|
@ -72,11 +71,11 @@ class EditDefaultNotificationSettingsPresenterTest {
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
roomListService.postAllRooms(listOf(RoomSummary.Filled(aRoomSummaryDetails(notificationMode = RoomNotificationMode.ALL_MESSAGES))))
|
roomListService.postAllRooms(listOf(aRoomSummary(notificationMode = RoomNotificationMode.ALL_MESSAGES)))
|
||||||
val loadedState = consumeItemsUntilPredicate { state ->
|
val loadedState = consumeItemsUntilPredicate { state ->
|
||||||
state.roomsWithUserDefinedMode.any { it.details.userDefinedNotificationMode == RoomNotificationMode.ALL_MESSAGES }
|
state.roomsWithUserDefinedMode.any { it.userDefinedNotificationMode == RoomNotificationMode.ALL_MESSAGES }
|
||||||
}.last()
|
}.last()
|
||||||
assertThat(loadedState.roomsWithUserDefinedMode.any { it.details.userDefinedNotificationMode == RoomNotificationMode.ALL_MESSAGES }).isTrue()
|
assertThat(loadedState.roomsWithUserDefinedMode.any { it.userDefinedNotificationMode == RoomNotificationMode.ALL_MESSAGES }).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
sealed interface RoomListEvents {
|
sealed interface RoomListEvents {
|
||||||
data class UpdateVisibleRange(val range: IntRange) : RoomListEvents
|
|
||||||
data object DismissRequestVerificationPrompt : RoomListEvents
|
data object DismissRequestVerificationPrompt : RoomListEvents
|
||||||
data object DismissRecoveryKeyPrompt : RoomListEvents
|
data object DismissRecoveryKeyPrompt : RoomListEvents
|
||||||
data object ToggleSearchResults : RoomListEvents
|
data object ToggleSearchResults : RoomListEvents
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,6 @@ import kotlinx.coroutines.flow.takeWhile
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
private const val EXTENDED_RANGE_SIZE = 40
|
|
||||||
|
|
||||||
class RoomListPresenter @Inject constructor(
|
class RoomListPresenter @Inject constructor(
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
private val networkMonitor: NetworkMonitor,
|
private val networkMonitor: NetworkMonitor,
|
||||||
|
|
@ -124,7 +122,6 @@ class RoomListPresenter @Inject constructor(
|
||||||
|
|
||||||
fun handleEvents(event: RoomListEvents) {
|
fun handleEvents(event: RoomListEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is RoomListEvents.UpdateVisibleRange -> updateVisibleRange(event.range)
|
|
||||||
RoomListEvents.DismissRequestVerificationPrompt -> securityBannerDismissed = true
|
RoomListEvents.DismissRequestVerificationPrompt -> securityBannerDismissed = true
|
||||||
RoomListEvents.DismissRecoveryKeyPrompt -> securityBannerDismissed = true
|
RoomListEvents.DismissRecoveryKeyPrompt -> securityBannerDismissed = true
|
||||||
RoomListEvents.ToggleSearchResults -> searchState.eventSink(RoomListSearchEvents.ToggleSearchVisibility)
|
RoomListEvents.ToggleSearchResults -> searchState.eventSink(RoomListSearchEvents.ToggleSearchVisibility)
|
||||||
|
|
@ -286,16 +283,6 @@ class RoomListPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateVisibleRange(range: IntRange) {
|
|
||||||
if (range.isEmpty()) return
|
|
||||||
val midExtendedRangeSize = EXTENDED_RANGE_SIZE / 2
|
|
||||||
val extendedRangeStart = (range.first - midExtendedRangeSize).coerceAtLeast(0)
|
|
||||||
// Safe to give bigger size than room list
|
|
||||||
val extendedRangeEnd = range.last + midExtendedRangeSize
|
|
||||||
val extendedRange = IntRange(extendedRangeStart, extendedRangeEnd)
|
|
||||||
client.roomListService.updateAllRoomsVisibleRange(extendedRange)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,11 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.Velocity
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
|
|
@ -171,25 +165,9 @@ private fun RoomsViewList(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
val visibleRange by remember {
|
|
||||||
derivedStateOf {
|
|
||||||
val layoutInfo = lazyListState.layoutInfo
|
|
||||||
val firstItemIndex = layoutInfo.visibleItemsInfo.firstOrNull()?.index ?: 0
|
|
||||||
val size = layoutInfo.visibleItemsInfo.size
|
|
||||||
firstItemIndex until firstItemIndex + size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val nestedScrollConnection = remember {
|
|
||||||
object : NestedScrollConnection {
|
|
||||||
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
|
|
||||||
eventSink(RoomListEvents.UpdateVisibleRange(visibleRange))
|
|
||||||
return super.onPostFling(consumed, available)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
modifier = modifier.nestedScroll(nestedScrollConnection),
|
modifier = modifier,
|
||||||
// FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80
|
// FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80
|
||||||
contentPadding = PaddingValues(bottom = 80.dp)
|
contentPadding = PaddingValues(bottom = 80.dp)
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class RoomListDataSource @Inject constructor(
|
||||||
private val lock = Mutex()
|
private val lock = Mutex()
|
||||||
private val diffCache = MutableListDiffCache<RoomListRoomSummary>()
|
private val diffCache = MutableListDiffCache<RoomListRoomSummary>()
|
||||||
private val diffCacheUpdater = DiffCacheUpdater<RoomSummary, RoomListRoomSummary>(diffCache = diffCache, detectMoves = true) { old, new ->
|
private val diffCacheUpdater = DiffCacheUpdater<RoomSummary, RoomListRoomSummary>(diffCache = diffCache, detectMoves = true) { old, new ->
|
||||||
old?.identifier() == new?.identifier()
|
old?.roomId == new?.roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launchIn(coroutineScope: CoroutineScope) {
|
fun launchIn(coroutineScope: CoroutineScope) {
|
||||||
|
|
@ -96,12 +96,8 @@ class RoomListDataSource @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildAndCacheItem(roomSummaries: List<RoomSummary>, index: Int): RoomListRoomSummary? {
|
private fun buildAndCacheItem(roomSummaries: List<RoomSummary>, index: Int): RoomListRoomSummary? {
|
||||||
val roomListRoomSummary = when (val roomSummary = roomSummaries.getOrNull(index)) {
|
val roomListSummary = roomSummaries.getOrNull(index)?.let { roomListRoomSummaryFactory.create(it) }
|
||||||
is RoomSummary.Empty -> RoomListRoomSummaryFactory.createPlaceholder(roomSummary.identifier)
|
diffCache[index] = roomListSummary
|
||||||
is RoomSummary.Filled -> roomListRoomSummaryFactory.create(roomSummary)
|
return roomListSummary
|
||||||
null -> null
|
|
||||||
}
|
|
||||||
diffCache[index] = roomListRoomSummary
|
|
||||||
return roomListRoomSummary
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,12 @@ import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||||
import io.element.android.features.roomlist.impl.model.RoomSummaryDisplayType
|
import io.element.android.features.roomlist.impl.model.RoomSummaryDisplayType
|
||||||
import io.element.android.libraries.core.extensions.orEmpty
|
import io.element.android.libraries.core.extensions.orEmpty
|
||||||
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
|
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||||
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.CurrentUserMembership
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
|
||||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -37,37 +33,7 @@ class RoomListRoomSummaryFactory @Inject constructor(
|
||||||
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
|
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
|
||||||
private val roomLastMessageFormatter: RoomLastMessageFormatter,
|
private val roomLastMessageFormatter: RoomLastMessageFormatter,
|
||||||
) {
|
) {
|
||||||
companion object {
|
fun create(details: RoomSummary): RoomListRoomSummary {
|
||||||
fun createPlaceholder(id: String): RoomListRoomSummary {
|
|
||||||
return RoomListRoomSummary(
|
|
||||||
id = id,
|
|
||||||
roomId = RoomId(id),
|
|
||||||
displayType = RoomSummaryDisplayType.PLACEHOLDER,
|
|
||||||
name = "Short name",
|
|
||||||
timestamp = "hh:mm",
|
|
||||||
lastMessage = "Last message for placeholder",
|
|
||||||
avatarData = AvatarData(id, "S", size = AvatarSize.RoomListItem),
|
|
||||||
numberOfUnreadMessages = 0,
|
|
||||||
numberOfUnreadMentions = 0,
|
|
||||||
numberOfUnreadNotifications = 0,
|
|
||||||
isMarkedUnread = false,
|
|
||||||
userDefinedNotificationMode = null,
|
|
||||||
hasRoomCall = false,
|
|
||||||
isDirect = false,
|
|
||||||
isFavorite = false,
|
|
||||||
inviteSender = null,
|
|
||||||
isDm = false,
|
|
||||||
canonicalAlias = null,
|
|
||||||
heroes = persistentListOf(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun create(roomSummary: RoomSummary.Filled): RoomListRoomSummary {
|
|
||||||
return create(roomSummary.details)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun create(details: RoomSummaryDetails): RoomListRoomSummary {
|
|
||||||
val avatarData = details.getAvatarData(size = AvatarSize.RoomListItem)
|
val avatarData = details.getAvatarData(size = AvatarSize.RoomListItem)
|
||||||
return RoomListRoomSummary(
|
return RoomListRoomSummary(
|
||||||
id = details.roomId.value,
|
id = details.roomId.value,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
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.RoomListFilter
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
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.api.roomlist.loadAllIncrementally
|
||||||
import kotlinx.collections.immutable.PersistentList
|
import kotlinx.collections.immutable.PersistentList
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
@ -48,7 +47,6 @@ class RoomListSearchDataSource @Inject constructor(
|
||||||
val roomSummaries: Flow<PersistentList<RoomListRoomSummary>> = roomList.filteredSummaries
|
val roomSummaries: Flow<PersistentList<RoomListRoomSummary>> = roomList.filteredSummaries
|
||||||
.map { roomSummaries ->
|
.map { roomSummaries ->
|
||||||
roomSummaries
|
roomSummaries
|
||||||
.filterIsInstance<RoomSummary.Filled>()
|
|
||||||
.map(roomSummaryFactory::create)
|
.map(roomSummaryFactory::create)
|
||||||
.toPersistentList()
|
.toPersistentList()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
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.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
||||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||||
|
|
@ -183,7 +183,7 @@ class RoomListPresenterTest {
|
||||||
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
||||||
roomListService.postAllRooms(
|
roomListService.postAllRooms(
|
||||||
listOf(
|
listOf(
|
||||||
aRoomSummaryFilled(
|
aRoomSummary(
|
||||||
numUnreadMentions = 1,
|
numUnreadMentions = 1,
|
||||||
numUnreadMessages = 2,
|
numUnreadMessages = 2,
|
||||||
)
|
)
|
||||||
|
|
@ -203,48 +203,6 @@ class RoomListPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - update visible range`() = runTest {
|
|
||||||
val roomListService = FakeRoomListService()
|
|
||||||
val matrixClient = FakeMatrixClient(
|
|
||||||
roomListService = roomListService
|
|
||||||
)
|
|
||||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
|
||||||
val presenter = createRoomListPresenter(client = matrixClient, coroutineScope = scope)
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
roomListService.postAllRooms(listOf(aRoomSummaryFilled()))
|
|
||||||
val loadedState = awaitItem()
|
|
||||||
// check initial value
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange).isNull()
|
|
||||||
// Test empty range
|
|
||||||
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(1, 0)))
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange).isNull()
|
|
||||||
// Update visible range and check that range is transmitted to the SDK after computation
|
|
||||||
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 0)))
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange)
|
|
||||||
.isEqualTo(IntRange(0, 20))
|
|
||||||
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 1)))
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange)
|
|
||||||
.isEqualTo(IntRange(0, 21))
|
|
||||||
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(19, 29)))
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange)
|
|
||||||
.isEqualTo(IntRange(0, 49))
|
|
||||||
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(49, 59)))
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange)
|
|
||||||
.isEqualTo(IntRange(29, 79))
|
|
||||||
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 159)))
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange)
|
|
||||||
.isEqualTo(IntRange(129, 179))
|
|
||||||
loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 259)))
|
|
||||||
assertThat(roomListService.latestSlidingSyncRange)
|
|
||||||
.isEqualTo(IntRange(129, 279))
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
|
||||||
scope.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - handle DismissRequestVerificationPrompt`() = runTest {
|
fun `present - handle DismissRequestVerificationPrompt`() = runTest {
|
||||||
val scope = CoroutineScope(context = coroutineContext + SupervisorJob())
|
val scope = CoroutineScope(context = coroutineContext + SupervisorJob())
|
||||||
|
|
@ -449,7 +407,7 @@ class RoomListPresenterTest {
|
||||||
val notificationSettingsService = FakeNotificationSettingsService()
|
val notificationSettingsService = FakeNotificationSettingsService()
|
||||||
val roomListService = FakeRoomListService()
|
val roomListService = FakeRoomListService()
|
||||||
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
||||||
roomListService.postAllRooms(listOf(aRoomSummaryFilled(notificationMode = userDefinedMode)))
|
roomListService.postAllRooms(listOf(aRoomSummary(notificationMode = userDefinedMode)))
|
||||||
val matrixClient = FakeMatrixClient(
|
val matrixClient = FakeMatrixClient(
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
notificationSettingsService = notificationSettingsService
|
notificationSettingsService = notificationSettingsService
|
||||||
|
|
@ -594,7 +552,7 @@ class RoomListPresenterTest {
|
||||||
val matrixClient = FakeMatrixClient(
|
val matrixClient = FakeMatrixClient(
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
)
|
)
|
||||||
val roomSummary = aRoomSummaryFilled(
|
val roomSummary = aRoomSummary(
|
||||||
currentUserMembership = CurrentUserMembership.INVITED
|
currentUserMembership = CurrentUserMembership.INVITED
|
||||||
)
|
)
|
||||||
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
||||||
|
|
@ -610,7 +568,7 @@ class RoomListPresenterTest {
|
||||||
}.last()
|
}.last()
|
||||||
|
|
||||||
val roomListRoomSummary = state.contentAsRooms().summaries.first {
|
val roomListRoomSummary = state.contentAsRooms().summaries.first {
|
||||||
it.id == roomSummary.identifier()
|
it.id == roomSummary.roomId.value
|
||||||
}
|
}
|
||||||
state.eventSink(RoomListEvents.AcceptInvite(roomListRoomSummary))
|
state.eventSink(RoomListEvents.AcceptInvite(roomListRoomSummary))
|
||||||
state.eventSink(RoomListEvents.DeclineInvite(roomListRoomSummary))
|
state.eventSink(RoomListEvents.DeclineInvite(roomListRoomSummary))
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
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.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.room.aRoomSummaryFilled
|
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
|
@ -117,10 +116,7 @@ class RoomListSearchPresenterTest {
|
||||||
assertThat(state.results).isEmpty()
|
assertThat(state.results).isEmpty()
|
||||||
}
|
}
|
||||||
roomListService.postAllRooms(
|
roomListService.postAllRooms(
|
||||||
listOf(
|
listOf(aRoomSummary())
|
||||||
RoomSummary.Empty("1"),
|
|
||||||
aRoomSummaryFilled()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
awaitItem().let { state ->
|
awaitItem().let { state ->
|
||||||
assertThat(state.results).hasSize(1)
|
assertThat(state.results).hasSize(1)
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ jsoup = "org.jsoup:jsoup:1.17.2"
|
||||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||||
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
||||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.30"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.31"
|
||||||
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
||||||
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
||||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,6 @@ interface RoomListService {
|
||||||
*/
|
*/
|
||||||
val allRooms: DynamicRoomList
|
val allRooms: DynamicRoomList
|
||||||
|
|
||||||
/**
|
|
||||||
* Will set the visible range of all rooms.
|
|
||||||
* This is useful to load more data when the user scrolls down.
|
|
||||||
*/
|
|
||||||
fun updateAllRoomsVisibleRange(range: IntRange)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sync indicator as a flow.
|
* The sync indicator as a flow.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,7 @@ 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.room.message.RoomMessage
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
||||||
sealed interface RoomSummary {
|
data class RoomSummary(
|
||||||
data class Empty(val identifier: String) : RoomSummary
|
|
||||||
data class Filled(val details: RoomSummaryDetails) : RoomSummary
|
|
||||||
|
|
||||||
fun identifier(): String {
|
|
||||||
return when (this) {
|
|
||||||
is Empty -> identifier
|
|
||||||
is Filled -> details.roomId.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class RoomSummaryDetails(
|
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
val name: String?,
|
val name: String?,
|
||||||
val canonicalAlias: RoomAlias?,
|
val canonicalAlias: RoomAlias?,
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ class RustMatrixClient(
|
||||||
withTimeout(timeout) {
|
withTimeout(timeout) {
|
||||||
roomListService.allRooms.summaries
|
roomListService.allRooms.summaries
|
||||||
.filter { roomSummaries ->
|
.filter { roomSummaries ->
|
||||||
roomSummaries.map { it.identifier() }.contains(roomId.value)
|
roomSummaries.map { it.roomId }.contains(roomId)
|
||||||
}
|
}
|
||||||
.first()
|
.first()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
|
|
||||||
|
internal fun RoomListEntriesUpdate.describe(): String {
|
||||||
|
return when (this) {
|
||||||
|
is RoomListEntriesUpdate.Set -> {
|
||||||
|
"Set #$index to '${value.displayName()}'"
|
||||||
|
}
|
||||||
|
is RoomListEntriesUpdate.Append -> {
|
||||||
|
"Append ${values.map { "'" + it.displayName() + "'" }}"
|
||||||
|
}
|
||||||
|
is RoomListEntriesUpdate.PushBack -> {
|
||||||
|
"PushBack '${value.displayName()}'"
|
||||||
|
}
|
||||||
|
is RoomListEntriesUpdate.PushFront -> {
|
||||||
|
"PushFront '${value.displayName()}'"
|
||||||
|
}
|
||||||
|
is RoomListEntriesUpdate.Insert -> {
|
||||||
|
"Insert at #$index: '${value.displayName()}'"
|
||||||
|
}
|
||||||
|
is RoomListEntriesUpdate.Remove -> {
|
||||||
|
"Remove #$index"
|
||||||
|
}
|
||||||
|
is RoomListEntriesUpdate.Reset -> {
|
||||||
|
"Reset all to ${values.map { "'" + it.displayName() + "'" }}"
|
||||||
|
}
|
||||||
|
RoomListEntriesUpdate.PopBack -> {
|
||||||
|
"PopBack"
|
||||||
|
}
|
||||||
|
RoomListEntriesUpdate.PopFront -> {
|
||||||
|
"PopFront"
|
||||||
|
}
|
||||||
|
RoomListEntriesUpdate.Clear -> {
|
||||||
|
"Clear"
|
||||||
|
}
|
||||||
|
is RoomListEntriesUpdate.Truncate -> {
|
||||||
|
"Truncate to $length items"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -76,7 +76,7 @@ internal fun RoomListInterface.entriesFlow(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val result = entriesWithDynamicAdapters(pageSize.toUInt(), listener)
|
val result = entriesWithDynamicAdapters(pageSize.toUInt(), listener)
|
||||||
val controller = result.controller
|
val controller = result.controller()
|
||||||
controller.setFilter(initialFilterKind)
|
controller.setFilter(initialFilterKind)
|
||||||
roomListDynamicEvents.onEach { controllerEvents ->
|
roomListDynamicEvents.onEach { controllerEvents ->
|
||||||
when (controllerEvents) {
|
when (controllerEvents) {
|
||||||
|
|
@ -92,7 +92,8 @@ internal fun RoomListInterface.entriesFlow(
|
||||||
}
|
}
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
awaitClose {
|
awaitClose {
|
||||||
result.entriesStream.cancelAndDestroy()
|
result.entriesStream().cancelAndDestroy()
|
||||||
|
controller.destroy()
|
||||||
result.destroy()
|
result.destroy()
|
||||||
}
|
}
|
||||||
}.catch {
|
}.catch {
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,19 @@ val RoomListFilter.predicate
|
||||||
is RoomListFilter.Any -> { _: RoomSummary -> true }
|
is RoomListFilter.Any -> { _: RoomSummary -> true }
|
||||||
RoomListFilter.None -> { _: RoomSummary -> false }
|
RoomListFilter.None -> { _: RoomSummary -> false }
|
||||||
RoomListFilter.Category.Group -> { roomSummary: RoomSummary ->
|
RoomListFilter.Category.Group -> { roomSummary: RoomSummary ->
|
||||||
roomSummary is RoomSummary.Filled && !roomSummary.details.isDirect && !roomSummary.isInvited()
|
!roomSummary.isDirect && !roomSummary.isInvited()
|
||||||
}
|
}
|
||||||
RoomListFilter.Category.People -> { roomSummary: RoomSummary ->
|
RoomListFilter.Category.People -> { roomSummary: RoomSummary ->
|
||||||
roomSummary is RoomSummary.Filled && roomSummary.details.isDirect && !roomSummary.isInvited()
|
roomSummary.isDirect && !roomSummary.isInvited()
|
||||||
}
|
}
|
||||||
RoomListFilter.Favorite -> { roomSummary: RoomSummary ->
|
RoomListFilter.Favorite -> { roomSummary: RoomSummary ->
|
||||||
roomSummary is RoomSummary.Filled && roomSummary.details.isFavorite && !roomSummary.isInvited()
|
roomSummary.isFavorite && !roomSummary.isInvited()
|
||||||
}
|
}
|
||||||
RoomListFilter.Unread -> { roomSummary: RoomSummary ->
|
RoomListFilter.Unread -> { roomSummary: RoomSummary ->
|
||||||
roomSummary is RoomSummary.Filled &&
|
!roomSummary.isInvited() && (roomSummary.numUnreadNotifications > 0 || roomSummary.isMarkedUnread)
|
||||||
!roomSummary.isInvited() &&
|
|
||||||
(roomSummary.details.numUnreadNotifications > 0 || roomSummary.details.isMarkedUnread)
|
|
||||||
}
|
}
|
||||||
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
||||||
roomSummary is RoomSummary.Filled && roomSummary.details.name.orEmpty().contains(pattern, ignoreCase = true)
|
roomSummary.name.orEmpty().contains(pattern, ignoreCase = true)
|
||||||
}
|
}
|
||||||
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
|
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
|
||||||
roomSummary.isInvited()
|
roomSummary.isInvited()
|
||||||
|
|
@ -61,4 +59,4 @@ fun List<RoomSummary>.filter(filter: RoomListFilter): List<RoomSummary> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RoomSummary.isInvited() = this is RoomSummary.Filled && this.details.currentUserMembership == CurrentUserMembership.INVITED
|
private fun RoomSummary.isInvited() = currentUserMembership == CurrentUserMembership.INVITED
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ 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.RoomAlias
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
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.notificationsettings.RoomNotificationSettingsMapper
|
||||||
import io.element.android.libraries.matrix.impl.room.elementHeroes
|
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.map
|
||||||
|
|
@ -28,12 +28,12 @@ import org.matrix.rustcomponents.sdk.RoomListItem
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
|
|
||||||
class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory()) {
|
class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory()) {
|
||||||
suspend fun create(roomListItem: RoomListItem): RoomSummaryDetails {
|
suspend fun create(roomListItem: RoomListItem): RoomSummary {
|
||||||
val roomInfo = roomListItem.roomInfo()
|
val roomInfo = roomListItem.roomInfo()
|
||||||
val latestRoomMessage = roomListItem.latestEvent()?.use {
|
val latestRoomMessage = roomListItem.latestEvent()?.use {
|
||||||
roomMessageFactory.create(it)
|
roomMessageFactory.create(it)
|
||||||
}
|
}
|
||||||
return RoomSummaryDetails(
|
return RoomSummary(
|
||||||
roomId = RoomId(roomInfo.id),
|
roomId = RoomId(roomInfo.id),
|
||||||
name = roomInfo.displayName,
|
name = roomInfo.displayName,
|
||||||
canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias),
|
canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias),
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,10 @@ import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntry
|
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class RoomSummaryListProcessor(
|
class RoomSummaryListProcessor(
|
||||||
|
|
@ -50,15 +49,17 @@ class RoomSummaryListProcessor(
|
||||||
suspend fun rebuildRoomSummaries() {
|
suspend fun rebuildRoomSummaries() {
|
||||||
updateRoomSummaries {
|
updateRoomSummaries {
|
||||||
forEachIndexed { i, summary ->
|
forEachIndexed { i, summary ->
|
||||||
this[i] = when (summary) {
|
val result = buildAndCacheRoomSummaryForIdentifier(summary.roomId.value)
|
||||||
is RoomSummary.Empty -> summary
|
if (result != null) {
|
||||||
is RoomSummary.Filled -> buildAndCacheRoomSummaryForIdentifier(summary.identifier())
|
this[i] = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun MutableList<RoomSummary>.applyUpdate(update: RoomListEntriesUpdate) {
|
private suspend fun MutableList<RoomSummary>.applyUpdate(update: RoomListEntriesUpdate) {
|
||||||
|
// Remove this comment to debug changes in the room list
|
||||||
|
// Timber.d("Apply room list update: ${update.describe()}")
|
||||||
when (update) {
|
when (update) {
|
||||||
is RoomListEntriesUpdate.Append -> {
|
is RoomListEntriesUpdate.Append -> {
|
||||||
val roomSummaries = update.values.map {
|
val roomSummaries = update.values.map {
|
||||||
|
|
@ -104,27 +105,25 @@ class RoomSummaryListProcessor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun buildSummaryForRoomListEntry(entry: RoomListEntry): RoomSummary {
|
private suspend fun buildSummaryForRoomListEntry(entry: RoomListItem): RoomSummary {
|
||||||
return when (entry) {
|
return buildAndCacheRoomSummaryForRoomListItem(entry)
|
||||||
RoomListEntry.Empty -> buildEmptyRoomSummary()
|
|
||||||
is RoomListEntry.Filled -> buildAndCacheRoomSummaryForIdentifier(entry.roomId)
|
|
||||||
is RoomListEntry.Invalidated -> {
|
|
||||||
roomSummariesByIdentifier[entry.roomId] ?: buildAndCacheRoomSummaryForIdentifier(entry.roomId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildEmptyRoomSummary(): RoomSummary {
|
private suspend fun buildAndCacheRoomSummaryForIdentifier(identifier: String): RoomSummary? {
|
||||||
return RoomSummary.Empty(UUID.randomUUID().toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun buildAndCacheRoomSummaryForIdentifier(identifier: String): RoomSummary {
|
|
||||||
val builtRoomSummary = roomListService.roomOrNull(identifier)?.use { roomListItem ->
|
val builtRoomSummary = roomListService.roomOrNull(identifier)?.use { roomListItem ->
|
||||||
RoomSummary.Filled(
|
buildAndCacheRoomSummaryForRoomListItem(roomListItem)
|
||||||
details = roomSummaryDetailsFactory.create(roomListItem)
|
}
|
||||||
)
|
if (builtRoomSummary != null) {
|
||||||
} ?: buildEmptyRoomSummary()
|
roomSummariesByIdentifier[identifier] = builtRoomSummary
|
||||||
roomSummariesByIdentifier[builtRoomSummary.identifier()] = builtRoomSummary
|
} else {
|
||||||
|
roomSummariesByIdentifier.remove(identifier)
|
||||||
|
}
|
||||||
|
return builtRoomSummary
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun buildAndCacheRoomSummaryForRoomListItem(roomListItem: RoomListItem): RoomSummary {
|
||||||
|
val builtRoomSummary = roomSummaryDetailsFactory.create(roomListItem = roomListItem)
|
||||||
|
roomSummariesByIdentifier[builtRoomSummary.roomId.value] = builtRoomSummary
|
||||||
return builtRoomSummary
|
return builtRoomSummary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListException
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListInput
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListRange
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -73,20 +69,6 @@ internal class RustRoomListService(
|
||||||
allRooms.loadAllIncrementally(sessionCoroutineScope)
|
allRooms.loadAllIncrementally(sessionCoroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAllRoomsVisibleRange(range: IntRange) {
|
|
||||||
Timber.v("setVisibleRange=$range")
|
|
||||||
sessionCoroutineScope.launch {
|
|
||||||
try {
|
|
||||||
val ranges = listOf(RoomListRange(range.first.toUInt(), range.last.toUInt()))
|
|
||||||
innerRoomListService.applyInput(
|
|
||||||
RoomListInput.Viewport(ranges)
|
|
||||||
)
|
|
||||||
} catch (exception: RoomListException) {
|
|
||||||
Timber.e(exception, "Failed updating visible range")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> =
|
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> =
|
||||||
innerRoomListService.syncIndicator()
|
innerRoomListService.syncIndicator()
|
||||||
.map { it.toSyncIndicator() }
|
.map { it.toSyncIndicator() }
|
||||||
|
|
|
||||||
|
|
@ -19,46 +19,31 @@ package io.element.android.libraries.matrix.impl.roomlist
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails
|
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class RoomListFilterTest {
|
class RoomListFilterTest {
|
||||||
private val regularRoom = aRoomSummaryFilled(
|
private val regularRoom = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
isDirect = false
|
||||||
isDirect = false
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
private val directRoom = aRoomSummaryFilled(
|
private val directRoom = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
isDirect = true
|
||||||
isDirect = true
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
private val favoriteRoom = aRoomSummaryFilled(
|
private val favoriteRoom = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
isFavorite = true
|
||||||
isFavorite = true
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
private val markedAsUnreadRoom = aRoomSummaryFilled(
|
private val markedAsUnreadRoom = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
isMarkedUnread = true
|
||||||
isMarkedUnread = true
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
private val unreadNotificationRoom = aRoomSummaryFilled(
|
private val unreadNotificationRoom = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
numUnreadNotifications = 1
|
||||||
numUnreadNotifications = 1
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
private val roomToSearch = aRoomSummaryFilled(
|
private val roomToSearch = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
name = "Room to search"
|
||||||
name = "Room to search"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
private val invitedRoom = aRoomSummaryFilled(
|
private val invitedRoom = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
currentUserMembership = CurrentUserMembership.INVITED
|
||||||
currentUserMembership = CurrentUserMembership.INVITED
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val roomSummaries = listOf(
|
private val roomSummaries = listOf(
|
||||||
|
|
|
||||||
|
|
@ -18,23 +18,31 @@ package io.element.android.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import com.sun.jna.Pointer
|
import com.sun.jna.Pointer
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
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_2
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||||
|
import org.matrix.rustcomponents.sdk.Membership
|
||||||
|
import org.matrix.rustcomponents.sdk.NoPointer
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomHero
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||||
import org.matrix.rustcomponents.sdk.RoomList
|
import org.matrix.rustcomponents.sdk.RoomList
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntry
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListInput
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomMember
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
||||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||||
|
|
||||||
// NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers.
|
// NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers.
|
||||||
|
|
@ -44,33 +52,34 @@ class RoomSummaryListProcessorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Append adds new entries at the end of the list`() = runTest {
|
fun `Append adds new entries at the end of the list`() = runTest {
|
||||||
summaries.value = listOf(aRoomSummaryFilled())
|
summaries.value = listOf(aRoomSummary())
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))))
|
val newEntry = FakeRoomListItem(A_ROOM_ID_2)
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(newEntry, newEntry, newEntry))))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(4)
|
assertThat(summaries.value.count()).isEqualTo(4)
|
||||||
assertThat(summaries.value.subList(1, 4).all { it is RoomSummary.Empty }).isTrue()
|
assertThat(summaries.value.subList(1, 4).all { it.roomId == A_ROOM_ID_2 }).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
||||||
summaries.value = listOf(aRoomSummaryFilled())
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(FakeRoomListItem(A_ROOM_ID_2))))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(2)
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
assertThat(summaries.value.last()).isInstanceOf(RoomSummary.Empty::class.java)
|
assertThat(summaries.value.last().roomId).isEqualTo(A_ROOM_ID_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
||||||
summaries.value = listOf(aRoomSummaryFilled())
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(FakeRoomListItem(A_ROOM_ID_2))))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(2)
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
assertThat(summaries.value.first()).isInstanceOf(RoomSummary.Empty::class.java)
|
assertThat(summaries.value.first().roomId).isEqualTo(A_ROOM_ID_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -79,10 +88,10 @@ class RoomSummaryListProcessorTest {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), FakeRoomListItem(A_ROOM_ID_2))))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
assertThat(summaries.value[index]).isInstanceOf(RoomSummary.Empty::class.java)
|
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -91,10 +100,10 @@ class RoomSummaryListProcessorTest {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), FakeRoomListItem(A_ROOM_ID_2))))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(2)
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
assertThat(summaries.value[index]).isInstanceOf(RoomSummary.Empty::class.java)
|
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -106,7 +115,7 @@ class RoomSummaryListProcessorTest {
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Remove(index.toUInt())))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Remove(index.toUInt())))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID_2.value)
|
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -118,7 +127,7 @@ class RoomSummaryListProcessorTest {
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PopBack))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PopBack))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID.value)
|
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -130,7 +139,7 @@ class RoomSummaryListProcessorTest {
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PopFront))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PopFront))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID_2.value)
|
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -152,7 +161,7 @@ class RoomSummaryListProcessorTest {
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Truncate(1u)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Truncate(1u)))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID.value)
|
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TestScope.createProcessor() = RoomSummaryListProcessor(
|
private fun TestScope.createProcessor() = RoomSummaryListProcessor(
|
||||||
|
|
@ -168,8 +177,6 @@ class RoomSummaryListProcessorTest {
|
||||||
return RoomList(Pointer.NULL)
|
return RoomList(Pointer.NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun applyInput(input: RoomListInput) = Unit
|
|
||||||
|
|
||||||
override fun room(roomId: String): RoomListItem {
|
override fun room(roomId: String): RoomListItem {
|
||||||
return RoomListItem(Pointer.NULL)
|
return RoomListItem(Pointer.NULL)
|
||||||
}
|
}
|
||||||
|
|
@ -183,3 +190,81 @@ class RoomSummaryListProcessorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun aRustRoomInfo(
|
||||||
|
id: String = A_ROOM_ID.value,
|
||||||
|
displayName: String = A_ROOM_NAME,
|
||||||
|
rawName: String = A_ROOM_NAME,
|
||||||
|
topic: String? = null,
|
||||||
|
avatarUrl: String? = null,
|
||||||
|
isDirect: Boolean = false,
|
||||||
|
isPublic: Boolean = false,
|
||||||
|
isSpace: Boolean = false,
|
||||||
|
isTombstoned: Boolean = false,
|
||||||
|
isFavourite: Boolean = false,
|
||||||
|
canonicalAlias: String? = null,
|
||||||
|
alternativeAliases: List<String> = listOf(),
|
||||||
|
membership: Membership = Membership.JOINED,
|
||||||
|
inviter: RoomMember? = null,
|
||||||
|
heroes: List<RoomHero> = listOf(),
|
||||||
|
activeMembersCount: ULong = 0uL,
|
||||||
|
invitedMembersCount: ULong = 0uL,
|
||||||
|
joinedMembersCount: ULong = 0uL,
|
||||||
|
userPowerLevels: Map<String, Long> = mapOf(),
|
||||||
|
highlightCount: ULong = 0uL,
|
||||||
|
notificationCount: ULong = 0uL,
|
||||||
|
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||||
|
hasRoomCall: Boolean = false,
|
||||||
|
activeRoomCallParticipants: List<String> = listOf(),
|
||||||
|
isMarkedUnread: Boolean = false,
|
||||||
|
numUnreadMessages: ULong = 0uL,
|
||||||
|
numUnreadNotifications: ULong = 0uL,
|
||||||
|
numUnreadMentions: ULong = 0uL,
|
||||||
|
) = RoomInfo(
|
||||||
|
id = id,
|
||||||
|
displayName = displayName,
|
||||||
|
rawName = rawName,
|
||||||
|
topic = topic,
|
||||||
|
avatarUrl = avatarUrl,
|
||||||
|
isDirect = isDirect,
|
||||||
|
isPublic = isPublic,
|
||||||
|
isSpace = isSpace,
|
||||||
|
isTombstoned = isTombstoned,
|
||||||
|
isFavourite = isFavourite,
|
||||||
|
canonicalAlias = canonicalAlias,
|
||||||
|
alternativeAliases = alternativeAliases,
|
||||||
|
membership = membership,
|
||||||
|
inviter = inviter,
|
||||||
|
heroes = heroes,
|
||||||
|
activeMembersCount = activeMembersCount,
|
||||||
|
invitedMembersCount = invitedMembersCount,
|
||||||
|
joinedMembersCount = joinedMembersCount,
|
||||||
|
userPowerLevels = userPowerLevels,
|
||||||
|
highlightCount = highlightCount,
|
||||||
|
notificationCount = notificationCount,
|
||||||
|
userDefinedNotificationMode = userDefinedNotificationMode,
|
||||||
|
hasRoomCall = hasRoomCall,
|
||||||
|
activeRoomCallParticipants = activeRoomCallParticipants,
|
||||||
|
isMarkedUnread = isMarkedUnread,
|
||||||
|
numUnreadMessages = numUnreadMessages,
|
||||||
|
numUnreadNotifications = numUnreadNotifications,
|
||||||
|
numUnreadMentions = numUnreadMentions
|
||||||
|
)
|
||||||
|
|
||||||
|
class FakeRoomListItem(
|
||||||
|
private val roomId: RoomId,
|
||||||
|
private val roomInfo: RoomInfo = aRustRoomInfo(id = roomId.value),
|
||||||
|
private val latestEvent: EventTimelineItem? = null,
|
||||||
|
) : RoomListItem(NoPointer) {
|
||||||
|
override fun id(): String {
|
||||||
|
return roomId.value
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun roomInfo(): RoomInfo {
|
||||||
|
return roomInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun latestEvent(): EventTimelineItem? {
|
||||||
|
return latestEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ 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.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
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.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
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.AN_EVENT_ID
|
||||||
|
|
@ -44,25 +43,19 @@ fun aRoomSummaryFilled(
|
||||||
numUnreadMessages: Int = 0,
|
numUnreadMessages: Int = 0,
|
||||||
notificationMode: RoomNotificationMode? = null,
|
notificationMode: RoomNotificationMode? = null,
|
||||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||||
) = RoomSummary.Filled(
|
) = aRoomSummary(
|
||||||
aRoomSummaryDetails(
|
roomId = roomId,
|
||||||
roomId = roomId,
|
name = name,
|
||||||
name = name,
|
isDirect = isDirect,
|
||||||
isDirect = isDirect,
|
avatarUrl = avatarUrl,
|
||||||
avatarUrl = avatarUrl,
|
lastMessage = lastMessage,
|
||||||
lastMessage = lastMessage,
|
numUnreadMentions = numUnreadMentions,
|
||||||
numUnreadMentions = numUnreadMentions,
|
numUnreadMessages = numUnreadMessages,
|
||||||
numUnreadMessages = numUnreadMessages,
|
notificationMode = notificationMode,
|
||||||
notificationMode = notificationMode,
|
currentUserMembership = currentUserMembership,
|
||||||
currentUserMembership = currentUserMembership,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aRoomSummaryFilled(
|
fun aRoomSummary(
|
||||||
details: RoomSummaryDetails = aRoomSummaryDetails(),
|
|
||||||
) = RoomSummary.Filled(details)
|
|
||||||
|
|
||||||
fun aRoomSummaryDetails(
|
|
||||||
roomId: RoomId = A_ROOM_ID,
|
roomId: RoomId = A_ROOM_ID,
|
||||||
name: String? = A_ROOM_NAME,
|
name: String? = A_ROOM_NAME,
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
|
|
@ -80,7 +73,7 @@ fun aRoomSummaryDetails(
|
||||||
isFavorite: Boolean = false,
|
isFavorite: Boolean = false,
|
||||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||||
heroes: List<MatrixUser> = emptyList(),
|
heroes: List<MatrixUser> = emptyList(),
|
||||||
) = RoomSummaryDetails(
|
) = RoomSummary(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
name = name,
|
name = name,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,6 @@ class FakeRoomListService : RoomListService {
|
||||||
syncIndicatorStateFlow.emit(value)
|
syncIndicatorStateFlow.emit(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
var latestSlidingSyncRange: IntRange? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
override fun createRoomList(
|
override fun createRoomList(
|
||||||
pageSize: Int,
|
pageSize: Int,
|
||||||
initialFilter: RoomListFilter,
|
initialFilter: RoomListFilter,
|
||||||
|
|
@ -65,10 +62,6 @@ class FakeRoomListService : RoomListService {
|
||||||
MutableStateFlow(RoomListFilter.all())
|
MutableStateFlow(RoomListFilter.all())
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun updateAllRoomsVisibleRange(range: IntRange) {
|
|
||||||
latestSlidingSyncRange = range
|
|
||||||
}
|
|
||||||
|
|
||||||
override val state: StateFlow<RoomListService.State> = roomListStateFlow
|
override val state: StateFlow<RoomListService.State> = roomListStateFlow
|
||||||
|
|
||||||
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> = syncIndicatorStateFlow
|
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> = syncIndicatorStateFlow
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ 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.RoomMember
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
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.room.message.RoomMessage
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
||||||
open class RoomSummaryDetailsProvider : PreviewParameterProvider<RoomSummaryDetails> {
|
open class RoomSummaryDetailsProvider : PreviewParameterProvider<RoomSummary> {
|
||||||
override val values: Sequence<RoomSummaryDetails>
|
override val values: Sequence<RoomSummary>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aRoomSummaryDetails(),
|
aRoomSummaryDetails(),
|
||||||
aRoomSummaryDetails(name = null),
|
aRoomSummaryDetails(name = null),
|
||||||
|
|
@ -52,7 +52,7 @@ fun aRoomSummaryDetails(
|
||||||
isFavorite: Boolean = false,
|
isFavorite: Boolean = false,
|
||||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||||
heroes: List<MatrixUser> = emptyList(),
|
heroes: List<MatrixUser> = emptyList(),
|
||||||
) = RoomSummaryDetails(
|
) = RoomSummary(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
name = name,
|
name = name,
|
||||||
canonicalAlias = canonicalAlias,
|
canonicalAlias = canonicalAlias,
|
||||||
|
|
|
||||||
|
|
@ -43,15 +43,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.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
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.getAvatarData
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectedRoom(
|
fun SelectedRoom(
|
||||||
roomSummary: RoomSummaryDetails,
|
roomSummary: RoomSummary,
|
||||||
onRemoveRoom: (RoomSummaryDetails) -> Unit,
|
onRemoveRoom: (RoomSummary) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -100,10 +100,10 @@ fun SelectedRoom(
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SelectedRoomPreview(
|
internal fun SelectedRoomPreview(
|
||||||
@PreviewParameter(RoomSummaryDetailsProvider::class) roomSummaryDetails: RoomSummaryDetails
|
@PreviewParameter(RoomSummaryDetailsProvider::class) roomSummary: RoomSummary
|
||||||
) = ElementPreview {
|
) = ElementPreview {
|
||||||
SelectedRoom(
|
SelectedRoom(
|
||||||
roomSummary = roomSummaryDetails,
|
roomSummary = roomSummary,
|
||||||
onRemoveRoom = {},
|
onRemoveRoom = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ 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.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
|
|
||||||
fun RoomSummaryDetails.getAvatarData(size: AvatarSize) = AvatarData(
|
fun RoomSummary.getAvatarData(size: AvatarSize) = AvatarData(
|
||||||
id = roomId.value,
|
id = roomId.value,
|
||||||
name = name,
|
name = name,
|
||||||
url = avatarUrl,
|
url = avatarUrl,
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
package io.element.android.libraries.roomselect.impl
|
package io.element.android.libraries.roomselect.impl
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
|
|
||||||
sealed interface RoomSelectEvents {
|
sealed interface RoomSelectEvents {
|
||||||
data class SetSelectedRoom(val room: RoomSummaryDetails) : RoomSelectEvents
|
data class SetSelectedRoom(val room: RoomSummary) : RoomSelectEvents
|
||||||
|
|
||||||
// TODO remove to restore multi-selection
|
// TODO remove to restore multi-selection
|
||||||
data object RemoveSelectedRoom : RoomSelectEvents
|
data object RemoveSelectedRoom : RoomSelectEvents
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
@ -45,7 +45,7 @@ class RoomSelectPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomSelectState {
|
override fun present(): RoomSelectState {
|
||||||
var selectedRooms by remember { mutableStateOf(persistentListOf<RoomSummaryDetails>()) }
|
var selectedRooms by remember { mutableStateOf(persistentListOf<RoomSummary>()) }
|
||||||
var searchQuery by remember { mutableStateOf("") }
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
var isSearchActive by remember { mutableStateOf(false) }
|
var isSearchActive by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ 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.RoomListFilter
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
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.RoomSummary
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally
|
import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally
|
||||||
import kotlinx.collections.immutable.PersistentList
|
import kotlinx.collections.immutable.PersistentList
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
@ -48,11 +47,9 @@ class RoomSelectSearchDataSource @Inject constructor(
|
||||||
source = RoomList.Source.All,
|
source = RoomList.Source.All,
|
||||||
)
|
)
|
||||||
|
|
||||||
val roomSummaries: Flow<PersistentList<RoomSummaryDetails>> = roomList.filteredSummaries
|
val roomSummaries: Flow<PersistentList<RoomSummary>> = roomList.filteredSummaries
|
||||||
.map { roomSummaries ->
|
.map { roomSummaries ->
|
||||||
roomSummaries
|
roomSummaries
|
||||||
.filterIsInstance<RoomSummary.Filled>()
|
|
||||||
.map { it.details }
|
|
||||||
.filter { it.currentUserMembership == CurrentUserMembership.JOINED }
|
.filter { it.currentUserMembership == CurrentUserMembership.JOINED }
|
||||||
.distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received
|
.distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received
|
||||||
.toPersistentList()
|
.toPersistentList()
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,15 @@
|
||||||
package io.element.android.libraries.roomselect.impl
|
package io.element.android.libraries.roomselect.impl
|
||||||
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class RoomSelectState(
|
data class RoomSelectState(
|
||||||
val mode: RoomSelectMode,
|
val mode: RoomSelectMode,
|
||||||
val resultState: SearchBarResultState<ImmutableList<RoomSummaryDetails>>,
|
val resultState: SearchBarResultState<ImmutableList<RoomSummary>>,
|
||||||
val query: String,
|
val query: String,
|
||||||
val isSearchActive: Boolean,
|
val isSearchActive: Boolean,
|
||||||
val selectedRooms: ImmutableList<RoomSummaryDetails>,
|
val selectedRooms: ImmutableList<RoomSummary>,
|
||||||
val eventSink: (RoomSelectEvents) -> Unit
|
val eventSink: (RoomSelectEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
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.RoomAlias
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
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.aRoomSummaryDetails
|
||||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -52,10 +52,10 @@ open class RoomSelectStateProvider : PreviewParameterProvider<RoomSelectState> {
|
||||||
|
|
||||||
private fun aRoomSelectState(
|
private fun aRoomSelectState(
|
||||||
mode: RoomSelectMode = RoomSelectMode.Forward,
|
mode: RoomSelectMode = RoomSelectMode.Forward,
|
||||||
resultState: SearchBarResultState<ImmutableList<RoomSummaryDetails>> = SearchBarResultState.Initial(),
|
resultState: SearchBarResultState<ImmutableList<RoomSummary>> = SearchBarResultState.Initial(),
|
||||||
query: String = "",
|
query: String = "",
|
||||||
isSearchActive: Boolean = false,
|
isSearchActive: Boolean = false,
|
||||||
selectedRooms: ImmutableList<RoomSummaryDetails> = persistentListOf(),
|
selectedRooms: ImmutableList<RoomSummary> = persistentListOf(),
|
||||||
) = RoomSelectState(
|
) = RoomSelectState(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
resultState = resultState,
|
resultState = resultState,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ 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.TextButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
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.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
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.components.SelectedRoom
|
||||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||||
|
|
@ -73,13 +73,13 @@ fun RoomSelectView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun onRoomRemoved(roomSummaryDetails: RoomSummaryDetails) {
|
fun onRoomRemoved(roomSummary: RoomSummary) {
|
||||||
// TODO toggle selection when multi-selection is enabled
|
// TODO toggle selection when multi-selection is enabled
|
||||||
state.eventSink(RoomSelectEvents.RemoveSelectedRoom)
|
state.eventSink(RoomSelectEvents.RemoveSelectedRoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList<RoomSummaryDetails>) {
|
fun SelectedRoomsHelper(isForwarding: Boolean, selectedRooms: ImmutableList<RoomSummary>) {
|
||||||
if (isForwarding) return
|
if (isForwarding) return
|
||||||
SelectedRooms(
|
SelectedRooms(
|
||||||
selectedRooms = selectedRooms,
|
selectedRooms = selectedRooms,
|
||||||
|
|
@ -193,8 +193,8 @@ fun RoomSelectView(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SelectedRooms(
|
private fun SelectedRooms(
|
||||||
selectedRooms: ImmutableList<RoomSummaryDetails>,
|
selectedRooms: ImmutableList<RoomSummary>,
|
||||||
onRemoveRoom: (RoomSummaryDetails) -> Unit,
|
onRemoveRoom: (RoomSummary) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
LazyRow(
|
LazyRow(
|
||||||
|
|
@ -210,9 +210,9 @@ private fun SelectedRooms(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RoomSummaryView(
|
private fun RoomSummaryView(
|
||||||
summary: RoomSummaryDetails,
|
summary: RoomSummary,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
onSelection: (RoomSummaryDetails) -> Unit,
|
onSelection: (RoomSummary) -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,7 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
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.RoomListFilter
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
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.room.aRoomSummaryDetails
|
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
|
@ -69,7 +68,7 @@ class RoomSelectPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - update query`() = runTest {
|
fun `present - update query`() = runTest {
|
||||||
val roomListService = FakeRoomListService().apply {
|
val roomListService = FakeRoomListService().apply {
|
||||||
postAllRooms(listOf(RoomSummary.Filled(aRoomSummaryDetails())))
|
postAllRooms(listOf(aRoomSummary()))
|
||||||
}
|
}
|
||||||
val presenter = createRoomSelectPresenter(
|
val presenter = createRoomSelectPresenter(
|
||||||
roomListService = roomListService
|
roomListService = roomListService
|
||||||
|
|
@ -78,7 +77,7 @@ class RoomSelectPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(awaitItem().resultState as? SearchBarResultState.Results).isEqualTo(SearchBarResultState.Results(listOf(aRoomSummaryDetails())))
|
assertThat(awaitItem().resultState as? SearchBarResultState.Results).isEqualTo(SearchBarResultState.Results(listOf(aRoomSummary())))
|
||||||
initialState.eventSink(RoomSelectEvents.ToggleSearchActive)
|
initialState.eventSink(RoomSelectEvents.ToggleSearchActive)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
initialState.eventSink(RoomSelectEvents.UpdateQuery("string not contained"))
|
initialState.eventSink(RoomSelectEvents.UpdateQuery("string not contained"))
|
||||||
|
|
@ -98,7 +97,7 @@ class RoomSelectPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - select and remove a room`() = runTest {
|
fun `present - select and remove a room`() = runTest {
|
||||||
val roomListService = FakeRoomListService().apply {
|
val roomListService = FakeRoomListService().apply {
|
||||||
postAllRooms(listOf(RoomSummary.Filled(aRoomSummaryDetails())))
|
postAllRooms(listOf(aRoomSummary()))
|
||||||
}
|
}
|
||||||
val presenter = createRoomSelectPresenter(
|
val presenter = createRoomSelectPresenter(
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
|
|
@ -107,7 +106,7 @@ class RoomSelectPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
val summary = aRoomSummaryDetails()
|
val summary = aRoomSummary()
|
||||||
initialState.eventSink(RoomSelectEvents.SetSelectedRoom(summary))
|
initialState.eventSink(RoomSelectEvents.SetSelectedRoom(summary))
|
||||||
assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(summary))
|
assertThat(awaitItem().selectedRooms).isEqualTo(persistentListOf(summary))
|
||||||
initialState.eventSink(RoomSelectEvents.RemoveSelectedRoom)
|
initialState.eventSink(RoomSelectEvents.RemoveSelectedRoom)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:fda6dfc8c57df5456535a8ec33f78830beaab61cc27ba4119e9576c5d641023b
|
oid sha256:fe7dd8d4416b397b63fa2b3295e94e53af62f731eaeecca43b5df298a3e834d3
|
||||||
size 47856
|
size 17253
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:60b169ac392d4b51e8260c595a97dd14f267facd8f428ad69814bd27e502e189
|
oid sha256:dd6e7de23ed7048d02a2e7f412158e2372a19c0f6106d0f2876345aadfff906e
|
||||||
size 44355
|
size 16247
|
||||||
|
|
|
||||||