From 3092630a91885504cab645a3082d134ec9197b7d Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 13 May 2024 12:37:03 +0200 Subject: [PATCH 01/38] init react-native-keyboard-controller --- ios/Podfile.lock | 23 +++++++++++++++++++++++ package-lock.json | 10 ++++++++++ package.json | 1 + src/App.tsx | 15 +++++++++------ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d0155051fc3b..2e3261d2149b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1282,6 +1282,25 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-keyboard-controller (1.11.7): + - 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): @@ -2114,6 +2133,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`) @@ -2308,6 +2328,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: @@ -2508,6 +2530,7 @@ SPEC CHECKSUMS: react-native-geolocation: f9e92eb774cb30ac1e099f34b3a94f03b4db7eb3 react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440 react-native-key-command: 28ccfa09520e7d7e30739480dea4df003493bfe8 + react-native-keyboard-controller: 7ac0083f3f16b3587d1d305e9ed75e4d759a84b8 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 58f3d860148f..6c6a6c8a687b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,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": "github:kirillzyusko/react-native-keyboard-controller#main", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -31384,6 +31385,15 @@ "version": "5.0.1", "license": "MIT" }, + "node_modules/react-native-keyboard-controller": { + "version": "1.11.7", + "resolved": "git+ssh://git@github.com/kirillzyusko/react-native-keyboard-controller.git#4cabd4d0a84515cffce2607cd7b2d39c0bc35710", + "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 c71f489daa36..ec0f22396993 100644 --- a/package.json +++ b/package.json @@ -148,6 +148,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": "github:kirillzyusko/react-native-keyboard-controller#main", "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..6cd37ac84bb2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import {PortalProvider} from '@gorhom/portal'; import React from 'react'; import {LogBox} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; +import {KeyboardProvider} from 'react-native-keyboard-controller'; import {PickerStateProvider} from 'react-native-picker-select'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import '../wdyr'; @@ -86,12 +87,14 @@ function App({url}: AppProps) { VideoPopoverMenuContextProvider, ]} > - - - - - - + + + + + + + + From aa6498c9145644e5d5915d889a45126054793143 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 13 May 2024 16:44:58 +0200 Subject: [PATCH 02/38] patch --- ...ct-native-keyboard-controller+1.11.7.patch | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 patches/react-native-keyboard-controller+1.11.7.patch diff --git a/patches/react-native-keyboard-controller+1.11.7.patch b/patches/react-native-keyboard-controller+1.11.7.patch new file mode 100644 index 000000000000..af2321bc50eb --- /dev/null +++ b/patches/react-native-keyboard-controller+1.11.7.patch @@ -0,0 +1,23 @@ +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..68572e7 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() { From bc2819a7e22218c7e78d137248f5a488ef196a61 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 14 May 2024 10:02:20 +0200 Subject: [PATCH 03/38] patch --- ...act-native-keyboard-controller+1.11.7.patch | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/patches/react-native-keyboard-controller+1.11.7.patch b/patches/react-native-keyboard-controller+1.11.7.patch index af2321bc50eb..0ea97d3731a1 100644 --- a/patches/react-native-keyboard-controller+1.11.7.patch +++ b/patches/react-native-keyboard-controller+1.11.7.patch @@ -1,5 +1,5 @@ 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..68572e7 100644 +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 @@ -21,3 +21,19 @@ index 83884d8..68572e7 100644 } 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 From a40f63fd71b17da2147dc8b22862d41c9f31251e Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 14 May 2024 11:20:29 +0200 Subject: [PATCH 04/38] wrap KeyboardProvider --- src/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6cd37ac84bb2..09edbc3987c3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -87,14 +87,14 @@ function App({url}: AppProps) { VideoPopoverMenuContextProvider, ]} > - + - + From 62a085fef7115e905a6c8ca2665b7a0e453ebf51 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 15:23:37 +0200 Subject: [PATCH 05/38] CONST --- src/CONST.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index 8a380188d05e..31bd7fc99938 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1227,6 +1227,8 @@ const CONST = { MAX_AMOUNT_OF_SUGGESTIONS: 20, MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER: 5, HERE_TEXT: '@here', + SUGGESTION_BOX_MAX_SAFE_DISTANCE: 38, + BIG_SCREEN_SUGGESTION_WIDTH: 300, }, COMPOSER_MAX_HEIGHT: 125, CHAT_FOOTER_SECONDARY_ROW_HEIGHT: 15, From e3968e61d4582ad611748363cb6e05da788df366 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 15:39:31 +0200 Subject: [PATCH 06/38] AutoCompleteSuggestionsPortal --- .../index.native.tsx | 34 +++++++++++ .../AutoCompleteSuggestionsPortal/index.tsx | 60 +++++++++++++++++++ .../AutoCompleteSuggestionsPortal/types.ts | 13 ++++ .../AutoCompleteSuggestions/index.native.tsx | 17 ------ 4 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx create mode 100644 src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx create mode 100644 src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/types.ts delete mode 100644 src/components/AutoCompleteSuggestions/index.native.tsx diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx new file mode 100644 index 000000000000..213d6257558f --- /dev/null +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx @@ -0,0 +1,34 @@ +import {Portal} from '@gorhom/portal'; +import React, {useMemo} from 'react'; +import {View} from 'react-native'; +import BaseAutoCompleteSuggestions from '@components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions'; +import useStyleUtils from '@hooks/useStyleUtils'; +import getBottomSuggestionPadding from './getBottomSuggestionPadding'; +import type {AutoCompleteSuggestionsPortalProps} from './types'; + +function AutoCompleteSuggestionsPortal({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps) { + const StyleUtils = useStyleUtils(); + const styles = useMemo(() => StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom + getBottomSuggestionPadding()}), [StyleUtils, left, width, bottom]); + // const styles = useMemo(() => StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom + 0}), [StyleUtils, left, width, bottom]); + + if (!width) { + return null; + } + + return ( + + + {/* eslint-disable-next-line react/jsx-props-no-spreading */} + + width={width} + // eslint-disable-next-line react/jsx-props-no-spreading + {...props} + /> + + + ); +} + +AutoCompleteSuggestionsPortal.displayName = 'AutoCompleteSuggestionsPortal'; + +export default AutoCompleteSuggestionsPortal; diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx new file mode 100644 index 000000000000..3b247b516d41 --- /dev/null +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import type {ReactElement} from 'react'; +import ReactDOM from 'react-dom'; +import {View} from 'react-native'; +import BaseAutoCompleteSuggestions from '@components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions'; +import useStyleUtils from '@hooks/useStyleUtils'; +import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import getBottomSuggestionPadding from './getBottomSuggestionPadding'; +import type {AutoCompleteSuggestionsPortalProps} from './types'; + +/** + * On the mobile-web platform, when long-pressing on auto-complete suggestions, + * we need to prevent focus shifting to avoid blurring the main input (which makes the suggestions picker close and fires the onSelect callback). + * The desired pattern for all platforms is to do nothing on long-press. + * On the native platform, tapping on auto-complete suggestions will not blur the main input. + */ + +function AutoCompleteSuggestionsPortal({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps): ReactElement | null | false { + const StyleUtils = useStyleUtils(); + const containerRef = React.useRef(null); + + const bodyElement = document.querySelector('body'); + + React.useEffect(() => { + const container = containerRef.current; + if (!container) { + return () => {}; + } + container.onpointerdown = (e) => { + if (DeviceCapabilities.hasHoverSupport()) { + return; + } + e.preventDefault(); + }; + return () => (container.onpointerdown = null); + }, []); + + const componentToRender = ( + + width={width} + // eslint-disable-next-line react/jsx-props-no-spreading + {...props} + ref={containerRef} + /> + ); + + return ( + !!width && + bodyElement && + ReactDOM.createPortal( + {componentToRender}, + bodyElement, + ) + ); +} + +AutoCompleteSuggestionsPortal.displayName = 'AutoCompleteSuggestionsPortal'; + +export default AutoCompleteSuggestionsPortal; +export type {AutoCompleteSuggestionsPortalProps}; diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/types.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/types.ts new file mode 100644 index 000000000000..61fa3e8dcd48 --- /dev/null +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/types.ts @@ -0,0 +1,13 @@ +import type {AutoCompleteSuggestionsProps} from '@components/AutoCompleteSuggestions/types'; + +type ExternalProps = Omit, 'measureParentContainerAndReportCursor'>; + +type AutoCompleteSuggestionsPortalProps = ExternalProps & { + left: number; + width: number; + bottom: number; + measuredHeightOfSuggestionRows: number; +}; + +// eslint-disable-next-line import/prefer-default-export +export type {AutoCompleteSuggestionsPortalProps}; diff --git a/src/components/AutoCompleteSuggestions/index.native.tsx b/src/components/AutoCompleteSuggestions/index.native.tsx deleted file mode 100644 index fbfa7d953581..000000000000 --- a/src/components/AutoCompleteSuggestions/index.native.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {Portal} from '@gorhom/portal'; -import React from 'react'; -import BaseAutoCompleteSuggestions from './BaseAutoCompleteSuggestions'; -import type {AutoCompleteSuggestionsProps} from './types'; - -function AutoCompleteSuggestions({measureParentContainer, ...props}: AutoCompleteSuggestionsProps) { - return ( - - {/* eslint-disable-next-line react/jsx-props-no-spreading */} - {...props} /> - - ); -} - -AutoCompleteSuggestions.displayName = 'AutoCompleteSuggestions'; - -export default AutoCompleteSuggestions; From 3ad8445d3a160bd8a63a52683541923334eaa817 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 15:56:33 +0200 Subject: [PATCH 07/38] getScrollPosition --- .../getScrollPosition/index.native.ts | 14 ++++++++++++++ .../ReportActionCompose/getScrollPosition/index.ts | 13 +++++++++++++ .../ReportActionCompose/getScrollPosition/types.ts | 10 ++++++++++ 3 files changed, 37 insertions(+) create mode 100644 src/pages/home/report/ReportActionCompose/getScrollPosition/index.native.ts create mode 100644 src/pages/home/report/ReportActionCompose/getScrollPosition/index.ts create mode 100644 src/pages/home/report/ReportActionCompose/getScrollPosition/types.ts diff --git a/src/pages/home/report/ReportActionCompose/getScrollPosition/index.native.ts b/src/pages/home/report/ReportActionCompose/getScrollPosition/index.native.ts new file mode 100644 index 000000000000..2045549959b8 --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/getScrollPosition/index.native.ts @@ -0,0 +1,14 @@ +import type {GetScrollPositionType, TextInputScrollProps} from './types'; + +function getScrollPosition({mobileInputScrollPosition}: TextInputScrollProps): GetScrollPositionType { + if (!mobileInputScrollPosition.current) { + return { + scrollValue: 0, + }; + } + return { + scrollValue: mobileInputScrollPosition.current, + }; +} + +export default getScrollPosition; diff --git a/src/pages/home/report/ReportActionCompose/getScrollPosition/index.ts b/src/pages/home/report/ReportActionCompose/getScrollPosition/index.ts new file mode 100644 index 000000000000..0d9fb701cabd --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/getScrollPosition/index.ts @@ -0,0 +1,13 @@ +import type {GetScrollPositionType, TextInputScrollProps} from './types'; + +function getScrollPosition({textInputRef}: TextInputScrollProps): GetScrollPositionType { + let scrollValue = 0; + if (textInputRef?.current) { + if ('scrollTop' in textInputRef.current) { + scrollValue = textInputRef.current.scrollTop; + } + } + return {scrollValue}; +} + +export default getScrollPosition; diff --git a/src/pages/home/report/ReportActionCompose/getScrollPosition/types.ts b/src/pages/home/report/ReportActionCompose/getScrollPosition/types.ts new file mode 100644 index 000000000000..abb48e2cc079 --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/getScrollPosition/types.ts @@ -0,0 +1,10 @@ +import type {TextInput} from 'react-native'; + +type TextInputScrollProps = { + mobileInputScrollPosition: React.RefObject; + textInputRef: React.RefObject; +}; + +type GetScrollPositionType = {scrollValue: number}; + +export type {TextInputScrollProps, GetScrollPositionType}; From e29f1d091756727aad5ad231cdf60d61afbc6377 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 16:00:11 +0200 Subject: [PATCH 08/38] move PortalHost --- src/pages/home/ReportScreen.tsx | 2 ++ .../home/report/ReportActionCompose/ReportActionCompose.tsx | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 54e7ab923aa9..4f21a3570377 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -1,3 +1,4 @@ +import {PortalHost} from '@gorhom/portal'; import {useIsFocused} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import lodashIsEqual from 'lodash/isEqual'; @@ -733,6 +734,7 @@ function ReportScreen({ /> ) : null} + diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 5bfa2475ee23..94fc61427dc4 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -379,7 +379,6 @@ function ReportActionCompose({ {shouldShowReportRecipientLocalTime && hasReportRecipient && } - Date: Wed, 15 May 2024 16:00:31 +0200 Subject: [PATCH 09/38] MentionSuggestions --- src/components/MentionSuggestions.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index b11ae4f5ecd8..481a734224cd 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -1,5 +1,4 @@ import React, {useCallback} from 'react'; -import type {MeasureInWindowOnSuccessCallback} from 'react-native'; import {View} from 'react-native'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -8,6 +7,7 @@ import getStyledTextArray from '@libs/GetStyledTextArray'; import CONST from '@src/CONST'; import type {Icon} from '@src/types/onyx/OnyxCommon'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; +import type {MeasureParentContainerAndCursorCallback} from './AutoCompleteSuggestions/types'; import Avatar from './Avatar'; import Text from './Text'; @@ -53,8 +53,8 @@ type MentionSuggestionsProps = { * When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */ isMentionPickerLarge: boolean; - /** Measures the parent container's position and dimensions. */ - measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; + /** Measures the parent container's position and dimensions. Also add cursor coordinates */ + measureParentContainerAndReportCursor: (callback: MeasureParentContainerAndCursorCallback) => void; }; /** @@ -62,7 +62,7 @@ type MentionSuggestionsProps = { */ const keyExtractor = (item: Mention) => item.alternateText; -function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSelect, isMentionPickerLarge, measureParentContainer = () => {}}: MentionSuggestionsProps) { +function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSelect, isMentionPickerLarge, measureParentContainerAndReportCursor = () => {}}: MentionSuggestionsProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -148,7 +148,7 @@ function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSe onSelect={onSelect} isSuggestionPickerLarge={isMentionPickerLarge} accessibilityLabelExtractor={keyExtractor} - measureParentContainer={measureParentContainer} + measureParentContainerAndReportCursor={measureParentContainerAndReportCursor} /> ); } From 6c123363f471f30ef6f04e842f7e83cd3b12c5b8 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 16:01:29 +0200 Subject: [PATCH 10/38] EmojiSuggestions --- src/components/EmojiSuggestions.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/EmojiSuggestions.tsx b/src/components/EmojiSuggestions.tsx index 1c0306741048..3781507b544c 100644 --- a/src/components/EmojiSuggestions.tsx +++ b/src/components/EmojiSuggestions.tsx @@ -7,10 +7,9 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as EmojiUtils from '@libs/EmojiUtils'; import getStyledTextArray from '@libs/GetStyledTextArray'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; +import type {MeasureParentContainerAndCursorCallback} from './AutoCompleteSuggestions/types'; import Text from './Text'; -type MeasureParentContainerCallback = (x: number, y: number, width: number) => void; - type EmojiSuggestionsProps = { /** The index of the highlighted emoji */ highlightedEmojiIndex?: number; @@ -33,8 +32,8 @@ type EmojiSuggestionsProps = { /** Stores user's preferred skin tone */ preferredSkinToneIndex: number; - /** Meaures the parent container's position and dimensions. */ - measureParentContainer: (callback: MeasureParentContainerCallback) => void; + /** Measures the parent container's position and dimensions. Also add cursor coordinates */ + measureParentContainerAndReportCursor: (callback: MeasureParentContainerAndCursorCallback) => void; }; /** @@ -42,7 +41,15 @@ type EmojiSuggestionsProps = { */ const keyExtractor = (item: Emoji, index: number): string => `${item.name}+${index}}`; -function EmojiSuggestions({emojis, onSelect, prefix, isEmojiPickerLarge, preferredSkinToneIndex, highlightedEmojiIndex = 0, measureParentContainer = () => {}}: EmojiSuggestionsProps) { +function EmojiSuggestions({ + emojis, + onSelect, + prefix, + isEmojiPickerLarge, + preferredSkinToneIndex, + highlightedEmojiIndex = 0, + measureParentContainerAndReportCursor = () => {}, +}: EmojiSuggestionsProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); /** @@ -85,7 +92,7 @@ function EmojiSuggestions({emojis, onSelect, prefix, isEmojiPickerLarge, preferr onSelect={onSelect} isSuggestionPickerLarge={isEmojiPickerLarge} accessibilityLabelExtractor={keyExtractor} - measureParentContainer={measureParentContainer} + measureParentContainerAndReportCursor={measureParentContainerAndReportCursor} /> ); } From 91d447c67ba6131564d15f174720e1162e6ed46a Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 16:02:35 +0200 Subject: [PATCH 11/38] AutoCompleteSuggestions, use measureParentContainerAndReportCursor --- .../AutoCompleteSuggestions/index.tsx | 133 +++++++++++++++--- .../AutoCompleteSuggestions/types.ts | 17 ++- 2 files changed, 125 insertions(+), 25 deletions(-) diff --git a/src/components/AutoCompleteSuggestions/index.tsx b/src/components/AutoCompleteSuggestions/index.tsx index c7f2aaea4d82..f20289efa9f0 100644 --- a/src/components/AutoCompleteSuggestions/index.tsx +++ b/src/components/AutoCompleteSuggestions/index.tsx @@ -1,38 +1,129 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import {View} from 'react-native'; +import React, {useEffect, useRef} from 'react'; +import useKeyboardState from '@hooks/useKeyboardState'; +import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import BaseAutoCompleteSuggestions from './BaseAutoCompleteSuggestions'; -import type {AutoCompleteSuggestionsProps} from './types'; +import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import CONST from '@src/CONST'; +import AutoCompleteSuggestionsPortal from './AutoCompleteSuggestionsPortal'; +import type {AutoCompleteSuggestionsProps, MeasureParentContainerAndCursor} from './types'; -function AutoCompleteSuggestions({measureParentContainer = () => {}, ...props}: AutoCompleteSuggestionsProps) { - const StyleUtils = useStyleUtils(); - const {windowHeight, windowWidth} = useWindowDimensions(); - const [{width, left, bottom}, setContainerState] = React.useState({ +const measureHeightOfSuggestionRows = (numRows: number, canBeBig: boolean): number => { + if (canBeBig) { + if (numRows > CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER) { + // On large screens, if there are more than 5 suggestions, we display a scrollable window with a height of 5 items, indicating that there are more items available + return CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; + } + return numRows * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; + } + if (numRows > 2) { + // On small screens, we display a scrollable window with a height of 2.5 items, indicating that there are more items available beyond what is currently visible + return CONST.AUTO_COMPLETE_SUGGESTER.SMALL_CONTAINER_HEIGHT_FACTOR * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; + } + return numRows * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; +}; +function isSuggestionRenderedAbove(isEnoughSpaceAboveForBig: boolean, isEnoughSpaceAboveForSmall: boolean): boolean { + return isEnoughSpaceAboveForBig || isEnoughSpaceAboveForSmall; +} + +/** + * On the mobile-web platform, when long-pressing on auto-complete suggestions, + * we need to prevent focus shifting to avoid blurring the main input (which makes the suggestions picker close and fires the onSelect callback). + * The desired pattern for all platforms is to do nothing on long-press. + * On the native platform, tapping on auto-complete suggestions will not blur the main input. + */ +function AutoCompleteSuggestions({measureParentContainerAndReportCursor = () => {}, ...props}: AutoCompleteSuggestionsProps) { + const containerRef = React.useRef(null); + const isInitialRender = React.useRef(true); + const isSuggestionAboveRef = React.useRef(false); + const leftValue = React.useRef(0); + const {windowHeight, windowWidth, isSmallScreenWidth} = useWindowDimensions(); + const [suggestionHeight, setSuggestionHeight] = React.useState(0); + const [containerState, setContainerState] = React.useState({ width: 0, left: 0, bottom: 0, }); + const StyleUtils = useStyleUtils(); + const insets = useSafeAreaInsets(); + const {keyboardHeight} = useKeyboardState(); + const {paddingBottom: bottomInset} = StyleUtils.getSafeAreaPadding(insets ?? undefined); - React.useEffect(() => { - if (!measureParentContainer) { + useEffect(() => { + const container = containerRef.current; + if (!container) { + return () => {}; + } + container.onpointerdown = (e) => { + if (DeviceCapabilities.hasHoverSupport()) { + return; + } + e.preventDefault(); + }; + return () => (container.onpointerdown = null); + }, []); + + const suggestionsLength = props.suggestions.length; + + useEffect(() => { + if (!measureParentContainerAndReportCursor) { return; } - measureParentContainer((x, y, w) => setContainerState({left: x, bottom: windowHeight - y, width: w})); - }, [measureParentContainer, windowHeight, windowWidth]); - const componentToRender = ( - - // eslint-disable-next-line react/jsx-props-no-spreading - {...props} - /> - ); + measureParentContainerAndReportCursor(({x, y, width, scrollValue, cursorCoordinates}: MeasureParentContainerAndCursor) => { + const xCoordinatesOfCursor = x + cursorCoordinates.x; + const leftValueForBigScreen = + xCoordinatesOfCursor + CONST.AUTO_COMPLETE_SUGGESTER.BIG_SCREEN_SUGGESTION_WIDTH > windowWidth + ? windowWidth - CONST.AUTO_COMPLETE_SUGGESTER.BIG_SCREEN_SUGGESTION_WIDTH + : xCoordinatesOfCursor; + + let bottomValue = windowHeight - y - cursorCoordinates.y + scrollValue - (keyboardHeight || bottomInset); + const widthValue = isSmallScreenWidth ? width : CONST.AUTO_COMPLETE_SUGGESTER.BIG_SCREEN_SUGGESTION_WIDTH; + + const contentMaxHeight = measureHeightOfSuggestionRows(suggestionsLength, true); + const contentMinHeight = measureHeightOfSuggestionRows(suggestionsLength, false); + const isEnoughSpaceAboveForBig = windowHeight - bottomValue - contentMaxHeight > CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE; + const isEnoughSpaceAboveForSmall = windowHeight - bottomValue - contentMinHeight > CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE; - const bodyElement = document.querySelector('body'); + if (isInitialRender.current) { + isSuggestionAboveRef.current = isSuggestionRenderedAbove(isEnoughSpaceAboveForBig, isEnoughSpaceAboveForSmall); + leftValue.current = isSmallScreenWidth ? x : leftValueForBigScreen; + isInitialRender.current = false; + } + let measuredHeight = 0; + if (isSuggestionAboveRef.current && isEnoughSpaceAboveForBig) { + // calculation for big suggestion box above the cursor + measuredHeight = measureHeightOfSuggestionRows(suggestionsLength, true); + } else if (isSuggestionAboveRef.current && isEnoughSpaceAboveForSmall) { + // calculation for small suggestion box above the cursor + measuredHeight = measureHeightOfSuggestionRows(suggestionsLength, false); + } else { + // calculation for big suggestion box below the cursor + measuredHeight = measureHeightOfSuggestionRows(suggestionsLength, true); + bottomValue = windowHeight - y - cursorCoordinates.y + scrollValue - measuredHeight - CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; + } + setSuggestionHeight(measuredHeight); + setContainerState({ + left: leftValue.current, + bottom: bottomValue, + width: widthValue, + }); + }); + }, [measureParentContainerAndReportCursor, windowHeight, windowWidth, keyboardHeight, isSmallScreenWidth, suggestionsLength, bottomInset]); + + if (containerState.width === 0 && containerState.left === 0 && containerState.bottom === 0) { + return null; + } return ( - !!width && bodyElement && ReactDOM.createPortal({componentToRender}, bodyElement) + ); } diff --git a/src/components/AutoCompleteSuggestions/types.ts b/src/components/AutoCompleteSuggestions/types.ts index 61d614dcf2e4..48bb6b713032 100644 --- a/src/components/AutoCompleteSuggestions/types.ts +++ b/src/components/AutoCompleteSuggestions/types.ts @@ -1,6 +1,15 @@ import type {ReactElement} from 'react'; -type MeasureParentContainerCallback = (x: number, y: number, width: number) => void; +type MeasureParentContainerAndCursor = { + x: number; + y: number; + width: number; + height: number; + scrollValue: number; + cursorCoordinates: {x: number; y: number}; +}; + +type MeasureParentContainerAndCursorCallback = (props: MeasureParentContainerAndCursor) => void; type RenderSuggestionMenuItemProps = { item: TSuggestion; @@ -31,8 +40,8 @@ type AutoCompleteSuggestionsProps = { /** create accessibility label for each item */ accessibilityLabelExtractor: (item: TSuggestion, index: number) => string; - /** Meaures the parent container's position and dimensions. */ - measureParentContainer?: (callback: MeasureParentContainerCallback) => void; + /** Measures the parent container's position and dimensions. Also add a cursor coordinates */ + measureParentContainerAndReportCursor?: (props: MeasureParentContainerAndCursorCallback) => void; }; -export type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps}; +export type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps, MeasureParentContainerAndCursorCallback, MeasureParentContainerAndCursor}; From 47f31b519ff92f09b31a8ab0dece3ad47ba87466 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 16:15:50 +0200 Subject: [PATCH 12/38] getBottomSuggestionPadding --- .../getBottomSuggestionPadding/index.android.ts | 5 +++++ .../getBottomSuggestionPadding/index.ios.ts | 5 +++++ .../getBottomSuggestionPadding/index.ts | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts create mode 100644 src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts create mode 100644 src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts new file mode 100644 index 000000000000..58688325d81f --- /dev/null +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts @@ -0,0 +1,5 @@ +function getBottomSuggestionPadding(): number { + return -6; +} + +export default getBottomSuggestionPadding; diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts new file mode 100644 index 000000000000..49072a04bc6b --- /dev/null +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts @@ -0,0 +1,5 @@ +function getBottomSuggestionPadding(): number { + return 12; +} + +export default getBottomSuggestionPadding; diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts new file mode 100644 index 000000000000..b39b3604ec47 --- /dev/null +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts @@ -0,0 +1,5 @@ +function getBottomSuggestionPadding(): number { + return 10; +} + +export default getBottomSuggestionPadding; From 44877f4e085cf685006a9749a67038fa3c144112 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 19:07:08 +0200 Subject: [PATCH 13/38] BaseAutoCompleteSuggestions --- .../BaseAutoCompleteSuggestions.tsx | 67 +++++-------------- 1 file changed, 16 insertions(+), 51 deletions(-) diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx index 32ac018acb04..517a3d0793cf 100644 --- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx +++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx @@ -1,40 +1,18 @@ import {FlashList} from '@shopify/flash-list'; import type {ReactElement} from 'react'; -import React, {useCallback, useEffect, useMemo, useRef} from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; // We take ScrollView from this package to properly handle the scrolling of AutoCompleteSuggestions in chats since one scroll is nested inside another import {ScrollView} from 'react-native-gesture-handler'; -import Animated, {Easing, FadeOutDown, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; +import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import ColorSchemeWrapper from '@components/ColorSchemeWrapper'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import variables from '@styles/variables'; import CONST from '@src/CONST'; -import type {AutoCompleteSuggestionsProps, RenderSuggestionMenuItemProps} from './types'; +import type {AutoCompleteSuggestionsPortalProps} from './AutoCompleteSuggestionsPortal'; +import type {RenderSuggestionMenuItemProps} from './types'; -const measureHeightOfSuggestionRows = (numRows: number, isSuggestionPickerLarge: boolean): number => { - if (isSuggestionPickerLarge) { - if (numRows > CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER) { - // On large screens, if there are more than 5 suggestions, we display a scrollable window with a height of 5 items, indicating that there are more items available - return CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; - } - return numRows * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; - } - if (numRows > 2) { - // On small screens, we display a scrollable window with a height of 2.5 items, indicating that there are more items available beyond what is currently visible - return CONST.AUTO_COMPLETE_SUGGESTER.SMALL_CONTAINER_HEIGHT_FACTOR * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; - } - return numRows * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT; -}; - -/** - * On the mobile-web platform, when long-pressing on auto-complete suggestions, - * we need to prevent focus shifting to avoid blurring the main input (which makes the suggestions picker close and fires the onSelect callback). - * The desired pattern for all platforms is to do nothing on long-press. - * On the native platform, tapping on auto-complete suggestions will not blur the main input. - */ +type ExternalProps = Omit, 'left' | 'bottom'>; function BaseAutoCompleteSuggestions({ highlightedSuggestionIndex, @@ -42,13 +20,13 @@ function BaseAutoCompleteSuggestions({ accessibilityLabelExtractor, renderSuggestionMenuItem, suggestions, - isSuggestionPickerLarge, keyExtractor, -}: AutoCompleteSuggestionsProps) { - const {windowWidth, isLargeScreenWidth} = useWindowDimensions(); + measuredHeightOfSuggestionRows, +}: ExternalProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const rowHeight = useSharedValue(0); + const fadeInOpacity = useSharedValue(0); const scrollRef = useRef>(null); /** * Render a suggestion menu item component. @@ -61,7 +39,6 @@ function BaseAutoCompleteSuggestions({ onMouseDown={(e) => e.preventDefault()} onPress={() => onSelect(index)} onLongPress={() => {}} - shouldUseHapticsOnLongPress={false} accessibilityLabel={accessibilityLabelExtractor(item, index)} > {renderSuggestionMenuItem(item, index)} @@ -71,20 +48,15 @@ function BaseAutoCompleteSuggestions({ ); const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length; - const animatedStyles = useAnimatedStyle(() => StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value)); - const estimatedListSize = useMemo( - () => ({ - height: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length, - width: (isLargeScreenWidth ? windowWidth - variables.sideBarWidth : windowWidth) - CONST.CHAT_FOOTER_HORIZONTAL_PADDING, - }), - [isLargeScreenWidth, suggestions.length, windowWidth], - ); + const animatedStyles = useAnimatedStyle(() => ({ + opacity: fadeInOpacity.value, + })); useEffect(() => { - rowHeight.value = withTiming(measureHeightOfSuggestionRows(suggestions.length, isSuggestionPickerLarge), { - duration: 100, + fadeInOpacity.value = withTiming(1, { + duration: 70, easing: Easing.inOut(Easing.ease), }); - }, [suggestions.length, isSuggestionPickerLarge, rowHeight]); + }, [suggestions.length, fadeInOpacity]); useEffect(() => { if (!scrollRef.current) { @@ -95,19 +67,12 @@ function BaseAutoCompleteSuggestions({ return ( { - if (DeviceCapabilities.hasHoverSupport()) { - return; - } - e.preventDefault(); - }} + style={[styles.autoCompleteSuggestionsContainer, animatedStyles, StyleUtils.getAutoCompleteSuggestionContainerStyle(measuredHeightOfSuggestionRows)]} + // style={[styles.autoCompleteSuggestionsContainer, StyleUtils.getAutoCompleteSuggestionContainerStyle(measuredHeightOfSuggestionRows)]} > Date: Wed, 15 May 2024 19:10:38 +0200 Subject: [PATCH 14/38] add suggestion to ComposerWithSuggestions --- .../ComposerWithSuggestions.tsx | 75 ++++++++++++++----- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 3120bbe9bed2..61756180bc06 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -10,14 +10,17 @@ import type { TextInput, TextInputFocusEventData, TextInputKeyPressEventData, + TextInputScrollEventData, TextInputSelectionChangeEventData, } from 'react-native'; import {DeviceEventEmitter, findNodeHandle, InteractionManager, NativeModules, View} from 'react-native'; +import {useFocusedInputHandler} from 'react-native-keyboard-controller'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import type {useAnimatedRef} from 'react-native-reanimated'; +import Reanimated, {typeuseAnimatedRef, useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; +import type {MeasureParentContainerAndCursorCallback} from '@components/AutoCompleteSuggestions/types'; import Composer from '@components/Composer'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; @@ -41,6 +44,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as SuggestionUtils from '@libs/SuggestionUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; +import getScrollPosition from '@pages/home/report/ReportActionCompose/getScrollPosition'; import type {ComposerRef, SuggestionsRef} from '@pages/home/report/ReportActionCompose/ReportActionCompose'; import SilentCommentUpdater from '@pages/home/report/ReportActionCompose/SilentCommentUpdater'; import Suggestions from '@pages/home/report/ReportActionCompose/Suggestions'; @@ -266,6 +270,9 @@ function ComposerWithSuggestions( const isFocused = useIsFocused(); const navigation = useNavigation(); const emojisPresentBefore = useRef([]); + const mobileInputScrollPosition = useRef(0); + const position = useSharedValue({x: 0, y: 0}); + const tag = useSharedValue(-1); const draftComment = getDraftComment(reportID) ?? ''; const [value, setValue] = useState(() => { if (draftComment) { @@ -295,12 +302,6 @@ function ComposerWithSuggestions( const syncSelectionWithOnChangeTextRef = useRef(null); - const suggestions = suggestionsRef.current?.getSuggestions() ?? []; - - const hasEnoughSpaceForLargeSuggestion = SuggestionUtils.hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, suggestions?.length ?? 0); - - const isAutoSuggestionPickerLarge = !isSmallScreenWidth || (isSmallScreenWidth && hasEnoughSpaceForLargeSuggestion); - // The ref to check whether the comment saving is in progress const isCommentPendingSaved = useRef(false); @@ -563,21 +564,26 @@ function ComposerWithSuggestions( const onSelectionChange = useCallback( (e: NativeSyntheticEvent) => { - if (textInputRef.current?.isFocused() && suggestionsRef.current?.onSelectionChange?.(e)) { + if (!textInputRef.current?.isFocused()) { return; } + suggestionsRef.current?.onSelectionChange?.(e); setSelection(e.nativeEvent.selection); }, [suggestionsRef], ); - const hideSuggestionMenu = useCallback(() => { - if (!suggestionsRef.current || isScrollLikelyLayoutTriggered.current) { - return; - } - suggestionsRef.current.updateShouldShowSuggestionMenuToFalse(false); - }, [suggestionsRef, isScrollLikelyLayoutTriggered]); + const hideSuggestionMenu = useCallback( + (e: NativeSyntheticEvent) => { + mobileInputScrollPosition.current = e?.nativeEvent?.contentOffset?.y ?? 0; + if (!suggestionsRef.current || isScrollLikelyLayoutTriggered.current) { + return; + } + suggestionsRef.current.updateShouldShowSuggestionMenuToFalse(false); + }, + [suggestionsRef, isScrollLikelyLayoutTriggered], + ); const setShouldBlockSuggestionCalcToFalse = useCallback(() => { if (!suggestionsRef.current) { @@ -733,6 +739,42 @@ function ComposerWithSuggestions( return isOnlyEmoji ? {lineHeight: variables.fontSizeOnlyEmojisHeight} : {}; }, [value]); + useEffect(() => { + tag.value = findNodeHandle(textInputRef.current) ?? -1; + }, []); + useFocusedInputHandler( + { + onSelectionChange: (event) => { + 'worklet'; + + if (event.target === tag.value) { + position.value = { + x: event.selection.end.x, + y: event.selection.end.y, + }; + } + }, + }, + [], + ); + + const measureParentContainerAndReportCursor = useCallback( + (callback: MeasureParentContainerAndCursorCallback) => { + const {scrollValue} = getScrollPosition({mobileInputScrollPosition, textInputRef}); + measureParentContainer((x, y, width, height) => { + callback({ + x, + y, + width, + height, + scrollValue, + cursorCoordinates: {x: position.value.x, y: position.value.y}, + }); + }); + }, + [measureParentContainer, position], + ); + return ( <> @@ -772,12 +814,9 @@ function ComposerWithSuggestions( Date: Wed, 15 May 2024 19:10:58 +0200 Subject: [PATCH 15/38] update Suggestions --- .../ReportActionCompose/Suggestions.tsx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.tsx b/src/pages/home/report/ReportActionCompose/Suggestions.tsx index 8ebd52f62428..c6a8e1a9979d 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -1,7 +1,8 @@ import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useRef} from 'react'; -import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; +import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; +import type {MeasureParentContainerAndCursorCallback} from '@components/AutoCompleteSuggestions/types'; import {DragAndDropContext} from '@components/DragAndDrop/Provider'; import usePrevious from '@hooks/usePrevious'; import type {SuggestionsRef} from './ReportActionCompose'; @@ -29,11 +30,8 @@ type SuggestionProps = { /** Callback to update the comment draft */ updateComment: (newComment: string, shouldDebounceSaveComment?: boolean) => void; - /** Meaures the parent container's position and dimensions. */ - measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; - - /** Whether the composer is expanded */ - isComposerFullSize: boolean; + /** Measures the parent container's position and dimensions. Also add cursor coordinates */ + measureParentContainerAndReportCursor: (callback: MeasureParentContainerAndCursorCallback) => void; /** Report composer focus state */ isComposerFocused?: boolean; @@ -61,15 +59,13 @@ type SuggestionProps = { */ function Suggestions( { - isComposerFullSize, value, setValue, selection, setSelection, updateComment, - composerHeight, resetKeyboardInput, - measureParentContainer, + measureParentContainerAndReportCursor, isAutoSuggestionPickerLarge = true, isComposerFocused, isGroupPolicyReport, @@ -119,6 +115,7 @@ function Suggestions( const onSelectionChange = useCallback((e: NativeSyntheticEvent) => { const emojiHandler = suggestionEmojiRef.current?.onSelectionChange?.(e); + suggestionMentionRef.current?.onSelectionChange?.(e); return emojiHandler; }, []); @@ -157,11 +154,9 @@ function Suggestions( setValue, setSelection, selection, - isComposerFullSize, updateComment, - composerHeight, isAutoSuggestionPickerLarge, - measureParentContainer, + measureParentContainerAndReportCursor, isComposerFocused, isGroupPolicyReport, policyID, From bf3878b686a60d6d9316908f977d8872d78e8a0b Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 15 May 2024 19:11:53 +0200 Subject: [PATCH 16/38] update SuggestionMention --- .../home/report/ReportActionCompose/SuggestionMention.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 05e1163da200..f7384771560a 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -47,7 +47,7 @@ const defaultSuggestionsValues: SuggestionValues = { }; function SuggestionMention( - {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused, isGroupPolicyReport, policyID}: SuggestionProps, + {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainerAndReportCursor, isComposerFocused, isGroupPolicyReport, policyID}: SuggestionProps, ref: ForwardedRef, ) { const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; @@ -392,7 +392,7 @@ function SuggestionMention( prefix={suggestionValues.mentionPrefix} onSelect={insertSelectedMention} isMentionPickerLarge={!!isAutoSuggestionPickerLarge} - measureParentContainer={measureParentContainer} + measureParentContainerAndReportCursor={measureParentContainerAndReportCursor} /> ); } From 9df7a243450b18b82b0ee0f3172f696d1286bbe9 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 16 May 2024 10:24:07 +0200 Subject: [PATCH 17/38] remove onSelectionChange form SuggestionEmoji --- .../ReportActionCompose/SuggestionEmoji.tsx | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index b23c0be72592..ffa815f33a07 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -55,11 +55,13 @@ function SuggestionEmoji( updateComment, isAutoSuggestionPickerLarge, resetKeyboardInput, - measureParentContainer, + measureParentContainerAndReportCursor, isComposerFocused, }: SuggestionEmojiProps, ref: ForwardedRef, ) { + const prevValueRef = useRef(value); + const prevSelectionEndRef = useRef(selection.end); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); const isEmojiSuggestionsMenuVisible = suggestionValues.suggestedEmojis.length > 0 && suggestionValues.shouldShowSuggestionMenu; @@ -185,17 +187,6 @@ function SuggestionEmoji( calculateEmojiSuggestion(selection.end); }, [selection, calculateEmojiSuggestion, isComposerFocused]); - const onSelectionChange = useCallback( - (e: NativeSyntheticEvent) => { - /** - * we pass here e.nativeEvent.selection.end directly to calculateEmojiSuggestion - * because in other case calculateEmojiSuggestion will have an old calculation value - * of suggestion instead of current one - */ - calculateEmojiSuggestion(e.nativeEvent.selection.end); - }, - [calculateEmojiSuggestion], - ); const setShouldBlockSuggestionCalc = useCallback( (shouldBlockSuggestionCalc: boolean) => { @@ -210,13 +201,12 @@ function SuggestionEmoji( ref, () => ({ resetSuggestions, - onSelectionChange, triggerHotkeyActions, setShouldBlockSuggestionCalc, updateShouldShowSuggestionMenuToFalse, getSuggestions, }), - [onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions], + [resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions], ); if (!isEmojiSuggestionsMenuVisible) { @@ -231,7 +221,7 @@ function SuggestionEmoji( onSelect={insertSelectedEmoji} preferredSkinToneIndex={preferredSkinTone} isEmojiPickerLarge={!!isAutoSuggestionPickerLarge} - measureParentContainer={measureParentContainer} + measureParentContainerAndReportCursor={measureParentContainerAndReportCursor} /> ); } From bd9785db5650ae5ace7af16617739d076e491aa2 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 16 May 2024 11:05:28 +0200 Subject: [PATCH 18/38] getCursorPosition --- .../getCursorPosition/index.native.ts | 10 ++++++++++ .../ReportActionCompose/getCursorPosition/index.ts | 9 +++++++++ .../ReportActionCompose/getCursorPosition/types.ts | 13 +++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/pages/home/report/ReportActionCompose/getCursorPosition/index.native.ts create mode 100644 src/pages/home/report/ReportActionCompose/getCursorPosition/index.ts create mode 100644 src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts diff --git a/src/pages/home/report/ReportActionCompose/getCursorPosition/index.native.ts b/src/pages/home/report/ReportActionCompose/getCursorPosition/index.native.ts new file mode 100644 index 000000000000..5107e2c37362 --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/getCursorPosition/index.native.ts @@ -0,0 +1,10 @@ +import type {CursorPositionParamsType, PositionType} from './types'; + +function getCursorPosition({positionOnMobile}: CursorPositionParamsType): PositionType { + return { + x: positionOnMobile?.x ?? 0, + y: positionOnMobile?.y ?? 0, + }; +} + +export default getCursorPosition; diff --git a/src/pages/home/report/ReportActionCompose/getCursorPosition/index.ts b/src/pages/home/report/ReportActionCompose/getCursorPosition/index.ts new file mode 100644 index 000000000000..e1619b4cd45c --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/getCursorPosition/index.ts @@ -0,0 +1,9 @@ +import type {CursorPositionParamsType, PositionType} from './types'; + +function getCursorPosition({positionOnWeb}: CursorPositionParamsType): PositionType { + const x = positionOnWeb?.positionX ?? 0; + const y = positionOnWeb?.positionY ?? 0; + return {x, y}; +} + +export default getCursorPosition; diff --git a/src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts b/src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts new file mode 100644 index 000000000000..a7caf9f1ef41 --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts @@ -0,0 +1,13 @@ +type PositionType = { + x: number; + y: number; +}; + +type CursorPositionParamsType = { + positionOnMobile?: PositionType; + positionOnWeb?: {positionX: number; positionY: number}; +}; + +type GetCursorPositionType = (params: CursorPositionParamsType) => PositionType; + +export type {PositionType, CursorPositionParamsType, GetCursorPositionType}; From 0a914d0452b72da7acf0fb9241e25d1d0f6e98bc Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 16 May 2024 11:17:52 +0200 Subject: [PATCH 19/38] add types for selection --- src/components/Composer/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 4bc54d13b056..4500fd6062c8 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -84,6 +84,8 @@ function Composer( | { start: number; end?: number; + positionX?: number; + positionY?: number; } | undefined >({ From 88beb37c873e67a4fc1a70513aa3636838d672ff Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 16 May 2024 11:30:12 +0200 Subject: [PATCH 20/38] fix types --- .../ComposerWithSuggestions.tsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 61756180bc06..43e170bd3f4f 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -17,7 +17,8 @@ import {DeviceEventEmitter, findNodeHandle, InteractionManager, NativeModules, V import {useFocusedInputHandler} from 'react-native-keyboard-controller'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import Reanimated, {typeuseAnimatedRef, useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; +import {useSharedValue} from 'react-native-reanimated'; +import type {useAnimatedRef} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; import type {MeasureParentContainerAndCursorCallback} from '@components/AutoCompleteSuggestions/types'; @@ -41,9 +42,9 @@ import * as KeyDownListener from '@libs/KeyboardShortcut/KeyDownPressListener'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import * as SuggestionUtils from '@libs/SuggestionUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; +import getCursorPosition from '@pages/home/report/ReportActionCompose/getCursorPosition'; import getScrollPosition from '@pages/home/report/ReportActionCompose/getScrollPosition'; import type {ComposerRef, SuggestionsRef} from '@pages/home/report/ReportActionCompose/ReportActionCompose'; import SilentCommentUpdater from '@pages/home/report/ReportActionCompose/SilentCommentUpdater'; @@ -137,9 +138,6 @@ type ComposerWithSuggestionsProps = ComposerWithSuggestionsOnyxProps & /** Function to measure the parent container */ measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void; - /** The height of the list */ - listHeight: number; - /** Whether the scroll is likely to trigger a layout */ isScrollLikelyLayoutTriggered: RefObject; @@ -247,7 +245,6 @@ function ComposerWithSuggestions( handleSendMessage, shouldShowComposeInput, measureParentContainer = () => {}, - listHeight, isScrollLikelyLayoutTriggered, raiseIsScrollLikelyLayoutTriggered, @@ -271,7 +268,7 @@ function ComposerWithSuggestions( const navigation = useNavigation(); const emojisPresentBefore = useRef([]); const mobileInputScrollPosition = useRef(0); - const position = useSharedValue({x: 0, y: 0}); + const cursorPositionValue = useSharedValue({x: 0, y: 0}); const tag = useSharedValue(-1); const draftComment = getDraftComment(reportID) ?? ''; const [value, setValue] = useState(() => { @@ -293,7 +290,7 @@ function ComposerWithSuggestions( const valueRef = useRef(value); valueRef.current = value; - const [selection, setSelection] = useState(() => ({start: 0, end: 0})); + const [selection, setSelection] = useState(() => ({start: 0, end: 0, positionX: 0, positionY: 0})); const [composerHeight, setComposerHeight] = useState(0); @@ -440,10 +437,12 @@ function ComposerWithSuggestions( syncSelectionWithOnChangeTextRef.current = {position, value: newComment}; } - setSelection({ + setSelection((prevSelection) => ({ start: position, end: position, - }); + positionX: prevSelection.positionX, + positionY: prevSelection.positionY, + })); } commentRef.current = newCommentConverted; @@ -741,6 +740,7 @@ function ComposerWithSuggestions( useEffect(() => { tag.value = findNodeHandle(textInputRef.current) ?? -1; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useFocusedInputHandler( { @@ -748,7 +748,7 @@ function ComposerWithSuggestions( 'worklet'; if (event.target === tag.value) { - position.value = { + cursorPositionValue.value = { x: event.selection.end.x, y: event.selection.end.y, }; @@ -757,10 +757,10 @@ function ComposerWithSuggestions( }, [], ); - const measureParentContainerAndReportCursor = useCallback( (callback: MeasureParentContainerAndCursorCallback) => { const {scrollValue} = getScrollPosition({mobileInputScrollPosition, textInputRef}); + const {x: xPosition, y: yPosition} = getCursorPosition({positionOnMobile: cursorPositionValue.value, positionOnWeb: selection}); measureParentContainer((x, y, width, height) => { callback({ x, @@ -768,11 +768,11 @@ function ComposerWithSuggestions( width, height, scrollValue, - cursorCoordinates: {x: position.value.x, y: position.value.y}, + cursorCoordinates: {x: xPosition, y: yPosition}, }); }); }, - [measureParentContainer, position], + [measureParentContainer, cursorPositionValue, selection], ); return ( From 13fad61f7b5482daa7926c9c6c1a652a9e872ac1 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 16 May 2024 13:11:57 +0200 Subject: [PATCH 21/38] adjust the left position --- src/components/AutoCompleteSuggestions/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/AutoCompleteSuggestions/index.tsx b/src/components/AutoCompleteSuggestions/index.tsx index f20289efa9f0..21739307b85d 100644 --- a/src/components/AutoCompleteSuggestions/index.tsx +++ b/src/components/AutoCompleteSuggestions/index.tsx @@ -37,6 +37,7 @@ function AutoCompleteSuggestions({measureParentContainerAndReportCu const isInitialRender = React.useRef(true); const isSuggestionAboveRef = React.useRef(false); const leftValue = React.useRef(0); + const prevLeftValue = React.useRef(0); const {windowHeight, windowWidth, isSmallScreenWidth} = useWindowDimensions(); const [suggestionHeight, setSuggestionHeight] = React.useState(0); const [containerState, setContainerState] = React.useState({ @@ -85,10 +86,14 @@ function AutoCompleteSuggestions({measureParentContainerAndReportCu const isEnoughSpaceAboveForBig = windowHeight - bottomValue - contentMaxHeight > CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE; const isEnoughSpaceAboveForSmall = windowHeight - bottomValue - contentMinHeight > CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE; - if (isInitialRender.current) { + const newLeftValue = isSmallScreenWidth ? x : leftValueForBigScreen; + // If the suggested word is longer than half the width of the suggestion popup, then adjust a new position of popup + const isAdjustmentNeeded = Math.abs(prevLeftValue.current - leftValueForBigScreen) > widthValue / 2; + if (isInitialRender.current || isAdjustmentNeeded) { isSuggestionAboveRef.current = isSuggestionRenderedAbove(isEnoughSpaceAboveForBig, isEnoughSpaceAboveForSmall); - leftValue.current = isSmallScreenWidth ? x : leftValueForBigScreen; + leftValue.current = newLeftValue; isInitialRender.current = false; + prevLeftValue.current = newLeftValue; } let measuredHeight = 0; From 704688d1d38e5521b9f827071d34d38f5195718f Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 16 May 2024 13:12:11 +0200 Subject: [PATCH 22/38] fix animation --- .../BaseAutoCompleteSuggestions.tsx | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx index 517a3d0793cf..2e1e6b26bb88 100644 --- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx +++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx @@ -26,6 +26,7 @@ function BaseAutoCompleteSuggestions({ const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const rowHeight = useSharedValue(0); + const prevRowHeightRef = useRef(measuredHeightOfSuggestionRows); const fadeInOpacity = useSharedValue(0); const scrollRef = useRef>(null); /** @@ -50,13 +51,26 @@ function BaseAutoCompleteSuggestions({ const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length; const animatedStyles = useAnimatedStyle(() => ({ opacity: fadeInOpacity.value, + ...StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value), })); + useEffect(() => { - fadeInOpacity.value = withTiming(1, { - duration: 70, - easing: Easing.inOut(Easing.ease), - }); - }, [suggestions.length, fadeInOpacity]); + if (measuredHeightOfSuggestionRows === prevRowHeightRef.current) { + fadeInOpacity.value = withTiming(1, { + duration: 70, + easing: Easing.inOut(Easing.ease), + }); + rowHeight.value = measuredHeightOfSuggestionRows; + } else { + fadeInOpacity.value = 1; + rowHeight.value = withTiming(measuredHeightOfSuggestionRows, { + duration: 100, + easing: Easing.bezier(0.25, 0.1, 0.25, 1), + }); + } + + prevRowHeightRef.current = measuredHeightOfSuggestionRows; + }, [suggestions.length, rowHeight, measuredHeightOfSuggestionRows, prevRowHeightRef, fadeInOpacity]); useEffect(() => { if (!scrollRef.current) { @@ -65,11 +79,11 @@ function BaseAutoCompleteSuggestions({ scrollRef.current.scrollToIndex({index: highlightedSuggestionIndex, animated: true}); }, [highlightedSuggestionIndex]); + if (suggestions.length === 0) { + return null; + } return ( - + Date: Mon, 27 May 2024 10:10:44 +0200 Subject: [PATCH 23/38] update isAdjustmentNeeded --- src/components/AutoCompleteSuggestions/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AutoCompleteSuggestions/index.tsx b/src/components/AutoCompleteSuggestions/index.tsx index 21739307b85d..eeb1b4d8753c 100644 --- a/src/components/AutoCompleteSuggestions/index.tsx +++ b/src/components/AutoCompleteSuggestions/index.tsx @@ -87,8 +87,8 @@ function AutoCompleteSuggestions({measureParentContainerAndReportCu const isEnoughSpaceAboveForSmall = windowHeight - bottomValue - contentMinHeight > CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE; const newLeftValue = isSmallScreenWidth ? x : leftValueForBigScreen; - // If the suggested word is longer than half the width of the suggestion popup, then adjust a new position of popup - const isAdjustmentNeeded = Math.abs(prevLeftValue.current - leftValueForBigScreen) > widthValue / 2; + // If the suggested word is longer than 150 (approximately half the width of the suggestion popup), then adjust a new position of popup + const isAdjustmentNeeded = Math.abs(prevLeftValue.current - leftValueForBigScreen) > 150; if (isInitialRender.current || isAdjustmentNeeded) { isSuggestionAboveRef.current = isSuggestionRenderedAbove(isEnoughSpaceAboveForBig, isEnoughSpaceAboveForSmall); leftValue.current = newLeftValue; From bdd584edddbb6c12b2e7252ba5f27ecc0ea76d1f Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 10:54:06 +0200 Subject: [PATCH 24/38] remove weird styles --- src/styles/index.ts | 4 +--- src/styles/utils/index.ts | 6 +----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 1d8e14733d02..402f8aeb17be 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -265,10 +265,8 @@ const styles = (theme: ThemeColors) => borderWidth: 1, borderColor: theme.border, justifyContent: 'center', + overflow: 'hidden', boxShadow: variables.popoverMenuShadow, - position: 'absolute', - left: 0, - right: 0, paddingVertical: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING, }, diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 82602efa88b3..542e04f332af 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -845,7 +845,7 @@ type GetBaseAutoCompleteSuggestionContainerStyleParams = { */ function getBaseAutoCompleteSuggestionContainerStyle({left, bottom, width}: GetBaseAutoCompleteSuggestionContainerStyleParams): ViewStyle { return { - ...positioning.pFixed, + position: 'absolute', bottom, left, width, @@ -863,11 +863,7 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle const borderWidth = 2; const height = itemsHeight + 2 * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING + (shouldPreventScroll ? borderWidth : 0); - // The suggester is positioned absolutely within the component that includes the input and RecipientLocalTime view (for non-expanded mode only). To position it correctly, - // we need to shift it by the suggester's height plus its padding and, if applicable, the height of the RecipientLocalTime view. return { - overflow: 'hidden', - top: -(height + CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_PADDING + (shouldPreventScroll ? 0 : borderWidth)), height, minHeight: CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT, }; From 4e5f726d6b055581f50ef94c2de5683f287efcc6 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 10:56:37 +0200 Subject: [PATCH 25/38] adjust getBottomSuggestionPadding --- .../getBottomSuggestionPadding/index.android.ts | 5 ----- .../getBottomSuggestionPadding/index.ios.ts | 2 +- .../getBottomSuggestionPadding/index.ts | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts deleted file mode 100644 index 58688325d81f..000000000000 --- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.android.ts +++ /dev/null @@ -1,5 +0,0 @@ -function getBottomSuggestionPadding(): number { - return -6; -} - -export default getBottomSuggestionPadding; diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts index 49072a04bc6b..03c852d40d37 100644 --- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts @@ -1,5 +1,5 @@ function getBottomSuggestionPadding(): number { - return 12; + return 16; } export default getBottomSuggestionPadding; diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts index b39b3604ec47..a812bf461442 100644 --- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts @@ -1,5 +1,5 @@ function getBottomSuggestionPadding(): number { - return 10; + return 0; } export default getBottomSuggestionPadding; From 4d05e3634ca4c8dd1f6d946aa3863425e1dc4e4d Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 12:52:22 +0200 Subject: [PATCH 26/38] iOS: Fixes textinput onscroll event payload --- ...3.4+016+iOS-textinput-onscroll-event.patch | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 patches/react-native+0.73.4+016+iOS-textinput-onscroll-event.patch diff --git a/patches/react-native+0.73.4+016+iOS-textinput-onscroll-event.patch b/patches/react-native+0.73.4+016+iOS-textinput-onscroll-event.patch new file mode 100644 index 000000000000..1a5b4c40477b --- /dev/null +++ b/patches/react-native+0.73.4+016+iOS-textinput-onscroll-event.patch @@ -0,0 +1,70 @@ +diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp +index 88ae3f3..497569a 100644 +--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp ++++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp +@@ -36,6 +36,54 @@ static jsi::Value textInputMetricsPayload( + return payload; + }; + ++static jsi::Value textInputMetricsScrollPayload( ++ jsi::Runtime& runtime, ++ const TextInputMetrics& textInputMetrics) { ++ auto payload = jsi::Object(runtime); ++ ++ { ++ auto contentOffset = jsi::Object(runtime); ++ contentOffset.setProperty(runtime, "x", textInputMetrics.contentOffset.x); ++ contentOffset.setProperty(runtime, "y", textInputMetrics.contentOffset.y); ++ payload.setProperty(runtime, "contentOffset", contentOffset); ++ } ++ ++ { ++ auto contentInset = jsi::Object(runtime); ++ contentInset.setProperty(runtime, "top", textInputMetrics.contentInset.top); ++ contentInset.setProperty( ++ runtime, "left", textInputMetrics.contentInset.left); ++ contentInset.setProperty( ++ runtime, "bottom", textInputMetrics.contentInset.bottom); ++ contentInset.setProperty( ++ runtime, "right", textInputMetrics.contentInset.right); ++ payload.setProperty(runtime, "contentInset", contentInset); ++ } ++ ++ { ++ auto contentSize = jsi::Object(runtime); ++ contentSize.setProperty( ++ runtime, "width", textInputMetrics.contentSize.width); ++ contentSize.setProperty( ++ runtime, "height", textInputMetrics.contentSize.height); ++ payload.setProperty(runtime, "contentSize", contentSize); ++ } ++ ++ { ++ auto layoutMeasurement = jsi::Object(runtime); ++ layoutMeasurement.setProperty( ++ runtime, "width", textInputMetrics.layoutMeasurement.width); ++ layoutMeasurement.setProperty( ++ runtime, "height", textInputMetrics.layoutMeasurement.height); ++ payload.setProperty(runtime, "layoutMeasurement", layoutMeasurement); ++ } ++ ++ payload.setProperty(runtime, "zoomScale", textInputMetrics.zoomScale ?: 1); ++ ++ ++ return payload; ++ }; ++ + static jsi::Value textInputMetricsContentSizePayload( + jsi::Runtime& runtime, + const TextInputMetrics& textInputMetrics) { +@@ -140,7 +188,9 @@ void TextInputEventEmitter::onKeyPressSync( + + void TextInputEventEmitter::onScroll( + const TextInputMetrics& textInputMetrics) const { +- dispatchTextInputEvent("scroll", textInputMetrics); ++ dispatchEvent("scroll", [textInputMetrics](jsi::Runtime& runtime) { ++ return textInputMetricsScrollPayload(runtime, textInputMetrics); ++ }); + } + + void TextInputEventEmitter::dispatchTextInputEvent( From a8528822ad4aca969c4b3faa0d0ca351f853bb81 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 15:16:02 +0200 Subject: [PATCH 27/38] remove old patch --- ios/Podfile.lock | 10 ++--- package-lock.json | 7 ++-- package.json | 2 +- ...ct-native-keyboard-controller+1.11.7.patch | 39 ------------------- 4 files changed, 10 insertions(+), 48 deletions(-) delete mode 100644 patches/react-native-keyboard-controller+1.11.7.patch diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0c3493643e40..23d235772170 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.11.7): + - react-native-keyboard-controller (1.12.2): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -1961,7 +1961,7 @@ PODS: - React-Codegen - React-Core - ReactCommon/turbomodule/core - - RNReanimated (3.7.2): + - RNReanimated (3.8.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: 7ac0083f3f16b3587d1d305e9ed75e4d759a84b8 + react-native-keyboard-controller: 47c01b0741ae5fc84e53cf282e61cfa5c2edb19b react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d react-native-netinfo: 02d31de0e08ab043d48f2a1a8baade109d7b6ca5 react-native-pager-view: ccd4bbf9fc7effaf8f91f8dae43389844d9ef9fa @@ -2617,7 +2617,7 @@ SPEC CHECKSUMS: rnmapbox-maps: df8fe93dbd251f25022f4023d31bc04160d4d65c RNPermissions: 0b61d30d21acbeafe25baaa47d9bae40a0c65216 RNReactNativeHapticFeedback: 616c35bdec7d20d4c524a7949ca9829c09e35f37 - RNReanimated: 51db0fff543694d931bd3b7cab1a3b36bd86c738 + RNReanimated: 323436b1a5364dca3b5f8b1a13458455e0de9efe RNScreens: 9ec969a95987a6caae170ef09313138abf3331e1 RNShare: 2a4cdfc0626ad56b0ef583d424f2038f772afe58 RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852 @@ -2629,7 +2629,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 1394a316c7add37e619c48d7aa40b38b954bf055 - Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70 + Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312 PODFILE CHECKSUM: 66a5c97ae1059e4da1993a4ad95abe5d819f555b diff --git a/package-lock.json b/package-lock.json index 221b5ee1e85f..7ecdc0b10196 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": "github:kirillzyusko/react-native-keyboard-controller#main", + "react-native-keyboard-controller": "^1.12.2", "react-native-launch-arguments": "^4.0.2", "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", @@ -31421,8 +31421,9 @@ "license": "MIT" }, "node_modules/react-native-keyboard-controller": { - "version": "1.11.7", - "resolved": "git+ssh://git@github.com/kirillzyusko/react-native-keyboard-controller.git#4cabd4d0a84515cffce2607cd7b2d39c0bc35710", + "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==", "peerDependencies": { "react": "*", "react-native": "*", diff --git a/package.json b/package.json index dcdaea896620..ca5bf014ef2a 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": "github:kirillzyusko/react-native-keyboard-controller#main", + "react-native-keyboard-controller": "^1.12.2", "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.11.7.patch b/patches/react-native-keyboard-controller+1.11.7.patch deleted file mode 100644 index 0ea97d3731a1..000000000000 --- a/patches/react-native-keyboard-controller+1.11.7.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 From d8dfad4ee50874735a47f4416775c7344644dde4 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 16:14:05 +0200 Subject: [PATCH 28/38] bring back --- ...ive-keyboard-controller+1.11.7.patch.patch | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 patches/react-native-keyboard-controller+1.11.7.patch.patch diff --git a/patches/react-native-keyboard-controller+1.11.7.patch.patch b/patches/react-native-keyboard-controller+1.11.7.patch.patch new file mode 100644 index 000000000000..3c8034354481 --- /dev/null +++ b/patches/react-native-keyboard-controller+1.11.7.patch.patch @@ -0,0 +1,39 @@ +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 7ae98a831c90696b0ff0419d7b8f1be6814d6946 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 16:19:39 +0200 Subject: [PATCH 29/38] lint --- src/App.tsx | 4 ++-- .../getBottomSuggestionPadding/index.ios.ts | 2 +- .../getBottomSuggestionPadding/index.ts | 2 +- .../AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx | 3 --- src/components/AutoCompleteSuggestions/index.tsx | 2 +- .../home/report/ReportActionCompose/ReportActionCompose.tsx | 1 - src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx | 4 ---- 7 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 09edbc3987c3..6cd37ac84bb2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -87,14 +87,14 @@ function App({url}: AppProps) { VideoPopoverMenuContextProvider, ]} > - + - + diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts index 03c852d40d37..5bb671c5edac 100644 --- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ios.ts @@ -1,5 +1,5 @@ function getBottomSuggestionPadding(): number { - return 16; + return 16; } export default getBottomSuggestionPadding; diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts index a812bf461442..3ad9bbe7b152 100644 --- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/getBottomSuggestionPadding/index.ts @@ -1,5 +1,5 @@ function getBottomSuggestionPadding(): number { - return 0; + return 0; } export default getBottomSuggestionPadding; diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx index d5393dce0846..da3bcd2affb6 100644 --- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx +++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx @@ -79,9 +79,6 @@ function BaseAutoCompleteSuggestions({ scrollRef.current.scrollToIndex({index: highlightedSuggestionIndex, animated: true}); }, [highlightedSuggestionIndex]); - // if (suggestions.length === 0) { - // return null; - // } return ( , ) { - const prevValueRef = useRef(value); - const prevSelectionEndRef = useRef(selection.end); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); const isEmojiSuggestionsMenuVisible = suggestionValues.suggestedEmojis.length > 0 && suggestionValues.shouldShowSuggestionMenu; @@ -187,7 +184,6 @@ function SuggestionEmoji( calculateEmojiSuggestion(selection.end); }, [selection, calculateEmojiSuggestion, isComposerFocused]); - const setShouldBlockSuggestionCalc = useCallback( (shouldBlockSuggestionCalc: boolean) => { shouldBlockCalc.current = shouldBlockSuggestionCalc; From aedb9b0615a1ac594abdff0e4f57737af0a853c2 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 17:30:37 +0200 Subject: [PATCH 30/38] ts --- src/components/Composer/types.ts | 10 ++++++++-- src/libs/ComposerUtils/index.ts | 3 ++- .../ComposerWithSuggestions.tsx | 14 +++++++------- .../report/ReportActionCompose/Suggestions.tsx | 10 +++------- .../ReportActionCompose/getCursorPosition/types.ts | 2 +- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index 531bcd03f8bf..6df9c96a73bb 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -3,6 +3,12 @@ import type {NativeSyntheticEvent, StyleProp, TextInputProps, TextInputSelection type TextSelection = { start: number; end?: number; + positionX?: number; + positionY?: number; +}; +type CustomSelectionChangeEvent = NativeSyntheticEvent & { + positionX?: number; + positionY?: number; }; type ComposerProps = TextInputProps & { @@ -45,7 +51,7 @@ type ComposerProps = TextInputProps & { autoFocus?: boolean; /** Update selection position on change */ - onSelectionChange?: (event: NativeSyntheticEvent) => void; + onSelectionChange?: (event: CustomSelectionChangeEvent) => void; /** Selection Object */ selection?: TextSelection; @@ -72,4 +78,4 @@ type ComposerProps = TextInputProps & { shouldContainScroll?: boolean; }; -export type {TextSelection, ComposerProps}; +export type {TextSelection, ComposerProps, CustomSelectionChangeEvent}; diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index 04d857a8faeb..7fc0299fa393 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -1,3 +1,4 @@ +import type {TextSelection} from '@components/Composer/types'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; type Selection = { @@ -8,7 +9,7 @@ type Selection = { /** * Replace substring between selection with a text. */ -function insertText(text: string, selection: Selection, textToInsert: string): string { +function insertText(text: string, selection: TextSelection, textToInsert: string): string { return text.slice(0, selection.start) + textToInsert + text.slice(selection.end, text.length); } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 5ced4c54bc28..b2c94c6625b8 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -11,7 +11,6 @@ import type { TextInputFocusEventData, TextInputKeyPressEventData, TextInputScrollEventData, - TextInputSelectionChangeEventData, } from 'react-native'; import {DeviceEventEmitter, findNodeHandle, InteractionManager, NativeModules, View} from 'react-native'; import {useFocusedInputHandler} from 'react-native-keyboard-controller'; @@ -23,6 +22,7 @@ import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; import type {MeasureParentContainerAndCursorCallback} from '@components/AutoCompleteSuggestions/types'; import Composer from '@components/Composer'; +import type {CustomSelectionChangeEvent, TextSelection} from '@components/Composer/types'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; @@ -294,7 +294,7 @@ function ComposerWithSuggestions( const valueRef = useRef(value); valueRef.current = value; - const [selection, setSelection] = useState(() => ({start: 0, end: 0, positionX: 0, positionY: 0})); + const [selection, setSelection] = useState(() => ({start: 0, end: 0, positionX: 0, positionY: 0})); const [composerHeight, setComposerHeight] = useState(0); @@ -382,9 +382,9 @@ function ComposerWithSuggestions( if (currentIndex < newText.length) { startIndex = currentIndex; - const commonSuffixLength = ComposerUtils.findCommonSuffixLength(prevText, newText, selection.end); + const commonSuffixLength = ComposerUtils.findCommonSuffixLength(prevText, newText, selection?.end ?? 0); // if text is getting pasted over find length of common suffix and subtract it from new text length - if (commonSuffixLength > 0 || selection.end - selection.start > 0) { + if (commonSuffixLength > 0 || (selection?.end ?? 0) - selection.start > 0) { endIndex = newText.length - commonSuffixLength; } else { endIndex = currentIndex + newText.length; @@ -434,7 +434,7 @@ function ComposerWithSuggestions( emojisPresentBefore.current = emojis; setValue(newCommentConverted); if (commentValue !== newComment) { - const position = Math.max(selection.end + (newComment.length - commentRef.current.length), cursorPosition ?? 0); + const position = Math.max((selection.end ?? 0) + (newComment.length - commentRef.current.length), cursorPosition ?? 0); if (isIOSNative) { syncSelectionWithOnChangeTextRef.current = {position, value: newComment}; @@ -488,7 +488,7 @@ function ComposerWithSuggestions( debouncedSaveReportComment.cancel(); isCommentPendingSaved.current = false; - setSelection({start: 0, end: 0}); + setSelection({start: 0, end: 0, positionX: 0, positionY: 0}); updateComment(''); setTextInputShouldClear(true); if (isComposerFullSize) { @@ -568,7 +568,7 @@ function ComposerWithSuggestions( ); const onSelectionChange = useCallback( - (e: NativeSyntheticEvent) => { + (e: CustomSelectionChangeEvent) => { if (!textInputRef.current?.isFocused()) { return; } diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.tsx b/src/pages/home/report/ReportActionCompose/Suggestions.tsx index c6a8e1a9979d..f82b38c3e154 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -3,17 +3,13 @@ import React, {forwardRef, useCallback, useContext, useEffect, useImperativeHand import type {NativeSyntheticEvent, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; import type {MeasureParentContainerAndCursorCallback} from '@components/AutoCompleteSuggestions/types'; +import type {TextSelection} from '@components/Composer/types'; import {DragAndDropContext} from '@components/DragAndDrop/Provider'; import usePrevious from '@hooks/usePrevious'; import type {SuggestionsRef} from './ReportActionCompose'; import SuggestionEmoji from './SuggestionEmoji'; import SuggestionMention from './SuggestionMention'; -type Selection = { - start: number; - end: number; -}; - type SuggestionProps = { /** The current input value */ value: string; @@ -22,10 +18,10 @@ type SuggestionProps = { setValue: (newValue: string) => void; /** The current selection value */ - selection: Selection; + selection: TextSelection; /** Callback to update the current selection */ - setSelection: (newSelection: Selection) => void; + setSelection: (newSelection: TextSelection) => void; /** Callback to update the comment draft */ updateComment: (newComment: string, shouldDebounceSaveComment?: boolean) => void; diff --git a/src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts b/src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts index a7caf9f1ef41..424e71377ecd 100644 --- a/src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts +++ b/src/pages/home/report/ReportActionCompose/getCursorPosition/types.ts @@ -5,7 +5,7 @@ type PositionType = { type CursorPositionParamsType = { positionOnMobile?: PositionType; - positionOnWeb?: {positionX: number; positionY: number}; + positionOnWeb?: {positionX?: number; positionY?: number}; }; type GetCursorPositionType = (params: CursorPositionParamsType) => PositionType; From 1f6eaa44b440fab61287951eebeb4f7bdfd51624 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Thu, 6 Jun 2024 20:49:58 +0200 Subject: [PATCH 31/38] safe selectionEnd --- .../home/report/ReportActionCompose/SuggestionMention.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index a0660e69c5e2..c39b8ef9eb61 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -277,8 +277,8 @@ function SuggestionMention( ); const calculateMentionSuggestion = useCallback( - (selectionEnd: number) => { - if (shouldBlockCalc.current || selectionEnd < 1 || !isComposerFocused) { + (selectionEnd?: number) => { + if (!selectionEnd || shouldBlockCalc.current || selectionEnd < 1 || !isComposerFocused) { shouldBlockCalc.current = false; resetSuggestions(); return; From 96064d7890379ca3663fd3b0b3211b97ad12a053 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 7 Jun 2024 11:31:54 +0200 Subject: [PATCH 32/38] fix scroll error --- .../AutoCompleteSuggestionsPortal/index.tsx | 17 ----------------- .../BaseAutoCompleteSuggestions.tsx | 12 ++++++++---- src/pages/home/ReportScreen.tsx | 6 +----- .../ReportActionCompose/ReportActionCompose.tsx | 4 +--- .../ReportActionCompose/SuggestionEmoji.tsx | 4 ++-- src/pages/home/report/ReportFooter.tsx | 6 ------ .../perf-test/ReportActionCompose.perf-test.tsx | 1 - 7 files changed, 12 insertions(+), 38 deletions(-) diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx index 3b247b516d41..2d1d533c2859 100644 --- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.tsx @@ -4,7 +4,6 @@ import ReactDOM from 'react-dom'; import {View} from 'react-native'; import BaseAutoCompleteSuggestions from '@components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions'; import useStyleUtils from '@hooks/useStyleUtils'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getBottomSuggestionPadding from './getBottomSuggestionPadding'; import type {AutoCompleteSuggestionsPortalProps} from './types'; @@ -17,30 +16,14 @@ import type {AutoCompleteSuggestionsPortalProps} from './types'; function AutoCompleteSuggestionsPortal({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps): ReactElement | null | false { const StyleUtils = useStyleUtils(); - const containerRef = React.useRef(null); const bodyElement = document.querySelector('body'); - React.useEffect(() => { - const container = containerRef.current; - if (!container) { - return () => {}; - } - container.onpointerdown = (e) => { - if (DeviceCapabilities.hasHoverSupport()) { - return; - } - e.preventDefault(); - }; - return () => (container.onpointerdown = null); - }, []); - const componentToRender = ( width={width} // eslint-disable-next-line react/jsx-props-no-spreading {...props} - ref={containerRef} /> ); diff --git a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx index da3bcd2affb6..70d70a8c1844 100644 --- a/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx +++ b/src/components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions.tsx @@ -1,7 +1,7 @@ import type {ReactElement} from 'react'; import React, {useCallback, useEffect, useRef} from 'react'; import {FlatList} from 'react-native-gesture-handler'; -import Animated, {Easing, FadeOutDown, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; +import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import ColorSchemeWrapper from '@components/ColorSchemeWrapper'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -14,7 +14,7 @@ import type {RenderSuggestionMenuItemProps} from './types'; type ExternalProps = Omit, 'left' | 'bottom'>; function BaseAutoCompleteSuggestions({ - highlightedSuggestionIndex, + highlightedSuggestionIndex = 0, onSelect, accessibilityLabelExtractor, renderSuggestionMenuItem, @@ -76,13 +76,17 @@ function BaseAutoCompleteSuggestions({ if (!scrollRef.current) { return; } - scrollRef.current.scrollToIndex({index: highlightedSuggestionIndex, animated: true}); + // When using cursor control (moving the cursor with the space bar on the keyboard) on Android, moving the cursor too fast may cause an error. + try { + scrollRef.current.scrollToIndex({index: highlightedSuggestionIndex, animated: true}); + } catch (e) { + // eslint-disable-next-line no-console + } }, [highlightedSuggestionIndex]); return ( { if (DeviceCapabilities.hasHoverSupport()) { return; diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 5ef7a27d2db3..2d2dee31fe78 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -7,7 +7,6 @@ import type {FlatList, ViewStyle} from 'react-native'; import {InteractionManager, View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {useOnyx, withOnyx} from 'react-native-onyx'; -import type {LayoutChangeEvent} from 'react-native/Libraries/Types/CoreEventTypes'; import Banner from '@components/Banner'; import BlockingView from '@components/BlockingViews/BlockingView'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -266,7 +265,6 @@ function ReportScreen({ }, [route, reportActionIDFromRoute]); const [isBannerVisible, setIsBannerVisible] = useState(true); - const [listHeight, setListHeight] = useState(0); const [scrollPosition, setScrollPosition] = useState({}); const wasReportAccessibleRef = useRef(false); @@ -593,8 +591,7 @@ function ReportScreen({ }; }, [report, didSubscribeToReportLeavingEvents, reportIDFromRoute]); - const onListLayout = useCallback((event: LayoutChangeEvent) => { - setListHeight((prev) => event.nativeEvent?.layout?.height ?? prev); + const onListLayout = useCallback(() => { if (!markReadyForHydration) { return; } @@ -715,7 +712,6 @@ function ReportScreen({ reportNameValuePairs={reportNameValuePairs} pendingAction={reportPendingAction} isComposerFullSize={!!isComposerFullSize} - listHeight={listHeight} isEmptyChat={isEmptyChat} lastReportAction={lastReportAction} /> diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index e765abf8cf74..3c76886c219d 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -72,7 +72,7 @@ type ReportActionComposeOnyxProps = { type ReportActionComposeProps = ReportActionComposeOnyxProps & WithCurrentUserPersonalDetailsProps & - Pick & { + Pick & { /** A method to call when the form is submitted */ onSubmit: (newComment: string) => void; @@ -110,7 +110,6 @@ function ReportActionCompose({ pendingAction, report, reportID, - listHeight = 0, shouldShowComposeInput = true, isReportReadyForDisplay = true, isEmptyChat, @@ -459,7 +458,6 @@ function ReportActionCompose({ onFocus={onFocus} onBlur={onBlur} measureParentContainer={measureContainer} - listHeight={listHeight} onValueChange={(value) => { if (value.length === 0 && isComposerFullSize) { Report.setIsComposerFullSize(reportID, false); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index a7a55668b810..b08ee77745db 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -149,8 +149,8 @@ function SuggestionEmoji( * Calculates and cares about the content of an Emoji Suggester */ const calculateEmojiSuggestion = useCallback( - (selectionEnd: number) => { - if (shouldBlockCalc.current || !value) { + (selectionEnd?: number) => { + if (!selectionEnd || shouldBlockCalc.current || !value) { shouldBlockCalc.current = false; resetSuggestions(); return; diff --git a/src/pages/home/report/ReportFooter.tsx b/src/pages/home/report/ReportFooter.tsx index ac56fe916bc9..9688e39b14a5 100644 --- a/src/pages/home/report/ReportFooter.tsx +++ b/src/pages/home/report/ReportFooter.tsx @@ -51,9 +51,6 @@ type ReportFooterProps = ReportFooterOnyxProps & { /** The pending action when we are adding a chat */ pendingAction?: PendingAction; - /** Height of the list which the composer is part of */ - listHeight?: number; - /** Whether the report is ready for display */ isReportReadyForDisplay?: boolean; @@ -76,7 +73,6 @@ function ReportFooter({ shouldShowComposeInput = false, isEmptyChat = true, isReportReadyForDisplay = true, - listHeight = 0, isComposerFullSize = false, blockedFromChat, onComposerBlur, @@ -176,7 +172,6 @@ function ReportFooter({ lastReportAction={lastReportAction} pendingAction={pendingAction} isComposerFullSize={isComposerFullSize} - listHeight={listHeight} isReportReadyForDisplay={isReportReadyForDisplay} /> @@ -205,7 +200,6 @@ export default withOnyx({ (prevProps, nextProps) => lodashIsEqual(prevProps.report, nextProps.report) && prevProps.pendingAction === nextProps.pendingAction && - prevProps.listHeight === nextProps.listHeight && prevProps.isComposerFullSize === nextProps.isComposerFullSize && prevProps.isEmptyChat === nextProps.isEmptyChat && prevProps.lastReportAction === nextProps.lastReportAction && diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx index 242008965c83..136b9c86de71 100644 --- a/tests/perf-test/ReportActionCompose.perf-test.tsx +++ b/tests/perf-test/ReportActionCompose.perf-test.tsx @@ -95,7 +95,6 @@ function ReportActionComposeWrapper() { disabled={false} report={LHNTestUtils.getFakeReport()} isComposerFullSize - listHeight={200} /> ); From 260f0368f2038aef032e12d01b45cbfae32cb92a Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 7 Jun 2024 11:38:18 +0200 Subject: [PATCH 33/38] clean --- .../NewExpensifyDebugDevelopment.entitlements | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 ios/NewExpensify/NewExpensifyDebugDevelopment.entitlements diff --git a/ios/NewExpensify/NewExpensifyDebugDevelopment.entitlements b/ios/NewExpensify/NewExpensifyDebugDevelopment.entitlements deleted file mode 100644 index efe74ad9e8d8..000000000000 --- a/ios/NewExpensify/NewExpensifyDebugDevelopment.entitlements +++ /dev/null @@ -1,14 +0,0 @@ - - - - - aps-environment - development - com.apple.developer.applesignin - - Default - - com.apple.developer.usernotifications.communication - - - From 2ad390c10d16891f17fa3c46a0ea455bcc479c9a Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 7 Jun 2024 11:56:20 +0200 Subject: [PATCH 34/38] rename patch --- ...native-keyboard-controller+1.12.2.patch.patch} | 0 src/App.tsx | 15 +++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) rename patches/{react-native-keyboard-controller+1.11.7.patch.patch => react-native-keyboard-controller+1.12.2.patch.patch} (100%) diff --git a/patches/react-native-keyboard-controller+1.11.7.patch.patch b/patches/react-native-keyboard-controller+1.12.2.patch.patch similarity index 100% rename from patches/react-native-keyboard-controller+1.11.7.patch.patch rename to patches/react-native-keyboard-controller+1.12.2.patch.patch diff --git a/src/App.tsx b/src/App.tsx index 6cd37ac84bb2..64c75e61941e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -85,16 +85,15 @@ function App({url}: AppProps) { FullScreenContextProvider, VolumeContextProvider, VideoPopoverMenuContextProvider, + KeyboardProvider, ]} > - - - - - - - - + + + + + + From 9d7dca8756b2a1032419119d1bf32fde8c925cd6 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 7 Jun 2024 11:56:36 +0200 Subject: [PATCH 35/38] fix test --- tests/ui/UnreadIndicatorsTest.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 0f13062b2e94..003d266d0fb7 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -207,8 +207,10 @@ function signInAndGetAppWithUnreadChat(): Promise { .then(async () => { await waitForBatchedUpdatesWithAct(); const hintText = Localize.translateLocal('loginForm.loginForm'); - const loginForm = screen.queryAllByLabelText(hintText); - expect(loginForm).toHaveLength(1); + await waitFor(() =>{ + const loginForm = screen.queryAllByLabelText(hintText); + expect(loginForm).toHaveLength(1); + }) await act(async () => { await TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); From 3cd18668a41d8e68f81af0b3622a89b8e4e5020e Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 7 Jun 2024 12:18:07 +0200 Subject: [PATCH 36/38] remove waitFor --- tests/ui/UnreadIndicatorsTest.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 003d266d0fb7..0f13062b2e94 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -207,10 +207,8 @@ function signInAndGetAppWithUnreadChat(): Promise { .then(async () => { await waitForBatchedUpdatesWithAct(); const hintText = Localize.translateLocal('loginForm.loginForm'); - await waitFor(() =>{ - const loginForm = screen.queryAllByLabelText(hintText); - expect(loginForm).toHaveLength(1); - }) + const loginForm = screen.queryAllByLabelText(hintText); + expect(loginForm).toHaveLength(1); await act(async () => { await TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); From 34984eff252e1a559d089e503ef33e65fd2d5748 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Fri, 7 Jun 2024 13:41:14 +0200 Subject: [PATCH 37/38] jest mock --- jest/setup.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jest/setup.ts b/jest/setup.ts index 174e59a7e493..543439b76d38 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -53,3 +53,6 @@ jest.mock('react-native-sound', () => { jest.mock('react-native-share', () => ({ default: jest.fn(), })); + +// eslint-disable-next-line @typescript-eslint/no-unsafe-return +jest.mock('react-native-keyboard-controller', () => require('react-native-keyboard-controller/jest')); From 305eebb09733c6eac108dbb0bce3aa360ade038b Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 18 Jun 2024 08:22:40 +0200 Subject: [PATCH 38/38] clean --- .../AutoCompleteSuggestionsPortal/index.native.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx index 213d6257558f..9848d77e479e 100644 --- a/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx +++ b/src/components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/index.native.tsx @@ -9,7 +9,6 @@ import type {AutoCompleteSuggestionsPortalProps} from './types'; function AutoCompleteSuggestionsPortal({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps) { const StyleUtils = useStyleUtils(); const styles = useMemo(() => StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom + getBottomSuggestionPadding()}), [StyleUtils, left, width, bottom]); - // const styles = useMemo(() => StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom + 0}), [StyleUtils, left, width, bottom]); if (!width) { return null;