diff options
author | Eric Bailey <git@esb.lol> | 2024-06-20 17:06:57 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-20 17:06:57 -0500 |
commit | 4bba59790a04d9c708dd3cbecf96fdab7f306d94 (patch) | |
tree | 968cd47e606711abe9cf705e0b03afb6459f6910 /src | |
parent | 4d8537bcd46866c9c613cdb8a1ff9ae77ed52dfa (diff) | |
download | voidsky-4bba59790a04d9c708dd3cbecf96fdab7f306d94.tar.zst |
Add a11y context (#4586)
* Add a11y context * Feedback
Diffstat (limited to 'src')
-rw-r--r-- | src/App.native.tsx | 45 | ||||
-rw-r--r-- | src/App.web.tsx | 41 | ||||
-rw-r--r-- | src/state/a11y.tsx | 65 |
3 files changed, 111 insertions, 40 deletions
diff --git a/src/App.native.tsx b/src/App.native.tsx index 18461fdd0..4c73d8752 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -24,6 +24,7 @@ import { import {s} from '#/lib/styles' import {ThemeProvider} from '#/lib/ThemeContext' import {logger} from '#/logger' +import {Provider as A11yProvider} from '#/state/a11y' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as InvitesStateProvider} from '#/state/invites' @@ -152,27 +153,29 @@ function App() { * that is set up in the InnerApp component above. */ return ( - <KeyboardProvider enabled={false} statusBarTranslucent={true}> - <SessionProvider> - <ShellStateProvider> - <PrefsStateProvider> - <InvitesStateProvider> - <ModalStateProvider> - <DialogStateProvider> - <LightboxStateProvider> - <I18nProvider> - <PortalProvider> - <InnerApp /> - </PortalProvider> - </I18nProvider> - </LightboxStateProvider> - </DialogStateProvider> - </ModalStateProvider> - </InvitesStateProvider> - </PrefsStateProvider> - </ShellStateProvider> - </SessionProvider> - </KeyboardProvider> + <A11yProvider> + <KeyboardProvider enabled={false} statusBarTranslucent={true}> + <SessionProvider> + <ShellStateProvider> + <PrefsStateProvider> + <InvitesStateProvider> + <ModalStateProvider> + <DialogStateProvider> + <LightboxStateProvider> + <I18nProvider> + <PortalProvider> + <InnerApp /> + </PortalProvider> + </I18nProvider> + </LightboxStateProvider> + </DialogStateProvider> + </ModalStateProvider> + </InvitesStateProvider> + </PrefsStateProvider> + </ShellStateProvider> + </SessionProvider> + </KeyboardProvider> + </A11yProvider> ) } diff --git a/src/App.web.tsx b/src/App.web.tsx index 6af3c7d6f..00939c9eb 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -13,6 +13,7 @@ import {QueryProvider} from '#/lib/react-query' import {Provider as StatsigProvider} from '#/lib/statsig/statsig' import {ThemeProvider} from '#/lib/ThemeContext' import {logger} from '#/logger' +import {Provider as A11yProvider} from '#/state/a11y' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as InvitesStateProvider} from '#/state/invites' @@ -135,25 +136,27 @@ function App() { * that is set up in the InnerApp component above. */ return ( - <SessionProvider> - <ShellStateProvider> - <PrefsStateProvider> - <InvitesStateProvider> - <ModalStateProvider> - <DialogStateProvider> - <LightboxStateProvider> - <I18nProvider> - <PortalProvider> - <InnerApp /> - </PortalProvider> - </I18nProvider> - </LightboxStateProvider> - </DialogStateProvider> - </ModalStateProvider> - </InvitesStateProvider> - </PrefsStateProvider> - </ShellStateProvider> - </SessionProvider> + <A11yProvider> + <SessionProvider> + <ShellStateProvider> + <PrefsStateProvider> + <InvitesStateProvider> + <ModalStateProvider> + <DialogStateProvider> + <LightboxStateProvider> + <I18nProvider> + <PortalProvider> + <InnerApp /> + </PortalProvider> + </I18nProvider> + </LightboxStateProvider> + </DialogStateProvider> + </ModalStateProvider> + </InvitesStateProvider> + </PrefsStateProvider> + </ShellStateProvider> + </SessionProvider> + </A11yProvider> ) } diff --git a/src/state/a11y.tsx b/src/state/a11y.tsx new file mode 100644 index 000000000..aefcfd1ec --- /dev/null +++ b/src/state/a11y.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import {AccessibilityInfo} from 'react-native' +import {isReducedMotion} from 'react-native-reanimated' + +import {isWeb} from '#/platform/detection' + +const Context = React.createContext({ + reduceMotionEnabled: false, + screenReaderEnabled: false, +}) + +export function useA11y() { + return React.useContext(Context) +} + +export function Provider({children}: React.PropsWithChildren<{}>) { + const [reduceMotionEnabled, setReduceMotionEnabled] = React.useState(() => + isReducedMotion(), + ) + const [screenReaderEnabled, setScreenReaderEnabled] = React.useState(false) + + React.useEffect(() => { + const reduceMotionChangedSubscription = AccessibilityInfo.addEventListener( + 'reduceMotionChanged', + enabled => { + setReduceMotionEnabled(enabled) + }, + ) + const screenReaderChangedSubscription = AccessibilityInfo.addEventListener( + 'screenReaderChanged', + enabled => { + setScreenReaderEnabled(enabled) + }, + ) + + ;(async () => { + const [_reduceMotionEnabled, _screenReaderEnabled] = await Promise.all([ + AccessibilityInfo.isReduceMotionEnabled(), + AccessibilityInfo.isScreenReaderEnabled(), + ]) + setReduceMotionEnabled(_reduceMotionEnabled) + setScreenReaderEnabled(_screenReaderEnabled) + })() + + return () => { + reduceMotionChangedSubscription.remove() + screenReaderChangedSubscription.remove() + } + }, []) + + const ctx = React.useMemo(() => { + return { + reduceMotionEnabled, + /** + * Always returns true on web. For now, we're using this for mobile a11y, + * so we reset to false on web. + * + * @see https://github.com/necolas/react-native-web/discussions/2072 + */ + screenReaderEnabled: isWeb ? false : screenReaderEnabled, + } + }, [reduceMotionEnabled, screenReaderEnabled]) + + return <Context.Provider value={ctx}>{children}</Context.Provider> +} |