import { Gesture, Directions, GestureDetector, GestureStateChangeEvent, FlingGestureHandlerEventPayload, } from 'react-native-gesture-handler' import { useMemo, useCallback } from 'use-memo-one' import { Animated, StyleSheet, ViewProps, ViewStyle } from 'react-native' import React, { ReactNode, useEffect, useRef, memo, useState } from 'react' import type { SharedProps, ModalOptions, ModalfyParams, ModalStackItem, ModalEventName, ModalEventCallback, ModalStackItemOptions, ModalPendingClosingAction, ModalOnCloseEventCallback, ModalOnAnimateEventCallback, ModalStackSavedStackItemsOptions, } from '../types' import { computeUpdatedModalOptions, queueMacroTask, getStackItemOptions, validateStackItemOptions, vh } from '../utils' type Props
= SharedProps
& { zIndex: number position: number hideBackdrop: () => void isLastOpenedModal: boolean isFirstVisibleModal: boolean stackItem: ModalStackItem
wasOpenCallbackCalled: boolean wasClosedByBackdropPress: boolean pendingClosingAction?: ModalPendingClosingAction setModalStackOptions: (modalOptions: ModalOptions) => void savedStackItemsOptions: ModalStackSavedStackItemsOptions
setSavedStackItemsOptions: React.Dispatch ({
stack,
zIndex,
getParam,
position,
stackItem,
openModal,
closeModal,
closeModals,
currentModal,
hideBackdrop,
closeAllModals,
eventListeners,
clearListeners,
registerListener,
isLastOpenedModal,
isFirstVisibleModal,
removeClosingAction,
setModalStackOptions,
pendingClosingAction,
wasOpenCallbackCalled,
savedStackItemsOptions,
wasClosedByBackdropPress,
setSavedStackItemsOptions,
}: Props ) => {
const [
{
animationIn,
animationOut,
backBehavior,
containerStyle,
animateInConfig,
animateOutConfig,
transitionOptions,
disableFlingGesture,
pointerEventsBehavior,
position: verticalPosition,
},
setModalOptions,
] = useState ) => void,
stackItemCallback?: () => void,
) => {
if (!internalClosingCallback && animationIn) {
animationIn(animatedValue, toValue, stackItemCallback)
} else if (internalClosingCallback && animationOut) {
animationOut(animatedValue, toValue, () => {
internalClosingCallback(stackItem)
queueMacroTask(stackItemCallback)
})
} else {
Animated.timing(animatedValue, {
toValue,
useNativeDriver: true,
...(internalClosingCallback ? animateOutConfig : animateInConfig),
}).start(() => {
queueMacroTask(() => {
internalClosingCallback?.(stackItem)
stackItemCallback?.()
})
})
}
},
[stackItem, animationIn, animationOut, animatedValue, animateInConfig, animateOutConfig],
)
const setStackItemModalOptions = useCallback(
(newModalOptions: ModalOptions) => {
if (position !== 1) return
if (!newModalOptions || !Object.keys(newModalOptions).length) {
throw new Error(`'${stackItem.name}' setModalOptions expects an object with valid modal options. For instance:
...
useEffect(() => {
setModalOptions({
backBehavior: 'clear',
disableFlingGesture: true,
})
}, [])`)
}
localStackItemOptionsCache = newModalOptions
setSavedStackItemsOptions({ [stackItem.hash]: newModalOptions })
queueMacroTask(() => {
setModalOptions(computeUpdatedModalOptions('stackItem', newModalOptions, getStackItemOptions(stackItem, stack)))
})
queueMacroTask(() => setModalStackOptions(newModalOptions), 1)
},
[position, savedStackItemsOptions[stackItem.hash]],
)
const resetStackItemModalOptions = useCallback(() => {
if (!localStackItemOptionsCache && !savedStackItemsOptions[stackItem.hash]) {
setModalOptions(getStackItemOptions(stackItem, stack))
}
}, [])
const closeStackItem = useCallback(
(modalName, callback?: () => void) => {
if (isLastOpenedModal) hideBackdrop()
else resetStackItemModalOptions()
updateAnimatedValue(position - 1, () => {
onCloseListener.current({ type: 'closeModal', origin: wasClosedByBackdropPress ? 'backdrop' : 'default' })
closeModal(modalName)
if (pendingClosingAction?.action === 'closeModal') removeClosingAction(pendingClosingAction)
queueMacroTask(callback)
})
},
[
position,
closeModal,
currentModal,
hideBackdrop,
onCloseListener,
updateAnimatedValue,
pendingClosingAction,
wasClosedByBackdropPress,
],
)
const closeStackItems = useCallback(
(closingElement, callback?: () => void) => {
if (isLastOpenedModal) hideBackdrop()
else resetStackItemModalOptions()
return updateAnimatedValue(position - 1, () => {
onCloseListener.current({ type: 'closeModals', origin: 'default' })
let output = closeModals(closingElement)
if (pendingClosingAction?.action === 'closeModals') removeClosingAction(pendingClosingAction)
queueMacroTask(callback)
return output
})
},
[closeModals, currentModal, hideBackdrop, pendingClosingAction, position, updateAnimatedValue],
)
const closeAllStackItems = useCallback(
(callback?: () => void) => {
hideBackdrop()
updateAnimatedValue(0, () => {
onCloseListener.current({ type: 'closeAllModals', origin: wasClosedByBackdropPress ? 'backdrop' : 'default' })
closeAllModals()
if (pendingClosingAction?.action === 'closeAllModals') removeClosingAction(pendingClosingAction)
queueMacroTask(callback)
})
},
[closeAllModals, hideBackdrop, pendingClosingAction, position, updateAnimatedValue, wasClosedByBackdropPress],
)
const onFling = useCallback(
(_: GestureStateChangeEvent