diff --git a/src/Router.tsx b/src/Router.tsx index 258ba5a1..05478aed 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -18,6 +18,7 @@ import { useListenToCampaigns } from "stores/campaign/useListenToCampaigns"; import { useListenToAuth } from "stores/auth/useListenToAuth"; import { useListenToWorlds } from "stores/world/useListenToWorlds"; import { useListenToOracleSettings } from "stores/settings/useListenToOracleSettings"; +import { useListenToAccessibilitySettings } from "stores/accessibilitySettings/useListenToAccessibilitySettings"; const router = createBrowserRouter( createRoutesFromElements( @@ -108,6 +109,8 @@ const router = createBrowserRouter( export function Router() { useListenToAuth(); + useListenToAccessibilitySettings(); + useListenToCharacters(); useListenToCampaigns(); useListenToWorlds(); diff --git a/src/api-calls/custom-move-oracle-settings/settings/_getRef.ts b/src/api-calls/custom-move-oracle-settings/settings/_getRef.ts index e37ee7d3..f5e99f24 100644 --- a/src/api-calls/custom-move-oracle-settings/settings/_getRef.ts +++ b/src/api-calls/custom-move-oracle-settings/settings/_getRef.ts @@ -5,7 +5,7 @@ import { doc, DocumentReference, } from "firebase/firestore"; -import { OracleSettings } from "types/UserSettings.type"; +import { OracleSettings } from "types/UserOracleSettings.type"; export function constructUserSettingsCollectionPath(userId: string) { return `/users/${userId}/settings`; diff --git a/src/api-calls/custom-move-oracle-settings/settings/listenToOracleSettings.ts b/src/api-calls/custom-move-oracle-settings/settings/listenToOracleSettings.ts index e2f0db5d..c460ef8e 100644 --- a/src/api-calls/custom-move-oracle-settings/settings/listenToOracleSettings.ts +++ b/src/api-calls/custom-move-oracle-settings/settings/listenToOracleSettings.ts @@ -1,6 +1,6 @@ import { onSnapshot, setDoc } from "firebase/firestore"; import { decodeDataswornId } from "functions/dataswornIdEncoder"; -import { OracleSettings } from "types/UserSettings.type"; +import { OracleSettings } from "types/UserOracleSettings.type"; import { getUserOracleSettingsDoc } from "./_getRef"; export function listenToOracleSettings( diff --git a/src/api-calls/custom-move-oracle-settings/updateSettings.ts b/src/api-calls/custom-move-oracle-settings/updateSettings.ts index 85092369..0a1f49d7 100644 --- a/src/api-calls/custom-move-oracle-settings/updateSettings.ts +++ b/src/api-calls/custom-move-oracle-settings/updateSettings.ts @@ -1,4 +1,4 @@ -import { arrayRemove, arrayUnion, setDoc, updateDoc } from "firebase/firestore"; +import { setDoc, updateDoc } from "firebase/firestore"; import { getCampaignSettingsDoc, getCharacterSettingsDoc } from "./_getRef"; import { createApiFunction } from "api-calls/createApiFunction"; import { SettingsDoc } from "types/Settings.type"; diff --git a/src/api-calls/user/settings/_getRef.ts b/src/api-calls/user/settings/_getRef.ts new file mode 100644 index 00000000..15bad48c --- /dev/null +++ b/src/api-calls/user/settings/_getRef.ts @@ -0,0 +1,30 @@ +import { firestore } from "config/firebase.config"; +import { + CollectionReference, + DocumentReference, + collection, + doc, +} from "firebase/firestore"; +import { IAccessibilitySettings } from "types/UserAccessibilitySettings.type"; + +export function constructUserSettingsCollectionPath(userId: string) { + return `/users/${userId}/settings`; +} + +export function constructUserAccessibilitySettingsDocPath(userId: string) { + return `/users/${userId}/settings/accessibility`; +} + +export function getUserSettingsCollectionRef(userId: string) { + return collection( + firestore, + constructUserSettingsCollectionPath(userId) + ) as CollectionReference; +} + +export function getUserAccessibilitySettingsDoc(userId: string) { + return doc( + firestore, + constructUserAccessibilitySettingsDocPath(userId) + ) as DocumentReference; +} diff --git a/src/api-calls/user/settings/listenToAccessibilitySettings.ts b/src/api-calls/user/settings/listenToAccessibilitySettings.ts new file mode 100644 index 00000000..c3869442 --- /dev/null +++ b/src/api-calls/user/settings/listenToAccessibilitySettings.ts @@ -0,0 +1,12 @@ +import { onSnapshot } from "firebase/firestore"; +import { IAccessibilitySettings } from "types/UserAccessibilitySettings.type"; +import { getUserAccessibilitySettingsDoc } from "./_getRef"; + +export const listenToAccessibilitySettings = ( + uid: string, + onSettings: (settings: IAccessibilitySettings) => void +) => { + return onSnapshot(getUserAccessibilitySettingsDoc(uid), (snapshot) => { + onSettings(snapshot.data() ?? {}); + }); +}; diff --git a/src/api-calls/user/settings/updateAccessibilitySettings.ts b/src/api-calls/user/settings/updateAccessibilitySettings.ts new file mode 100644 index 00000000..de5ca344 --- /dev/null +++ b/src/api-calls/user/settings/updateAccessibilitySettings.ts @@ -0,0 +1,14 @@ +import { ApiFunction } from "api-calls/createApiFunction"; +import { setDoc, updateDoc } from "firebase/firestore"; +import { IAccessibilitySettings } from "types/UserAccessibilitySettings.type"; +import { getUserAccessibilitySettingsDoc } from "./_getRef"; + +export const updateAccessibilitySettings: ApiFunction< + { uid: string; settings: Partial }, + void +> = (params) => { + const { uid, settings } = params; + return setDoc(getUserAccessibilitySettingsDoc(uid), settings, { + merge: true, + }); +}; diff --git a/src/components/features/characters/StatComponent.tsx b/src/components/features/characters/StatComponent.tsx index a2f5a683..ce9218c8 100644 --- a/src/components/features/characters/StatComponent.tsx +++ b/src/components/features/characters/StatComponent.tsx @@ -68,7 +68,7 @@ export function StatComponent(props: StatComponentProps) { ["background-color", "border-color", "outline-width"], { duration: theme.transitions.duration.shorter } ), - "&>p[id$='-label']": { + "&>[id$='-label']": { transition: theme.transitions.create( ["background-color", "color"], { duration: theme.transitions.duration.shorter } @@ -82,7 +82,7 @@ export function StatComponent(props: StatComponentProps) { updateTrack || disableRoll ? {} : { - "&>p[id$='-label']": { + "&>[id$='-label']": { backgroundColor: theme.palette.background.paperInlayDarker, color: theme.palette.text.primary, }, @@ -114,7 +114,7 @@ export function StatComponent(props: StatComponentProps) { display={"block"} textAlign={"center"} variant={"subtitle1"} - component={"p"} + component={"span"} lineHeight={1} id={`${label}-label`} > @@ -135,7 +135,7 @@ export function StatComponent(props: StatComponentProps) { updateTrack ? { lineHeight: "1.5rem" } : {}, ]} variant={"h6"} - component={"p"} + component={"span"} textAlign={"center"} > diff --git a/src/components/shared/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx b/src/components/shared/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx new file mode 100644 index 00000000..e64a0986 --- /dev/null +++ b/src/components/shared/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx @@ -0,0 +1,59 @@ +import { + Box, + Button, + Checkbox, + Dialog, + DialogActions, + DialogContent, + FormControlLabel, +} from "@mui/material"; +import { DialogTitleWithCloseButton } from "../DialogTitleWithCloseButton"; +import { useStore } from "stores/store"; + +export interface AccessibilitySettingsDialogProps { + open?: boolean; + onClose: () => void; +} + +export function AccessibilitySettingsDialog( + props: AccessibilitySettingsDialogProps +) { + const { open, onClose } = props; + + const accessibilitySettings = useStore( + (store) => store.accessibilitySettings.settings + ); + + const updateSettings = useStore( + (store) => store.accessibilitySettings.updateSettings + ); + + return ( + + + Accessibility Settings + + + + + updateSettings({ verboseRollResults: value }).catch(() => {}) + } + /> + } + label={"Announce rolls and modifiers when rolling?"} + sx={{ textTransform: "capitalize", marginRight: 3 }} + /> + + + + + + + ); +} diff --git a/src/components/shared/DialogTitleWithCloseButton.tsx b/src/components/shared/DialogTitleWithCloseButton.tsx index 8ed17518..607b1f09 100644 --- a/src/components/shared/DialogTitleWithCloseButton.tsx +++ b/src/components/shared/DialogTitleWithCloseButton.tsx @@ -1,6 +1,13 @@ import { Box, DialogTitle, IconButton, Typography } from "@mui/material"; -import { PropsWithChildren, ReactNode } from "react"; +import { + PropsWithChildren, + ReactNode, + useEffect, + useRef, + useState, +} from "react"; import CloseIcon from "@mui/icons-material/Close"; +import { useScreenReaderAnnouncement } from "providers/ScreenReaderAnnouncementProvider"; export interface DialogTitleWithCloseButtonProps extends PropsWithChildren { onClose: () => void; @@ -11,22 +18,52 @@ export function DialogTitleWithCloseButton( ) { const { children, actions, onClose } = props; + const { announcement } = useScreenReaderAnnouncement(); + + const [changedAnnouncement, setChangedAnnouncement] = useState< + string | undefined + >(); + const isFirstLoadRef = useRef(true); + useEffect(() => { + if (!isFirstLoadRef.current) { + setChangedAnnouncement(announcement); + } + isFirstLoadRef.current = false; + }, [announcement]); + return ( - - - {children} - - - {actions} - onClose()}> - - + <> + + {changedAnnouncement} - + + + {children} + + + {actions} + onClose()}> + + + + + ); } diff --git a/src/components/shared/Layout/HeaderMenu.tsx b/src/components/shared/Layout/HeaderMenu.tsx index 2182bb31..15b17df0 100644 --- a/src/components/shared/Layout/HeaderMenu.tsx +++ b/src/components/shared/Layout/HeaderMenu.tsx @@ -22,6 +22,8 @@ import { CHARACTER_ROUTES, constructCharacterPath, } from "pages/Character/routes"; +import AccessibilityIcon from "@mui/icons-material/AccessibilityNew"; +import { AccessibilitySettingsDialog } from "../AccessibilitySettingsDialog/AccessibilitySettingsDialog"; export function HeaderMenu() { const userId = useStore((store) => store.auth.uid); @@ -34,6 +36,9 @@ export function HeaderMenu() { const { gameSystem, chooseGameSystem } = useGameSystem(); const isLocal = getIsLocalEnvironment(); + const [accessibilitySettingsOpen, setAccessibilitySettingsOpen] = + useState(false); + return ( <> setMenuOpen(false)} anchorEl={anchorRef.current} > + { + setMenuOpen(false); + setAccessibilitySettingsOpen(true); + }} + > + + + + Accessibility Settings + { setMenuOpen(false); @@ -99,6 +115,10 @@ export function HeaderMenu() { )} + setAccessibilitySettingsOpen(false)} + /> ); } diff --git a/src/components/shared/Layout/Layout.tsx b/src/components/shared/Layout/Layout.tsx index fef27ae2..baf79ff7 100644 --- a/src/components/shared/Layout/Layout.tsx +++ b/src/components/shared/Layout/Layout.tsx @@ -16,6 +16,7 @@ import { sendPageViewEvent } from "lib/analytics.lib"; import { UserNameDialog } from "components/shared/UserNameDialog"; import { useStore } from "stores/store"; import { AUTH_STATE } from "stores/auth/auth.slice.type"; +import { SkipToContentButton } from "./SkipToContentButton"; export interface LayoutProps {} @@ -68,8 +69,9 @@ export function Layout(props: LayoutProps) { backgroundColor: theme.palette.background.default, })} > +
-
+