mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
web/timeline: omit profile on consecutive messages from same sender
This commit is contained in:
parent
cba5dbd912
commit
3ded0f4eb9
3 changed files with 61 additions and 12 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import "./TimelineEvent.css"
|
|||
export interface TimelineEventProps {
|
||||
room: RoomStateStore
|
||||
evt: MemDBEvent
|
||||
prevEvt: MemDBEvent | null
|
||||
}
|
||||
|
||||
function getBodyType(evt: MemDBEvent): React.FunctionComponent<EventContentProps> {
|
||||
|
@ -61,13 +62,22 @@ const EventReactions = ({ reactions }: { reactions: Record<string, number> }) =>
|
|||
</div>
|
||||
}
|
||||
|
||||
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 <div className={`timeline-event ${BodyType === HiddenEvent ? "hidden-event" : ""}`}>
|
||||
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 <div className={wrapperClassNames.join(" ")}>
|
||||
<div className="sender-avatar" title={evt.sender}>
|
||||
<img
|
||||
className="avatar"
|
||||
|
@ -78,11 +88,14 @@ const TimelineEvent = ({ room, evt }: TimelineEventProps) => {
|
|||
</div>
|
||||
<div className="event-sender-and-time">
|
||||
<span className="event-sender">{memberEvtContent?.displayname ?? evt.sender}</span>
|
||||
<span className="event-time" title={fullTimeFormatter.format(eventTS)}>{formatShortTime(eventTS)}</span>
|
||||
{editEventTS ? <span className="event-edited" title={`Edited at ${fullTimeFormatter.format(editEventTS)}`}>
|
||||
<span className="event-time" title={fullTime}>{shortTime}</span>
|
||||
{(editEventTS && editTime) ? <span className="event-edited" title={editTime}>
|
||||
(edited at {formatShortTime(editEventTS)})
|
||||
</span> : null}
|
||||
</div>
|
||||
<div className="event-time-only">
|
||||
<span className="event-time" title={editTime ? `${fullTime} - ${editTime}` : fullTime}>{shortTime}</span>
|
||||
</div>
|
||||
<div className="event-content">
|
||||
<BodyType room={room} event={evt}/>
|
||||
{evt.reactions ? <EventReactions reactions={evt.reactions}/> : null}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
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 <div className="timeline-view" onScroll={handleScroll} ref={timelineViewRef}>
|
||||
<div className="timeline-beginning">
|
||||
<button onClick={loadHistory}>Load history</button>
|
||||
</div>
|
||||
<div className="timeline-list">
|
||||
<div className="timeline-top-ref" ref={topRef}/>
|
||||
{timeline.map(entry => entry ? <TimelineEvent
|
||||
key={entry.rowid} room={room} evt={entry}
|
||||
/> : null)}
|
||||
{timeline.map(entry => {
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
const thisEvt = <TimelineEvent
|
||||
key={entry.rowid} room={room} evt={entry} prevEvt={prevEvt}
|
||||
/>
|
||||
prevEvt = entry
|
||||
return thisEvt
|
||||
})}
|
||||
<div className="timeline-bottom-ref" ref={bottomRef}/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue