fix: export deduplication and PiP remote mute detection

Export: timeline.getEvents() returns the entire growing window on every
pagination step, causing the same events to be added multiple times.
Fixed by tracking seen eventIds in a Set and skipping duplicates.

PiP mute: replace silence-inference with real remote participant mute
state. EC renders a [data-muted] attribute per participant tile with
aria-label=userId. Watch attribute changes via MutationObserver,
filter out local user, show overlay when any remote is muted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 22:44:11 -04:00
parent ee717e8361
commit 160db1eaef
2 changed files with 69 additions and 45 deletions
@@ -49,12 +49,18 @@ export function ExportRoomHistory({ requestClose }: ExportRoomHistoryProps) {
};
const collected: MsgRecord[] = [];
// timeline.getEvents() returns the entire growing window on every call,
// so we must deduplicate by eventId to avoid re-adding the same events
// on each pagination step.
const seen = new Set<string>();
const timeline = room.getLiveTimeline();
let canLoadMore = true;
// Collect events already in the live timeline
const addEvents = (events: ReturnType<typeof timeline.getEvents>) => {
for (const ev of events) {
const evId = ev.getId();
if (!evId || seen.has(evId)) continue;
seen.add(evId);
if (ev.getType() !== EventType.RoomMessage) continue;
if (ev.isDecryptionFailure()) continue;
const ts = ev.getTs();
@@ -68,7 +74,7 @@ export function ExportRoomHistory({ requestClose }: ExportRoomHistoryProps) {
ts,
sender: ev.getSender() ?? '',
body,
eventId: ev.getId() ?? '',
eventId: evId,
msgtype,
});
}