From e1450329f2a9397505caa40fc5111c365a51e347 Mon Sep 17 00:00:00 2001 From: Cyril Date: Wed, 7 Jan 2026 12:23:32 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BF=EF=B8=8F(frontend)=20add=20screen=20r?= =?UTF-8?q?eader=20announcements=20for=20reactions=20interactions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ensures users get feedback when adding reactions via assistive tech --- .../livekit/components/ReactionPortal.tsx | 87 +++++++++++++++++-- src/frontend/src/locales/en/rooms.json | 13 ++- src/frontend/src/locales/fr/rooms.json | 13 ++- 3 files changed, 103 insertions(+), 10 deletions(-) diff --git a/src/frontend/src/features/rooms/livekit/components/ReactionPortal.tsx b/src/frontend/src/features/rooms/livekit/components/ReactionPortal.tsx index ea9a7abf..e0f71eac 100644 --- a/src/frontend/src/features/rooms/livekit/components/ReactionPortal.tsx +++ b/src/frontend/src/features/rooms/livekit/components/ReactionPortal.tsx @@ -12,6 +12,41 @@ export const FADE_OUT_THRESHOLD = 0.7 export const REACTION_SPAWN_WIDTH_RATIO = 0.2 export const INITIAL_POSITION = 200 +const srOnly = css({ + position: 'absolute', + width: '1px', + height: '1px', + padding: 0, + margin: '-1px', + overflow: 'hidden', + clip: 'rect(0, 0, 0, 0)', + whiteSpace: 'nowrap', + border: 0, +}) + +const getEmojiLabel = ( + emoji: string, + t: ReturnType['t'] +) => { + const emojiLabels: Record = { + 'thumbs-up': t('emojis.thumbs-up', { defaultValue: 'thumbs up' }), + 'thumbs-down': t('emojis.thumbs-down', { defaultValue: 'thumbs down' }), + 'clapping-hands': t('emojis.clapping-hands', { + defaultValue: 'clapping hands', + }), + 'red-heart': t('emojis.red-heart', { defaultValue: 'red heart' }), + 'face-with-tears-of-joy': t('emojis.face-with-tears-of-joy', { + defaultValue: 'face with tears of joy', + }), + 'face-with-open-mouth': t('emojis.face-with-open-mouth', { + defaultValue: 'surprised face', + }), + 'party-popper': t('emojis.party-popper', { defaultValue: 'party popper' }), + 'folded-hands': t('emojis.folded-hands', { defaultValue: 'folded hands' }), + } + return emojiLabels[emoji] ?? emoji +} + interface FloatingReactionProps { emoji: string name?: string @@ -140,11 +175,47 @@ export function ReactionPortal({ ) } -export const ReactionPortals = ({ reactions }: { reactions: Reaction[] }) => - reactions.map((instance) => ( - - )) +export const ReactionPortals = ({ reactions }: { reactions: Reaction[] }) => { + const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' }) + const [announcement, setAnnouncement] = useState(null) + const [lastAnnouncedId, setLastAnnouncedId] = useState(null) + + const latestReaction = + reactions.length > 0 ? reactions[reactions.length - 1] : undefined + + useEffect(() => { + if (!latestReaction) return + const isNewReaction = latestReaction.id !== lastAnnouncedId + if (!isNewReaction) return + + const emojiLabel = getEmojiLabel(latestReaction.emoji, t) + const participantName = latestReaction.participant?.isLocal + ? t('you') + : (latestReaction.participant?.name ?? '') + setAnnouncement(t('announce', { name: participantName, emoji: emojiLabel })) + setLastAnnouncedId(latestReaction.id) + + const timer = setTimeout(() => setAnnouncement(null), 1200) + return () => clearTimeout(timer) + }, [latestReaction, lastAnnouncedId, t]) + + return ( + <> + {reactions.map((instance) => ( + + ))} +
+ {announcement ?? ''} +
+ + ) +} diff --git a/src/frontend/src/locales/en/rooms.json b/src/frontend/src/locales/en/rooms.json index d7ee959b..8db65cbe 100644 --- a/src/frontend/src/locales/en/rooms.json +++ b/src/frontend/src/locales/en/rooms.json @@ -208,7 +208,18 @@ "reactions": { "button": "Send reaction", "send": "Send reaction {{emoji}}", - "you": "you" + "announce": "{{name}} : {{emoji}}", + "you": "you", + "emojis": { + "thumbs-up": "thumbs up", + "thumbs-down": "thumbs down", + "clapping-hands": "clapping hands", + "red-heart": "red heart", + "face-with-tears-of-joy": "face with tears of joy", + "face-with-open-mouth": "surprised face", + "party-popper": "party popper", + "folded-hands": "folded hands" + } } }, "options": { diff --git a/src/frontend/src/locales/fr/rooms.json b/src/frontend/src/locales/fr/rooms.json index 2874a42b..c9473fff 100644 --- a/src/frontend/src/locales/fr/rooms.json +++ b/src/frontend/src/locales/fr/rooms.json @@ -208,7 +208,18 @@ "reactions": { "button": "Envoyer une réaction", "send": "Envoyer la réaction {{emoji}}", - "you": "vous" + "announce": "{{name}} : {{emoji}}", + "you": "vous", + "emojis": { + "thumbs-up": "pouce levé", + "thumbs-down": "pouce baissé", + "clapping-hands": "applaudissements", + "red-heart": "cœur", + "face-with-tears-of-joy": "visage qui rit", + "face-with-open-mouth": "visage étonné", + "party-popper": "confettis", + "folded-hands": "mains jointes" + } } }, "options": {