✨(frontend) create a11y store to manage user option toggles
sets up state handling for enabling or disabling a11y preferences
This commit is contained in:
@@ -6,6 +6,8 @@ import { Participant } from 'livekit-client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Reaction } from '@/features/rooms/livekit/components/controls/ReactionsToggle'
|
||||
import { getEmojiLabel } from '@/features/rooms/livekit/utils/reactionUtils'
|
||||
import { accessibilityStore } from '@/stores/accessibility'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
export const ANIMATION_DURATION = 3000
|
||||
export const ANIMATION_DISTANCE = 300
|
||||
@@ -155,6 +157,7 @@ export function ReactionPortal({
|
||||
|
||||
export const ReactionPortals = ({ reactions }: { reactions: Reaction[] }) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
|
||||
const { announceReactions } = useSnapshot(accessibilityStore)
|
||||
const [announcement, setAnnouncement] = useState<string | null>(null)
|
||||
const [lastAnnouncedId, setLastAnnouncedId] = useState<number | null>(null)
|
||||
|
||||
@@ -162,6 +165,10 @@ export const ReactionPortals = ({ reactions }: { reactions: Reaction[] }) => {
|
||||
reactions.length > 0 ? reactions[reactions.length - 1] : undefined
|
||||
|
||||
useEffect(() => {
|
||||
if (!announceReactions) {
|
||||
setAnnouncement(null)
|
||||
return
|
||||
}
|
||||
if (!latestReaction) return
|
||||
const isNewReaction = latestReaction.id !== lastAnnouncedId
|
||||
if (!isNewReaction) return
|
||||
@@ -175,7 +182,7 @@ export const ReactionPortals = ({ reactions }: { reactions: Reaction[] }) => {
|
||||
|
||||
const timer = setTimeout(() => setAnnouncement(null), 1200)
|
||||
return () => clearTimeout(timer)
|
||||
}, [latestReaction, lastAnnouncedId, t])
|
||||
}, [latestReaction, lastAnnouncedId, announceReactions, t])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
76
src/frontend/src/stores/accessibility.ts
Normal file
76
src/frontend/src/stores/accessibility.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { proxy, subscribe } from 'valtio'
|
||||
import { STORAGE_KEYS } from '@/utils/storageKeys'
|
||||
import { deserializeToProxyMap } from '@/utils/valtio'
|
||||
|
||||
type AccessibilityState = {
|
||||
announceReactions: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_STATE: AccessibilityState = {
|
||||
announceReactions: false,
|
||||
}
|
||||
|
||||
function getAccessibilityState(): AccessibilityState {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEYS.ACCESSIBILITY)
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored)
|
||||
return {
|
||||
...DEFAULT_STATE,
|
||||
...parsed,
|
||||
announceReactions:
|
||||
typeof parsed.announceReactions === 'boolean'
|
||||
? parsed.announceReactions
|
||||
: DEFAULT_STATE.announceReactions,
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy migration: if the setting was previously stored in notifications
|
||||
const legacy = localStorage.getItem(STORAGE_KEYS.NOTIFICATIONS)
|
||||
if (legacy) {
|
||||
try {
|
||||
const parsedLegacy = JSON.parse(legacy, deserializeToProxyMap)
|
||||
if (typeof parsedLegacy?.announceReactions === 'boolean') {
|
||||
const migratedState: AccessibilityState = {
|
||||
...DEFAULT_STATE,
|
||||
...parsedLegacy,
|
||||
announceReactions: parsedLegacy.announceReactions,
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.ACCESSIBILITY,
|
||||
JSON.stringify(migratedState)
|
||||
)
|
||||
localStorage.removeItem(STORAGE_KEYS.NOTIFICATIONS)
|
||||
} catch {
|
||||
// ignore persistence issues during migration
|
||||
}
|
||||
|
||||
return migratedState
|
||||
}
|
||||
} catch {
|
||||
// ignore legacy parsing issues
|
||||
}
|
||||
}
|
||||
|
||||
return DEFAULT_STATE
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[AccessibilityStore] Failed to parse stored settings:',
|
||||
error
|
||||
)
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}
|
||||
|
||||
export const accessibilityStore = proxy<AccessibilityState>(
|
||||
getAccessibilityState()
|
||||
)
|
||||
|
||||
subscribe(accessibilityStore, () => {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.ACCESSIBILITY,
|
||||
JSON.stringify(accessibilityStore)
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user