Merge pull request #1330 from vector-im/dla/feature/room_list_decoration
Show a room list decoration for notification setting applied
This commit is contained in:
commit
abb11215ce
28 changed files with 228 additions and 43 deletions
|
|
@ -19,6 +19,7 @@ package io.element.android.features.roomlist.impl.components
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
|
@ -33,6 +34,9 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
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.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
@ -41,16 +45,20 @@ import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryProvider
|
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryProvider
|
||||||
import io.element.android.libraries.core.extensions.orEmpty
|
import io.element.android.libraries.core.extensions.orEmpty
|
||||||
|
import io.element.android.libraries.designsystem.VectorIcons
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.roomListRoomMessage
|
import io.element.android.libraries.designsystem.theme.roomListRoomMessage
|
||||||
import io.element.android.libraries.designsystem.theme.roomListRoomMessageDate
|
import io.element.android.libraries.designsystem.theme.roomListRoomMessageDate
|
||||||
import io.element.android.libraries.designsystem.theme.roomListRoomName
|
import io.element.android.libraries.designsystem.theme.roomListRoomName
|
||||||
import io.element.android.libraries.designsystem.theme.unreadIndicator
|
import io.element.android.libraries.designsystem.theme.unreadIndicator
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
internal val minHeight = 84.dp
|
internal val minHeight = 84.dp
|
||||||
|
|
||||||
|
|
@ -161,11 +169,39 @@ private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) {
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unread
|
// Unread
|
||||||
UnreadIndicatorAtom(
|
Row(
|
||||||
modifier = Modifier.padding(top = 3.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
isVisible = room.hasUnread,
|
) {
|
||||||
)
|
NotificationIcon(room)
|
||||||
|
if (room.hasUnread) {
|
||||||
|
UnreadIndicatorAtom(
|
||||||
|
modifier = Modifier.padding(top = 3.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun NotificationIcon(room: RoomListRoomSummary) {
|
||||||
|
val tint = if(room.hasUnread) ElementTheme.colors.unreadIndicator else ElementTheme.colors.iconQuaternary
|
||||||
|
when(room.notificationMode) {
|
||||||
|
null, RoomNotificationMode.ALL_MESSAGES -> return
|
||||||
|
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY ->
|
||||||
|
Icon(
|
||||||
|
contentDescription = stringResource(CommonStrings.screen_notification_settings_mode_mentions),
|
||||||
|
imageVector = ImageVector.vectorResource(VectorIcons.Mention),
|
||||||
|
tint = tint,
|
||||||
|
)
|
||||||
|
RoomNotificationMode.MUTE ->
|
||||||
|
Icon(
|
||||||
|
contentDescription = stringResource(CommonStrings.common_mute),
|
||||||
|
imageVector = ImageVector.vectorResource(VectorIcons.Mute),
|
||||||
|
tint = tint,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
|
|
|
||||||
|
|
@ -27,28 +27,37 @@ 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.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||||
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 kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class RoomListDataSource @Inject constructor(
|
class RoomListDataSource @Inject constructor(
|
||||||
private val roomListService: RoomListService,
|
private val roomListService: RoomListService,
|
||||||
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
|
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
|
||||||
private val roomLastMessageFormatter: RoomLastMessageFormatter,
|
private val roomLastMessageFormatter: RoomLastMessageFormatter,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
|
private val notificationSettingsService: NotificationSettingsService,
|
||||||
|
private val appScope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
|
init {
|
||||||
|
observeNotificationSettings()
|
||||||
|
}
|
||||||
|
|
||||||
private val _filter = MutableStateFlow("")
|
private val _filter = MutableStateFlow("")
|
||||||
private val _allRooms = MutableStateFlow<ImmutableList<RoomListRoomSummary>>(persistentListOf())
|
private val _allRooms = MutableStateFlow<ImmutableList<RoomListRoomSummary>>(persistentListOf())
|
||||||
|
|
@ -92,6 +101,16 @@ class RoomListDataSource @Inject constructor(
|
||||||
val allRooms: StateFlow<ImmutableList<RoomListRoomSummary>> = _allRooms
|
val allRooms: StateFlow<ImmutableList<RoomListRoomSummary>> = _allRooms
|
||||||
val filteredRooms: StateFlow<ImmutableList<RoomListRoomSummary>> = _filteredRooms
|
val filteredRooms: StateFlow<ImmutableList<RoomListRoomSummary>> = _filteredRooms
|
||||||
|
|
||||||
|
@OptIn(FlowPreview::class)
|
||||||
|
private fun observeNotificationSettings() {
|
||||||
|
notificationSettingsService.notificationSettingsChangeFlow
|
||||||
|
.debounce(0.5.seconds)
|
||||||
|
.onEach {
|
||||||
|
roomListService.rebuildRoomSummaries()
|
||||||
|
}
|
||||||
|
.launchIn(appScope)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun replaceWith(roomSummaries: List<RoomSummary>) = withContext(coroutineDispatchers.computation) {
|
private suspend fun replaceWith(roomSummaries: List<RoomSummary>) = withContext(coroutineDispatchers.computation) {
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
diffCacheUpdater.updateWith(roomSummaries)
|
diffCacheUpdater.updateWith(roomSummaries)
|
||||||
|
|
@ -120,10 +139,7 @@ class RoomListDataSource @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildAndCacheItem(
|
private fun buildAndCacheItem(roomSummaries: List<RoomSummary>, index: Int): RoomListRoomSummary? {
|
||||||
roomSummaries: List<RoomSummary>,
|
|
||||||
index: Int
|
|
||||||
): RoomListRoomSummary? {
|
|
||||||
val roomListRoomSummary = when (val roomSummary = roomSummaries.getOrNull(index)) {
|
val roomListRoomSummary = when (val roomSummary = roomSummaries.getOrNull(index)) {
|
||||||
is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
|
is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
|
||||||
is RoomSummary.Filled -> {
|
is RoomSummary.Filled -> {
|
||||||
|
|
@ -144,10 +160,12 @@ class RoomListDataSource @Inject constructor(
|
||||||
roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
|
roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
|
||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
avatarData = avatarData,
|
avatarData = avatarData,
|
||||||
|
notificationMode = roomSummary.details.notificationMode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
null -> null
|
null -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
diffCache[index] = roomListRoomSummary
|
diffCache[index] = roomListRoomSummary
|
||||||
return roomListRoomSummary
|
return roomListRoomSummary
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import androidx.compose.runtime.Immutable
|
||||||
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.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class RoomListRoomSummary constructor(
|
data class RoomListRoomSummary constructor(
|
||||||
|
|
@ -31,4 +32,5 @@ data class RoomListRoomSummary constructor(
|
||||||
val lastMessage: CharSequence? = null,
|
val lastMessage: CharSequence? = null,
|
||||||
val avatarData: AvatarData = AvatarData(id, name, size = AvatarSize.RoomListItem),
|
val avatarData: AvatarData = AvatarData(id, name, size = AvatarSize.RoomListItem),
|
||||||
val isPlaceholder: Boolean = false,
|
val isPlaceholder: Boolean = false,
|
||||||
|
val notificationMode: RoomNotificationMode? = null,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,16 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
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.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSummary> {
|
open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSummary> {
|
||||||
override val values: Sequence<RoomListRoomSummary>
|
override val values: Sequence<RoomListRoomSummary>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aRoomListRoomSummary(),
|
aRoomListRoomSummary(),
|
||||||
aRoomListRoomSummary().copy(lastMessage = null),
|
aRoomListRoomSummary().copy(lastMessage = null),
|
||||||
aRoomListRoomSummary().copy(hasUnread = true),
|
aRoomListRoomSummary().copy(hasUnread = true, notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY),
|
||||||
aRoomListRoomSummary().copy(timestamp = "88:88"),
|
aRoomListRoomSummary().copy(timestamp = "88:88", notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY),
|
||||||
|
aRoomListRoomSummary().copy(timestamp = "88:88", notificationMode = RoomNotificationMode.MUTE),
|
||||||
aRoomListRoomSummary().copy(timestamp = "88:88", hasUnread = true),
|
aRoomListRoomSummary().copy(timestamp = "88:88", hasUnread = true),
|
||||||
aRoomListRoomSummary().copy(isPlaceholder = true, timestamp = "88:88"),
|
aRoomListRoomSummary().copy(isPlaceholder = true, timestamp = "88:88"),
|
||||||
aRoomListRoomSummary().copy(
|
aRoomListRoomSummary().copy(
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||||
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
|
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||||
|
|
@ -47,12 +48,16 @@ import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||||
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.room.aRoomSummaryFilled
|
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.libraries.matrix.test.verification.FakeSessionVerificationService
|
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -66,7 +71,8 @@ class RoomListPresenterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - should start with no user and then load user with success`() = runTest {
|
fun `present - should start with no user and then load user with success`() = runTest {
|
||||||
val presenter = createRoomListPresenter()
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -77,6 +83,7 @@ class RoomListPresenterTests {
|
||||||
Truth.assertThat(withUserState.matrixUser!!.userId).isEqualTo(A_USER_ID)
|
Truth.assertThat(withUserState.matrixUser!!.userId).isEqualTo(A_USER_ID)
|
||||||
Truth.assertThat(withUserState.matrixUser!!.displayName).isEqualTo(A_USER_NAME)
|
Truth.assertThat(withUserState.matrixUser!!.displayName).isEqualTo(A_USER_NAME)
|
||||||
Truth.assertThat(withUserState.matrixUser!!.avatarUrl).isEqualTo(AN_AVATAR_URL)
|
Truth.assertThat(withUserState.matrixUser!!.avatarUrl).isEqualTo(AN_AVATAR_URL)
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +93,8 @@ class RoomListPresenterTests {
|
||||||
userDisplayName = Result.failure(AN_EXCEPTION),
|
userDisplayName = Result.failure(AN_EXCEPTION),
|
||||||
userAvatarURLString = Result.failure(AN_EXCEPTION),
|
userAvatarURLString = Result.failure(AN_EXCEPTION),
|
||||||
)
|
)
|
||||||
val presenter = createRoomListPresenter(matrixClient)
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(client = matrixClient, coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -94,12 +102,14 @@ class RoomListPresenterTests {
|
||||||
Truth.assertThat(initialState.matrixUser).isNull()
|
Truth.assertThat(initialState.matrixUser).isNull()
|
||||||
val withUserState = awaitItem()
|
val withUserState = awaitItem()
|
||||||
Truth.assertThat(withUserState.matrixUser).isNotNull()
|
Truth.assertThat(withUserState.matrixUser).isNotNull()
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - should filter room with success`() = runTest {
|
fun `present - should filter room with success`() = runTest {
|
||||||
val presenter = createRoomListPresenter()
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -109,8 +119,8 @@ class RoomListPresenterTests {
|
||||||
withUserState.eventSink.invoke(RoomListEvents.UpdateFilter("t"))
|
withUserState.eventSink.invoke(RoomListEvents.UpdateFilter("t"))
|
||||||
val withFilterState = awaitItem()
|
val withFilterState = awaitItem()
|
||||||
Truth.assertThat(withFilterState.filter).isEqualTo("t")
|
Truth.assertThat(withFilterState.filter).isEqualTo("t")
|
||||||
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,7 +130,8 @@ class RoomListPresenterTests {
|
||||||
val matrixClient = FakeMatrixClient(
|
val matrixClient = FakeMatrixClient(
|
||||||
roomListService = roomListService
|
roomListService = roomListService
|
||||||
)
|
)
|
||||||
val presenter = createRoomListPresenter(matrixClient)
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(client = matrixClient, coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -133,6 +144,7 @@ class RoomListPresenterTests {
|
||||||
Truth.assertThat(withRoomState.roomList.size).isEqualTo(1)
|
Truth.assertThat(withRoomState.roomList.size).isEqualTo(1)
|
||||||
Truth.assertThat(withRoomState.roomList.first())
|
Truth.assertThat(withRoomState.roomList.first())
|
||||||
.isEqualTo(aRoomListRoomSummary)
|
.isEqualTo(aRoomListRoomSummary)
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,7 +154,8 @@ class RoomListPresenterTests {
|
||||||
val matrixClient = FakeMatrixClient(
|
val matrixClient = FakeMatrixClient(
|
||||||
roomListService = roomListService
|
roomListService = roomListService
|
||||||
)
|
)
|
||||||
val presenter = createRoomListPresenter(matrixClient)
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(client = matrixClient, coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -160,6 +173,7 @@ class RoomListPresenterTests {
|
||||||
val withNotFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 0 }.last()
|
val withNotFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 0 }.last()
|
||||||
Truth.assertThat(withNotFilteredRoomState.filter).isEqualTo("tada")
|
Truth.assertThat(withNotFilteredRoomState.filter).isEqualTo("tada")
|
||||||
Truth.assertThat(withNotFilteredRoomState.filteredRoomList).isEmpty()
|
Truth.assertThat(withNotFilteredRoomState.filteredRoomList).isEmpty()
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +183,8 @@ class RoomListPresenterTests {
|
||||||
val matrixClient = FakeMatrixClient(
|
val matrixClient = FakeMatrixClient(
|
||||||
roomListService = roomListService
|
roomListService = roomListService
|
||||||
)
|
)
|
||||||
val presenter = createRoomListPresenter(matrixClient)
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(client = matrixClient, coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -200,6 +215,7 @@ class RoomListPresenterTests {
|
||||||
Truth.assertThat(roomListService.latestSlidingSyncRange)
|
Truth.assertThat(roomListService.latestSlidingSyncRange)
|
||||||
.isEqualTo(IntRange(129, 279))
|
.isEqualTo(IntRange(129, 279))
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,12 +225,14 @@ class RoomListPresenterTests {
|
||||||
val matrixClient = FakeMatrixClient(
|
val matrixClient = FakeMatrixClient(
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
)
|
)
|
||||||
|
val scope = CoroutineScope(context = coroutineContext + SupervisorJob())
|
||||||
val presenter = createRoomListPresenter(
|
val presenter = createRoomListPresenter(
|
||||||
client = matrixClient,
|
client = matrixClient,
|
||||||
sessionVerificationService = FakeSessionVerificationService().apply {
|
sessionVerificationService = FakeSessionVerificationService().apply {
|
||||||
givenIsReady(true)
|
givenIsReady(true)
|
||||||
givenVerifiedStatus(SessionVerifiedStatus.NotVerified)
|
givenVerifiedStatus(SessionVerifiedStatus.NotVerified)
|
||||||
},
|
},
|
||||||
|
coroutineScope = scope,
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
|
|
@ -224,6 +242,7 @@ class RoomListPresenterTests {
|
||||||
|
|
||||||
eventSink(RoomListEvents.DismissRequestVerificationPrompt)
|
eventSink(RoomListEvents.DismissRequestVerificationPrompt)
|
||||||
Truth.assertThat(awaitItem().displayVerificationPrompt).isFalse()
|
Truth.assertThat(awaitItem().displayVerificationPrompt).isFalse()
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,7 +250,8 @@ class RoomListPresenterTests {
|
||||||
fun `present - sets invite state`() = runTest {
|
fun `present - sets invite state`() = runTest {
|
||||||
val inviteStateFlow = MutableStateFlow(InvitesState.NoInvites)
|
val inviteStateFlow = MutableStateFlow(InvitesState.NoInvites)
|
||||||
val inviteStateDataSource = FakeInviteDataSource(inviteStateFlow)
|
val inviteStateDataSource = FakeInviteDataSource(inviteStateFlow)
|
||||||
val presenter = createRoomListPresenter(inviteStateDataSource = inviteStateDataSource)
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(inviteStateDataSource = inviteStateDataSource, coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -246,12 +266,14 @@ class RoomListPresenterTests {
|
||||||
|
|
||||||
inviteStateFlow.value = InvitesState.NoInvites
|
inviteStateFlow.value = InvitesState.NoInvites
|
||||||
Truth.assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites)
|
Truth.assertThat(awaitItem().invitesState).isEqualTo(InvitesState.NoInvites)
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - show context menu`() = runTest {
|
fun `present - show context menu`() = runTest {
|
||||||
val presenter = createRoomListPresenter()
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -264,12 +286,14 @@ class RoomListPresenterTests {
|
||||||
val shownState = awaitItem()
|
val shownState = awaitItem()
|
||||||
Truth.assertThat(shownState.contextMenu)
|
Truth.assertThat(shownState.contextMenu)
|
||||||
.isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name))
|
.isEqualTo(RoomListState.ContextMenu.Shown(summary.roomId, summary.name))
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - hide context menu`() = runTest {
|
fun `present - hide context menu`() = runTest {
|
||||||
val presenter = createRoomListPresenter()
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -286,13 +310,15 @@ class RoomListPresenterTests {
|
||||||
|
|
||||||
val hiddenState = awaitItem()
|
val hiddenState = awaitItem()
|
||||||
Truth.assertThat(hiddenState.contextMenu).isEqualTo(RoomListState.ContextMenu.Hidden)
|
Truth.assertThat(hiddenState.contextMenu).isEqualTo(RoomListState.ContextMenu.Hidden)
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - leave room calls into leave room presenter`() = runTest {
|
fun `present - leave room calls into leave room presenter`() = runTest {
|
||||||
val leaveRoomPresenter = LeaveRoomPresenterFake()
|
val leaveRoomPresenter = LeaveRoomPresenterFake()
|
||||||
val presenter = createRoomListPresenter(leaveRoomPresenter = leaveRoomPresenter)
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(leaveRoomPresenter = leaveRoomPresenter, coroutineScope = scope)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -300,6 +326,35 @@ class RoomListPresenterTests {
|
||||||
initialState.eventSink(RoomListEvents.LeaveRoom(A_ROOM_ID))
|
initialState.eventSink(RoomListEvents.LeaveRoom(A_ROOM_ID))
|
||||||
Truth.assertThat(leaveRoomPresenter.events).containsExactly(LeaveRoomEvent.ShowConfirmation(A_ROOM_ID))
|
Truth.assertThat(leaveRoomPresenter.events).containsExactly(LeaveRoomEvent.ShowConfirmation(A_ROOM_ID))
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
scope.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - change in notification settings updates the summary for decorations`() = runTest {
|
||||||
|
val userDefinedMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||||
|
val notificationSettingsService = FakeNotificationSettingsService()
|
||||||
|
val roomListService = FakeRoomListService()
|
||||||
|
roomListService.postAllRooms(listOf(aRoomSummaryFilled(notificationMode = userDefinedMode)))
|
||||||
|
val matrixClient = FakeMatrixClient(
|
||||||
|
roomListService = roomListService,
|
||||||
|
notificationSettingsService = notificationSettingsService
|
||||||
|
)
|
||||||
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val presenter = createRoomListPresenter(client = matrixClient , coroutineScope = scope)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
notificationSettingsService.setRoomNotificationMode(A_ROOM_ID, userDefinedMode)
|
||||||
|
|
||||||
|
val updatedState = consumeItemsUntilPredicate { state ->
|
||||||
|
state.roomList.any { it.id == A_ROOM_ID.value && it.notificationMode == userDefinedMode }
|
||||||
|
}.last()
|
||||||
|
|
||||||
|
val room = updatedState.roomList.find { it.id == A_ROOM_ID.value }
|
||||||
|
Truth.assertThat(room?.notificationMode).isEqualTo(userDefinedMode)
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
scope.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,7 +368,8 @@ class RoomListPresenterTests {
|
||||||
lastMessageTimestampFormatter: LastMessageTimestampFormatter = FakeLastMessageTimestampFormatter().apply {
|
lastMessageTimestampFormatter: LastMessageTimestampFormatter = FakeLastMessageTimestampFormatter().apply {
|
||||||
givenFormat(A_FORMATTED_DATE)
|
givenFormat(A_FORMATTED_DATE)
|
||||||
},
|
},
|
||||||
roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter()
|
roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(),
|
||||||
|
coroutineScope: CoroutineScope = this,
|
||||||
) = RoomListPresenter(
|
) = RoomListPresenter(
|
||||||
client = client,
|
client = client,
|
||||||
sessionVerificationService = sessionVerificationService,
|
sessionVerificationService = sessionVerificationService,
|
||||||
|
|
@ -325,7 +381,9 @@ class RoomListPresenterTests {
|
||||||
client.roomListService,
|
client.roomListService,
|
||||||
lastMessageTimestampFormatter,
|
lastMessageTimestampFormatter,
|
||||||
roomLastMessageFormatter,
|
roomLastMessageFormatter,
|
||||||
coroutineDispatchers = testCoroutineDispatchers()
|
coroutineDispatchers = testCoroutineDispatchers(),
|
||||||
|
notificationSettingsService = client.notificationSettingsService(),
|
||||||
|
appScope = coroutineScope
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ object VectorIcons {
|
||||||
val Quote = R.drawable.ic_quote
|
val Quote = R.drawable.ic_quote
|
||||||
val Strikethrough = R.drawable.ic_strikethrough
|
val Strikethrough = R.drawable.ic_strikethrough
|
||||||
val Underline = R.drawable.ic_underline
|
val Underline = R.drawable.ic_underline
|
||||||
|
val Mention = R.drawable.ic_mention
|
||||||
|
val Mute = R.drawable.ic_mute
|
||||||
val ThreadDecoration = R.drawable.ic_thread_decoration
|
val ThreadDecoration = R.drawable.ic_thread_decoration
|
||||||
val Plus = R.drawable.ic_plus
|
val Plus = R.drawable.ic_plus
|
||||||
val Cancel = R.drawable.ic_cancel
|
val Cancel = R.drawable.ic_cancel
|
||||||
|
|
|
||||||
13
libraries/designsystem/src/main/res/drawable/ic_mention.xml
Normal file
13
libraries/designsystem/src/main/res/drawable/ic_mention.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h16v16h-16z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M8,14.667C7.077,14.667 6.211,14.492 5.4,14.142C4.589,13.792 3.883,13.317 3.283,12.717C2.683,12.117 2.208,11.411 1.858,10.6C1.508,9.789 1.333,8.922 1.333,8C1.333,7.078 1.508,6.211 1.858,5.4C2.208,4.589 2.683,3.883 3.283,3.283C3.883,2.683 4.589,2.208 5.4,1.858C6.211,1.508 7.077,1.333 8,1.333C8.922,1.333 9.789,1.508 10.6,1.858C11.411,2.208 12.116,2.683 12.716,3.283C13.316,3.883 13.791,4.589 14.141,5.4C14.491,6.211 14.666,7.078 14.666,8V9.017C14.666,9.672 14.441,10.228 13.991,10.683C13.541,11.139 12.989,11.367 12.333,11.367C11.944,11.367 11.578,11.286 11.233,11.125C10.889,10.964 10.6,10.733 10.366,10.433C10.055,10.744 9.694,10.981 9.283,11.142C8.872,11.303 8.444,11.383 8,11.383C7.055,11.383 6.255,11.056 5.6,10.4C4.944,9.744 4.616,8.944 4.616,8C4.616,7.055 4.944,6.255 5.6,5.6C6.255,4.944 7.055,4.617 8,4.617C8.944,4.617 9.744,4.944 10.4,5.6C11.055,6.255 11.383,7.055 11.383,8V9.017C11.383,9.272 11.477,9.494 11.666,9.683C11.855,9.872 12.078,9.967 12.333,9.967C12.589,9.967 12.808,9.872 12.991,9.683C13.175,9.494 13.266,9.272 13.266,9.017V8C13.266,6.533 12.755,5.289 11.733,4.267C10.711,3.244 9.466,2.733 8,2.733C6.533,2.733 5.289,3.244 4.266,4.267C3.244,5.289 2.733,6.533 2.733,8C2.733,9.467 3.244,10.711 4.266,11.733C5.289,12.755 6.533,13.267 8,13.267H10.6C10.789,13.267 10.953,13.336 11.091,13.475C11.23,13.614 11.3,13.778 11.3,13.967C11.3,14.156 11.23,14.319 11.091,14.458C10.953,14.597 10.789,14.667 10.6,14.667H8ZM8,9.983C8.555,9.983 9.025,9.792 9.408,9.408C9.791,9.025 9.983,8.555 9.983,8C9.983,7.444 9.791,6.975 9.408,6.592C9.025,6.208 8.555,6.017 8,6.017C7.444,6.017 6.975,6.208 6.591,6.592C6.208,6.975 6.016,7.444 6.016,8C6.016,8.555 6.208,9.025 6.591,9.408C6.975,9.792 7.444,9.983 8,9.983Z"
|
||||||
|
android:fillColor="#A6ADB7"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
10
libraries/designsystem/src/main/res/drawable/ic_mute.xml
Normal file
10
libraries/designsystem/src/main/res/drawable/ic_mute.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="15dp"
|
||||||
|
android:height="15dp"
|
||||||
|
android:viewportWidth="15"
|
||||||
|
android:viewportHeight="15">
|
||||||
|
<path
|
||||||
|
android:pathData="M1.905,0.491C1.515,0.1 0.882,0.1 0.491,0.491C0.101,0.881 0.101,1.515 0.491,1.905L3.152,4.566C3.033,4.951 2.969,5.359 2.969,5.781V8.703C2.969,9.477 2.539,9.992 2.032,10.293C1.689,10.499 1.25,10.783 1.25,11.127C1.25,11.522 1.5,11.788 2.015,11.788H7.266H10.374L13.095,14.509C13.486,14.9 14.119,14.9 14.509,14.509C14.9,14.119 14.9,13.486 14.509,13.095L1.905,0.491ZM11.563,8.703C11.563,8.779 11.567,8.852 11.575,8.923L4.886,2.234C5.346,1.921 5.864,1.693 6.411,1.576C6.408,1.546 6.407,1.515 6.407,1.484C6.407,1.01 6.791,0.625 7.266,0.625C7.741,0.625 8.125,1.01 8.125,1.484C8.125,1.515 8.124,1.546 8.121,1.576C10.02,1.984 11.563,3.713 11.563,5.781V8.703ZM6.054,12.656C6.003,12.794 5.977,12.931 5.977,13.086C5.977,13.799 6.553,14.375 7.266,14.375C7.979,14.375 8.555,13.799 8.555,13.086C8.555,12.931 8.521,12.794 8.478,12.656H6.054Z"
|
||||||
|
android:fillColor="#A6ADB7"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -54,6 +54,11 @@ interface RoomListService {
|
||||||
*/
|
*/
|
||||||
fun updateAllRoomsVisibleRange(range: IntRange)
|
fun updateAllRoomsVisibleRange(range: IntRange)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild the room summaries, required when we know some data may have changed. (E.g. room notification settings)
|
||||||
|
*/
|
||||||
|
fun rebuildRoomSummaries()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sync indicator as a flow.
|
* The sync indicator as a flow.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.api.roomlist
|
||||||
|
|
||||||
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.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.message.RoomMessage
|
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
||||||
|
|
||||||
sealed interface RoomSummary {
|
sealed interface RoomSummary {
|
||||||
|
|
@ -42,4 +43,5 @@ data class RoomSummaryDetails(
|
||||||
val lastMessageTimestamp: Long?,
|
val lastMessageTimestamp: Long?,
|
||||||
val unreadNotificationCount: Int,
|
val unreadNotificationCount: Int,
|
||||||
val inviter: RoomMember? = null,
|
val inviter: RoomMember? = null,
|
||||||
|
val notificationMode: RoomNotificationMode? = null,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
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.RoomSummaryDetails
|
||||||
|
import io.element.android.libraries.matrix.impl.notificationsettings.RoomNotificationSettingsMapper
|
||||||
import io.element.android.libraries.matrix.impl.room.RoomMemberMapper
|
import io.element.android.libraries.matrix.impl.room.RoomMemberMapper
|
||||||
import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory
|
import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||||
|
|
@ -39,6 +40,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto
|
||||||
lastMessage = latestRoomMessage,
|
lastMessage = latestRoomMessage,
|
||||||
lastMessageTimestamp = latestRoomMessage?.originServerTs,
|
lastMessageTimestamp = latestRoomMessage?.originServerTs,
|
||||||
inviter = roomInfo.inviter?.let(RoomMemberMapper::map),
|
inviter = roomInfo.inviter?.let(RoomMemberMapper::map),
|
||||||
|
notificationMode = roomInfo.userDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.parallelMap
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
|
|
@ -59,6 +60,17 @@ class RoomSummaryListProcessor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun rebuildRoomSummaries() {
|
||||||
|
updateRoomSummaries {
|
||||||
|
forEachIndexed { i, summary ->
|
||||||
|
this[i] = when(summary) {
|
||||||
|
is RoomSummary.Empty -> summary
|
||||||
|
is RoomSummary.Filled -> buildAndCacheRoomSummaryForIdentifier(summary.identifier())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun MutableList<RoomSummary>.applyUpdate(update: RoomListEntriesUpdate) {
|
private fun MutableList<RoomSummary>.applyUpdate(update: RoomListEntriesUpdate) {
|
||||||
when (update) {
|
when (update) {
|
||||||
is RoomListEntriesUpdate.Append -> {
|
is RoomListEntriesUpdate.Append -> {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,12 @@ class RustRoomListService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun rebuildRoomSummaries() {
|
||||||
|
sessionCoroutineScope.launch {
|
||||||
|
allRoomsListProcessor.rebuildRoomSummaries()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> =
|
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> =
|
||||||
innerRoomListService.syncIndicator()
|
innerRoomListService.syncIndicator()
|
||||||
.map { it.toSyncIndicator() }
|
.map { it.toSyncIndicator() }
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,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.api.core.TransactionId
|
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
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.RoomSummaryDetails
|
||||||
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
||||||
|
|
@ -48,6 +49,7 @@ fun aRoomSummaryFilled(
|
||||||
lastMessage: RoomMessage? = aRoomMessage(),
|
lastMessage: RoomMessage? = aRoomMessage(),
|
||||||
lastMessageTimestamp: Long? = null,
|
lastMessageTimestamp: Long? = null,
|
||||||
unreadNotificationCount: Int = 2,
|
unreadNotificationCount: Int = 2,
|
||||||
|
notificationMode: RoomNotificationMode? = null,
|
||||||
) = RoomSummary.Filled(
|
) = RoomSummary.Filled(
|
||||||
aRoomSummaryDetail(
|
aRoomSummaryDetail(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
|
|
@ -57,6 +59,7 @@ fun aRoomSummaryFilled(
|
||||||
lastMessage = lastMessage,
|
lastMessage = lastMessage,
|
||||||
lastMessageTimestamp = lastMessageTimestamp,
|
lastMessageTimestamp = lastMessageTimestamp,
|
||||||
unreadNotificationCount = unreadNotificationCount,
|
unreadNotificationCount = unreadNotificationCount,
|
||||||
|
notificationMode = notificationMode,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -68,6 +71,7 @@ fun aRoomSummaryDetail(
|
||||||
lastMessage: RoomMessage? = aRoomMessage(),
|
lastMessage: RoomMessage? = aRoomMessage(),
|
||||||
lastMessageTimestamp: Long? = null,
|
lastMessageTimestamp: Long? = null,
|
||||||
unreadNotificationCount: Int = 2,
|
unreadNotificationCount: Int = 2,
|
||||||
|
notificationMode: RoomNotificationMode? = null,
|
||||||
) = RoomSummaryDetails(
|
) = RoomSummaryDetails(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
name = name,
|
name = name,
|
||||||
|
|
@ -76,6 +80,7 @@ fun aRoomSummaryDetail(
|
||||||
lastMessage = lastMessage,
|
lastMessage = lastMessage,
|
||||||
lastMessageTimestamp = lastMessageTimestamp,
|
lastMessageTimestamp = lastMessageTimestamp,
|
||||||
unreadNotificationCount = unreadNotificationCount,
|
unreadNotificationCount = unreadNotificationCount,
|
||||||
|
notificationMode = notificationMode
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aRoomMessage(
|
fun aRoomMessage(
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ class FakeRoomListService : RoomListService {
|
||||||
latestSlidingSyncRange = range
|
latestSlidingSyncRange = range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun rebuildRoomSummaries() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override fun allRooms(): RoomList {
|
override fun allRooms(): RoomList {
|
||||||
return SimpleRoomList(
|
return SimpleRoomList(
|
||||||
summaries = allRoomSummariesFlow,
|
summaries = allRoomSummariesFlow,
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,8 @@ class RoomListScreen(
|
||||||
stateContentFormatter = StateContentFormatter(stringProvider),
|
stateContentFormatter = StateContentFormatter(stringProvider),
|
||||||
),
|
),
|
||||||
coroutineDispatchers = coroutineDispatchers,
|
coroutineDispatchers = coroutineDispatchers,
|
||||||
|
notificationSettingsService = matrixClient.notificationSettingsService(),
|
||||||
|
appScope = Singleton.appScope
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e710857b4a1c3da12ceb51e7400229d0fd7f42d6d6158a07e2c2814c728abfdc
|
oid sha256:a4451ed5c83109d1a36d262b9d6cca1df4f65998fa2933e77b30ffeec2a1688e
|
||||||
size 12229
|
size 12980
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:db014e76a88015053b1be458ac758dde99f8cf01a7a78850c0bf6ff4db27dcbc
|
oid sha256:6ca678d3cb26b3224c1fe44169eebd0a96ddcc344e2d9d45ff84c41116c03a0e
|
||||||
size 13173
|
size 13829
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4d062feda6fc808aa87df5a5f4b3229153f7e50925e3c55a8c8a99a583212857
|
oid sha256:8618c1af337c98cbd812632fc6dbb468b64a3b53e2141a7073a5ea52656ddaa7
|
||||||
size 13458
|
size 13611
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7cd7f6969aa48fdde75e1ad046d595882ebdadfc310cd6b6753f43895aec7b99
|
oid sha256:4d062feda6fc808aa87df5a5f4b3229153f7e50925e3c55a8c8a99a583212857
|
||||||
size 6281
|
size 13458
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:79b24ca5c6426aa966024d79bdbbc4a612169037cf6bda7496299ea298fa7517
|
oid sha256:7cd7f6969aa48fdde75e1ad046d595882ebdadfc310cd6b6753f43895aec7b99
|
||||||
size 21951
|
size 6281
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:79b24ca5c6426aa966024d79bdbbc4a612169037cf6bda7496299ea298fa7517
|
||||||
|
size 21951
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9c9b33e480bfad9c55d55e25cef2a0ab1f25235382e1b53c93107a75e95fa90e
|
oid sha256:496059d16235c52562acf31c209d917b3eff6142324e07476e01a732d9aaf816
|
||||||
size 11840
|
size 12661
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:824e708f277ea66a161f17cb07a3070e03ddcfc326b9dd73d0ad6b7e62da32c1
|
oid sha256:6f6bfe205fe096afe6d976ed242d823d02d06a01b8f81e5feca7b9a71d7b5767
|
||||||
size 12807
|
size 13543
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:39b7b8354b98bca791698ad65f4e21619136f446b2fd050c302bdd27ce138a4a
|
oid sha256:8337468ce85f5f1aa9fec867d9037b71c7460fa823c7f6fdcbf0efe48ad97d40
|
||||||
size 13263
|
size 13293
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c978bc799ab79290f89568de692d3ace219a4192ec9706fef63fe977a853f74d
|
oid sha256:39b7b8354b98bca791698ad65f4e21619136f446b2fd050c302bdd27ce138a4a
|
||||||
size 6046
|
size 13263
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:64e7ca21b0213413ed45d0640295ceb2764b01b2bce20a01518e55e620925086
|
oid sha256:c978bc799ab79290f89568de692d3ace219a4192ec9706fef63fe977a853f74d
|
||||||
size 22193
|
size 6046
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:64e7ca21b0213413ed45d0640295ceb2764b01b2bce20a01518e55e620925086
|
||||||
|
size 22193
|
||||||
Loading…
Add table
Add a link
Reference in a new issue