Implement month separator for the Gallery.

Improve day separator rendering in the timeline.
Use Today, Yesterday, and the name of the day if less than 7 days and do not render the year for the current year.
Improve date format for the media viewer.
Rework how date and time are computed.
ActionListView: Time can take more space, so update the layout.
This commit is contained in:
Benoit Marty 2024-12-11 23:43:20 +01:00 committed by Benoit Marty
parent 5515c938d4
commit da272ddb07
60 changed files with 1271 additions and 351 deletions

View file

@ -10,7 +10,8 @@ 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.RoomSummaryDisplayType
import io.element.android.libraries.core.extensions.orEmpty
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.api.DateFormatter
import io.element.android.libraries.dateformatter.api.DateFormatterMode
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
@ -22,7 +23,7 @@ import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
class RoomListRoomSummaryFactory @Inject constructor(
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
private val dateFormatter: DateFormatter,
private val roomLastMessageFormatter: RoomLastMessageFormatter,
) {
fun create(roomSummary: RoomSummary): RoomListRoomSummary {
@ -36,7 +37,11 @@ class RoomListRoomSummaryFactory @Inject constructor(
numberOfUnreadMentions = roomInfo.numUnreadMentions,
numberOfUnreadNotifications = roomInfo.numUnreadNotifications,
isMarkedUnread = roomInfo.isMarkedUnread,
timestamp = lastMessageTimestampFormatter.format(roomSummary.lastMessageTimestamp),
timestamp = dateFormatter.format(
timestamp = roomSummary.lastMessageTimestamp,
mode = DateFormatterMode.TimeOrDate,
useRelative = true,
),
lastMessage = roomSummary.lastMessage?.let { message ->
roomLastMessageFormatter.format(message.event, roomInfo.isDm)
}.orEmpty(),

View file

@ -31,9 +31,8 @@ import io.element.android.features.roomlist.impl.search.RoomListSearchState
import io.element.android.features.roomlist.impl.search.aRoomListSearchState
import io.element.android.libraries.androidutils.system.DateTimeObserver
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.api.DateFormatter
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
@ -188,6 +187,7 @@ class RoomListPresenterTest {
createRoomListRoomSummary(
numberOfUnreadMentions = 1,
numberOfUnreadMessages = 2,
timestamp = "0 TimeOrDate true",
)
)
cancelAndIgnoreRemainingEvents()
@ -633,9 +633,7 @@ class RoomListPresenterTest {
networkMonitor: NetworkMonitor = FakeNetworkMonitor(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
leaveRoomState: LeaveRoomState = aLeaveRoomState(),
lastMessageTimestampFormatter: LastMessageTimestampFormatter = FakeLastMessageTimestampFormatter().apply {
givenFormat(A_FORMATTED_DATE)
},
dateFormatter: DateFormatter = FakeDateFormatter(),
roomLastMessageFormatter: RoomLastMessageFormatter = FakeRoomLastMessageFormatter(),
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
@ -652,7 +650,7 @@ class RoomListPresenterTest {
roomListDataSource = RoomListDataSource(
roomListService = client.roomListService,
roomListRoomSummaryFactory = aRoomListRoomSummaryFactory(
lastMessageTimestampFormatter = lastMessageTimestampFormatter,
dateFormatter = dateFormatter,
roomLastMessageFormatter = roomLastMessageFormatter,
),
coroutineDispatchers = testCoroutineDispatchers(),

View file

@ -11,7 +11,7 @@ import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomlist.impl.FakeDateTimeObserver
import io.element.android.libraries.androidutils.system.DateTimeObserver
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.room.aRoomSummary
@ -30,12 +30,12 @@ class RoomListDataSourceTest {
postAllRooms(listOf(aRoomSummary()))
}
val dateTimeObserver = FakeDateTimeObserver()
val lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter()
lastMessageTimestampFormatter.givenFormat("Today")
var dateFormatterResult = "Today"
val dateFormatter = FakeDateFormatter({ _, _, _ -> dateFormatterResult })
val roomListDataSource = createRoomListDataSource(
roomListService = roomListService,
roomListRoomSummaryFactory = aRoomListRoomSummaryFactory(
lastMessageTimestampFormatter = lastMessageTimestampFormatter,
dateFormatter = dateFormatter,
),
dateTimeObserver = dateTimeObserver,
)
@ -47,7 +47,7 @@ class RoomListDataSourceTest {
val initialRoomList = awaitItem()
assertThat(initialRoomList).isNotEmpty()
assertThat(initialRoomList.first().timestamp).isEqualTo("Today")
lastMessageTimestampFormatter.givenFormat("Yesterday")
dateFormatterResult = "Yesterday"
// Trigger a date change
dateTimeObserver.given(DateTimeObserver.Event.DateChanged(Instant.MIN, Instant.now()))
// Check there is a new list and it's not the same as the previous one
@ -64,12 +64,12 @@ class RoomListDataSourceTest {
postAllRooms(listOf(aRoomSummary()))
}
val dateTimeObserver = FakeDateTimeObserver()
val lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter()
lastMessageTimestampFormatter.givenFormat("Today")
var dateFormatterResult = "Today"
val dateFormatter = FakeDateFormatter({ _, _, _ -> dateFormatterResult })
val roomListDataSource = createRoomListDataSource(
roomListService = roomListService,
roomListRoomSummaryFactory = aRoomListRoomSummaryFactory(
lastMessageTimestampFormatter = lastMessageTimestampFormatter,
dateFormatter = dateFormatter,
),
dateTimeObserver = dateTimeObserver,
)
@ -80,7 +80,7 @@ class RoomListDataSourceTest {
val initialRoomList = awaitItem()
assertThat(initialRoomList).isNotEmpty()
assertThat(initialRoomList.first().timestamp).isEqualTo("Today")
lastMessageTimestampFormatter.givenFormat("Yesterday")
dateFormatterResult = "Yesterday"
// Trigger a timezone change
dateTimeObserver.given(DateTimeObserver.Event.TimeZoneChanged)
// Check there is a new list and it's not the same as the previous one

View file

@ -7,13 +7,14 @@
package io.element.android.features.roomlist.impl.datasource
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.api.DateFormatter
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
fun aRoomListRoomSummaryFactory(
lastMessageTimestampFormatter: LastMessageTimestampFormatter = LastMessageTimestampFormatter { _ -> "Today" },
dateFormatter: DateFormatter = FakeDateFormatter { _, _, _ -> "Today" },
roomLastMessageFormatter: RoomLastMessageFormatter = RoomLastMessageFormatter { _, _ -> "Hey" }
) = RoomListRoomSummaryFactory(
lastMessageTimestampFormatter = lastMessageTimestampFormatter,
dateFormatter = dateFormatter,
roomLastMessageFormatter = roomLastMessageFormatter
)

View file

@ -8,7 +8,6 @@
package io.element.android.features.roomlist.impl.model
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.dateformatter.test.A_FORMATTED_DATE
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.room.RoomNotificationMode
@ -84,6 +83,7 @@ internal fun createRoomListRoomSummary(
isFavorite: Boolean = false,
displayType: RoomSummaryDisplayType = RoomSummaryDisplayType.ROOM,
heroes: List<AvatarData> = emptyList(),
timestamp: String? = null,
) = RoomListRoomSummary(
id = A_ROOM_ID.value,
roomId = A_ROOM_ID,
@ -92,7 +92,7 @@ internal fun createRoomListRoomSummary(
numberOfUnreadMessages = numberOfUnreadMessages,
numberOfUnreadNotifications = numberOfUnreadNotifications,
isMarkedUnread = isMarkedUnread,
timestamp = A_FORMATTED_DATE,
timestamp = timestamp,
lastMessage = "",
avatarData = AvatarData(id = A_ROOM_ID.value, name = A_ROOM_NAME, size = AvatarSize.RoomListItem),
displayType = displayType,

View file

@ -12,7 +12,7 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomlist.impl.datasource.aRoomListRoomSummaryFactory
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
@ -143,7 +143,7 @@ fun TestScope.createRoomListSearchPresenter(
dataSource = RoomListSearchDataSource(
roomListService = roomListService,
roomSummaryFactory = aRoomListRoomSummaryFactory(
lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(),
dateFormatter = FakeDateFormatter(),
roomLastMessageFormatter = FakeRoomLastMessageFormatter(),
),
coroutineDispatchers = testCoroutineDispatchers(),