Merge pull request #2273 from element-hq/feature/bma/roomListRoomSummaryCleanup

Room list room summary cleanup
This commit is contained in:
Benoit Marty 2024-01-22 15:08:22 +01:00 committed by GitHub
commit bee56c83ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 181 additions and 153 deletions

View file

@ -19,7 +19,7 @@ package io.element.android.features.roomlist.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.leaveroom.api.aLeaveRoomState
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders
import io.element.android.features.roomlist.impl.model.aRoomListRoomSummary
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
@ -71,25 +71,29 @@ internal fun aRoomListState() = RoomListState(
internal fun aRoomListRoomSummaryList(): ImmutableList<RoomListRoomSummary> {
return persistentListOf(
RoomListRoomSummary(
aRoomListRoomSummary(
name = "Room",
hasUnread = true,
timestamp = "14:18",
lastMessage = "A very very very very long message which suites on two lines",
avatarData = AvatarData("!id", "R", size = AvatarSize.RoomListItem),
id = "!roomId:domain",
roomId = RoomId("!roomId:domain")
),
RoomListRoomSummary(
aRoomListRoomSummary(
name = "Room#2",
hasUnread = false,
timestamp = "14:16",
lastMessage = "A short message",
avatarData = AvatarData("!id", "Z", size = AvatarSize.RoomListItem),
id = "!roomId2:domain",
roomId = RoomId("!roomId2:domain")
),
RoomListRoomSummaryPlaceholders.create("!roomId2:domain"),
RoomListRoomSummaryPlaceholders.create("!roomId3:domain"),
aRoomListRoomSummary(
id = "!roomId3:domain",
isPlaceholder = true,
),
aRoomListRoomSummary(
id = "!roomId4:domain",
isPlaceholder = true,
),
)
}

View file

@ -17,16 +17,9 @@
package io.element.android.features.roomlist.impl.datasource
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders
import io.element.android.libraries.androidutils.diff.DiffCacheUpdater
import io.element.android.libraries.androidutils.diff.MutableListDiffCache
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.orEmpty
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.eventformatter.api.RoomLastMessageFormatter
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.RoomSummary
@ -49,8 +42,7 @@ import kotlin.time.Duration.Companion.seconds
class RoomListDataSource @Inject constructor(
private val roomListService: RoomListService,
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
private val roomLastMessageFormatter: RoomLastMessageFormatter,
private val roomListRoomSummaryFactory: RoomListRoomSummaryFactory,
private val coroutineDispatchers: CoroutineDispatchers,
private val notificationSettingsService: NotificationSettingsService,
private val appScope: CoroutineScope,
@ -121,7 +113,7 @@ class RoomListDataSource @Inject constructor(
private suspend fun buildAndEmitAllRooms(roomSummaries: List<RoomSummary>) {
if (diffCache.isEmpty()) {
_allRooms.emit(
RoomListRoomSummaryPlaceholders.createFakeList(16).toImmutableList()
roomListRoomSummaryFactory.createFakeList()
)
} else {
val roomListRoomSummaries = ArrayList<RoomListRoomSummary>()
@ -141,33 +133,10 @@ class RoomListDataSource @Inject constructor(
private fun buildAndCacheItem(roomSummaries: List<RoomSummary>, index: Int): RoomListRoomSummary? {
val roomListRoomSummary = when (val roomSummary = roomSummaries.getOrNull(index)) {
is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
is RoomSummary.Filled -> {
val avatarData = AvatarData(
id = roomSummary.identifier(),
name = roomSummary.details.name,
url = roomSummary.details.avatarUrl,
size = AvatarSize.RoomListItem,
)
val roomIdentifier = roomSummary.identifier()
RoomListRoomSummary(
id = roomSummary.identifier(),
roomId = RoomId(roomIdentifier),
name = roomSummary.details.name,
hasUnread = roomSummary.details.unreadNotificationCount > 0,
timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
lastMessage = roomSummary.details.lastMessage?.let { message ->
roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
}.orEmpty(),
avatarData = avatarData,
userDefinedNotificationMode = roomSummary.details.userDefinedNotificationMode,
hasRoomCall = roomSummary.details.hasRoomCall,
isDm = roomSummary.details.isDm,
)
}
is RoomSummary.Empty -> roomListRoomSummaryFactory.createPlaceholder(roomSummary.identifier)
is RoomSummary.Filled -> roomListRoomSummaryFactory.create(roomSummary)
null -> null
}
diffCache[index] = roomListRoomSummary
return roomListRoomSummary
}

View file

@ -0,0 +1,81 @@
/*
* 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
*
* http://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.features.roomlist.impl.datasource
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.libraries.core.extensions.orEmpty
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.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
class RoomListRoomSummaryFactory @Inject constructor(
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
private val roomLastMessageFormatter: RoomLastMessageFormatter,
) {
fun createPlaceholder(id: String): RoomListRoomSummary {
return RoomListRoomSummary(
id = id,
roomId = RoomId("!aRoom:domain"),
isPlaceholder = true,
name = "Short name",
timestamp = "hh:mm",
lastMessage = "Last message for placeholder",
avatarData = AvatarData(id, "S", size = AvatarSize.RoomListItem),
hasUnread = false,
userDefinedNotificationMode = null,
hasRoomCall = false,
isDm = false,
)
}
fun createFakeList(): ImmutableList<RoomListRoomSummary> {
return List(16) {
createPlaceholder("!fakeRoom$it:domain")
}.toImmutableList()
}
fun create(roomSummary: RoomSummary.Filled): RoomListRoomSummary {
val roomIdentifier = roomSummary.identifier()
val avatarData = AvatarData(
id = roomIdentifier,
name = roomSummary.details.name,
url = roomSummary.details.avatarUrl,
size = AvatarSize.RoomListItem,
)
return RoomListRoomSummary(
id = roomIdentifier,
roomId = RoomId(roomIdentifier),
name = roomSummary.details.name,
hasUnread = roomSummary.details.unreadNotificationCount > 0,
timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
lastMessage = roomSummary.details.lastMessage?.let { message ->
roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
}.orEmpty(),
avatarData = avatarData,
isPlaceholder = false,
userDefinedNotificationMode = roomSummary.details.userDefinedNotificationMode,
hasRoomCall = roomSummary.details.hasRoomCall,
isDm = roomSummary.details.isDm,
)
}
}

View file

@ -18,7 +18,6 @@ package io.element.android.features.roomlist.impl.model
import androidx.compose.runtime.Immutable
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
@ -26,13 +25,13 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode
data class RoomListRoomSummary(
val id: String,
val roomId: RoomId,
val name: String = "",
val hasUnread: Boolean = false,
val timestamp: String? = null,
val lastMessage: CharSequence? = null,
val avatarData: AvatarData = AvatarData(id, name, size = AvatarSize.RoomListItem),
val isPlaceholder: Boolean = false,
val userDefinedNotificationMode: RoomNotificationMode? = null,
val hasRoomCall: Boolean = false,
val isDm: Boolean = false,
val name: String,
val hasUnread: Boolean,
val timestamp: String?,
val lastMessage: CharSequence?,
val avatarData: AvatarData,
val isPlaceholder: Boolean,
val userDefinedNotificationMode: RoomNotificationMode?,
val hasRoomCall: Boolean,
val isDm: Boolean,
)

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2023 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
*
* http://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.features.roomlist.impl.model
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.RoomId
object RoomListRoomSummaryPlaceholders {
fun create(id: String): RoomListRoomSummary {
return RoomListRoomSummary(
id = id,
roomId = RoomId("!aRoom:domain"),
isPlaceholder = true,
name = "Short name",
timestamp = "hh:mm",
lastMessage = "Last message for placeholder",
avatarData = AvatarData(id, "S", size = AvatarSize.RoomListItem)
)
}
fun createFakeList(size: Int): List<RoomListRoomSummary> {
return mutableListOf<RoomListRoomSummary>().apply {
repeat(size) {
add(create("!fakeRoom$it:domain"))
}
}
}
}

View file

@ -28,10 +28,10 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSu
aRoomListRoomSummary(),
aRoomListRoomSummary(lastMessage = null),
aRoomListRoomSummary(hasUnread = true, notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY),
aRoomListRoomSummary(timestamp = "88:88", notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY),
aRoomListRoomSummary(timestamp = "88:88", notificationMode = RoomNotificationMode.MUTE),
aRoomListRoomSummary(timestamp = "88:88", hasUnread = true),
aRoomListRoomSummary(isPlaceholder = true, timestamp = "88:88"),
aRoomListRoomSummary(notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY),
aRoomListRoomSummary(notificationMode = RoomNotificationMode.MUTE),
aRoomListRoomSummary(hasUnread = true),
aRoomListRoomSummary(isPlaceholder = true),
aRoomListRoomSummary(
name = "A very long room name that should be truncated",
lastMessage = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
@ -44,23 +44,27 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSu
)
}
fun aRoomListRoomSummary(
lastMessage: String? = "Last message",
notificationMode: RoomNotificationMode? = null,
hasUnread: Boolean = false,
timestamp: String? = "88:88",
hasRoomCall: Boolean = false,
isPlaceholder: Boolean = false,
internal fun aRoomListRoomSummary(
id: String = "!roomId:domain",
name: String = "Room name",
hasUnread: Boolean = false,
lastMessage: String? = "Last message",
timestamp: String? = lastMessage?.let { "88:88" },
isPlaceholder: Boolean = false,
notificationMode: RoomNotificationMode? = null,
hasRoomCall: Boolean = false,
avatarData: AvatarData = AvatarData(id, name, size = AvatarSize.RoomListItem),
isDm: Boolean = false,
) = RoomListRoomSummary(
id = "!roomId",
roomId = RoomId("!roomId:domain"),
id = id,
roomId = RoomId(id),
name = name,
hasUnread = hasUnread,
timestamp = timestamp,
lastMessage = lastMessage,
avatarData = AvatarData("!roomId", "Room name", size = AvatarSize.RoomListItem),
avatarData = avatarData,
isPlaceholder = isPlaceholder,
userDefinedNotificationMode = notificationMode,
hasRoomCall = hasRoomCall,
isDm = isDm,
)

View file

@ -28,8 +28,8 @@ import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
import io.element.android.features.roomlist.impl.datasource.FakeInviteDataSource
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.aRoomListRoomSummary
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
import io.element.android.libraries.designsystem.components.avatar.AvatarData
@ -316,7 +316,7 @@ class RoomListPresenterTests {
skipItems(1)
val initialState = awaitItem()
val summary = aRoomListRoomSummary()
val summary = aRoomListRoomSummary
initialState.eventSink(RoomListEvents.ShowContextMenu(summary))
val shownState = awaitItem()
@ -336,7 +336,7 @@ class RoomListPresenterTests {
skipItems(1)
val initialState = awaitItem()
val summary = aRoomListRoomSummary()
val summary = aRoomListRoomSummary
initialState.eventSink(RoomListEvents.ShowContextMenu(summary))
val shownState = awaitItem()
@ -415,9 +415,11 @@ class RoomListPresenterTests {
inviteStateDataSource = inviteStateDataSource,
leaveRoomPresenter = leaveRoomPresenter,
roomListDataSource = RoomListDataSource(
client.roomListService,
lastMessageTimestampFormatter,
roomLastMessageFormatter,
roomListService = client.roomListService,
roomListRoomSummaryFactory = RoomListRoomSummaryFactory(
lastMessageTimestampFormatter = lastMessageTimestampFormatter,
roomLastMessageFormatter = roomLastMessageFormatter,
),
coroutineDispatchers = testCoroutineDispatchers(),
notificationSettingsService = client.notificationSettingsService(),
appScope = coroutineScope
@ -443,4 +445,7 @@ private val aRoomListRoomSummary = RoomListRoomSummary(
lastMessage = "",
avatarData = AvatarData(id = A_ROOM_ID.value, name = A_ROOM_NAME, size = AvatarSize.RoomListItem),
isPlaceholder = false,
userDefinedNotificationMode = null,
hasRoomCall = false,
isDm = false,
)

View file

@ -27,6 +27,7 @@ import io.element.android.features.roomlist.impl.RoomListPresenter
import io.element.android.features.roomlist.impl.RoomListView
import io.element.android.features.roomlist.impl.datasource.DefaultInviteStateDataSource
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
import io.element.android.features.roomlist.impl.datasource.RoomListRoomSummaryFactory
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.dateformatter.impl.DateFormatters
import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimestampFormatter
@ -76,12 +77,20 @@ class RoomListScreen(
leaveRoomPresenter = LeaveRoomPresenterImpl(matrixClient, RoomMembershipObserver(), coroutineDispatchers),
roomListDataSource = RoomListDataSource(
roomListService = matrixClient.roomListService,
lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters),
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(
sp = stringProvider,
roomMembershipContentFormatter = RoomMembershipContentFormatter(matrixClient, stringProvider),
profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider),
stateContentFormatter = StateContentFormatter(stringProvider),
roomListRoomSummaryFactory = RoomListRoomSummaryFactory(
lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(
localDateTimeProvider = dateTimeProvider,
dateFormatters = dateFormatters
),
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(
sp = stringProvider,
roomMembershipContentFormatter = RoomMembershipContentFormatter(
matrixClient = matrixClient,
sp = stringProvider
),
profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider),
stateContentFormatter = StateContentFormatter(stringProvider),
),
),
coroutineDispatchers = coroutineDispatchers,
notificationSettingsService = matrixClient.notificationSettingsService(),

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cf71e045231208529eb524abce7b246a0ac6283c422f9695d560071fda7085f2
size 12820
oid sha256:57e0c90fa627974aba0d62363906bcc7911a6ada7d9d7db00db0df1d51fed54c
size 12911

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34972a9257a96b4ac77edf06e5f29ebc2d8e581dcaef131ecba9730cb0f48c1c
size 10176
oid sha256:fcf2f25bbcb2f43d70a8a3228ba97426dbb98b533b74dd13ef6459f96a711db5
size 8877

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ef683a74c785d57190508952f18ed5c669ff32a38cbc4f5b66d02330115843b
size 14103
oid sha256:d1d8c374c7c3f8ad66b3936e8eda3161a93281aa3107027516ab671958f10980
size 14187

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:28b9ccdc834d9643694f72ce1c01fb988685a7b0300912bada032be1e426b467
size 13539
oid sha256:352b75021319958e86affa3d44c38cf72151d32796d70a0920ef63d73984ad72
size 13627

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ada109a4f7b3b3bfae402eaddcdc240fd5f08aad41e7f1e70878baa8e257f2b
size 13288
oid sha256:14e33bc8286952b7608adfa9864a1649c817992d35e9e0de8edc9957b8f4a6c6
size 13388

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b18e5991be70ab16f983b929d08e566c85483e6d79f4de7a4191a800f4cae35
size 13305
oid sha256:d8442308cab3c75f64559bcd67dc39ce6d95a546ae33a7eb885c775349ab36f5
size 13378

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:950e80e7e4be904967d35aaffe196548fa4f5ae9afdf2b86e65611ccdb14bf80
size 22203
oid sha256:a1dfe518ad4ee77539f017a39c684e0ce7bfb733a33161639fdf606a188b13d0
size 22406

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9c90561dc7355b1917ebe96bebc717de14aac5c4897ef3e8dd0b2aca8c51c362
size 13610
oid sha256:b044aa278ad070f75ffcf3d0d895c93bd784eb9c3e19e6aa842bbc1170bc92b4
size 13688

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7fcbdfc3e45fa0f111cc384c54c40980913e52ff755e9380fba262a4c659a5c5
size 12715
oid sha256:1d914ad609cda9e94ba793027c17f3b7289fec71e65e1880b837615203f7718e
size 12907

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:063d4415a254eef251f4c0f63827062259f76c0d8a1e309e64a4e7376fc04a9b
size 10101
oid sha256:e05f294579b4420e6671f268d10fae8fb49e93e5ae5b0f0d4548f7cdfeb82742
size 8928

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fd40343c425b1c7499e72d0d4119f36d06200b54e72034f97c172e5c2dad57af
size 13756
oid sha256:4fcbb393a93d6a04e0415d2bb7bed4c002ec82907fbccf6070a4880a6b208e3c
size 13942

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:584487de266a9b3e9cdefee0378023a946b309e19b09acffc5bf1a08dfc0d390
size 13335
oid sha256:811d95287c12d59fa1014b62ac8ae755f6355dbbd0700c2adc331293adfcc7a4
size 13535

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8bda8b4f6a84b37548e3926f180ddb282d220b8ff581a60e69e0e309ad3ae06a
size 13119
oid sha256:435737296b59d70210fc93ba70d2fa228ba4bab645fb9a6366609197a97f87c0
size 13305

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2c17e87f046bbdd7a0300262737508cd1ffab4b54da9df7e7a7803237e070243
size 13032
oid sha256:d5a2d15ec2b36255963bc2514df0ff2dc8588c177d06c5431ca457af4de916e6
size 13210

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:06df893c35c81609b46317f4de733db0bed4f5888eac189d09078ad7bd411cf0
size 21340
oid sha256:b5f274b1ad4ca31aaf585d34ce48c2898fbb9ada8294b211fe1440a097da21f6
size 21626

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5efc2679fc43f952d1a41ffff7c5400baeb6f46e7faba0faef6409ea178c08de
size 13319
oid sha256:a13837fffa77d7f95d6f4f163fb2e674070777caf02cce74b6a52e91f3103224
size 13500