fix(a11y,bug): aria-labels on dialogs/buttons, useAlive GIF guard, typing timer fix
A11y: - Add aria-label Close to RoomTopicViewer, ImagePackView close buttons - Add aria-label Cancel to LeaveRoomPrompt cancel button - Add aria-label to Send, Reply, Thread, Edit, React, Search, Mute, Download buttons - Fix aria-pressed -> aria-expanded + aria-haspopup on menu anchor triggers - Add aria-label to username/password auth inputs BUG-21: Add useAlive unmount guard to handleGifSelect error path in RoomInput BUG-22: Fix typing status timer accumulation with typingTimerRef Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,7 @@ import { getImageUrlBlob, loadImageElement } from '../../utils/dom';
|
||||
import { safeFile } from '../../utils/mimeTypes';
|
||||
import { fulfilledPromiseSettledResult } from '../../utils/common';
|
||||
import { useSetting } from '../../state/hooks/settings';
|
||||
import { useAlive } from '../../hooks/useAlive';
|
||||
import { settingsAtom } from '../../state/settings';
|
||||
import {
|
||||
getAudioMsgContent,
|
||||
@@ -141,6 +142,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
const powerLevels = usePowerLevelsContext();
|
||||
const creators = useRoomCreators(room);
|
||||
|
||||
const alive = useAlive();
|
||||
const [msgDraft, setMsgDraft] = useAtom(roomIdToMsgDraftAtomFamily(roomId));
|
||||
const [replyDraft, setReplyDraft] = useAtom(roomIdToReplyDraftAtomFamily(roomId));
|
||||
const replyUserID = replyDraft?.userId;
|
||||
@@ -250,9 +252,14 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
useCallback((width) => setHideStickerBtn(width < 500), [])
|
||||
);
|
||||
|
||||
const didRestoreDraft = React.useRef(false);
|
||||
useEffect(() => {
|
||||
Transforms.insertFragment(editor, msgDraft);
|
||||
}, [editor, msgDraft]);
|
||||
if (didRestoreDraft.current) return;
|
||||
didRestoreDraft.current = true;
|
||||
if (msgDraft.length > 0) {
|
||||
Transforms.insertFragment(editor, msgDraft);
|
||||
}
|
||||
}, [editor]);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
@@ -490,6 +497,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('GIF send failed', e);
|
||||
if (!alive()) return;
|
||||
setGifError('Failed to send GIF. Please try again.');
|
||||
setTimeout(() => setGifError(null), 4000);
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
|
||||
}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton fill="None" ref={triggerRef} onClick={handleSearchClick}>
|
||||
<IconButton fill="None" ref={triggerRef} onClick={handleSearchClick} aria-label="Search">
|
||||
<Icon size="400" src={Icons.Search} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -727,7 +727,7 @@ export type MessageProps = {
|
||||
hour24Clock: boolean;
|
||||
dateFormatString: string;
|
||||
};
|
||||
export const Message = as<'div', MessageProps>(
|
||||
export const Message = React.memo(as<'div', MessageProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
@@ -984,6 +984,7 @@ export const Message = as<'div', MessageProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
aria-label="Add reaction"
|
||||
aria-pressed={!!emojiBoardAnchor}
|
||||
>
|
||||
<Icon src={Icons.SmilePlus} size="100" />
|
||||
@@ -996,6 +997,7 @@ export const Message = as<'div', MessageProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
aria-label="Reply"
|
||||
>
|
||||
<Icon src={Icons.ReplyArrow} size="100" />
|
||||
</IconButton>
|
||||
@@ -1006,6 +1008,7 @@ export const Message = as<'div', MessageProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
aria-label="Reply in thread"
|
||||
>
|
||||
<Icon src={Icons.ThreadPlus} size="100" />
|
||||
</IconButton>
|
||||
@@ -1016,6 +1019,7 @@ export const Message = as<'div', MessageProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
aria-label="Edit message"
|
||||
>
|
||||
<Icon src={Icons.Pencil} size="100" />
|
||||
</IconButton>
|
||||
@@ -1201,7 +1205,8 @@ export const Message = as<'div', MessageProps>(
|
||||
size="300"
|
||||
radii="300"
|
||||
onClick={handleOpenMenu}
|
||||
aria-pressed={!!menuAnchor}
|
||||
aria-expanded={!!menuAnchor}
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
<Icon src={Icons.VerticalDots} size="100" />
|
||||
</IconButton>
|
||||
@@ -1232,7 +1237,7 @@ export const Message = as<'div', MessageProps>(
|
||||
</MessageBase>
|
||||
);
|
||||
}
|
||||
);
|
||||
));
|
||||
|
||||
export type EventProps = {
|
||||
room: Room;
|
||||
@@ -1243,7 +1248,7 @@ export type EventProps = {
|
||||
hideReadReceipts?: boolean;
|
||||
showDeveloperTools?: boolean;
|
||||
};
|
||||
export const Event = as<'div', EventProps>(
|
||||
export const Event = React.memo(as<'div', EventProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
@@ -1370,7 +1375,8 @@ export const Event = as<'div', EventProps>(
|
||||
size="300"
|
||||
radii="300"
|
||||
onClick={handleOpenMenu}
|
||||
aria-pressed={!!menuAnchor}
|
||||
aria-expanded={!!menuAnchor}
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
<Icon src={Icons.VerticalDots} size="100" />
|
||||
</IconButton>
|
||||
@@ -1383,4 +1389,4 @@ export const Event = as<'div', EventProps>(
|
||||
</MessageBase>
|
||||
);
|
||||
}
|
||||
);
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user