From 5e5ce614ef6d2c0116cf9b69379852e0cbc6d0bf Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 23 Nov 2021 17:09:58 +0200 Subject: [PATCH] Add date in view all threads UI Improvements on threads summary Add View In Room bottom sheet action from within thread timeline root message --- .../home/room/detail/RoomDetailViewModel.kt | 4 +-- .../home/room/detail/TimelineFragment.kt | 7 ++++ .../timeline/TimelineEventController.kt | 10 +++--- .../timeline/action/EventSharedAction.kt | 6 +++- .../action/MessageActionsViewModel.kt | 35 +++++++++++++++++++ .../factory/MergedHeaderItemFactory.kt | 2 +- .../timeline/factory/MessageItemFactory.kt | 11 +++++- .../timeline/factory/TimelineItemFactory.kt | 10 +++--- .../factory/TimelineItemFactoryParams.kt | 2 ++ .../helper/TimelineEventVisibilityHelper.kt | 17 ++++----- .../detail/timeline/item/AbsMessageItem.kt | 4 +-- .../threads/list/model/ThreadSummaryModel.kt | 8 +++-- .../list/viewmodel/ThreadSummaryController.kt | 8 +++-- .../main/res/layout/item_thread_summary.xml | 12 +++---- .../res/layout/view_thread_room_summary.xml | 3 +- vector/src/main/res/values/strings.xml | 1 + 16 files changed, 103 insertions(+), 37 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 5854d35fb6..1f5550b27f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -676,7 +676,7 @@ class RoomDetailViewModel @AssistedInject constructor( if (initialState.isThreadTimeline()) { when (itemId) { R.id.menu_thread_timeline_more -> true - else -> false + else -> false } } else { when (itemId) { @@ -688,7 +688,7 @@ class RoomDetailViewModel @AssistedInject constructor( // Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^ R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined R.id.search -> true - R.id.threads -> true + R.id.threads -> BuildConfig.THREADING_ENABLED R.id.dev_tools -> vectorPreferences.developerMode() else -> false } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index a8e8e11b57..30d4a881f2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -2012,6 +2012,13 @@ class TimelineFragment @Inject constructor( requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit) } } + is EventSharedAction.ViewInRoom -> { + if (!views.voiceMessageRecorderView.isActive()) { + handleViewInRoomAction() + } else { + requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit) + } + } is EventSharedAction.CopyPermalink -> { val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, action.eventId) copyToClipboard(requireContext(), permalink, false) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index 11c90b3482..caa4783573 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -104,6 +104,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec roomSummary = state.asyncRoomSummary(), rootThreadEventId = state.rootThreadEventId ) + + fun isFromThreadTimeline():Boolean = rootThreadEventId != null } interface Callback : @@ -193,7 +195,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec // it's sent by the same user so we are sure we have up to date information. val invalidatedSenderId: String? = currentSnapshot.getOrNull(position)?.senderInfo?.userId val prevDisplayableEventIndex = currentSnapshot.subList(0, position).indexOfLast { - timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.rootThreadEventId ) + timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.isFromThreadTimeline() ) } if (prevDisplayableEventIndex != -1 && currentSnapshot[prevDisplayableEventIndex].senderInfo.userId == invalidatedSenderId) { modelCache[prevDisplayableEventIndex] = null @@ -370,7 +372,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec val nextEvent = currentSnapshot.nextOrNull(position) val prevEvent = currentSnapshot.prevOrNull(position) val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull { - timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.rootThreadEventId) + timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId, partialState.isFromThreadTimeline()) } // Should be build if not cached or if model should be refreshed if (modelCache[position] == null || modelCache[position]?.isCacheable == false) { @@ -452,7 +454,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec return null } // If the event is not shown, we go to the next one - if (!timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.rootThreadEventId)) { + if (!timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.isFromThreadTimeline())) { continue } // If the event is sent by us, we update the holder with the eventId and stop the search @@ -474,7 +476,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec val currentReadReceipts = ArrayList(event.readReceipts).filter { it.user.userId != session.myUserId } - if (timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.rootThreadEventId)) { + if (timelineEventVisibilityHelper.shouldShowEvent(event, partialState.highlightedEventId, partialState.isFromThreadTimeline())) { lastShownEventId = event.eventId } if (lastShownEventId == null) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt index c57d844974..7bffba69b4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt @@ -48,10 +48,14 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, data class Reply(val eventId: String) : EventSharedAction(R.string.reply, R.drawable.ic_reply) + // TODO add translations data class ReplyInThread(val eventId: String) : - // TODO add translations EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread) + // TODO add translations + object ViewInRoom : + EventSharedAction(R.string.view_in_room, R.drawable.ic_thread_view_in_room_menu_item) + data class Share(val eventId: String, val messageContent: MessageContent) : EventSharedAction(R.string.share, R.drawable.ic_share) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 6762ed1479..76e415eceb 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -331,6 +331,10 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted add(EventSharedAction.ReplyInThread(eventId)) } + if (canViewInRoom(timelineEvent, messageContent, actionPermissions)) { + add(EventSharedAction.ViewInRoom) + } + if (canEdit(timelineEvent, session.myUserId, actionPermissions)) { add(EventSharedAction.Edit(eventId)) } @@ -417,6 +421,11 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } } + /** + * Determine whether or not the Reply In Thread bottom sheet setting will be visible + * to the user + */ + // TODO handle reply in thread for images etc private fun canReplyInThread(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean { @@ -437,6 +446,32 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } } + /** + * Determine whether or no the selected event is a root thread event from within + * a thread timeline + */ + private fun canViewInRoom(event: TimelineEvent, + messageContent: MessageContent?, + actionPermissions: ActionPermissions): Boolean { + // Only event of type EventType.MESSAGE are supported for the moment + if (!BuildConfig.THREADING_ENABLED) return false + if (!initialState.isFromThreadTimeline) return false + if (event.root.getClearType() != EventType.MESSAGE) return false + if (!actionPermissions.canSendMessage) return false + + return when (messageContent?.msgType) { + MessageType.MSGTYPE_TEXT -> event.root.threadDetails?.isRootThread ?: false +// MessageType.MSGTYPE_NOTICE, +// MessageType.MSGTYPE_EMOTE, +// MessageType.MSGTYPE_IMAGE, +// MessageType.MSGTYPE_VIDEO, +// MessageType.MSGTYPE_AUDIO, +// MessageType.MSGTYPE_FILE -> true + else -> false + } + } + + private fun canQuote(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean { // Only event of type EventType.MESSAGE are supported for the moment if (event.root.getClearType() != EventType.MESSAGE) return false diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index fa699f0c78..1c25f923cf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -83,7 +83,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde eventIdToHighlight: String?, requestModelBuild: () -> Unit, callback: TimelineEventController.Callback?): MergedMembershipEventsItem_? { - val mergedEvents = timelineEventVisibilityHelper.prevSameTypeEvents(items, currentPosition, 2, eventIdToHighlight, partialState.rootThreadEventId) + val mergedEvents = timelineEventVisibilityHelper.prevSameTypeEvents(items, currentPosition, 2, eventIdToHighlight, partialState.isFromThreadTimeline()) return if (mergedEvents.isEmpty()) { null } else { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 0ee28404df..5e25b52473 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -125,6 +125,9 @@ class MessageItemFactory @Inject constructor( pillsPostProcessorFactory.create(roomId) } + + + fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? { val event = params.event val highlight = params.isHighlighted @@ -149,7 +152,10 @@ class MessageItemFactory @Inject constructor( // This is an edit event, we should display it when debugging as a notice event return noticeItemFactory.create(params) } - val attributes = messageItemAttributesFactory.create(messageContent, informationData, callback, event.root.threadDetails) + + // always hide summary when we are on thread timeline + val threadDetails = if(params.isFromThreadTimeline()) null else event.root.threadDetails + val attributes = messageItemAttributesFactory.create(messageContent, informationData, callback, threadDetails) // val all = event.root.toContent() // val ev = all.toModel() @@ -174,6 +180,9 @@ class MessageItemFactory @Inject constructor( } } + private fun isFromThreadTimeline(params: TimelineItemFactoryParams){ + params.rootThreadEventId + } private fun buildOptionsMessageItem(messageContent: MessageOptionsContent, informationData: MessageInformationData, highlight: Boolean, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index 96786e3377..11c46026d6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -42,8 +42,8 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me fun create(params: TimelineItemFactoryParams): VectorEpoxyModel<*> { val event = params.event val computedModel = try { - if (!timelineEventVisibilityHelper.shouldShowEvent(event, params.highlightedEventId, params.rootThreadEventId)) { - return buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.rootThreadEventId) + if (!timelineEventVisibilityHelper.shouldShowEvent(event, params.highlightedEventId, params.isFromThreadTimeline())) { + return buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.isFromThreadTimeline()) } when (event.root.getClearType()) { // Message itemsX @@ -109,11 +109,11 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me Timber.e(throwable, "failed to create message item") defaultItemFactory.create(params, throwable) } - return computedModel ?: buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.rootThreadEventId) + return computedModel ?: buildEmptyItem(event, params.prevEvent, params.highlightedEventId, params.isFromThreadTimeline()) } - private fun buildEmptyItem(timelineEvent: TimelineEvent, prevEvent: TimelineEvent?, highlightedEventId: String?, rootThreadEventId: String?): TimelineEmptyItem { - val isNotBlank = prevEvent == null || timelineEventVisibilityHelper.shouldShowEvent(prevEvent, highlightedEventId, rootThreadEventId) + private fun buildEmptyItem(timelineEvent: TimelineEvent, prevEvent: TimelineEvent?, highlightedEventId: String?, isFromThreadTimeline: Boolean): TimelineEmptyItem { + val isNotBlank = prevEvent == null || timelineEventVisibilityHelper.shouldShowEvent(prevEvent, highlightedEventId, isFromThreadTimeline) return TimelineEmptyItem_() .id(timelineEvent.localId) .eventId(timelineEvent.eventId) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactoryParams.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactoryParams.kt index 94e94911c0..8479d6b589 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactoryParams.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactoryParams.kt @@ -38,4 +38,6 @@ data class TimelineItemFactoryParams( get() = partialState.rootThreadEventId val isHighlighted = highlightedEventId == event.eventId + + fun isFromThreadTimeline(): Boolean = rootThreadEventId != null } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt index c56e9d1336..59a6c82aff 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt @@ -40,7 +40,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen * * @return a list of timeline events which have sequentially the same type following the next direction. */ - private fun nextSameTypeEvents(timelineEvents: List, index: Int, minSize: Int, eventIdToHighlight: String?, rootThreadEventId: String?): List { + private fun nextSameTypeEvents(timelineEvents: List, index: Int, minSize: Int, eventIdToHighlight: String?, isFromThreadTimeline: Boolean): List { if (index >= timelineEvents.size - 1) { return emptyList() } @@ -62,7 +62,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen } else { nextSameDayEvents.subList(0, indexOfFirstDifferentEventType) } - val filteredSameTypeEvents = sameTypeEvents.filter { shouldShowEvent(it, eventIdToHighlight, rootThreadEventId) } + val filteredSameTypeEvents = sameTypeEvents.filter { shouldShowEvent(it, eventIdToHighlight, isFromThreadTimeline) } if (filteredSameTypeEvents.size < minSize) { return emptyList() } @@ -77,21 +77,22 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen * * @return a list of timeline events which have sequentially the same type following the prev direction. */ - fun prevSameTypeEvents(timelineEvents: List, index: Int, minSize: Int, eventIdToHighlight: String?, rootThreadEventId: String?): List { + fun prevSameTypeEvents(timelineEvents: List, index: Int, minSize: Int, eventIdToHighlight: String?, isFromThreadTimeline: Boolean): List { val prevSub = timelineEvents.subList(0, index + 1) return prevSub .reversed() .let { - nextSameTypeEvents(it, 0, minSize, eventIdToHighlight, rootThreadEventId) + nextSameTypeEvents(it, 0, minSize, eventIdToHighlight, isFromThreadTimeline) } } /** * @param timelineEvent the event to check for visibility * @param highlightedEventId can be checked to force visibility to true + * @param rootThreadEventId if this param is null it means we are in the original timeline * @return true if the event should be shown in the timeline. */ - fun shouldShowEvent(timelineEvent: TimelineEvent, highlightedEventId: String?, rootThreadEventId: String?): Boolean { + fun shouldShowEvent(timelineEvent: TimelineEvent, highlightedEventId: String?, isFromThreadTimeline: Boolean): Boolean { // If show hidden events is true we should always display something if (userPreferencesProvider.shouldShowHiddenEvents()) { return true @@ -105,14 +106,14 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen } // Check for special case where we should hide the event, like redacted, relation, memberships... according to user preferences. - return !timelineEvent.shouldBeHidden(rootThreadEventId) + return !timelineEvent.shouldBeHidden(isFromThreadTimeline) } private fun TimelineEvent.isDisplayable(): Boolean { return TimelineDisplayableEvents.DISPLAYABLE_TYPES.contains(root.getClearType()) } - private fun TimelineEvent.shouldBeHidden(rootThreadEventId: String?): Boolean { + private fun TimelineEvent.shouldBeHidden(isFromThreadTimeline: Boolean): Boolean { if (root.isRedacted() && !userPreferencesProvider.shouldShowRedactedMessages()) { return true } @@ -125,7 +126,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen if ((diff.isAvatarChange || diff.isDisplaynameChange) && !userPreferencesProvider.shouldShowAvatarDisplayNameChanges()) return true } - if(BuildConfig.THREADING_ENABLED && rootThreadEventId == null && root.isThread() && root.getRootThreadEventId() != null){ + if(BuildConfig.THREADING_ENABLED && !isFromThreadTimeline && root.isThread() && root.getRootThreadEventId() != null){ return true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt index 188a195ae6..ba7865ac57 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -114,9 +114,7 @@ abstract class AbsMessageItem : AbsBaseMessageItem threadDetails.threadSummarySenderInfo?.let { senderInfo -> attributes.avatarRenderer.render(MatrixItem.UserItem(senderInfo.userId, senderInfo.displayName, senderInfo.avatarUrl), holder.threadSummaryAvatarImageView) } - } - }else{ - holder.threadSummaryConstraintLayout.isVisible = false + } ?: run{holder.threadSummaryConstraintLayout.isVisible = false} } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/list/model/ThreadSummaryModel.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/list/model/ThreadSummaryModel.kt index 85e375d00d..57cba163eb 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/list/model/ThreadSummaryModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/list/model/ThreadSummaryModel.kt @@ -37,7 +37,7 @@ abstract class ThreadSummaryModel : VectorEpoxyModel( @EpoxyAttribute lateinit var rootMessage: String @EpoxyAttribute lateinit var lastMessage: String @EpoxyAttribute lateinit var lastMessageCounter: String - @EpoxyAttribute lateinit var lastMessageMatrixItem: MatrixItem + @EpoxyAttribute var lastMessageMatrixItem: MatrixItem? = null override fun bind(holder: Holder) { super.bind(holder) @@ -48,8 +48,10 @@ abstract class ThreadSummaryModel : VectorEpoxyModel( holder.rootMessageTextView.text = rootMessage // Last message summary - avatarRenderer.render(lastMessageMatrixItem, holder.lastMessageAvatarImageView) - holder.lastMessageAvatarImageView.contentDescription = lastMessageMatrixItem.getBestName() + lastMessageMatrixItem?.let { + avatarRenderer.render(it, holder.lastMessageAvatarImageView) + } + holder.lastMessageAvatarImageView.contentDescription = lastMessageMatrixItem?.getBestName() holder.lastMessageTextView.text = lastMessage holder.lastMessageCounterTextView.text = lastMessageCounter } diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadSummaryController.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadSummaryController.kt index bd19c8e3ff..a47d2b6c3b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadSummaryController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/threads/list/viewmodel/ThreadSummaryController.kt @@ -17,13 +17,16 @@ package im.vector.app.features.home.room.threads.list.viewmodel import com.airbnb.epoxy.EpoxyController +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.threads.list.model.threadSummary import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class ThreadSummaryController @Inject constructor( - private val avatarRenderer: AvatarRenderer + private val avatarRenderer: AvatarRenderer, + private val dateFormatter: VectorDateFormatter ) : EpoxyController() { var listener: Listener? = null @@ -53,12 +56,13 @@ class ThreadSummaryController @Inject constructor( // this one is added to the breadcrumbs safeViewState.rootThreadEventList.invoke() ?.forEach { timelineEvent -> + val date = dateFormatter.format(timelineEvent.root.originServerTs, DateFormatKind.ROOM_LIST) threadSummary { id(timelineEvent.eventId) avatarRenderer(host.avatarRenderer) matrixItem(timelineEvent.senderInfo.toMatrixItem()) title(timelineEvent.senderInfo.displayName) - date(timelineEvent.root.ageLocalTs.toString()) + date(date) rootMessage(timelineEvent.root.getDecryptedUserFriendlyTextSummary()) lastMessage(timelineEvent.root.threadDetails?.threadSummaryLatestTextMessage.orEmpty()) lastMessageCounter(timelineEvent.root.threadDetails?.numberOfThreads.toString()) diff --git a/vector/src/main/res/layout/item_thread_summary.xml b/vector/src/main/res/layout/item_thread_summary.xml index 075709ef00..130dae44b1 100644 --- a/vector/src/main/res/layout/item_thread_summary.xml +++ b/vector/src/main/res/layout/item_thread_summary.xml @@ -31,7 +31,7 @@ app:layout_constraintEnd_toStartOf="@id/threadSummaryDateTextView" app:layout_constraintStart_toEndOf="@id/threadSummaryAvatarImageView" app:layout_constraintTop_toTopOf="parent" - tools:text="Aris" /> + tools:text="Aris Kots" /> + tools:text="10 minutes" /> + tools:text="192" /> Edit Reply Reply In Thread + View In Room Retry "Join a room to start using the app."