From 24a9fb35aaf8897dd24f0b4b98b23cbbb95e29ea Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Wed, 13 Mar 2024 15:20:24 +0100 Subject: [PATCH 01/64] fix: messages content overlap when bottom sheet is shown --- ios/Podfile.lock | 3 + jest/setup.ts | 3 + package-lock.json | 11 + package.json | 1 + src/App.tsx | 4 + .../ActionSheetAwareScrollViewContext.tsx | 209 ++++++++++++ .../ActionSheetKeyboardSpace.tsx | 301 ++++++++++++++++++ .../ActionSheetAwareScrollView/index.ios.tsx | 31 ++ .../ActionSheetAwareScrollView/index.tsx | 31 ++ .../BaseAnchorForAttachmentsOnly.tsx | 6 +- .../AttachmentPicker/index.native.tsx | 3 +- src/components/AttachmentPicker/types.ts | 7 + src/components/ConfirmContent.tsx | 25 +- .../EmojiPicker/EmojiPickerButton.tsx | 63 ++-- .../EmojiPickerMenu/index.native.tsx | 21 +- .../HTMLRenderers/ImageRenderer.tsx | 4 +- .../HTMLRenderers/MentionUserRenderer.tsx | 6 +- .../HTMLRenderers/PreRenderer.tsx | 6 +- .../KeyboardAvoidingView/index.ios.tsx | 2 +- src/components/KeyboardHandlerProvider.tsx | 12 + src/components/PopoverMenu.tsx | 10 +- src/components/PopoverWithMeasuredContent.tsx | 21 +- .../Reactions/AddReactionBubble.tsx | 3 +- .../QuickEmojiReactions/index.native.tsx | 28 +- .../Reactions/QuickEmojiReactions/types.ts | 2 +- .../ReportActionItemEmojiReactions.tsx | 12 + .../ReportActionItem/MoneyRequestAction.tsx | 5 + .../MoneyRequestPreviewContent.tsx | 3 +- .../MoneyRequestPreview/types.ts | 3 + .../ReportActionItem/ReportPreview.tsx | 6 +- .../ReportActionItem/TaskPreview.tsx | 17 +- src/components/ShowContextMenuContext.ts | 4 +- src/components/ThreeDotsMenu/index.tsx | 16 +- src/hooks/useWorkletStateMachine.ts | 170 ++++++++++ .../BaseReportActionContextMenu.tsx | 5 +- .../report/ContextMenu/ContextMenuActions.tsx | 25 +- .../AttachmentPickerWithMenuItems.tsx | 18 +- .../ReportActionCompose.tsx | 29 +- src/pages/home/report/ReportActionItem.tsx | 111 +++++-- src/pages/home/report/ReportActionsList.tsx | 2 + 40 files changed, 1151 insertions(+), 88 deletions(-) create mode 100644 src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx create mode 100644 src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx create mode 100644 src/components/ActionSheetAwareScrollView/index.ios.tsx create mode 100644 src/components/ActionSheetAwareScrollView/index.tsx create mode 100644 src/components/KeyboardHandlerProvider.tsx create mode 100644 src/hooks/useWorkletStateMachine.ts diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1ea6b65a58b7..1cac95e15036 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2137,6 +2137,7 @@ DEPENDENCIES: - "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)" - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-key-command (from `../node_modules/react-native-key-command`) + - react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`) - react-native-launch-arguments (from `../node_modules/react-native-launch-arguments`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-pager-view (from `../node_modules/react-native-pager-view`) @@ -2335,6 +2336,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-image-picker" react-native-key-command: :path: "../node_modules/react-native-key-command" + react-native-keyboard-controller: + :path: "../node_modules/react-native-keyboard-controller" react-native-launch-arguments: :path: "../node_modules/react-native-launch-arguments" react-native-netinfo: diff --git a/jest/setup.ts b/jest/setup.ts index 174e59a7e493..107540140f1d 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -50,6 +50,9 @@ jest.mock('react-native-sound', () => { return SoundMock; }); +// eslint-disable-next-line @typescript-eslint/no-unsafe-return +jest.mock('react-native-keyboard-controller', () => require('react-native-keyboard-controller/jest')); + jest.mock('react-native-share', () => ({ default: jest.fn(), })); diff --git a/package-lock.json b/package-lock.json index 8ceeacdbc086..d3bcfea84c51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,6 +98,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", + "react-native-keyboard-controller": "^1.10.4", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -31419,6 +31420,16 @@ "version": "5.0.1", "license": "MIT" }, + "node_modules/react-native-keyboard-controller": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.10.4.tgz", + "integrity": "sha512-PQ3AcKGnacDBeA1zB1y44XLgj0sZd3Py5Kpml412bKgYiM09JgoK7YbJcUxMayTeEGtZ8GTOteevGTbGq1Otrg==", + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-reanimated": ">=2.3.0" + } + }, "node_modules/react-native-launch-arguments": { "version": "4.0.2", "license": "MIT", diff --git a/package.json b/package.json index a8e16bb7baa6..efadc759b85f 100644 --- a/package.json +++ b/package.json @@ -150,6 +150,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", + "react-native-keyboard-controller": "^1.10.4", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", diff --git a/src/App.tsx b/src/App.tsx index 6316fa80fba1..2759913e7207 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ 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/ActiveWorkspace/ActiveWorkspaceProvider'; import ColorSchemeWrapper from './components/ColorSchemeWrapper'; @@ -14,6 +15,7 @@ import CustomStatusBarAndBackgroundContextProvider from './components/CustomStat import ErrorBoundary from './components/ErrorBoundary'; import HTMLEngineProvider from './components/HTMLEngineProvider'; import InitialURLContextProvider from './components/InitialURLContextProvider'; +import KeyboardHandlerProvider from './components/KeyboardHandlerProvider'; import {LocaleContextProvider} from './components/LocaleContextProvider'; import OnyxProvider from './components/OnyxProvider'; import PopoverContextProvider from './components/PopoverProvider'; @@ -79,6 +81,8 @@ function App({url}: AppProps) { CustomStatusBarAndBackgroundContextProvider, ActiveElementRoleProvider, ActiveWorkspaceContextProvider, + KeyboardHandlerProvider, + ActionSheetAwareScrollView.ActionSheetAwareScrollViewProvider, ReportIDsContextProvider, PlaybackContextProvider, FullScreenContextProvider, diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx new file mode 100644 index 000000000000..a8dda4adb621 --- /dev/null +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -0,0 +1,209 @@ +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; +}; +const defaultValue: Context = { + currentActionSheetState: { + value: { + previous: { + state: 'idle', + payload: null, + }, + current: { + state: 'idle', + payload: null, + }, + }, + addListener: noop, + removeListener: noop, + modify: 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', + OPEN_EMOJI_PICKER_POPOVER: 'OPEN_EMOJI_PICKER_POPOVER', + OPEN_EMOJI_PICKER_POPOVER_STANDALONE: 'OPEN_EMOJI_PICKER_POPOVER_STANDALONE', + CLOSE_EMOJI_PICKER_POPOVER: 'CLOSE_EMOJI_PICKER_POPOVER', + MEASURE_EMOJI_PICKER_POPOVER: 'MEASURE_EMOJI_PICKER_POPOVER', + HIDE_WITHOUT_ANIMATION: 'HIDE_WITHOUT_ANIMATION', + EDIT_REPORT: 'EDIT_REPORT', + SHOW_DELETE_CONFIRM_MODAL: 'SHOW_DELETE_CONFIRM_MODAL', + END_TRANSITION: 'END_TRANSITION', + OPEN_CALL_POPOVER: 'OPEN_CALL_POPOVER', + CLOSE_CONFIRM_MODAL: 'CLOSE_CONFIRM_MODAL', + MEASURE_CONFIRM_MODAL: 'MEASURE_CONFIRM_MODAL', + SHOW_ATTACHMENTS_POPOVER: 'SHOW_ATTACHMENTS_POPOVER', + CLOSE_ATTACHMENTS_POPOVER: 'CLOSE_ATTACHMENTS_POPOVER', + SHOW_ATTACHMENTS_PICKER_POPOVER: 'SHOW_ATTACHMENTS_PICKER_POPOVER', + CLOSE_EMOJI_PICKER_POPOVER_STANDALONE: 'CLOSE_EMOJI_PICKER_POPOVER_STANDALONE', + MEASURE_CALL_POPOVER: 'MEASURE_CALL_POPOVER', + CLOSE_CALL_POPOVER: 'CLOSE_CALL_POPOVER', +}; + +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', + EMOJI_PICKER_POPOVER_OPEN: 'emojiPickerPopoverOpen', + DELETE_MODAL_OPEN: 'deleteModalOpen', + DELETE_MODAL_WITH_KEYBOARD_OPEN: 'deleteModalWithKeyboardOpen', + EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN: 'emojiPickerPopoverWithKeyboardOpen', + EMOJI_PICKER_WITH_KEYBOARD_OPEN: 'emojiPickerWithKeyboardOpen', + CALL_POPOVER_WITH_KEYBOARD_OPEN: 'callPopoverWithKeyboardOpen', + CALL_POPOVER_WITH_KEYBOARD_CLOSED: 'callPopoverWithKeyboardClosed', + ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN: 'attachmentsPopoverWithKeyboardOpen', + ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED: 'attachmentsPopoverWithKeyboardClosed', + MODAL_DELETED: 'modalDeleted', + 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, + [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, + [Actions.SHOW_ATTACHMENTS_PICKER_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, + }, + [States.POPOVER_OPEN]: { + [Actions.CLOSE_POPOVER]: States.POPOVER_CLOSED, + [Actions.MEASURE_POPOVER]: States.POPOVER_OPEN, + [Actions.MEASURE_COMPOSER]: States.POPOVER_OPEN, + [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, + [Actions.POPOVER_ANY_ACTION]: States.POPOVER_CLOSED, + [Actions.HIDE_WITHOUT_ANIMATION]: States.IDLE, + [Actions.EDIT_REPORT]: States.IDLE, + [Actions.SHOW_DELETE_CONFIRM_MODAL]: States.MODAL_DELETED, + }, + [States.POPOVER_CLOSED]: { + [Actions.END_TRANSITION]: States.IDLE, + }, + [States.EMOJI_PICKER_POPOVER_OPEN]: { + [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, + [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.POPOVER_CLOSED, + }, + [States.MODAL_DELETED]: { + [Actions.MEASURE_CONFIRM_MODAL]: States.MODAL_DELETED, + [Actions.CLOSE_CONFIRM_MODAL]: States.POPOVER_CLOSED, + }, + [States.KEYBOARD_OPEN]: { + [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, + [Actions.OPEN_POPOVER]: States.KEYBOARD_POPOVER_OPEN, + [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.KEYBOARD_POPOVER_OPEN, + [Actions.OPEN_EMOJI_PICKER_POPOVER_STANDALONE]: States.EMOJI_PICKER_WITH_KEYBOARD_OPEN, + [Actions.CLOSE_KEYBOARD]: States.IDLE, + [Actions.OPEN_CALL_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.SHOW_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.SHOW_ATTACHMENTS_PICKER_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.MEASURE_COMPOSER]: States.KEYBOARD_OPEN, + }, + [States.KEYBOARD_POPOVER_OPEN]: { + [Actions.MEASURE_POPOVER]: States.KEYBOARD_POPOVER_OPEN, + [Actions.MEASURE_COMPOSER]: States.KEYBOARD_POPOVER_OPEN, + [Actions.CLOSE_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, + [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, + [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.KEYBOARD_POPOVER_OPEN, + [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.SHOW_DELETE_CONFIRM_MODAL]: States.MODAL_WITH_KEYBOARD_OPEN_DELETED, + }, + [States.MODAL_WITH_KEYBOARD_OPEN_DELETED]: { + [Actions.MEASURE_CONFIRM_MODAL]: States.MODAL_WITH_KEYBOARD_OPEN_DELETED, + [Actions.CLOSE_CONFIRM_MODAL]: States.KEYBOARD_CLOSED_POPOVER, + }, + [States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN]: { + [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, + }, + [States.EMOJI_PICKER_WITH_KEYBOARD_OPEN]: { + [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_WITH_KEYBOARD_OPEN, + [Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE]: States.KEYBOARD_POPOVER_CLOSED, + }, + [States.CALL_POPOVER_WITH_KEYBOARD_OPEN]: { + [Actions.MEASURE_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.MEASURE_CALL_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.CLOSE_CALL_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_CLOSED, + }, + [States.CALL_POPOVER_WITH_KEYBOARD_CLOSED]: { + [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, + }, + [States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN]: { + [Actions.MEASURE_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.MEASURE_COMPOSER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED, + }, + [States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED]: { + [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 new file mode 100644 index 000000000000..095cb2556077 --- /dev/null +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -0,0 +1,301 @@ +import React, {useContext, useEffect, useRef} from 'react'; +import type {ViewProps} from 'react-native'; +import {useKeyboardHandler} from 'react-native-keyboard-controller'; +import Reanimated, {interpolate, runOnJS, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withSequence, 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 useAnimatedKeyboard = () => { + const state = useSharedValue(KeyboardState.UNKNOWN); + const height = useSharedValue(0); + const progress = useSharedValue(0); + const heightWhenOpened = useSharedValue(0); + + useKeyboardHandler( + { + onStart: (e) => { + 'worklet'; + + // save the last keyboard height + if (e.height === 0) { + heightWhenOpened.value = height.value; + } + + if (e.height > 0) { + state.value = KeyboardState.OPENING; + } else { + state.value = KeyboardState.CLOSING; + } + }, + onMove: (e) => { + 'worklet'; + + progress.value = e.progress; + height.value = e.height; + }, + onEnd: (e) => { + 'worklet'; + + if (e.height > 0) { + state.value = KeyboardState.OPEN; + } else { + state.value = KeyboardState.CLOSED; + } + + height.value = e.height; + progress.value = e.progress; + }, + }, + [], + ); + + return {state, height, heightWhenOpened, progress}; +}; +const setInitialValueAndRunAnimation = (value: number, animation: number) => { + 'worklet'; + + return withSequence(withTiming(value, {duration: 0}), animation); +}; + +const useSafeAreaPaddings = () => { + const StyleUtils = useStyleUtils(); + const insets = useSafeAreaInsets(); + const {paddingTop, paddingBottom} = StyleUtils.getSafeAreaPadding(insets ?? undefined); + + return {top: paddingTop, bottom: paddingBottom}; +}; + +const config = { + mass: 3, + stiffness: 1000, + damping: 500, +}; + +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 = useRef({ + lastState: KeyboardState.UNKNOWN, + }).current; + const {windowHeight} = useWindowDimensions(); + const {currentActionSheetState, transitionActionSheetStateWorklet: transition, transitionActionSheetState, resetStateMachine} = useContext(ActionSheetAwareScrollViewContext); + + // Reset state machine when component unmounts + useEffect(() => () => resetStateMachine(), [resetStateMachine]); + + useAnimatedReaction( + () => keyboard.state.value, + (lastState) => { + if (lastState === syncLocalWorkletState.lastState) { + return; + } + + syncLocalWorkletState.lastState = lastState; + + if (lastState === KeyboardState.OPEN) { + runOnJS(transitionActionSheetState)({ + type: Actions.OPEN_KEYBOARD, + }); + } else if (lastState === KeyboardState.CLOSED) { + runOnJS(transitionActionSheetState)({ + type: Actions.CLOSE_KEYBOARD, + }); + } + }, + [], + ); + + const translateY = useDerivedValue(() => { + const {current, previous} = currentActionSheetState.value; + + // 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, config); + } + + const keyboardHeight = keyboard.height.value === 0 ? 0 : keyboard.height.value - safeArea.bottom; + // sometimes we need to know the last keyboard height + const lastKeyboardHeight = keyboard.heightWhenOpened.value - safeArea.bottom; + + const {popoverHeight = 0, fy, height, composerHeight = 0} = current.payload ?? {}; + + const invertedKeyboardHeight = keyboard.state.value === KeyboardState.CLOSED ? lastKeyboardHeight : 0; + + let elementOffset = 0; + + if (fy !== undefined && height !== undefined && popoverHeight !== undefined) { + elementOffset = fy + safeArea.top + height - (windowHeight - popoverHeight); + } + + // when the sate is not idle we know for sure we have previous state + const previousPayload = previous.payload ?? {}; + + let previousElementOffset = 0; + + if (previousPayload.fy !== undefined && previousPayload.height !== undefined && previousPayload.popoverHeight !== undefined) { + previousElementOffset = previousPayload.fy + safeArea.top + previousPayload.height - (windowHeight - previousPayload.popoverHeight); + } + + // 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 (previous.state === States.KEYBOARD_CLOSED_POPOVER) { + return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0); + } + + return withSpring(0, config); + } + + case States.POPOVER_CLOSED: { + return withSpring(0, config, () => { + transition({ + type: Actions.END_TRANSITION, + }); + }); + } + + case States.MODAL_DELETED: + case States.EMOJI_PICKER_POPOVER_OPEN: + case States.POPOVER_OPEN: { + if (popoverHeight) { + if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { + return withSpring(elementOffset < 0 ? 0 : elementOffset, config); + } + + return withSpring(Math.max(previousElementOffset, 0), config); + } + + return 0; + } + + case States.MODAL_WITH_KEYBOARD_OPEN_DELETED: + case States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN: { + // when item is higher than keyboard and bottom sheet + // we should just stay in place + if (elementOffset < 0) { + return invertedKeyboardHeight; + } + + const nextOffset = invertedKeyboardHeight + elementOffset; + if (previous?.payload?.popoverHeight !== popoverHeight) { + const previousOffset = invertedKeyboardHeight + previousElementOffset; + + if (previousElementOffset === 0 || nextOffset > previousOffset) { + return withSpring(nextOffset, config); + } + + return previousOffset; + } + + return nextOffset; + } + + case States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED: + case States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN: { + return interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]); + } + case States.CALL_POPOVER_WITH_KEYBOARD_OPEN: { + if (keyboard.height.value > 0) { + return 0; + } + + return setInitialValueAndRunAnimation(lastKeyboardHeight, withSpring(popoverHeight - composerHeight, config)); + } + case States.CALL_POPOVER_WITH_KEYBOARD_CLOSED: { + // keyboard is opened + if (keyboard.height.value > 0) { + return 0; + } + + return withSpring(lastKeyboardHeight, config); + } + case States.EMOJI_PICKER_WITH_KEYBOARD_OPEN: { + if (keyboard.state.value === KeyboardState.CLOSED) { + return popoverHeight - composerHeight; + } + + return 0; + } + + case States.KEYBOARD_POPOVER_CLOSED: { + if (keyboard.heightWhenOpened.value === keyboard.height.value) { + return 0; + } + + return popoverHeight - composerHeight; + } + + case States.KEYBOARD_POPOVER_OPEN: { + if (keyboard.state.value === KeyboardState.OPEN) { + return 0; + } + + const nextOffset = elementOffset + lastKeyboardHeight; + + if (keyboard.state.value === KeyboardState.CLOSED && nextOffset > invertedKeyboardHeight) { + return withSpring(nextOffset < 0 ? 0 : nextOffset, config); + } + + if (elementOffset < 0) { + return lastKeyboardHeight - keyboardHeight; + } + + return lastKeyboardHeight; + } + + case States.KEYBOARD_CLOSED_POPOVER: { + if (elementOffset < 0) { + transition({type: Actions.END_TRANSITION}); + + return 0; + } + + if (keyboard.state.value === KeyboardState.CLOSED) { + return elementOffset + lastKeyboardHeight; + } + + if (keyboard.height.value > 0) { + return keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset; + } + + return withTiming(elementOffset + lastKeyboardHeight, { + duration: 0, + }); + } + + default: + return 0; + } + }, []); + + const animatedStyle = useAnimatedStyle(() => ({ + paddingTop: translateY.value, + })); + + return ( + + ); +} + +ActionSheetKeyboardSpace.displayName = 'ReportKeyboardSpace'; + +export default ActionSheetKeyboardSpace; diff --git a/src/components/ActionSheetAwareScrollView/index.ios.tsx b/src/components/ActionSheetAwareScrollView/index.ios.tsx new file mode 100644 index 000000000000..2c40df7e61c6 --- /dev/null +++ b/src/components/ActionSheetAwareScrollView/index.ios.tsx @@ -0,0 +1,31 @@ +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 new file mode 100644 index 000000000000..d22f991ce4cf --- /dev/null +++ b/src/components/ActionSheetAwareScrollView/index.tsx @@ -0,0 +1,31 @@ +// 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/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx index 595e28acd3bc..53f9daf3e044 100644 --- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx +++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx @@ -41,7 +41,7 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', dow return ( - {({anchor, report, action, checkIfContextMenuActive}) => ( + {({onShowContextMenu, anchor, report, action, checkIfContextMenuActive}) => ( { @@ -53,7 +53,9 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', dow }} onPressIn={onPressIn} onPressOut={onPressOut} - onLongPress={(event) => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))} + onLongPress={(event) => + onShowContextMenu(() => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))) + } shouldUseHapticsOnLongPress accessibilityLabel={displayName} role={CONST.ROLE.BUTTON} diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index f6730f4b81d9..6824a8f59335 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -110,7 +110,7 @@ const getDataForUpload = (fileData: FileResponse): Promise => { * a callback. This is the ios/android implementation * opening a modal with attachment options */ -function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, shouldHideCameraOption = false}: AttachmentPickerProps) { +function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, shouldHideCameraOption = false, onLayout}: AttachmentPickerProps) { const styles = useThemeStyles(); const [isVisible, setIsVisible] = useState(false); @@ -347,6 +347,7 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s return ( <> { close(); onCanceled.current(); diff --git a/src/components/AttachmentPicker/types.ts b/src/components/AttachmentPicker/types.ts index 445d79bce07a..8f98275da1bd 100644 --- a/src/components/AttachmentPicker/types.ts +++ b/src/components/AttachmentPicker/types.ts @@ -1,4 +1,5 @@ import type {ReactNode} from 'react'; +import type {LayoutChangeEvent} from 'react-native'; import type {ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; import type CONST from '@src/CONST'; @@ -40,6 +41,12 @@ type AttachmentPickerProps = { /** The types of files that can be selected with this picker. */ type?: ValueOf; + + /** + * Optional callback attached to popover's children container. + * Invoked on Popover mount and layout changes. + */ + onLayout?: ((event: LayoutChangeEvent) => void) | undefined; }; export default AttachmentPickerProps; diff --git a/src/components/ConfirmContent.tsx b/src/components/ConfirmContent.tsx index 26331f92401c..c0fb6d94018d 100644 --- a/src/components/ConfirmContent.tsx +++ b/src/components/ConfirmContent.tsx @@ -1,6 +1,6 @@ import type {ReactNode} from 'react'; -import React from 'react'; -import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; +import React, {useCallback, useContext} from 'react'; +import type {LayoutChangeEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -11,6 +11,7 @@ import colors from '@styles/theme/colors'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type IconAsset from '@src/types/utils/IconAsset'; +import {Actions, ActionSheetAwareScrollViewContext} from './ActionSheetAwareScrollView'; import Button from './Button'; import Header from './Header'; import Icon from './Icon'; @@ -93,12 +94,27 @@ function ConfirmContent({ iconAdditionalStyles, image, }: ConfirmContentProps) { + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); const {isOffline} = useNetwork(); const StyleUtils = useStyleUtils(); + const onLayout = useCallback( + (event: LayoutChangeEvent) => { + const {height} = event.nativeEvent.layout; + + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: Actions.MEASURE_CONFIRM_MODAL, + payload: { + popoverHeight: height, + }, + }); + }, + [actionSheetAwareScrollViewContext], + ); + const isCentered = shouldCenterContent; return ( @@ -115,7 +131,10 @@ function ConfirmContent({ )} - + {typeof iconSource === 'function' && ( diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 6e0944e5a913..d2e732728af9 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -1,5 +1,6 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {memo, useEffect, useRef} from 'react'; +import React, {memo, useContext, useEffect, useRef} from 'react'; +import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; @@ -30,12 +31,50 @@ type EmojiPickerButtonProps = { }; function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shiftVertical = 0, 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 onPress = () => { + if (!isFocused) { + return; + } + + actionSheetContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.OPEN_EMOJI_PICKER_POPOVER_STANDALONE, + }); + + const onHide = () => { + actionSheetContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE, + }); + + if (onModalHide) { + onModalHide(); + } + }; + + if (!EmojiPickerAction.emojiPickerRef.current?.isEmojiPickerVisible) { + EmojiPickerAction.showEmojiPicker( + onHide, + onEmojiSelected, + emojiPopoverAnchor, + { + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, + shiftVertical, + }, + () => {}, + emojiPickerID, + ); + } else { + EmojiPickerAction.emojiPickerRef.current.hideEmojiPicker(); + } + }; + useEffect(() => EmojiPickerAction.resetEmojiPopoverAnchor, []); return ( @@ -44,27 +83,7 @@ function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shi ref={emojiPopoverAnchor} style={({hovered, pressed}) => [styles.chatItemEmojiButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed))]} disabled={isDisabled} - onPress={() => { - 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={onPress} id={id} accessibilityLabel={translate('reportActionCompose.emoji')} > diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx index b5b4c2d7e71c..01826cd07163 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx @@ -1,9 +1,10 @@ import type {ListRenderItem} from '@shopify/flash-list'; import lodashDebounce from 'lodash/debounce'; -import React, {useCallback} from 'react'; +import React, {useCallback, useContext} from 'react'; import type {ForwardedRef} from 'react'; import {View} from 'react-native'; import {runOnUI, scrollTo} from 'react-native-reanimated'; +import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import EmojiPickerMenuItem from '@components/EmojiPicker/EmojiPickerMenuItem'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; @@ -114,9 +115,25 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r }, [styles, windowWidth, preferredSkinTone, singleExecution, onEmojiSelected, translate, activeEmoji], ); + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); + const onLayout = useCallback( + (event) => { + const {height} = event.nativeEvent.layout; + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.MEASURE_EMOJI_PICKER_POPOVER, + payload: { + popoverHeight: height, + }, + }); + }, + [actionSheetAwareScrollViewContext], + ); return ( - + - {({anchor, report, action, checkIfContextMenuActive}) => ( + {({onShowContextMenu, anchor, report, action, checkIfContextMenuActive}) => ( {({reportID, accountID, type}) => ( showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))} + onLongPress={(event) => onShowContextMenu(() => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report)))} shouldUseHapticsOnLongPress accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} accessibilityLabel={translate('accessibilityHints.viewAttachment')} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index 504ddecb492b..95c4d7e636d7 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -83,10 +83,12 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona return ( - {({anchor, report, action, checkIfContextMenuActive}) => ( + {({onShowContextMenu, anchor, report, action, checkIfContextMenuActive}) => ( showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))} + onLongPress={(event) => + onShowContextMenu(() => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))) + } onPress={(event) => { event.preventDefault(); Navigation.navigate(navigationRoute); diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx index 39a1993c2334..69e9fa0e0f80 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx @@ -34,12 +34,14 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d return ( - {({anchor, report, action, checkIfContextMenuActive}) => ( + {({onShowContextMenu, anchor, report, action, checkIfContextMenuActive}) => ( {})} onPressIn={onPressIn} onPressOut={onPressOut} - onLongPress={(event) => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))} + onLongPress={(event) => + onShowContextMenu(() => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))) + } shouldUseHapticsOnLongPress role={CONST.ROLE.PRESENTATION} accessibilityLabel={translate('accessibilityHints.prestyledText')} diff --git a/src/components/KeyboardAvoidingView/index.ios.tsx b/src/components/KeyboardAvoidingView/index.ios.tsx index a7cd767377ef..485d230bbfd8 100644 --- a/src/components/KeyboardAvoidingView/index.ios.tsx +++ b/src/components/KeyboardAvoidingView/index.ios.tsx @@ -2,7 +2,7 @@ * The KeyboardAvoidingView is only used on ios */ import React from 'react'; -import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native'; +import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native-keyboard-controller'; import type KeyboardAvoidingViewProps from './types'; function KeyboardAvoidingView(props: KeyboardAvoidingViewProps) { diff --git a/src/components/KeyboardHandlerProvider.tsx b/src/components/KeyboardHandlerProvider.tsx new file mode 100644 index 000000000000..dc208d10aeb3 --- /dev/null +++ b/src/components/KeyboardHandlerProvider.tsx @@ -0,0 +1,12 @@ +import type {PropsWithChildren} from 'react'; +import React from 'react'; +import {Platform} from 'react-native'; +import {KeyboardProvider} from 'react-native-keyboard-controller'; + +type Props = PropsWithChildren; + +function KeyboardHandlerProvider({children}: Props) { + return {children}; +} + +export default KeyboardHandlerProvider; diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index e525d07fa1d3..f0f708d9deb0 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -1,5 +1,6 @@ import type {RefObject} from 'react'; import React, {useEffect, useRef, useState} from 'react'; +import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import type {ModalProps} from 'react-native-modal'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; @@ -36,6 +37,9 @@ 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; + /** State that determines whether to display the modal or not */ isVisible: boolean; @@ -83,6 +87,7 @@ function PopoverMenu({ anchorPosition, anchorRef, onClose, + onLayout, headerText, fromSidebarMediumScreen, anchorAlignment = { @@ -198,7 +203,10 @@ function PopoverMenu({ shouldSetModalVisibility={shouldSetModalVisibility} shouldEnableNewFocusManagement={shouldEnableNewFocusManagement} > - + {!!headerText && {headerText}} {enteredSubMenuIndexes.length > 0 && renderBackButtonItem()} {currentMenuItems.map((item, menuIndex) => ( diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index 32cc589bf0fb..ecfb6c9d3ca7 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -1,5 +1,5 @@ import isEqual from 'lodash/isEqual'; -import React, {useMemo, useState} from 'react'; +import React, {useContext, useMemo, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -8,6 +8,7 @@ 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'; import type {WindowDimensionsProps} from './withWindowDimensions/types'; @@ -61,6 +62,7 @@ function PopoverWithMeasuredContent({ shouldEnableNewFocusManagement, ...props }: PopoverWithMeasuredContentProps) { + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const {windowWidth, windowHeight} = useWindowDimensions(); const [popoverWidth, setPopoverWidth] = useState(popoverDimensions.width); @@ -89,9 +91,22 @@ function PopoverWithMeasuredContent({ * Measure the size of the popover's content. */ const measurePopover = ({nativeEvent}: LayoutChangeEvent) => { - setPopoverWidth(nativeEvent.layout.width); - setPopoverHeight(nativeEvent.layout.height); + const {width, height} = nativeEvent.layout; + setPopoverWidth(width); + setPopoverHeight(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.value.current.payload?.popoverHeight !== Math.floor(popoverHeight)) { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.MEASURE_POPOVER, + payload: { + popoverHeight: Math.floor(popoverHeight), + }, + }); + } }; const adjustedAnchorPosition = useMemo(() => { diff --git a/src/components/Reactions/AddReactionBubble.tsx b/src/components/Reactions/AddReactionBubble.tsx index 8364a6658270..e7af464d61f5 100644 --- a/src/components/Reactions/AddReactionBubble.tsx +++ b/src/components/Reactions/AddReactionBubble.tsx @@ -57,9 +57,10 @@ function AddReactionBubble({onSelectEmoji, reportAction, onPressOpenPicker, onWi useEffect(() => EmojiPickerAction.resetEmojiPopoverAnchor, []); const onPress = () => { - const openPicker = (refParam?: PickerRefElement, anchorOrigin?: AnchorOrigin) => { + const openPicker = (refParam?: PickerRefElement, anchorOrigin?: AnchorOrigin, onHide = () => {}) => { EmojiPickerAction.showEmojiPicker( () => { + onHide(); setIsEmojiPickerActive?.(false); }, (emojiCode, emojiObject) => { diff --git a/src/components/Reactions/QuickEmojiReactions/index.native.tsx b/src/components/Reactions/QuickEmojiReactions/index.native.tsx index b0eb88b31b68..6c55beb9741d 100644 --- a/src/components/Reactions/QuickEmojiReactions/index.native.tsx +++ b/src/components/Reactions/QuickEmojiReactions/index.native.tsx @@ -1,10 +1,17 @@ -import React from 'react'; +import React, {useContext} from 'react'; +import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import BaseQuickEmojiReactions from './BaseQuickEmojiReactions'; -import type {OpenPickerCallback, QuickEmojiReactionsProps} from './types'; +import type {BaseQuickEmojiReactionsProps, OpenPickerCallback, QuickEmojiReactionsProps} from './types'; + +function QuickEmojiReactions({closeContextMenu, onEmojiSelected, ...rest}: QuickEmojiReactionsProps) { + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); -function QuickEmojiReactions({closeContextMenu, ...rest}: QuickEmojiReactionsProps) { const onPressOpenPicker = (openPicker?: OpenPickerCallback) => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.OPEN_EMOJI_PICKER_POPOVER, + }); + // We first need to close the menu as it's a popover. // The picker is a popover as well and on mobile there can only // be one active popover at a time. @@ -13,13 +20,28 @@ function QuickEmojiReactions({closeContextMenu, ...rest}: QuickEmojiReactionsPro // gets closed, before the picker actually opens, we pass the composer // ref as anchor for the emoji picker popover. openPicker?.(ReportActionComposeFocusManager.composerRef); + + openPicker?.(ReportActionComposeFocusManager.composerRef, undefined, () => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER, + }); + }); }); }; + const onEmojiSelectedCallback: BaseQuickEmojiReactionsProps['onEmojiSelected'] = (emoji, emojiReactions) => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER, + }); + + onEmojiSelected(emoji, emojiReactions); + }; + return ( ); diff --git a/src/components/Reactions/QuickEmojiReactions/types.ts b/src/components/Reactions/QuickEmojiReactions/types.ts index 0021f33ce2c0..725b5aea764f 100644 --- a/src/components/Reactions/QuickEmojiReactions/types.ts +++ b/src/components/Reactions/QuickEmojiReactions/types.ts @@ -7,7 +7,7 @@ import type {Locale, ReportAction, ReportActionReactions} from '@src/types/onyx' type PickerRefElement = RefObject; -type OpenPickerCallback = (element?: PickerRefElement, anchorOrigin?: AnchorOrigin) => void; +type OpenPickerCallback = (element?: PickerRefElement, anchorOrigin?: AnchorOrigin, callback?: () => void) => void; type CloseContextMenuCallback = () => void; diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.tsx b/src/components/Reactions/ReportActionItemEmojiReactions.tsx index c6bf4f9e4016..f932c55b97ce 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.tsx +++ b/src/components/Reactions/ReportActionItemEmojiReactions.tsx @@ -16,6 +16,7 @@ import type {Locale, ReportAction, ReportActionReactions} from '@src/types/onyx' import type {PendingAction} from '@src/types/onyx/OnyxCommon'; import AddReactionBubble from './AddReactionBubble'; import EmojiReactionBubble from './EmojiReactionBubble'; +import type {OpenPickerCallback} from './QuickEmojiReactions/types'; import ReactionTooltipContent from './ReactionTooltipContent'; type ReportActionItemEmojiReactionsProps = WithCurrentUserPersonalDetailsProps & { @@ -35,6 +36,15 @@ type ReportActionItemEmojiReactionsProps = WithCurrentUserPersonalDetailsProps & */ toggleReaction: (emoji: Emoji) => void; + /** + * Function to call when the user presses on the add reaction button. + * This is only called when the user presses on the button, not on the + * reaction bubbles. + * This is optional, because we don't need it everywhere. + * For example in the ReportActionContextMenu we don't need it. + */ + onPressOpenPicker: (openPicker: OpenPickerCallback) => void; + /** We disable reacting with emojis on report actions that have errors */ shouldBlockReactions?: boolean; @@ -79,6 +89,7 @@ function ReportActionItemEmojiReactions({ reportAction, currentUserPersonalDetails, toggleReaction, + onPressOpenPicker, emojiReactions = {}, shouldBlockReactions = false, preferredLocale = CONST.LOCALES.DEFAULT, @@ -170,6 +181,7 @@ function ReportActionItemEmojiReactions({ })} {!shouldBlockReactions && ( 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; @@ -69,6 +72,7 @@ function MoneyRequestAction({ reportID, isMostRecentIOUReportAction, contextMenuAnchor, + onShowContextMenu = () => {}, checkIfContextMenuActive = () => {}, chatReport, iouReport, @@ -128,6 +132,7 @@ 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 3331572ab625..ed7df3c4de1e 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -56,6 +56,7 @@ function MoneyRequestPreviewContent({ containerStyles, walletTerms, checkIfContextMenuActive = () => {}, + onShowContextMenu = () => {}, shouldShowPendingConversionMessage = false, isHovered = false, isWhisper = false, @@ -137,7 +138,7 @@ function MoneyRequestPreviewContent({ }; const showContextMenu = (event: GestureResponderEvent) => { - showContextMenuForReport(event, contextMenuAnchor, reportID, action, checkIfContextMenuActive); + onShowContextMenu(() => 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 9dcea80fdc05..694f229a8388 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestPreview/types.ts @@ -51,6 +51,9 @@ type MoneyRequestPreviewProps = MoneyRequestPreviewOnyxProps & { /** 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 be3b104018db..deb8aead451c 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -78,6 +78,9 @@ type ReportPreviewProps = ReportPreviewOnyxProps & { /** 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 a message is a whisper */ isWhisper?: boolean; @@ -101,6 +104,7 @@ function ReportPreview({ isWhisper = false, checkIfContextMenuActive = () => {}, userWallet, + onShowContextMenu = () => {}, }: ReportPreviewProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -293,7 +297,7 @@ function ReportPreview({ }} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} - onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive)} + onLongPress={(event) => onShowContextMenu(() => 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 8e8b3b930be7..33256116cae7 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -58,9 +58,22 @@ 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; }; -function TaskPreview({taskReport, taskReportID, action, contextMenuAnchor, chatReportID, checkIfContextMenuActive, currentUserPersonalDetails, isHovered = false}: TaskPreviewProps) { +function TaskPreview({ + taskReport, + taskReportID, + action, + contextMenuAnchor, + chatReportID, + checkIfContextMenuActive, + currentUserPersonalDetails, + onShowContextMenu, + isHovered = false, +}: TaskPreviewProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); @@ -87,7 +100,7 @@ function TaskPreview({taskReport, taskReportID, action, contextMenuAnchor, chatR onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(taskReportID))} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} - onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive)} + onLongPress={(event) => onShowContextMenu(() => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive))} shouldUseHapticsOnLongPress style={[styles.flexRow, styles.justifyContentBetween]} role={CONST.ROLE.BUTTON} diff --git a/src/components/ShowContextMenuContext.ts b/src/components/ShowContextMenuContext.ts index 3a996a8d2c64..e0ab5a215f17 100644 --- a/src/components/ShowContextMenuContext.ts +++ b/src/components/ShowContextMenuContext.ts @@ -15,10 +15,12 @@ type ShowContextMenuContextProps = { action: OnyxEntry; transactionThreadReport: OnyxEntry; checkIfContextMenuActive: () => void; + onShowContextMenu: (callback: () => void) => void; }; const ShowContextMenuContext = createContext({ anchor: null, + onShowContextMenu: (callback) => callback(), report: null, action: null, transactionThreadReport: null, @@ -58,7 +60,7 @@ function showContextMenuForReport( action?.reportActionID, ReportUtils.getOriginalReportID(reportID, action), undefined, - checkIfContextMenuActive, + undefined, checkIfContextMenuActive, isArchivedRoom, ); diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index 7c3358e4688c..2e45d9a741df 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -1,7 +1,8 @@ -import React, {useEffect, useRef, useState} from 'react'; +import React, {useCallback, useContext, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; +import {Actions, ActionSheetAwareScrollViewContext} from '@components/ActionSheetAwareScrollView'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PopoverMenu from '@components/PopoverMenu'; @@ -38,6 +39,7 @@ function ThreeDotsMenu({ disabled = false, modal = {}, }: ThreeDotsMenuProps) { + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollViewContext); const theme = useTheme(); const styles = useThemeStyles(); const [isPopupMenuVisible, setPopupMenuVisible] = useState(false); @@ -46,19 +48,25 @@ function ThreeDotsMenu({ const isBehindModal = modal?.willAlertModalBecomeVisible && !modal?.isPopover && !shouldOverlay; const showPopoverMenu = () => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: Actions.OPEN_CALL_POPOVER, + }); setPopupMenuVisible(true); }; - const hidePopoverMenu = () => { + const hidePopoverMenu = useCallback(() => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: Actions.CLOSE_CALL_POPOVER, + }); setPopupMenuVisible(false); - }; + }, [actionSheetAwareScrollViewContext]); useEffect(() => { if (!isBehindModal || !isPopupMenuVisible) { return; } hidePopoverMenu(); - }, [isBehindModal, isPopupMenuVisible]); + }, [hidePopoverMenu, isBehindModal, isPopupMenuVisible]); return ( <> diff --git a/src/hooks/useWorkletStateMachine.ts b/src/hooks/useWorkletStateMachine.ts new file mode 100644 index 000000000000..5b09dc22b059 --- /dev/null +++ b/src/hooks/useWorkletStateMachine.ts @@ -0,0 +1,170 @@ +import {useCallback} from 'react'; +import {runOnJS, runOnUI, useSharedValue} from 'react-native-reanimated'; +import Log from '@libs/Log'; + +// 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

; +}; + +type StateMachine = Record>; + +/** + * 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(Log.client)(`[StateMachine] ${message}. Params: ${params}`); + }, []); + + const transitionWorklet = useCallback( + (action: ActionWithPayload

) => { + 'worklet'; + + if (!action) { + throw new Error('state machine action is required'); + } + + const state = currentState.value; + + 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.value = { + previous: state.current, + current: { + state: nextState, + payload: nextPayload, + }, + }; + }, + [currentState, log, stateMachine], + ); + + const resetWorklet = useCallback(() => { + 'worklet'; + + log('RESET STATE MACHINE'); + currentState.value = initialState; + }, [currentState, initialState, log]); + + const reset = useCallback(() => { + runOnUI(resetWorklet)(); + }, [resetWorklet]); + + const transition = useCallback( + (action: ActionWithPayload

) => { + runOnUI(transitionWorklet)(action); + }, + [transitionWorklet], + ); + + return { + currentState, + transitionWorklet, + transition, + reset, + resetWorklet, + }; +} + +export type {ActionWithPayload, State}; +export default useWorkletStateMachine; diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 46ebdd751762..c6858b57b868 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -1,11 +1,12 @@ import lodashIsEqual from 'lodash/isEqual'; import type {MutableRefObject, RefObject} from 'react'; -import React, {memo, useMemo, useRef, useState} from 'react'; +import React, {memo, useContext, 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 {withOnyx} from 'react-native-onyx'; +import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import type {ContextMenuItemHandle} from '@components/ContextMenuItem'; import ContextMenuItem from '@components/ContextMenuItem'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; @@ -118,6 +119,7 @@ function BaseReportActionContextMenu({ disabledActions = [], setIsEmojiPickerActive, }: BaseReportActionContextMenuProps) { + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -241,6 +243,7 @@ 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 efcd08c35a00..95cbf5845ab4 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -7,6 +7,7 @@ import {InteractionManager} from 'react-native'; import type {GestureResponderEvent, Text, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {Emoji} from '@assets/emojis/types'; +import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import * as Expensicons from '@components/Icon/Expensicons'; import MiniQuickEmojiReactions from '@components/Reactions/MiniQuickEmojiReactions'; import QuickEmojiReactions from '@components/Reactions/QuickEmojiReactions'; @@ -30,8 +31,8 @@ import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type {Beta, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; +import {clearActiveReportAction, 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: OnyxEntry): string { @@ -73,6 +74,7 @@ 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; @@ -229,7 +231,7 @@ const ContextMenuActions: ContextMenuAction[] = [ icon: Expensicons.Pencil, shouldShow: (type, reportAction, isArchivedRoom, betas, menuTarget, isChronosReport) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canEditReportAction(reportAction) && !isArchivedRoom && !isChronosReport, - onPress: (closePopover, {reportID, reportAction, draftMessage}) => { + onPress: (closePopover, {reportID, reportAction, draftMessage, transitionActionSheetState}) => { if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { hideContextMenu(false); const childReportID = reportAction?.childReportID ?? '0'; @@ -247,6 +249,10 @@ const ContextMenuActions: ContextMenuAction[] = [ }; if (closePopover) { + transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.EDIT_REPORT, + }); + // Hide popover, then call editAction hideContextMenu(false, editAction); return; @@ -490,10 +496,21 @@ const ContextMenuActions: ContextMenuAction[] = [ !isArchivedRoom && !isChronosReport && !ReportActionsUtils.isMessageDeleted(reportAction), - onPress: (closePopover, {reportID, reportAction}) => { + onPress: (closePopover, {reportID, reportAction, transitionActionSheetState}) => { if (closePopover) { + transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.SHOW_DELETE_CONFIRM_MODAL, + }); + + const onClose = () => { + transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.CLOSE_CONFIRM_MODAL, + }); + clearActiveReportAction(); + }; + // Hide popover, then call showDeleteConfirmModal - hideContextMenu(false, () => showDeleteModal(reportID, reportAction)); + hideContextMenu(false, () => showDeleteModal(reportID, reportAction, true, onClose, onClose)); return; } diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 26aefe24bb20..7d2402813193 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,8 +1,10 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo} from 'react'; +import React, {useCallback, useContext, useEffect, useMemo} from 'react'; +import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; +import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentPicker from '@components/AttachmentPicker'; import Icon from '@components/Icon'; @@ -112,6 +114,7 @@ function AttachmentPickerWithMenuItems({ actionButtonRef, raiseIsScrollLikelyLayoutTriggered, }: AttachmentPickerWithMenuItemsProps) { + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const isFocused = useIsFocused(); const theme = useTheme(); const styles = useThemeStyles(); @@ -175,6 +178,18 @@ function AttachmentPickerWithMenuItems({ ]; }, [report, reportID, translate]); + const measurePopover = useCallback( + ({nativeEvent}: LayoutChangeEvent) => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.MEASURE_POPOVER, + payload: { + popoverHeight: nativeEvent.layout.height, + }, + }); + }, + [actionSheetAwareScrollViewContext], + ); + const onPopoverMenuClose = () => { setMenuVisibility(false); onMenuClosed(); @@ -296,6 +311,7 @@ function AttachmentPickerWithMenuItems({ { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: isMenuVisible ? ActionSheetAwareScrollView.Actions.SHOW_ATTACHMENTS_POPOVER : ActionSheetAwareScrollView.Actions.CLOSE_ATTACHMENTS_POPOVER, + }); + }, [actionSheetAwareScrollViewContext, isMenuVisible]); + // When we invite someone to a room they don't have the policy object, but we still want them to be able to mention other reports they are members of, so we only check if the policyID in the report is from a workspace const isGroupPolicyReport = useMemo(() => !!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE, [report]); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); @@ -371,6 +379,18 @@ function ReportActionCompose({ runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); + const measureComposer = useCallback( + (e: LayoutChangeEvent) => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.MEASURE_COMPOSER, + payload: { + composerHeight: e.nativeEvent.layout.height, + }, + }); + }, + [actionSheetAwareScrollViewContext], + ); + const emojiShiftVertical = useMemo(() => { const chatItemComposeSecondaryRowHeight = styles.chatItemComposeSecondaryRow.height + styles.chatItemComposeSecondaryRow.marginTop + styles.chatItemComposeSecondaryRow.marginBottom; const reportActionComposeHeight = styles.chatItemComposeBox.minHeight + chatItemComposeSecondaryRowHeight; @@ -383,7 +403,10 @@ function ReportActionCompose({ {shouldShowReportRecipientLocalTime && hasReportRecipient && } - + { setIsContextMenuActive(ReportActionContextMenu.isActiveReportAction(action.reportActionID)); - }, [action.reportActionID]); + + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.CLOSE_POPOVER, + }); + }, [actionSheetAwareScrollViewContext, action.reportActionID]); + + const handlePressOpenPicker = useCallback( + (openPicker: OpenPickerCallback) => { + 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_EMOJI_PICKER_POPOVER, + payload: { + fy, + height, + }, + }); + + openPicker(undefined, undefined, () => { + actionSheetAwareScrollViewContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER, + }); + }); + }); + }, + [actionSheetAwareScrollViewContext], + ); + + 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], + ); /** * Show the ReportActionContextMenu modal popover. @@ -335,29 +391,31 @@ function ReportActionItem({ return; } - setIsContextMenuActive(true); - const selection = SelectionScraper.getCurrentSelection(); - ReportActionContextMenu.showContextMenu( - CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, - event, - selection, - popoverAnchorRef.current, - report.reportID, - action.reportActionID, - originalReportID, - draftMessage ?? '', - () => setIsContextMenuActive(true), - toggleContextMenuFromActiveReportAction, - ReportUtils.isArchivedRoom(originalReport), - ReportUtils.chatIncludesChronos(originalReport), - false, - false, - [], - false, - setIsEmojiPickerActive as () => void, - ); + handleShowContextMenu(() => { + setIsContextMenuActive(true); + const selection = SelectionScraper.getCurrentSelection(); + ReportActionContextMenu.showContextMenu( + CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, + event, + selection, + popoverAnchorRef.current, + report.reportID, + action.reportActionID, + originalReportID, + draftMessage ?? '', + () => setIsContextMenuActive(true), + toggleContextMenuFromActiveReportAction, + ReportUtils.isArchivedRoom(originalReport), + ReportUtils.chatIncludesChronos(originalReport), + false, + false, + [], + false, + setIsEmojiPickerActive as () => void, + ); + }); }, - [draftMessage, action, report.reportID, toggleContextMenuFromActiveReportAction, originalReport, originalReportID], + [draftMessage, action, report.reportID, toggleContextMenuFromActiveReportAction, originalReport, originalReportID, handleShowContextMenu], ); // Handles manual scrolling to the bottom of the chat when the last message is an actionable whisper and it's resolved. @@ -386,8 +444,9 @@ function ReportActionItem({ action, transactionThreadReport, checkIfContextMenuActive: toggleContextMenuFromActiveReportAction, + onShowContextMenu: handleShowContextMenu, }), - [report, action, toggleContextMenuFromActiveReportAction, transactionThreadReport], + [report, action, toggleContextMenuFromActiveReportAction, transactionThreadReport, handleShowContextMenu], ); const attachmentContextValue = useMemo(() => ({reportID: report.reportID, type: CONST.ATTACHMENT_TYPE.REPORT}), [report.reportID]); @@ -530,6 +589,7 @@ function ReportActionItem({ isMostRecentIOUReportAction={isMostRecentIOUReportAction} isHovered={hovered} contextMenuAnchor={popoverAnchorRef.current} + onShowContextMenu={handleShowContextMenu} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} style={displayAsGroup ? [] : [styles.mt2]} isWhisper={isWhisper} @@ -546,6 +606,7 @@ function ReportActionItem({ containerStyles={displayAsGroup ? [] : [styles.mt2]} action={action} isHovered={hovered} + onShowContextMenu={handleShowContextMenu} contextMenuAnchor={popoverAnchorRef.current} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} isWhisper={isWhisper} @@ -561,6 +622,7 @@ function ReportActionItem({ chatReportID={report.reportID} action={action} isHovered={hovered} + onShowContextMenu={handleShowContextMenu} contextMenuAnchor={popoverAnchorRef.current} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} policyID={report.policyID ?? ''} @@ -707,6 +769,7 @@ function ReportActionItem({ {!ReportActionsUtils.isMessageDeleted(action) && ( Date: Fri, 15 Mar 2024 15:50:17 +0100 Subject: [PATCH 02/64] chore: fixed after code review --- .../ActionSheetAwareScrollViewContext.tsx | 2 ++ .../ActionSheetKeyboardSpace.tsx | 17 +++++++---------- .../EmojiPicker/EmojiPickerButton.tsx | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index a8dda4adb621..59cf826bfa5b 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -12,12 +12,14 @@ type MeasuredElements = { height?: number; composerHeight?: number; }; + type Context = { currentActionSheetState: SharedValue>; transitionActionSheetState: (action: ActionWithPayload) => void; transitionActionSheetStateWorklet: (action: ActionWithPayload) => void; resetStateMachine: () => void; }; + const defaultValue: Context = { currentActionSheetState: { value: { diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 095cb2556077..75ebf396e155 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -15,6 +15,7 @@ const KeyboardState = { CLOSING: 3, CLOSED: 4, }; + const useAnimatedKeyboard = () => { const state = useSharedValue(KeyboardState.UNKNOWN); const height = useSharedValue(0); @@ -61,6 +62,7 @@ const useAnimatedKeyboard = () => { return {state, height, heightWhenOpened, progress}; }; + const setInitialValueAndRunAnimation = (value: number, animation: number) => { 'worklet'; @@ -135,20 +137,15 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const invertedKeyboardHeight = keyboard.state.value === KeyboardState.CLOSED ? lastKeyboardHeight : 0; - let elementOffset = 0; - - if (fy !== undefined && height !== undefined && popoverHeight !== undefined) { - elementOffset = fy + safeArea.top + height - (windowHeight - popoverHeight); - } + const elementOffset = fy !== undefined && height !== undefined && popoverHeight !== undefined ? fy + safeArea.top + height - (windowHeight - popoverHeight) : 0; // when the sate is not idle we know for sure we have previous state const previousPayload = previous.payload ?? {}; - let previousElementOffset = 0; - - if (previousPayload.fy !== undefined && previousPayload.height !== undefined && previousPayload.popoverHeight !== undefined) { - previousElementOffset = previousPayload.fy + safeArea.top + previousPayload.height - (windowHeight - previousPayload.popoverHeight); - } + const previousElementOffset = + previousPayload.fy !== undefined && previousPayload.height !== undefined && previousPayload.popoverHeight !== undefined + ? previousPayload.fy + safeArea.top + previousPayload.height - (windowHeight - previousPayload.popoverHeight) + : 0; // Depending on the current and sometimes previous state we can return // either animation or just a value diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index d2e732728af9..40e3cdd45f71 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -38,7 +38,7 @@ function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shi const {translate} = useLocalize(); const isFocused = useIsFocused(); - const onPress = () => { + const openEmojiPicker = () => { if (!isFocused) { return; } @@ -83,7 +83,7 @@ function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shi ref={emojiPopoverAnchor} style={({hovered, pressed}) => [styles.chatItemEmojiButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed))]} disabled={isDisabled} - onPress={onPress} + onPress={openEmojiPicker} id={id} accessibilityLabel={translate('reportActionCompose.emoji')} > From 30d22dcb7ba4972cf1b0ab3181eaf16c8cfd479d Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 18 Mar 2024 13:32:52 +0100 Subject: [PATCH 03/64] fix: remove resetWorklet since it's not used right now --- src/hooks/useWorkletStateMachine.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/useWorkletStateMachine.ts b/src/hooks/useWorkletStateMachine.ts index 5b09dc22b059..f0c3fde0012d 100644 --- a/src/hooks/useWorkletStateMachine.ts +++ b/src/hooks/useWorkletStateMachine.ts @@ -162,7 +162,6 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta transitionWorklet, transition, reset, - resetWorklet, }; } From a6eebcce3f3b480fd53174453cc3f660ee9352ab Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 19 Mar 2024 16:31:00 +0100 Subject: [PATCH 04/64] fix: frozen KeyboardAvoidingView when it was mounted with an opened keyboard --- .../react-native-keyboard-controller+1.10.4.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 patches/react-native-keyboard-controller+1.10.4.patch diff --git a/patches/react-native-keyboard-controller+1.10.4.patch b/patches/react-native-keyboard-controller+1.10.4.patch new file mode 100644 index 000000000000..ab2d23fcd093 --- /dev/null +++ b/patches/react-native-keyboard-controller+1.10.4.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx b/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx +index 9e73caf..0d5f143 100644 +--- a/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx ++++ b/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx +@@ -81,7 +81,7 @@ const KeyboardAvoidingView = forwardRef>( + const onLayoutWorklet = useCallback((layout: LayoutRectangle) => { + "worklet"; + +- if (keyboard.isClosed.value) { ++ if (keyboard.isClosed.value || initialFrame.value === null) { + initialFrame.value = layout; + } + }, []); From 8b0b29754a7ec780b697954de20b5a2268c835f3 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 21 Mar 2024 12:42:05 +0100 Subject: [PATCH 05/64] chore: update keyboard-controller --- package-lock.json | 8 ++++---- package.json | 2 +- .../react-native-keyboard-controller+1.10.4.patch | 13 ------------- 3 files changed, 5 insertions(+), 18 deletions(-) delete mode 100644 patches/react-native-keyboard-controller+1.10.4.patch diff --git a/package-lock.json b/package-lock.json index d3bcfea84c51..d42a93b9496a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,7 +98,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.10.4", + "react-native-keyboard-controller": "^1.11.4", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -31421,9 +31421,9 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.10.4.tgz", - "integrity": "sha512-PQ3AcKGnacDBeA1zB1y44XLgj0sZd3Py5Kpml412bKgYiM09JgoK7YbJcUxMayTeEGtZ8GTOteevGTbGq1Otrg==", + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.11.4.tgz", + "integrity": "sha512-Gn/3d7dut8IyCZikvywKivwrYFfpj3YFaT8YcxtTL46jeoG61qFIjzEV2OPdRy476c6Mea3rByhrdiR30XCfVg==", "peerDependencies": { "react": "*", "react-native": "*", diff --git a/package.json b/package.json index efadc759b85f..72ca02f36d82 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.10.4", + "react-native-keyboard-controller": "^1.11.4", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", diff --git a/patches/react-native-keyboard-controller+1.10.4.patch b/patches/react-native-keyboard-controller+1.10.4.patch deleted file mode 100644 index ab2d23fcd093..000000000000 --- a/patches/react-native-keyboard-controller+1.10.4.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx b/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx -index 9e73caf..0d5f143 100644 ---- a/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx -+++ b/node_modules/react-native-keyboard-controller/src/components/KeyboardAvoidingView/index.tsx -@@ -81,7 +81,7 @@ const KeyboardAvoidingView = forwardRef>( - const onLayoutWorklet = useCallback((layout: LayoutRectangle) => { - "worklet"; - -- if (keyboard.isClosed.value) { -+ if (keyboard.isClosed.value || initialFrame.value === null) { - initialFrame.value = layout; - } - }, []); From 7de98477e75ee0c634ef46fbc8067aa00e1d5ef0 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 21 Mar 2024 13:24:11 +0100 Subject: [PATCH 06/64] fix: composer under keyboard when you press Edit message --- .../ActionSheetAwareScrollViewContext.tsx | 5 +++++ .../ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index 59cf826bfa5b..f592146659f4 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -90,6 +90,7 @@ const States = { ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED: 'attachmentsPopoverWithKeyboardClosed', MODAL_DELETED: 'modalDeleted', MODAL_WITH_KEYBOARD_OPEN_DELETED: 'modalWithKeyboardOpenDeleted', + EDIT_MESSAGE: 'editMessage', }; const STATE_MACHINE = { @@ -141,6 +142,7 @@ const STATE_MACHINE = { [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.KEYBOARD_POPOVER_OPEN, [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN, [Actions.SHOW_DELETE_CONFIRM_MODAL]: States.MODAL_WITH_KEYBOARD_OPEN_DELETED, + [Actions.EDIT_REPORT]: States.EDIT_MESSAGE, }, [States.MODAL_WITH_KEYBOARD_OPEN_DELETED]: { [Actions.MEASURE_CONFIRM_MODAL]: States.MODAL_WITH_KEYBOARD_OPEN_DELETED, @@ -177,6 +179,9 @@ const STATE_MACHINE = { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, [Actions.END_TRANSITION]: States.KEYBOARD_OPEN, }, + [States.EDIT_MESSAGE]: { + [Actions.CLOSE_KEYBOARD]: States.IDLE, + }, }; function ActionSheetAwareScrollViewProvider(props: PropsWithChildren) { diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 75ebf396e155..cfef99c87d08 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -274,6 +274,9 @@ function ActionSheetKeyboardSpace(props: ViewProps) { duration: 0, }); } + case States.EDIT_MESSAGE: { + return 0; + } default: return 0; From d7052062d1cd539a5530d8143c7110c6a9638629 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Wed, 27 Mar 2024 13:17:54 +0100 Subject: [PATCH 07/64] chore: use latest RNKC version --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index d42a93b9496a..d671b07f21e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,7 +98,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.11.4", + "react-native-keyboard-controller": "^1.11.5", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -31421,9 +31421,9 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.11.4.tgz", - "integrity": "sha512-Gn/3d7dut8IyCZikvywKivwrYFfpj3YFaT8YcxtTL46jeoG61qFIjzEV2OPdRy476c6Mea3rByhrdiR30XCfVg==", + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.11.5.tgz", + "integrity": "sha512-AtlJVz+Sm9uh5Y+k0AhqJDVnbpYkieOE+qHwO8046Fyt9blxK5pGprOQLNy9hcJjfxwhiwiSFk9VyZlZfaYe6Q==", "peerDependencies": { "react": "*", "react-native": "*", diff --git a/package.json b/package.json index 72ca02f36d82..37c2a0af4e35 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.11.4", + "react-native-keyboard-controller": "^1.11.5", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", From f0ed018c225f4fc1222f60849930a1e66a1e0a7a Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 14 May 2024 13:26:32 +0200 Subject: [PATCH 08/64] post rebase changes --- ios/Podfile.lock | 20 +++++++++++++++++++ package-lock.json | 8 ++++---- package.json | 2 +- .../EmojiPickerMenu/index.native.tsx | 3 ++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1cac95e15036..23558b1a9b7a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1303,6 +1303,25 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-keyboard-controller (1.12.0): + - glog + - hermes-engine + - RCT-Folly (= 2022.05.16.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - react-native-launch-arguments (4.0.2): - React - react-native-netinfo (11.2.1): @@ -2544,6 +2563,7 @@ SPEC CHECKSUMS: react-native-geolocation: f9e92eb774cb30ac1e099f34b3a94f03b4db7eb3 react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 react-native-key-command: 28ccfa09520e7d7e30739480dea4df003493bfe8 + react-native-keyboard-controller: 967a185a802fbc07a8ff4562d246158908bdf590 react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: 02d31de0e08ab043d48f2a1a8baade109d7b6ca5 react-native-pager-view: ccd4bbf9fc7effaf8f91f8dae43389844d9ef9fa diff --git a/package-lock.json b/package-lock.json index d671b07f21e7..434fc0752fca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,7 +98,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.11.5", + "react-native-keyboard-controller": "^1.12.0", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -31421,9 +31421,9 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.11.5.tgz", - "integrity": "sha512-AtlJVz+Sm9uh5Y+k0AhqJDVnbpYkieOE+qHwO8046Fyt9blxK5pGprOQLNy9hcJjfxwhiwiSFk9VyZlZfaYe6Q==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.0.tgz", + "integrity": "sha512-am/1qIClurdUEgCD43E5/YJEO62XrBJLnvq3dFJt6KJAMN9/f3Ffz04tbQ5xEY5k91uurJX4Ev8bEXXJfaIvaA==", "peerDependencies": { "react": "*", "react-native": "*", diff --git a/package.json b/package.json index 37c2a0af4e35..3dd91101f02f 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.11.5", + "react-native-keyboard-controller": "^1.12.0", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx index 01826cd07163..41fac94e9e7d 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx @@ -2,6 +2,7 @@ import type {ListRenderItem} from '@shopify/flash-list'; import lodashDebounce from 'lodash/debounce'; import React, {useCallback, useContext} from 'react'; import type {ForwardedRef} from 'react'; +import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import {runOnUI, scrollTo} from 'react-native-reanimated'; import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; @@ -117,7 +118,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r ); const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const onLayout = useCallback( - (event) => { + (event: LayoutChangeEvent) => { const {height} = event.nativeEvent.layout; actionSheetAwareScrollViewContext.transitionActionSheetState({ type: ActionSheetAwareScrollView.Actions.MEASURE_EMOJI_PICKER_POPOVER, From 5fe5b3466675f051d66cd509234b2f4199677dee Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 16 May 2024 11:36:55 +0200 Subject: [PATCH 09/64] feat: update keyboard-controller to avoid crashes --- ios/Podfile.lock | 4 ++-- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 23558b1a9b7a..97397b5e213a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1303,7 +1303,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-keyboard-controller (1.12.0): + - react-native-keyboard-controller (1.12.1): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -2563,7 +2563,7 @@ SPEC CHECKSUMS: react-native-geolocation: f9e92eb774cb30ac1e099f34b3a94f03b4db7eb3 react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 react-native-key-command: 28ccfa09520e7d7e30739480dea4df003493bfe8 - react-native-keyboard-controller: 967a185a802fbc07a8ff4562d246158908bdf590 + react-native-keyboard-controller: 36bc3176750b519d746aa5f328fdac7ec2e82bb2 react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: 02d31de0e08ab043d48f2a1a8baade109d7b6ca5 react-native-pager-view: ccd4bbf9fc7effaf8f91f8dae43389844d9ef9fa diff --git a/package-lock.json b/package-lock.json index 434fc0752fca..562a433c14f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,7 +98,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.12.0", + "react-native-keyboard-controller": "^1.12.1", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -31421,9 +31421,9 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.0.tgz", - "integrity": "sha512-am/1qIClurdUEgCD43E5/YJEO62XrBJLnvq3dFJt6KJAMN9/f3Ffz04tbQ5xEY5k91uurJX4Ev8bEXXJfaIvaA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.1.tgz", + "integrity": "sha512-2OpQcesiYsMilrTzgcTafSGexd9UryRQRuHudIcOn0YaqvvzNpnhVZMVuJMH93fJv/iaZYp3138rgUKOdHhtSw==", "peerDependencies": { "react": "*", "react-native": "*", diff --git a/package.json b/package.json index 3dd91101f02f..78c2f5ec4415 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.12.0", + "react-native-keyboard-controller": "^1.12.1", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", From acfbdcb0f8bb35655522bbcc858e7411cbf7063b Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 4 Jun 2024 17:25:03 +0200 Subject: [PATCH 10/64] chore: apply prettier --- .../HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx | 4 +++- src/pages/home/report/ReportActionItem.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index 902489731d94..d8b13f08909b 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -93,7 +93,9 @@ function ImageRenderer({tnode}: ImageRendererProps) { Navigation.navigate(route); } }} - onLongPress={(event) => onShowContextMenu(() => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report)))} + onLongPress={(event) => + onShowContextMenu(() => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))) + } shouldUseHapticsOnLongPress accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} accessibilityLabel={translate('accessibilityHints.viewAttachment')} diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 58d0e606c8a9..196dd2a825fb 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -5,8 +5,8 @@ import {InteractionManager, View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {Emoji} from '@assets/emojis/types'; -import {AttachmentContext} from '@components/AttachmentContext'; import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; +import {AttachmentContext} from '@components/AttachmentContext'; import Button from '@components/Button'; import DisplayNames from '@components/DisplayNames'; import Hoverable from '@components/Hoverable'; From 41044a199b8734617e0d8fe5af9a10ce02cd1fe9 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 20 Jun 2024 15:04:42 +0300 Subject: [PATCH 11/64] chore: keep important changes from experiments --- ...ated+3.8.1+003+concurrent-rn-updates.patch | 79 +++++++++++++++++++ src/hooks/useWorkletStateMachine.ts | 5 +- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch diff --git a/patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch b/patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch new file mode 100644 index 000000000000..02a298f4c5a6 --- /dev/null +++ b/patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch @@ -0,0 +1,79 @@ +diff --git a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp +index b34579d..87513aa 100644 +--- a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp ++++ b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp +@@ -418,6 +418,24 @@ bool NativeReanimatedModule::isThereAnyLayoutProp( + return false; + } + ++jsi::Object NativeReanimatedModule::getUIProps( ++ jsi::Runtime &rt, ++ const jsi::Object &props) { ++ jsi::Object res = jsi::Object(rt); ++ const jsi::Array propNames = props.getPropertyNames(rt); ++ for (size_t i = 0; i < propNames.size(rt); ++i) { ++ const std::string propName = ++ propNames.getValueAtIndex(rt, i).asString(rt).utf8(rt); ++ bool isLayoutProp = ++ nativePropNames_.find(propName) != nativePropNames_.end(); ++ if (!isLayoutProp) { ++ const jsi::Value &propValue = props.getProperty(rt, propName.c_str()); ++ res.setProperty(rt, propName.c_str(), propValue); ++ } ++ } ++ return res; ++} ++ + jsi::Value NativeReanimatedModule::filterNonAnimatableProps( + jsi::Runtime &rt, + const jsi::Value &props) { +@@ -565,13 +583,15 @@ void NativeReanimatedModule::performOperations() { + } + } + ++ // If there's no layout props to be updated, we can apply the updates ++ // directly onto the components and skip the commit. ++ for (const auto &[shadowNode, props] : copiedOperationsQueue) { ++ Tag tag = shadowNode->getTag(); ++ jsi::Object uiProps = getUIProps(rt, props->asObject(rt)); ++ synchronouslyUpdateUIPropsFunction_(rt, tag, uiProps); ++ } ++ + if (!hasLayoutUpdates) { +- // If there's no layout props to be updated, we can apply the updates +- // directly onto the components and skip the commit. +- for (const auto &[shadowNode, props] : copiedOperationsQueue) { +- Tag tag = shadowNode->getTag(); +- synchronouslyUpdateUIPropsFunction_(rt, tag, props->asObject(rt)); +- } + return; + } + +diff --git a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h +index 9f8c32d..cb31205 100644 +--- a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h ++++ b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h +@@ -163,6 +163,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { + + #ifdef RCT_NEW_ARCH_ENABLED + bool isThereAnyLayoutProp(jsi::Runtime &rt, const jsi::Object &props); ++ jsi::Object getUIProps(jsi::Runtime &rt, const jsi::Object &props); + jsi::Value filterNonAnimatableProps( + jsi::Runtime &rt, + const jsi::Value &props); +diff --git a/node_modules/react-native-reanimated/apple/REANodesManager.mm b/node_modules/react-native-reanimated/apple/REANodesManager.mm +index ed36c99..0c64925 100644 +--- a/node_modules/react-native-reanimated/apple/REANodesManager.mm ++++ b/node_modules/react-native-reanimated/apple/REANodesManager.mm +@@ -432,9 +432,9 @@ - (void)synchronouslyUpdateViewOnUIThread:(nonnull NSNumber *)viewTag props:(non + REAUIView *componentView = + [componentViewRegistry findComponentViewWithTag:[viewTag integerValue]]; + +- NSSet *propKeysManagedByAnimated = [componentView propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN]; ++ // NSSet *propKeysManagedByAnimated = [componentView propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN]; + [surfacePresenter synchronouslyUpdateViewOnUIThread:viewTag props:uiProps]; +- [componentView setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:propKeysManagedByAnimated]; ++ // [componentView setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:propKeysManagedByAnimated]; + + // `synchronouslyUpdateViewOnUIThread` does not flush props like `backgroundColor` etc. + // so that's why we need to call `finalizeUpdates` here. diff --git a/src/hooks/useWorkletStateMachine.ts b/src/hooks/useWorkletStateMachine.ts index f0c3fde0012d..5bc81a0629b1 100644 --- a/src/hooks/useWorkletStateMachine.ts +++ b/src/hooks/useWorkletStateMachine.ts @@ -21,6 +21,9 @@ type State

= { 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. @@ -83,7 +86,7 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta } // eslint-disable-next-line @typescript-eslint/unbound-method, @typescript-eslint/restrict-template-expressions - runOnJS(Log.client)(`[StateMachine] ${message}. Params: ${params}`); + runOnJS(client)(`[StateMachine] ${message}. Params: ${JSON.stringify(params)}`); }, []); const transitionWorklet = useCallback( From 843bf2580a2c1102b27fab83f0711909a9c90d55 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 20 Jun 2024 15:56:10 +0300 Subject: [PATCH 12/64] fix: don't mount two providers --- src/App.tsx | 2 -- src/components/KeyboardHandlerProvider.tsx | 12 ------------ 2 files changed, 14 deletions(-) delete mode 100644 src/components/KeyboardHandlerProvider.tsx diff --git a/src/App.tsx b/src/App.tsx index 4adadbce7cc9..56c3b167ddac 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,7 +16,6 @@ import CustomStatusBarAndBackgroundContextProvider from './components/CustomStat import ErrorBoundary from './components/ErrorBoundary'; import HTMLEngineProvider from './components/HTMLEngineProvider'; import InitialURLContextProvider from './components/InitialURLContextProvider'; -import KeyboardHandlerProvider from './components/KeyboardHandlerProvider'; import {LocaleContextProvider} from './components/LocaleContextProvider'; import OnyxProvider from './components/OnyxProvider'; import PopoverContextProvider from './components/PopoverProvider'; @@ -82,7 +81,6 @@ function App({url}: AppProps) { CustomStatusBarAndBackgroundContextProvider, ActiveElementRoleProvider, ActiveWorkspaceContextProvider, - KeyboardHandlerProvider, ActionSheetAwareScrollView.ActionSheetAwareScrollViewProvider, ReportIDsContextProvider, PlaybackContextProvider, diff --git a/src/components/KeyboardHandlerProvider.tsx b/src/components/KeyboardHandlerProvider.tsx deleted file mode 100644 index dc208d10aeb3..000000000000 --- a/src/components/KeyboardHandlerProvider.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type {PropsWithChildren} from 'react'; -import React from 'react'; -import {Platform} from 'react-native'; -import {KeyboardProvider} from 'react-native-keyboard-controller'; - -type Props = PropsWithChildren; - -function KeyboardHandlerProvider({children}: Props) { - return {children}; -} - -export default KeyboardHandlerProvider; From 7d2df0a0b8a89bf5371487edc22beb62f2e23b3b Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 20 Jun 2024 18:05:17 +0300 Subject: [PATCH 13/64] fix: + button transitions --- .../ActionSheetKeyboardSpace.tsx | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index cfef99c87d08..a7154ac9d19d 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -155,10 +155,12 @@ function ActionSheetKeyboardSpace(props: ViewProps) { return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0); } + console.log(111, 0); return withSpring(0, config); } case States.POPOVER_CLOSED: { + console.log(112, 0); return withSpring(0, config, () => { transition({ type: Actions.END_TRANSITION, @@ -171,12 +173,15 @@ function ActionSheetKeyboardSpace(props: ViewProps) { case States.POPOVER_OPEN: { if (popoverHeight) { if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { + console.log(113, elementOffset < 0 ? 0 : elementOffset, elementOffset); return withSpring(elementOffset < 0 ? 0 : elementOffset, config); } + console.log(114, Math.max(previousElementOffset, 0), previousElementOffset); return withSpring(Math.max(previousElementOffset, 0), config); } + console.log(115, 0); return 0; } @@ -185,6 +190,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { // when item is higher than keyboard and bottom sheet // we should just stay in place if (elementOffset < 0) { + console.log(116, invertedKeyboardHeight); return invertedKeyboardHeight; } @@ -193,65 +199,93 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const previousOffset = invertedKeyboardHeight + previousElementOffset; if (previousElementOffset === 0 || nextOffset > previousOffset) { + console.log(117, nextOffset); return withSpring(nextOffset, config); } + console.log(118, previousOffset); return previousOffset; } - + console.log(119, nextOffset); return nextOffset; } case States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED: case States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN: { + // this transition is extremely slow and we may not have `popoverHeight` when keyboard is hiding + // so we run two fold animation: + // - when keyboard is hiding -> we return `0` and thus the content is sticky to composer + // - when keyboard is closed and we have `popoverHeight` (i. e. popup was measured) -> we run spring animation + if (keyboard.state.value === KeyboardState.CLOSING) { + console.log(1200, 0); + return 0; + } + if (keyboard.progress.value === 0) { + console.log(1201, keyboard.progress.value, interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]), popoverHeight, composerHeight); + return withSpring(popoverHeight - composerHeight, config); + } + + // when keyboard appears -> we already have all values so we do interpolation based on keyboard position + console.log(1202, keyboard.progress.value, interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]), popoverHeight, composerHeight); return interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]); } case States.CALL_POPOVER_WITH_KEYBOARD_OPEN: { if (keyboard.height.value > 0) { + console.log(121, 0); return 0; } - + console.log(122, lastKeyboardHeight, popoverHeight - composerHeight); return setInitialValueAndRunAnimation(lastKeyboardHeight, withSpring(popoverHeight - composerHeight, config)); } case States.CALL_POPOVER_WITH_KEYBOARD_CLOSED: { // keyboard is opened if (keyboard.height.value > 0) { + console.log(123, 0); return 0; } + console.log(124, lastKeyboardHeight); return withSpring(lastKeyboardHeight, config); } case States.EMOJI_PICKER_WITH_KEYBOARD_OPEN: { if (keyboard.state.value === KeyboardState.CLOSED) { + console.log(125, popoverHeight - composerHeight); return popoverHeight - composerHeight; } + console.log(126, 0); return 0; } case States.KEYBOARD_POPOVER_CLOSED: { if (keyboard.heightWhenOpened.value === keyboard.height.value) { + console.log(127, 0); return 0; } + console.log(128, popoverHeight - composerHeight); return popoverHeight - composerHeight; } case States.KEYBOARD_POPOVER_OPEN: { if (keyboard.state.value === KeyboardState.OPEN) { + console.log(129, 0); return 0; } const nextOffset = elementOffset + lastKeyboardHeight; if (keyboard.state.value === KeyboardState.CLOSED && nextOffset > invertedKeyboardHeight) { + console.log(130, nextOffset < 0 ? 0 : nextOffset, nextOffset); return withSpring(nextOffset < 0 ? 0 : nextOffset, config); } if (elementOffset < 0) { + console.log(131, lastKeyboardHeight - keyboardHeight); return lastKeyboardHeight - keyboardHeight; } + console.log(132, lastKeyboardHeight); return lastKeyboardHeight; } @@ -259,26 +293,32 @@ function ActionSheetKeyboardSpace(props: ViewProps) { if (elementOffset < 0) { transition({type: Actions.END_TRANSITION}); + console.log(133, 0); return 0; } if (keyboard.state.value === KeyboardState.CLOSED) { + console.log(134, elementOffset + lastKeyboardHeight); return elementOffset + lastKeyboardHeight; } if (keyboard.height.value > 0) { + console.log(135, keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset); return keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset; } + console.log(136, elementOffset + lastKeyboardHeight); return withTiming(elementOffset + lastKeyboardHeight, { duration: 0, }); } case States.EDIT_MESSAGE: { + console.log(137, 0); return 0; } default: + console.log(138, 0); return 0; } }, []); From 5d3c6e7aa0d4cfbc1fda0e53b273597a4df4a786 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 19 Jul 2024 17:52:11 +0200 Subject: [PATCH 14/64] use executeOnUIRuntimeSync --- src/hooks/useWorkletStateMachine.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/useWorkletStateMachine.ts b/src/hooks/useWorkletStateMachine.ts index 5bc81a0629b1..d54a586b1a42 100644 --- a/src/hooks/useWorkletStateMachine.ts +++ b/src/hooks/useWorkletStateMachine.ts @@ -1,5 +1,5 @@ import {useCallback} from 'react'; -import {runOnJS, runOnUI, useSharedValue} from 'react-native-reanimated'; +import {executeOnUIRuntimeSync, runOnJS, useSharedValue} from 'react-native-reanimated'; import Log from '@libs/Log'; // When you need to debug state machine change this to true @@ -150,12 +150,12 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta }, [currentState, initialState, log]); const reset = useCallback(() => { - runOnUI(resetWorklet)(); + executeOnUIRuntimeSync(resetWorklet)(); }, [resetWorklet]); const transition = useCallback( (action: ActionWithPayload

) => { - runOnUI(transitionWorklet)(action); + executeOnUIRuntimeSync(transitionWorklet)(action); }, [transitionWorklet], ); From 67d5dcab3aa251ae6d2062170335df4a22ecfa95 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 25 Jul 2024 16:03:59 +0200 Subject: [PATCH 15/64] reset worklet on UI thread --- src/hooks/useWorkletStateMachine.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useWorkletStateMachine.ts b/src/hooks/useWorkletStateMachine.ts index d54a586b1a42..b9c02faa7bd1 100644 --- a/src/hooks/useWorkletStateMachine.ts +++ b/src/hooks/useWorkletStateMachine.ts @@ -1,5 +1,5 @@ import {useCallback} from 'react'; -import {executeOnUIRuntimeSync, runOnJS, useSharedValue} from 'react-native-reanimated'; +import {executeOnUIRuntimeSync, runOnJS, runOnUI, useSharedValue} from 'react-native-reanimated'; import Log from '@libs/Log'; // When you need to debug state machine change this to true @@ -150,7 +150,7 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta }, [currentState, initialState, log]); const reset = useCallback(() => { - executeOnUIRuntimeSync(resetWorklet)(); + runOnUI(resetWorklet)(); }, [resetWorklet]); const transition = useCallback( From cbeec879a57b73893134b37a47069c0cec2aa37c Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 26 Jul 2024 12:56:12 +0200 Subject: [PATCH 16/64] fix jumping while KEYBOARD_OPEN --- .../ActionSheetKeyboardSpace.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index a7154ac9d19d..afd6bcf7fdea 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -97,10 +97,9 @@ function ActionSheetKeyboardSpace(props: ViewProps) { // Reset state machine when component unmounts useEffect(() => () => resetStateMachine(), [resetStateMachine]); - useAnimatedReaction( () => keyboard.state.value, - (lastState) => { + (lastState, prev) => { if (lastState === syncLocalWorkletState.lastState) { return; } @@ -149,9 +148,10 @@ function ActionSheetKeyboardSpace(props: ViewProps) { // 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 (previous.state === States.KEYBOARD_CLOSED_POPOVER) { + if (previous.state === States.KEYBOARD_CLOSED_POPOVER || (previous.state === States.KEYBOARD_OPEN && elementOffset < 0)) { return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0); } @@ -285,7 +285,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { return lastKeyboardHeight - keyboardHeight; } - console.log(132, lastKeyboardHeight); + console.log(132, lastKeyboardHeight, lastKeyboardHeight - keyboardHeight, keyboardHeight); return lastKeyboardHeight; } @@ -303,7 +303,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } if (keyboard.height.value > 0) { - console.log(135, keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset); + console.log(135, keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset, 'elementOffset', elementOffset); return keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset; } From 812e9ca431df6983111ec8c8846327bce2d07fd1 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 26 Jul 2024 12:56:43 +0200 Subject: [PATCH 17/64] update controller --- ...ct-native-keyboard-controller+1.12.2.patch | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 patches/react-native-keyboard-controller+1.12.2.patch diff --git a/patches/react-native-keyboard-controller+1.12.2.patch b/patches/react-native-keyboard-controller+1.12.2.patch deleted file mode 100644 index 3c8034354481..000000000000 --- a/patches/react-native-keyboard-controller+1.12.2.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt -index 83884d8..5d9e989 100644 ---- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt -+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt -@@ -99,12 +99,12 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R - } - - private fun goToEdgeToEdge(edgeToEdge: Boolean) { -- reactContext.currentActivity?.let { -- WindowCompat.setDecorFitsSystemWindows( -- it.window, -- !edgeToEdge, -- ) -- } -+ // reactContext.currentActivity?.let { -+ // WindowCompat.setDecorFitsSystemWindows( -+ // it.window, -+ // !edgeToEdge, -+ // ) -+ // } - } - - private fun setupKeyboardCallbacks() { -@@ -158,13 +158,13 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R - // region State managers - private fun enable() { - this.goToEdgeToEdge(true) -- this.setupWindowInsets() -+ // this.setupWindowInsets() - this.setupKeyboardCallbacks() - } - - private fun disable() { - this.goToEdgeToEdge(false) -- this.setupWindowInsets() -+ // this.setupWindowInsets() - this.removeKeyboardCallbacks() - } - // endregion \ No newline at end of file From 05677c9408bdd663e0a1c7ffb2446c6e393e50af Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 26 Jul 2024 17:20:31 +0200 Subject: [PATCH 18/64] update SafeAreaPaddings --- .../ActionSheetKeyboardSpace.tsx | 69 +++++++------------ 1 file changed, 26 insertions(+), 43 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index afd6bcf7fdea..c2214dea813b 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -16,6 +16,12 @@ const KeyboardState = { CLOSED: 4, }; +const SPRING_CONFIG = { + mass: 3, + stiffness: 1000, + damping: 500, +}; + const useAnimatedKeyboard = () => { const state = useSharedValue(KeyboardState.UNKNOWN); const height = useSharedValue(0); @@ -31,12 +37,7 @@ const useAnimatedKeyboard = () => { if (e.height === 0) { heightWhenOpened.value = height.value; } - - if (e.height > 0) { - state.value = KeyboardState.OPENING; - } else { - state.value = KeyboardState.CLOSING; - } + state.value = e.height > 0 ? KeyboardState.OPENING : KeyboardState.CLOSING; }, onMove: (e) => { 'worklet'; @@ -46,13 +47,7 @@ const useAnimatedKeyboard = () => { }, onEnd: (e) => { 'worklet'; - - if (e.height > 0) { - state.value = KeyboardState.OPEN; - } else { - state.value = KeyboardState.CLOSED; - } - + state.value = e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED; height.value = e.height; progress.value = e.progress; }, @@ -77,12 +72,6 @@ const useSafeAreaPaddings = () => { return {top: paddingTop, bottom: paddingBottom}; }; -const config = { - mass: 3, - stiffness: 1000, - damping: 500, -}; - function ActionSheetKeyboardSpace(props: ViewProps) { const styles = useThemeStyles(); const safeArea = useSafeAreaPaddings(); @@ -97,23 +86,22 @@ function ActionSheetKeyboardSpace(props: ViewProps) { // Reset state machine when component unmounts useEffect(() => () => resetStateMachine(), [resetStateMachine]); + // eslint-disable-next-line arrow-body-style + // useEffect(() => { + // return () => resetStateMachine(); + // }, [resetStateMachine]); + useAnimatedReaction( () => keyboard.state.value, - (lastState, prev) => { + (lastState) => { if (lastState === syncLocalWorkletState.lastState) { return; } - syncLocalWorkletState.lastState = lastState; - if (lastState === KeyboardState.OPEN) { - runOnJS(transitionActionSheetState)({ - type: Actions.OPEN_KEYBOARD, - }); + runOnJS(transitionActionSheetState)({type: Actions.OPEN_KEYBOARD}); } else if (lastState === KeyboardState.CLOSED) { - runOnJS(transitionActionSheetState)({ - type: Actions.CLOSE_KEYBOARD, - }); + runOnJS(transitionActionSheetState)({type: Actions.CLOSE_KEYBOARD}); } }, [], @@ -125,22 +113,17 @@ function ActionSheetKeyboardSpace(props: ViewProps) { // 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, config); + return withSpring(0, SPRING_CONFIG); } const keyboardHeight = keyboard.height.value === 0 ? 0 : keyboard.height.value - safeArea.bottom; // sometimes we need to know the last keyboard height const lastKeyboardHeight = keyboard.heightWhenOpened.value - safeArea.bottom; - const {popoverHeight = 0, fy, height, composerHeight = 0} = current.payload ?? {}; - const invertedKeyboardHeight = keyboard.state.value === KeyboardState.CLOSED ? lastKeyboardHeight : 0; - const elementOffset = fy !== undefined && height !== undefined && popoverHeight !== undefined ? fy + safeArea.top + height - (windowHeight - popoverHeight) : 0; - // when the sate is not idle we know for sure we have 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) @@ -156,12 +139,12 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } console.log(111, 0); - return withSpring(0, config); + return withSpring(0, SPRING_CONFIG); } case States.POPOVER_CLOSED: { console.log(112, 0); - return withSpring(0, config, () => { + return withSpring(0, SPRING_CONFIG, () => { transition({ type: Actions.END_TRANSITION, }); @@ -174,11 +157,11 @@ function ActionSheetKeyboardSpace(props: ViewProps) { if (popoverHeight) { if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { console.log(113, elementOffset < 0 ? 0 : elementOffset, elementOffset); - return withSpring(elementOffset < 0 ? 0 : elementOffset, config); + return withSpring(elementOffset < 0 ? 0 : elementOffset, SPRING_CONFIG); } console.log(114, Math.max(previousElementOffset, 0), previousElementOffset); - return withSpring(Math.max(previousElementOffset, 0), config); + return withSpring(Math.max(previousElementOffset, 0), SPRING_CONFIG); } console.log(115, 0); @@ -200,7 +183,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { if (previousElementOffset === 0 || nextOffset > previousOffset) { console.log(117, nextOffset); - return withSpring(nextOffset, config); + return withSpring(nextOffset, SPRING_CONFIG); } console.log(118, previousOffset); @@ -222,7 +205,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } if (keyboard.progress.value === 0) { console.log(1201, keyboard.progress.value, interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]), popoverHeight, composerHeight); - return withSpring(popoverHeight - composerHeight, config); + return withSpring(popoverHeight - composerHeight, SPRING_CONFIG); } // when keyboard appears -> we already have all values so we do interpolation based on keyboard position @@ -235,7 +218,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { return 0; } console.log(122, lastKeyboardHeight, popoverHeight - composerHeight); - return setInitialValueAndRunAnimation(lastKeyboardHeight, withSpring(popoverHeight - composerHeight, config)); + return setInitialValueAndRunAnimation(lastKeyboardHeight, withSpring(popoverHeight - composerHeight, SPRING_CONFIG)); } case States.CALL_POPOVER_WITH_KEYBOARD_CLOSED: { // keyboard is opened @@ -245,7 +228,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } console.log(124, lastKeyboardHeight); - return withSpring(lastKeyboardHeight, config); + return withSpring(lastKeyboardHeight, SPRING_CONFIG); } case States.EMOJI_PICKER_WITH_KEYBOARD_OPEN: { if (keyboard.state.value === KeyboardState.CLOSED) { @@ -277,7 +260,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { if (keyboard.state.value === KeyboardState.CLOSED && nextOffset > invertedKeyboardHeight) { console.log(130, nextOffset < 0 ? 0 : nextOffset, nextOffset); - return withSpring(nextOffset < 0 ? 0 : nextOffset, config); + return withSpring(nextOffset < 0 ? 0 : nextOffset, SPRING_CONFIG); } if (elementOffset < 0) { From 31a74624418532d667e4eb7d35b55cd33f1fed40 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 26 Jul 2024 17:20:57 +0200 Subject: [PATCH 19/64] bump keyboard-controller --- ios/Podfile.lock | 4 ++-- package-lock.json | 10 +++++----- package.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e7854c886c75..5729dbbd2906 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1303,7 +1303,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-keyboard-controller (1.12.2): + - react-native-keyboard-controller (1.12.6): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -2585,7 +2585,7 @@ SPEC CHECKSUMS: react-native-geolocation: 580c86eb531c0aaf7a14bc76fd2983ce47ca58aa react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 react-native-key-command: 28ccfa09520e7d7e30739480dea4df003493bfe8 - react-native-keyboard-controller: 47c01b0741ae5fc84e53cf282e61cfa5c2edb19b + react-native-keyboard-controller: 87bd777183a9e55c455670c3abbb9974c7c7f77f react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: 02d31de0e08ab043d48f2a1a8baade109d7b6ca5 react-native-pager-view: c7372cab7caef173f7f81d78520fe21f08805020 diff --git a/package-lock.json b/package-lock.json index 2ced5738cca8..887809d84a77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,7 +97,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.12.2", + "react-native-keyboard-controller": "^1.12.6", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -36893,13 +36893,13 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.2.tgz", - "integrity": "sha512-10Sy0+neSHGJxOmOxrUJR8TQznnrQ+jTFQtM1PP6YnblNQeAw1eOa+lO6YLGenRr5WuNSMZbks/3Ay0e2yMKLw==", + "version": "1.12.6", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.6.tgz", + "integrity": "sha512-TPqgEelsPOjdmsKmV3iGi1+eG3hzcCXVzJo4F54JeDoCOtk/QP5Qwu586RRgAganogs4XULFRRFXLx8u73GoYw==", "peerDependencies": { "react": "*", "react-native": "*", - "react-native-reanimated": ">=2.3.0" + "react-native-reanimated": ">=2.11.0" } }, "node_modules/react-native-launch-arguments": { diff --git a/package.json b/package.json index 81e963e12671..8b667c2c3517 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.12.2", + "react-native-keyboard-controller": "^1.12.6", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", From 6b205ccc38710a3594fed8a24f8e5679139840a9 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 26 Jul 2024 17:23:50 +0200 Subject: [PATCH 20/64] lint --- .../BaseAnchorForAttachmentsOnly.tsx | 9 ++++---- src/components/ConfirmContent.tsx | 5 +++- .../HTMLRenderers/ImageRenderer.tsx | 23 +++++++++---------- .../HTMLRenderers/MentionUserRenderer.tsx | 9 ++++---- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx index 15fae49f072d..a003181883b2 100644 --- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx +++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx @@ -53,11 +53,10 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', dow }} onPressIn={onPressIn} onPressOut={onPressOut} - onLongPress={ - (event) => - onShowContextMenu(() => - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)), - ) + onLongPress={(event) => + onShowContextMenu(() => + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)), + ) } shouldUseHapticsOnLongPress accessibilityLabel={displayName} diff --git a/src/components/ConfirmContent.tsx b/src/components/ConfirmContent.tsx index 721666ee240c..b3bbf891bf06 100644 --- a/src/components/ConfirmContent.tsx +++ b/src/components/ConfirmContent.tsx @@ -162,7 +162,10 @@ function ConfirmContent({ )} - + {shouldShowDismissIcon && ( diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx index 2d2282cc869a..f20ae7ac46e8 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.tsx @@ -93,18 +93,17 @@ function ImageRenderer({tnode}: ImageRendererProps) { Navigation.navigate(route); } }} - onLongPress={ - (event) => - onShowContextMenu(() => - showContextMenuForReport( - event, - anchor, - report?.reportID ?? '-1', - action, - checkIfContextMenuActive, - ReportUtils.isArchivedRoom(report, reportNameValuePairs), - ), - ) + onLongPress={(event) => + onShowContextMenu(() => + showContextMenuForReport( + event, + anchor, + report?.reportID ?? '-1', + action, + checkIfContextMenuActive, + ReportUtils.isArchivedRoom(report, reportNameValuePairs), + ), + ) } shouldUseHapticsOnLongPress accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index e005212a0755..34a5b5e19298 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -86,11 +86,10 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona {({onShowContextMenu, anchor, report, reportNameValuePairs, action, checkIfContextMenuActive}) => ( - onShowContextMenu(() => - showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)), - ) + onLongPress={(event) => + onShowContextMenu(() => + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)), + ) } onPress={(event) => { event.preventDefault(); From 8a27af57067bc25b9aa79eb25e271333f6d34a43 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 7 Aug 2024 14:03:20 +0200 Subject: [PATCH 21/64] add more states and actions --- .../ActionSheetAwareScrollViewContext.tsx | 19 ++++++++++++++++++- .../HTMLRenderers/PreRenderer.tsx | 19 ++++++------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index f592146659f4..f38d00a50a30 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -53,6 +53,7 @@ const Actions = { POPOVER_ANY_ACTION: 'POPOVER_ANY_ACTION', OPEN_EMOJI_PICKER_POPOVER: 'OPEN_EMOJI_PICKER_POPOVER', OPEN_EMOJI_PICKER_POPOVER_STANDALONE: 'OPEN_EMOJI_PICKER_POPOVER_STANDALONE', + CLOSE_EMOJI_PICKER_POPOVER_STANDALONE: 'CLOSE_EMOJI_PICKER_POPOVER_STANDALONE', CLOSE_EMOJI_PICKER_POPOVER: 'CLOSE_EMOJI_PICKER_POPOVER', MEASURE_EMOJI_PICKER_POPOVER: 'MEASURE_EMOJI_PICKER_POPOVER', HIDE_WITHOUT_ANIMATION: 'HIDE_WITHOUT_ANIMATION', @@ -65,7 +66,7 @@ const Actions = { SHOW_ATTACHMENTS_POPOVER: 'SHOW_ATTACHMENTS_POPOVER', CLOSE_ATTACHMENTS_POPOVER: 'CLOSE_ATTACHMENTS_POPOVER', SHOW_ATTACHMENTS_PICKER_POPOVER: 'SHOW_ATTACHMENTS_PICKER_POPOVER', - CLOSE_EMOJI_PICKER_POPOVER_STANDALONE: 'CLOSE_EMOJI_PICKER_POPOVER_STANDALONE', + CLOSE_ATTACHMENTS_PICKER_POPOVER: 'CLOSE_ATTACHMENTS_PICKER_POPOVER', MEASURE_CALL_POPOVER: 'MEASURE_CALL_POPOVER', CLOSE_CALL_POPOVER: 'CLOSE_CALL_POPOVER', }; @@ -88,6 +89,8 @@ const States = { CALL_POPOVER_WITH_KEYBOARD_CLOSED: 'callPopoverWithKeyboardClosed', ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN: 'attachmentsPopoverWithKeyboardOpen', ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED: 'attachmentsPopoverWithKeyboardClosed', + ATTACHMENTS_POPOVER_OPEN: 'attachmentsPopoverOpen', + ATTACHMENTS_POPOVER_CLOSED: 'attachmentsPopoverClosed', MODAL_DELETED: 'modalDeleted', MODAL_WITH_KEYBOARD_OPEN_DELETED: 'modalWithKeyboardOpenDeleted', EDIT_MESSAGE: 'editMessage', @@ -101,6 +104,8 @@ const STATE_MACHINE = { [Actions.MEASURE_COMPOSER]: States.IDLE, [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, [Actions.SHOW_ATTACHMENTS_PICKER_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, + [Actions.OPEN_EMOJI_PICKER_POPOVER_STANDALONE]: States.EMOJI_PICKER_POPOVER_OPEN, + [Actions.SHOW_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_OPEN, }, [States.POPOVER_OPEN]: { [Actions.CLOSE_POPOVER]: States.POPOVER_CLOSED, @@ -118,6 +123,7 @@ const STATE_MACHINE = { [States.EMOJI_PICKER_POPOVER_OPEN]: { [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.POPOVER_CLOSED, + [Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE]: States.KEYBOARD_POPOVER_CLOSED, }, [States.MODAL_DELETED]: { [Actions.MEASURE_CONFIRM_MODAL]: States.MODAL_DELETED, @@ -169,6 +175,17 @@ const STATE_MACHINE = { [Actions.MEASURE_COMPOSER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED, }, + + [States.ATTACHMENTS_POPOVER_OPEN]: { + [Actions.MEASURE_POPOVER]: States.ATTACHMENTS_POPOVER_OPEN, + [Actions.MEASURE_COMPOSER]: States.ATTACHMENTS_POPOVER_OPEN, + [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_CLOSED, + }, + [States.ATTACHMENTS_POPOVER_CLOSED]: { + [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_CLOSED, + [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, + }, + [States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED]: { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, }, diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx index 72a89a183ec9..54f1a7ec77b7 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx @@ -39,19 +39,12 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d onPress={onPressIn ?? (() => {})} onPressIn={onPressIn} onPressOut={onPressOut} - onLongPress={ - (event) =>console.log(111, 0); - onShowContextMenu(() => - showContextMenuForReport( - event, - anchor, - report?.reportID ?? '-1', - action, - checkIfContextMenuActive, - ReportUtils.isArchivedRoom(report, reportNameValuePairs), - ), - ) - } + onLongPress={(event) => { + console.log(111, 0); + onShowContextMenu(() => + showContextMenuForReport(event, anchor, report?.reportID ?? '-1', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report, reportNameValuePairs)), + ); + }} shouldUseHapticsOnLongPress role={CONST.ROLE.PRESENTATION} accessibilityLabel={translate('accessibilityHints.prestyledText')} From 1123d522734f8363b83f29288887ac6489a7ff46 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 8 Aug 2024 18:09:46 +0200 Subject: [PATCH 22/64] attachment popover and emoji picker without keyboard --- .../ActionSheetKeyboardSpace.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index c2214dea813b..02a1e4e8d8e7 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -47,6 +47,7 @@ const useAnimatedKeyboard = () => { }, onEnd: (e) => { 'worklet'; + state.value = e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED; height.value = e.height; progress.value = e.progress; @@ -152,7 +153,6 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } case States.MODAL_DELETED: - case States.EMOJI_PICKER_POPOVER_OPEN: case States.POPOVER_OPEN: { if (popoverHeight) { if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { @@ -160,13 +160,28 @@ function ActionSheetKeyboardSpace(props: ViewProps) { return withSpring(elementOffset < 0 ? 0 : elementOffset, SPRING_CONFIG); } - console.log(114, Math.max(previousElementOffset, 0), previousElementOffset); + console.log(114, Math.max(previousElementOffset, 0)); return withSpring(Math.max(previousElementOffset, 0), SPRING_CONFIG); } console.log(115, 0); return 0; } + case States.ATTACHMENTS_POPOVER_OPEN: + case States.EMOJI_PICKER_POPOVER_OPEN: { + if (popoverHeight) { + if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { + console.log(98, elementOffset < 0 ? 0 : elementOffset, elementOffset); + return withSpring(elementOffset < 0 ? 0 : elementOffset, SPRING_CONFIG); + } + + console.log(99, Math.max(previousElementOffset, 0), previousElementOffset, popoverHeight - composerHeight); + return withSpring(popoverHeight - composerHeight, SPRING_CONFIG); + } + + console.log(1100, 0); + return 0; + } case States.MODAL_WITH_KEYBOARD_OPEN_DELETED: case States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN: { From 3e7680e77b672f01c9fcf251afe37e74eeb6fe0d Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 8 Aug 2024 18:10:47 +0200 Subject: [PATCH 23/64] EMOJI_PICKER_WITH_KEYBOARD_OPEN --- .../ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 02a1e4e8d8e7..6dd6296ad0ab 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -252,7 +252,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } console.log(126, 0); - return 0; + return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0); } case States.KEYBOARD_POPOVER_CLOSED: { From 6281b7b1caf46c63c2e499d56422f247d41a38d2 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 8 Aug 2024 18:52:29 +0200 Subject: [PATCH 24/64] popover closing --- .../ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 6dd6296ad0ab..68a0e91caa48 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -262,7 +262,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } console.log(128, popoverHeight - composerHeight); - return popoverHeight - composerHeight; + return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0); } case States.KEYBOARD_POPOVER_OPEN: { From b5c697f536bb29ae6cf623152547eb905f832478 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 8 Aug 2024 19:17:46 +0200 Subject: [PATCH 25/64] state machine --- .../ActionSheetAwareScrollViewContext.tsx | 5 ++--- src/components/EmojiPicker/EmojiPickerButton.tsx | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index f38d00a50a30..6f0dad2b1b19 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -123,7 +123,7 @@ const STATE_MACHINE = { [States.EMOJI_PICKER_POPOVER_OPEN]: { [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.POPOVER_CLOSED, - [Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE]: States.KEYBOARD_POPOVER_CLOSED, + [Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE]: States.POPOVER_CLOSED, }, [States.MODAL_DELETED]: { [Actions.MEASURE_CONFIRM_MODAL]: States.MODAL_DELETED, @@ -175,11 +175,10 @@ const STATE_MACHINE = { [Actions.MEASURE_COMPOSER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED, }, - [States.ATTACHMENTS_POPOVER_OPEN]: { [Actions.MEASURE_POPOVER]: States.ATTACHMENTS_POPOVER_OPEN, [Actions.MEASURE_COMPOSER]: States.ATTACHMENTS_POPOVER_OPEN, - [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_CLOSED, + [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.POPOVER_CLOSED, }, [States.ATTACHMENTS_POPOVER_CLOSED]: { [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_CLOSED, diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 40e3cdd45f71..00389e3b9e10 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -31,7 +31,7 @@ type EmojiPickerButtonProps = { }; function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shiftVertical = 0, onModalHide, onEmojiSelected}: EmojiPickerButtonProps) { - const actionSheetContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); + const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const emojiPopoverAnchor = useRef(null); @@ -43,12 +43,12 @@ function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shi return; } - actionSheetContext.transitionActionSheetState({ + actionSheetAwareScrollViewContext.transitionActionSheetState({ type: ActionSheetAwareScrollView.Actions.OPEN_EMOJI_PICKER_POPOVER_STANDALONE, }); const onHide = () => { - actionSheetContext.transitionActionSheetState({ + actionSheetAwareScrollViewContext.transitionActionSheetState({ type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE, }); From 426a5425df93bf60f46e76447e0d8ffaca9942ac Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 23 Aug 2024 11:12:10 +0200 Subject: [PATCH 26/64] keyboard-controller bump --- ios/Podfile.lock | 64 +++++++++++++++++++++++------------------------ package-lock.json | 10 ++++---- package.json | 2 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 01c4fc376d30..f6f7a0e8946a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -19,12 +19,12 @@ PODS: - Airship/Core - AirshipFrameworkProxy (5.1.1): - Airship (= 17.7.3) - - AirshipServiceExtension (17.8.0) - - AppAuth (1.6.2): - - AppAuth/Core (= 1.6.2) - - AppAuth/ExternalUserAgent (= 1.6.2) - - AppAuth/Core (1.6.2) - - AppAuth/ExternalUserAgent (1.6.2): + - AirshipServiceExtension (18.7.0) + - AppAuth (1.7.5): + - AppAuth/Core (= 1.7.5) + - AppAuth/ExternalUserAgent (= 1.7.5) + - AppAuth/Core (1.7.5) + - AppAuth/ExternalUserAgent (1.7.5): - AppAuth/Core - boost (1.83.0) - BVLinearGradient (2.8.1): @@ -184,44 +184,44 @@ PODS: - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30911.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleSignIn (7.0.0): - - AppAuth (~> 1.5) - - GTMAppAuth (< 3.0, >= 1.3) - - GTMSessionFetcher/Core (< 4.0, >= 1.1) - - GoogleUtilities/AppDelegateSwizzler (7.13.0): + - GoogleSignIn (7.1.0): + - AppAuth (< 2.0, >= 1.7.3) + - GTMAppAuth (< 5.0, >= 4.1.1) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleUtilities/AppDelegateSwizzler (7.13.3): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - GoogleUtilities/Privacy - - GoogleUtilities/Environment (7.13.0): + - GoogleUtilities/Environment (7.13.3): - GoogleUtilities/Privacy - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/ISASwizzler (7.13.0): + - GoogleUtilities/ISASwizzler (7.13.3): - GoogleUtilities/Privacy - - GoogleUtilities/Logger (7.13.0): + - GoogleUtilities/Logger (7.13.3): - GoogleUtilities/Environment - GoogleUtilities/Privacy - - GoogleUtilities/MethodSwizzler (7.13.0): + - GoogleUtilities/MethodSwizzler (7.13.3): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GoogleUtilities/Network (7.13.0): + - GoogleUtilities/Network (7.13.3): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Privacy - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.13.0)": + - "GoogleUtilities/NSData+zlib (7.13.3)": - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (7.13.0) - - GoogleUtilities/Reachability (7.13.0): + - GoogleUtilities/Privacy (7.13.3) + - GoogleUtilities/Reachability (7.13.3): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GoogleUtilities/UserDefaults (7.13.0): + - GoogleUtilities/UserDefaults (7.13.3): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GTMAppAuth (2.0.0): - - AppAuth/Core (~> 1.6) - - GTMSessionFetcher/Core (< 4.0, >= 1.5) - - GTMSessionFetcher/Core (3.3.1) + - GTMAppAuth (4.1.1): + - AppAuth/Core (~> 1.7) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher/Core (3.5.0) - hermes-engine (0.73.4): - hermes-engine/Pre-built (= 0.73.4) - hermes-engine/Pre-built (0.73.4) @@ -1303,7 +1303,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-keyboard-controller (1.12.6): + - react-native-keyboard-controller (1.13.2): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -2487,8 +2487,8 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: Airship: 5a6d3f8a982398940b0d48423bb9b8736717c123 AirshipFrameworkProxy: 7255f4ed9836dc2920f2f1ea5657ced4cee8a35c - AirshipServiceExtension: 0a5fb14c3fd1879355ab05a81d10f64512a4f79c - AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570 + AirshipServiceExtension: 7f00d1c36a7deddd435a8ef4a052aa9dbc67d357 + AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa boost: d3f49c53809116a5d38da093a8aa78bf551aed09 BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 @@ -2514,10 +2514,10 @@ SPEC CHECKSUMS: glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 GoogleAppMeasurement: 5ba1164e3c844ba84272555e916d0a6d3d977e91 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a - GoogleSignIn: b232380cf495a429b8095d3178a8d5855b42e842 - GoogleUtilities: d053d902a8edaa9904e1bd00c37535385b8ed152 - GTMAppAuth: 99fb010047ba3973b7026e45393f51f27ab965ae - GTMSessionFetcher: 8a1b34ad97ebe6f909fb8b9b77fba99943007556 + GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db + GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 + GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 hermes-engine: b2669ce35fc4ac14f523b307aff8896799829fe2 libaom: 144606b1da4b5915a1054383c3a4459ccdb3c661 libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 @@ -2565,7 +2565,7 @@ SPEC CHECKSUMS: react-native-geolocation: 580c86eb531c0aaf7a14bc76fd2983ce47ca58aa react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 react-native-key-command: 28ccfa09520e7d7e30739480dea4df003493bfe8 - react-native-keyboard-controller: 87bd777183a9e55c455670c3abbb9974c7c7f77f + react-native-keyboard-controller: ed5da3350e5c500d1a250b453de9546e39c4568e react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: 02d31de0e08ab043d48f2a1a8baade109d7b6ca5 react-native-pager-view: ccd4bbf9fc7effaf8f91f8dae43389844d9ef9fa diff --git a/package-lock.json b/package-lock.json index 8312884fd874..bae1d1a548c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,7 +97,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#93399c6410de32966eb57085936ef6951398c2c3", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.12.6", + "react-native-keyboard-controller": "^1.13.2", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -37289,13 +37289,13 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.12.6", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.6.tgz", - "integrity": "sha512-TPqgEelsPOjdmsKmV3iGi1+eG3hzcCXVzJo4F54JeDoCOtk/QP5Qwu586RRgAganogs4XULFRRFXLx8u73GoYw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.13.2.tgz", + "integrity": "sha512-FZkxByqboqa2bq2fXtEnD7f78VmKbu5cHjEfubHfV2ZtkGolZ01XTqKkEQ172GvFSjC5iuF1L3h7C4g8R6Xq9Q==", "peerDependencies": { "react": "*", "react-native": "*", - "react-native-reanimated": ">=2.11.0" + "react-native-reanimated": ">=3.0.0" } }, "node_modules/react-native-launch-arguments": { diff --git a/package.json b/package.json index 0e5e5b3a086a..7c287686374a 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#93399c6410de32966eb57085936ef6951398c2c3", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.12.6", + "react-native-keyboard-controller": "^1.13.2", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", From 1b4f2adc03e8b0334c6face08e72ad91a1418d67 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 3 Sep 2024 14:49:32 +0200 Subject: [PATCH 27/64] fix popover measure --- src/components/PopoverWithMeasuredContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index ecfb6c9d3ca7..deb7d10a80f2 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -103,7 +103,7 @@ function PopoverWithMeasuredContent({ actionSheetAwareScrollViewContext.transitionActionSheetState({ type: ActionSheetAwareScrollView.Actions.MEASURE_POPOVER, payload: { - popoverHeight: Math.floor(popoverHeight), + popoverHeight: Math.floor(height), }, }); } From 3b17fd7788840db0ceea99f285670d1102eb0092 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 3 Sep 2024 14:50:22 +0200 Subject: [PATCH 28/64] clean patch after RN75 bump --- ...ated+3.8.1+003+concurrent-rn-updates.patch | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch diff --git a/patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch b/patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch deleted file mode 100644 index 02a298f4c5a6..000000000000 --- a/patches/react-native-reanimated+3.8.1+003+concurrent-rn-updates.patch +++ /dev/null @@ -1,79 +0,0 @@ -diff --git a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp -index b34579d..87513aa 100644 ---- a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp -+++ b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.cpp -@@ -418,6 +418,24 @@ bool NativeReanimatedModule::isThereAnyLayoutProp( - return false; - } - -+jsi::Object NativeReanimatedModule::getUIProps( -+ jsi::Runtime &rt, -+ const jsi::Object &props) { -+ jsi::Object res = jsi::Object(rt); -+ const jsi::Array propNames = props.getPropertyNames(rt); -+ for (size_t i = 0; i < propNames.size(rt); ++i) { -+ const std::string propName = -+ propNames.getValueAtIndex(rt, i).asString(rt).utf8(rt); -+ bool isLayoutProp = -+ nativePropNames_.find(propName) != nativePropNames_.end(); -+ if (!isLayoutProp) { -+ const jsi::Value &propValue = props.getProperty(rt, propName.c_str()); -+ res.setProperty(rt, propName.c_str(), propValue); -+ } -+ } -+ return res; -+} -+ - jsi::Value NativeReanimatedModule::filterNonAnimatableProps( - jsi::Runtime &rt, - const jsi::Value &props) { -@@ -565,13 +583,15 @@ void NativeReanimatedModule::performOperations() { - } - } - -+ // If there's no layout props to be updated, we can apply the updates -+ // directly onto the components and skip the commit. -+ for (const auto &[shadowNode, props] : copiedOperationsQueue) { -+ Tag tag = shadowNode->getTag(); -+ jsi::Object uiProps = getUIProps(rt, props->asObject(rt)); -+ synchronouslyUpdateUIPropsFunction_(rt, tag, uiProps); -+ } -+ - if (!hasLayoutUpdates) { -- // If there's no layout props to be updated, we can apply the updates -- // directly onto the components and skip the commit. -- for (const auto &[shadowNode, props] : copiedOperationsQueue) { -- Tag tag = shadowNode->getTag(); -- synchronouslyUpdateUIPropsFunction_(rt, tag, props->asObject(rt)); -- } - return; - } - -diff --git a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h -index 9f8c32d..cb31205 100644 ---- a/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h -+++ b/node_modules/react-native-reanimated/Common/cpp/NativeModules/NativeReanimatedModule.h -@@ -163,6 +163,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec { - - #ifdef RCT_NEW_ARCH_ENABLED - bool isThereAnyLayoutProp(jsi::Runtime &rt, const jsi::Object &props); -+ jsi::Object getUIProps(jsi::Runtime &rt, const jsi::Object &props); - jsi::Value filterNonAnimatableProps( - jsi::Runtime &rt, - const jsi::Value &props); -diff --git a/node_modules/react-native-reanimated/apple/REANodesManager.mm b/node_modules/react-native-reanimated/apple/REANodesManager.mm -index ed36c99..0c64925 100644 ---- a/node_modules/react-native-reanimated/apple/REANodesManager.mm -+++ b/node_modules/react-native-reanimated/apple/REANodesManager.mm -@@ -432,9 +432,9 @@ - (void)synchronouslyUpdateViewOnUIThread:(nonnull NSNumber *)viewTag props:(non - REAUIView *componentView = - [componentViewRegistry findComponentViewWithTag:[viewTag integerValue]]; - -- NSSet *propKeysManagedByAnimated = [componentView propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN]; -+ // NSSet *propKeysManagedByAnimated = [componentView propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN]; - [surfacePresenter synchronouslyUpdateViewOnUIThread:viewTag props:uiProps]; -- [componentView setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:propKeysManagedByAnimated]; -+ // [componentView setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:propKeysManagedByAnimated]; - - // `synchronouslyUpdateViewOnUIThread` does not flush props like `backgroundColor` etc. - // so that's why we need to call `finalizeUpdates` here. From f57c4f8ba275f02fbdfd6d2a8ceae6cc842c3f12 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 3 Sep 2024 15:16:56 +0200 Subject: [PATCH 29/64] bump react-native-keyboard-controller --- ios/Podfile.lock | 4 ++-- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d688213fafb4..b66b25716382 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1689,7 +1689,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-keyboard-controller (1.12.2): + - react-native-keyboard-controller (1.13.3): - DoubleConversion - glog - hermes-engine @@ -3184,7 +3184,7 @@ SPEC CHECKSUMS: react-native-geolocation: b9bd12beaf0ebca61a01514517ca8455bd26fa06 react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 react-native-key-command: aae312752fcdfaa2240be9a015fc41ce54087546 - react-native-keyboard-controller: 5075321af7b1c834cfb9582230659d032c963278 + react-native-keyboard-controller: ee7d85b59a4555075b5050eab29bda0aadd6791f react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: fb5112b1fa754975485884ae85a3fb6a684f49d5 react-native-pager-view: 6bff9b0883b902571530ddd1b2ea9dc570f321f6 diff --git a/package-lock.json b/package-lock.json index f01231311246..a60f8e009583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,7 +98,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#93399c6410de32966eb57085936ef6951398c2c3", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.13.2", + "react-native-keyboard-controller": "^1.13.3", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -37360,9 +37360,9 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.13.2.tgz", - "integrity": "sha512-FZkxByqboqa2bq2fXtEnD7f78VmKbu5cHjEfubHfV2ZtkGolZ01XTqKkEQ172GvFSjC5iuF1L3h7C4g8R6Xq9Q==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.13.3.tgz", + "integrity": "sha512-C6W0Ta5cCKa58pTL3A8WPFNvDgwc5+Qs3pj4v3Q4Emk1INkUEkJDsWyV7HdR232V/98mLSWai2W/8HlqsOAqhQ==", "peerDependencies": { "react": "*", "react-native": "*", diff --git a/package.json b/package.json index e34f1430e29c..8a47fe4d7de8 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#93399c6410de32966eb57085936ef6951398c2c3", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.13.2", + "react-native-keyboard-controller": "^1.13.3", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", From e042032b043454a80cd5b5340c741b420c96b84d Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 3 Sep 2024 15:17:26 +0200 Subject: [PATCH 30/64] remove patch react-native-keyboard-controller --- ...keyboard-controller+1.12.2+002+rn-75-fixes.patch | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 patches/react-native-keyboard-controller+1.12.2+002+rn-75-fixes.patch diff --git a/patches/react-native-keyboard-controller+1.12.2+002+rn-75-fixes.patch b/patches/react-native-keyboard-controller+1.12.2+002+rn-75-fixes.patch deleted file mode 100644 index f7ab542a2a2b..000000000000 --- a/patches/react-native-keyboard-controller+1.12.2+002+rn-75-fixes.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt -index 50252f0..28a70d6 100644 ---- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt -+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt -@@ -13,7 +13,7 @@ val ThemedReactContext.rootView: View? - - fun ThemedReactContext?.dispatchEvent(viewId: Int, event: Event<*>) { - val eventDispatcher: EventDispatcher? = -- UIManagerHelper.getEventDispatcherForReactTag(this, viewId) -+ UIManagerHelper.getEventDispatcherForReactTag(this!!, viewId) - eventDispatcher?.dispatchEvent(event) - } - From c0c2e52886ea8da7cf862a3d8d4fb4e97d3c5299 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 9 Sep 2024 11:50:29 +0200 Subject: [PATCH 31/64] fix firebase traceMap error --- src/libs/Firebase/index.native.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Firebase/index.native.ts b/src/libs/Firebase/index.native.ts index d2746d8b25e7..0af52eefb58c 100644 --- a/src/libs/Firebase/index.native.ts +++ b/src/libs/Firebase/index.native.ts @@ -41,7 +41,7 @@ const stopTrace: StopTrace = (customEventName) => { return; } - const trace = traceMap[customEventName].trace; + const trace = traceMap[customEventName]?.trace; if (!trace) { return; } From 88030516257f020476e377766e2f31ffa17b8b7f Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 11 Sep 2024 10:30:15 +0200 Subject: [PATCH 32/64] bump react-native-keyboard-controller --- ios/Podfile.lock | 4 ++-- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b119e04c3ae5..79cae5374b71 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1689,7 +1689,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-keyboard-controller (1.13.3): + - react-native-keyboard-controller (1.13.4): - DoubleConversion - glog - hermes-engine @@ -3184,7 +3184,7 @@ SPEC CHECKSUMS: react-native-geolocation: b9bd12beaf0ebca61a01514517ca8455bd26fa06 react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 react-native-key-command: aae312752fcdfaa2240be9a015fc41ce54087546 - react-native-keyboard-controller: ee7d85b59a4555075b5050eab29bda0aadd6791f + react-native-keyboard-controller: 56b8c30d8ba0eb27b406eec79799d870d95e9046 react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: fb5112b1fa754975485884ae85a3fb6a684f49d5 react-native-pager-view: 94195f1bf32e7f78359fa20057c97e632364a08b diff --git a/package-lock.json b/package-lock.json index 6139b904e62e..9166c7e16e98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -98,7 +98,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#cb392140db4953a283590d7cf93b4d0461baa2a9", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.13.3", + "react-native-keyboard-controller": "^1.13.4", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -37352,9 +37352,9 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.13.3.tgz", - "integrity": "sha512-C6W0Ta5cCKa58pTL3A8WPFNvDgwc5+Qs3pj4v3Q4Emk1INkUEkJDsWyV7HdR232V/98mLSWai2W/8HlqsOAqhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.13.4.tgz", + "integrity": "sha512-80unzD4S+ybgYOluhdeV4zV62ejg6Jt0l5iw7PuA1y3aM1a1+5tS2WoHLNk8605oDaGcVLGNMNF0Qv4GWe97Bg==", "peerDependencies": { "react": "*", "react-native": "*", diff --git a/package.json b/package.json index d2ac50e1dbe5..44e592363302 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#cb392140db4953a283590d7cf93b4d0461baa2a9", "react-native-key-command": "^1.0.8", - "react-native-keyboard-controller": "^1.13.3", + "react-native-keyboard-controller": "^1.13.4", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", From 808d75f277359610b8614970921bbca841c1fa96 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 11 Sep 2024 10:56:20 +0200 Subject: [PATCH 33/64] left only context menu transition: clean components --- .../BaseAnchorForAttachmentsOnly.tsx | 6 ++--- .../AttachmentPicker/index.native.tsx | 3 +-- src/components/AttachmentPicker/types.ts | 6 ----- src/components/ConfirmContent.tsx | 23 +++------------- .../EmojiPicker/EmojiPickerButton.tsx | 16 +---------- .../EmojiPickerMenu/index.native.tsx | 22 ++------------- .../Reactions/AddReactionBubble.tsx | 3 +-- .../QuickEmojiReactions/index.native.tsx | 26 ++---------------- .../ReportActionItemEmojiReactions.tsx | 11 -------- .../report/ContextMenu/ContextMenuActions.tsx | 21 +++------------ .../AttachmentPickerWithMenuItems.tsx | 18 +------------ src/pages/home/report/ReportActionItem.tsx | 27 ------------------- 12 files changed, 16 insertions(+), 166 deletions(-) diff --git a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx index 6e2ced803501..1d273e847d26 100644 --- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx +++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx @@ -41,7 +41,7 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', dow return ( - {({onShowContextMenu, anchor, report, reportNameValuePairs, action, checkIfContextMenuActive}) => ( + {({anchor, report, reportNameValuePairs, action, checkIfContextMenuActive}) => ( { @@ -54,9 +54,7 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', dow onPressIn={onPressIn} onPressOut={onPressOut} onLongPress={(event) => - 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)) } shouldUseHapticsOnLongPress accessibilityLabel={displayName} diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index 68cc7458728c..366366423324 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -112,7 +112,7 @@ const getDataForUpload = (fileData: FileResponse): Promise => { * a callback. This is the ios/android implementation * opening a modal with attachment options */ -function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, shouldHideCameraOption = false, onLayout}: AttachmentPickerProps) { +function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, shouldHideCameraOption = false}: AttachmentPickerProps) { const styles = useThemeStyles(); const [isVisible, setIsVisible] = useState(false); @@ -382,7 +382,6 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s return ( <> { close(); onCanceled.current(); diff --git a/src/components/AttachmentPicker/types.ts b/src/components/AttachmentPicker/types.ts index 7ef0e1120afd..63ed27408eea 100644 --- a/src/components/AttachmentPicker/types.ts +++ b/src/components/AttachmentPicker/types.ts @@ -42,12 +42,6 @@ type AttachmentPickerProps = { /** The types of files that can be selected with this picker. */ type?: ValueOf; - /** - * Optional callback attached to popover's children container. - * Invoked on Popover mount and layout changes. - */ - onLayout?: ((event: LayoutChangeEvent) => void) | undefined; - acceptedFileTypes?: Array>; }; diff --git a/src/components/ConfirmContent.tsx b/src/components/ConfirmContent.tsx index b3bbf891bf06..a75179dd3831 100644 --- a/src/components/ConfirmContent.tsx +++ b/src/components/ConfirmContent.tsx @@ -1,6 +1,6 @@ import type {ReactNode} from 'react'; -import React, {useCallback, useContext} from 'react'; -import type {LayoutChangeEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; +import React, {useContext} from 'react'; +import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -132,20 +132,6 @@ function ConfirmContent({ const {isOffline} = useNetwork(); const StyleUtils = useStyleUtils(); - const onLayout = useCallback( - (event: LayoutChangeEvent) => { - const {height} = event.nativeEvent.layout; - - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: Actions.MEASURE_CONFIRM_MODAL, - payload: { - popoverHeight: height, - }, - }); - }, - [actionSheetAwareScrollViewContext], - ); - const isCentered = shouldCenterContent; return ( @@ -162,10 +148,7 @@ function ConfirmContent({ )} - + {shouldShowDismissIcon && ( diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 00389e3b9e10..a0dffa3486ee 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -43,23 +43,9 @@ function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shi return; } - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.OPEN_EMOJI_PICKER_POPOVER_STANDALONE, - }); - - const onHide = () => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE, - }); - - if (onModalHide) { - onModalHide(); - } - }; - if (!EmojiPickerAction.emojiPickerRef.current?.isEmojiPickerVisible) { EmojiPickerAction.showEmojiPicker( - onHide, + onModalHide, onEmojiSelected, emojiPopoverAnchor, { diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx index 41fac94e9e7d..b5b4c2d7e71c 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx @@ -1,11 +1,9 @@ import type {ListRenderItem} from '@shopify/flash-list'; import lodashDebounce from 'lodash/debounce'; -import React, {useCallback, useContext} from 'react'; +import React, {useCallback} from 'react'; import type {ForwardedRef} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import {runOnUI, scrollTo} from 'react-native-reanimated'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import EmojiPickerMenuItem from '@components/EmojiPicker/EmojiPickerMenuItem'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; @@ -116,25 +114,9 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r }, [styles, windowWidth, preferredSkinTone, singleExecution, onEmojiSelected, translate, activeEmoji], ); - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); - const onLayout = useCallback( - (event: LayoutChangeEvent) => { - const {height} = event.nativeEvent.layout; - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.MEASURE_EMOJI_PICKER_POPOVER, - payload: { - popoverHeight: height, - }, - }); - }, - [actionSheetAwareScrollViewContext], - ); return ( - + EmojiPickerAction.resetEmojiPopoverAnchor, []); const onPress = () => { - const openPicker = (refParam?: PickerRefElement, anchorOrigin?: AnchorOrigin, onHide = () => {}) => { + const openPicker = (refParam?: PickerRefElement, anchorOrigin?: AnchorOrigin) => { EmojiPickerAction.showEmojiPicker( () => { - onHide(); setIsEmojiPickerActive?.(false); }, (emojiCode, emojiObject) => { diff --git a/src/components/Reactions/QuickEmojiReactions/index.native.tsx b/src/components/Reactions/QuickEmojiReactions/index.native.tsx index 6c55beb9741d..28e2125e1c40 100644 --- a/src/components/Reactions/QuickEmojiReactions/index.native.tsx +++ b/src/components/Reactions/QuickEmojiReactions/index.native.tsx @@ -1,17 +1,10 @@ -import React, {useContext} from 'react'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; +import React from 'react'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import BaseQuickEmojiReactions from './BaseQuickEmojiReactions'; -import type {BaseQuickEmojiReactionsProps, OpenPickerCallback, QuickEmojiReactionsProps} from './types'; +import type {OpenPickerCallback, QuickEmojiReactionsProps} from './types'; function QuickEmojiReactions({closeContextMenu, onEmojiSelected, ...rest}: QuickEmojiReactionsProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); - const onPressOpenPicker = (openPicker?: OpenPickerCallback) => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.OPEN_EMOJI_PICKER_POPOVER, - }); - // We first need to close the menu as it's a popover. // The picker is a popover as well and on mobile there can only // be one active popover at a time. @@ -20,28 +13,13 @@ function QuickEmojiReactions({closeContextMenu, onEmojiSelected, ...rest}: Quick // gets closed, before the picker actually opens, we pass the composer // ref as anchor for the emoji picker popover. openPicker?.(ReportActionComposeFocusManager.composerRef); - - openPicker?.(ReportActionComposeFocusManager.composerRef, undefined, () => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER, - }); - }); }); }; - const onEmojiSelectedCallback: BaseQuickEmojiReactionsProps['onEmojiSelected'] = (emoji, emojiReactions) => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER, - }); - - onEmojiSelected(emoji, emojiReactions); - }; - return ( ); diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.tsx b/src/components/Reactions/ReportActionItemEmojiReactions.tsx index 605e7ea4bb77..fe683f51aa15 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.tsx +++ b/src/components/Reactions/ReportActionItemEmojiReactions.tsx @@ -36,15 +36,6 @@ type ReportActionItemEmojiReactionsProps = WithCurrentUserPersonalDetailsProps & */ toggleReaction: (emoji: Emoji, ignoreSkinToneOnCompare?: boolean) => void; - /** - * Function to call when the user presses on the add reaction button. - * This is only called when the user presses on the button, not on the - * reaction bubbles. - * This is optional, because we don't need it everywhere. - * For example in the ReportActionContextMenu we don't need it. - */ - onPressOpenPicker: (openPicker: OpenPickerCallback) => void; - /** We disable reacting with emojis on report actions that have errors */ shouldBlockReactions?: boolean; @@ -89,7 +80,6 @@ function ReportActionItemEmojiReactions({ reportAction, currentUserPersonalDetails, toggleReaction, - onPressOpenPicker, emojiReactions = {}, shouldBlockReactions = false, preferredLocale = CONST.LOCALES.DEFAULT, @@ -181,7 +171,6 @@ function ReportActionItemEmojiReactions({ })} {!shouldBlockReactions && ( type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canEditReportAction(reportAction) && !isArchivedRoom && !isChronosReport, - onPress: (closePopover, {reportID, reportAction, draftMessage, transitionActionSheetState}) => { + onPress: (closePopover, {reportID, reportAction, draftMessage}) => { if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { hideContextMenu(false); const childReportID = reportAction?.childReportID ?? '-1'; @@ -255,10 +255,6 @@ const ContextMenuActions: ContextMenuAction[] = [ }; if (closePopover) { - transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.EDIT_REPORT, - }); - // Hide popover, then call editAction hideContextMenu(false, editAction); return; @@ -622,21 +618,10 @@ const ContextMenuActions: ContextMenuAction[] = [ !isArchivedRoom && !isChronosReport && !ReportActionsUtils.isMessageDeleted(reportAction), - onPress: (closePopover, {reportID, reportAction, transitionActionSheetState}) => { + onPress: (closePopover, {reportID, reportAction}) => { if (closePopover) { - transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.SHOW_DELETE_CONFIRM_MODAL, - }); - - const onClose = () => { - transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.CLOSE_CONFIRM_MODAL, - }); - clearActiveReportAction(); - }; - // Hide popover, then call showDeleteConfirmModal - hideContextMenu(false, () => showDeleteModal(reportID, reportAction, true, onClose, onClose)); + hideContextMenu(false, () => showDeleteModal(reportID, reportAction)); return; } diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index dd394a65911f..6e3c3a48de74 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,10 +1,8 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {useCallback, useContext, useEffect, useMemo} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; +import React, {useCallback, useEffect, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentPicker from '@components/AttachmentPicker'; import Icon from '@components/Icon'; @@ -117,7 +115,6 @@ function AttachmentPickerWithMenuItems({ actionButtonRef, raiseIsScrollLikelyLayoutTriggered, }: AttachmentPickerWithMenuItemsProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const isFocused = useIsFocused(); const theme = useTheme(); const styles = useThemeStyles(); @@ -188,18 +185,6 @@ function AttachmentPickerWithMenuItems({ ]; }, [report, reportID, translate]); - const measurePopover = useCallback( - ({nativeEvent}: LayoutChangeEvent) => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.MEASURE_POPOVER, - payload: { - popoverHeight: nativeEvent.layout.height, - }, - }); - }, - [actionSheetAwareScrollViewContext], - ); - const onPopoverMenuClose = () => { setMenuVisibility(false); onMenuClosed(); @@ -322,7 +307,6 @@ function AttachmentPickerWithMenuItems({ { - 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_EMOJI_PICKER_POPOVER, - payload: { - fy, - height, - }, - }); - - openPicker(undefined, undefined, () => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: ActionSheetAwareScrollView.Actions.CLOSE_EMOJI_PICKER_POPOVER, - }); - }); - }); - }, - [actionSheetAwareScrollViewContext], - ); - const handleShowContextMenu = useCallback( (callback: () => void) => { if (!(popoverAnchorRef.current && 'measureInWindow' in popoverAnchorRef.current)) { @@ -819,7 +793,6 @@ function ReportActionItem({ {!ReportActionsUtils.isMessageDeleted(action) && ( Date: Wed, 11 Sep 2024 11:23:35 +0200 Subject: [PATCH 34/64] left only context menu transition: clean actions --- .../ActionSheetAwareScrollViewContext.tsx | 82 ------------ .../ActionSheetKeyboardSpace.tsx | 126 ++---------------- 2 files changed, 9 insertions(+), 199 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index 6f0dad2b1b19..ded1719fcb35 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -51,22 +51,9 @@ const Actions = { MEASURE_POPOVER: 'MEASURE_POPOVER', MEASURE_COMPOSER: 'MEASURE_COMPOSER', POPOVER_ANY_ACTION: 'POPOVER_ANY_ACTION', - OPEN_EMOJI_PICKER_POPOVER: 'OPEN_EMOJI_PICKER_POPOVER', - OPEN_EMOJI_PICKER_POPOVER_STANDALONE: 'OPEN_EMOJI_PICKER_POPOVER_STANDALONE', - CLOSE_EMOJI_PICKER_POPOVER_STANDALONE: 'CLOSE_EMOJI_PICKER_POPOVER_STANDALONE', - CLOSE_EMOJI_PICKER_POPOVER: 'CLOSE_EMOJI_PICKER_POPOVER', - MEASURE_EMOJI_PICKER_POPOVER: 'MEASURE_EMOJI_PICKER_POPOVER', HIDE_WITHOUT_ANIMATION: 'HIDE_WITHOUT_ANIMATION', - EDIT_REPORT: 'EDIT_REPORT', - SHOW_DELETE_CONFIRM_MODAL: 'SHOW_DELETE_CONFIRM_MODAL', END_TRANSITION: 'END_TRANSITION', OPEN_CALL_POPOVER: 'OPEN_CALL_POPOVER', - CLOSE_CONFIRM_MODAL: 'CLOSE_CONFIRM_MODAL', - MEASURE_CONFIRM_MODAL: 'MEASURE_CONFIRM_MODAL', - SHOW_ATTACHMENTS_POPOVER: 'SHOW_ATTACHMENTS_POPOVER', - CLOSE_ATTACHMENTS_POPOVER: 'CLOSE_ATTACHMENTS_POPOVER', - SHOW_ATTACHMENTS_PICKER_POPOVER: 'SHOW_ATTACHMENTS_PICKER_POPOVER', - CLOSE_ATTACHMENTS_PICKER_POPOVER: 'CLOSE_ATTACHMENTS_PICKER_POPOVER', MEASURE_CALL_POPOVER: 'MEASURE_CALL_POPOVER', CLOSE_CALL_POPOVER: 'CLOSE_CALL_POPOVER', }; @@ -80,20 +67,9 @@ const States = { KEYBOARD_POPOVER_OPEN: 'keyboardPopoverOpen', KEYBOARD_CLOSED_POPOVER: 'keyboardClosingPopover', POPOVER_MEASURED: 'popoverMeasured', - EMOJI_PICKER_POPOVER_OPEN: 'emojiPickerPopoverOpen', - DELETE_MODAL_OPEN: 'deleteModalOpen', - DELETE_MODAL_WITH_KEYBOARD_OPEN: 'deleteModalWithKeyboardOpen', - EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN: 'emojiPickerPopoverWithKeyboardOpen', - EMOJI_PICKER_WITH_KEYBOARD_OPEN: 'emojiPickerWithKeyboardOpen', CALL_POPOVER_WITH_KEYBOARD_OPEN: 'callPopoverWithKeyboardOpen', CALL_POPOVER_WITH_KEYBOARD_CLOSED: 'callPopoverWithKeyboardClosed', - ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN: 'attachmentsPopoverWithKeyboardOpen', - ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED: 'attachmentsPopoverWithKeyboardClosed', - ATTACHMENTS_POPOVER_OPEN: 'attachmentsPopoverOpen', - ATTACHMENTS_POPOVER_CLOSED: 'attachmentsPopoverClosed', - MODAL_DELETED: 'modalDeleted', MODAL_WITH_KEYBOARD_OPEN_DELETED: 'modalWithKeyboardOpenDeleted', - EDIT_MESSAGE: 'editMessage', }; const STATE_MACHINE = { @@ -102,65 +78,28 @@ const STATE_MACHINE = { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, [Actions.MEASURE_POPOVER]: States.IDLE, [Actions.MEASURE_COMPOSER]: States.IDLE, - [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, - [Actions.SHOW_ATTACHMENTS_PICKER_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.OPEN_EMOJI_PICKER_POPOVER_STANDALONE]: States.EMOJI_PICKER_POPOVER_OPEN, - [Actions.SHOW_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_OPEN, }, [States.POPOVER_OPEN]: { [Actions.CLOSE_POPOVER]: States.POPOVER_CLOSED, [Actions.MEASURE_POPOVER]: States.POPOVER_OPEN, [Actions.MEASURE_COMPOSER]: States.POPOVER_OPEN, - [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, [Actions.POPOVER_ANY_ACTION]: States.POPOVER_CLOSED, [Actions.HIDE_WITHOUT_ANIMATION]: States.IDLE, - [Actions.EDIT_REPORT]: States.IDLE, - [Actions.SHOW_DELETE_CONFIRM_MODAL]: States.MODAL_DELETED, }, [States.POPOVER_CLOSED]: { [Actions.END_TRANSITION]: States.IDLE, }, - [States.EMOJI_PICKER_POPOVER_OPEN]: { - [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, - [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.POPOVER_CLOSED, - [Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE]: States.POPOVER_CLOSED, - }, - [States.MODAL_DELETED]: { - [Actions.MEASURE_CONFIRM_MODAL]: States.MODAL_DELETED, - [Actions.CLOSE_CONFIRM_MODAL]: States.POPOVER_CLOSED, - }, [States.KEYBOARD_OPEN]: { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, [Actions.OPEN_POPOVER]: States.KEYBOARD_POPOVER_OPEN, - [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.KEYBOARD_POPOVER_OPEN, - [Actions.OPEN_EMOJI_PICKER_POPOVER_STANDALONE]: States.EMOJI_PICKER_WITH_KEYBOARD_OPEN, [Actions.CLOSE_KEYBOARD]: States.IDLE, [Actions.OPEN_CALL_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.SHOW_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.SHOW_ATTACHMENTS_PICKER_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, [Actions.MEASURE_COMPOSER]: States.KEYBOARD_OPEN, }, [States.KEYBOARD_POPOVER_OPEN]: { [Actions.MEASURE_POPOVER]: States.KEYBOARD_POPOVER_OPEN, [Actions.MEASURE_COMPOSER]: States.KEYBOARD_POPOVER_OPEN, [Actions.CLOSE_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, - [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, - [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.KEYBOARD_POPOVER_OPEN, - [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.SHOW_DELETE_CONFIRM_MODAL]: States.MODAL_WITH_KEYBOARD_OPEN_DELETED, - [Actions.EDIT_REPORT]: States.EDIT_MESSAGE, - }, - [States.MODAL_WITH_KEYBOARD_OPEN_DELETED]: { - [Actions.MEASURE_CONFIRM_MODAL]: States.MODAL_WITH_KEYBOARD_OPEN_DELETED, - [Actions.CLOSE_CONFIRM_MODAL]: States.KEYBOARD_CLOSED_POPOVER, - }, - [States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN]: { - [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.CLOSE_EMOJI_PICKER_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, - }, - [States.EMOJI_PICKER_WITH_KEYBOARD_OPEN]: { - [Actions.MEASURE_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_WITH_KEYBOARD_OPEN, - [Actions.CLOSE_EMOJI_PICKER_POPOVER_STANDALONE]: States.KEYBOARD_POPOVER_CLOSED, }, [States.CALL_POPOVER_WITH_KEYBOARD_OPEN]: { [Actions.MEASURE_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, @@ -170,24 +109,6 @@ const STATE_MACHINE = { [States.CALL_POPOVER_WITH_KEYBOARD_CLOSED]: { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, }, - [States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN]: { - [Actions.MEASURE_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.MEASURE_COMPOSER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED, - }, - [States.ATTACHMENTS_POPOVER_OPEN]: { - [Actions.MEASURE_POPOVER]: States.ATTACHMENTS_POPOVER_OPEN, - [Actions.MEASURE_COMPOSER]: States.ATTACHMENTS_POPOVER_OPEN, - [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.POPOVER_CLOSED, - }, - [States.ATTACHMENTS_POPOVER_CLOSED]: { - [Actions.CLOSE_ATTACHMENTS_POPOVER]: States.ATTACHMENTS_POPOVER_CLOSED, - [Actions.OPEN_EMOJI_PICKER_POPOVER]: States.EMOJI_PICKER_POPOVER_OPEN, - }, - - [States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED]: { - [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, - }, [States.KEYBOARD_POPOVER_CLOSED]: { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, }, @@ -195,9 +116,6 @@ const STATE_MACHINE = { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, [Actions.END_TRANSITION]: States.KEYBOARD_OPEN, }, - [States.EDIT_MESSAGE]: { - [Actions.CLOSE_KEYBOARD]: States.IDLE, - }, }; function ActionSheetAwareScrollViewProvider(props: PropsWithChildren) { diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 68a0e91caa48..e69732ef410a 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -32,7 +32,6 @@ const useAnimatedKeyboard = () => { { onStart: (e) => { 'worklet'; - // save the last keyboard height if (e.height === 0) { heightWhenOpened.value = height.value; @@ -47,7 +46,6 @@ const useAnimatedKeyboard = () => { }, onEnd: (e) => { 'worklet'; - state.value = e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED; height.value = e.height; progress.value = e.progress; @@ -59,12 +57,6 @@ const useAnimatedKeyboard = () => { return {state, height, heightWhenOpened, progress}; }; -const setInitialValueAndRunAnimation = (value: number, animation: number) => { - 'worklet'; - - return withSequence(withTiming(value, {duration: 0}), animation); -}; - const useSafeAreaPaddings = () => { const StyleUtils = useStyleUtils(); const insets = useSafeAreaInsets(); @@ -95,10 +87,11 @@ function ActionSheetKeyboardSpace(props: ViewProps) { useAnimatedReaction( () => keyboard.state.value, (lastState) => { - if (lastState === syncLocalWorkletState.lastState) { - return; - } - + if (lastState === syncLocalWorkletState.lastState) { + return; + } + syncLocalWorkletState.lastState = lastState; + if (lastState === KeyboardState.OPEN) { runOnJS(transitionActionSheetState)({type: Actions.OPEN_KEYBOARD}); } else if (lastState === KeyboardState.CLOSED) { @@ -132,14 +125,15 @@ function ActionSheetKeyboardSpace(props: ViewProps) { // 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 (previous.state === States.KEYBOARD_CLOSED_POPOVER || (previous.state === States.KEYBOARD_OPEN && elementOffset < 0)) { + if (previous.state === States.KEYBOARD_CLOSED_POPOVER || (previous.state === States.KEYBOARD_OPEN && elementOffset < 0)) { + console.log(110, Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0)); + console.log(1101, previous.state === States.KEYBOARD_CLOSED_POPOVER, previous.state === States.KEYBOARD_OPEN, elementOffset); return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0); } - console.log(111, 0); + console.log(111, 0, previous.state === States.KEYBOARD_CLOSED_POPOVER, previous.state === States.KEYBOARD_OPEN, elementOffset); return withSpring(0, SPRING_CONFIG); } @@ -152,7 +146,6 @@ function ActionSheetKeyboardSpace(props: ViewProps) { }); } - case States.MODAL_DELETED: case States.POPOVER_OPEN: { if (popoverHeight) { if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { @@ -167,103 +160,6 @@ function ActionSheetKeyboardSpace(props: ViewProps) { console.log(115, 0); return 0; } - case States.ATTACHMENTS_POPOVER_OPEN: - case States.EMOJI_PICKER_POPOVER_OPEN: { - if (popoverHeight) { - if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { - console.log(98, elementOffset < 0 ? 0 : elementOffset, elementOffset); - return withSpring(elementOffset < 0 ? 0 : elementOffset, SPRING_CONFIG); - } - - console.log(99, Math.max(previousElementOffset, 0), previousElementOffset, popoverHeight - composerHeight); - return withSpring(popoverHeight - composerHeight, SPRING_CONFIG); - } - - console.log(1100, 0); - return 0; - } - - case States.MODAL_WITH_KEYBOARD_OPEN_DELETED: - case States.EMOJI_PICKER_POPOVER_WITH_KEYBOARD_OPEN: { - // when item is higher than keyboard and bottom sheet - // we should just stay in place - if (elementOffset < 0) { - console.log(116, invertedKeyboardHeight); - return invertedKeyboardHeight; - } - - const nextOffset = invertedKeyboardHeight + elementOffset; - if (previous?.payload?.popoverHeight !== popoverHeight) { - const previousOffset = invertedKeyboardHeight + previousElementOffset; - - if (previousElementOffset === 0 || nextOffset > previousOffset) { - console.log(117, nextOffset); - return withSpring(nextOffset, SPRING_CONFIG); - } - - console.log(118, previousOffset); - return previousOffset; - } - console.log(119, nextOffset); - return nextOffset; - } - - case States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_CLOSED: - case States.ATTACHMENTS_POPOVER_WITH_KEYBOARD_OPEN: { - // this transition is extremely slow and we may not have `popoverHeight` when keyboard is hiding - // so we run two fold animation: - // - when keyboard is hiding -> we return `0` and thus the content is sticky to composer - // - when keyboard is closed and we have `popoverHeight` (i. e. popup was measured) -> we run spring animation - if (keyboard.state.value === KeyboardState.CLOSING) { - console.log(1200, 0); - return 0; - } - if (keyboard.progress.value === 0) { - console.log(1201, keyboard.progress.value, interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]), popoverHeight, composerHeight); - return withSpring(popoverHeight - composerHeight, SPRING_CONFIG); - } - - // when keyboard appears -> we already have all values so we do interpolation based on keyboard position - console.log(1202, keyboard.progress.value, interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]), popoverHeight, composerHeight); - return interpolate(keyboard.progress.value, [0, 1], [popoverHeight - composerHeight, 0]); - } - case States.CALL_POPOVER_WITH_KEYBOARD_OPEN: { - if (keyboard.height.value > 0) { - console.log(121, 0); - return 0; - } - console.log(122, lastKeyboardHeight, popoverHeight - composerHeight); - return setInitialValueAndRunAnimation(lastKeyboardHeight, withSpring(popoverHeight - composerHeight, SPRING_CONFIG)); - } - case States.CALL_POPOVER_WITH_KEYBOARD_CLOSED: { - // keyboard is opened - if (keyboard.height.value > 0) { - console.log(123, 0); - return 0; - } - - console.log(124, lastKeyboardHeight); - return withSpring(lastKeyboardHeight, SPRING_CONFIG); - } - case States.EMOJI_PICKER_WITH_KEYBOARD_OPEN: { - if (keyboard.state.value === KeyboardState.CLOSED) { - console.log(125, popoverHeight - composerHeight); - return popoverHeight - composerHeight; - } - - console.log(126, 0); - return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0); - } - - case States.KEYBOARD_POPOVER_CLOSED: { - if (keyboard.heightWhenOpened.value === keyboard.height.value) { - console.log(127, 0); - return 0; - } - - console.log(128, popoverHeight - composerHeight); - return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0); - } case States.KEYBOARD_POPOVER_OPEN: { if (keyboard.state.value === KeyboardState.OPEN) { @@ -310,10 +206,6 @@ function ActionSheetKeyboardSpace(props: ViewProps) { duration: 0, }); } - case States.EDIT_MESSAGE: { - console.log(137, 0); - return 0; - } default: console.log(138, 0); From 2427bbe33e3a07cfeea8672cbc171ded8a3c304d Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 11 Sep 2024 11:32:40 +0200 Subject: [PATCH 35/64] left only context menu transition: the rest --- src/components/ConfirmContent.tsx | 4 +--- src/components/EmojiPicker/EmojiPickerButton.tsx | 4 +--- src/components/Reactions/QuickEmojiReactions/index.native.tsx | 2 +- src/components/Reactions/QuickEmojiReactions/types.ts | 2 +- src/components/Reactions/ReportActionItemEmojiReactions.tsx | 1 - .../home/report/ReportActionCompose/ReportActionCompose.tsx | 1 - 6 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/components/ConfirmContent.tsx b/src/components/ConfirmContent.tsx index a75179dd3831..36f24c2a3477 100644 --- a/src/components/ConfirmContent.tsx +++ b/src/components/ConfirmContent.tsx @@ -1,5 +1,5 @@ import type {ReactNode} from 'react'; -import React, {useContext} from 'react'; +import React from 'react'; import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; @@ -11,7 +11,6 @@ import colors from '@styles/theme/colors'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type IconAsset from '@src/types/utils/IconAsset'; -import {Actions, ActionSheetAwareScrollViewContext} from './ActionSheetAwareScrollView'; import Button from './Button'; import Header from './Header'; import Icon from './Icon'; @@ -125,7 +124,6 @@ function ConfirmContent({ titleContainerStyles, shouldReverseStackedButtons = false, }: ConfirmContentProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index a0dffa3486ee..466cfe0dffd5 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -1,6 +1,5 @@ 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 Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; @@ -31,7 +30,6 @@ type EmojiPickerButtonProps = { }; function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shiftVertical = 0, onModalHide, onEmojiSelected}: EmojiPickerButtonProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const emojiPopoverAnchor = useRef(null); diff --git a/src/components/Reactions/QuickEmojiReactions/index.native.tsx b/src/components/Reactions/QuickEmojiReactions/index.native.tsx index 28e2125e1c40..b0eb88b31b68 100644 --- a/src/components/Reactions/QuickEmojiReactions/index.native.tsx +++ b/src/components/Reactions/QuickEmojiReactions/index.native.tsx @@ -3,7 +3,7 @@ import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManag import BaseQuickEmojiReactions from './BaseQuickEmojiReactions'; import type {OpenPickerCallback, QuickEmojiReactionsProps} from './types'; -function QuickEmojiReactions({closeContextMenu, onEmojiSelected, ...rest}: QuickEmojiReactionsProps) { +function QuickEmojiReactions({closeContextMenu, ...rest}: QuickEmojiReactionsProps) { const onPressOpenPicker = (openPicker?: OpenPickerCallback) => { // We first need to close the menu as it's a popover. // The picker is a popover as well and on mobile there can only diff --git a/src/components/Reactions/QuickEmojiReactions/types.ts b/src/components/Reactions/QuickEmojiReactions/types.ts index 725b5aea764f..0021f33ce2c0 100644 --- a/src/components/Reactions/QuickEmojiReactions/types.ts +++ b/src/components/Reactions/QuickEmojiReactions/types.ts @@ -7,7 +7,7 @@ import type {Locale, ReportAction, ReportActionReactions} from '@src/types/onyx' type PickerRefElement = RefObject; -type OpenPickerCallback = (element?: PickerRefElement, anchorOrigin?: AnchorOrigin, callback?: () => void) => void; +type OpenPickerCallback = (element?: PickerRefElement, anchorOrigin?: AnchorOrigin) => void; type CloseContextMenuCallback = () => void; diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.tsx b/src/components/Reactions/ReportActionItemEmojiReactions.tsx index fe683f51aa15..943158607db4 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.tsx +++ b/src/components/Reactions/ReportActionItemEmojiReactions.tsx @@ -16,7 +16,6 @@ import type {Locale, ReportAction, ReportActionReactions} from '@src/types/onyx' import type {PendingAction} from '@src/types/onyx/OnyxCommon'; import AddReactionBubble from './AddReactionBubble'; import EmojiReactionBubble from './EmojiReactionBubble'; -import type {OpenPickerCallback} from './QuickEmojiReactions/types'; import ReactionTooltipContent from './ReactionTooltipContent'; type ReportActionItemEmojiReactionsProps = WithCurrentUserPersonalDetailsProps & { diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index cc4603bbd166..cae2dbeece33 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -146,7 +146,6 @@ function ReportActionCompose({ const actionButtonRef = useRef(null); const {isSmallScreenWidth, isMediumScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {isOffline} = useNetwork(); - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const navigation = useNavigation(); From 195d6bee112548ba8a2ecde8e9c1c1cb76195838 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 16 Sep 2024 17:40:47 +0200 Subject: [PATCH 36/64] adjust ActionSheetKeyboardSpace --- .../ActionSheetKeyboardSpace.tsx | 68 ++++++++----------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index e69732ef410a..2667bf3a8260 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -1,7 +1,7 @@ -import React, {useContext, useEffect, useRef} from 'react'; +import React, {useContext, useEffect} from 'react'; import type {ViewProps} from 'react-native'; import {useKeyboardHandler} from 'react-native-keyboard-controller'; -import Reanimated, {interpolate, runOnJS, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withSequence, withSpring, withTiming} from 'react-native-reanimated'; +import Reanimated, {runOnJS, 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'; @@ -25,7 +25,7 @@ const SPRING_CONFIG = { const useAnimatedKeyboard = () => { const state = useSharedValue(KeyboardState.UNKNOWN); const height = useSharedValue(0); - const progress = useSharedValue(0); + const lastHeight = useSharedValue(0); const heightWhenOpened = useSharedValue(0); useKeyboardHandler( @@ -33,28 +33,28 @@ const useAnimatedKeyboard = () => { onStart: (e) => { 'worklet'; // save the last keyboard height - if (e.height === 0) { - heightWhenOpened.value = height.value; + if (e.height !== 0) { + heightWhenOpened.value = e.height; + height.value = 0; } + height.value = heightWhenOpened.value; + lastHeight.value = e.height; state.value = e.height > 0 ? KeyboardState.OPENING : KeyboardState.CLOSING; }, onMove: (e) => { 'worklet'; - - progress.value = e.progress; height.value = e.height; }, onEnd: (e) => { 'worklet'; state.value = e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED; height.value = e.height; - progress.value = e.progress; }, }, [], ); - return {state, height, heightWhenOpened, progress}; + return {state, height, heightWhenOpened}; }; const useSafeAreaPaddings = () => { @@ -71,27 +71,24 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const keyboard = useAnimatedKeyboard(); // similar to using `global` in worklet but it's just a local object - const syncLocalWorkletState = useRef({ - lastState: KeyboardState.UNKNOWN, - }).current; + const syncLocalWorkletStateL = useSharedValue(KeyboardState.UNKNOWN); const {windowHeight} = useWindowDimensions(); const {currentActionSheetState, transitionActionSheetStateWorklet: transition, transitionActionSheetState, resetStateMachine} = useContext(ActionSheetAwareScrollViewContext); // Reset state machine when component unmounts - useEffect(() => () => resetStateMachine(), [resetStateMachine]); // eslint-disable-next-line arrow-body-style - // useEffect(() => { - // return () => resetStateMachine(); - // }, [resetStateMachine]); + useEffect(() => { + return () => resetStateMachine(); + }, [resetStateMachine]); useAnimatedReaction( () => keyboard.state.value, (lastState) => { - if (lastState === syncLocalWorkletState.lastState) { - return; - } - syncLocalWorkletState.lastState = lastState; - + if (lastState === syncLocalWorkletStateL.lastState) { + return; + } + syncLocalWorkletStateL.value = lastState; + if (lastState === KeyboardState.OPEN) { runOnJS(transitionActionSheetState)({type: Actions.OPEN_KEYBOARD}); } else if (lastState === KeyboardState.CLOSED) { @@ -123,22 +120,23 @@ function ActionSheetKeyboardSpace(props: ViewProps) { ? previousPayload.fy + safeArea.top + previousPayload.height - (windowHeight - previousPayload.popoverHeight) : 0; + const isOpeningKeyboard = syncLocalWorkletStateL.value === 1; + const isClosingKeyboard = syncLocalWorkletStateL.value === 3; + const isClosedKeyboard = syncLocalWorkletStateL.value === 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 (previous.state === States.KEYBOARD_CLOSED_POPOVER || (previous.state === States.KEYBOARD_OPEN && elementOffset < 0)) { - console.log(110, Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0)); - console.log(1101, previous.state === States.KEYBOARD_CLOSED_POPOVER, previous.state === States.KEYBOARD_OPEN, elementOffset); + 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.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0); } - - console.log(111, 0, previous.state === States.KEYBOARD_CLOSED_POPOVER, previous.state === States.KEYBOARD_OPEN, elementOffset); return withSpring(0, SPRING_CONFIG); } case States.POPOVER_CLOSED: { - console.log(112, 0); return withSpring(0, SPRING_CONFIG, () => { transition({ type: Actions.END_TRANSITION, @@ -149,37 +147,30 @@ function ActionSheetKeyboardSpace(props: ViewProps) { case States.POPOVER_OPEN: { if (popoverHeight) { if (previousElementOffset !== 0 || elementOffset > previousElementOffset) { - console.log(113, elementOffset < 0 ? 0 : elementOffset, elementOffset); return withSpring(elementOffset < 0 ? 0 : elementOffset, SPRING_CONFIG); } - console.log(114, Math.max(previousElementOffset, 0)); return withSpring(Math.max(previousElementOffset, 0), SPRING_CONFIG); } - console.log(115, 0); return 0; } case States.KEYBOARD_POPOVER_OPEN: { if (keyboard.state.value === KeyboardState.OPEN) { - console.log(129, 0); - return 0; + return withSpring(0, SPRING_CONFIG); } const nextOffset = elementOffset + lastKeyboardHeight; if (keyboard.state.value === KeyboardState.CLOSED && nextOffset > invertedKeyboardHeight) { - console.log(130, nextOffset < 0 ? 0 : nextOffset, nextOffset); return withSpring(nextOffset < 0 ? 0 : nextOffset, SPRING_CONFIG); } if (elementOffset < 0) { - console.log(131, lastKeyboardHeight - keyboardHeight); - return lastKeyboardHeight - keyboardHeight; + return isClosingKeyboard ? 0 : lastKeyboardHeight - keyboardHeight; } - console.log(132, lastKeyboardHeight, lastKeyboardHeight - keyboardHeight, keyboardHeight); return lastKeyboardHeight; } @@ -187,28 +178,23 @@ function ActionSheetKeyboardSpace(props: ViewProps) { if (elementOffset < 0) { transition({type: Actions.END_TRANSITION}); - console.log(133, 0); return 0; } if (keyboard.state.value === KeyboardState.CLOSED) { - console.log(134, elementOffset + lastKeyboardHeight); return elementOffset + lastKeyboardHeight; } if (keyboard.height.value > 0) { - console.log(135, keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset, 'elementOffset', elementOffset); return keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset; } - console.log(136, elementOffset + lastKeyboardHeight); return withTiming(elementOffset + lastKeyboardHeight, { duration: 0, }); } default: - console.log(138, 0); return 0; } }, []); From 9e25d7f232055ef17018a7e8ab989c0dc2c5f944 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 16 Sep 2024 17:41:38 +0200 Subject: [PATCH 37/64] clean STATE_MACHINE --- .../ActionSheetAwareScrollViewContext.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index ded1719fcb35..4f56185d64da 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -98,7 +98,6 @@ const STATE_MACHINE = { }, [States.KEYBOARD_POPOVER_OPEN]: { [Actions.MEASURE_POPOVER]: States.KEYBOARD_POPOVER_OPEN, - [Actions.MEASURE_COMPOSER]: States.KEYBOARD_POPOVER_OPEN, [Actions.CLOSE_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, }, [States.CALL_POPOVER_WITH_KEYBOARD_OPEN]: { From 2eb8595e9e7fa347fbe643aedbb5d557bdb00568 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 16 Sep 2024 21:23:31 +0200 Subject: [PATCH 38/64] lint --- .../ActionSheetKeyboardSpace.tsx | 7 ++++++- src/components/AttachmentPicker/types.ts | 1 - src/components/EmojiPicker/EmojiPickerButton.tsx | 2 +- .../HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx | 1 - src/components/PopoverMenu.tsx | 9 +++++---- src/hooks/useWorkletStateMachine.ts | 1 + .../report/ContextMenu/BaseReportActionContextMenu.tsx | 2 +- src/pages/home/report/ContextMenu/ContextMenuActions.tsx | 3 +-- .../report/ReportActionCompose/ReportActionCompose.tsx | 6 ------ src/pages/home/report/ReportActionItem.tsx | 1 - 10 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 2667bf3a8260..afb4f674438e 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -32,8 +32,10 @@ const useAnimatedKeyboard = () => { { onStart: (e) => { 'worklet'; + // save the last keyboard height if (e.height !== 0) { + // eslint-disable-next-line react-compiler/react-compiler heightWhenOpened.value = e.height; height.value = 0; } @@ -43,10 +45,12 @@ const useAnimatedKeyboard = () => { }, onMove: (e) => { 'worklet'; + height.value = e.height; }, onEnd: (e) => { 'worklet'; + state.value = e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED; height.value = e.height; }, @@ -87,6 +91,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { if (lastState === syncLocalWorkletStateL.lastState) { return; } + // eslint-disable-next-line react-compiler/react-compiler syncLocalWorkletStateL.value = lastState; if (lastState === KeyboardState.OPEN) { @@ -110,7 +115,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const keyboardHeight = keyboard.height.value === 0 ? 0 : keyboard.height.value - safeArea.bottom; // sometimes we need to know the last keyboard height const lastKeyboardHeight = keyboard.heightWhenOpened.value - safeArea.bottom; - const {popoverHeight = 0, fy, height, composerHeight = 0} = current.payload ?? {}; + const {popoverHeight = 0, fy, height} = current.payload ?? {}; const invertedKeyboardHeight = keyboard.state.value === KeyboardState.CLOSED ? lastKeyboardHeight : 0; const elementOffset = fy !== undefined && height !== undefined && popoverHeight !== undefined ? fy + safeArea.top + height - (windowHeight - popoverHeight) : 0; // when the sate is not idle we know for sure we have previous state diff --git a/src/components/AttachmentPicker/types.ts b/src/components/AttachmentPicker/types.ts index 63ed27408eea..057ec72de27e 100644 --- a/src/components/AttachmentPicker/types.ts +++ b/src/components/AttachmentPicker/types.ts @@ -1,5 +1,4 @@ import type {ReactNode} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; import type {ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; import type CONST from '@src/CONST'; diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 34133b88c11a..4ec90caa2e26 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -40,7 +40,7 @@ function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shi const {translate} = useLocalize(); const isFocused = useIsFocused(); - const openEmojiPicker = (e) => { + const openEmojiPicker = (e: GestureResponderEvent | KeyboardEvent) => { if (!isFocused) { return; } diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx index 694959d1f9de..b7c428e72f29 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer.tsx @@ -40,7 +40,6 @@ function PreRenderer({TDefaultRenderer, onPressIn, onPressOut, onLongPress, ...d onPressIn={onPressIn} onPressOut={onPressOut} onLongPress={(event) => { - console.log(111, 0); onShowContextMenu(() => { if (isDisabled) { return; diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 0f9b29ecd971..b950f9b1e244 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -1,9 +1,8 @@ import lodashIsEqual from 'lodash/isEqual'; import type {RefObject} from 'react'; import React, {useLayoutEffect, useState} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; +import type {LayoutChangeEvent, View} from 'react-native'; import {StyleSheet} from 'react-native'; -import type {View} from 'react-native'; import type {ModalProps} from 'react-native-modal'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; @@ -263,8 +262,10 @@ function PopoverMenu({ restoreFocusType={restoreFocusType} > - + {renderHeaderText()} {enteredSubMenuIndexes.length > 0 && renderBackButtonItem()} {currentMenuItems.map((item, menuIndex) => ( diff --git a/src/hooks/useWorkletStateMachine.ts b/src/hooks/useWorkletStateMachine.ts index b9c02faa7bd1..105814c094eb 100644 --- a/src/hooks/useWorkletStateMachine.ts +++ b/src/hooks/useWorkletStateMachine.ts @@ -146,6 +146,7 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta 'worklet'; log('RESET STATE MACHINE'); + // eslint-disable-next-line react-compiler/react-compiler currentState.value = initialState; }, [currentState, initialState, log]); diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 72975b8193ea..bc48f0b966a2 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -5,8 +5,8 @@ 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 * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import {useOnyx, withOnyx} 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'; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 9b0a2507b6a6..9dd6ca30f5be 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -6,7 +6,6 @@ import {InteractionManager} from 'react-native'; import type {GestureResponderEvent, Text, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {Emoji} from '@assets/emojis/types'; -import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import * as Expensicons from '@components/Icon/Expensicons'; import MiniQuickEmojiReactions from '@components/Reactions/MiniQuickEmojiReactions'; import QuickEmojiReactions from '@components/Reactions/QuickEmojiReactions'; @@ -34,7 +33,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Download as DownloadOnyx, OnyxInputOrEntry, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; -import {clearActiveReportAction, hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; +import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; /** Gets the HTML version of the message in an action */ diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index e6164bf65cc1..25b209786f0f 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -335,12 +335,6 @@ function ReportActionCompose({ [], ); - useEffect(() => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: isMenuVisible ? ActionSheetAwareScrollView.Actions.SHOW_ATTACHMENTS_POPOVER : ActionSheetAwareScrollView.Actions.CLOSE_ATTACHMENTS_POPOVER, - }); - }, [actionSheetAwareScrollViewContext, isMenuVisible]); - useEffect(() => { const unsubscribe = navigation.addListener('blur', () => { setShouldHideEducationalTooltip(true); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 8af1fd33c63b..649f6d774eef 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -17,7 +17,6 @@ import KYCWall from '@components/KYCWall'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {useBlockedFromConcierge, usePersonalDetails} from '@components/OnyxProvider'; import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction'; -import type {OpenPickerCallback} from '@components/Reactions/QuickEmojiReactions/types'; import ReportActionItemEmojiReactions from '@components/Reactions/ReportActionItemEmojiReactions'; import RenderHTML from '@components/RenderHTML'; import type {ActionableItem} from '@components/ReportActionItem/ActionableItemButtons'; From c4cb4cb08c0edc598aba0337864473a3b812471f Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 28 Oct 2024 13:46:08 +0100 Subject: [PATCH 39/64] fix: ci checks --- .../ActionSheetKeyboardSpace.tsx | 2 +- src/components/EmojiPicker/EmojiPickerButton.tsx | 6 +++--- .../MoneyRequestConfirmationListFooter.tsx | 1 + src/components/SelectionList/ChatListItem.tsx | 1 + src/pages/TransactionDuplicate/Confirmation.tsx | 1 + src/pages/home/report/ReportActionItem.tsx | 13 ++++++++++++- 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index afb4f674438e..55ad0877f31b 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -88,7 +88,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { useAnimatedReaction( () => keyboard.state.value, (lastState) => { - if (lastState === syncLocalWorkletStateL.lastState) { + if (lastState === syncLocalWorkletStateL.value) { return; } // eslint-disable-next-line react-compiler/react-compiler diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 6182d10c88bd..9b64467c9716 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -1,8 +1,8 @@ import {useIsFocused} from '@react-navigation/native'; 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'; @@ -20,7 +20,7 @@ type EmojiPickerButtonProps = { emojiPickerID?: string; /** A callback function when the button is pressed */ - onPress?: (event?: GestureResponderEvent | KeyboardEvent) => void; + onPress?: PressableProps['onPress']; /** Emoji popup anchor offset shift vertical */ shiftVertical?: number; @@ -37,7 +37,7 @@ function EmojiPickerButton({isDisabled = false, emojiPickerID = '', shiftVertica const {translate} = useLocalize(); const isFocused = useIsFocused(); - const openEmojiPicker = (e: GestureResponderEvent | KeyboardEvent) => { + const openEmojiPicker: PressableProps['onPress'] = (e) => { if (!isFocused) { return; } diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index dcfe72369651..9f8dc76cf1ca 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -285,6 +285,7 @@ function MoneyRequestConfirmationListFooter({ reportNameValuePairs: undefined, action: undefined, checkIfContextMenuActive: () => {}, + onShowContextMenu: () => {}, isDisabled: true, }), [], diff --git a/src/components/SelectionList/ChatListItem.tsx b/src/components/SelectionList/ChatListItem.tsx index 52b42b0c64dd..03071a45be8f 100644 --- a/src/components/SelectionList/ChatListItem.tsx +++ b/src/components/SelectionList/ChatListItem.tsx @@ -49,6 +49,7 @@ function ChatListItem({ action: undefined, transactionThreadReport: undefined, checkIfContextMenuActive: () => {}, + onShowContextMenu: () => {}, isDisabled: true, }; diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 87748a9697a7..5a67fc03fff2 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -68,6 +68,7 @@ function Confirmation() { action: reportAction, report, checkIfContextMenuActive: () => {}, + onShowContextMenu: () => {}, reportNameValuePairs: undefined, anchor: null, isDisabled: false, diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index f9acddb550cf..1abb35916889 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -391,7 +391,18 @@ function ReportActionItem({ ); }); }, - [draftMessage, action, reportID, toggleContextMenuFromActiveReportAction, originalReportID, shouldDisplayContextMenu, disabledActions, isArchivedRoom, isChronosReport], + [ + draftMessage, + action, + reportID, + toggleContextMenuFromActiveReportAction, + originalReportID, + shouldDisplayContextMenu, + disabledActions, + isArchivedRoom, + isChronosReport, + handleShowContextMenu, + ], ); // Handles manual scrolling to the bottom of the chat when the last message is an actionable whisper and it's resolved. From 9d271ae485c938de5e3745eef202ba4270ed576d Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 28 Oct 2024 14:02:19 +0100 Subject: [PATCH 40/64] fix: don't mock keyboard-controller twice --- jest/setup.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/jest/setup.ts b/jest/setup.ts index 093435971929..7dbe91c32fda 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -64,9 +64,6 @@ jest.mock('react-native-sound', () => { return SoundMock; }); -// eslint-disable-next-line @typescript-eslint/no-unsafe-return -jest.mock('react-native-keyboard-controller', () => require('react-native-keyboard-controller/jest')); - jest.mock('react-native-share', () => ({ default: jest.fn(), })); From 31ba00b22e78526e6e8d54866e9ed619f98b27fe Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 28 Oct 2024 15:43:47 +0100 Subject: [PATCH 41/64] fix: button jumps in Display Name page --- src/components/ScreenWrapper.tsx | 7 +++++ src/hooks/useSafePaddingBottomStyle.ts | 30 +++++++------------ .../settings/Profile/DisplayNamePage.tsx | 1 + 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 3645b832ed43..203217b0b1f4 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -11,6 +11,7 @@ import useInitialDimensions from '@hooks/useInitialWindowDimensions'; import useKeyboardState from '@hooks/useKeyboardState'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import {useSafePaddingBottomValue} from '@hooks/useSafePaddingBottomStyle'; import useTackInputFocus from '@hooks/useTackInputFocus'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -69,6 +70,9 @@ type ScreenWrapperProps = { /** Whether KeyboardAvoidingView should be enabled. Use false for screens where this functionality is not necessary */ shouldEnableKeyboardAvoidingView?: boolean; + /** Whether to remove the safe padding bottom (produced by `useSafePaddingBottomStyle`) when the keyboard is shown */ + shouldRemoveSafePaddingBottomWhenKeyboardShown?: boolean; + /** Whether picker modal avoiding should be enabled. Should be enabled when there's a picker at the bottom of a * scrollable form, gives a subtly better UX if disabled on non-scrollable screens with a submit button */ shouldEnablePickerAvoiding?: boolean; @@ -119,6 +123,7 @@ function ScreenWrapper( includeSafeAreaPaddingBottom = true, shouldEnableKeyboardAvoidingView = true, shouldEnablePickerAvoiding = true, + shouldRemoveSafePaddingBottomWhenKeyboardShown = false, headerGapStyles, children, shouldShowOfflineIndicator = true, @@ -152,6 +157,7 @@ function ScreenWrapper( const {isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {initialHeight} = useInitialDimensions(); const styles = useThemeStyles(); + const safePaddingBottom = useSafePaddingBottomValue(); const keyboardState = useKeyboardState(); const {isDevelopment} = useEnvironment(); const {isOffline} = useNetwork(); @@ -271,6 +277,7 @@ function ScreenWrapper( style={[styles.w100, styles.h100, {maxHeight}, isAvoidingViewportScroll ? [styles.overflowAuto, styles.overscrollBehaviorContain] : {}]} behavior={keyboardAvoidingViewBehavior} enabled={shouldEnableKeyboardAvoidingView} + keyboardVerticalOffset={shouldRemoveSafePaddingBottomWhenKeyboardShown ? -safePaddingBottom : undefined} > { +const useSafePaddingBottomStyle = (): ViewStyle => { const styles = useThemeStyles(); - const [willKeyboardShow, setWillKeyboardShow] = useState(false); - useEffect(() => { - const keyboardWillShowListener = Keyboard.addListener('keyboardWillShow', () => { - setWillKeyboardShow(true); - }); - const keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', () => { - setWillKeyboardShow(false); - }); - return () => { - keyboardWillShowListener.remove(); - keyboardWillHideListener.remove(); - }; - }, []); - const {paddingBottom} = useStyledSafeAreaInsets(); const extraPaddingBottomStyle = useMemo(() => { // Do not add extra padding at the bottom if the keyboard is open or if there is no safe area bottom padding style. - if (willKeyboardShow || !paddingBottom) { + if (!paddingBottom) { return {}; } return styles.pb5; - }, [willKeyboardShow, paddingBottom, styles.pb5]); + }, [paddingBottom, styles.pb5]); return extraPaddingBottomStyle; }; +const useSafePaddingBottomValue = () => { + const style = useSafePaddingBottomStyle(); + + return style.paddingBottom ?? 0; +}; +export {useSafePaddingBottomValue}; export default useSafePaddingBottomStyle; diff --git a/src/pages/settings/Profile/DisplayNamePage.tsx b/src/pages/settings/Profile/DisplayNamePage.tsx index 90f7ca3abbd6..c26aa3f92793 100644 --- a/src/pages/settings/Profile/DisplayNamePage.tsx +++ b/src/pages/settings/Profile/DisplayNamePage.tsx @@ -71,6 +71,7 @@ function DisplayNamePage({isLoadingApp = true, currentUserPersonalDetails}: Disp return ( From 491c481a0c1d9049615d1f93c9cdb92c6238c3da Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 28 Oct 2024 16:16:55 +0100 Subject: [PATCH 42/64] fix: safe area glitch on workspace selection screen --- src/components/SelectionList/BaseSelectionList.tsx | 3 ++- src/components/SelectionList/types.ts | 3 +++ src/pages/WorkspaceSwitcherPage/index.tsx | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 57423992e43e..ae9242d47084 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -106,6 +106,7 @@ function BaseSelectionList( scrollEventThrottle, contentContainerStyle, shouldHighlightSelectedItem = false, + shouldHandleSafeAreaPaddings = true, }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -671,7 +672,7 @@ function BaseSelectionList( return ( {({safeAreaPaddingBottomStyle}) => ( - + {shouldShowTextInput && ( = Partial & { /** Whether we highlight all the selected items */ shouldHighlightSelectedItem?: boolean; + + /** Whether safe area paddings should be handled */ + shouldHandleSafeAreaPaddings?: boolean; } & TRightHandSideComponent; type SelectionListHandle = { diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index 221889b80b49..f3e67f73d713 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -159,7 +159,7 @@ function WorkspaceSwitcherPage() { return ( {({didScreenTransitionEnd}) => ( <> @@ -196,6 +196,7 @@ function WorkspaceSwitcherPage() { shouldShowListEmptyContent={shouldShowCreateWorkspace} initiallyFocusedOptionKey={activeWorkspaceID ?? CONST.WORKSPACE_SWITCHER.NAME} showLoadingPlaceholder={fetchStatus.status === 'loading' || !didScreenTransitionEnd} + shouldHandleSafeAreaPaddings={false} /> )} From ec57d6f4aae6e825263a6f6d6f83a010ef4896cc Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 28 Oct 2024 18:09:48 +0100 Subject: [PATCH 43/64] fix: emoji picker transition --- src/components/EmojiPicker/EmojiPickerButton.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 9b64467c9716..a10e7d9fd1f3 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -1,5 +1,6 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {memo, useEffect, useRef} from 'react'; +import React, {memo, useContext, useEffect, useRef} from 'react'; +import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import type PressableProps from '@components/Pressable/GenericPressable/types'; @@ -31,6 +32,7 @@ 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); @@ -42,6 +44,10 @@ function EmojiPickerButton({isDisabled = false, emojiPickerID = '', shiftVertica return; } + actionSheetContext.transitionActionSheetState({ + type: ActionSheetAwareScrollView.Actions.CLOSE_KEYBOARD, + }); + if (!EmojiPickerAction.emojiPickerRef?.current?.isEmojiPickerVisible) { EmojiPickerAction.showEmojiPicker( onModalHide, From b6073e408b9774078315e7ad07664035de039617 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 29 Oct 2024 14:24:01 +0100 Subject: [PATCH 44/64] fix: random jump when keyboard closes --- .../ActionSheetKeyboardSpace.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 55ad0877f31b..47dd173e8e8d 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -1,7 +1,7 @@ import React, {useContext, useEffect} from 'react'; import type {ViewProps} from 'react-native'; import {useKeyboardHandler} from 'react-native-keyboard-controller'; -import Reanimated, {runOnJS, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring, withTiming} from 'react-native-reanimated'; +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'; @@ -75,9 +75,9 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const keyboard = useAnimatedKeyboard(); // similar to using `global` in worklet but it's just a local object - const syncLocalWorkletStateL = useSharedValue(KeyboardState.UNKNOWN); + const syncLocalWorkletState = useSharedValue(KeyboardState.UNKNOWN); const {windowHeight} = useWindowDimensions(); - const {currentActionSheetState, transitionActionSheetStateWorklet: transition, transitionActionSheetState, resetStateMachine} = useContext(ActionSheetAwareScrollViewContext); + const {currentActionSheetState, transitionActionSheetStateWorklet: transition, resetStateMachine} = useContext(ActionSheetAwareScrollViewContext); // Reset state machine when component unmounts // eslint-disable-next-line arrow-body-style @@ -88,16 +88,16 @@ function ActionSheetKeyboardSpace(props: ViewProps) { useAnimatedReaction( () => keyboard.state.value, (lastState) => { - if (lastState === syncLocalWorkletStateL.value) { + if (lastState === syncLocalWorkletState.value) { return; } // eslint-disable-next-line react-compiler/react-compiler - syncLocalWorkletStateL.value = lastState; + syncLocalWorkletState.value = lastState; if (lastState === KeyboardState.OPEN) { - runOnJS(transitionActionSheetState)({type: Actions.OPEN_KEYBOARD}); + transition({type: Actions.OPEN_KEYBOARD}); } else if (lastState === KeyboardState.CLOSED) { - runOnJS(transitionActionSheetState)({type: Actions.CLOSE_KEYBOARD}); + transition({type: Actions.CLOSE_KEYBOARD}); } }, [], @@ -125,9 +125,9 @@ function ActionSheetKeyboardSpace(props: ViewProps) { ? previousPayload.fy + safeArea.top + previousPayload.height - (windowHeight - previousPayload.popoverHeight) : 0; - const isOpeningKeyboard = syncLocalWorkletStateL.value === 1; - const isClosingKeyboard = syncLocalWorkletStateL.value === 3; - const isClosedKeyboard = syncLocalWorkletStateL.value === 4; + const isOpeningKeyboard = syncLocalWorkletState.value === 1; + const isClosingKeyboard = syncLocalWorkletState.value === 3; + const isClosedKeyboard = syncLocalWorkletState.value === 4; // Depending on the current and sometimes previous state we can return // either animation or just a value switch (current.state) { From d186f7f68418606610c2c0c6058e5dfb720bada0 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 29 Oct 2024 15:11:46 +0100 Subject: [PATCH 45/64] fix: eslint --- src/pages/WorkspaceSwitcherPage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index f3e67f73d713..7775f1f0a635 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -159,7 +159,7 @@ function WorkspaceSwitcherPage() { return ( {({didScreenTransitionEnd}) => ( <> From 3ef967ffbeaf08b124ad7a8d9aebb8a41aabad4d Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 31 Oct 2024 14:21:22 +0100 Subject: [PATCH 46/64] fix: bottom sheet avoidance when keyboard is hidden --- src/components/PopoverWithMeasuredContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index 725f07eb24a7..f032fbd1bbbf 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -98,7 +98,7 @@ function PopoverWithMeasuredContent({ // 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.value.current.payload?.popoverHeight !== Math.floor(popoverHeight)) { + if (actionSheetAwareScrollViewContext.currentActionSheetState.value.current.payload?.popoverHeight !== Math.floor(height) && height !== 0) { actionSheetAwareScrollViewContext.transitionActionSheetState({ type: ActionSheetAwareScrollView.Actions.MEASURE_POPOVER, payload: { From e6de40d7845f2cddcd30f7852aff63982b69687d Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 31 Oct 2024 17:12:15 +0100 Subject: [PATCH 47/64] fix: random transitions when popover gets closed --- .../ActionSheetAwareScrollViewContext.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index 4f56185d64da..00b662aaba8c 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -99,6 +99,7 @@ const STATE_MACHINE = { [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.CALL_POPOVER_WITH_KEYBOARD_OPEN]: { [Actions.MEASURE_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, From cb5d8fa9805c0d6749e914d466ea276645de478d Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 19 Nov 2024 10:52:46 +0100 Subject: [PATCH 48/64] fix: typescript checks --- .../ActionSheetAwareScrollViewContext.tsx | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index 00b662aaba8c..133afbacbd72 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -20,21 +20,24 @@ type Context = { resetStateMachine: () => void; }; +const currentActionSheetStateValue = { + previous: { + state: 'idle', + payload: null, + }, + current: { + state: 'idle', + payload: null, + }, +}; const defaultValue: Context = { currentActionSheetState: { - value: { - previous: { - state: 'idle', - payload: null, - }, - current: { - state: 'idle', - payload: null, - }, - }, + value: currentActionSheetStateValue, addListener: noop, removeListener: noop, modify: noop, + get: () => currentActionSheetStateValue, + set: noop, }, transitionActionSheetState: noop, transitionActionSheetStateWorklet: noop, From f375a4f2b2e6491a4884c769ffa75e418379cdc9 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Fri, 22 Nov 2024 13:53:30 +0100 Subject: [PATCH 49/64] fix: web project crashes --- .../executeOnUIRuntimeSync/index.native.ts | 3 +++ .../useWorkletStateMachine/executeOnUIRuntimeSync/index.ts | 3 +++ .../index.ts} | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts create mode 100644 src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.ts rename src/hooks/{useWorkletStateMachine.ts => useWorkletStateMachine/index.ts} (97%) diff --git a/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts b/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts new file mode 100644 index 000000000000..eab78097aa05 --- /dev/null +++ b/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.native.ts @@ -0,0 +1,3 @@ +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 new file mode 100644 index 000000000000..3bc8059d8762 --- /dev/null +++ b/src/hooks/useWorkletStateMachine/executeOnUIRuntimeSync/index.ts @@ -0,0 +1,3 @@ +import {runOnUI} from 'react-native-reanimated'; + +export default runOnUI; diff --git a/src/hooks/useWorkletStateMachine.ts b/src/hooks/useWorkletStateMachine/index.ts similarity index 97% rename from src/hooks/useWorkletStateMachine.ts rename to src/hooks/useWorkletStateMachine/index.ts index 105814c094eb..dcedf002fc15 100644 --- a/src/hooks/useWorkletStateMachine.ts +++ b/src/hooks/useWorkletStateMachine/index.ts @@ -1,6 +1,7 @@ import {useCallback} from 'react'; -import {executeOnUIRuntimeSync, runOnJS, runOnUI, useSharedValue} from 'react-native-reanimated'; +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; From 4e7d9316e9a2626514043c5f373b54082db8a863 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Fri, 22 Nov 2024 14:03:50 +0100 Subject: [PATCH 50/64] fix: prettier --- src/pages/home/report/ReportActionItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 1e0f4140a6c8..1f1d5e0fdf4f 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -370,7 +370,7 @@ function ReportActionItem({ } handleShowContextMenu(() => { - setIsContextMenuActive(true); + setIsContextMenuActive(true); const selection = SelectionScraper.getCurrentSelection(); ReportActionContextMenu.showContextMenu( CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, From b48a247a6f20a0eec11c4e73bd19af604c9ec867 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Fri, 22 Nov 2024 14:58:05 +0100 Subject: [PATCH 51/64] fix: long press of a video attachment does not push up the video message --- .../VideoPlayerPreview/VideoPlayerThumbnail.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx b/src/components/VideoPlayerPreview/VideoPlayerThumbnail.tsx index 832b5eef45f0..e1c1a000d9bd 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}) => ( + {({anchor, report, reportNameValuePairs, action, checkIfContextMenuActive, isDisabled, onShowContextMenu}) => ( { + showContextMenuForReport( + event, + anchor, + report?.reportID ?? '-1', + action, + checkIfContextMenuActive, + ReportUtils.isArchivedRoom(report, reportNameValuePairs), + ); + }); }} shouldUseHapticsOnLongPress > From c5724043e2904f672bcb703cf520f9803ef0774d Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 26 Nov 2024 11:36:04 +0100 Subject: [PATCH 52/64] fix: wrong displayName for ActionSheetKeyboardSpace --- .../ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 47dd173e8e8d..88aba48890c9 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -217,6 +217,6 @@ function ActionSheetKeyboardSpace(props: ViewProps) { ); } -ActionSheetKeyboardSpace.displayName = 'ReportKeyboardSpace'; +ActionSheetKeyboardSpace.displayName = 'ActionSheetKeyboardSpace'; export default ActionSheetKeyboardSpace; From f93a72012ca0838c98311e13efde50f9508da4b1 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 26 Nov 2024 11:41:41 +0100 Subject: [PATCH 53/64] fix: remove dead code --- .../ActionSheetAwareScrollViewContext.tsx | 14 -------------- src/components/ThreeDotsMenu/index.tsx | 12 ++---------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index 133afbacbd72..02b8b4c1f97b 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -56,9 +56,6 @@ const Actions = { POPOVER_ANY_ACTION: 'POPOVER_ANY_ACTION', HIDE_WITHOUT_ANIMATION: 'HIDE_WITHOUT_ANIMATION', END_TRANSITION: 'END_TRANSITION', - OPEN_CALL_POPOVER: 'OPEN_CALL_POPOVER', - MEASURE_CALL_POPOVER: 'MEASURE_CALL_POPOVER', - CLOSE_CALL_POPOVER: 'CLOSE_CALL_POPOVER', }; const States = { @@ -70,8 +67,6 @@ const States = { KEYBOARD_POPOVER_OPEN: 'keyboardPopoverOpen', KEYBOARD_CLOSED_POPOVER: 'keyboardClosingPopover', POPOVER_MEASURED: 'popoverMeasured', - CALL_POPOVER_WITH_KEYBOARD_OPEN: 'callPopoverWithKeyboardOpen', - CALL_POPOVER_WITH_KEYBOARD_CLOSED: 'callPopoverWithKeyboardClosed', MODAL_WITH_KEYBOARD_OPEN_DELETED: 'modalWithKeyboardOpenDeleted', }; @@ -96,7 +91,6 @@ const STATE_MACHINE = { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, [Actions.OPEN_POPOVER]: States.KEYBOARD_POPOVER_OPEN, [Actions.CLOSE_KEYBOARD]: States.IDLE, - [Actions.OPEN_CALL_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, [Actions.MEASURE_COMPOSER]: States.KEYBOARD_OPEN, }, [States.KEYBOARD_POPOVER_OPEN]: { @@ -104,14 +98,6 @@ const STATE_MACHINE = { [Actions.CLOSE_POPOVER]: States.KEYBOARD_CLOSED_POPOVER, [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, }, - [States.CALL_POPOVER_WITH_KEYBOARD_OPEN]: { - [Actions.MEASURE_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.MEASURE_CALL_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_OPEN, - [Actions.CLOSE_CALL_POPOVER]: States.CALL_POPOVER_WITH_KEYBOARD_CLOSED, - }, - [States.CALL_POPOVER_WITH_KEYBOARD_CLOSED]: { - [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, - }, [States.KEYBOARD_POPOVER_CLOSED]: { [Actions.OPEN_KEYBOARD]: States.KEYBOARD_OPEN, }, diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index 1efba8f49d0f..bc062fffd787 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -1,7 +1,6 @@ -import React, {useCallback, useContext, useEffect, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; -import {Actions, ActionSheetAwareScrollViewContext} from '@components/ActionSheetAwareScrollView'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PopoverMenu from '@components/PopoverMenu'; @@ -31,7 +30,6 @@ function ThreeDotsMenu({ shouldSetModalVisibility = true, disabled = false, }: ThreeDotsMenuProps) { - const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollViewContext); const [modal] = useOnyx(ONYXKEYS.MODAL); const theme = useTheme(); @@ -42,18 +40,12 @@ function ThreeDotsMenu({ const isBehindModal = modal?.willAlertModalBecomeVisible && !modal?.isPopover && !shouldOverlay; const showPopoverMenu = () => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: Actions.OPEN_CALL_POPOVER, - }); setPopupMenuVisible(true); }; const hidePopoverMenu = useCallback(() => { - actionSheetAwareScrollViewContext.transitionActionSheetState({ - type: Actions.CLOSE_CALL_POPOVER, - }); setPopupMenuVisible(false); - }, [actionSheetAwareScrollViewContext]); + }, []); useEffect(() => { if (!isBehindModal || !isPopupMenuVisible) { From a415b2b273606172fbff8844650681bf50adb4bd Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 26 Nov 2024 13:32:34 +0100 Subject: [PATCH 54/64] fix: revern changes that produces issue on many screens --- src/components/ScreenWrapper.tsx | 7 ----- src/hooks/useSafePaddingBottomStyle.ts | 29 ++++++++++++------- .../settings/Profile/DisplayNamePage.tsx | 1 - 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 3836534c9c27..c74ccf0470d0 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -11,7 +11,6 @@ import useInitialDimensions from '@hooks/useInitialWindowDimensions'; import useKeyboardState from '@hooks/useKeyboardState'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import {useSafePaddingBottomValue} from '@hooks/useSafePaddingBottomStyle'; import useTackInputFocus from '@hooks/useTackInputFocus'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -69,9 +68,6 @@ type ScreenWrapperProps = { /** Whether KeyboardAvoidingView should be enabled. Use false for screens where this functionality is not necessary */ shouldEnableKeyboardAvoidingView?: boolean; - /** Whether to remove the safe padding bottom (produced by `useSafePaddingBottomStyle`) when the keyboard is shown */ - shouldRemoveSafePaddingBottomWhenKeyboardShown?: boolean; - /** Whether picker modal avoiding should be enabled. Should be enabled when there's a picker at the bottom of a * scrollable form, gives a subtly better UX if disabled on non-scrollable screens with a submit button */ shouldEnablePickerAvoiding?: boolean; @@ -122,7 +118,6 @@ function ScreenWrapper( includeSafeAreaPaddingBottom = true, shouldEnableKeyboardAvoidingView = true, shouldEnablePickerAvoiding = true, - shouldRemoveSafePaddingBottomWhenKeyboardShown = false, headerGapStyles, children, shouldShowOfflineIndicator = true, @@ -156,7 +151,6 @@ function ScreenWrapper( const {isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {initialHeight} = useInitialDimensions(); const styles = useThemeStyles(); - const safePaddingBottom = useSafePaddingBottomValue(); const keyboardState = useKeyboardState(); const {isDevelopment} = useEnvironment(); const {isOffline} = useNetwork(); @@ -285,7 +279,6 @@ function ScreenWrapper( style={[styles.w100, styles.h100, {maxHeight}, isAvoidingViewportScroll ? [styles.overflowAuto, styles.overscrollBehaviorContain] : {}]} behavior={keyboardAvoidingViewBehavior} enabled={shouldEnableKeyboardAvoidingView} - keyboardVerticalOffset={shouldRemoveSafePaddingBottomWhenKeyboardShown ? -safePaddingBottom : undefined} > { +const useSafePaddingBottomStyle = () => { const styles = useThemeStyles(); + const [willKeyboardShow, setWillKeyboardShow] = useState(false); + useEffect(() => { + const keyboardWillShowListener = KeyboardEvents.addListener('keyboardWillShow', () => { + setWillKeyboardShow(true); + }); + const keyboardWillHideListener = KeyboardEvents.addListener('keyboardWillHide', () => { + setWillKeyboardShow(false); + }); + return () => { + keyboardWillShowListener.remove(); + keyboardWillHideListener.remove(); + }; + }, []); const {paddingBottom} = useStyledSafeAreaInsets(); const extraPaddingBottomStyle = useMemo(() => { // Do not add extra padding at the bottom if the keyboard is open or if there is no safe area bottom padding style. - if (!paddingBottom) { + if (willKeyboardShow || !paddingBottom) { return {}; } return styles.pb5; - }, [paddingBottom, styles.pb5]); + }, [willKeyboardShow, paddingBottom, styles.pb5]); return extraPaddingBottomStyle; }; -const useSafePaddingBottomValue = () => { - const style = useSafePaddingBottomStyle(); - return style.paddingBottom ?? 0; -}; - -export {useSafePaddingBottomValue}; export default useSafePaddingBottomStyle; diff --git a/src/pages/settings/Profile/DisplayNamePage.tsx b/src/pages/settings/Profile/DisplayNamePage.tsx index 6ce7122c0d50..4c6211bc3e37 100644 --- a/src/pages/settings/Profile/DisplayNamePage.tsx +++ b/src/pages/settings/Profile/DisplayNamePage.tsx @@ -67,7 +67,6 @@ function DisplayNamePage({currentUserPersonalDetails}: DisplayNamePageProps) { return ( From fd87b027458e0140e39cfaa21f9457bdedf7375e Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 28 Nov 2024 12:00:50 +0100 Subject: [PATCH 55/64] fix: comments suggestions --- .../ActionSheetKeyboardSpace.tsx | 14 ++++++++------ src/hooks/useWorkletStateMachine/index.ts | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index 88aba48890c9..d1892da3c4cc 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -33,7 +33,7 @@ const useAnimatedKeyboard = () => { onStart: (e) => { 'worklet'; - // save the last keyboard height + // Save the last keyboard height if (e.height !== 0) { // eslint-disable-next-line react-compiler/react-compiler heightWhenOpened.value = e.height; @@ -74,7 +74,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const safeArea = useSafeAreaPaddings(); const keyboard = useAnimatedKeyboard(); - // similar to using `global` in worklet but it's just a local object + // 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); @@ -106,19 +106,20 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const translateY = useDerivedValue(() => { const {current, previous} = currentActionSheetState.value; - // we don't need to run any additional logic - // it will always return 0 for idle state + // 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.value === 0 ? 0 : keyboard.height.value - safeArea.bottom; - // sometimes we need to know the last keyboard height + + // Sometimes we need to know the last keyboard height const lastKeyboardHeight = keyboard.heightWhenOpened.value - safeArea.bottom; const {popoverHeight = 0, fy, height} = current.payload ?? {}; const invertedKeyboardHeight = keyboard.state.value === KeyboardState.CLOSED ? lastKeyboardHeight : 0; const elementOffset = fy !== undefined && height !== undefined && popoverHeight !== undefined ? fy + safeArea.top + height - (windowHeight - popoverHeight) : 0; - // when the sate is not idle we know for sure we have previous state + + // 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 @@ -128,6 +129,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { const isOpeningKeyboard = syncLocalWorkletState.value === 1; const isClosingKeyboard = syncLocalWorkletState.value === 3; const isClosedKeyboard = syncLocalWorkletState.value === 4; + // Depending on the current and sometimes previous state we can return // either animation or just a value switch (current.state) { diff --git a/src/hooks/useWorkletStateMachine/index.ts b/src/hooks/useWorkletStateMachine/index.ts index dcedf002fc15..5ce52f41faad 100644 --- a/src/hooks/useWorkletStateMachine/index.ts +++ b/src/hooks/useWorkletStateMachine/index.ts @@ -45,9 +45,9 @@ const client = Log.client; * 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 + * 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. + * 7. We close the picker and send EMOJI_PICKER_CLOSE action which transitions us back into keyboardOpen state. * * State machine object example: * const stateMachine = { From 6140d8fce13b0546eb6f92e32724d4f858f43fa0 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 28 Nov 2024 13:21:16 +0100 Subject: [PATCH 56/64] fix: migrate to new reanimated API --- .../ActionSheetKeyboardSpace.tsx | 51 +++++++++---------- src/hooks/useWorkletStateMachine/index.ts | 8 +-- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx index d1892da3c4cc..e15ac941a09d 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetKeyboardSpace.tsx @@ -35,24 +35,23 @@ const useAnimatedKeyboard = () => { // Save the last keyboard height if (e.height !== 0) { - // eslint-disable-next-line react-compiler/react-compiler - heightWhenOpened.value = e.height; - height.value = 0; + heightWhenOpened.set(e.height); + height.set(0); } - height.value = heightWhenOpened.value; - lastHeight.value = e.height; - state.value = e.height > 0 ? KeyboardState.OPENING : KeyboardState.CLOSING; + height.set(heightWhenOpened.get()); + lastHeight.set(e.height); + state.set(e.height > 0 ? KeyboardState.OPENING : KeyboardState.CLOSING); }, onMove: (e) => { 'worklet'; - height.value = e.height; + height.set(e.height); }, onEnd: (e) => { 'worklet'; - state.value = e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED; - height.value = e.height; + state.set(e.height > 0 ? KeyboardState.OPEN : KeyboardState.CLOSED); + height.set(e.height); }, }, [], @@ -86,13 +85,13 @@ function ActionSheetKeyboardSpace(props: ViewProps) { }, [resetStateMachine]); useAnimatedReaction( - () => keyboard.state.value, + () => keyboard.state.get(), (lastState) => { - if (lastState === syncLocalWorkletState.value) { + if (lastState === syncLocalWorkletState.get()) { return; } // eslint-disable-next-line react-compiler/react-compiler - syncLocalWorkletState.value = lastState; + syncLocalWorkletState.set(lastState); if (lastState === KeyboardState.OPEN) { transition({type: Actions.OPEN_KEYBOARD}); @@ -104,19 +103,19 @@ function ActionSheetKeyboardSpace(props: ViewProps) { ); const translateY = useDerivedValue(() => { - const {current, previous} = currentActionSheetState.value; + 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.value === 0 ? 0 : keyboard.height.value - safeArea.bottom; + 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.value - safeArea.bottom; + const lastKeyboardHeight = keyboard.heightWhenOpened.get() - safeArea.bottom; const {popoverHeight = 0, fy, height} = current.payload ?? {}; - const invertedKeyboardHeight = keyboard.state.value === KeyboardState.CLOSED ? lastKeyboardHeight : 0; + 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 @@ -126,9 +125,9 @@ function ActionSheetKeyboardSpace(props: ViewProps) { ? previousPayload.fy + safeArea.top + previousPayload.height - (windowHeight - previousPayload.popoverHeight) : 0; - const isOpeningKeyboard = syncLocalWorkletState.value === 1; - const isClosingKeyboard = syncLocalWorkletState.value === 3; - const isClosedKeyboard = syncLocalWorkletState.value === 4; + 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 @@ -138,7 +137,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { return lastKeyboardHeight - keyboardHeight; } if (previous.state === States.KEYBOARD_CLOSED_POPOVER || (previous.state === States.KEYBOARD_OPEN && elementOffset < 0)) { - return Math.max(keyboard.heightWhenOpened.value - keyboard.height.value - safeArea.bottom, 0) + Math.max(elementOffset, 0); + return Math.max(keyboard.heightWhenOpened.get() - keyboard.height.get() - safeArea.bottom, 0) + Math.max(elementOffset, 0); } return withSpring(0, SPRING_CONFIG); } @@ -164,13 +163,13 @@ function ActionSheetKeyboardSpace(props: ViewProps) { } case States.KEYBOARD_POPOVER_OPEN: { - if (keyboard.state.value === KeyboardState.OPEN) { + if (keyboard.state.get() === KeyboardState.OPEN) { return withSpring(0, SPRING_CONFIG); } const nextOffset = elementOffset + lastKeyboardHeight; - if (keyboard.state.value === KeyboardState.CLOSED && nextOffset > invertedKeyboardHeight) { + if (keyboard.state.get() === KeyboardState.CLOSED && nextOffset > invertedKeyboardHeight) { return withSpring(nextOffset < 0 ? 0 : nextOffset, SPRING_CONFIG); } @@ -188,12 +187,12 @@ function ActionSheetKeyboardSpace(props: ViewProps) { return 0; } - if (keyboard.state.value === KeyboardState.CLOSED) { + if (keyboard.state.get() === KeyboardState.CLOSED) { return elementOffset + lastKeyboardHeight; } - if (keyboard.height.value > 0) { - return keyboard.heightWhenOpened.value - keyboard.height.value + elementOffset; + if (keyboard.height.get() > 0) { + return keyboard.heightWhenOpened.get() - keyboard.height.get() + elementOffset; } return withTiming(elementOffset + lastKeyboardHeight, { @@ -207,7 +206,7 @@ function ActionSheetKeyboardSpace(props: ViewProps) { }, []); const animatedStyle = useAnimatedStyle(() => ({ - paddingTop: translateY.value, + paddingTop: translateY.get(), })); return ( diff --git a/src/hooks/useWorkletStateMachine/index.ts b/src/hooks/useWorkletStateMachine/index.ts index 5ce52f41faad..f95a43ef2ae8 100644 --- a/src/hooks/useWorkletStateMachine/index.ts +++ b/src/hooks/useWorkletStateMachine/index.ts @@ -98,7 +98,7 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta throw new Error('state machine action is required'); } - const state = currentState.value; + const state = currentState.get(); log(`Current STATE: ${state.current.state}`); log(`Next ACTION: ${action.type}`, action.payload); @@ -132,13 +132,13 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta log(`Next STATE: ${nextState}`, nextPayload); - currentState.value = { + currentState.set({ previous: state.current, current: { state: nextState, payload: nextPayload, }, - }; + }); }, [currentState, log, stateMachine], ); @@ -148,7 +148,7 @@ function useWorkletStateMachine

(stateMachine: StateMachine, initialState: Sta log('RESET STATE MACHINE'); // eslint-disable-next-line react-compiler/react-compiler - currentState.value = initialState; + currentState.set(initialState); }, [currentState, initialState, log]); const reset = useCallback(() => { From 4cb6b6f6d93afd81a2152fb1cf1dcc8a44f70daa Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 28 Nov 2024 13:23:41 +0100 Subject: [PATCH 57/64] fix: last .value usage --- src/components/PopoverWithMeasuredContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index 56ae91c9cf8c..80b9fb1a9564 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -99,7 +99,7 @@ function PopoverWithMeasuredContent({ // 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.value.current.payload?.popoverHeight !== Math.floor(height) && height !== 0) { + if (actionSheetAwareScrollViewContext.currentActionSheetState.get().current.payload?.popoverHeight !== Math.floor(height) && height !== 0) { actionSheetAwareScrollViewContext.transitionActionSheetState({ type: ActionSheetAwareScrollView.Actions.MEASURE_POPOVER, payload: { From 0ca998b1e455d32a6f2fbaf1c609741b66ceeaf7 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Wed, 11 Dec 2024 18:25:35 +0100 Subject: [PATCH 58/64] fix: unit tests (missing reanimated getter in mocks) --- ....16.3+001+mock-useDerivedValue-getter.patch | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 patches/react-native-reanimated+3.16.3+001+mock-useDerivedValue-getter.patch diff --git a/patches/react-native-reanimated+3.16.3+001+mock-useDerivedValue-getter.patch b/patches/react-native-reanimated+3.16.3+001+mock-useDerivedValue-getter.patch new file mode 100644 index 000000000000..972ddeedf67a --- /dev/null +++ b/patches/react-native-reanimated+3.16.3+001+mock-useDerivedValue-getter.patch @@ -0,0 +1,18 @@ +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: { From 522e1dea6b6b924499564c712036036e582af21b Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 16 Dec 2024 17:58:24 +0100 Subject: [PATCH 59/64] chore: update patch --- ...ative-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename patches/{react-native-reanimated+3.16.3+001+mock-useDerivedValue-getter.patch => react-native-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch} (100%) diff --git a/patches/react-native-reanimated+3.16.3+001+mock-useDerivedValue-getter.patch b/patches/react-native-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch similarity index 100% rename from patches/react-native-reanimated+3.16.3+001+mock-useDerivedValue-getter.patch rename to patches/react-native-reanimated+3.16.4+001+mock-useDerivedValue-getter.patch From 542dc7a636d7daf8f0859b91938dd79a1a941163 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 17 Dec 2024 16:18:45 +0100 Subject: [PATCH 60/64] fix: docstring --- .../ActionSheetAwareScrollViewContext.tsx | 1 + src/hooks/useWorkletStateMachine/index.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx index 02b8b4c1f97b..6fd9914c70e1 100644 --- a/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx +++ b/src/components/ActionSheetAwareScrollView/ActionSheetAwareScrollViewContext.tsx @@ -20,6 +20,7 @@ type Context = { resetStateMachine: () => void; }; +/** Holds all information that are needed to coordinate the state value for the action sheet state machine. */ const currentActionSheetStateValue = { previous: { state: 'idle', diff --git a/src/hooks/useWorkletStateMachine/index.ts b/src/hooks/useWorkletStateMachine/index.ts index f95a43ef2ae8..cfaffe968370 100644 --- a/src/hooks/useWorkletStateMachine/index.ts +++ b/src/hooks/useWorkletStateMachine/index.ts @@ -20,6 +20,12 @@ type State

= { 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 From 108444eb694601c13bda46640246b85554305cf6 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 17 Dec 2024 16:24:38 +0100 Subject: [PATCH 61/64] fix: TS --- src/components/KeyboardAvoidingView/index.ios.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/KeyboardAvoidingView/index.ios.tsx b/src/components/KeyboardAvoidingView/index.ios.tsx index 68cfa73e90b5..e81ebd6ff671 100644 --- a/src/components/KeyboardAvoidingView/index.ios.tsx +++ b/src/components/KeyboardAvoidingView/index.ios.tsx @@ -1,9 +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'; -import type {KeyboardAvoidingViewProps} from './types'; function KeyboardAvoidingView(props: KeyboardAvoidingViewProps) { // eslint-disable-next-line react/jsx-props-no-spreading From 6271d734d5ebe74f78e0b6f293c55722f3971797 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 23 Dec 2024 16:04:58 +0100 Subject: [PATCH 62/64] fix: ios stuttering animation --- .../index.android.ts} | 0 src/hooks/useRestoreInputFocus/index.ts | 2 ++ 2 files changed, 2 insertions(+) rename src/hooks/{useRestoreInputFocus.ts => useRestoreInputFocus/index.android.ts} (100%) create mode 100644 src/hooks/useRestoreInputFocus/index.ts diff --git a/src/hooks/useRestoreInputFocus.ts b/src/hooks/useRestoreInputFocus/index.android.ts similarity index 100% rename from src/hooks/useRestoreInputFocus.ts rename to src/hooks/useRestoreInputFocus/index.android.ts diff --git a/src/hooks/useRestoreInputFocus/index.ts b/src/hooks/useRestoreInputFocus/index.ts new file mode 100644 index 000000000000..857868ca4114 --- /dev/null +++ b/src/hooks/useRestoreInputFocus/index.ts @@ -0,0 +1,2 @@ +const useRestoreInputFocus = (_isLostFocus: boolean) => {}; +export default useRestoreInputFocus; From 59975d3ce1d766a3c16c0a08e9a8c0feca3d945b Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Mon, 23 Dec 2024 16:34:14 +0100 Subject: [PATCH 63/64] fix: eslint --- src/hooks/useRestoreInputFocus/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hooks/useRestoreInputFocus/index.ts b/src/hooks/useRestoreInputFocus/index.ts index 857868ca4114..4105455698dc 100644 --- a/src/hooks/useRestoreInputFocus/index.ts +++ b/src/hooks/useRestoreInputFocus/index.ts @@ -1,2 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars const useRestoreInputFocus = (_isLostFocus: boolean) => {}; + export default useRestoreInputFocus; From 497f6af6b64ccd563397bb3af468da5511934399 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Thu, 26 Dec 2024 13:14:59 +0100 Subject: [PATCH 64/64] chore: remove comment after incorrect conflict resolving --- src/components/KeyboardAvoidingView/index.android.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/KeyboardAvoidingView/index.android.tsx b/src/components/KeyboardAvoidingView/index.android.tsx index ec2dc3bd18d7..e81ebd6ff671 100644 --- a/src/components/KeyboardAvoidingView/index.android.tsx +++ b/src/components/KeyboardAvoidingView/index.android.tsx @@ -1,6 +1,3 @@ -/* - * 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';