-
+
- {getDisplayname(event.sender, memberEvtContent)}
+ {getDisplayname(event.sender, perMessageSender ?? memberEvtContent)}
+ {perMessageSender &&
+ via
+
+ {getDisplayname(event.sender, memberEvtContent)}
+
+
}
{onClose &&
{onSetSilent && (isExplicitInThread || !isThread) &&
div.per-message-event-sender {
+ color: var(--secondary-text-color);
+ font-size: 0.85rem;
+
+ > span.via {
+ margin-right: 0.3rem;
+ }
+
+ > span.event-sender {
+ font-weight: bold;
+ user-select: none;
+ cursor: var(--clickable-cursor);
+ }
+ }
+
> span.event-time, > span.event-edited {
font-size: .7rem;
color: var(--secondary-text-color);
diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx
index 524d84b..5017f3e 100644
--- a/web/src/ui/timeline/TimelineEvent.tsx
+++ b/web/src/ui/timeline/TimelineEvent.tsx
@@ -27,7 +27,7 @@ import { useRoomContext } from "../roomview/roomcontext.ts"
import ReadReceipts from "./ReadReceipts.tsx"
import { ReplyIDBody } from "./ReplyBody.tsx"
import URLPreviews from "./URLPreviews.tsx"
-import { ContentErrorBoundary, HiddenEvent, getBodyType, isSmallEvent } from "./content"
+import { ContentErrorBoundary, HiddenEvent, getBodyType, getPerMessageProfile, isSmallEvent } from "./content"
import { EventFixedMenu, EventFullMenu, EventHoverMenu, getModalStyleFromMouse } from "./menu"
import ErrorIcon from "@/icons/error.svg?react"
import PendingIcon from "@/icons/pending.svg?react"
@@ -114,6 +114,7 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T
}
const memberEvt = useRoomMember(client, roomCtx.store, evt.sender)
const memberEvtContent = memberEvt?.content as MemberEventContent | undefined
+ let renderMemberEvtContent = memberEvtContent
const BodyType = getBodyType(evt)
const eventTS = new Date(evt.timestamp)
const editEventTS = evt.last_edit ? new Date(evt.last_edit.timestamp) : null
@@ -170,6 +171,17 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T
replyInMessage = replyElem
}
}
+ const perMessageSender = getPerMessageProfile(evt)
+ const prevPerMessageSender = getPerMessageProfile(prevEvt)
+ if (perMessageSender) {
+ renderMemberEvtContent = {
+ membership: "join",
+ displayname: perMessageSender.displayname ?? memberEvtContent?.displayname,
+ avatar_url: perMessageSender.avatar_url ?? memberEvtContent?.avatar_url,
+ avatar_file: perMessageSender.avatar_file ?? memberEvtContent?.avatar_file,
+ }
+ }
+
let smallAvatar = false
let renderAvatar = true
let eventTimeOnly = false
@@ -183,6 +195,7 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T
&& dateSeparator === null
&& !replyAboveMessage
&& !isSmallEvent(getBodyType(prevEvt))
+ && prevPerMessageSender?.id === perMessageSender?.id
) {
wrapperClassNames.push("same-sender")
eventTimeOnly = true
@@ -209,7 +222,7 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T
{replyAboveMessage}
{renderAvatar &&
}
{!eventTimeOnly ?
- {getDisplayname(evt.sender, memberEvtContent)}
+ {getDisplayname(evt.sender, renderMemberEvtContent)}
+ {perMessageSender &&
+ via
+
+ {getDisplayname(evt.sender, memberEvtContent)}
+
+
}
{shortTime}
{(editEventTS && editTime) ?
(edited at {formatShortTime(editEventTS)})
@@ -245,7 +268,7 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies, isFocused }: T
{evt.reactions ? : null}
{!evt.event_id.startsWith("~") && roomCtx.store.preferences.display_read_receipts &&
-
}
+
}
{evt.sender === client.userID && evt.transaction_id ? : null}
return <>
diff --git a/web/src/ui/timeline/content/index.ts b/web/src/ui/timeline/content/index.ts
index 108d44a..a0a08de 100644
--- a/web/src/ui/timeline/content/index.ts
+++ b/web/src/ui/timeline/content/index.ts
@@ -1,5 +1,5 @@
import React from "react"
-import { MemDBEvent } from "@/api/types"
+import { BeeperPerMessageProfile, MemDBEvent, MessageEventContent } from "@/api/types"
import ACLBody from "./ACLBody.tsx"
import EncryptedBody from "./EncryptedBody.tsx"
import HiddenEvent from "./HiddenEvent.tsx"
@@ -104,3 +104,8 @@ export function isSmallEvent(bodyType: React.FunctionComponent