From 8ae1d1538e8a7cd23485d20c1ea1d54c653d7306 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 5 Jun 2026 00:30:42 -0400 Subject: [PATCH] fix: presence ring shape mismatch, edit history (no text) on E2EE, reaction count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PresenceRingAvatar: replace circular wrapper div (borderRadius 50%) with React.cloneElement injecting outline+outlineOffset directly onto the child Avatar element — outline follows the child's actual border-radius so the ring matches the avatar shape in every context - EditHistoryModal: use getClearContent() for the Original entry instead of evt.event.content, which is still the ciphertext for E2EE messages and has no body field. getClearContent() returns decrypted content bypassing the _replacingEvent chain, fixing the "(no text)" shown for encrypted originals - MessageQuickReactions: 5 → 3 emoji (toolbar too wide with 5) Co-Authored-By: Claude Sonnet 4.6 --- .../presence/PresenceRingAvatar.tsx | 26 +++++++++---------- .../room/message/EditHistoryModal.tsx | 11 +++++--- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/app/components/presence/PresenceRingAvatar.tsx b/src/app/components/presence/PresenceRingAvatar.tsx index f18a90406..3fde97214 100644 --- a/src/app/components/presence/PresenceRingAvatar.tsx +++ b/src/app/components/presence/PresenceRingAvatar.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { CSSProperties } from 'react'; import { color } from 'folds'; import { Presence, useUserPresence } from '../../hooks/useUserPresence'; @@ -12,7 +12,7 @@ function presenceRingColor(presence: Presence | undefined, status?: string): str type PresenceRingAvatarProps = { userId: string; - children: ReactNode; + children: React.ReactElement<{ style?: CSSProperties }>; }; export function PresenceRingAvatar({ userId, children }: PresenceRingAvatarProps) { @@ -21,16 +21,14 @@ export function PresenceRingAvatar({ userId, children }: PresenceRingAvatarProps if (!ringColor) return <>{children}; - return ( -
- {children} -
- ); + // Apply outline directly to the child so it follows the child's actual border-radius. + // outline follows border-radius in Chrome 94+, Firefox 88+, Safari 16.4+. + // This avoids the mismatch of a circular wrapper around a rounded-square avatar. + return React.cloneElement(children, { + style: { + ...children.props.style, + outline: `2px solid ${ringColor}`, + outlineOffset: '2px', + }, + }); } diff --git a/src/app/features/room/message/EditHistoryModal.tsx b/src/app/features/room/message/EditHistoryModal.tsx index 53daeb892..562ea61ce 100644 --- a/src/app/features/room/message/EditHistoryModal.tsx +++ b/src/app/features/room/message/EditHistoryModal.tsx @@ -72,9 +72,14 @@ function renderContent(source: Record): ReactNode { } function getOriginalContent(evt: MatrixEvent): ReactNode { - // mEvent.getContent() returns the SDK-applied post-edit content. - // Read the raw server event to get the actual original pre-edit text. - const raw = (evt.event as { content?: Record }).content ?? {}; + // For E2EE events, evt.event.content is the ciphertext (no body field) — "(no text)" bug. + // getClearContent() returns the decrypted original content, bypassing _replacingEvent, + // so it gives us the pre-edit body even when the SDK has an edit applied. + // For unencrypted events, getClearContent() returns null, so we fall back to event.content. + const raw = + (evt.getClearContent() as Record | null) ?? + (evt.event as { content?: Record }).content ?? + {}; return renderContent(raw); }