From 2f638407191fb82c7c83edb635cdb55f90b815e4 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Fri, 20 Mar 2026 22:04:42 -0500 Subject: [PATCH 1/3] Fix pmp messages collapsing when different profiles are used --- .changeset/fix-pmp-collapsing.md | 5 +++++ src/app/hooks/timeline/useProcessedTimeline.ts | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-pmp-collapsing.md diff --git a/.changeset/fix-pmp-collapsing.md b/.changeset/fix-pmp-collapsing.md new file mode 100644 index 000000000..03989cbd1 --- /dev/null +++ b/.changeset/fix-pmp-collapsing.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix per message profile messages collapsing together when different profiles are used. diff --git a/src/app/hooks/timeline/useProcessedTimeline.ts b/src/app/hooks/timeline/useProcessedTimeline.ts index d4ed3b129..f1f799308 100644 --- a/src/app/hooks/timeline/useProcessedTimeline.ts +++ b/src/app/hooks/timeline/useProcessedTimeline.ts @@ -143,8 +143,15 @@ export function useProcessedTimeline({ const typeMatch = normalizeMessageType(getPrevType.call(prevEvent)) === normalizeMessageType(type); const dividerOk = !newDivider || eventSender === mxUserId; - - collapsed = dividerOk && senderMatch && typeMatch && withinTimeThreshold; + const getPmpId = (ev: MatrixEvent): string | null => + ev.getContent()?.['com.beeper.per_message_profile']?.id ?? null; + + collapsed = + dividerOk && + senderMatch && + typeMatch && + withinTimeThreshold && + getPmpId(prevEvent) === getPmpId(mEvent); } else { const prevIsMessageEvent = MESSAGE_EVENT_TYPES.includes(getPrevType.call(prevEvent)); collapsed = !prevIsMessageEvent; From d0db174d9953750a8ded5cb5640f10c89037d8eb Mon Sep 17 00:00:00 2001 From: 7w1 Date: Fri, 20 Mar 2026 22:08:16 -0500 Subject: [PATCH 2/3] fix pmps not rendering in encrypted rooms --- .changeset/fix-pmp-collapsing.md | 2 +- .changeset/fix-pmp-encrypted-rooms.md | 5 +++++ src/app/features/room/message/Message.tsx | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-pmp-encrypted-rooms.md diff --git a/.changeset/fix-pmp-collapsing.md b/.changeset/fix-pmp-collapsing.md index 03989cbd1..18ce0539c 100644 --- a/.changeset/fix-pmp-collapsing.md +++ b/.changeset/fix-pmp-collapsing.md @@ -2,4 +2,4 @@ default: patch --- -Fix per message profile messages collapsing together when different profiles are used. +Fix per-message profile messages collapsing together when different profiles are used. diff --git a/.changeset/fix-pmp-encrypted-rooms.md b/.changeset/fix-pmp-encrypted-rooms.md new file mode 100644 index 000000000..9bfd9f532 --- /dev/null +++ b/.changeset/fix-pmp-encrypted-rooms.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix per-message profiles not rendering in encrypted rooms. diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index 909b0ab0b..6bf313a8a 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -387,7 +387,7 @@ function MessageInternal( */ const pmp: PerMessageProfileBeeperFormat | undefined = useMemo( () => - mEvent.event.content?.['com.beeper.per_message_profile'] as + mEvent.getContent()?.['com.beeper.per_message_profile'] as | PerMessageProfileBeeperFormat | undefined, [mEvent] From 8c34d4a95b888e1d21c7a5880a5815eb5bc68cd7 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Fri, 20 Mar 2026 22:26:56 -0500 Subject: [PATCH 3/3] fix pmp ignoring edit events --- .changeset/fix-pmp-edit-render.md | 5 ++++ src/app/features/room/message/Message.tsx | 36 ++++++++++++++++++----- 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 .changeset/fix-pmp-edit-render.md diff --git a/.changeset/fix-pmp-edit-render.md b/.changeset/fix-pmp-edit-render.md new file mode 100644 index 000000000..433966f89 --- /dev/null +++ b/.changeset/fix-pmp-edit-render.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix per-message profiles not updating avatar/name if edit events are recieved. diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index 6bf313a8a..8534d6b7c 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -49,7 +49,7 @@ import { Username, UsernameBold, } from '$components/message'; -import { canEditEvent, getEventEdits, getMemberAvatarMxc } from '$utils/room'; +import { canEditEvent, getEditedEvent, getEventEdits, getMemberAvatarMxc } from '$utils/room'; import { mxcUrlToHttp } from '$utils/matrix'; import { getSettings, MessageLayout, MessageSpacing, settingsAtom } from '$state/settings'; import { nicknamesAtom, setNicknameAtom } from '$state/nicknames'; @@ -380,18 +380,38 @@ function MessageInternal( const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); + const [editVersion, setEditVersion] = useState(0); + + useEffect(() => { + const onReplaced = () => setEditVersion((v) => v + 1); + mEvent.on('Event.replaced' as any, onReplaced); + return () => { + mEvent.off('Event.replaced' as any, onReplaced); + }; + }, [mEvent]); + /** * We read the per-message profile from the event content here. * We have to do this in the message component because the per-message profile can be different for each message, and we need to read it for each message individually. * We also want to avoid reading and parsing the per-message profile in a parent component like the timeline, because that would be inefficient and would cause unnecessary re-renders of the entire timeline whenever a per-message profile changes. */ - const pmp: PerMessageProfileBeeperFormat | undefined = useMemo( - () => - mEvent.getContent()?.['com.beeper.per_message_profile'] as - | PerMessageProfileBeeperFormat - | undefined, - [mEvent] - ); + const pmp: PerMessageProfileBeeperFormat | undefined = useMemo(() => { + const evtId = mEvent.getId(); + const evtTimeline = evtId ? room.getTimelineForEvent(evtId) : undefined; + const editedEvent = + evtTimeline && evtId + ? getEditedEvent(evtId, mEvent, evtTimeline.getTimelineSet()) + : undefined; + + const resolvedContent = editedEvent + ? editedEvent.getContent()['m.new_content'] + : mEvent.getContent(); + + return resolvedContent?.['com.beeper.per_message_profile'] as + | PerMessageProfileBeeperFormat + | undefined; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mEvent, room, editVersion]); /** * We convert the per-message profile from the Beeper format to our internal format here in the message component