Iterate on indicators on room list #2079
This commit is contained in:
parent
98d9a65a1f
commit
f2c989cbd6
25 changed files with 255 additions and 169 deletions
|
|
@ -71,7 +71,7 @@ internal fun aRoomListRoomSummaryList(): ImmutableList<RoomListRoomSummary> {
|
|||
return persistentListOf(
|
||||
RoomListRoomSummary(
|
||||
name = "Room",
|
||||
hasUnread = true,
|
||||
numUnreadMessages = 1,
|
||||
timestamp = "14:18",
|
||||
lastMessage = "A very very very very long message which suites on two lines",
|
||||
avatarData = AvatarData("!id", "R", size = AvatarSize.RoomListItem),
|
||||
|
|
@ -80,7 +80,7 @@ internal fun aRoomListRoomSummaryList(): ImmutableList<RoomListRoomSummary> {
|
|||
),
|
||||
RoomListRoomSummary(
|
||||
name = "Room#2",
|
||||
hasUnread = false,
|
||||
numUnreadMessages = 0,
|
||||
timestamp = "14:16",
|
||||
lastMessage = "A short message",
|
||||
avatarData = AvatarData("!id", "Z", size = AvatarSize.RoomListItem),
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import io.element.android.compound.theme.ElementTheme
|
|||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
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.isTimestampHighlighted
|
||||
import io.element.android.libraries.core.extensions.orEmpty
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
|
|
@ -141,7 +142,7 @@ private fun RowScope.NameAndTimestampRow(room: RoomListRoomSummary) {
|
|||
Text(
|
||||
text = room.timestamp ?: "",
|
||||
style = ElementTheme.typography.fontBodySmMedium,
|
||||
color = if (room.hasUnread) {
|
||||
color = if (room.isTimestampHighlighted()) {
|
||||
ElementTheme.colors.unreadIndicator
|
||||
} else {
|
||||
MaterialTheme.roomListRoomMessageDate()
|
||||
|
|
@ -173,40 +174,69 @@ private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) {
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
// Video call
|
||||
if (room.hasOngoingCall) {
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
imageVector = CompoundIcons.VideoCallSolid,
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.unreadIndicator,
|
||||
)
|
||||
}
|
||||
NotificationIcon(room)
|
||||
if (room.hasUnread) {
|
||||
UnreadIndicatorAtom()
|
||||
}
|
||||
OnGoingCallIcon(room)
|
||||
// Other indicators
|
||||
NotificationIcons(room)
|
||||
}
|
||||
}
|
||||
|
||||
@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(
|
||||
modifier = Modifier.size(16.dp),
|
||||
contentDescription = null,
|
||||
imageVector = CompoundIcons.Mention,
|
||||
tint = tint,
|
||||
)
|
||||
RoomNotificationMode.MUTE ->
|
||||
private fun OnGoingCallIcon(room: RoomListRoomSummary) {
|
||||
if (room.hasRoomCall) {
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
imageVector = CompoundIcons.VideoCallSolid,
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.unreadIndicator,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.NotificationIcons(room: RoomListRoomSummary) {
|
||||
when (room.userDefinedNotificationMode) {
|
||||
null,
|
||||
RoomNotificationMode.ALL_MESSAGES -> {
|
||||
if (room.numUnreadMentions > 0) {
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
contentDescription = null,
|
||||
imageVector = CompoundIcons.Mention,
|
||||
tint = ElementTheme.colors.unreadIndicator,
|
||||
)
|
||||
}
|
||||
if (room.numUnreadMessages > 0) {
|
||||
UnreadIndicatorAtom()
|
||||
}
|
||||
}
|
||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> {
|
||||
if (room.numUnreadMentions > 0) {
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
contentDescription = null,
|
||||
imageVector = CompoundIcons.Mention,
|
||||
tint = ElementTheme.colors.unreadIndicator,
|
||||
)
|
||||
if (room.numUnreadMessages > 0) {
|
||||
UnreadIndicatorAtom()
|
||||
}
|
||||
} else {
|
||||
if (room.numUnreadMessages > 0) {
|
||||
UnreadIndicatorAtom(color = ElementTheme.colors.iconQuaternary)
|
||||
}
|
||||
}
|
||||
}
|
||||
RoomNotificationMode.MUTE -> {
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
contentDescription = null,
|
||||
imageVector = CompoundIcons.NotificationsSolidOff,
|
||||
tint = tint,
|
||||
tint = ElementTheme.colors.iconQuaternary,
|
||||
)
|
||||
if (room.numUnreadMessages > 0) {
|
||||
UnreadIndicatorAtom(color = ElementTheme.colors.iconQuaternary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ class RoomListDataSource @Inject constructor(
|
|||
val avatarData = AvatarData(
|
||||
id = roomSummary.identifier(),
|
||||
name = roomSummary.details.name,
|
||||
url = roomSummary.details.avatarURLString,
|
||||
url = roomSummary.details.avatarUrl,
|
||||
size = AvatarSize.RoomListItem,
|
||||
)
|
||||
val roomIdentifier = roomSummary.identifier()
|
||||
|
|
@ -154,14 +154,15 @@ class RoomListDataSource @Inject constructor(
|
|||
id = roomSummary.identifier(),
|
||||
roomId = RoomId(roomIdentifier),
|
||||
name = roomSummary.details.name,
|
||||
hasUnread = roomSummary.details.unreadNotificationCount > 0,
|
||||
numUnreadMessages = roomSummary.details.numUnreadMessages,
|
||||
numUnreadMentions = roomSummary.details.numUnreadMentions,
|
||||
timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
|
||||
lastMessage = roomSummary.details.lastMessage?.let { message ->
|
||||
roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
|
||||
}.orEmpty(),
|
||||
avatarData = avatarData,
|
||||
notificationMode = roomSummary.details.notificationMode,
|
||||
hasOngoingCall = roomSummary.details.hasOngoingCall,
|
||||
userDefinedNotificationMode = roomSummary.details.userDefinedNotificationMode,
|
||||
hasRoomCall = roomSummary.details.hasRoomCall,
|
||||
)
|
||||
}
|
||||
null -> null
|
||||
|
|
|
|||
|
|
@ -27,11 +27,22 @@ 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 notificationMode: RoomNotificationMode? = null,
|
||||
val hasOngoingCall: Boolean = false,
|
||||
val userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
val numUnreadMessages: Int = 0,
|
||||
val numUnreadMentions: Int = 0,
|
||||
val hasRoomCall: Boolean = false,
|
||||
)
|
||||
|
||||
fun RoomListRoomSummary.isTimestampHighlighted(): Boolean {
|
||||
return hasRoomCall ||
|
||||
when (userDefinedNotificationMode) {
|
||||
null,
|
||||
RoomNotificationMode.ALL_MESSAGES -> numUnreadMessages > 0 || numUnreadMentions > 0
|
||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> numUnreadMentions > 0
|
||||
RoomNotificationMode.MUTE -> false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,32 +25,75 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
|||
open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSummary> {
|
||||
override val values: Sequence<RoomListRoomSummary>
|
||||
get() = sequenceOf(
|
||||
aRoomListRoomSummary(),
|
||||
aRoomListRoomSummary().copy(lastMessage = null),
|
||||
aRoomListRoomSummary().copy(hasUnread = true, notificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY),
|
||||
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(isPlaceholder = true, timestamp = "88:88"),
|
||||
aRoomListRoomSummary().copy(
|
||||
name = "A very long room name that should be truncated",
|
||||
lastMessage = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
|
||||
" ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea com" +
|
||||
"modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
|
||||
timestamp = "yesterday",
|
||||
hasUnread = true,
|
||||
listOf(
|
||||
aRoomListRoomSummary(isPlaceholder = true),
|
||||
aRoomListRoomSummary(timestamp = null),
|
||||
aRoomListRoomSummary(lastMessage = "Last message"),
|
||||
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" +
|
||||
" ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea com" +
|
||||
"modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
|
||||
timestamp = "yesterday",
|
||||
numUnreadMessages = 1,
|
||||
),
|
||||
),
|
||||
aRoomListRoomSummary().copy(hasUnread = true, hasOngoingCall = true),
|
||||
)
|
||||
listOf(false, true).map { hasCall ->
|
||||
listOf(
|
||||
RoomNotificationMode.ALL_MESSAGES,
|
||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||
RoomNotificationMode.MUTE,
|
||||
).map { roomNotificationMode ->
|
||||
listOf(
|
||||
aRoomListRoomSummary(
|
||||
name = roomNotificationMode.name,
|
||||
lastMessage = "No activity" + if (hasCall) ", call" else "",
|
||||
notificationMode = roomNotificationMode,
|
||||
numUnreadMessages = 0,
|
||||
numUnreadMentions = 0,
|
||||
hasOngoingCall = hasCall,
|
||||
),
|
||||
aRoomListRoomSummary(
|
||||
name = roomNotificationMode.name,
|
||||
lastMessage = "New messages" + if (hasCall) ", call" else "",
|
||||
notificationMode = roomNotificationMode,
|
||||
numUnreadMessages = 1,
|
||||
numUnreadMentions = 0,
|
||||
hasOngoingCall = hasCall,
|
||||
),
|
||||
aRoomListRoomSummary(
|
||||
name = roomNotificationMode.name,
|
||||
lastMessage = "New messages, mentions" + if (hasCall) ", call" else "",
|
||||
notificationMode = roomNotificationMode,
|
||||
numUnreadMessages = 1,
|
||||
numUnreadMentions = 1,
|
||||
hasOngoingCall = hasCall,
|
||||
),
|
||||
)
|
||||
}.flatten()
|
||||
}.flatten(),
|
||||
).flatten()
|
||||
}
|
||||
|
||||
fun aRoomListRoomSummary() = RoomListRoomSummary(
|
||||
fun aRoomListRoomSummary(
|
||||
lastMessage: String? = null,
|
||||
notificationMode: RoomNotificationMode? = null,
|
||||
numUnreadMessages: Int = 0,
|
||||
numUnreadMentions: Int = 0,
|
||||
timestamp: String? = "88:88",
|
||||
hasOngoingCall: Boolean = false,
|
||||
isPlaceholder: Boolean = false,
|
||||
name: String = "Room name",
|
||||
) = RoomListRoomSummary(
|
||||
id = "!roomId",
|
||||
roomId = RoomId("!roomId:domain"),
|
||||
name = "Room name",
|
||||
hasUnread = false,
|
||||
timestamp = null,
|
||||
lastMessage = "Last message",
|
||||
name = name,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
timestamp = timestamp,
|
||||
lastMessage = lastMessage,
|
||||
avatarData = AvatarData("!roomId", "Room name", size = AvatarSize.RoomListItem),
|
||||
isPlaceholder = false,
|
||||
isPlaceholder = isPlaceholder,
|
||||
userDefinedNotificationMode = notificationMode,
|
||||
hasRoomCall = hasOngoingCall,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class RoomListPresenterTests {
|
|||
fun `present - should start with no user and then load user with error`() = runTest {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
userDisplayName = Result.failure(AN_EXCEPTION),
|
||||
userAvatarURLString = Result.failure(AN_EXCEPTION),
|
||||
userAvatarUrl = Result.failure(AN_EXCEPTION),
|
||||
)
|
||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||
val presenter = createRoomListPresenter(client = matrixClient, coroutineScope = scope)
|
||||
|
|
@ -385,11 +385,11 @@ class RoomListPresenterTests {
|
|||
notificationSettingsService.setRoomNotificationMode(A_ROOM_ID, userDefinedMode)
|
||||
|
||||
val updatedState = consumeItemsUntilPredicate { state ->
|
||||
state.roomList.any { it.id == A_ROOM_ID.value && it.notificationMode == userDefinedMode }
|
||||
state.roomList.any { it.id == A_ROOM_ID.value && it.userDefinedNotificationMode == userDefinedMode }
|
||||
}.last()
|
||||
|
||||
val room = updatedState.roomList.find { it.id == A_ROOM_ID.value }
|
||||
assertThat(room?.notificationMode).isEqualTo(userDefinedMode)
|
||||
assertThat(room?.userDefinedNotificationMode).isEqualTo(userDefinedMode)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
scope.cancel()
|
||||
}
|
||||
|
|
@ -439,7 +439,7 @@ private val aRoomListRoomSummary = RoomListRoomSummary(
|
|||
id = A_ROOM_ID.value,
|
||||
roomId = A_ROOM_ID,
|
||||
name = A_ROOM_NAME,
|
||||
hasUnread = true,
|
||||
numUnreadMessages = 1,
|
||||
timestamp = A_FORMATTED_DATE,
|
||||
lastMessage = "",
|
||||
avatarData = AvatarData(id = A_ROOM_ID.value, name = A_ROOM_NAME, size = AvatarSize.RoomListItem),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue