dedupe some shared hooks

This commit is contained in:
vineyardbovines
2026-03-05 12:16:01 -05:00
committed by Samuel Newman
parent 507290474d
commit ecc5927002
2 changed files with 82 additions and 88 deletions
+35 -87
View File
@@ -31,9 +31,7 @@ import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {ScrollProvider} from '#/lib/ScrollContext' import {ScrollProvider} from '#/lib/ScrollContext'
import {logger} from '#/logger'
import {useA11y} from '#/state/a11y' import {useA11y} from '#/state/a11y'
import {useDialogStateControlContext} from '#/state/dialogs'
import {List, type ListMethods, type ListProps} from '#/view/com/util/List' import {List, type ListMethods, type ListProps} from '#/view/com/util/List'
import {android, atoms as a, ios, platform, tokens, useTheme} from '#/alf' import {android, atoms as a, ios, platform, tokens, useTheme} from '#/alf'
import {useThemeName} from '#/alf/util/useColorModeTheme' import {useThemeName} from '#/alf/util/useColorModeTheme'
@@ -43,6 +41,7 @@ import {
type DialogInnerProps, type DialogInnerProps,
type DialogOuterProps, type DialogOuterProps,
} from '#/components/Dialog/types' } from '#/components/Dialog/types'
import {useDialogCallbackQueue} from '#/components/Dialog/utils'
import {createInput} from '#/components/forms/TextField' import {createInput} from '#/components/forms/TextField'
import {useOnKeyboard} from '#/components/hooks/useOnKeyboard' import {useOnKeyboard} from '#/components/hooks/useOnKeyboard'
import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS} from '#/env' import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS} from '#/env'
@@ -74,50 +73,29 @@ function OuterAlert({
testID, testID,
}: React.PropsWithChildren<DialogOuterProps>) { }: React.PropsWithChildren<DialogOuterProps>) {
const t = useTheme() const t = useTheme()
const [isOpen, setIsOpen] = React.useState(false) const [isOpen, setIsOpen] = useState(false)
const closeCallbacks = React.useRef<(() => void)[]>([]) const {enqueueCallback, handleOpen, handleClose} = useDialogCallbackQueue(
const {setDialogIsOpen} = useDialogStateControlContext() control,
onClose,
const callQueuedCallbacks = React.useCallback(() => {
for (const cb of closeCallbacks.current) {
try {
cb()
} catch (e: any) {
logger.error(e || 'Error running close callback')
}
}
closeCallbacks.current = []
}, [])
const open = React.useCallback<DialogControlProps['open']>(() => {
callQueuedCallbacks()
setDialogIsOpen(control.id, true)
setIsOpen(true)
}, [setDialogIsOpen, control.id, callQueuedCallbacks])
const close = React.useCallback<DialogControlProps['close']>(cb => {
if (typeof cb === 'function') {
closeCallbacks.current.push(cb)
}
setIsOpen(false)
}, [])
const onDismiss = React.useCallback(() => {
setDialogIsOpen(control.id, false)
callQueuedCallbacks()
onClose?.()
}, [callQueuedCallbacks, control.id, onClose, setDialogIsOpen])
useImperativeHandle(
control.ref,
() => ({
open,
close,
}),
[open, close],
) )
const context = React.useMemo( const open: DialogControlProps['open'] = () => {
handleOpen()
setIsOpen(true)
}
const close: DialogControlProps['close'] = cb => {
enqueueCallback(cb)
setIsOpen(false)
}
const onDismiss = () => {
handleClose()
}
useImperativeHandle(control.ref, () => ({open, close}), [open, close])
const context = useMemo(
() => ({ () => ({
close, close,
isNativeDialog: true, isNativeDialog: true,
@@ -175,9 +153,8 @@ function OuterSheet({
const themeName = useThemeName() const themeName = useThemeName()
const t = useTheme(themeName) const t = useTheme(themeName)
const ref = useRef<BottomSheetNativeComponent>(null) const ref = useRef<BottomSheetNativeComponent>(null)
const closeCallbacks = useRef<(() => void)[]>([]) const {enqueueCallback, handleOpen, handleClose, setFullyExpandedCount} =
const {setDialogIsOpen, setFullyExpandedCount} = useDialogCallbackQueue(control, onClose)
useDialogStateControlContext()
const prevSnapPoint = useRef<BottomSheetSnapPoint>( const prevSnapPoint = useRef<BottomSheetSnapPoint>(
BottomSheetSnapPoint.Hidden, BottomSheetSnapPoint.Hidden,
@@ -188,42 +165,20 @@ function OuterSheet({
BottomSheetSnapPoint.Partial, BottomSheetSnapPoint.Partial,
) )
const callQueuedCallbacks = useCallback(() => { const open: DialogControlProps['open'] = () => {
for (const cb of closeCallbacks.current) { handleOpen()
try {
cb()
} catch (e: any) {
logger.error(e || 'Error running close callback')
}
}
closeCallbacks.current = []
}, [])
const open = useCallback<DialogControlProps['open']>(() => {
// Run any leftover callbacks that might have been queued up before calling `.open()`
callQueuedCallbacks()
setDialogIsOpen(control.id, true)
ref.current?.present() ref.current?.present()
}, [setDialogIsOpen, control.id, callQueuedCallbacks]) }
// This is the function that we call when we want to dismiss the dialog. const close: DialogControlProps['close'] = cb => {
const close = useCallback<DialogControlProps['close']>(cb => { enqueueCallback(cb)
if (typeof cb === 'function') {
closeCallbacks.current.push(cb)
}
ref.current?.dismiss() ref.current?.dismiss()
}, []) }
// This is the actual thing we are doing once we "confirm" the dialog. We want the dialog's close animation to // Runs after the bottom sheet close animation completes.
// happen before we run this. It is passed to the `BottomSheet` component. const onCloseAnimationComplete = () => {
const onCloseAnimationComplete = useCallback(() => { handleClose()
// This removes the dialog from our list of stored dialogs. Not super necessary on iOS, but on Android this }
// tells us that we need to toggle the accessibility overlay setting
setDialogIsOpen(control.id, false)
callQueuedCallbacks()
onClose?.()
}, [callQueuedCallbacks, control.id, onClose, setDialogIsOpen])
const onSnapPointChange = (e: BottomSheetSnapPointChangeEvent) => { const onSnapPointChange = (e: BottomSheetSnapPointChangeEvent) => {
const {snapPoint} = e.nativeEvent const {snapPoint} = e.nativeEvent
@@ -254,14 +209,7 @@ function OuterSheet({
} }
} }
useImperativeHandle( useImperativeHandle(control.ref, () => ({open, close}), [open, close])
control.ref,
() => ({
open,
close,
}),
[open, close],
)
const context = useMemo( const context = useMemo(
() => ({ () => ({
+47 -1
View File
@@ -1,7 +1,53 @@
import {useEffect} from 'react' import {useEffect, useRef} from 'react'
import {logger} from '#/logger'
import {useDialogStateControlContext} from '#/state/dialogs'
import {type DialogControlProps} from '#/components/Dialog/types' import {type DialogControlProps} from '#/components/Dialog/types'
export function useDialogCallbackQueue(
control: DialogControlProps,
onClose?: () => void,
) {
const closeCallbacks = useRef<(() => void)[]>([])
const {setDialogIsOpen, setFullyExpandedCount} =
useDialogStateControlContext()
const callQueuedCallbacks = () => {
for (const cb of closeCallbacks.current) {
try {
cb()
} catch (e: any) {
logger.error(e || 'Error running close callback')
}
}
closeCallbacks.current = []
}
const enqueueCallback = (cb?: () => void) => {
if (typeof cb === 'function') {
closeCallbacks.current.push(cb)
}
}
const handleOpen = () => {
callQueuedCallbacks()
setDialogIsOpen(control.id, true)
}
const handleClose = () => {
setDialogIsOpen(control.id, false)
callQueuedCallbacks()
onClose?.()
}
return {
enqueueCallback,
handleOpen,
handleClose,
setFullyExpandedCount,
}
}
export function useAutoOpen(control: DialogControlProps, showTimeout?: number) { export function useAutoOpen(control: DialogControlProps, showTimeout?: number) {
useEffect(() => { useEffect(() => {
if (showTimeout) { if (showTimeout) {