diff --git a/src/App.native.tsx b/src/App.native.tsx index 87429d8458..8ad62b1295 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -72,6 +72,7 @@ import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs' import {Provider as PortalProvider} from '#/components/Portal' import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' +import {Provider as BlockingAnnouncementsPortalProvider} from '#/components/dialogs/BlockingAnnouncements' import {Splash} from '#/Splash' import {BottomSheetProvider} from '../modules/bottom-sheet' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' @@ -220,16 +221,18 @@ function App() { - - - - - - - - - + + + + + + + + + + + diff --git a/src/App.web.tsx b/src/App.web.tsx index 1f795cb3e0..2abada05d7 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -60,6 +60,7 @@ import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialo import {Provider as PortalProvider} from '#/components/Portal' import {Provider as ActiveVideoProvider} from '#/components/Post/Embed/VideoEmbed/ActiveVideoWebContext' import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' +import {Provider as BlockingAnnouncementsPortalProvider} from '#/components/dialogs/BlockingAnnouncements' import {ToastContainer} from '#/components/Toast' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder' @@ -196,11 +197,13 @@ function App() { + + diff --git a/src/components/FocusScope/index.tsx b/src/components/FocusScope/index.tsx new file mode 100644 index 0000000000..37538265b2 --- /dev/null +++ b/src/components/FocusScope/index.tsx @@ -0,0 +1,106 @@ +import { + Children, + cloneElement, + ReactNode, + useRef, + useMemo, + useCallback, + useEffect, + isValidElement, + FunctionComponentElement, +} from 'react' +import { + AccessibilityInfo, + Pressable, + View, + Text, + findNodeHandle, +} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useA11y} from '#/state/a11y' + +export function FocusScope({children}: {children: ReactNode}) { + const {screenReaderEnabled} = useA11y() + + return screenReaderEnabled ? {children} : children +} + +function FocusTrap({children}: {children: ReactNode}) { + const {_} = useLingui() + const child = useRef(null) + + const content = useMemo(() => { + return Children.toArray(children).map((node, i) => { + if (i === 0 && isValidElement(node)) { + return cloneElement(node as FunctionComponentElement, { + ref: child, + }) + } + return node + }) + }, [children]) + + const focus = useCallback((ref: View | null) => { + if (!ref) return + const node = findNodeHandle(ref) + if (node) { + AccessibilityInfo.setAccessibilityFocus(node) + } + }, []) + + useEffect(() => { + setTimeout(() => { + focus(child.current) + }, 1e3) + }, [focus]) + + return ( + <> + { + switch (event.nativeEvent.actionName) { + case 'activate': { + focus(child.current) + } + } + }}> + + + {content} + { + switch (event.nativeEvent.actionName) { + case 'activate': { + focus(child.current) + } + } + }}> + + + + ) +} + +function Noop() { + return ( + + {' '} + + ) +} diff --git a/src/components/dialogs/BlockingAnnouncements/AnnouncementDialog.tsx b/src/components/dialogs/BlockingAnnouncements/AnnouncementDialog.tsx index 90201da905..58254abcc7 100644 --- a/src/components/dialogs/BlockingAnnouncements/AnnouncementDialog.tsx +++ b/src/components/dialogs/BlockingAnnouncements/AnnouncementDialog.tsx @@ -5,7 +5,8 @@ import {View, ScrollView, useWindowDimensions} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {LinearGradient} from 'expo-linear-gradient' -import {isNative} from '#/platform/detection' +import {FocusScope} from '#/components/FocusScope' +import {isNative, isAndroid} from '#/platform/detection' import {atoms as a, useTheme, useBreakpoints, web, flatten} from '#/alf' import {transparentifyColor} from '#/alf/util/colorGeneration' import {useA11y} from '#/state/a11y' @@ -49,7 +50,7 @@ export function AnnouncementDialog({ const insets = useSafeAreaInsets() return ( - <> + - {/* */} - {!gtPhone && ( )} - - {children} - - {/* */} + + + {children} + + - + ) } diff --git a/src/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801.tsx b/src/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801.tsx index 8774ea17db..833fd45d8a 100644 --- a/src/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801.tsx +++ b/src/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801.tsx @@ -12,7 +12,10 @@ import { } from '#/components/dialogs/BlockingAnnouncements/common' import {InlineLinkText} from '#/components/Link' import {Span, Text} from '#/components/Typography' -import {AnnouncementDialog, useAnnouncementDialogContext} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog' +import { + AnnouncementDialog, + useAnnouncementDialogContext, +} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog' export function useLocalState() { return useAnnouncementState({ @@ -111,6 +114,8 @@ export function Announcement() {