From bafd9cbe75dd953fa8954639a843dfed5807cc97 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Tue, 9 Jun 2026 22:56:06 -0400 Subject: [PATCH] fix: address confirmed bugs from LOTUS_BUGS.md audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useFileDrop: reset drag overlay when mouse leaves browser window (relatedTarget === null signals viewport exit, counter was getting stuck) - useDeviceVerificationStatus: add member count to useMemo deps so new room members' devices get checked, not just initial joined members - index.css: define --bg-surface-variant used by VoiceMessageRecorder, MessageSearch, SearchFilters, UserRoomProfile (was falling back to transparent) - syntaxHighlight: fix Python inline comments — # after space/tab was treated as plain text; only start-of-line was recognised - usePresenceUpdater: replace internal baseUrl cast with mx.getHomeserverUrl() - useLocalMessageSearch: scan all linked timelines via getUnfilteredTimelineSet() not just the live window, so scrolled-back history is included in E2EE search - RoomViewHeader: show search button in encrypted rooms — local search is implemented and handles them; the guard was a holdover from before it existed - recent-emoji: return emojis in recency order (array is already unshifted on use) instead of sorting by total usage count Skipped: media gallery memory leak (needs virtualization refactor), bookmark race condition (needs queue/lock), Night Light portal coverage (position:fixed already covers full viewport — not a real bug). Co-Authored-By: Claude Sonnet 4.6 --- src/app/features/message-search/useLocalMessageSearch.ts | 5 ++++- src/app/features/room/RoomViewHeader.tsx | 2 +- src/app/hooks/useDeviceVerificationStatus.ts | 4 +++- src/app/hooks/useFileDrop.ts | 8 +++++++- src/app/hooks/usePresenceUpdater.ts | 2 +- src/app/plugins/recent-emoji.ts | 1 - src/app/utils/syntaxHighlight.ts | 2 +- src/index.css | 2 ++ 8 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/app/features/message-search/useLocalMessageSearch.ts b/src/app/features/message-search/useLocalMessageSearch.ts index b7340d269..6431787a6 100644 --- a/src/app/features/message-search/useLocalMessageSearch.ts +++ b/src/app/features/message-search/useLocalMessageSearch.ts @@ -50,7 +50,10 @@ export const useLocalMessageSearch = () => { encryptedRoomsCount += 1; - const events = room.getLiveTimeline().getEvents(); + const events = room + .getUnfilteredTimelineSet() + .getTimelines() + .flatMap((tl) => tl.getEvents()); if (events.length === 0) continue; searchedRoomsCount += 1; diff --git a/src/app/features/room/RoomViewHeader.tsx b/src/app/features/room/RoomViewHeader.tsx index 8d96c1c78..b43d3acfe 100644 --- a/src/app/features/room/RoomViewHeader.tsx +++ b/src/app/features/room/RoomViewHeader.tsx @@ -558,7 +558,7 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) { - {!encryptedRoom && ( + {( room.getJoinedMembers().map((m) => m.userId), + // room.roomId guards against room changes; getJoinedMemberCount() ensures + // the list refreshes when members join/leave so new devices get checked. // eslint-disable-next-line react-hooks/exhaustive-deps - [room.roomId], + [room.roomId, room.getJoinedMemberCount()], ); const updateCount = useCallback(async () => { diff --git a/src/app/hooks/useFileDrop.ts b/src/app/hooks/useFileDrop.ts index e7d97ba86..d88def72e 100644 --- a/src/app/hooks/useFileDrop.ts +++ b/src/app/hooks/useFileDrop.ts @@ -42,7 +42,13 @@ export const useFileDropZone = ( setActive(true); } }; - const handleDragLeave = () => { + const handleDragLeave = (evt: DragEvent) => { + if (evt.relatedTarget === null) { + // Mouse left the browser window — reset unconditionally + dragCounterRef.current = 0; + setActive(false); + return; + } dragCounterRef.current -= 1; if (dragCounterRef.current <= 0) { dragCounterRef.current = 0; diff --git a/src/app/hooks/usePresenceUpdater.ts b/src/app/hooks/usePresenceUpdater.ts index e84b62dbc..742649cb1 100644 --- a/src/app/hooks/usePresenceUpdater.ts +++ b/src/app/hooks/usePresenceUpdater.ts @@ -77,7 +77,7 @@ export function usePresenceUpdater() { const handlePageHide = () => { const userId = mx.getUserId(); const token = mx.getAccessToken(); - const baseUrl = (mx as unknown as { baseUrl: string }).baseUrl; + const baseUrl = mx.getHomeserverUrl(); if (!userId || !token || !baseUrl) return; fetch(`${baseUrl}/_matrix/client/v3/presence/${encodeURIComponent(userId)}/status`, { diff --git a/src/app/plugins/recent-emoji.ts b/src/app/plugins/recent-emoji.ts index 49eec6a29..804fbd640 100644 --- a/src/app/plugins/recent-emoji.ts +++ b/src/app/plugins/recent-emoji.ts @@ -16,7 +16,6 @@ export const getRecentEmojis = (mx: MatrixClient, limit?: number): IEmoji[] => { if (!Array.isArray(recentEmoji)) return []; return recentEmoji - .sort((e1, e2) => e2[1] - e1[1]) .slice(0, limit) .reduce((list, [unicode]) => { const emoji = emojis.find((e) => e.unicode === unicode); diff --git a/src/app/utils/syntaxHighlight.ts b/src/app/utils/syntaxHighlight.ts index 1c69f7577..ee7d82e9e 100644 --- a/src/app/utils/syntaxHighlight.ts +++ b/src/app/utils/syntaxHighlight.ts @@ -222,7 +222,7 @@ export function tokenize(code: string, lang: string): SyntaxToken[] { if ( code[i] === '#' && (normalised === 'python' || normalised === 'py') && - (i === 0 || code[i - 1] === '\n') + (i === 0 || code[i - 1] === '\n' || code[i - 1] === ' ' || code[i - 1] === '\t') ) { const nlHash = code.indexOf('\n', i); const closeIdx = nlHash === -1 ? len : nlHash; diff --git a/src/index.css b/src/index.css index f9323921d..a33284952 100644 --- a/src/index.css +++ b/src/index.css @@ -12,6 +12,7 @@ /* semantic surface vars used by poll, location, upload card, gif picker */ --bg-surface: #ffffff; --bg-surface-low: rgba(0, 0, 0, 0.04); + --bg-surface-variant: rgba(0, 0, 0, 0.07); --bg-surface-active: rgba(0, 0, 0, 0.1); --bg-surface-border: rgba(0, 0, 0, 0.14); --text-primary: #1a1a1a; @@ -37,6 +38,7 @@ /* semantic surface vars — dark overrides */ --bg-surface: #25272e; --bg-surface-low: rgba(255, 255, 255, 0.05); + --bg-surface-variant: rgba(255, 255, 255, 0.08); --bg-surface-active: rgba(255, 255, 255, 0.1); --bg-surface-border: rgba(255, 255, 255, 0.12); --text-primary: #e0e5ed;