From 5ba5ee73d2d13401cec7d1424050a9dec37ea3a8 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Mon, 18 Sep 2023 22:34:55 +0530 Subject: [PATCH 01/14] multiline focus logic updated --- ...tRange.js => UpdateMultilineInputRange.js} | 3 +- src/pages/EditRequestDescriptionPage.js | 81 +++++++---- .../PrivateNotes/PrivateNotesEditPage.js | 130 ++++++++++-------- src/pages/ReportWelcomeMessagePage.js | 13 +- src/pages/iou/MoneyRequestDescriptionPage.js | 80 +++++++---- src/pages/tasks/NewTaskDescriptionPage.js | 80 +++++++---- src/pages/tasks/TaskDescriptionPage.js | 18 ++- 7 files changed, 259 insertions(+), 146 deletions(-) rename src/libs/{focusAndUpdateMultilineInputRange.js => UpdateMultilineInputRange.js} (91%) diff --git a/src/libs/focusAndUpdateMultilineInputRange.js b/src/libs/UpdateMultilineInputRange.js similarity index 91% rename from src/libs/focusAndUpdateMultilineInputRange.js rename to src/libs/UpdateMultilineInputRange.js index b5e438899d3d..018ba8d98bf1 100644 --- a/src/libs/focusAndUpdateMultilineInputRange.js +++ b/src/libs/UpdateMultilineInputRange.js @@ -9,12 +9,11 @@ * * @param {Object} input the input element */ -export default function focusAndUpdateMultilineInputRange(input) { +export default function UpdateMultilineInputRange(input) { if (!input) { return; } - input.focus(); if (input.value && input.setSelectionRange) { const length = input.value.length; input.setSelectionRange(length, length); diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index 3916daecd33c..a726409364b9 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -11,6 +11,8 @@ import Navigation from '../libs/Navigation/Navigation'; import CONST from '../CONST'; import useLocalize from '../hooks/useLocalize'; import * as Browser from '../libs/Browser'; +import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { /** Transaction default description value */ @@ -27,36 +29,59 @@ function EditRequestDescriptionPage({defaultDescription, onSubmit}) { descriptionInputRef.current && descriptionInputRef.current.focus()} + onEntryTransitionEnd={() => { + if (!descriptionInputRef.current) { + return; + } + UpdateMultilineInputRange(descriptionInputRef.current); + descriptionInputRef.current.focus(); + }} > - Navigation.goBack()} - /> -
- - (descriptionInputRef.current = e)} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} + {(didScreenTransitionEnd) => ( + <> + Navigation.goBack()} /> - -
+
+ + { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!descriptionInputRef.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + descriptionInputRef.current = el; + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+ + )}
); } diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 4cada83941ac..96d9cd370dbe 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -23,7 +23,8 @@ import personalDetailsPropType from '../personalDetailsPropType'; import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** All of the personal details for everyone */ @@ -78,62 +79,83 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { return ( focusAndUpdateMultilineInputRange(privateNotesInput.current)} + onEntryTransitionEnd={() => { + if (!privateNotesInput.current) { + return; + } + UpdateMultilineInputRange(privateNotesInput.current); + privateNotesInput.current.focus(); + }} > - Navigation.goBack()} - > - Navigation.dismissModal()} + {(didScreenTransitionEnd) => ( + Navigation.goBack()} - /> - - - - {translate( - Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN - ? 'privateNotes.sharedNoteMessage' - : 'privateNotes.personalNoteMessage', - )} - - -
- Report.clearPrivateNotesError(report.reportID, route.params.accountID)} - style={[styles.mb3]} + > + Navigation.dismissModal()} + onBackButtonPress={() => Navigation.goBack()} + /> + + + + {translate( + Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN + ? 'privateNotes.sharedNoteMessage' + : 'privateNotes.personalNoteMessage', + )} + + + - setPrivateNote(text)} - ref={(el) => (privateNotesInput.current = el)} - /> - -
-
-
+ Report.clearPrivateNotesError(report.reportID, route.params.accountID)} + style={[styles.mb3]} + > + setPrivateNote(text)} + ref={(el) => { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!privateNotesInput.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + privateNotesInput.current = el; + }} + /> + + + +
+ )}
); } diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index f14b6c48b9dd..2f2db8819019 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -19,7 +19,8 @@ import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundVi import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; -import focusAndUpdateMultilineInputRange from '../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { ...withLocalizePropTypes, @@ -60,7 +61,8 @@ function ReportWelcomeMessagePage(props) { if (!welcomeMessageInputRef.current) { return; } - focusAndUpdateMultilineInputRange(welcomeMessageInputRef.current); + UpdateMultilineInputRange(welcomeMessageInputRef.current); + welcomeMessageInputRef.current.focus(); }} > {({didScreenTransitionEnd}) => ( @@ -88,8 +90,13 @@ function ReportWelcomeMessagePage(props) { if (!el) { return; } + UpdateMultilineInputRange(el); if (!welcomeMessageInputRef.current && didScreenTransitionEnd) { - focusAndUpdateMultilineInputRange(el); + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); } welcomeMessageInputRef.current = el; }} diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index 63aea67ce598..c7f4113b7be7 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -17,7 +17,8 @@ import * as IOU from '../../libs/actions/IOU'; import * as MoneyRequestUtils from '../../libs/MoneyRequestUtils'; import CONST from '../../CONST'; import useLocalize from '../../hooks/useLocalize'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -89,35 +90,58 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { focusAndUpdateMultilineInputRange(inputRef.current)} + onEntryTransitionEnd={() => { + if (!inputRef.current) { + return; + } + UpdateMultilineInputRange(inputRef.current); + inputRef.current.focus(); + }} > - navigateBack()} - /> -
updateComment(value)} - submitButtonText={translate('common.save')} - enabledWhenOffline - > - - (inputRef.current = el)} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} + {({didScreenTransitionEnd}) => ( + <> + navigateBack()} /> - -
+
updateComment(value)} + submitButtonText={translate('common.save')} + enabledWhenOffline + > + + { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!inputRef.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + inputRef.current = el; + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+ + )}
); } diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index dacf368a574e..3f3c764f6417 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -14,8 +14,9 @@ import TextInput from '../../components/TextInput'; import Permissions from '../../libs/Permissions'; import ROUTES from '../../ROUTES'; import * as Task from '../../libs/actions/Task'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import CONST from '../../CONST'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -55,36 +56,59 @@ function NewTaskDescriptionPage(props) { return ( focusAndUpdateMultilineInputRange(inputRef.current)} + onEntryTransitionEnd={() => { + if (!inputRef.current) { + return; + } + UpdateMultilineInputRange(inputRef.current); + inputRef.current.focus(); + }} shouldEnableMaxHeight > - Task.dismissModalAndClearOutTaskInfo()} - onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} - /> -
onSubmit(values)} - enabledWhenOffline - > - - (inputRef.current = el)} - autoGrowHeight - submitOnEnter={!Browser.isMobile()} - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" + {({didScreenTransitionEnd}) => ( + <> + Task.dismissModalAndClearOutTaskInfo()} + onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} /> - -
+
onSubmit(values)} + enabledWhenOffline + > + + { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!inputRef.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + inputRef.current = el; + }} + autoGrowHeight + submitOnEnter={!Browser.isMobile()} + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + /> + +
+ + )}
); } diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 382e8d80d4ad..14641cf4397d 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -14,12 +14,13 @@ import compose from '../../libs/compose'; import * as Task from '../../libs/actions/Task'; import * as ReportUtils from '../../libs/ReportUtils'; import CONST from '../../CONST'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import * as Browser from '../../libs/Browser'; import Navigation from '../../libs/Navigation/Navigation'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails'; import withReportOrNotFound from '../home/report/withReportOrNotFound'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** Current user session */ @@ -65,7 +66,13 @@ function TaskDescriptionPage(props) { return ( focusAndUpdateMultilineInputRange(inputRef.current)} + onEntryTransitionEnd={() => { + if (!inputRef.current) { + return; + } + UpdateMultilineInputRange(inputRef.current); + inputRef.current.focus(); + }} shouldEnableMaxHeight > {({didScreenTransitionEnd}) => ( @@ -92,8 +99,13 @@ function TaskDescriptionPage(props) { if (!el) { return; } + UpdateMultilineInputRange(el); if (!inputRef.current && didScreenTransitionEnd) { - focusAndUpdateMultilineInputRange(el); + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); } inputRef.current = el; }} From 32168a280c3357cc8c0bf97d4a466dd0d7cbba16 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Tue, 19 Sep 2023 01:02:04 +0530 Subject: [PATCH 02/14] using focus hook --- src/pages/ReportWelcomeMessagePage.js | 44 ++++++++++++--------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 2f2db8819019..7f29d012326c 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import {View} from 'react-native'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import {useFocusEffect} from '@react-navigation/native'; import compose from '../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; import ScreenWrapper from '../components/ScreenWrapper'; @@ -20,7 +21,6 @@ import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { ...withLocalizePropTypes, @@ -46,6 +46,7 @@ function ReportWelcomeMessagePage(props) { const parser = new ExpensiMark(); const [welcomeMessage, setWelcomeMessage] = useState(parser.htmlToMarkdown(props.report.welcomeMessage)); const welcomeMessageInputRef = useRef(null); + const focusTimeoutRef = useRef(null); const handleWelcomeMessageChange = useCallback((value) => { setWelcomeMessage(value); @@ -55,18 +56,25 @@ function ReportWelcomeMessagePage(props) { Report.updateWelcomeMessage(props.report.reportID, props.report.welcomeMessage, welcomeMessage.trim()); }, [props.report.reportID, props.report.welcomeMessage, welcomeMessage]); + useFocusEffect( + useCallback(() => { + + focusTimeoutRef.current = setTimeout(() => { + welcomeMessageInputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, [welcomeMessageInputRef]) + ) + return ( { - if (!welcomeMessageInputRef.current) { - return; - } - UpdateMultilineInputRange(welcomeMessageInputRef.current); - welcomeMessageInputRef.current.focus(); - }} > - {({didScreenTransitionEnd}) => ( - +
{ - // Before updating the DOM, React sets the affected ref.current values to null. After updating the DOM, React immediately sets them to the corresponding DOM nodes - // to avoid focus multiple time, we should early return if el is null. - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!welcomeMessageInputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } + if(!el) return; welcomeMessageInputRef.current = el; + UpdateMultilineInputRange(welcomeMessageInputRef.current); }} value={welcomeMessage} onChangeText={handleWelcomeMessageChange} @@ -109,7 +106,6 @@ function ReportWelcomeMessagePage(props) {
- )}
); } From cadf08232889f77fd5945ef8519dd5e3e390159b Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Tue, 19 Sep 2023 01:24:35 +0530 Subject: [PATCH 03/14] using hook in other multiline inputs --- src/pages/EditRequestDescriptionPage.js | 99 +++++++------ .../PrivateNotes/PrivateNotesEditPage.js | 140 +++++++++--------- src/pages/ReportWelcomeMessagePage.js | 72 +++++---- src/pages/iou/MoneyRequestDescriptionPage.js | 96 ++++++------ src/pages/tasks/NewTaskDescriptionPage.js | 105 +++++++------ src/pages/tasks/TaskDescriptionPage.js | 98 ++++++------ 6 files changed, 307 insertions(+), 303 deletions(-) diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index a726409364b9..850272c089cf 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -1,6 +1,7 @@ -import React, {useRef} from 'react'; +import React, {useRef, useCallback} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; +import {useFocusEffect} from '@react-navigation/native'; import TextInput from '../components/TextInput'; import ScreenWrapper from '../components/ScreenWrapper'; import HeaderWithBackButton from '../components/HeaderWithBackButton'; @@ -12,7 +13,6 @@ import CONST from '../CONST'; import useLocalize from '../hooks/useLocalize'; import * as Browser from '../libs/Browser'; import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { /** Transaction default description value */ @@ -25,6 +25,22 @@ const propTypes = { function EditRequestDescriptionPage({defaultDescription, onSubmit}) { const {translate} = useLocalize(); const descriptionInputRef = useRef(null); + const focusTimeoutRef = useRef(null); + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (descriptionInputRef.current) descriptionInputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + return ( - {(didScreenTransitionEnd) => ( - <> - Navigation.goBack()} - /> -
- - { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!descriptionInputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - descriptionInputRef.current = el; - }} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} - /> - -
- - )} + <> + Navigation.goBack()} + /> +
+ + { + if (!el) return; + descriptionInputRef.current = el; + UpdateMultilineInputRange(descriptionInputRef.current); + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+
); } diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 96d9cd370dbe..f84dc41ea9a5 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -1,7 +1,8 @@ -import React, {useState, useRef} from 'react'; +import React, {useState, useRef, useCallback} from 'react'; import PropTypes from 'prop-types'; import {View, Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; @@ -24,7 +25,6 @@ import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** All of the personal details for everyone */ @@ -66,6 +66,21 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { // To focus on the input field when the page loads const privateNotesInput = useRef(null); + const focusTimeoutRef = useRef(null); + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (privateNotesInput.current) privateNotesInput.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); const savePrivateNote = () => { const editedNote = parser.replace(privateNote); @@ -87,75 +102,64 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { privateNotesInput.current.focus(); }} > - {(didScreenTransitionEnd) => ( - Navigation.goBack()} + > + Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} - > - Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack()} - /> - - - - {translate( - Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN - ? 'privateNotes.sharedNoteMessage' - : 'privateNotes.personalNoteMessage', - )} - - -
+ + + + {translate( + Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN + ? 'privateNotes.sharedNoteMessage' + : 'privateNotes.personalNoteMessage', + )} + + + + Report.clearPrivateNotesError(report.reportID, route.params.accountID)} + style={[styles.mb3]} > - setPrivateNote(text)} + ref={(el) => { + if (!el) return; + privateNotesInput.current = el; + UpdateMultilineInputRange(privateNotesInput.current); }} - onClose={() => Report.clearPrivateNotesError(report.reportID, route.params.accountID)} - style={[styles.mb3]} - > - setPrivateNote(text)} - ref={(el) => { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!privateNotesInput.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - privateNotesInput.current = el; - }} - /> - - -
-
- )} + /> + + + +
); } diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 7f29d012326c..fc05b4ae5d56 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -58,9 +58,8 @@ function ReportWelcomeMessagePage(props) { useFocusEffect( useCallback(() => { - focusTimeoutRef.current = setTimeout(() => { - welcomeMessageInputRef.current.focus(); + if (welcomeMessageInputRef.current) welcomeMessageInputRef.current.focus(); return () => { if (!focusTimeoutRef.current) { return; @@ -68,44 +67,43 @@ function ReportWelcomeMessagePage(props) { clearTimeout(focusTimeoutRef.current); }; }, CONST.ANIMATED_TRANSITION); - }, [welcomeMessageInputRef]) - ) + }, []), + ); return ( - + - -
- {props.translate('welcomeMessagePage.explainerText')} - - { - if(!el) return; - welcomeMessageInputRef.current = el; - UpdateMultilineInputRange(welcomeMessageInputRef.current); - }} - value={welcomeMessage} - onChangeText={handleWelcomeMessageChange} - autoCapitalize="none" - textAlignVertical="top" - containerStyles={[styles.autoGrowHeightMultilineInput]} - /> - -
-
+ +
+ {props.translate('welcomeMessagePage.explainerText')} + + { + if (!el) return; + welcomeMessageInputRef.current = el; + UpdateMultilineInputRange(welcomeMessageInputRef.current); + }} + value={welcomeMessage} + onChangeText={handleWelcomeMessageChange} + autoCapitalize="none" + textAlignVertical="top" + containerStyles={[styles.autoGrowHeightMultilineInput]} + /> + +
+
); } diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index c7f4113b7be7..692e5f0ccac3 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -1,6 +1,7 @@ -import React, {useEffect, useRef} from 'react'; +import React, {useEffect, useRef, useCallback} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import lodashGet from 'lodash/get'; @@ -18,7 +19,6 @@ import * as MoneyRequestUtils from '../../libs/MoneyRequestUtils'; import CONST from '../../CONST'; import useLocalize from '../../hooks/useLocalize'; import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -55,10 +55,25 @@ const defaultProps = { function MoneyRequestDescriptionPage({iou, route, selectedTab}) { const {translate} = useLocalize(); const inputRef = useRef(null); + const focusTimeoutRef = useRef(null); const iouType = lodashGet(route, 'params.iouType', ''); const reportID = lodashGet(route, 'params.reportID', ''); const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, selectedTab); + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (inputRef.current) inputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + useEffect(() => { const moneyRequestId = `${iouType}${reportID}`; const shouldReset = iou.id !== moneyRequestId; @@ -98,50 +113,39 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { inputRef.current.focus(); }} > - {({didScreenTransitionEnd}) => ( - <> - navigateBack()} - /> -
updateComment(value)} - submitButtonText={translate('common.save')} - enabledWhenOffline - > - - { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!inputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - inputRef.current = el; - }} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} - /> - -
- - )} + <> + navigateBack()} + /> +
updateComment(value)} + submitButtonText={translate('common.save')} + enabledWhenOffline + > + + { + if (!el) return; + inputRef.current = el; + UpdateMultilineInputRange(inputRef.current); + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+
); } diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index 3f3c764f6417..d3cdc52553d1 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -1,6 +1,7 @@ -import React, {useRef} from 'react'; +import React, {useRef, useCallback} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; @@ -16,7 +17,6 @@ import ROUTES from '../../ROUTES'; import * as Task from '../../libs/actions/Task'; import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import CONST from '../../CONST'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -41,9 +41,24 @@ const defaultProps = { function NewTaskDescriptionPage(props) { const inputRef = useRef(null); - + const focusTimeoutRef = useRef(null); // On submit, we want to call the assignTask function and wait to validate // the response + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (inputRef.current) inputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + const onSubmit = (values) => { Task.setDescriptionValue(values.taskDescription); Navigation.goBack(ROUTES.NEW_TASK); @@ -56,59 +71,41 @@ function NewTaskDescriptionPage(props) { return ( { - if (!inputRef.current) { - return; - } - UpdateMultilineInputRange(inputRef.current); - inputRef.current.focus(); - }} shouldEnableMaxHeight > - {({didScreenTransitionEnd}) => ( - <> - Task.dismissModalAndClearOutTaskInfo()} - onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} - /> -
onSubmit(values)} - enabledWhenOffline - > - - { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!inputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - inputRef.current = el; - }} - autoGrowHeight - submitOnEnter={!Browser.isMobile()} - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - /> - -
- - )} + <> + Task.dismissModalAndClearOutTaskInfo()} + onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} + /> +
onSubmit(values)} + enabledWhenOffline + > + + { + if (!el) return; + inputRef.current = el; + UpdateMultilineInputRange(inputRef.current); + }} + autoGrowHeight + submitOnEnter={!Browser.isMobile()} + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + /> + +
+
); } diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 14641cf4397d..0d16f8b1e5bc 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -1,6 +1,7 @@ import React, {useCallback, useRef} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; +import {useFocusEffect} from '@react-navigation/native'; import {withOnyx} from 'react-native-onyx'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -20,7 +21,6 @@ import Navigation from '../../libs/Navigation/Navigation'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails'; import withReportOrNotFound from '../home/report/withReportOrNotFound'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** Current user session */ @@ -58,66 +58,62 @@ function TaskDescriptionPage(props) { }); } const inputRef = useRef(null); + const focusTimeoutRef = useRef(null); const isOpen = ReportUtils.isOpenTaskReport(props.report); const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID); const isTaskNonEditable = ReportUtils.isTaskReport(props.report) && (!canModifyTask || !isOpen); + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (inputRef.current) inputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + return ( { - if (!inputRef.current) { - return; - } - UpdateMultilineInputRange(inputRef.current); - inputRef.current.focus(); - }} shouldEnableMaxHeight > - {({didScreenTransitionEnd}) => ( - - -
- - { - // if we wrap the page with FullPageNotFoundView we need to explicitly handle focusing on text input - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!inputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - inputRef.current = el; - }} - autoGrowHeight - submitOnEnter={!Browser.isMobile()} - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - /> - -
-
- )} + + +
+ + { + if (!el) return; + inputRef.current = el; + UpdateMultilineInputRange(inputRef.current); + }} + autoGrowHeight + submitOnEnter={!Browser.isMobile()} + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + /> + +
+
); } From 82361a78cb4817e42695715b9da1e7288d860b06 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Tue, 19 Sep 2023 22:59:16 +0530 Subject: [PATCH 04/14] refactor --- src/pages/EditRequestDescriptionPage.js | 7 ------- src/pages/PrivateNotes/PrivateNotesEditPage.js | 7 ------- src/pages/iou/MoneyRequestDescriptionPage.js | 7 ------- 3 files changed, 21 deletions(-) diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index 850272c089cf..ba0fd0dbd854 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -45,13 +45,6 @@ function EditRequestDescriptionPage({defaultDescription, onSubmit}) { { - if (!descriptionInputRef.current) { - return; - } - UpdateMultilineInputRange(descriptionInputRef.current); - descriptionInputRef.current.focus(); - }} > <> { - if (!privateNotesInput.current) { - return; - } - UpdateMultilineInputRange(privateNotesInput.current); - privateNotesInput.current.focus(); - }} > { - if (!inputRef.current) { - return; - } - UpdateMultilineInputRange(inputRef.current); - inputRef.current.focus(); - }} > <> Date: Tue, 19 Sep 2023 23:21:34 +0530 Subject: [PATCH 05/14] prettier --- src/pages/PrivateNotes/PrivateNotesEditPage.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index ee3c951fadf3..a47c54a53d4f 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -92,9 +92,7 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { }; return ( - + Date: Tue, 19 Sep 2023 23:38:08 +0530 Subject: [PATCH 06/14] rename --- src/libs/UpdateMultilineInputRange.js | 2 +- src/pages/EditRequestDescriptionPage.js | 4 ++-- src/pages/PrivateNotes/PrivateNotesEditPage.js | 4 ++-- src/pages/ReportWelcomeMessagePage.js | 4 ++-- src/pages/iou/MoneyRequestDescriptionPage.js | 4 ++-- src/pages/tasks/NewTaskDescriptionPage.js | 4 ++-- src/pages/tasks/TaskDescriptionPage.js | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libs/UpdateMultilineInputRange.js b/src/libs/UpdateMultilineInputRange.js index 018ba8d98bf1..14735ca3804f 100644 --- a/src/libs/UpdateMultilineInputRange.js +++ b/src/libs/UpdateMultilineInputRange.js @@ -9,7 +9,7 @@ * * @param {Object} input the input element */ -export default function UpdateMultilineInputRange(input) { +export default function updateMultilineInputRange(input) { if (!input) { return; } diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index ba0fd0dbd854..fdf20752c961 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -12,7 +12,7 @@ import Navigation from '../libs/Navigation/Navigation'; import CONST from '../CONST'; import useLocalize from '../hooks/useLocalize'; import * as Browser from '../libs/Browser'; -import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; +import updateMultilineInputRange from '../libs/updateMultilineInputRange'; const propTypes = { /** Transaction default description value */ @@ -70,7 +70,7 @@ function EditRequestDescriptionPage({defaultDescription, onSubmit}) { ref={(el) => { if (!el) return; descriptionInputRef.current = el; - UpdateMultilineInputRange(descriptionInputRef.current); + updateMultilineInputRange(descriptionInputRef.current); }} autoGrowHeight containerStyles={[styles.autoGrowHeightMultilineInput]} diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index a47c54a53d4f..c7ebf6ba8c5c 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -24,7 +24,7 @@ import personalDetailsPropType from '../personalDetailsPropType'; import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; -import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; const propTypes = { /** All of the personal details for everyone */ @@ -144,7 +144,7 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { ref={(el) => { if (!el) return; privateNotesInput.current = el; - UpdateMultilineInputRange(privateNotesInput.current); + updateMultilineInputRange(privateNotesInput.current); }} /> diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index fc05b4ae5d56..97269905723b 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -20,7 +20,7 @@ import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundVi import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; -import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; +import updateMultilineInputRange from '../libs/updateMultilineInputRange'; const propTypes = { ...withLocalizePropTypes, @@ -93,7 +93,7 @@ function ReportWelcomeMessagePage(props) { ref={(el) => { if (!el) return; welcomeMessageInputRef.current = el; - UpdateMultilineInputRange(welcomeMessageInputRef.current); + updateMultilineInputRange(welcomeMessageInputRef.current); }} value={welcomeMessage} onChangeText={handleWelcomeMessageChange} diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index ed99b7d1a118..817e947dd53c 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -18,7 +18,7 @@ import * as IOU from '../../libs/actions/IOU'; import * as MoneyRequestUtils from '../../libs/MoneyRequestUtils'; import CONST from '../../CONST'; import useLocalize from '../../hooks/useLocalize'; -import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -129,7 +129,7 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { ref={(el) => { if (!el) return; inputRef.current = el; - UpdateMultilineInputRange(inputRef.current); + updateMultilineInputRange(inputRef.current); }} autoGrowHeight containerStyles={[styles.autoGrowHeightMultilineInput]} diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index d3cdc52553d1..47429e6d3ac8 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -15,7 +15,7 @@ import TextInput from '../../components/TextInput'; import Permissions from '../../libs/Permissions'; import ROUTES from '../../ROUTES'; import * as Task from '../../libs/actions/Task'; -import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; import CONST from '../../CONST'; import * as Browser from '../../libs/Browser'; @@ -96,7 +96,7 @@ function NewTaskDescriptionPage(props) { ref={(el) => { if (!el) return; inputRef.current = el; - UpdateMultilineInputRange(inputRef.current); + updateMultilineInputRange(inputRef.current); }} autoGrowHeight submitOnEnter={!Browser.isMobile()} diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 0d16f8b1e5bc..3020d2ec056d 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -15,7 +15,7 @@ import compose from '../../libs/compose'; import * as Task from '../../libs/actions/Task'; import * as ReportUtils from '../../libs/ReportUtils'; import CONST from '../../CONST'; -import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; import * as Browser from '../../libs/Browser'; import Navigation from '../../libs/Navigation/Navigation'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; @@ -104,7 +104,7 @@ function TaskDescriptionPage(props) { ref={(el) => { if (!el) return; inputRef.current = el; - UpdateMultilineInputRange(inputRef.current); + updateMultilineInputRange(inputRef.current); }} autoGrowHeight submitOnEnter={!Browser.isMobile()} From 208325c44aa855733afd045688586d803d9195b2 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Wed, 20 Sep 2023 21:25:24 +0530 Subject: [PATCH 07/14] jsdoc edit --- src/libs/UpdateMultilineInputRange.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/UpdateMultilineInputRange.js b/src/libs/UpdateMultilineInputRange.js index 14735ca3804f..179d30dc611d 100644 --- a/src/libs/UpdateMultilineInputRange.js +++ b/src/libs/UpdateMultilineInputRange.js @@ -1,5 +1,5 @@ /** - * Focus a multiline text input and place the cursor at the end of the value (if there is a value in the input). + * Place the cursor at the end of the value (if there is a value in the input). * * When a multiline input contains a text value that goes beyond the scroll height, the cursor will be placed * at the end of the text value, and automatically scroll the input field to this position after the field gains From 8cb52967890e3e1ab922ff10fc7a78d3bb804e30 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Wed, 20 Sep 2023 21:37:37 +0530 Subject: [PATCH 08/14] lint fix --- src/pages/EditRequestDescriptionPage.js | 8 ++++++-- src/pages/PrivateNotes/PrivateNotesEditPage.js | 8 ++++++-- src/pages/ReportWelcomeMessagePage.js | 8 ++++++-- src/pages/iou/MoneyRequestDescriptionPage.js | 8 ++++++-- src/pages/tasks/NewTaskDescriptionPage.js | 8 ++++++-- src/pages/tasks/TaskDescriptionPage.js | 8 ++++++-- 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index b16bc5dd71b2..2b3db4f37437 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -29,7 +29,9 @@ function EditRequestDescriptionPage({defaultDescription, onSubmit}) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (descriptionInputRef.current) descriptionInputRef.current.focus(); + if (descriptionInputRef.current) { + descriptionInputRef.current.focus(); + } return () => { if (!focusTimeoutRef.current) { return; @@ -63,7 +65,9 @@ function EditRequestDescriptionPage({defaultDescription, onSubmit}) { accessibilityLabel={translate('moneyRequestConfirmationList.whatsItFor')} accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} ref={(el) => { - if (!el) return; + if (!el) { + return; + } descriptionInputRef.current = el; updateMultilineInputRange(descriptionInputRef.current); }} diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 17b0c38e3d42..2b7b174db9f8 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -72,7 +72,9 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (privateNotesInput.current) privateNotesInput.current.focus(); + if (privateNotesInput.current) { + privateNotesInput.current.focus(); + } return () => { if (!focusTimeoutRef.current) { return; @@ -141,7 +143,9 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { value={privateNote} onChangeText={(text) => setPrivateNote(text)} ref={(el) => { - if (!el) return; + if (!el) { + return; + } privateNotesInput.current = el; updateMultilineInputRange(privateNotesInput.current); }} diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 97269905723b..511e03243d42 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -59,7 +59,9 @@ function ReportWelcomeMessagePage(props) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (welcomeMessageInputRef.current) welcomeMessageInputRef.current.focus(); + if (welcomeMessageInputRef.current) { + welcomeMessageInputRef.current.focus(); + } return () => { if (!focusTimeoutRef.current) { return; @@ -91,7 +93,9 @@ function ReportWelcomeMessagePage(props) { autoGrowHeight maxLength={CONST.MAX_COMMENT_LENGTH} ref={(el) => { - if (!el) return; + if (!el) { + return; + } welcomeMessageInputRef.current = el; updateMultilineInputRange(welcomeMessageInputRef.current); }} diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index 817e947dd53c..689413afaa0c 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -63,7 +63,9 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (inputRef.current) inputRef.current.focus(); + if (inputRef.current) { + inputRef.current.focus(); + } return () => { if (!focusTimeoutRef.current) { return; @@ -127,7 +129,9 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { accessibilityLabel={translate('moneyRequestConfirmationList.whatsItFor')} accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} ref={(el) => { - if (!el) return; + if (!el) { + return; + } inputRef.current = el; updateMultilineInputRange(inputRef.current); }} diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index 47429e6d3ac8..5ac36c679d8e 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -48,7 +48,9 @@ function NewTaskDescriptionPage(props) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (inputRef.current) inputRef.current.focus(); + if (inputRef.current) { + inputRef.current.focus(); + } return () => { if (!focusTimeoutRef.current) { return; @@ -94,7 +96,9 @@ function NewTaskDescriptionPage(props) { accessibilityLabel={props.translate('newTaskPage.descriptionOptional')} accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} ref={(el) => { - if (!el) return; + if (!el) { + return; + } inputRef.current = el; updateMultilineInputRange(inputRef.current); }} diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 3020d2ec056d..5ba8294c5202 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -67,7 +67,9 @@ function TaskDescriptionPage(props) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (inputRef.current) inputRef.current.focus(); + if (inputRef.current) { + inputRef.current.focus(); + } return () => { if (!focusTimeoutRef.current) { return; @@ -102,7 +104,9 @@ function TaskDescriptionPage(props) { accessibilityLabel={props.translate('newTaskPage.descriptionOptional')} defaultValue={(props.report && props.report.description) || ''} ref={(el) => { - if (!el) return; + if (!el) { + return; + } inputRef.current = el; updateMultilineInputRange(inputRef.current); }} From 8e858451ad83fcc8a91842e13474648926bfcdbf Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Wed, 20 Sep 2023 21:47:44 +0530 Subject: [PATCH 09/14] rename --- src/pages/EditRequestDescriptionPage.js | 2 +- src/pages/PrivateNotes/PrivateNotesEditPage.js | 2 +- src/pages/ReportWelcomeMessagePage.js | 2 +- src/pages/iou/MoneyRequestDescriptionPage.js | 2 +- src/pages/tasks/NewTaskDescriptionPage.js | 2 +- src/pages/tasks/TaskDescriptionPage.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index 2b3db4f37437..b38fed3b66a9 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -11,7 +11,7 @@ import styles from '../styles/styles'; import CONST from '../CONST'; import useLocalize from '../hooks/useLocalize'; import * as Browser from '../libs/Browser'; -import updateMultilineInputRange from '../libs/updateMultilineInputRange'; +import updateMultilineInputRange from '../libs/UpdateMultilineInputRange'; const propTypes = { /** Transaction default description value */ diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 2b7b174db9f8..6c73e472fe41 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -24,7 +24,7 @@ import personalDetailsPropType from '../personalDetailsPropType'; import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; -import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import ROUTES from '../../ROUTES'; const propTypes = { diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 511e03243d42..70eba316e6da 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -20,7 +20,7 @@ import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundVi import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; -import updateMultilineInputRange from '../libs/updateMultilineInputRange'; +import updateMultilineInputRange from '../libs/UpdateMultilineInputRange'; const propTypes = { ...withLocalizePropTypes, diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index 689413afaa0c..246ca4bb82b4 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -18,7 +18,7 @@ import * as IOU from '../../libs/actions/IOU'; import * as MoneyRequestUtils from '../../libs/MoneyRequestUtils'; import CONST from '../../CONST'; import useLocalize from '../../hooks/useLocalize'; -import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import * as Browser from '../../libs/Browser'; const propTypes = { diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index 5ac36c679d8e..1054593b5275 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -15,7 +15,7 @@ import TextInput from '../../components/TextInput'; import Permissions from '../../libs/Permissions'; import ROUTES from '../../ROUTES'; import * as Task from '../../libs/actions/Task'; -import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import CONST from '../../CONST'; import * as Browser from '../../libs/Browser'; diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 5ba8294c5202..88ec29c7c233 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -15,7 +15,7 @@ import compose from '../../libs/compose'; import * as Task from '../../libs/actions/Task'; import * as ReportUtils from '../../libs/ReportUtils'; import CONST from '../../CONST'; -import updateMultilineInputRange from '../../libs/updateMultilineInputRange'; +import updateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import * as Browser from '../../libs/Browser'; import Navigation from '../../libs/Navigation/Navigation'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; From 423d6b607c00ff59aa798660fed1c3bf9742e033 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Thu, 21 Sep 2023 21:08:01 +0530 Subject: [PATCH 10/14] ios specific patch --- src/pages/ReportWelcomeMessagePage.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 70eba316e6da..c9f643dfb27a 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -21,6 +21,8 @@ import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; import updateMultilineInputRange from '../libs/UpdateMultilineInputRange'; +import getPlatform from '../libs/getPlatform'; +import * as Browser from '../libs/Browser'; const propTypes = { ...withLocalizePropTypes, @@ -47,7 +49,8 @@ function ReportWelcomeMessagePage(props) { const [welcomeMessage, setWelcomeMessage] = useState(parser.htmlToMarkdown(props.report.welcomeMessage)); const welcomeMessageInputRef = useRef(null); const focusTimeoutRef = useRef(null); - + const isNativeIOS = getPlatform() === CONST.PLATFORM.IOS && !Browser.isMobileSafari(); + if(isNativeIOS) console.log("true call!"); const handleWelcomeMessageChange = useCallback((value) => { setWelcomeMessage(value); }, []); @@ -59,7 +62,7 @@ function ReportWelcomeMessagePage(props) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (welcomeMessageInputRef.current) { + if (welcomeMessageInputRef.current && !isNativeIOS) { welcomeMessageInputRef.current.focus(); } return () => { @@ -98,6 +101,9 @@ function ReportWelcomeMessagePage(props) { } welcomeMessageInputRef.current = el; updateMultilineInputRange(welcomeMessageInputRef.current); + if(isNativeIOS) { + welcomeMessageInputRef.current.focus(); + } }} value={welcomeMessage} onChangeText={handleWelcomeMessageChange} From 48c10bb39b42decdd33ad7e000b944399b17fd3f Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Thu, 21 Sep 2023 21:23:33 +0530 Subject: [PATCH 11/14] revert last --- src/pages/ReportWelcomeMessagePage.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index c9f643dfb27a..70eba316e6da 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -21,8 +21,6 @@ import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; import updateMultilineInputRange from '../libs/UpdateMultilineInputRange'; -import getPlatform from '../libs/getPlatform'; -import * as Browser from '../libs/Browser'; const propTypes = { ...withLocalizePropTypes, @@ -49,8 +47,7 @@ function ReportWelcomeMessagePage(props) { const [welcomeMessage, setWelcomeMessage] = useState(parser.htmlToMarkdown(props.report.welcomeMessage)); const welcomeMessageInputRef = useRef(null); const focusTimeoutRef = useRef(null); - const isNativeIOS = getPlatform() === CONST.PLATFORM.IOS && !Browser.isMobileSafari(); - if(isNativeIOS) console.log("true call!"); + const handleWelcomeMessageChange = useCallback((value) => { setWelcomeMessage(value); }, []); @@ -62,7 +59,7 @@ function ReportWelcomeMessagePage(props) { useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (welcomeMessageInputRef.current && !isNativeIOS) { + if (welcomeMessageInputRef.current) { welcomeMessageInputRef.current.focus(); } return () => { @@ -101,9 +98,6 @@ function ReportWelcomeMessagePage(props) { } welcomeMessageInputRef.current = el; updateMultilineInputRange(welcomeMessageInputRef.current); - if(isNativeIOS) { - welcomeMessageInputRef.current.focus(); - } }} value={welcomeMessage} onChangeText={handleWelcomeMessageChange} From ab6ba9d20fc8b67939fddd68c44d5bf3310edd16 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Thu, 21 Sep 2023 21:36:40 +0530 Subject: [PATCH 12/14] ios specific patch for scroll issue --- .../UpdateMultilineInputRange/index.ios.js | 29 +++++++++++++++++++ .../index.js} | 0 2 files changed, 29 insertions(+) create mode 100644 src/libs/UpdateMultilineInputRange/index.ios.js rename src/libs/{UpdateMultilineInputRange.js => UpdateMultilineInputRange/index.js} (100%) diff --git a/src/libs/UpdateMultilineInputRange/index.ios.js b/src/libs/UpdateMultilineInputRange/index.ios.js new file mode 100644 index 000000000000..e07b91bf5bd2 --- /dev/null +++ b/src/libs/UpdateMultilineInputRange/index.ios.js @@ -0,0 +1,29 @@ +/** + * Place the cursor at the end of the value (if there is a value in the input). + * + * When a multiline input contains a text value that goes beyond the scroll height, the cursor will be placed + * at the end of the text value, and automatically scroll the input field to this position after the field gains + * focus. This provides a better user experience in cases where the text in the field has to be edited. The auto- + * scroll behaviour works on all platforms except iOS native. + * See https://github.com/Expensify/App/issues/20836 for more details. + * + * @param {Object} input the input element + */ +export default function updateMultilineInputRange(input) { + if (!input) { + return; + } + + if (input.value && input.setSelectionRange) { + const length = input.value.length; + input.setSelectionRange(length, length); + // eslint-disable-next-line no-param-reassign + input.scrollTop = input.scrollHeight; + } + /* + * adding this ios specific patch because of the scroll issue in native iOS + * issue: does not scroll multiline input when text exceeds the maximum number of lines + * for more details: https://github.com/Expensify/App/pull/27702#issuecomment-1728651132 + */ + input.focus(); +} diff --git a/src/libs/UpdateMultilineInputRange.js b/src/libs/UpdateMultilineInputRange/index.js similarity index 100% rename from src/libs/UpdateMultilineInputRange.js rename to src/libs/UpdateMultilineInputRange/index.js From b6122b47204ce76d1bbfbde4fd7ad6850f4d698b Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Thu, 21 Sep 2023 23:20:41 +0530 Subject: [PATCH 13/14] refactor --- src/libs/UpdateMultilineInputRange/index.ios.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/libs/UpdateMultilineInputRange/index.ios.js b/src/libs/UpdateMultilineInputRange/index.ios.js index e07b91bf5bd2..85ed529a33bc 100644 --- a/src/libs/UpdateMultilineInputRange/index.ios.js +++ b/src/libs/UpdateMultilineInputRange/index.ios.js @@ -14,16 +14,10 @@ export default function updateMultilineInputRange(input) { return; } - if (input.value && input.setSelectionRange) { - const length = input.value.length; - input.setSelectionRange(length, length); - // eslint-disable-next-line no-param-reassign - input.scrollTop = input.scrollHeight; - } /* - * adding this ios specific patch because of the scroll issue in native iOS - * issue: does not scroll multiline input when text exceeds the maximum number of lines - * for more details: https://github.com/Expensify/App/pull/27702#issuecomment-1728651132 + * Adding this iOS specific patch because of the scroll issue in native iOS + * Issue: does not scroll multiline input when text exceeds the maximum number of lines + * For more details: https://github.com/Expensify/App/pull/27702#issuecomment-1728651132 */ input.focus(); } From 723402c64aa5e46fb7b7210681d09ceae662016f Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Fri, 22 Sep 2023 09:57:49 +0530 Subject: [PATCH 14/14] prettier --- src/pages/PrivateNotes/PrivateNotesEditPage.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 05ec7c3f80c7..c59d3d319291 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -95,7 +95,10 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { }; return ( - +