Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement month separator for the Gallery, and improve date rendering. #4026

Merged
merged 10 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .maestro/tests/roomList/createAndDeleteRoom.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ appId: ${MAESTRO_APP_ID}
# assert there's 1 member and 2 invitees
- tapOn: "Back"
- scroll
- scroll
- tapOn: "Leave room"
- tapOn: "Leave"
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.overlay.Overlay
import io.element.android.libraries.architecture.overlay.operation.hide
import io.element.android.libraries.architecture.overlay.operation.show
import io.element.android.libraries.dateformatter.api.DateFormatter
import io.element.android.libraries.dateformatter.api.DateFormatterMode
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId
Expand Down Expand Up @@ -97,6 +99,7 @@ class MessagesFlowNode @AssistedInject constructor(
private val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider,
private val timelineController: TimelineController,
private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint,
private val dateFormatter: DateFormatter,
) : BaseFlowNode<MessagesFlowNode.NavTarget>(
backstack = BackStack(
initialElement = plugins.filterIsInstance<MessagesEntryPoint.Params>().first().initialTarget.toNavTarget(),
Expand Down Expand Up @@ -436,7 +439,14 @@ class MessagesFlowNode @AssistedInject constructor(
senderId = event.senderId,
senderName = event.safeSenderName,
senderAvatar = event.senderAvatar.url,
dateSent = event.sentTime,
dateSent = dateFormatter.format(
event.sentTimeMillis,
mode = DateFormatterMode.Day,
),
dateSentFull = dateFormatter.format(
timestamp = event.sentTimeMillis,
mode = DateFormatterMode.Full,
),
),
mediaSource = mediaSource,
thumbnailSource = thumbnailSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import io.element.android.features.messages.impl.timeline.model.event.canBeCopie
import io.element.android.features.messages.impl.timeline.model.event.canBeForwarded
import io.element.android.features.messages.impl.timeline.model.event.canReact
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.dateformatter.api.DateFormatter
import io.element.android.libraries.dateformatter.api.DateFormatterMode
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
Expand Down Expand Up @@ -64,6 +66,7 @@ class DefaultActionListPresenter @AssistedInject constructor(
private val room: MatrixRoom,
private val userSendFailureFactory: VerifiedUserSendFailureFactory,
private val featureFlagService: FeatureFlagService,
private val dateFormatter: DateFormatter,
) : ActionListPresenter {
@AssistedFactory
@ContributesBinding(RoomScope::class)
Expand Down Expand Up @@ -131,6 +134,11 @@ class DefaultActionListPresenter @AssistedInject constructor(
if (actions.isNotEmpty() || displayEmojiReactions || verifiedUserSendFailure != VerifiedUserSendFailure.None) {
target.value = ActionListState.Target.Success(
event = timelineItem,
sentTimeFull = dateFormatter.format(
timelineItem.sentTimeMillis,
DateFormatterMode.Full,
useRelative = true,
),
displayEmojiReactions = displayEmojiReactions,
verifiedUserSendFailure = verifiedUserSendFailure,
actions = actions.toImmutableList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ data class ActionListState(
data class Loading(val event: TimelineItem.Event) : Target
data class Success(
val event: TimelineItem.Event,
val sentTimeFull: String,
val displayEmojiReactions: Boolean,
val verifiedUserSendFailure: VerifiedUserSendFailure,
val actions: ImmutableList<TimelineItemAction>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
event = aTimelineItemEvent(
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
Expand All @@ -49,6 +50,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
displayNameAmbiguous = true,
timelineItemReactions = reactionsState,
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(
Expand All @@ -62,6 +64,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
content = aTimelineItemVideoContent(),
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(
Expand All @@ -75,6 +78,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
content = aTimelineItemFileContent(),
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(
Expand All @@ -88,6 +92,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
content = aTimelineItemAudioContent(),
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(
Expand All @@ -101,6 +106,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
content = aTimelineItemVoiceContent(caption = null),
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(
Expand All @@ -114,6 +120,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
content = aTimelineItemLocationContent(),
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
Expand All @@ -125,6 +132,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
content = aTimelineItemLocationContent(),
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = false,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
Expand All @@ -136,6 +144,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
content = aTimelineItemPollContent(),
timelineItemReactions = reactionsState
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = false,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemPollActionList(),
Expand All @@ -147,6 +156,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
timelineItemReactions = reactionsState,
messageShield = MessageShield.UnknownDevice(isCritical = true)
),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
Expand All @@ -155,6 +165,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
anActionListState(
target = ActionListState.Target.Success(
event = aTimelineItemEvent(),
sentTimeFull = "January 1, 1970 at 12:00 AM",
displayEmojiReactions = true,
verifiedUserSendFailure = anUnsignedDeviceSendFailure(),
actions = aTimelineItemActionList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ private fun ActionListViewContent(
Column {
MessageSummary(
event = target.event,
sentTimeFull = target.sentTimeFull,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
Expand Down Expand Up @@ -245,7 +246,11 @@ private fun ActionListViewContent(

@Suppress("MultipleEmitters") // False positive
@Composable
private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modifier) {
private fun MessageSummary(
event: TimelineItem.Event,
sentTimeFull: String,
modifier: Modifier = Modifier,
) {
val content: @Composable () -> Unit
val icon: @Composable () -> Unit = { Avatar(avatarData = event.senderAvatar.copy(size = AvatarSize.MessageActionSender)) }
val contentStyle = ElementTheme.typography.fontBodyMdRegular.copy(color = MaterialTheme.colorScheme.secondary)
Expand Down Expand Up @@ -300,20 +305,23 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
icon()
Spacer(modifier = Modifier.width(8.dp))
Column(modifier = Modifier.weight(1f)) {
SenderName(
senderId = event.senderId,
senderProfile = event.senderProfile,
senderNameMode = SenderNameMode.ActionList,
)
Row {
SenderName(
modifier = Modifier.weight(1f),
senderId = event.senderId,
senderProfile = event.senderProfile,
senderNameMode = SenderNameMode.ActionList,
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = sentTimeFull,
style = ElementTheme.typography.fontBodyXsRegular,
color = MaterialTheme.colorScheme.secondary,
textAlign = TextAlign.End,
)
}
content()
}
Spacer(modifier = Modifier.width(16.dp))
Text(
event.sentTime,
style = ElementTheme.typography.fontBodyXsRegular,
color = MaterialTheme.colorScheme.secondary,
textAlign = TextAlign.End,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts
import io.element.android.libraries.core.bool.orTrue
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.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.MatrixClient
Expand All @@ -32,14 +33,13 @@ import io.element.android.libraries.matrix.api.timeline.item.event.getDisambigua
import io.element.android.libraries.matrix.ui.messages.reply.map
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import java.text.DateFormat
import java.util.Date

class TimelineItemEventFactory @AssistedInject constructor(
@Assisted private val config: TimelineItemsFactoryConfig,
private val contentFactory: TimelineItemContentFactory,
private val matrixClient: MatrixClient,
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
private val dateFormatter: DateFormatter,
private val permalinkParser: PermalinkParser,
) {
@AssistedFactory
Expand All @@ -57,9 +57,10 @@ class TimelineItemEventFactory @AssistedInject constructor(
val groupPosition =
computeGroupPosition(currentTimelineItem, timelineItems, index)
val senderProfile = currentTimelineItem.event.senderProfile
val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT)
val sentTime = timeFormatter.format(Date(currentTimelineItem.event.timestamp))

val sentTime = dateFormatter.format(
timestamp = currentTimelineItem.event.timestamp,
mode = DateFormatterMode.TimeOnly,
)
val senderAvatarData = AvatarData(
id = currentSender.value,
name = senderProfile.getDisambiguatedDisplayName(currentSender),
Expand All @@ -78,6 +79,7 @@ class TimelineItemEventFactory @AssistedInject constructor(
isMine = currentTimelineItem.event.isOwn,
isEditable = currentTimelineItem.event.isEditable,
canBeRepliedTo = currentTimelineItem.event.canBeRepliedTo,
sentTimeMillis = currentTimelineItem.event.timestamp,
sentTime = sentTime,
groupPosition = groupPosition,
reactionsState = currentTimelineItem.computeReactionsState(),
Expand Down Expand Up @@ -106,7 +108,6 @@ class TimelineItemEventFactory @AssistedInject constructor(
if (!config.computeReactions) {
return TimelineItemReactions(reactions = persistentListOf())
}
val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT)
var aggregatedReactions = this.event.reactions.map { reaction ->
// Sort reactions within an aggregation by timestamp descending.
// This puts the most recent at the top, useful in cases like the
Expand All @@ -121,7 +122,10 @@ class TimelineItemEventFactory @AssistedInject constructor(
AggregatedReactionSender(
senderId = it.senderId,
timestamp = date,
sentTime = timeFormatter.format(date),
sentTime = dateFormatter.format(
it.timestamp,
DateFormatterMode.TimeOrDate,
),
)
}
.toImmutableList()
Expand Down Expand Up @@ -157,7 +161,10 @@ class TimelineItemEventFactory @AssistedInject constructor(
url = roomMember?.avatarUrl,
size = AvatarSize.TimelineReadReceipt,
),
formattedDate = lastMessageTimestampFormatter.format(receipt.timestamp)
formattedDate = dateFormatter.format(
receipt.timestamp,
mode = DateFormatterMode.TimeOrDate,
)
)
}
.toImmutableList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ package io.element.android.features.messages.impl.timeline.factories.virtual

import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel
import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter
import io.element.android.libraries.dateformatter.api.DateFormatter
import io.element.android.libraries.dateformatter.api.DateFormatterMode
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import javax.inject.Inject

class TimelineItemDaySeparatorFactory @Inject constructor(private val daySeparatorFormatter: DaySeparatorFormatter) {
class TimelineItemDaySeparatorFactory @Inject constructor(
private val dateFormatter: DateFormatter,
) {
fun create(virtualItem: VirtualTimelineItem.DayDivider): TimelineItemVirtualModel {
val formattedDate = daySeparatorFormatter.format(virtualItem.timestamp)
val formattedDate = dateFormatter.format(
timestamp = virtualItem.timestamp,
mode = DateFormatterMode.Day,
useRelative = true,
)
return TimelineItemDaySeparatorModel(
formattedDate = formattedDate
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ sealed interface TimelineItem {
val senderProfile: ProfileTimelineDetails,
val senderAvatar: AvatarData,
val content: TimelineItemEventContent,
val sentTimeMillis: Long = 0L,
val sentTime: String = "",
val isMine: Boolean = false,
val isEditable: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ class MessagesViewTest {
actionListState = anActionListState(
target = ActionListState.Target.Success(
event = timelineItem,
sentTimeFull = "",
displayEmojiReactions = true,
actions = persistentListOf(TimelineItemAction.Edit),
verifiedUserSendFailure = VerifiedUserSendFailure.None,
Expand Down Expand Up @@ -399,6 +400,7 @@ class MessagesViewTest {
actionListState = anActionListState(
target = ActionListState.Target.Success(
event = timelineItem,
sentTimeFull = "",
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = persistentListOf(TimelineItemAction.Edit),
Expand Down Expand Up @@ -427,6 +429,7 @@ class MessagesViewTest {
actionListState = anActionListState(
target = ActionListState.Target.Success(
event = timelineItem,
sentTimeFull = "",
displayEmojiReactions = true,
verifiedUserSendFailure = aChangedIdentitySendFailure(),
actions = persistentListOf(),
Expand Down
Loading
Loading