import React, {useImperativeHandle} from 'react' import {StyleProp, View, ViewStyle} from 'react-native' import { GestureHandlerRootView, ScrollView, TextInput as RNGHTextInput, } from 'react-native-gesture-handler' import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {StatusBar} from 'expo-status-bar' import {logger} from '#/logger' import {isIOS} from '#/platform/detection' import {useDialogStateControlContext} from '#/state/dialogs' import {List, ListMethods, ListProps} from '#/view/com/util/List' import {atoms as a, flatten, useTheme} from '#/alf' import {Context, useDialogContext} from '#/components/Dialog/context' import { DialogControlProps, DialogInnerProps, DialogOuterProps, } from '#/components/Dialog/types' import {createInput} from '#/components/forms/TextField' import {Portal as DefaultPortal} from '#/components/Portal' import {BottomSheet, BottomSheetSnapPoint} from '../../../modules/bottom-sheet' export {useDialogContext, useDialogControl} from '#/components/Dialog/context' export * from '#/components/Dialog/types' export * from '#/components/Dialog/utils' // @ts-ignore export const Input = createInput(RNGHTextInput) export function Outer({ children, control, onClose, nativeOptions, testID, Portal = DefaultPortal, }: React.PropsWithChildren) { const t = useTheme() const ref = React.useRef(null) const closeCallbacks = React.useRef<(() => void)[]>([]) const {setDialogIsOpen} = useDialogStateControlContext() const [snapPoint, setSnapPoint] = React.useState( BottomSheetSnapPoint.Partial, ) const callQueuedCallbacks = React.useCallback(() => { for (const cb of closeCallbacks.current) { try { cb() } catch (e: any) { logger.error('Error running close callback', e) } } closeCallbacks.current = [] }, []) const open = React.useCallback(() => { // Run any leftover callbacks that might have been queued up before calling `.open()` callQueuedCallbacks() setDialogIsOpen(control.id, true) ref.current?.present() }, [setDialogIsOpen, control.id, callQueuedCallbacks]) // This is the function that we call when we want to dismiss the dialog. const close = React.useCallback(cb => { if (typeof cb === 'function') { closeCallbacks.current.push(cb) } ref.current?.dismiss() }, []) // This is the actual thing we are doing once we "confirm" the dialog. We want the dialog's close animation to // happen before we run this. It is passed to the `BottomSheet` component. const onCloseAnimationComplete = React.useCallback(() => { // 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]) useImperativeHandle( control.ref, () => ({ open, close, }), [open, close], ) const context = React.useMemo( () => ({close, isNativeDialog: true, nativeSnapPoint: snapPoint}), [close, snapPoint], ) const isFullHeightIOS = isIOS && snapPoint === BottomSheetSnapPoint.Full const Wrapper = isIOS ? View : GestureHandlerRootView return ( <> { setSnapPoint(e.nativeEvent.snapPoint) }} onStateChange={e => { if (e.nativeEvent.state === 'closed') { onCloseAnimationComplete() } }} cornerRadius={20} {...nativeOptions}> {children} {isFullHeightIOS && } ) } export function Inner({children, style}: DialogInnerProps) { const insets = useSafeAreaInsets() return ( {children} ) } export const ScrollableInner = React.forwardRef( function ScrollableInner({children, style}, ref) { const insets = useSafeAreaInsets() const {nativeSnapPoint} = useDialogContext() return ( {children} ) }, ) export const InnerFlatList = React.forwardRef< ListMethods, ListProps & {webInnerStyle?: StyleProp} >(function InnerFlatList({style, contentContainerStyle, ...props}, ref) { const insets = useSafeAreaInsets() return ( } ref={ref} {...props} style={style} /> ) }) export function Close() { return null }