Bug fixes, security hardening, and performance improvements
- BUG-16: Fixed pagination deadlock (fetching flag stuck on error path) - BUG-17: Fixed absoluteIndex===0 falsy check skipping unread jump - BUG-19: Fixed mEvt.getRoomId()! non-null assertion crash - BUG-20: Wrapped getSettings()/setSettings() in try/catch for corrupt localStorage - SEC: Replaced randomStr() Math.random() with crypto.getRandomValues() CSPRNG - SEC: Fixed afterLoginRedirectPath open redirect validation - SEC: Narrowed OSM iframe sandbox to scripts-only (removed allow-same-origin) - Perf-2: Memoized selectAtom in useSetting (prevented new atom ref per render) - Perf-4: Fixed typingMembers setTimeout leak (tracked timers per user/room) - Perf-8: Memoized getChatBg() result in RoomView (not inline in JSX) - Perf-12: Replaced body.class * font-family with body.class (inherited) - Perf-15: Memoized typingNames array chain in RoomViewTyping - Perf-9: Added blob URL cleanup useEffect in AudioContent - BUG: Fixed forEach(async) -> Promise.all in useCommands join handler - BUG: Fixed useCompositionEndTracking missing dependency array - A11y: Fixed spoiler button aria-pressed + keyboard handler - A11y: Added aria-label to Send message button - Build: Set sourcemap:false, removed netlify.toml from copyFiles Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -802,7 +802,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
<Icon src={Icons.Pin} size="100" />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton onClick={submit} variant="SurfaceVariant" size="300" radii="300">
|
||||
<IconButton onClick={submit} variant="SurfaceVariant" size="300" radii="300" aria-label="Send message">
|
||||
<Icon src={Icons.Send} />
|
||||
</IconButton>
|
||||
</>
|
||||
|
||||
@@ -341,7 +341,7 @@ const useTimelinePagination = (
|
||||
})
|
||||
);
|
||||
if (err) {
|
||||
// TODO: handle pagination error.
|
||||
fetching = false;
|
||||
return;
|
||||
}
|
||||
const fetchedTimeline =
|
||||
@@ -622,7 +622,8 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
// Check if the document is in focus (user is actively viewing the app),
|
||||
// and either there are no unread messages or the latest message is from the current user.
|
||||
// If either condition is met, trigger the markAsRead function to send a read receipt.
|
||||
requestAnimationFrame(() => markAsRead(mx, mEvt.getRoomId()!, hideActivity));
|
||||
const _roomId = mEvt.getRoomId();
|
||||
if (_roomId) requestAnimationFrame(() => markAsRead(mx, _roomId, hideActivity));
|
||||
}
|
||||
|
||||
if (!document.hasFocus() && !unreadInfo) {
|
||||
@@ -826,7 +827,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
const evtTimeline = getEventTimeline(room, readUptoEventId);
|
||||
const absoluteIndex =
|
||||
evtTimeline && getEventIdAbsoluteIndex(linkedTimelines, evtTimeline, readUptoEventId);
|
||||
if (absoluteIndex) {
|
||||
if (typeof absoluteIndex === 'number') {
|
||||
scrollToItem(absoluteIndex, {
|
||||
behavior: 'instant',
|
||||
align: 'start',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import { Box, Text, config } from 'folds';
|
||||
import { EventType } from 'matrix-js-sdk';
|
||||
import { ReactEditor } from 'slate-react';
|
||||
@@ -98,8 +98,13 @@ export function RoomView({ eventId }: { eventId?: string }) {
|
||||
)
|
||||
);
|
||||
|
||||
const chatBgStyle = useMemo(
|
||||
() => getChatBg(lotusTerminal && chatBackground === 'none' ? 'tactical' : chatBackground, isDark),
|
||||
[chatBackground, lotusTerminal, isDark]
|
||||
);
|
||||
|
||||
return (
|
||||
<Page ref={roomViewRef} style={getChatBg(lotusTerminal && chatBackground === 'none' ? 'tactical' : chatBackground, isDark)}>
|
||||
<Page ref={roomViewRef} style={chatBgStyle}>
|
||||
<Box grow="Yes" direction="Column">
|
||||
<RoomTimeline
|
||||
key={roomId}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, Icon, IconButton, Icons, Text, as } from 'folds';
|
||||
import { Room } from 'matrix-js-sdk';
|
||||
import classNames from 'classnames';
|
||||
@@ -20,12 +20,18 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>(
|
||||
const mx = useMatrixClient();
|
||||
const typingMembers = useRoomTypingMember(room.roomId);
|
||||
|
||||
const typingNames = typingMembers
|
||||
.filter((receipt) => receipt.userId !== mx.getUserId())
|
||||
.map(
|
||||
(receipt) => getMemberDisplayName(room, receipt.userId) ?? getMxIdLocalPart(receipt.userId)
|
||||
)
|
||||
.reverse();
|
||||
const myUserId = mx.getUserId();
|
||||
const typingNames = useMemo(
|
||||
() =>
|
||||
typingMembers
|
||||
.filter((receipt) => receipt.userId !== myUserId)
|
||||
.map(
|
||||
(receipt) =>
|
||||
getMemberDisplayName(room, receipt.userId) ?? getMxIdLocalPart(receipt.userId)
|
||||
)
|
||||
.reverse(),
|
||||
[typingMembers, myUserId, room]
|
||||
);
|
||||
|
||||
if (typingNames.length === 0) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user