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 React, { useEffect, useState } from 'react';
|
||||||
import { Room } from 'matrix-js-sdk';
|
import { Room, RoomStateEvent, RoomStateEventHandlerMap } from 'matrix-js-sdk';
|
||||||
import {
|
import {
|
||||||
Icon,
|
Icon,
|
||||||
Icons,
|
Icons,
|
||||||
@@ -23,6 +23,7 @@ import { StackedAvatar } from '../stacked-avatar';
|
|||||||
import { EventReaders } from '../event-readers';
|
import { EventReaders } from '../event-readers';
|
||||||
import { stopPropagation } from '../../utils/keyboard';
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
import { useModalStyle } from '../../hooks/useModalStyle';
|
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||||
|
import { useForceUpdate } from '../../hooks/useForceUpdate';
|
||||||
import * as css from './ReadReceiptAvatars.css';
|
import * as css from './ReadReceiptAvatars.css';
|
||||||
|
|
||||||
const MAX_DISPLAY = 5;
|
const MAX_DISPLAY = 5;
|
||||||
@@ -41,6 +42,28 @@ export function ReadReceiptAvatars({
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
||||||
const modalStyle = useModalStyle(360);
|
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;
|
if (userIds.length === 0) return null;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user