From ba4f07815afb8341240e503445b3ddd3403a93fa Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 1 Jan 2025 11:28:28 -0700 Subject: [PATCH] Revert "fix: messages content overlap when bottom sheet is shown" --- ...16.4+001+mock-useDerivedValue-getter.patch | 18 -- src/App.tsx | 2 - .../ActionSheetAwareScrollViewContext.tsx | 140 ----------- .../ActionSheetKeyboardSpace.tsx | 223 ------------------ .../ActionSheetAwareScrollView/index.ios.tsx | 31 --- .../ActionSheetAwareScrollView/index.tsx | 31 --- .../EmojiPicker/EmojiPickerButton.tsx | 59 ++--- .../HTMLRenderers/ImageRenderer.tsx | 13 +- .../HTMLRenderers/MentionUserRenderer.tsx | 6 +- .../HTMLRenderers/PreRenderer.tsx | 19 +- .../KeyboardAvoidingView/index.android.tsx | 3 + .../KeyboardAvoidingView/index.ios.tsx | 7 +- .../MoneyRequestConfirmationListFooter.tsx | 1 - src/components/PopoverMenu.tsx | 11 +- src/components/PopoverWithMeasuredContent.tsx | 21 +- .../ReportActionItem/MoneyRequestAction.tsx | 5 - .../MoneyRequestPreviewContent.tsx | 3 +- .../MoneyRequestPreview/types.ts | 3 - .../ReportActionItem/ReportPreview.tsx | 6 +- .../ReportActionItem/TaskPreview.tsx | 17 +- src/components/SelectionList/ChatListItem.tsx | 1 - src/components/ShowContextMenuContext.ts | 4 +- src/components/ThreeDotsMenu/index.tsx | 8 +- .../VideoPlayerThumbnail.tsx | 13 +- ...dex.android.ts => useRestoreInputFocus.ts} | 0 src/hooks/useRestoreInputFocus/index.ts | 4 - .../executeOnUIRuntimeSync/index.native.ts | 3 - .../executeOnUIRuntimeSync/index.ts | 3 - src/hooks/useWorkletStateMachine/index.ts | 180 -------------- .../TransactionDuplicate/Confirmation.tsx | 1 - .../BaseReportActionContextMenu.tsx | 5 +- .../report/ContextMenu/ContextMenuActions.tsx | 3 +- .../home/report/PureReportActionItem.tsx | 86 ++----- .../ReportActionCompose.tsx | 23 +- src/pages/home/report/ReportActionsList.tsx | 2 - 35 files changed, 88 insertions(+), 867 deletions(-) delete mode 100644 patches/react-native-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch delete mode 100644 src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx delete mode 100644 src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx delete mode 100644 src/components/ActionSheetAwareScrollView/index.ios.tsx delete mode 100644 src/components/ActionSheetAwareScrollView/index.tsx rename src/hooks/{useRestoreInputFocus/index.android.ts => useRestoreInputFocus.ts} (100%) delete mode 100644 src/hooks/useRestoreInputFocus/index.ts delete mode 100644 src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts delete mode 100644 src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.ts delete mode 100644 src/hooks/useWorkletStateMachine/index.ts diff --git a/patches/react-native-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch b/patches/react-native-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch deleted file mode 100644 index 972ddeedf67a..000000000000 --- a/patches/react-native-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/node_modules/react-native-reanimated/src/mock.ts b/node_modules/react-native-reanimated/src/mock.ts -index 3d8e3f8..5eba613 100644 ---- a/node_modules/react-native-reanimated/src/mock.ts -+++ b/node_modules/react-native-reanimated/src/mock.ts -@@ -87,7 +87,12 @@ const hook = { - useAnimatedReaction: NOOP, - useAnimatedRef: () => ({ current: null }), - useAnimatedScrollHandler: NOOP_FACTORY, -- useDerivedValue: (processor: () => Value) => ({ value: processor() }), -+ // https://github.com/software-mansion/react-native-reanimated/pull/6809 -+ useDerivedValue: (processor: () => Value) => { -+ const result = processor(); -+ -+ return { value: result, get: () => result }; -+ }, - useAnimatedSensor: () => ({ - sensor: { - value: { diff --git a/src/App.tsx b/src/App.tsx index 5de99365aadb..cc824b78fa4c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,6 @@ import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {PickerStateProvider} from 'react-native-picker-select'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import '../wdyr'; -import * as ActionSheetAwareScrollView from './components/ActionSheetAwareScrollView'; import ActiveElementRoleProvider from './components/ActiveElementRoleProvider'; import ActiveWorkspaceContextProvider from './components/ActiveWorkspaceProvider'; import ColorSchemeWrapper from './components/ColorSchemeWrapper'; @@ -90,7 +89,6 @@ function App({url}: AppProps) { CustomStatusBarAndBackgroundContextProvider, ActiveElementRoleProvider, ActiveWorkspaceContextProvider, - ActionSheetAwareScrollView.ActionSheetAwareScrollViewProvider, ReportIDsContextProvider, PlaybackContextProvider, FullScreenContextProvider, diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx deleted file mode 100644 index 6fd9914c70e1..000000000000 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import noop from 'lodash/noop'; -import PropTypes from 'prop-types'; -import type {PropsWithChildren} from 'react'; -import React, {createContext, useMemo} from 'react'; -import type {SharedValue} from 'react-native-reanimated'; -import type {ActionWithPayload, State} from '@hooks/useWorkletStateMachine'; -import useWorkletStateMachine from '@hooks/useWorkletStateMachine'; - -type MeasuredElements = { - fy?: number; - popoverHeight?: number; - height?: number; - composerHeight?: number; -}; - -type Context = { - currentActionSheetState: SharedValue>; - transitionActionSheetState: (action: ActionWithPayload) => void; - transitionActionSheetStateWorklet: (action: ActionWithPayload) => void; - resetStateMachine: () => void; -}; - -/** Holds all information that are needed to coordinate the state value for the action sheet state machine. */ -const currentActionSheetStateValue = { - previous: { - state: 'idle', - payload: null, - }, - current: { - state: 'idle', - payload: null, - }, -}; -const defaultValue: Context = { - currentActionSheetState: { - value: currentActionSheetStateValue, - addListener: noop, - removeListener: noop, - modify: noop, - get: () => currentActionSheetStateValue, - set: noop, - }, - transitionActionSheetState: noop, - transitionActionSheetStateWorklet: noop, - resetStateMachine: noop, -}; - -const ActionSheetAwareScrollViewContext = createContext(defaultValue); - -const Actions = { - OPEN_KEYBOARD: 'KEYBOARD_OPEN', - CLOSE_KEYBOARD: 'CLOSE_KEYBOARD', - OPEN_POPOVER: 'OPEN_POPOVER', - CLOSE_POPOVER: 'CLOSE_POPOVER', - MEASURE_POPOVER: 'MEASURE_POPOVER', - MEASURE_COMPOSER: 'MEASURE_COMPOSER', - POPOVER_ANY_ACTION: 'POPOVER_ANY_ACTION', - HIDE_WITHOUT_ANIMATION: 'HIDE_WITHOUT_ANIMATION', - END_TRANSITION: 'END_TRANSITION', -}; - -const States = { - IDLE: 'idle', - KEYBOARD_OPEN: 'keyboardOpen', - POPOVER_OPEN: 'popoverOpen', - POPOVER_CLOSED: 'popoverClosed', - KEYBOARD_POPOVER_CLOSED: 'keyboardPopoverClosed', - KEYBOARD_POPOVER_OPEN: 'keyboardPopoverOpen', - KEYBOARD_CLOSED_POPOVER: 'keyboardClosingPopover', - POPOVER_MEASURED: 'popoverMeasured', - MODAL_WITH_KEYBOARD_OPEN_DELETED: 'modalWithKeyboardOpenDeleted', -}; - -const STATE_MACHINE = { - [States.IDLE]: { - [Actions.OPEN_POPOVER]: States.POPOVER_OPEN, - [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, - [Actions.MEASURE_POPOVER]: States.IDLE, - [Actions.MEASURE_COMPOSER]: States.IDLE, - }, - [States.POPOVER_OPEN]: { - [Actions.CLOSE_POPOVER]: States.POPOVER_CLOSED, - [Actions.MEASURE_POPOVER]: States.POPOVER_OPEN, - [Actions.MEASURE_COMPOSER]: States.POPOVER_OPEN, - [Actions.POPOVER_ANY_ACTION]: States.POPOVER_CLOSED, - [Actions.HIDE_WITHOUT_ANIMATION]: States.IDLE, - }, - [States.POPOVER_CLOSED]: { - [Actions.END_TRANSITION]: States.IDLE, - }, - [States.KEYBOARD_OPEN]: { - [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, - [Actions.OPEN_POPOVER]: States.KEYBOARD_POPOVER_OPEN, - [Actions.CLOSE_KEYBOARD]: States.IDLE, - [Actions.MEASURE_COMPOSER]: States.KEYBOARD_OPEN, - }, - [States.KEYBOARD_POPOVER_OPEN]: { - [Actions.MEASURE_POPOVER]: States.KEYBOARD_POPOVER_OPEN, - [Actions.CLOSE_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, - [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, - }, - [States.KEYBOARD_POPOVER_CLOSED]: { - [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, - }, - [States.KEYBOARD_CLOSED_POPOVER]: { - [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, - [Actions.END_TRANSITION]: States.KEYBOARD_OPEN, - }, -}; - -function ActionSheetAwareScrollViewProvider(props: PropsWithChildren) { - const {currentState, transition, transitionWorklet, reset} = useWorkletStateMachine(STATE_MACHINE, { - previous: { - state: 'idle', - payload: null, - }, - current: { - state: 'idle', - payload: null, - }, - }); - - const value = useMemo( - () => ({ - currentActionSheetState: currentState, - transitionActionSheetState: transition, - transitionActionSheetStateWorklet: transitionWorklet, - resetStateMachine: reset, - }), - [currentState, reset, transition, transitionWorklet], - ); - - return {props.children}; -} - -ActionSheetAwareScrollViewProvider.propTypes = { - children: PropTypes.node.isRequired, -}; - -export {ActionSheetAwareScrollViewContext, ActionSheetAwareScrollViewProvider, Actions, States}; diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx deleted file mode 100644 index e15ac941a09d..000000000000 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import React, {useContext, useEffect} from 'react'; -import type {ViewProps} from 'react-native'; -import {useKeyboardHandler} from 'react-native-keyboard-controller'; -import Reanimated, {useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring, withTiming} from 'react-native-reanimated'; -import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; -import {Actions, ActionSheetAwareScrollViewContext, States} from './ActionSheetAwareScrollViewContext'; - -const KeyboardState = { - UNKNOWN: 0, - OPENING: 1, - OPEN: 2, - CLOSING: 3, - CLOSED: 4, -}; - -const SPRING_CONFIG = { - mass: 3, - stiffness: 1000, - damping: 500, -}; - -const useAnimatedKeyboard = () => { - const state = useSharedValue(KeyboardState.UNKNOWN); - const height = useSharedValue(0); - const lastHeight = useSharedValue(0); - const heightWhenOpened = useSharedValue(0); - - useKeyboardHandler( - { - onStart: (e) => { - 'worklet'; - - // Save the last keyboard height - if (e.height !== 0) { - heightWhenOpened.set(e.height); - height.set(0); - } - height.set(heightWhenOpened.get()); - lastHeight.set(e.height); - state.set(e.height > 0 ? KeyboardState.OPENING : KeyboardState.CLOSING); - }, - onMove: (e) => { - 'worklet'; - - height.set(e.height); - }, - onEnd: (e) => { - 'worklet'; - - state.set(e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED); - height.set(e.height); - }, - }, - [], - ); - - return {state, height, heightWhenOpened}; -}; - -const useSafeAreaPaddings = () => { - const StyleUtils = useStyleUtils(); - const insets = useSafeAreaInsets(); - const {paddingTop, paddingBottom} = StyleUtils.getSafeAreaPadding(insets ?? undefined); - - return {top: paddingTop, bottom: paddingBottom}; -}; - -function ActionSheetKeyboardSpace(props: ViewProps) { - const styles = useThemeStyles(); - const safeArea = useSafeAreaPaddings(); - const keyboard = useAnimatedKeyboard(); - - // Similar to using `global` in worklet but it's just a local object - const syncLocalWorkletState = useSharedValue(KeyboardState.UNKNOWN); - const {windowHeight} = useWindowDimensions(); - const {currentActionSheetState, transitionActionSheetStateWorklet: transition, resetStateMachine} = useContext(ActionSheetAwareScrollViewContext); - - // Reset state machine when component unmounts - // eslint-disable-next-line arrow-body-style - useEffect(() => { - return () => resetStateMachine(); - }, [resetStateMachine]); - - useAnimatedReaction( - () => keyboard.state.get(), - (lastState) => { - if (lastState === syncLocalWorkletState.get()) { - return; - } - // eslint-disable-next-line react-compiler/react-compiler - syncLocalWorkletState.set(lastState); - - if (lastState === KeyboardState.OPEN) { - transition({type: Actions.OPEN_KEYBOARD}); - } else if (lastState === KeyboardState.CLOSED) { - transition({type: Actions.CLOSE_KEYBOARD}); - } - }, - [], - ); - - const translateY = useDerivedValue(() => { - const {current, previous} = currentActionSheetState.get(); - - // We don't need to run any additional logic. it will always return 0 for idle state - if (current.state === States.IDLE) { - return withSpring(0, SPRING_CONFIG); - } - - const keyboardHeight = keyboard.height.get() === 0 ? 0 : keyboard.height.get() - safeArea.bottom; - - // Sometimes we need to know the last keyboard height - const lastKeyboardHeight = keyboard.heightWhenOpened.get() - safeArea.bottom; - const {popoverHeight = 0, fy, height} = current.payload ?? {}; - const invertedKeyboardHeight = keyboard.state.get() === KeyboardState.CLOSED ? lastKeyboardHeight : 0; - const elementOffset = fy !== undefined && height !== undefined && popoverHeight !== undefined ? fy + safeArea.top + height - (windowHeight - popoverHeight) : 0; - - // when the state is not idle we know for sure we have the previous state - const previousPayload = previous.payload ?? {}; - const previousElementOffset = - previousPayload.fy !== undefined && previousPayload.height !== undefined && previousPayload.popoverHeight !== undefined - ? previousPayload.fy + safeArea.top + previousPayload.height - (windowHeight - previousPayload.popoverHeight) - : 0; - - const isOpeningKeyboard = syncLocalWorkletState.get() === 1; - const isClosingKeyboard = syncLocalWorkletState.get() === 3; - const isClosedKeyboard = syncLocalWorkletState.get() === 4; - - // Depending on the current and sometimes previous state we can return - // either animation or just a value - switch (current.state) { - case States.KEYBOARD_OPEN: { - if (isClosedKeyboard || isOpeningKeyboard) { - return lastKeyboardHeight - keyboardHeight; - } - if (previous.state === States.KEYBOARD_CLOSED_POPOVER || (previous.state === States.KEYBOARD_OPEN && elementOffset < 0)) { - return Math.max(keyboard.heightWhenOpened.get() - keyboard.height.get() - safeArea.bottom, 0) + Math.max(elementOffset, 0); - } - return withSpring(0, SPRING_CONFIG); - } - - case States.POPOVER_CLOSED: { - return withSpring(0, SPRING_CONFIG, () => { - transition({ - type: Actions.END_TRANSITION, - }); - }); - } - - case States.POPOVER_OPEN: { - if (popoverHeight) { - if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { - return withSpring(elementOffset < 0 ? 0 : elementOffset, SPRING_CONFIG); - } - - return withSpring(Math.max(previousElementOffset, 0), SPRING_CONFIG); - } - - return 0; - } - - case States.KEYBOARD_POPOVER_OPEN: { - if (keyboard.state.get() === KeyboardState.OPEN) { - return withSpring(0, SPRING_CONFIG); - } - - const nextOffset = elementOffset + lastKeyboardHeight; - - if (keyboard.state.get() === KeyboardState.CLOSED && nextOffset > invertedKeyboardHeight) { - return withSpring(nextOffset < 0 ? 0 : nextOffset, SPRING_CONFIG); - } - - if (elementOffset < 0) { - return isClosingKeyboard ? 0 : lastKeyboardHeight - keyboardHeight; - } - - return lastKeyboardHeight; - } - - case States.KEYBOARD_CLOSED_POPOVER: { - if (elementOffset < 0) { - transition({type: Actions.END_TRANSITION}); - - return 0; - } - - if (keyboard.state.get() === KeyboardState.CLOSED) { - return elementOffset + lastKeyboardHeight; - } - - if (keyboard.height.get() > 0) { - return keyboard.heightWhenOpened.get() - keyboard.height.get() + elementOffset; - } - - return withTiming(elementOffset + lastKeyboardHeight, { - duration: 0, - }); - } - - default: - return 0; - } - }, []); - - const animatedStyle = useAnimatedStyle(() => ({ - paddingTop: translateY.get(), - })); - - return ( - - ); -} - -ActionSheetKeyboardSpace.displayName = 'ActionSheetKeyboardSpace'; - -export default ActionSheetKeyboardSpace; diff --git a/src/components/ActionSheetAwareScrollView/index.ios.tsx b/src/components/ActionSheetAwareScrollView/index.ios.tsx deleted file mode 100644 index 2c40df7e61c6..000000000000 --- a/src/components/ActionSheetAwareScrollView/index.ios.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type {PropsWithChildren} from 'react'; -import React, {forwardRef} from 'react'; -import type {ScrollViewProps} from 'react-native'; -// eslint-disable-next-line no-restricted-imports -import {ScrollView} from 'react-native'; -import {Actions, ActionSheetAwareScrollViewContext, ActionSheetAwareScrollViewProvider} from './ActionSheetAwareScrollViewContext'; -import ActionSheetKeyboardSpace from './ActionSheetKeyboardSpace'; - -const ActionSheetAwareScrollView = forwardRef>((props, ref) => ( - - {props.children} - -)); - -export default ActionSheetAwareScrollView; - -/** - * This function should be used as renderScrollComponent prop for FlatList - * @param props - props that will be passed to the ScrollView from FlatList - * @returns - ActionSheetAwareScrollView - */ -function renderScrollComponent(props: ScrollViewProps) { - // eslint-disable-next-line react/jsx-props-no-spreading - return ; -} - -export {renderScrollComponent, ActionSheetAwareScrollViewContext, ActionSheetAwareScrollViewProvider, Actions}; diff --git a/src/components/ActionSheetAwareScrollView/index.tsx b/src/components/ActionSheetAwareScrollView/index.tsx deleted file mode 100644 index d22f991ce4cf..000000000000 --- a/src/components/ActionSheetAwareScrollView/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// this whole file is just for other platforms -// iOS version has everything implemented -import type {PropsWithChildren} from 'react'; -import React, {forwardRef} from 'react'; -import type {ScrollViewProps} from 'react-native'; -// eslint-disable-next-line no-restricted-imports -import {ScrollView} from 'react-native'; -import {Actions, ActionSheetAwareScrollViewContext, ActionSheetAwareScrollViewProvider} from './ActionSheetAwareScrollViewContext'; - -const ActionSheetAwareScrollView = forwardRef>((props, ref) => ( - - {props.children} - -)); - -export default ActionSheetAwareScrollView; - -/** - * This is only used on iOS. On other platforms it's just undefined to be pass a prop to FlatList - * - * This function should be used as renderScrollComponent prop for FlatList - * @param {Object} props - props that will be passed to the ScrollView from FlatList - * @returns {React.ReactElement} - ActionSheetAwareScrollView - */ -const renderScrollComponent = undefined; - -export {renderScrollComponent, ActionSheetAwareScrollViewContext, ActionSheetAwareScrollViewProvider, Actions}; diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index a10e7d9fd1f3..26d1a902b475 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -1,9 +1,8 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {memo, useContext, useEffect, useRef} from 'react'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; +import React, {memo, useEffect, useRef} from 'react'; +import type {GestureResponderEvent} from 'react-native'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; -import type PressableProps from '@components/Pressable/GenericPressable/types'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; import useLocalize from '@hooks/useLocalize'; @@ -21,7 +20,7 @@ type EmojiPickerButtonProps = { emojiPickerID?: string; /** A callback function when the button is pressed */ - onPress?: PressableProps['onPress']; + onPress?: (event?: GestureResponderEvent | KeyboardEvent) => void; /** Emoji popup anchor offset shift vertical */ shiftVertical?: number; @@ -32,41 +31,12 @@ type EmojiPickerButtonProps = { }; function EmojiPickerButton({isDisabled = false, emojiPickerID = '', shiftVertical = 0, onPress, onModalHide, onEmojiSelected}: EmojiPickerButtonProps) { - const actionSheetContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const emojiPopoverAnchor = useRef(null); const {translate} = useLocalize(); const isFocused = useIsFocused(); - const openEmojiPicker: PressableProps['onPress'] = (e) => { - if (!isFocused) { - return; - } - - actionSheetContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.CLOSE_KEYBOARD, - }); - - if (!EmojiPickerAction.emojiPickerRef?.current?.isEmojiPickerVisible) { - EmojiPickerAction.showEmojiPicker( - onModalHide, - onEmojiSelected, - emojiPopoverAnchor, - { - horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, - vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, - shiftVertical, - }, - () => {}, - emojiPickerID, - ); - } else { - EmojiPickerAction.emojiPickerRef.current.hideEmojiPicker(); - } - onPress?.(e); - }; - useEffect(() => EmojiPickerAction.resetEmojiPopoverAnchor, []); return ( @@ -75,7 +45,28 @@ function EmojiPickerButton({isDisabled = false, emojiPickerID = '', shiftVertica ref={emojiPopoverAnchor} style={({hovered, pressed}) => [styles.chatItemEmojiButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed))]} disabled={isDisabled} - onPress={openEmojiPicker} + onPress={(e) => { + if (!isFocused) { + return; + } + if (!EmojiPickerAction.emojiPickerRef?.current?.isEmojiPickerVisible) { + EmojiPickerAction.showEmojiPicker( + onModalHide, + onEmojiSelected, + emojiPopoverAnchor, + { + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, + shiftVertical, + }, + () => {}, + emojiPickerID, + ); + } else { + EmojiPickerAction.emojiPickerRef.current.hideEmojiPicker(); + } + onPress?.(e); + }} id={CONST.EMOJI_PICKER_BUTTON_NATIVE_ID} accessibilityLabel={translate('reportActionCompose.emoji')} > diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index 128ebd2d3a84..c27eef1de91e 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -94,7 +94,7 @@ function ImageRenderer({tnode}: ImageRendererProps) { thumbnailImageComponent ) : ( - {({onShowContextMenu, anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled}) => ( + {({anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled}) => ( {({reportID, accountID, type}) => ( - showContextMenuForReport( - event, - anchor, - report?.reportID ?? '-1', - action, - checkIfContextMenuActive, - ReportUtils.isArchivedRoom(report, reportNameValuePairs), - ), - ); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); }} shouldUseHapticsOnLongPress accessibilityRole={CONST.ROLE.BUTTON} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index 29c1d290fa5f..96bdf8e9e1e8 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -84,16 +84,14 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona return ( - {({onShowContextMenu, anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled}) => ( + {({anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled}) => ( { if (isDisabled) { return; } - return onShowContextMenu(() => - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)), - ); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); }} onPress={(event) => { event.preventDefault(); diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx index b7c428e72f29..b1e5c21500f0 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx @@ -34,25 +34,16 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d return ( - {({onShowContextMenu, anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled}) => ( + {({anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled}) => ( {})} onPressIn={onPressIn} onPressOut={onPressOut} onLongPress={(event) => { - onShowContextMenu(() => { - if (isDisabled) { - return; - } - return showContextMenuForReport( - event, - anchor, - report?.reportID ?? '-1', - action, - checkIfContextMenuActive, - ReportUtils.isArchivedRoom(report, reportNameValuePairs), - ); - }); + if (isDisabled) { + return; + } + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); }} shouldUseHapticsOnLongPress role={CONST.ROLE.PRESENTATION} diff --git a/src/components/KeyboardAvoidingView/index.android.tsx b/src/components/KeyboardAvoidingView/index.android.tsx index e81ebd6ff671..ec2dc3bd18d7 100644 --- a/src/components/KeyboardAvoidingView/index.android.tsx +++ b/src/components/KeyboardAvoidingView/index.android.tsx @@ -1,3 +1,6 @@ +/* + * The KeyboardAvoidingView is only used on ios + */ import React from 'react'; import type {KeyboardAvoidingViewProps} from 'react-native-keyboard-controller'; import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native-keyboard-controller'; diff --git a/src/components/KeyboardAvoidingView/index.ios.tsx b/src/components/KeyboardAvoidingView/index.ios.tsx index e81ebd6ff671..171210eab7ac 100644 --- a/src/components/KeyboardAvoidingView/index.ios.tsx +++ b/src/components/KeyboardAvoidingView/index.ios.tsx @@ -1,6 +1,9 @@ +/* + * The KeyboardAvoidingView is only used on ios + */ import React from 'react'; -import type {KeyboardAvoidingViewProps} from 'react-native-keyboard-controller'; -import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native-keyboard-controller'; +import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native'; +import type {KeyboardAvoidingViewProps} from './types'; function KeyboardAvoidingView(props: KeyboardAvoidingViewProps) { // eslint-disable-next-line react/jsx-props-no-spreading diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 6c467145cc3c..51cb2a6d6f39 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -276,7 +276,6 @@ function MoneyRequestConfirmationListFooter({ reportNameValuePairs: undefined, action: undefined, checkIfContextMenuActive: () => {}, - onShowContextMenu: () => {}, isDisabled: true, }), [], diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 9ee06ad57e1d..b8dc71aef515 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -3,7 +3,7 @@ import lodashIsEqual from 'lodash/isEqual'; import type {ReactNode, RefObject} from 'react'; import React, {useLayoutEffect, useState} from 'react'; import {StyleSheet, View} from 'react-native'; -import type {LayoutChangeEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; +import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import type {ModalProps} from 'react-native-modal'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; @@ -62,9 +62,6 @@ type PopoverMenuProps = Partial & { /** Callback method fired when the user requests to close the modal */ onClose: () => void; - /** Optional callback passed to popover's children container */ - onLayout?: (e: LayoutChangeEvent) => void; - /** Callback method fired when the modal is shown */ onModalShow?: () => void; @@ -157,7 +154,6 @@ function PopoverMenu({ anchorPosition, anchorRef, onClose, - onLayout, onModalShow, headerText, fromSidebarMediumScreen, @@ -369,10 +365,7 @@ function PopoverMenu({ testID={testID} > - + {renderHeaderText()} {enteredSubMenuIndexes.length > 0 && renderBackButtonItem()} {renderWithConditionalWrapper(shouldUseScrollView, scrollContainerStyle, renderedMenuItems)} diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index 80b9fb1a9564..4fa58ac21ffa 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -1,5 +1,5 @@ import isEqual from 'lodash/isEqual'; -import React, {useContext, useMemo, useState} from 'react'; +import React, {useMemo, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -8,7 +8,6 @@ import ComposerFocusManager from '@libs/ComposerFocusManager'; import PopoverWithMeasuredContentUtils from '@libs/PopoverWithMeasuredContentUtils'; import CONST from '@src/CONST'; import type {AnchorDimensions, AnchorPosition} from '@src/styles'; -import * as ActionSheetAwareScrollView from './ActionSheetAwareScrollView'; import Popover from './Popover'; import type PopoverProps from './Popover/types'; @@ -62,7 +61,6 @@ function PopoverWithMeasuredContent({ shouldEnableNewFocusManagement, ...props }: PopoverWithMeasuredContentProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const {windowWidth, windowHeight} = useWindowDimensions(); const [popoverWidth, setPopoverWidth] = useState(popoverDimensions.width); @@ -91,22 +89,9 @@ function PopoverWithMeasuredContent({ * Measure the size of the popover's content. */ const measurePopover = ({nativeEvent}: LayoutChangeEvent) => { - const {width, height} = nativeEvent.layout; - setPopoverWidth(width); - setPopoverHeight(height); + setPopoverWidth(nativeEvent.layout.width); + setPopoverHeight(nativeEvent.layout.height); setIsContentMeasured(true); - - // it handles the case when `measurePopover` is called with values like: 192, 192.00003051757812, 192 - // if we update it, then animation in `ActionSheetAwareScrollView` may be re-running - // and we'll see unsynchronized and junky animation - if (actionSheetAwareScrollViewContext.currentActionSheetState.get().current.payload?.popoverHeight !== Math.floor(height) && height !== 0) { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.MEASURE_POPOVER, - payload: { - popoverHeight: Math.floor(height), - }, - }); - } }; const adjustedAnchorPosition = useMemo(() => { diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index e17c30e8bddb..af54e2940d3f 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -51,9 +51,6 @@ type MoneyRequestActionProps = MoneyRequestActionOnyxProps & { /** Callback for updating context menu active state, used for showing context menu */ checkIfContextMenuActive?: () => void; - /** Callback for measuring child and running a defined callback/action later */ - onShowContextMenu?: (callback: () => void) => void; - /** Whether the IOU is hovered so we can modify its style */ isHovered?: boolean; @@ -74,7 +71,6 @@ function MoneyRequestAction({ reportID, isMostRecentIOUReportAction, contextMenuAnchor, - onShowContextMenu = () => {}, checkIfContextMenuActive = () => {}, chatReport, iouReport, @@ -133,7 +129,6 @@ function MoneyRequestAction({ isTrackExpense={isTrackExpenseAction} action={action} contextMenuAnchor={contextMenuAnchor} - onShowContextMenu={onShowContextMenu} checkIfContextMenuActive={checkIfContextMenuActive} shouldShowPendingConversionMessage={shouldShowPendingConversionMessage} onPreviewPressed={onMoneyRequestPreviewPressed} diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 4cff7abe6d0f..86196f13d662 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -60,7 +60,6 @@ function MoneyRequestPreviewContent({ onPreviewPressed, containerStyles, checkIfContextMenuActive = () => {}, - onShowContextMenu = () => {}, shouldShowPendingConversionMessage = false, isHovered = false, isWhisper = false, @@ -190,7 +189,7 @@ function MoneyRequestPreviewContent({ if (!shouldDisplayContextMenu) { return; } - onShowContextMenu(() => showContextMenuForReport(event, contextMenuAnchor, reportID, action, checkIfContextMenuActive)); + showContextMenuForReport(event, contextMenuAnchor, reportID, action, checkIfContextMenuActive); }; const getPreviewHeaderText = (): string => { diff --git a/src/components/ReportActionItem/MoneyRequestPreview/types.ts b/src/components/ReportActionItem/MoneyRequestPreview/types.ts index 7f19120426c1..c40b45c6d2bd 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestPreview/types.ts @@ -27,9 +27,6 @@ type MoneyRequestPreviewProps = { /** Callback for updating context menu active state, used for showing context menu */ checkIfContextMenuActive?: () => void; - /** Callback for measuring child and running a defined callback/action later */ - onShowContextMenu?: (callback: () => void) => void; - /** Extra styles to pass to View wrapper */ containerStyles?: StyleProp; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 3b159c2e4fd5..a4ade8d77aa8 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -70,9 +70,6 @@ type ReportPreviewProps = { /** Callback for updating context menu active state, used for showing context menu */ checkIfContextMenuActive?: () => void; - /** Callback for measuring child and running a defined callback/action later */ - onShowContextMenu: (callback: () => void) => void; - /** Callback when the payment options popover is shown */ onPaymentOptionsShow?: () => void; @@ -98,7 +95,6 @@ function ReportPreview({ checkIfContextMenuActive = () => {}, onPaymentOptionsShow, onPaymentOptionsHide, - onShowContextMenu = () => {}, }: ReportPreviewProps) { const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); @@ -496,7 +492,7 @@ function ReportPreview({ onPress={openReportFromPreview} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} - onLongPress={(event) => onShowContextMenu(() => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive))} + onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive)} shouldUseHapticsOnLongPress style={[styles.flexRow, styles.justifyContentBetween, styles.reportPreviewBox]} role="button" diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index 8c6cf3d43e3f..2ea295d16143 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -57,24 +57,11 @@ type TaskPreviewProps = WithCurrentUserPersonalDetailsProps & { /** Callback for updating context menu active state, used for showing context menu */ checkIfContextMenuActive: () => void; - /** Callback that will do measure of necessary layout elements and run provided callback */ - onShowContextMenu: (callback: () => void) => void; - /** Style for the task preview container */ style: StyleProp; }; -function TaskPreview({ - taskReportID, - action, - contextMenuAnchor, - chatReportID, - checkIfContextMenuActive, - currentUserPersonalDetails, - onShowContextMenu, - isHovered = false, - style, -}: TaskPreviewProps) { +function TaskPreview({taskReportID, action, contextMenuAnchor, chatReportID, checkIfContextMenuActive, currentUserPersonalDetails, isHovered = false, style}: TaskPreviewProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -109,7 +96,7 @@ function TaskPreview({ onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(taskReportID))} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} - onLongPress={(event) => onShowContextMenu(() => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive))} + onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive)} shouldUseHapticsOnLongPress style={[styles.flexRow, styles.justifyContentBetween, style]} role={CONST.ROLE.BUTTON} diff --git a/src/components/SelectionList/ChatListItem.tsx b/src/components/SelectionList/ChatListItem.tsx index df9c3f9280d7..d6ce930d0ec7 100644 --- a/src/components/SelectionList/ChatListItem.tsx +++ b/src/components/SelectionList/ChatListItem.tsx @@ -51,7 +51,6 @@ function ChatListItem({ action: undefined, transactionThreadReport: undefined, checkIfContextMenuActive: () => {}, - onShowContextMenu: () => {}, isDisabled: true, }; diff --git a/src/components/ShowContextMenuContext.ts b/src/components/ShowContextMenuContext.ts index ee6e7e71dd7a..6fefa987fac3 100644 --- a/src/components/ShowContextMenuContext.ts +++ b/src/components/ShowContextMenuContext.ts @@ -16,13 +16,11 @@ type ShowContextMenuContextProps = { action: OnyxEntry; transactionThreadReport?: OnyxEntry; checkIfContextMenuActive: () => void; - onShowContextMenu: (callback: () => void) => void; isDisabled: boolean; }; const ShowContextMenuContext = createContext({ anchor: null, - onShowContextMenu: (callback) => callback(), report: undefined, reportNameValuePairs: undefined, action: undefined, @@ -64,7 +62,7 @@ function showContextMenuForReport( action?.reportActionID, ReportUtils.getOriginalReportID(reportID, action), undefined, - undefined, + checkIfContextMenuActive, checkIfContextMenuActive, isArchivedRoom, ); diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index bc062fffd787..e44d57ab18e2 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useRef, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Icon from '@components/Icon'; @@ -43,16 +43,16 @@ function ThreeDotsMenu({ setPopupMenuVisible(true); }; - const hidePopoverMenu = useCallback(() => { + const hidePopoverMenu = () => { setPopupMenuVisible(false); - }, []); + }; useEffect(() => { if (!isBehindModal || !isPopupMenuVisible) { return; } hidePopoverMenu(); - }, [hidePopoverMenu, isBehindModal, isPopupMenuVisible]); + }, [isBehindModal, isPopupMenuVisible]); return ( <> diff --git a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx index e1c1a000d9bd..832b5eef45f0 100644 --- a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx +++ b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx @@ -45,7 +45,7 @@ function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel, isDele )} {!isDeleted ? ( - {({anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled, onShowContextMenu}) => ( + {({anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled}) => ( { - showContextMenuForReport( - event, - anchor, - report?.reportID ?? '-1', - action, - checkIfContextMenuActive, - ReportUtils.isArchivedRoom(report, reportNameValuePairs), - ); - }); + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)); }} shouldUseHapticsOnLongPress > diff --git a/src/hooks/useRestoreInputFocus/index.android.ts b/src/hooks/useRestoreInputFocus.ts similarity index 100% rename from src/hooks/useRestoreInputFocus/index.android.ts rename to src/hooks/useRestoreInputFocus.ts diff --git a/src/hooks/useRestoreInputFocus/index.ts b/src/hooks/useRestoreInputFocus/index.ts deleted file mode 100644 index 4105455698dc..000000000000 --- a/src/hooks/useRestoreInputFocus/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const useRestoreInputFocus = (_isLostFocus: boolean) => {}; - -export default useRestoreInputFocus; diff --git a/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts b/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts deleted file mode 100644 index eab78097aa05..000000000000 --- a/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {executeOnUIRuntimeSync} from 'react-native-reanimated'; - -export default executeOnUIRuntimeSync; diff --git a/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.ts b/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.ts deleted file mode 100644 index 3bc8059d8762..000000000000 --- a/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {runOnUI} from 'react-native-reanimated'; - -export default runOnUI; diff --git a/src/hooks/useWorkletStateMachine/index.ts b/src/hooks/useWorkletStateMachine/index.ts deleted file mode 100644 index cfaffe968370..000000000000 --- a/src/hooks/useWorkletStateMachine/index.ts +++ /dev/null @@ -1,180 +0,0 @@ -import {useCallback} from 'react'; -import {runOnJS, runOnUI, useSharedValue} from 'react-native-reanimated'; -import Log from '@libs/Log'; -import executeOnUIRuntimeSync from './executeOnUIRuntimeSync'; - -// When you need to debug state machine change this to true -const DEBUG_MODE = false; - -type Payload = Record; -type ActionWithPayload

= { - type: string; - payload?: P; -}; -type StateHolder

= { - state: string; - payload: P | null; -}; -type State

= { - previous: StateHolder

; - current: StateHolder

; -}; - -/** - * Represents the state machine configuration as a nested record where: - * - The first level keys are the state names. - * - The second level keys are the action types valid for that state. - * - The corresponding values are the next states to transition to when the action is triggered. - */ -type StateMachine = Record>; - -// eslint-disable-next-line @typescript-eslint/unbound-method -const client = Log.client; - -/** - * A hook that creates a state machine that can be used with Reanimated Worklets. - * You can transition state from worklet or from the JS thread. - * - * State machines are helpful for managing complex UI interactions. We want to transition - * between states based on user actions. But also we want to ignore some actions - * when we are in certain states. - * - * For example: - * 1. Initial state is idle. It can react to KEYBOARD_OPEN action. - * 2. We open emoji picker. It sends EMOJI_PICKER_OPEN action. - * 2. There is no handling for this action in idle state so we do nothing. - * 3. We close emoji picker and it sends EMOJI_PICKER_CLOSE action which again does nothing. - * 4. We open keyboard. It sends KEYBOARD_OPEN action. idle can react to this action - * by transitioning into keyboardOpen state - * 5. Our state is keyboardOpen. It can react to KEYBOARD_CLOSE, EMOJI_PICKER_OPEN actions - * 6. We open emoji picker again. It sends EMOJI_PICKER_OPEN action which transitions our state - * into emojiPickerOpen state. Now we react only to EMOJI_PICKER_CLOSE action. - * 7. Before rendering the emoji picker, the app hides the keyboard. - * It sends KEYBOARD_CLOSE action. But we ignore it since our emojiPickerOpen state can only handle - * EMOJI_PICKER_CLOSE action. So we write the logic for handling hiding the keyboard, - * but maintaining the offset based on the keyboard state shared value - * 7. We close the picker and send EMOJI_PICKER_CLOSE action which transitions us back into keyboardOpen state. - * - * State machine object example: - * const stateMachine = { - * idle: { - * KEYBOARD_OPEN: 'keyboardOpen', - * }, - * keyboardOpen: { - * KEYBOARD_CLOSE: 'idle', - * EMOJI_PICKER_OPEN: 'emojiPickerOpen', - * }, - * emojiPickerOpen: { - * EMOJI_PICKER_CLOSE: 'keyboardOpen', - * }, - * } - * - * Initial state example: - * { - * previous: null, - * current: { - * state: 'idle', - * payload: null, - * }, - * } - * - * @param stateMachine - a state machine object - * @param initialState - the initial state of the state machine - * @returns an object containing the current state, a transition function, and a reset function - */ -function useWorkletStateMachine

(stateMachine: StateMachine, initialState: State

) { - const currentState = useSharedValue(initialState); - - const log = useCallback((message: string, params?: P | null) => { - 'worklet'; - - if (!DEBUG_MODE) { - return; - } - - // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/restrict-template-expressions - runOnJS(client)(`[StateMachine] ${message}. Params: ${JSON.stringify(params)}`); - }, []); - - const transitionWorklet = useCallback( - (action: ActionWithPayload

) => { - 'worklet'; - - if (!action) { - throw new Error('state machine action is required'); - } - - const state = currentState.get(); - - log(`Current STATE: ${state.current.state}`); - log(`Next ACTION: ${action.type}`, action.payload); - - const nextMachine = stateMachine[state.current.state]; - - if (!nextMachine) { - log(`No next machine found for state: ${state.current.state}`); - return; - } - - const nextState = nextMachine[action.type]; - - if (!nextState) { - log(`No next state found for action: ${action.type}`); - return; - } - - let nextPayload; - - if (typeof action.payload === 'undefined') { - // we save previous payload - nextPayload = state.current.payload; - } else { - // we merge previous payload with the new payload - nextPayload = { - ...state.current.payload, - ...action.payload, - }; - } - - log(`Next STATE: ${nextState}`, nextPayload); - - currentState.set({ - previous: state.current, - current: { - state: nextState, - payload: nextPayload, - }, - }); - }, - [currentState, log, stateMachine], - ); - - const resetWorklet = useCallback(() => { - 'worklet'; - - log('RESET STATE MACHINE'); - // eslint-disable-next-line react-compiler/react-compiler - currentState.set(initialState); - }, [currentState, initialState, log]); - - const reset = useCallback(() => { - runOnUI(resetWorklet)(); - }, [resetWorklet]); - - const transition = useCallback( - (action: ActionWithPayload

) => { - executeOnUIRuntimeSync(transitionWorklet)(action); - }, - [transitionWorklet], - ); - - return { - currentState, - transitionWorklet, - transition, - reset, - }; -} - -export type {ActionWithPayload, State}; -export default useWorkletStateMachine; diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 80995c95c741..8d973a262186 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -71,7 +71,6 @@ function Confirmation() { action: reportAction, report, checkIfContextMenuActive: () => {}, - onShowContextMenu: () => {}, reportNameValuePairs: undefined, anchor: null, isDisabled: false, diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 451d82a770d5..6877de271946 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -1,12 +1,11 @@ import lodashIsEqual from 'lodash/isEqual'; import type {MutableRefObject, RefObject} from 'react'; -import React, {memo, useContext, useMemo, useRef, useState} from 'react'; +import React, {memo, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, Text as RNText, View as ViewType} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import type {ContextMenuItemHandle} from '@components/ContextMenuItem'; import ContextMenuItem from '@components/ContextMenuItem'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; @@ -119,7 +118,6 @@ function BaseReportActionContextMenu({ disabledActions = [], setIsEmojiPickerActive, }: BaseReportActionContextMenuProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -330,7 +328,6 @@ function BaseReportActionContextMenu({ draftMessage, selection, close: () => setShouldKeepOpen(false), - transitionActionSheetState: actionSheetAwareScrollViewContext.transitionActionSheetState, openContextMenu: () => setShouldKeepOpen(true), interceptAnonymousUser, openOverflowMenu, diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 253e35630add..b8cdde2ecff3 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -31,8 +31,8 @@ import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type {Beta, Download as DownloadOnyx, OnyxInputOrEntry, ReportAction, ReportActionReactions, Report as ReportType, Transaction, User} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; -import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; +import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; /** Gets the HTML version of the message in an action */ function getActionHtml(reportAction: OnyxInputOrEntry): string { @@ -79,7 +79,6 @@ type ContextMenuActionPayload = { draftMessage: string; selection: string; close: () => void; - transitionActionSheetState: (params: {type: string; payload?: Record}) => void; openContextMenu: () => void; interceptAnonymousUser: (callback: () => void, isAnonymousAction?: boolean) => void; anchor?: MutableRefObject; diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 5dbd399e2737..34dd2a9d1350 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -5,7 +5,6 @@ import {InteractionManager, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import {AttachmentContext} from '@components/AttachmentContext'; import Button from '@components/Button'; import DisplayNames from '@components/DisplayNames'; @@ -295,7 +294,6 @@ function PureReportActionItem({ userBillingFundID, reportAutomaticallyForwardedMessage, }: PureReportActionItemProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const reportID = report?.reportID ?? ''; @@ -433,34 +431,7 @@ function PureReportActionItem({ const toggleContextMenuFromActiveReportAction = useCallback(() => { setIsContextMenuActive(ReportActionContextMenu.isActiveReportAction(action.reportActionID)); - - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.CLOSE_POPOVER, - }); - }, [actionSheetAwareScrollViewContext, action.reportActionID]); - - const handleShowContextMenu = useCallback( - (callback: () => void) => { - if (!(popoverAnchorRef.current && 'measureInWindow' in popoverAnchorRef.current)) { - return; - } - - // eslint-disable-next-line @typescript-eslint/naming-convention - popoverAnchorRef.current?.measureInWindow((_fx, fy, _width, height) => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.OPEN_POPOVER, - payload: { - popoverHeight: 0, - fy, - height, - }, - }); - - callback(); - }); - }, - [actionSheetAwareScrollViewContext], - ); + }, [action.reportActionID]); const disabledActions = useMemo(() => (!ReportUtils.canWriteInReport(report) ? RestrictedReadOnlyContextMenuActions : []), [report]); @@ -476,31 +447,29 @@ function PureReportActionItem({ return; } - handleShowContextMenu(() => { - setIsContextMenuActive(true); - const selection = SelectionScraper.getCurrentSelection(); - ReportActionContextMenu.showContextMenu( - CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, - event, - selection, - popoverAnchorRef.current, - reportID, - action.reportActionID, - originalReportID, - draftMessage ?? '', - () => setIsContextMenuActive(true), - toggleContextMenuFromActiveReportAction, - isArchivedRoom, - isChronosReport, - false, - false, - disabledActions, - false, - setIsEmojiPickerActive as () => void, - undefined, - isThreadReportParentAction, - ); - }); + setIsContextMenuActive(true); + const selection = SelectionScraper.getCurrentSelection(); + ReportActionContextMenu.showContextMenu( + CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, + event, + selection, + popoverAnchorRef.current, + reportID, + action.reportActionID, + originalReportID, + draftMessage ?? '', + () => setIsContextMenuActive(true), + toggleContextMenuFromActiveReportAction, + isArchivedRoom, + isChronosReport, + false, + false, + disabledActions, + false, + setIsEmojiPickerActive as () => void, + undefined, + isThreadReportParentAction, + ); }, [ draftMessage, @@ -512,7 +481,6 @@ function PureReportActionItem({ disabledActions, isArchivedRoom, isChronosReport, - handleShowContextMenu, isThreadReportParentAction, ], ); @@ -544,10 +512,9 @@ function PureReportActionItem({ action, transactionThreadReport, checkIfContextMenuActive: toggleContextMenuFromActiveReportAction, - onShowContextMenu: handleShowContextMenu, isDisabled: false, }), - [report, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, handleShowContextMenu, reportNameValuePairs], + [report, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, reportNameValuePairs], ); const attachmentContextValue = useMemo(() => ({reportID, type: CONST.ATTACHMENT_TYPE.REPORT}), [reportID]); @@ -698,7 +665,6 @@ function PureReportActionItem({ isMostRecentIOUReportAction={isMostRecentIOUReportAction} isHovered={hovered} contextMenuAnchor={popoverAnchorRef.current} - onShowContextMenu={handleShowContextMenu} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} style={displayAsGroup ? [] : [styles.mt2]} isWhisper={isWhisper} @@ -727,7 +693,6 @@ function PureReportActionItem({ containerStyles={displayAsGroup ? [] : [styles.mt2]} action={action} isHovered={hovered} - onShowContextMenu={handleShowContextMenu} contextMenuAnchor={popoverAnchorRef.current} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} onPaymentOptionsShow={() => setIsPaymentMethodPopoverActive(true)} @@ -746,7 +711,6 @@ function PureReportActionItem({ chatReportID={reportID} action={action} isHovered={hovered} - onShowContextMenu={handleShowContextMenu} contextMenuAnchor={popoverAnchorRef.current} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} policyID={report?.policyID ?? '-1'} diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 24d7da266eb0..03d71d959a0f 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -1,14 +1,13 @@ import {useNavigation} from '@react-navigation/native'; import lodashDebounce from 'lodash/debounce'; import noop from 'lodash/noop'; -import React, {memo, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; -import type {LayoutChangeEvent, MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native'; +import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import {runOnUI, useSharedValue} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentModal from '@components/AttachmentModal'; import EmojiPickerButton from '@components/EmojiPicker/EmojiPickerButton'; @@ -126,7 +125,6 @@ function ReportActionCompose({ setShowSoftInputOnFocus, didHideComposerInput, }: ReportActionComposeProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const {translate} = useLocalize(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -386,18 +384,6 @@ function ReportActionCompose({ clearComposer(); }, [isSendDisabled, isReportReadyForDisplay, composerRefShared]); - const measureComposer = useCallback( - (e: LayoutChangeEvent) => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.MEASURE_COMPOSER, - payload: { - composerHeight: e.nativeEvent.layout.height, - }, - }); - }, - [actionSheetAwareScrollViewContext], - ); - // eslint-disable-next-line react-compiler/react-compiler onSubmitAction = handleSendMessage; @@ -440,10 +426,7 @@ function ReportActionCompose({ {shouldShowReportRecipientLocalTime && hasReportRecipient && } - +