From 3ded0f4eb970df55705a8ce0a6c44e2f561769c4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 12 Oct 2024 13:41:15 +0300 Subject: [PATCH] web/timeline: omit profile on consecutive messages from same sender --- web/src/ui/timeline/TimelineEvent.css | 37 +++++++++++++++++++++++---- web/src/ui/timeline/TimelineEvent.tsx | 21 ++++++++++++--- web/src/ui/timeline/TimelineView.tsx | 15 ++++++++--- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/web/src/ui/timeline/TimelineEvent.css b/web/src/ui/timeline/TimelineEvent.css index 4cf1795..0b12d01 100644 --- a/web/src/ui/timeline/TimelineEvent.css +++ b/web/src/ui/timeline/TimelineEvent.css @@ -33,15 +33,40 @@ div.timeline-event { } } + > div.event-time-only { + grid-area: timestamp; + display: none; + align-items: end; + justify-content: center; + font-size: .8rem; + max-height: 1.25rem; + margin-right: .25rem; + } + > div.event-content { grid-area: content; overflow: hidden; } + &.same-sender { + grid-template: + "timestamp content" auto + / 2.75rem 1fr; + margin-top: 0; + + > div.sender-avatar, > div.event-sender-and-time { + display: none; + } + + > div.event-time-only { + display: flex; + } + } + &.hidden-event { grid-template: - "sender avatar content" auto - / 2.5rem 1.5rem 1fr; + "timestamp avatar content" auto + / 2.75rem 1.5rem 1fr; margin-top: 0; > div.sender-avatar { @@ -58,9 +83,11 @@ div.timeline-event { } > div.event-sender-and-time { - > span.event-sender, > span.event-edited { - display: none; - } + display: none; + } + + > div.event-time-only { + display: flex; } } } diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index d36d310..ab53805 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -27,6 +27,7 @@ import "./TimelineEvent.css" export interface TimelineEventProps { room: RoomStateStore evt: MemDBEvent + prevEvt: MemDBEvent | null } function getBodyType(evt: MemDBEvent): React.FunctionComponent { @@ -61,13 +62,22 @@ const EventReactions = ({ reactions }: { reactions: Record }) => } -const TimelineEvent = ({ room, evt }: TimelineEventProps) => { +const TimelineEvent = ({ room, evt, prevEvt }: TimelineEventProps) => { const memberEvt = room.getStateEvent("m.room.member", evt.sender) const memberEvtContent = memberEvt?.content as MemberEventContent | undefined const BodyType = getBodyType(evt) const eventTS = new Date(evt.timestamp) const editEventTS = evt.last_edit ? new Date(evt.last_edit.timestamp) : null - return
+ const wrapperClassNames = ["timeline-event"] + if (BodyType === HiddenEvent) { + wrapperClassNames.push("hidden-event") + } else if (prevEvt?.sender === evt.sender && getBodyType(prevEvt) !== HiddenEvent) { + wrapperClassNames.push("same-sender") + } + const fullTime = fullTimeFormatter.format(eventTS) + const shortTime = formatShortTime(eventTS) + const editTime = editEventTS ? `Edited at ${fullTimeFormatter.format(editEventTS)}` : null + return
{
{memberEvtContent?.displayname ?? evt.sender} - {formatShortTime(eventTS)} - {editEventTS ? + {shortTime} + {(editEventTS && editTime) ? (edited at {formatShortTime(editEventTS)}) : null}
+
+ {shortTime} +
{evt.reactions ? : null} diff --git a/web/src/ui/timeline/TimelineView.tsx b/web/src/ui/timeline/TimelineView.tsx index 006e036..908100c 100644 --- a/web/src/ui/timeline/TimelineView.tsx +++ b/web/src/ui/timeline/TimelineView.tsx @@ -15,6 +15,7 @@ // along with this program. If not, see . import { use, useCallback, useEffect, useRef } from "react" import { RoomStateStore, useRoomTimeline } from "../../api/statestore.ts" +import { MemDBEvent } from "../../api/types" import { ClientContext } from "../ClientContext.ts" import TimelineEvent from "./TimelineEvent.tsx" import "./TimelineView.css" @@ -80,15 +81,23 @@ const TimelineView = ({ room }: TimelineViewProps) => { return () => observer.unobserve(topElem) }, [loadHistory, topRef]) + let prevEvt: MemDBEvent | null = null return
- {timeline.map(entry => entry ? : null)} + {timeline.map(entry => { + if (!entry) { + return null + } + const thisEvt = + prevEvt = entry + return thisEvt + })}