fix(receipts): refresh read-receipt avatars/names on member changes (N6)
ReadReceiptAvatars read avatar + display-name from room member state at render time, so they only refreshed when the parent Message re-rendered (on a receipt change). A reader changing their avatar/display-name left a stale avatar/name until an unrelated re-render. Subscribe to RoomStateEvent.Members (the only signal that fires for name, avatar AND membership changes — RoomMemberEvent has no Avatar event) scoped to the displayed readers, and force a re-render so the pill updates live. Listener is cleaned up; userIds identity is stable (memoized in the read- positions map) so no subscribe churn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Room } from 'matrix-js-sdk';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Room, RoomStateEvent, RoomStateEventHandlerMap } from 'matrix-js-sdk';
|
||||
import {
|
||||
Icon,
|
||||
Icons,
|
||||
@@ -23,6 +23,7 @@ import { StackedAvatar } from '../stacked-avatar';
|
||||
import { EventReaders } from '../event-readers';
|
||||
import { stopPropagation } from '../../utils/keyboard';
|
||||
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||
import { useForceUpdate } from '../../hooks/useForceUpdate';
|
||||
import * as css from './ReadReceiptAvatars.css';
|
||||
|
||||
const MAX_DISPLAY = 5;
|
||||
@@ -41,6 +42,28 @@ export function ReadReceiptAvatars({
|
||||
const [open, setOpen] = useState(false);
|
||||
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
||||
const modalStyle = useModalStyle(360);
|
||||
const [, forceUpdate] = useForceUpdate();
|
||||
|
||||
// The avatars + names below are read from room member state at render time, so
|
||||
// they only refresh when this component re-renders (which the timeline does on a
|
||||
// receipt change). Re-render on a member-state change of any displayed reader so
|
||||
// an avatar/display-name update shows live. RoomStateEvent.Members fires for name,
|
||||
// avatar AND membership changes (RoomMemberEvent has no Avatar signal).
|
||||
useEffect(() => {
|
||||
const handleMembers: RoomStateEventHandlerMap[RoomStateEvent.Members] = (
|
||||
event,
|
||||
_state,
|
||||
member
|
||||
) => {
|
||||
if (event.getRoomId() === room.roomId && userIds.includes(member.userId)) {
|
||||
forceUpdate();
|
||||
}
|
||||
};
|
||||
mx.on(RoomStateEvent.Members, handleMembers);
|
||||
return () => {
|
||||
mx.removeListener(RoomStateEvent.Members, handleMembers);
|
||||
};
|
||||
}, [mx, room.roomId, userIds, forceUpdate]);
|
||||
|
||||
if (userIds.length === 0) return null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user