From b107b9caa20bd2de3545da82190c4bf7671d5820 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Tue, 5 Dec 2023 21:53:36 +0100 Subject: [PATCH] Fix Composer not positioning caret properly on iOS after substituting emoji for marker --- src/components/Composer/index.ios.js | 4 ++- .../ComposerWithSuggestions.js | 30 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/components/Composer/index.ios.js b/src/components/Composer/index.ios.js index b2d7a6e89230..a1b8c1a4ffe6 100644 --- a/src/components/Composer/index.ios.js +++ b/src/components/Composer/index.ios.js @@ -110,6 +110,8 @@ function Composer({shouldClear, onClear, isDisabled, maxLines, forwardedRef, isC // On native layers we like to have the Text Input not focused so the // user can read new chats without the keyboard in the way of the view. + // On Android the selection prop is required on the TextInput but this prop has issues on IOS + const propsToPass = _.omit(props, 'selection'); return ( ); diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index 663db82a6067..c363fb4e0f72 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -1,7 +1,7 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; -import {findNodeHandle, NativeModules, View} from 'react-native'; +import {findNodeHandle, InteractionManager, NativeModules, Platform, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import Composer from '@components/Composer'; @@ -44,6 +44,7 @@ const {RNTextInputReset} = NativeModules; // and subsequent programmatic focus shifts (e.g., modal focus trap) to show the blue frame (:focus-visible style), // so we need to ensure that it is only updated after focus. const isMobileSafari = Browser.isMobileSafari(); +const isIOSNative = Platform.OS === 'ios'; /** * Broadcast that the user is typing. Debounced to limit how often we publish client events. @@ -142,6 +143,8 @@ function ComposerWithSuggestions({ const textInputRef = useRef(null); const insertedEmojisRef = useRef([]); + const syncSelectionWithOnChangeTextRef = useRef(null); + // A flag to indicate whether the onScroll callback is likely triggered by a layout change (caused by text change) or not const isScrollLikelyLayoutTriggered = useRef(false); const suggestions = lodashGet(suggestionsRef, 'current.getSuggestions', () => [])(); @@ -241,6 +244,11 @@ function ComposerWithSuggestions({ setValue(newCommentConverted); if (commentValue !== newComment) { const position = Math.max(selection.end + (newComment.length - commentRef.current.length), cursorPosition || 0); + + if (isIOSNative) { + syncSelectionWithOnChangeTextRef.current = {position, value: newComment}; + } + setSelection({ start: position, end: position, @@ -543,7 +551,25 @@ function ComposerWithSuggestions({ ref={setTextInputRef} placeholder={inputPlaceholder} placeholderTextColor={theme.placeholderText} - onChangeText={(commentValue) => updateComment(commentValue, true)} + onChangeText={(commentValue) => { + if (syncSelectionWithOnChangeTextRef.current !== null) { + setSelection({ + start: syncSelectionWithOnChangeTextRef.current.position, + end: syncSelectionWithOnChangeTextRef.current.position, + }); + } + + updateComment(commentValue, true); + + if (syncSelectionWithOnChangeTextRef.current !== null) { + let positionSnaphsot = syncSelectionWithOnChangeTextRef.current.position; + syncSelectionWithOnChangeTextRef.current = null; + + InteractionManager.runAfterInteractions(() => { + textInputRef.current.setSelection(positionSnaphsot, positionSnaphsot); + }); + } + }} onKeyPress={triggerHotkeyActions} textAlignVertical="top" style={[styles.textInputCompose, isComposerFullSize ? styles.textInputFullCompose : styles.flex4]}