fix(bug,perf): poll first-vote race, stale timeline ref, lazy GifPicker/EmojiBoard, focusItem timer leak, RoomNavItem memo

BUG-18: clearTimeout cleanup in focusItem useLayoutEffect prevents leaked timers
BUG-24: Room timeline listener catches first poll vote before Relations object exists
BUG-25: Use timelineRef.current in handleOpenEvent to prevent stale index on rapid navigation
Perf-6: React.lazy + Suspense for GifPicker and EmojiBoard (initial bundle -114 kB)
Perf-7: React.memo on RoomNavItem to prevent re-renders on unrelated state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lotus Bot
2026-05-20 21:39:35 -04:00
parent 60c2c97ba6
commit 19c47fe88e
4 changed files with 32 additions and 10 deletions
@@ -1,6 +1,7 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Box, Text } from 'folds';
import { RelationsEvent } from 'matrix-js-sdk/lib/models/relations';
import { RoomEvent } from 'matrix-js-sdk';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
type PollTextValue = Array<{ body: string }> | string;
@@ -142,6 +143,21 @@ export function PollContent({
unstableRels?.on(RelationsEvent.Add, refresh);
unstableRels?.on(RelationsEvent.Remove, refresh);
unstableRels?.on(RelationsEvent.Redaction, refresh);
// Also listen at room level: if no votes exist yet, the Relations object is null
// and the listeners above are no-ops. The room timeline event catches the first vote.
const onTimeline = (ev: any) => {
const type = ev.getType?.();
const relatesTo = ev.getContent?.()?.['m.relates_to'];
if (
(type === 'm.poll.response' || type === 'org.matrix.msc3381.poll.response') &&
relatesTo?.event_id === eventId
) {
refresh();
}
};
const room2 = mx.getRoom(roomId);
room2?.on(RoomEvent.Timeline, onTimeline);
return () => {
stableRels?.off(RelationsEvent.Add, refresh);
stableRels?.off(RelationsEvent.Remove, refresh);
@@ -149,6 +165,7 @@ export function PollContent({
unstableRels?.off(RelationsEvent.Add, refresh);
unstableRels?.off(RelationsEvent.Remove, refresh);
unstableRels?.off(RelationsEvent.Redaction, refresh);
room2?.off(RoomEvent.Timeline, onTimeline);
};
}, [mx, roomId, eventId, refresh]);