Skip to content

Commit

Permalink
Fix reactivity of dialogs (Dialogs Pt. 1) (#3146)
Browse files Browse the repository at this point in the history
* Improve a11y on ios

* Format

* Remove android

* Fix android

* Revert some changes

* use sharedvalue for `importantForAccessibility`

* add back `isOpen`

* fix some more types

---------

Co-authored-by: Eric Bailey <[email protected]>
  • Loading branch information
haileyok and estrattonbailey authored Mar 8, 2024
1 parent 8f623c3 commit 0f9f08b
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 38 deletions.
6 changes: 2 additions & 4 deletions src/components/Dialog/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ export function useDialogControl(): DialogOuterProps['control'] {
open: () => {},
close: () => {},
})
const {activeDialogs, openDialogs} = useDialogStateContext()
const isOpen = openDialogs.includes(id)
const {activeDialogs} = useDialogStateContext()

React.useEffect(() => {
activeDialogs.current.set(id, control)
Expand All @@ -36,14 +35,13 @@ export function useDialogControl(): DialogOuterProps['control'] {
() => ({
id,
ref: control,
isOpen,
open: () => {
control.current.open()
},
close: cb => {
control.current.close(cb)
},
}),
[id, control, isOpen],
[id, control],
)
}
2 changes: 1 addition & 1 deletion src/components/Dialog/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type DialogControlRefProps = {
export type DialogControlProps = DialogControlRefProps & {
id: string
ref: React.RefObject<DialogControlRefProps>
isOpen: boolean
isOpen?: boolean
}

export type DialogContextProps = {
Expand Down
60 changes: 41 additions & 19 deletions src/state/dialogs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react'
import {SharedValue, useSharedValue} from 'react-native-reanimated'
import {DialogControlRefProps} from '#/components/Dialog'
import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context'

const DialogContext = React.createContext<{
interface IDialogContext {
/**
* The currently active `useDialogControl` hooks.
*/
Expand All @@ -13,13 +14,18 @@ const DialogContext = React.createContext<{
* The currently open dialogs, referenced by their IDs, generated from
* `useId`.
*/
openDialogs: string[]
}>({
activeDialogs: {
current: new Map(),
},
openDialogs: [],
})
openDialogs: React.MutableRefObject<Set<string>>
/**
* The counterpart to `accessibilityViewIsModal` for Android. This property
* applies to the parent of all non-modal views, and prevents TalkBack from
* navigating within content beneath an open dialog.
*
* @see https://reactnative.dev/docs/accessibility#importantforaccessibility-android
*/
importantForAccessibility: SharedValue<'auto' | 'no-hide-descendants'>
}

const DialogContext = React.createContext<IDialogContext>({} as IDialogContext)

const DialogControlContext = React.createContext<{
closeAllDialogs(): boolean
Expand All @@ -41,26 +47,42 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
const activeDialogs = React.useRef<
Map<string, React.MutableRefObject<DialogControlRefProps>>
>(new Map())
const [openDialogs, setOpenDialogs] = React.useState<string[]>([])
const openDialogs = React.useRef<Set<string>>(new Set())
const importantForAccessibility = useSharedValue<
'auto' | 'no-hide-descendants'
>('auto')

const closeAllDialogs = React.useCallback(() => {
activeDialogs.current.forEach(dialog => dialog.current.close())
return openDialogs.length > 0
}, [openDialogs])
return openDialogs.current.size > 0
}, [])

const setDialogIsOpen = React.useCallback(
(id: string, isOpen: boolean) => {
setOpenDialogs(prev => {
const filtered = prev.filter(dialogId => dialogId !== id) as string[]
return isOpen ? [...filtered, id] : filtered
})
if (isOpen) {
openDialogs.current.add(id)
importantForAccessibility.value = 'no-hide-descendants'
} else {
openDialogs.current.delete(id)
if (openDialogs.current.size < 1) {
importantForAccessibility.value = 'auto'
}
}
},
[setOpenDialogs],
[importantForAccessibility],
)

const context = React.useMemo(
() => ({activeDialogs, openDialogs}),
[openDialogs],
const context = React.useMemo<IDialogContext>(
() => ({
activeDialogs: {
current: new Map(),
},
openDialogs: {
current: new Set(),
},
importantForAccessibility,
}),
[importantForAccessibility],
)
const controls = React.useMemo(
() => ({closeAllDialogs, setDialogIsOpen}),
Expand Down
19 changes: 5 additions & 14 deletions src/view/shell/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import {useCloseAnyActiveElement} from '#/state/util'
import * as notifications from 'lib/notifications/notifications'
import {Outlet as PortalOutlet} from '#/components/Portal'
import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
import {useDialogStateContext} from '#/state/dialogs'
import {useDialogStateContext} from 'state/dialogs'
import Animated from 'react-native-reanimated'

function ShellInner() {
const isDrawerOpen = useIsDrawerOpen()
Expand All @@ -54,9 +55,9 @@ function ShellInner() {
const canGoBack = useNavigationState(state => !isStateAtTabRoot(state))
const {hasSession, currentAccount} = useSession()
const closeAnyActiveElement = useCloseAnyActiveElement()
const {importantForAccessibility} = useDialogStateContext()
// start undefined
const currentAccountDid = React.useRef<string | undefined>(undefined)
const {openDialogs} = useDialogStateContext()

React.useEffect(() => {
let listener = {remove() {}}
Expand All @@ -80,19 +81,9 @@ function ShellInner() {
}
}, [currentAccount])

/**
* The counterpart to `accessibilityViewIsModal` for Android. This property
* applies to the parent of all non-modal views, and prevents TalkBack from
* navigating within content beneath an open dialog.
*
* @see https://reactnative.dev/docs/accessibility#importantforaccessibility-android
*/
const importantForAccessibility =
openDialogs.length > 0 ? 'no-hide-descendants' : undefined

return (
<>
<View
<Animated.View
style={containerPadding}
importantForAccessibility={importantForAccessibility}>
<ErrorBoundary>
Expand All @@ -106,7 +97,7 @@ function ShellInner() {
<TabsNavigator />
</Drawer>
</ErrorBoundary>
</View>
</Animated.View>
<Composer winHeight={winDim.height} />
<ModalsContainer />
<MutedWordsDialog />
Expand Down

0 comments on commit 0f9f08b

Please sign in to comment.