From ac80c64fdd93af2fa39417e25fedc42fe19760c4 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Wed, 6 Sep 2023 11:43:32 +0700 Subject: [PATCH 01/46] add failure message when create a task error --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/actions/Task.js | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index af7957e1a560..2c9801b7a4fe 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1504,6 +1504,7 @@ export default { markAsDone: 'Mark as done', markAsIncomplete: 'Mark as incomplete', assigneeError: 'There was an error assigning this task, please try another assignee.', + genericCreateTaskFailureMessage: 'Unexpected error create task, please try again later', }, statementPage: { generatingPDF: "We're generating your PDF right now. Please come back later!", diff --git a/src/languages/es.ts b/src/languages/es.ts index f950733b005c..83c7ed1f9418 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1534,6 +1534,7 @@ export default { markAsDone: 'Marcar como completada', markAsIncomplete: 'Marcar como incompleta', assigneeError: 'Hubo un error al asignar esta tarea, inténtalo con otro usuario.', + genericCreateTaskFailureMessage: 'Error inesperado al crear la tarea, Por favor, inténtalo más tarde.', }, statementPage: { generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!', diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 6227686b3f45..5fa614a0e019 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -184,7 +184,11 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, - value: {[optimisticAddCommentReport.reportAction.reportActionID]: {pendingAction: null}}, + value: { + [optimisticAddCommentReport.reportAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), + }, + }, }); API.write( From 445b4e09aeb8bbe3c96dfda8b8d82a6be59a20a8 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Wed, 6 Sep 2023 23:15:32 +0700 Subject: [PATCH 02/46] fix minor --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 2c9801b7a4fe..6fff0c5ff58c 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1504,7 +1504,7 @@ export default { markAsDone: 'Mark as done', markAsIncomplete: 'Mark as incomplete', assigneeError: 'There was an error assigning this task, please try another assignee.', - genericCreateTaskFailureMessage: 'Unexpected error create task, please try again later', + genericCreateTaskFailureMessage: 'Unexpected error create task, please try again later.', }, statementPage: { generatingPDF: "We're generating your PDF right now. Please come back later!", diff --git a/src/languages/es.ts b/src/languages/es.ts index 83c7ed1f9418..a73b5045cf62 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1534,7 +1534,7 @@ export default { markAsDone: 'Marcar como completada', markAsIncomplete: 'Marcar como incompleta', assigneeError: 'Hubo un error al asignar esta tarea, inténtalo con otro usuario.', - genericCreateTaskFailureMessage: 'Error inesperado al crear la tarea, Por favor, inténtalo más tarde.', + genericCreateTaskFailureMessage: 'Error inesperado al crear la tarea, por favor, inténtalo más tarde.', }, statementPage: { generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!', From 0ca6752ad0cef8f6c4c8cd2baa1ca4d2380b00ef Mon Sep 17 00:00:00 2001 From: Nam Le Date: Mon, 11 Sep 2023 19:24:14 +0700 Subject: [PATCH 03/46] add error for report thread --- src/libs/actions/Task.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 5fa614a0e019..5dec7cd3bf9e 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -133,7 +133,11 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTaskReport.reportID}`, - value: null, + value: { + errorFields: { + errors: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), + }, + } }, { onyxMethod: Onyx.METHOD.MERGE, From b88e4db601032d674dc5b81d49a75d3e33d98878 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Mon, 11 Sep 2023 19:28:16 +0700 Subject: [PATCH 04/46] fix key --- src/libs/actions/Task.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 5dec7cd3bf9e..3c3648f9a7bb 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -135,7 +135,7 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTaskReport.reportID}`, value: { errorFields: { - errors: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), }, } }, From abff47b52699998243fdc533127d63056af33bb9 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Mon, 11 Sep 2023 19:31:40 +0700 Subject: [PATCH 05/46] fix lint --- src/libs/actions/Task.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 3c3648f9a7bb..9e0572e76a7c 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -137,7 +137,7 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail errorFields: { createChat: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), }, - } + }, }, { onyxMethod: Onyx.METHOD.MERGE, From 3b1f752eca3524dbc744077ec5823f6a6c7bf9e0 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Mon, 11 Sep 2023 19:35:20 +0700 Subject: [PATCH 06/46] fix translate --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index a73b5045cf62..0e49eb8fa0c0 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1534,7 +1534,7 @@ export default { markAsDone: 'Marcar como completada', markAsIncomplete: 'Marcar como incompleta', assigneeError: 'Hubo un error al asignar esta tarea, inténtalo con otro usuario.', - genericCreateTaskFailureMessage: 'Error inesperado al crear la tarea, por favor, inténtalo más tarde.', + genericCreateTaskFailureMessage: 'Error inesperado al crear el tarea, por favor, inténtalo más tarde.', }, statementPage: { generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!', From d3af70655848edc559e4e7eb520d751af93e031e Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Mon, 23 Oct 2023 16:03:57 +0200 Subject: [PATCH 07/46] Migrate 'ImageWithSizeCalculation.js' component to TypeScript --- src/components/Image/index.js | 2 + ...lation.js => ImageWithSizeCalculation.tsx} | 54 +++++++------------ 2 files changed, 22 insertions(+), 34 deletions(-) rename src/components/{ImageWithSizeCalculation.js => ImageWithSizeCalculation.tsx} (65%) diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 7434c16c6491..2511d916deef 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -69,4 +69,6 @@ const ImageWithOnyx = React.memo( imagePropsAreEqual, ); ImageWithOnyx.resizeMode = RESIZE_MODES; + +export {RESIZE_MODES}; export default ImageWithOnyx; diff --git a/src/components/ImageWithSizeCalculation.js b/src/components/ImageWithSizeCalculation.tsx similarity index 65% rename from src/components/ImageWithSizeCalculation.js rename to src/components/ImageWithSizeCalculation.tsx index 6aa87d07f23e..7c14d5af5d99 100644 --- a/src/components/ImageWithSizeCalculation.js +++ b/src/components/ImageWithSizeCalculation.tsx @@ -1,31 +1,23 @@ -import _ from 'underscore'; import React, {useState, useRef, useEffect} from 'react'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; +import {View, ViewStyle} from 'react-native'; +import delay from 'lodash/delay'; import Log from '../libs/Log'; import styles from '../styles/styles'; import FullscreenLoadingIndicator from './FullscreenLoadingIndicator'; -import Image from './Image'; +import Image, {RESIZE_MODES} from './Image'; -const propTypes = { +type ImageWithSizeCalculationProps = { /** Url for image to display */ - url: PropTypes.string.isRequired, + url: string; /** Any additional styles to apply */ - // eslint-disable-next-line react/forbid-prop-types - style: PropTypes.any, + style: ViewStyle; /** Callback fired when the image has been measured. */ - onMeasure: PropTypes.func, + onMeasure: (args: {width: number; height: number}) => void; /** Whether the image requires an authToken */ - isAuthTokenRequired: PropTypes.bool, -}; - -const defaultProps = { - style: {}, - onMeasure: () => {}, - isAuthTokenRequired: false, + isAuthTokenRequired: boolean; }; /** @@ -33,23 +25,19 @@ const defaultProps = { * Image size must be provided by parent via width and height props. Useful for * performing some calculation on a network image after fetching dimensions so * it can be appropriately resized. - * - * @param {Object} props - * @returns {React.Component} - * */ -function ImageWithSizeCalculation(props) { - const isLoadedRef = useRef(null); +function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired}: ImageWithSizeCalculationProps) { + const isLoadedRef = useRef(null); const [isImageCached, setIsImageCached] = useState(true); const [isLoading, setIsLoading] = useState(false); const onError = () => { - Log.hmmm('Unable to fetch image to calculate size', {url: props.url}); + Log.hmmm('Unable to fetch image to calculate size', {url}); }; - const imageLoadedSuccessfully = (event) => { + const imageLoadedSuccessfully = (event: {nativeEvent: {width: number; height: number}}) => { isLoadedRef.current = true; - props.onMeasure({ + onMeasure({ width: event.nativeEvent.width, height: event.nativeEvent.height, }); @@ -57,10 +45,10 @@ function ImageWithSizeCalculation(props) { /** Delay the loader to detect whether the image is being loaded from the cache or the internet. */ useEffect(() => { - if (isLoadedRef.current || !isLoading) { + if (isLoadedRef.current ?? !isLoading) { return; } - const timeout = _.delay(() => { + const timeout = delay(() => { if (!isLoading || isLoadedRef.current) { return; } @@ -70,14 +58,14 @@ function ImageWithSizeCalculation(props) { }, [isLoading]); return ( - + { - if (isLoadedRef.current || isLoading) { + if (isLoadedRef.current ?? isLoading) { return; } setIsLoading(true); @@ -94,7 +82,5 @@ function ImageWithSizeCalculation(props) { ); } -ImageWithSizeCalculation.propTypes = propTypes; -ImageWithSizeCalculation.defaultProps = defaultProps; ImageWithSizeCalculation.displayName = 'ImageWithSizeCalculation'; export default React.memo(ImageWithSizeCalculation); From 1f8343eaf92babaeea0c220919aad39868f9bd98 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 24 Oct 2023 12:33:50 +0200 Subject: [PATCH 08/46] fix type definitions --- src/components/ImageWithSizeCalculation.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index 7c14d5af5d99..e9821a06d98b 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -1,20 +1,23 @@ import React, {useState, useRef, useEffect} from 'react'; import {View, ViewStyle} from 'react-native'; import delay from 'lodash/delay'; +import { OnLoadEvent } from 'react-native-fast-image' import Log from '../libs/Log'; import styles from '../styles/styles'; import FullscreenLoadingIndicator from './FullscreenLoadingIndicator'; import Image, {RESIZE_MODES} from './Image'; +type OnMeasure = (args: {width: number; height: number}) => void; + type ImageWithSizeCalculationProps = { /** Url for image to display */ url: string; /** Any additional styles to apply */ - style: ViewStyle; + style?: ViewStyle; /** Callback fired when the image has been measured. */ - onMeasure: (args: {width: number; height: number}) => void; + onMeasure: OnMeasure; /** Whether the image requires an authToken */ isAuthTokenRequired: boolean; @@ -35,7 +38,7 @@ function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired}: Log.hmmm('Unable to fetch image to calculate size', {url}); }; - const imageLoadedSuccessfully = (event: {nativeEvent: {width: number; height: number}}) => { + const imageLoadedSuccessfully = (event: OnLoadEvent) => { isLoadedRef.current = true; onMeasure({ width: event.nativeEvent.width, From 59a84a5056c9c030fb4758f10368477698c65019 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 24 Oct 2023 12:42:58 +0200 Subject: [PATCH 09/46] fix prettier --- src/components/ImageWithSizeCalculation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index e9821a06d98b..0089d8bcd6ae 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -1,7 +1,7 @@ import React, {useState, useRef, useEffect} from 'react'; import {View, ViewStyle} from 'react-native'; import delay from 'lodash/delay'; -import { OnLoadEvent } from 'react-native-fast-image' +import {OnLoadEvent} from 'react-native-fast-image'; import Log from '../libs/Log'; import styles from '../styles/styles'; import FullscreenLoadingIndicator from './FullscreenLoadingIndicator'; From a63d988d57839b974293f9cb0e70dd6ad01f761a Mon Sep 17 00:00:00 2001 From: Nam Le Date: Wed, 25 Oct 2023 00:20:48 +0700 Subject: [PATCH 10/46] fix close error task --- src/components/ReportActionItem/TaskView.js | 4 ++-- src/libs/actions/ReportActions.ts | 3 +++ src/libs/actions/Task.js | 20 ++++++++++++++++---- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index 7cddc7a969dc..80bf408f284c 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -56,8 +56,8 @@ function TaskView(props) { Task.clearEditTaskErrors(props.report.reportID)} + errors={lodashGet(props, 'report.errorFields.editTask') || lodashGet(props, 'report.errorFields.createTask')} + onClose={() => Task.clearTaskErrors(props.report.reportID)} errorRowStyles={styles.ph5} > diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 3faa1dbe3574..19883bddb0b8 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -24,6 +24,9 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID}`, null); } + if (ReportUtils.isThreadParent(reportAction)) { + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportAction.childReportID}`, null); + } return; } diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index d9987da42c3d..3c08cca8d6fa 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -133,11 +133,11 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail // FOR TASK REPORT const failureData = [ { - onyxMethod: Onyx.METHOD.SET, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTaskReport.reportID}`, value: { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), + createTask: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), }, }, }, @@ -913,7 +913,19 @@ function canModifyTask(taskReport, sessionAccountID) { /** * @param {String} reportID */ -function clearEditTaskErrors(reportID) { +function clearTaskErrors(reportID) { + const report = ReportUtils.getReport(reportID); + if (lodashGet(report, 'pendingFields.createChat') === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, { + [report.parentReportActionID]: null + }); + + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, null); + + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.parentReportID)); + return; + } + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { pendingFields: null, errorFields: null, @@ -968,7 +980,7 @@ export { cancelTask, dismissModalAndClearOutTaskInfo, getTaskAssigneeAccountID, - clearEditTaskErrors, + clearTaskErrors, canModifyTask, getTaskReportActionMessage, }; From 07dbcc4f022317f6e6961e15abed8f71fafafb1c Mon Sep 17 00:00:00 2001 From: Nam Le Date: Wed, 25 Oct 2023 00:21:27 +0700 Subject: [PATCH 11/46] fix prettier --- src/libs/actions/Task.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 3c08cca8d6fa..4b2af0791264 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -917,7 +917,7 @@ function clearTaskErrors(reportID) { const report = ReportUtils.getReport(reportID); if (lodashGet(report, 'pendingFields.createChat') === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, { - [report.parentReportActionID]: null + [report.parentReportActionID]: null, }); Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, null); From d3497ee2b23a07bcfed53c2d361f06a3dc40966f Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 25 Oct 2023 16:07:24 +0200 Subject: [PATCH 12/46] fix type definitions --- src/components/ImageWithSizeCalculation.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index 0089d8bcd6ae..63faffef3358 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -1,5 +1,5 @@ import React, {useState, useRef, useEffect} from 'react'; -import {View, ViewStyle} from 'react-native'; +import {View, ViewStyle, StyleProp} from 'react-native'; import delay from 'lodash/delay'; import {OnLoadEvent} from 'react-native-fast-image'; import Log from '../libs/Log'; @@ -14,7 +14,7 @@ type ImageWithSizeCalculationProps = { url: string; /** Any additional styles to apply */ - style?: ViewStyle; + style?: StyleProp; /** Callback fired when the image has been measured. */ onMeasure: OnMeasure; From a5027338d28bb00d409d604101d56a42853aefa0 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Thu, 26 Oct 2023 17:07:54 +0700 Subject: [PATCH 13/46] add comment and fix minor --- src/components/ReportActionItem/TaskView.js | 2 +- src/libs/actions/ReportActions.ts | 1 + src/libs/actions/Task.js | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index 80bf408f284c..6123aa7be54f 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -56,7 +56,7 @@ function TaskView(props) { Task.clearTaskErrors(props.report.reportID)} errorRowStyles={styles.ph5} > diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 19883bddb0b8..467535ad368e 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -24,6 +24,7 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID}`, null); } + // Delete the failed task report too if (ReportUtils.isThreadParent(reportAction)) { Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportAction.childReportID}`, null); } diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 4b2af0791264..88b09ba18c84 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -137,7 +137,7 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTaskReport.reportID}`, value: { errorFields: { - createTask: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), }, }, }, @@ -915,6 +915,8 @@ function canModifyTask(taskReport, sessionAccountID) { */ function clearTaskErrors(reportID) { const report = ReportUtils.getReport(reportID); + + // Delete the task preview in the parent report if (lodashGet(report, 'pendingFields.createChat') === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, { [report.parentReportActionID]: null, From a578047037e67951d3977753e4ca7a4d4a9baa1b Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Thu, 26 Oct 2023 12:46:45 +0200 Subject: [PATCH 14/46] fix native export --- src/components/Image/index.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 9d9ad600b1d4..50c8d0c6a651 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -59,4 +59,5 @@ const ImageWithOnyx = withOnyx({ })(Image); ImageWithOnyx.resizeMode = RESIZE_MODES; ImageWithOnyx.resolveDimensions = resolveDimensions; +export {RESIZE_MODES}; export default ImageWithOnyx; From 2e3b7c2e06be5659724f3ea6bbe7c8935691a4d1 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Fri, 27 Oct 2023 23:36:36 +0200 Subject: [PATCH 15/46] Add goBack parameter for contact-methods route --- src/ROUTES.ts | 12 ++++++------ src/components/ConnectBankAccountButton.js | 2 +- src/components/CountrySelector.js | 2 +- src/components/MoneyRequestConfirmationList.js | 2 +- src/libs/Navigation/Navigation.js | 13 +++++++++++-- src/libs/Navigation/linkingConfig.js | 2 +- src/libs/actions/User.js | 6 +++--- src/pages/EditRequestPage.js | 2 +- src/pages/EditSplitBillPage.js | 2 +- src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js | 5 +++-- src/pages/iou/steps/NewRequestAmountPage.js | 2 +- .../Profile/Contacts/ContactMethodDetailsPage.js | 8 ++++---- .../settings/Profile/Contacts/ContactMethodsPage.js | 3 ++- .../Profile/Contacts/NewContactMethodPage.js | 2 +- src/pages/settings/Profile/ProfilePage.js | 2 +- src/pages/workspace/WorkspaceInitialPage.js | 2 +- 16 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index bcc4685368cb..864e8934ad88 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2,14 +2,11 @@ import {ValueOf} from 'type-fest'; import CONST from './CONST'; /** - * This is a file containing constants for all of the routes we want to be able to go to + * This is a file containing constants for all the routes we want to be able to go to */ /** - * This is a file containing constants for all of the routes we want to be able to go to - * Returns the URL with an encoded URI component for the backTo param which can be added to the end of URLs - * @param backTo - * @returns + * Builds a URL with an encoded URI component for the `backTo` param which can be added to the end of URLs */ function getUrlWithBackToParam(url: string, backTo?: string): string { const backToParam = backTo ? `${url.includes('?') ? '&' : '?'}backTo=${encodeURIComponent(backTo)}` : ''; @@ -111,7 +108,10 @@ export default { route: 'settings/profile/personal-details/address/country', getRoute: (country: string, backTo?: string) => getUrlWithBackToParam(`settings/profile/personal-details/address/country?country=${country}`, backTo), }, - SETTINGS_CONTACT_METHODS: 'settings/profile/contact-methods', + SETTINGS_CONTACT_METHODS: { + route: 'settings/profile/contact-methods', + getRoute: (backTo?: string) => getUrlWithBackToParam('settings/profile/contact-methods', backTo), + }, SETTINGS_CONTACT_METHOD_DETAILS: { route: 'settings/profile/contact-methods/:contactMethod/details', getRoute: (contactMethod: string) => `settings/profile/contact-methods/${encodeURIComponent(contactMethod)}/details`, diff --git a/src/components/ConnectBankAccountButton.js b/src/components/ConnectBankAccountButton.js index f5e0afe1d52e..b847cdd14c40 100644 --- a/src/components/ConnectBankAccountButton.js +++ b/src/components/ConnectBankAccountButton.js @@ -30,7 +30,7 @@ const defaultProps = { }; function ConnectBankAccountButton(props) { - const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, ''); + const activeRoute = Navigation.getActiveRouteWithoutParams(); return props.network.isOffline ? ( {`${props.translate('common.youAppearToBeOffline')} ${props.translate('common.thisFeatureRequiresInternet')}`} diff --git a/src/components/CountrySelector.js b/src/components/CountrySelector.js index 3dac127b1963..2b8f16ef8c6f 100644 --- a/src/components/CountrySelector.js +++ b/src/components/CountrySelector.js @@ -53,7 +53,7 @@ function CountrySelector({errorText, value: countryCode, onInputChange, forwarde descriptionTextStyle={countryTitleDescStyle} description={translate('common.country')} onPress={() => { - const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, ''); + const activeRoute = Navigation.getActiveRouteWithoutParams(); Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS_COUNTRY.getRoute(countryCode, activeRoute)); }} /> diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index fd3a89d14c86..06a1c367b1de 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -433,7 +433,7 @@ function MoneyRequestConfirmationList(props) { */ const navigateToReportOrUserDetail = (option) => { if (option.accountID) { - const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, ''); + const activeRoute = Navigation.getActiveRouteWithoutParams(); Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, activeRoute)); } else if (option.reportID) { diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 6bbf53ffa6ea..fd62ed2b0667 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -96,8 +96,8 @@ function navigate(route = ROUTES.HOME, type) { /** * @param {String} fallbackRoute - Fallback route if pop/goBack action should, but is not possible within RHP - * @param {Bool} shouldEnforceFallback - Enforces navigation to fallback route - * @param {Bool} shouldPopToTop - Should we navigate to LHN on back press + * @param {Boolean} shouldEnforceFallback - Enforces navigation to fallback route + * @param {Boolean} shouldPopToTop - Should we navigate to LHN on back press */ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = false) { if (!canNavigate('goBack')) { @@ -207,6 +207,14 @@ function getActiveRoute() { return ''; } +/** + * Returns the current active route without the URL params + * @returns {String} + */ +function getActiveRouteWithoutParams() { + return getActiveRoute().replace(/\?.*/, ''); +} + /** Returns the active route name from a state event from the navigationRef * @param {Object} event * @returns {String | undefined} @@ -270,6 +278,7 @@ export default { dismissModal, isActiveRoute, getActiveRoute, + getActiveRouteWithoutParams, goBack, isNavigationReady, setIsNavigationReady, diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 79609f551ee4..0a8143125a6a 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -142,7 +142,7 @@ export default { exact: true, }, Settings_ContactMethods: { - path: ROUTES.SETTINGS_CONTACT_METHODS, + path: ROUTES.SETTINGS_CONTACT_METHODS.route, exact: true, }, Settings_ContactMethodDetails: { diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index f65c20cd7e5b..eb360c9a1777 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -238,7 +238,7 @@ function deleteContactMethod(contactMethod, loginList) { }, {optimisticData, successData, failureData}, ); - Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS); + Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route); } /** @@ -328,7 +328,7 @@ function addNewContactMethodAndNavigate(contactMethod) { ]; API.write('AddNewContactMethod', {partnerUserID: contactMethod}, {optimisticData, successData, failureData}); - Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS); + Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route); } /** @@ -755,7 +755,7 @@ function setContactMethodAsDefault(newDefaultContactMethod) { }, ]; API.write('SetContactMethodAsDefault', {partnerUserID: newDefaultContactMethod}, {optimisticData, successData, failureData}); - Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS); + Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route); } /** diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index eedd5bcd10d4..f46fb20ccb2b 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -184,7 +184,7 @@ function EditRequestPage({betas, report, route, parentReport, policyCategories, }); }} onNavigateToCurrency={() => { - const activeRoute = encodeURIComponent(Navigation.getActiveRoute().replace(/\?.*/, '')); + const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); Navigation.navigate(ROUTES.EDIT_CURRENCY_REQUEST.getRoute(report.reportID, defaultCurrency, activeRoute)); }} /> diff --git a/src/pages/EditSplitBillPage.js b/src/pages/EditSplitBillPage.js index d10803cd40ea..813be6293a0f 100644 --- a/src/pages/EditSplitBillPage.js +++ b/src/pages/EditSplitBillPage.js @@ -112,7 +112,7 @@ function EditSplitBillPage({route, transaction, draftTransaction}) { }); }} onNavigateToCurrency={() => { - const activeRoute = encodeURIComponent(Navigation.getActiveRoute().replace(/\?.*/, '')); + const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); Navigation.navigate(ROUTES.EDIT_SPLIT_BILL_CURRENCY.getRoute(reportID, reportActionID, defaultCurrency, activeRoute)); }} /> diff --git a/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js b/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js index de83c71a01c7..9dd117db7a63 100644 --- a/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js +++ b/src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js @@ -17,6 +17,7 @@ const defaultProps = {}; function ImTeacherUpdateEmailPage() { const {translate} = useLocalize(); + const activeRoute = Navigation.getActiveRouteWithoutParams(); return ( @@ -31,7 +32,7 @@ function ImTeacherUpdateEmailPage() { title={translate('teachersUnitePage.updateYourEmail')} subtitle={translate('teachersUnitePage.schoolMailAsDefault')} linkKey="teachersUnitePage.contactMethods" - onLinkPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS)} + onLinkPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS.getRoute(activeRoute))} iconWidth={variables.signInLogoWidthLargeScreen} iconHeight={variables.lhnLogoWidth} /> @@ -40,7 +41,7 @@ function ImTeacherUpdateEmailPage() { success accessibilityLabel={translate('teachersUnitePage.updateEmail')} text={translate('teachersUnitePage.updateEmail')} - onPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS)} + onPress={() => Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS.getRoute(activeRoute))} /> diff --git a/src/pages/iou/steps/NewRequestAmountPage.js b/src/pages/iou/steps/NewRequestAmountPage.js index 15a2c74d8a95..d74ba40775b3 100644 --- a/src/pages/iou/steps/NewRequestAmountPage.js +++ b/src/pages/iou/steps/NewRequestAmountPage.js @@ -123,7 +123,7 @@ function NewRequestAmountPage({route, iou, report, selectedTab}) { } // Remove query from the route and encode it. - const activeRoute = encodeURIComponent(Navigation.getActiveRoute().replace(/\?.*/, '')); + const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); Navigation.navigate(ROUTES.MONEY_REQUEST_CURRENCY.getRoute(iouType, reportID, currency, activeRoute)); }; diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js index 1c9abcf535f6..b108cfdc6754 100644 --- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js +++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js @@ -123,7 +123,7 @@ class ContactMethodDetailsPage extends Component { // Navigate to methods page on successful magic code verification // validatedDate property is responsible to decide the status of the magic code verification if (!prevValidatedDate && validatedDate) { - Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS); + Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route); } } @@ -236,8 +236,8 @@ class ContactMethodDetailsPage extends Component { Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)} - onLinkPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)} + onLinkPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)} /> ); @@ -255,7 +255,7 @@ class ContactMethodDetailsPage extends Component { > Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)} /> Navigation.goBack(ROUTES.SETTINGS_PROFILE)} + onBackButtonPress={() => Navigation.goBack(navigateBackTo)} /> diff --git a/src/pages/settings/Profile/Contacts/NewContactMethodPage.js b/src/pages/settings/Profile/Contacts/NewContactMethodPage.js index e6f55b7cd6cb..0cd7b1ab47bc 100644 --- a/src/pages/settings/Profile/Contacts/NewContactMethodPage.js +++ b/src/pages/settings/Profile/Contacts/NewContactMethodPage.js @@ -103,7 +103,7 @@ function NewContactMethodPage(props) { > Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS)} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_CONTACT_METHODS.route)} /> policy.outputCurrency === CONST.CURRENCY.USD - ? singleExecution(waitForNavigate(() => ReimbursementAccount.navigateToBankAccountRoute(policy.id, Navigation.getActiveRoute().replace(/\?.*/, ''))))() + ? singleExecution(waitForNavigate(() => ReimbursementAccount.navigateToBankAccountRoute(policy.id, Navigation.getActiveRouteWithoutParams())))() : setIsCurrencyModalOpen(true), brickRoadIndicator: !_.isEmpty(props.reimbursementAccount.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', }, From 5d64de5aeb9998b5a1163aefbd3876ab34015be7 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Mon, 30 Oct 2023 12:27:01 +0100 Subject: [PATCH 16/46] fix prettier --- src/components/ImageWithSizeCalculation.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index d7cb364359b3..225c8e0a6b6b 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -1,6 +1,6 @@ -import React, {useState, useRef, useEffect} from 'react'; -import {View, ViewStyle, StyleProp} from 'react-native'; import delay from 'lodash/delay'; +import React, {useEffect, useRef, useState} from 'react'; +import {StyleProp, View, ViewStyle} from 'react-native'; import {OnLoadEvent} from 'react-native-fast-image'; import Log from '@libs/Log'; import styles from '@styles/styles'; From f360d946c662cbd270f4c53dae444d372a63ae71 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 31 Oct 2023 12:56:32 +0100 Subject: [PATCH 17/46] Rename files --- ...cPressable.js => BaseGenericPressable.tsx} | 0 .../{index.native.js => index.native.tsx} | 2 +- .../GenericPressable/{index.js => index.tsx} | 2 - .../Pressable/GenericPressable/types.ts | 111 ++++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) rename src/components/Pressable/GenericPressable/{BaseGenericPressable.js => BaseGenericPressable.tsx} (100%) rename src/components/Pressable/GenericPressable/{index.native.js => index.native.tsx} (91%) rename src/components/Pressable/GenericPressable/{index.js => index.tsx} (85%) create mode 100644 src/components/Pressable/GenericPressable/types.ts diff --git a/src/components/Pressable/GenericPressable/BaseGenericPressable.js b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx similarity index 100% rename from src/components/Pressable/GenericPressable/BaseGenericPressable.js rename to src/components/Pressable/GenericPressable/BaseGenericPressable.tsx diff --git a/src/components/Pressable/GenericPressable/index.native.js b/src/components/Pressable/GenericPressable/index.native.tsx similarity index 91% rename from src/components/Pressable/GenericPressable/index.native.js rename to src/components/Pressable/GenericPressable/index.native.tsx index 14a2c2bcbf82..8432faef24a8 100644 --- a/src/components/Pressable/GenericPressable/index.native.js +++ b/src/components/Pressable/GenericPressable/index.native.tsx @@ -15,6 +15,6 @@ const NativeGenericPressable = forwardRef((props, ref) => ( NativeGenericPressable.propTypes = GenericPressablePropTypes.pressablePropTypes; NativeGenericPressable.defaultProps = GenericPressablePropTypes.defaultProps; -NativeGenericPressable.displayName = 'WebGenericPressable'; +NativeGenericPressable.displayName = 'NativeGenericPressable'; export default NativeGenericPressable; diff --git a/src/components/Pressable/GenericPressable/index.js b/src/components/Pressable/GenericPressable/index.tsx similarity index 85% rename from src/components/Pressable/GenericPressable/index.js rename to src/components/Pressable/GenericPressable/index.tsx index 8247d0c35670..226c1035f5e0 100644 --- a/src/components/Pressable/GenericPressable/index.js +++ b/src/components/Pressable/GenericPressable/index.tsx @@ -19,8 +19,6 @@ const WebGenericPressable = forwardRef((props, ref) => ( /> )); -WebGenericPressable.propTypes = GenericPressablePropTypes.pressablePropTypes; -WebGenericPressable.defaultProps = GenericPressablePropTypes.defaultProps; WebGenericPressable.displayName = 'WebGenericPressable'; export default WebGenericPressable; diff --git a/src/components/Pressable/GenericPressable/types.ts b/src/components/Pressable/GenericPressable/types.ts new file mode 100644 index 000000000000..2f5397dbf53c --- /dev/null +++ b/src/components/Pressable/GenericPressable/types.ts @@ -0,0 +1,111 @@ +import {ValueOf} from 'type-fest'; +import {GestureResponderEvent, PressableProps as RNPressableProps, PressableStateCallbackType, StyleProp, ViewStyle, HostComponent} from 'react-native'; +import {ElementRef, RefObject} from 'react'; +import CONST from '@src/CONST'; + +type StylePropWithFunction = StyleProp | ((state: PressableStateCallbackType) => StyleProp); + +type PressableProps = RNPressableProps & { + /** + * onPress callback + */ + onPress: (event: GestureResponderEvent) => void; + + /** + * Specifies keyboard shortcut to trigger onPressHandler + * @example {shortcutKey: 'a', modifiers: ['ctrl', 'shift'], descriptionKey: 'keyboardShortcut.description'} + */ + keyboardShortcut: { + descriptionKey: string; + shortcutKey: string; + modifiers: string[]; + }; + + /** + * Specifies if haptic feedback should be used on press + * @default false + */ + shouldUseHapticsOnPress: boolean; + + /** + * Specifies if haptic feedback should be used on long press + * @default false + */ + shouldUseHapticsOnLongPress: boolean; + + /** + * style for when the component is disabled. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.isDisabled ? 'red' : 'blue'}) + */ + disabledStyle: StylePropWithFunction; + + /** + * style for when the component is hovered. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.hovered ? 'red' : 'blue'}) + */ + hoverStyle: StylePropWithFunction; + + /** + * style for when the component is focused. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.focused ? 'red' : 'blue'}) + */ + focusStyle: StylePropWithFunction; + + /** + * style for when the component is pressed. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.pressed ? 'red' : 'blue'}) + */ + pressStyle: StylePropWithFunction; + + /** + * style for when the component is active and the screen reader is on. + * Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.isScreenReaderActive ? 'red' : 'blue'}) + */ + screenReaderActiveStyle: StylePropWithFunction; + + /** + * Specifies if the component should be accessible when the screen reader is on + * @default 'all' + * @example 'all' - the component is accessible regardless of screen reader state + * @example 'active' - the component is accessible only when the screen reader is on + * @example 'disabled' - the component is not accessible when the screen reader is on + */ + enableInScreenReaderStates: ValueOf; + + /** + * Specifies which component should be focused after interacting with this component + */ + nextFocusRef: ElementRef> & RefObject; + + /** + * Specifies the accessibility label for the component + * @example 'Search' + * @example 'Close' + */ + // accessibilityLabel: requiredPropsCheck; + + /** + * Specifies the accessibility hint for the component + * @example 'Double tap to open' + */ + accessibilityHint: string; + + /** + * Specifies if the component should calculate its hitSlop automatically + * @default true + */ + shouldUseAutoHitSlop: boolean; +}; + +export default PressableProps; From 6159d2d201b5c1f13461807a22eec167bd2527da Mon Sep 17 00:00:00 2001 From: Nam Le Date: Tue, 31 Oct 2023 19:37:19 +0700 Subject: [PATCH 18/46] update navigate after delete --- src/libs/actions/ReportActions.ts | 6 ++++-- src/libs/actions/Task.js | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 467535ad368e..794b5aab81d9 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -3,6 +3,8 @@ import ONYXKEYS from '../../ONYXKEYS'; import CONST from '../../CONST'; import * as ReportActionUtils from '../ReportActionsUtils'; import * as ReportUtils from '../ReportUtils'; +import * as Report from './Report'; + import ReportAction from '../../types/onyx/ReportAction'; function clearReportActionErrors(reportID: string, reportAction: ReportAction) { @@ -25,8 +27,8 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction) { } // Delete the failed task report too - if (ReportUtils.isThreadParent(reportAction)) { - Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportAction.childReportID}`, null); + if (reportAction.childReportID) { + Report.deleteReport(reportAction.childReportID) } return; } diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 88b09ba18c84..3ebf88e7b38f 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -14,6 +14,7 @@ import * as ReportActionsUtils from '../ReportActionsUtils'; import * as Expensicons from '../../components/Icon/Expensicons'; import * as LocalePhoneNumber from '../LocalePhoneNumber'; import * as Localize from '../Localize'; +import * as Report from './Report'; let currentUserEmail; let currentUserAccountID; @@ -922,9 +923,7 @@ function clearTaskErrors(reportID) { [report.parentReportActionID]: null, }); - Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, null); - - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.parentReportID)); + Report.navigateToConciergeChatAndDeleteReport(reportID); return; } From 20319e11973dde8ef48418e818920a858a8c1162 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Tue, 31 Oct 2023 19:38:19 +0700 Subject: [PATCH 19/46] fix prettier --- src/libs/actions/ReportActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 794b5aab81d9..36b290f71cf4 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -28,7 +28,7 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction) { // Delete the failed task report too if (reportAction.childReportID) { - Report.deleteReport(reportAction.childReportID) + Report.deleteReport(reportAction.childReportID); } return; } From b2e99ad3812e7e05cd40404f6fa2e5342b2873b1 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Tue, 31 Oct 2023 20:03:42 +0700 Subject: [PATCH 20/46] fix lint --- src/libs/actions/ReportActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 2297203a36dd..64e69def0583 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -4,7 +4,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ReportAction from '@src/types/onyx/ReportAction'; -import * as Report from '@libs/actions/Report'; +import * as Report from './Report'; function clearReportActionErrors(reportID: string, reportAction: ReportAction) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); From 4d6c1830966ca3a460f5b2285a344696ca1cd97f Mon Sep 17 00:00:00 2001 From: Nam Le Date: Tue, 31 Oct 2023 20:24:39 +0700 Subject: [PATCH 21/46] fix lint --- src/libs/actions/Task.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index bc543123514f..96ff2ec040f6 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -15,7 +15,7 @@ import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import * as Report from '@libs/actions/Report'; +import * as Report from './Report'; let currentUserEmail; let currentUserAccountID; From 5f518243fc53e95c7ed11cef9ae6d09dce97434a Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 31 Oct 2023 14:58:07 +0100 Subject: [PATCH 22/46] remove constant export --- src/components/Image/index.js | 1 - src/components/Image/index.native.js | 2 +- src/components/ImageWithSizeCalculation.tsx | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Image/index.js b/src/components/Image/index.js index e973d8ca234e..ef1a69e19c12 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -70,5 +70,4 @@ const ImageWithOnyx = React.memo( ); ImageWithOnyx.resizeMode = RESIZE_MODES; -export {RESIZE_MODES}; export default ImageWithOnyx; diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 884c3fa17b39..cf5320392d1b 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -59,5 +59,5 @@ const ImageWithOnyx = withOnyx({ })(Image); ImageWithOnyx.resizeMode = RESIZE_MODES; ImageWithOnyx.resolveDimensions = resolveDimensions; -export {RESIZE_MODES}; + export default ImageWithOnyx; diff --git a/src/components/ImageWithSizeCalculation.tsx b/src/components/ImageWithSizeCalculation.tsx index 225c8e0a6b6b..fe4cc4a01bc0 100644 --- a/src/components/ImageWithSizeCalculation.tsx +++ b/src/components/ImageWithSizeCalculation.tsx @@ -5,7 +5,8 @@ import {OnLoadEvent} from 'react-native-fast-image'; import Log from '@libs/Log'; import styles from '@styles/styles'; import FullscreenLoadingIndicator from './FullscreenLoadingIndicator'; -import Image, {RESIZE_MODES} from './Image'; +import Image from './Image'; +import RESIZE_MODES from './Image/resizeModes'; type OnMeasure = (args: {width: number; height: number}) => void; From fd3351601787b8b358226c1cae3d5f40018c5e8f Mon Sep 17 00:00:00 2001 From: Nam Le Date: Thu, 2 Nov 2023 00:29:41 +0700 Subject: [PATCH 23/46] update key create task error --- src/components/ReportActionItem/TaskView.js | 2 +- src/libs/actions/Task.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index c50d99a0d18c..b12d6ae32128 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -58,7 +58,7 @@ function TaskView(props) { Task.clearTaskErrors(props.report.reportID)} errorRowStyles={styles.ph5} > diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 96ff2ec040f6..0c9caf9a3136 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -139,7 +139,7 @@ function createTaskAndNavigate(parentReportID, title, description, assigneeEmail key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTaskReport.reportID}`, value: { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), + createTask: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), }, }, }, From 4107a803afd0dfce844973172713663b942a0b18 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 10:16:14 +0100 Subject: [PATCH 24/46] Migrate general Pressables --- .../GenericPressable/BaseGenericPressable.tsx | 99 +++++++++---------- .../GenericPressable/index.native.tsx | 30 +++--- .../Pressable/GenericPressable/index.tsx | 44 +++++---- .../Pressable/GenericPressable/types.ts | 46 +++++---- .../Pressable/{index.js => index.ts} | 0 ...ngleExecution.js => useSingleExecution.ts} | 10 +- src/libs/Accessibility/index.ts | 2 +- src/libs/KeyboardShortcut/index.ts | 2 +- src/styles/StyleUtils.ts | 9 +- src/types/modules/react-native.d.ts | 7 +- 10 files changed, 127 insertions(+), 122 deletions(-) rename src/components/Pressable/{index.js => index.ts} (100%) rename src/hooks/{useSingleExecution.js => useSingleExecution.ts} (83%) diff --git a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx index a3ce55003cdd..a64fd9909f0d 100644 --- a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx +++ b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx @@ -1,7 +1,6 @@ -import React, {forwardRef, useCallback, useEffect, useMemo} from 'react'; +import React, {ForwardedRef, forwardRef, useCallback, useEffect, useMemo} from 'react'; // eslint-disable-next-line no-restricted-imports -import {Pressable} from 'react-native'; -import _ from 'underscore'; +import {GestureResponderEvent, Pressable, View, ViewStyle} from 'react-native'; import useSingleExecution from '@hooks/useSingleExecution'; import Accessibility from '@libs/Accessibility'; import HapticFeedback from '@libs/HapticFeedback'; @@ -9,15 +8,12 @@ import KeyboardShortcut from '@libs/KeyboardShortcut'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; import CONST from '@src/CONST'; -import genericPressablePropTypes from './PropTypes'; +import PressableProps from './types'; /** * Returns the cursor style based on the state of Pressable - * @param {Boolean} isDisabled - * @param {Boolean} isText - * @returns {Object} */ -const getCursorStyle = (isDisabled, isText) => { +function getCursorStyle(isDisabled: boolean, isText: boolean): Pick { if (isDisabled) { return styles.cursorDisabled; } @@ -27,28 +23,34 @@ const getCursorStyle = (isDisabled, isText) => { } return styles.cursorPointer; -}; +} -const GenericPressable = forwardRef((props, ref) => { - const { +function GenericPressable( + { children, - onPress, + onPress = () => {}, onLongPress, - onKeyPress, onKeyDown, disabled, style, - shouldUseHapticsOnLongPress, - shouldUseHapticsOnPress, + disabledStyle = {}, + hoverStyle = {}, + focusStyle = {}, + pressStyle = {}, + screenReaderActiveStyle = {}, + shouldUseHapticsOnLongPress = false, + shouldUseHapticsOnPress = false, nextFocusRef, keyboardShortcut, - shouldUseAutoHitSlop, - enableInScreenReaderStates, + shouldUseAutoHitSlop = false, + enableInScreenReaderStates = CONST.SCREEN_READER_STATES.ALL, onPressIn, onPressOut, + accessible = true, ...rest - } = props; - + }: PressableProps, + ref: ForwardedRef, +) { const {isExecuting, singleExecution} = useSingleExecution(); const isScreenReaderActive = Accessibility.useScreenReaderStatus(); const [hitSlop, onLayout] = Accessibility.useAutoHitSlop(); @@ -63,13 +65,14 @@ const GenericPressable = forwardRef((props, ref) => { shouldBeDisabledByScreenReader = isScreenReaderActive; } - return props.disabled || shouldBeDisabledByScreenReader || isExecuting; - }, [isScreenReaderActive, enableInScreenReaderStates, props.disabled, isExecuting]); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + return disabled || shouldBeDisabledByScreenReader || isExecuting; + }, [isScreenReaderActive, enableInScreenReaderStates, disabled, isExecuting]); const shouldUseDisabledCursor = useMemo(() => isDisabled && !isExecuting, [isDisabled, isExecuting]); const onLongPressHandler = useCallback( - (event) => { + (event: GestureResponderEvent) => { if (isDisabled) { return; } @@ -79,8 +82,8 @@ const GenericPressable = forwardRef((props, ref) => { if (shouldUseHapticsOnLongPress) { HapticFeedback.longPress(); } - if (ref && ref.current) { - ref.current.blur(); + if (ref && 'current' in ref) { + ref?.current?.blur(); } onLongPress(event); @@ -90,7 +93,7 @@ const GenericPressable = forwardRef((props, ref) => { ); const onPressHandler = useCallback( - (event) => { + (event?: GestureResponderEvent | KeyboardEvent) => { if (isDisabled) { return; } @@ -100,8 +103,8 @@ const GenericPressable = forwardRef((props, ref) => { if (shouldUseHapticsOnPress) { HapticFeedback.press(); } - if (ref && ref.current) { - ref.current.blur(); + if (ref && 'current' in ref) { + ref?.current?.blur(); } onPress(event); @@ -110,16 +113,6 @@ const GenericPressable = forwardRef((props, ref) => { [shouldUseHapticsOnPress, onPress, nextFocusRef, ref, isDisabled], ); - const onKeyPressHandler = useCallback( - (event) => { - if (event.key !== 'Enter') { - return; - } - onPressHandler(event); - }, - [onPressHandler], - ); - useEffect(() => { if (!keyboardShortcut) { return () => {}; @@ -135,39 +128,37 @@ const GenericPressable = forwardRef((props, ref) => { ref={ref} onPress={!isDisabled ? singleExecution(onPressHandler) : undefined} onLongPress={!isDisabled && onLongPress ? onLongPressHandler : undefined} - onKeyPress={!isDisabled ? onKeyPressHandler : undefined} onKeyDown={!isDisabled ? onKeyDown : undefined} onPressIn={!isDisabled ? onPressIn : undefined} onPressOut={!isDisabled ? onPressOut : undefined} style={(state) => [ - getCursorStyle(shouldUseDisabledCursor, [props.accessibilityRole, props.role].includes('text')), - StyleUtils.parseStyleFromFunction(props.style, state), - isScreenReaderActive && StyleUtils.parseStyleFromFunction(props.screenReaderActiveStyle, state), - state.focused && StyleUtils.parseStyleFromFunction(props.focusStyle, state), - state.hovered && StyleUtils.parseStyleFromFunction(props.hoverStyle, state), - state.pressed && StyleUtils.parseStyleFromFunction(props.pressStyle, state), - isDisabled && [...StyleUtils.parseStyleFromFunction(props.disabledStyle, state), styles.noSelect], + getCursorStyle(shouldUseDisabledCursor, [rest.accessibilityRole, rest.role].includes('text')), + StyleUtils.parseStyleFromFunction(style, state), + isScreenReaderActive && StyleUtils.parseStyleFromFunction(screenReaderActiveStyle, state), + state.focused && StyleUtils.parseStyleFromFunction(focusStyle, state), + state.hovered && StyleUtils.parseStyleFromFunction(hoverStyle, state), + state.pressed && StyleUtils.parseStyleFromFunction(pressStyle, state), + isDisabled && [StyleUtils.parseStyleFromFunction(disabledStyle, state), styles.noSelect], ]} // accessibility props accessibilityState={{ disabled: isDisabled, - ...props.accessibilityState, + ...rest.accessibilityState, }} aria-disabled={isDisabled} - aria-keyshortcuts={keyboardShortcut && `${keyboardShortcut.modifiers}+${keyboardShortcut.shortcutKey}`} + aria-keyshortcuts={keyboardShortcut?.modifiers.map((modifier) => `${modifier}+${keyboardShortcut.shortcutKey}`) ?? []} // ios-only form of inputs - onMagicTap={!isDisabled && onPressHandler} - onAccessibilityTap={!isDisabled && onPressHandler} + onMagicTap={!isDisabled ? onPressHandler : undefined} + onAccessibilityTap={!isDisabled ? onPressHandler : undefined} + accessible={accessible} // eslint-disable-next-line react/jsx-props-no-spreading {...rest} > - {(state) => (_.isFunction(props.children) ? props.children({...state, isScreenReaderActive, isDisabled}) : props.children)} + {(state) => (typeof children === 'function' ? children({...state, isScreenReaderActive, isDisabled}) : children)} ); -}); +} GenericPressable.displayName = 'GenericPressable'; -GenericPressable.propTypes = genericPressablePropTypes.pressablePropTypes; -GenericPressable.defaultProps = genericPressablePropTypes.defaultProps; -export default GenericPressable; +export default forwardRef(GenericPressable); diff --git a/src/components/Pressable/GenericPressable/index.native.tsx b/src/components/Pressable/GenericPressable/index.native.tsx index 8432faef24a8..fbc53551f435 100644 --- a/src/components/Pressable/GenericPressable/index.native.tsx +++ b/src/components/Pressable/GenericPressable/index.native.tsx @@ -1,20 +1,20 @@ -import React, {forwardRef} from 'react'; +import React, {ForwardedRef, forwardRef} from 'react'; +import {View} from 'react-native'; import GenericPressable from './BaseGenericPressable'; -import GenericPressablePropTypes from './PropTypes'; +import PressableProps from './types'; -const NativeGenericPressable = forwardRef((props, ref) => ( - -)); +function NativeGenericPressable(props: PressableProps, ref: ForwardedRef) { + return ( + + ); +} -NativeGenericPressable.propTypes = GenericPressablePropTypes.pressablePropTypes; -NativeGenericPressable.defaultProps = GenericPressablePropTypes.defaultProps; NativeGenericPressable.displayName = 'NativeGenericPressable'; -export default NativeGenericPressable; +export default forwardRef(NativeGenericPressable); diff --git a/src/components/Pressable/GenericPressable/index.tsx b/src/components/Pressable/GenericPressable/index.tsx index 226c1035f5e0..c8e9560062e0 100644 --- a/src/components/Pressable/GenericPressable/index.tsx +++ b/src/components/Pressable/GenericPressable/index.tsx @@ -1,24 +1,30 @@ -import React, {forwardRef} from 'react'; +import React, {ForwardedRef, forwardRef} from 'react'; +import {Role, View} from 'react-native'; import GenericPressable from './BaseGenericPressable'; -import GenericPressablePropTypes from './PropTypes'; +import PressableProps from './types'; -const WebGenericPressable = forwardRef((props, ref) => ( - -)); +function WebGenericPressable(props: PressableProps, ref: ForwardedRef) { + return ( + + ); +} WebGenericPressable.displayName = 'WebGenericPressable'; -export default WebGenericPressable; +export default forwardRef(WebGenericPressable); diff --git a/src/components/Pressable/GenericPressable/types.ts b/src/components/Pressable/GenericPressable/types.ts index 2f5397dbf53c..2e8857224646 100644 --- a/src/components/Pressable/GenericPressable/types.ts +++ b/src/components/Pressable/GenericPressable/types.ts @@ -1,37 +1,40 @@ -import {ValueOf} from 'type-fest'; -import {GestureResponderEvent, PressableProps as RNPressableProps, PressableStateCallbackType, StyleProp, ViewStyle, HostComponent} from 'react-native'; import {ElementRef, RefObject} from 'react'; +import {GestureResponderEvent, HostComponent, PressableStateCallbackType, PressableProps as RNPressableProps, StyleProp, ViewStyle} from 'react-native'; +import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; type StylePropWithFunction = StyleProp | ((state: PressableStateCallbackType) => StyleProp); +type Shortcut = { + displayName: string; + shortcutKey: string; + descriptionKey: string; + modifiers: string[]; +}; + type PressableProps = RNPressableProps & { /** * onPress callback */ - onPress: (event: GestureResponderEvent) => void; + onPress: (event?: GestureResponderEvent | KeyboardEvent) => void; /** * Specifies keyboard shortcut to trigger onPressHandler * @example {shortcutKey: 'a', modifiers: ['ctrl', 'shift'], descriptionKey: 'keyboardShortcut.description'} */ - keyboardShortcut: { - descriptionKey: string; - shortcutKey: string; - modifiers: string[]; - }; + keyboardShortcut?: Shortcut; /** * Specifies if haptic feedback should be used on press * @default false */ - shouldUseHapticsOnPress: boolean; + shouldUseHapticsOnPress?: boolean; /** * Specifies if haptic feedback should be used on long press * @default false */ - shouldUseHapticsOnLongPress: boolean; + shouldUseHapticsOnLongPress?: boolean; /** * style for when the component is disabled. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) @@ -39,7 +42,7 @@ type PressableProps = RNPressableProps & { * @example {backgroundColor: 'red'} * @example state => ({backgroundColor: state.isDisabled ? 'red' : 'blue'}) */ - disabledStyle: StylePropWithFunction; + disabledStyle?: StylePropWithFunction; /** * style for when the component is hovered. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) @@ -47,7 +50,7 @@ type PressableProps = RNPressableProps & { * @example {backgroundColor: 'red'} * @example state => ({backgroundColor: state.hovered ? 'red' : 'blue'}) */ - hoverStyle: StylePropWithFunction; + hoverStyle?: StylePropWithFunction; /** * style for when the component is focused. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) @@ -55,7 +58,7 @@ type PressableProps = RNPressableProps & { * @example {backgroundColor: 'red'} * @example state => ({backgroundColor: state.focused ? 'red' : 'blue'}) */ - focusStyle: StylePropWithFunction; + focusStyle?: StylePropWithFunction; /** * style for when the component is pressed. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) @@ -63,7 +66,7 @@ type PressableProps = RNPressableProps & { * @example {backgroundColor: 'red'} * @example state => ({backgroundColor: state.pressed ? 'red' : 'blue'}) */ - pressStyle: StylePropWithFunction; + pressStyle?: StylePropWithFunction; /** * style for when the component is active and the screen reader is on. @@ -72,7 +75,7 @@ type PressableProps = RNPressableProps & { * @example {backgroundColor: 'red'} * @example state => ({backgroundColor: state.isScreenReaderActive ? 'red' : 'blue'}) */ - screenReaderActiveStyle: StylePropWithFunction; + screenReaderActiveStyle?: StylePropWithFunction; /** * Specifies if the component should be accessible when the screen reader is on @@ -81,31 +84,34 @@ type PressableProps = RNPressableProps & { * @example 'active' - the component is accessible only when the screen reader is on * @example 'disabled' - the component is not accessible when the screen reader is on */ - enableInScreenReaderStates: ValueOf; + enableInScreenReaderStates?: ValueOf; /** * Specifies which component should be focused after interacting with this component */ - nextFocusRef: ElementRef> & RefObject; + nextFocusRef?: ElementRef> & RefObject; /** * Specifies the accessibility label for the component * @example 'Search' * @example 'Close' */ - // accessibilityLabel: requiredPropsCheck; + accessibilityLabel: string; /** * Specifies the accessibility hint for the component * @example 'Double tap to open' */ - accessibilityHint: string; + accessibilityHint?: string; /** * Specifies if the component should calculate its hitSlop automatically * @default true */ - shouldUseAutoHitSlop: boolean; + shouldUseAutoHitSlop?: boolean; + + /** Turns off drag area for the component */ + noDragArea?: boolean; }; export default PressableProps; diff --git a/src/components/Pressable/index.js b/src/components/Pressable/index.ts similarity index 100% rename from src/components/Pressable/index.js rename to src/components/Pressable/index.ts diff --git a/src/hooks/useSingleExecution.js b/src/hooks/useSingleExecution.ts similarity index 83% rename from src/hooks/useSingleExecution.js rename to src/hooks/useSingleExecution.ts index a2b4ccb4cd53..16a98152def1 100644 --- a/src/hooks/useSingleExecution.js +++ b/src/hooks/useSingleExecution.ts @@ -1,20 +1,20 @@ import {useCallback, useRef, useState} from 'react'; import {InteractionManager} from 'react-native'; +type Action = (...params: T) => void | Promise; + /** * With any action passed in, it will only allow 1 such action to occur at a time. - * - * @returns {Object} */ export default function useSingleExecution() { const [isExecuting, setIsExecuting] = useState(false); - const isExecutingRef = useRef(); + const isExecutingRef = useRef(); isExecutingRef.current = isExecuting; const singleExecution = useCallback( - (action) => - (...params) => { + (action: Action) => + (...params: T) => { if (isExecutingRef.current) { return; } diff --git a/src/libs/Accessibility/index.ts b/src/libs/Accessibility/index.ts index 5eceda8edcb1..aa167b1239b2 100644 --- a/src/libs/Accessibility/index.ts +++ b/src/libs/Accessibility/index.ts @@ -42,7 +42,7 @@ const useAutoHitSlop = () => { }, [frameSize], ); - return [getHitSlopForSize(frameSize), onLayout]; + return [getHitSlopForSize(frameSize), onLayout] as const; }; export default { diff --git a/src/libs/KeyboardShortcut/index.ts b/src/libs/KeyboardShortcut/index.ts index cfcf5d5ef535..1b684a7ab19f 100644 --- a/src/libs/KeyboardShortcut/index.ts +++ b/src/libs/KeyboardShortcut/index.ts @@ -128,7 +128,7 @@ function getPlatformEquivalentForKeys(keys: string[]): string[] { */ function subscribe( key: string, - callback: () => void, + callback: (event?: KeyboardEvent) => void, descriptionKey: string, modifiers: string[] = ['shift'], captureOnInputs = false, diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index faece4f44335..7a2135ead3cd 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1,5 +1,5 @@ import {CSSProperties} from 'react'; -import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, TextStyle, ViewStyle} from 'react-native'; +import {Animated, DimensionValue, ImageStyle, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native'; import {EdgeInsets} from 'react-native-safe-area-context'; import {ValueOf} from 'type-fest'; import * as Browser from '@libs/Browser'; @@ -16,7 +16,7 @@ import spacing from './utilities/spacing'; import variables from './variables'; type AllStyles = ViewStyle | TextStyle | ImageStyle; -type ParsableStyle = AllStyles | ((state: PressableStateCallbackType) => AllStyles); +type ParsableStyle = StyleProp | ((state: PressableStateCallbackType) => StyleProp); type ColorValue = ValueOf; type AvatarSizeName = ValueOf; @@ -748,9 +748,8 @@ function parseStyleAsArray(styleParam: T | T[]): T[] { /** * Parse style function and return Styles object */ -function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): AllStyles[] { - const functionAppliedStyle = typeof style === 'function' ? style(state) : style; - return parseStyleAsArray(functionAppliedStyle); +function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): StyleProp { + return typeof style === 'function' ? style(state) : style } /** diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index a816fc77625b..0c9d7ec7ff91 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -85,7 +85,7 @@ declare module 'react-native' { accessibilityInvalid?: boolean; accessibilityKeyShortcuts?: string[]; accessibilityLabel?: string; - accessibilityLabelledBy?: idRefList; + accessibilityLabelledBy?: idRef; accessibilityLevel?: number; accessibilityLiveRegion?: 'assertive' | 'none' | 'polite'; accessibilityModal?: boolean; @@ -312,7 +312,10 @@ declare module 'react-native' { readonly hovered: boolean; readonly pressed: boolean; } - interface PressableStateCallbackType extends WebPressableStateCallbackType {} + interface PressableStateCallbackType extends WebPressableStateCallbackType { + readonly isScreenReaderActive: boolean; + readonly isDisabled: boolean; + } // Extracted from react-native-web, packages/react-native-web/src/exports/Pressable/index.js interface WebPressableProps extends WebSharedProps { From 28c1d9f0041bcdb0587264588860f754f7ba7f70 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 10:20:50 +0100 Subject: [PATCH 25/46] Rename more files --- .../Pressable/GenericPressable/PropTypes.js | 142 ------------------ ...Toggle.js => PressableWithDelayToggle.tsx} | 0 ...hFeedback.js => PressableWithFeedback.tsx} | 0 ...edback.js => PressableWithoutFeedback.tsx} | 0 ...houtFocus.js => PressableWithoutFocus.tsx} | 0 5 files changed, 142 deletions(-) delete mode 100644 src/components/Pressable/GenericPressable/PropTypes.js rename src/components/Pressable/{PressableWithDelayToggle.js => PressableWithDelayToggle.tsx} (100%) rename src/components/Pressable/{PressableWithFeedback.js => PressableWithFeedback.tsx} (100%) rename src/components/Pressable/{PressableWithoutFeedback.js => PressableWithoutFeedback.tsx} (100%) rename src/components/Pressable/{PressableWithoutFocus.js => PressableWithoutFocus.tsx} (100%) diff --git a/src/components/Pressable/GenericPressable/PropTypes.js b/src/components/Pressable/GenericPressable/PropTypes.js deleted file mode 100644 index 870c63301239..000000000000 --- a/src/components/Pressable/GenericPressable/PropTypes.js +++ /dev/null @@ -1,142 +0,0 @@ -import PropTypes from 'prop-types'; -import stylePropType from '@styles/stylePropTypes'; -import CONST from '@src/CONST'; - -const stylePropTypeWithFunction = PropTypes.oneOfType([stylePropType, PropTypes.func]); - -/** - * Custom test for required props - * + accessibilityLabel is required when accessible is true - * @param {Object} props - * @returns {Error} Error if prop is required - */ -function requiredPropsCheck(props) { - if (props.accessible !== true || (props.accessibilityLabel !== undefined && typeof props.accessibilityLabel === 'string')) { - return; - } - return new Error(`Provide a valid string for accessibilityLabel prop when accessible is true`); -} - -const pressablePropTypes = { - /** - * onPress callback - */ - onPress: PropTypes.func, - - /** - * Specifies keyboard shortcut to trigger onPressHandler - * @example {shortcutKey: 'a', modifiers: ['ctrl', 'shift'], descriptionKey: 'keyboardShortcut.description'} - */ - keyboardShortcut: PropTypes.shape({ - descriptionKey: PropTypes.string.isRequired, - shortcutKey: PropTypes.string.isRequired, - modifiers: PropTypes.arrayOf(PropTypes.string), - }), - - /** - * Specifies if haptic feedback should be used on press - * @default false - */ - shouldUseHapticsOnPress: PropTypes.bool, - - /** - * Specifies if haptic feedback should be used on long press - * @default false - */ - shouldUseHapticsOnLongPress: PropTypes.bool, - - /** - * style for when the component is disabled. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.isDisabled ? 'red' : 'blue'}) - */ - disabledStyle: stylePropTypeWithFunction, - - /** - * style for when the component is hovered. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.hovered ? 'red' : 'blue'}) - */ - hoverStyle: stylePropTypeWithFunction, - - /** - * style for when the component is focused. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.focused ? 'red' : 'blue'}) - */ - focusStyle: stylePropTypeWithFunction, - - /** - * style for when the component is pressed. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.pressed ? 'red' : 'blue'}) - */ - pressStyle: stylePropTypeWithFunction, - - /** - * style for when the component is active and the screen reader is on. - * Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.isScreenReaderActive ? 'red' : 'blue'}) - */ - screenReaderActiveStyle: stylePropTypeWithFunction, - - /** - * Specifies if the component should be accessible when the screen reader is on - * @default 'all' - * @example 'all' - the component is accessible regardless of screen reader state - * @example 'active' - the component is accessible only when the screen reader is on - * @example 'disabled' - the component is not accessible when the screen reader is on - */ - enableInScreenReaderStates: PropTypes.oneOf([CONST.SCREEN_READER_STATES.ALL, CONST.SCREEN_READER_STATES.ACTIVE, CONST.SCREEN_READER_STATES.DISABLED]), - - /** - * Specifies which component should be focused after interacting with this component - */ - nextFocusRef: PropTypes.func, - - /** - * Specifies the accessibility label for the component - * @example 'Search' - * @example 'Close' - */ - accessibilityLabel: requiredPropsCheck, - - /** - * Specifies the accessibility hint for the component - * @example 'Double tap to open' - */ - accessibilityHint: PropTypes.string, - - /** - * Specifies if the component should calculate its hitSlop automatically - * @default true - */ - shouldUseAutoHitSlop: PropTypes.bool, -}; - -const defaultProps = { - onPress: () => {}, - keyboardShortcut: undefined, - shouldUseHapticsOnPress: false, - shouldUseHapticsOnLongPress: false, - disabledStyle: {}, - hoverStyle: {}, - focusStyle: {}, - pressStyle: {}, - screenReaderActiveStyle: {}, - enableInScreenReaderStates: CONST.SCREEN_READER_STATES.ALL, - nextFocusRef: undefined, - shouldUseAutoHitSlop: false, - accessible: true, -}; - -export default { - pressablePropTypes, - defaultProps, -}; diff --git a/src/components/Pressable/PressableWithDelayToggle.js b/src/components/Pressable/PressableWithDelayToggle.tsx similarity index 100% rename from src/components/Pressable/PressableWithDelayToggle.js rename to src/components/Pressable/PressableWithDelayToggle.tsx diff --git a/src/components/Pressable/PressableWithFeedback.js b/src/components/Pressable/PressableWithFeedback.tsx similarity index 100% rename from src/components/Pressable/PressableWithFeedback.js rename to src/components/Pressable/PressableWithFeedback.tsx diff --git a/src/components/Pressable/PressableWithoutFeedback.js b/src/components/Pressable/PressableWithoutFeedback.tsx similarity index 100% rename from src/components/Pressable/PressableWithoutFeedback.js rename to src/components/Pressable/PressableWithoutFeedback.tsx diff --git a/src/components/Pressable/PressableWithoutFocus.js b/src/components/Pressable/PressableWithoutFocus.tsx similarity index 100% rename from src/components/Pressable/PressableWithoutFocus.js rename to src/components/Pressable/PressableWithoutFocus.tsx From a63a8962ed3a1ba38de278b4331bbb655104ca7f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 10:28:38 +0100 Subject: [PATCH 26/46] Migrate PressableWithoutFocus --- .../Pressable/PressableWithoutFocus.tsx | 78 ++++++------------- 1 file changed, 23 insertions(+), 55 deletions(-) diff --git a/src/components/Pressable/PressableWithoutFocus.tsx b/src/components/Pressable/PressableWithoutFocus.tsx index 641e695b1013..32cb1708baf0 100644 --- a/src/components/Pressable/PressableWithoutFocus.tsx +++ b/src/components/Pressable/PressableWithoutFocus.tsx @@ -1,31 +1,7 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import _ from 'underscore'; -import StylePropType from '@styles/stylePropTypes'; +import React, {useRef} from 'react'; +import {View} from 'react-native'; import GenericPressable from './GenericPressable'; -import genericPressablePropTypes from './GenericPressable/PropTypes'; - -const propTypes = { - /** Element that should be clickable */ - children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired, - - /** Callback for onPress event */ - onPress: PropTypes.func.isRequired, - - /** Callback for onLongPress event */ - onLongPress: PropTypes.func, - - /** Styles that should be passed to touchable container */ - style: StylePropType, - - /** Proptypes of pressable component used for implementation */ - ...genericPressablePropTypes.pressablePropTypes, -}; - -const defaultProps = { - style: [], - onLongPress: undefined, -}; +import PressableProps from './GenericPressable/types'; /** * This component prevents the tapped element from capturing focus. @@ -34,35 +10,27 @@ const defaultProps = { * Therefore it shifts the element to bring it back to focus. * https://github.com/Expensify/App/issues/6806 */ -class PressableWithoutFocus extends React.Component { - constructor(props) { - super(props); - this.pressAndBlur = this.pressAndBlur.bind(this); - } - - pressAndBlur() { - this.pressableRef.blur(); - this.props.onPress(); - } - - render() { - const restProps = _.omit(this.props, ['children', 'onPress', 'onLongPress', 'style']); - return ( - (this.pressableRef = el)} - style={this.props.style} - // eslint-disable-next-line react/jsx-props-no-spreading - {...restProps} - > - {this.props.children} - - ); - } +function PressableWithoutFocus({children, onPress, onLongPress, ...rest}: PressableProps) { + const ref = useRef(null); + + const pressAndBlur = () => { + ref?.current?.blur(); + onPress(); + }; + + return ( + + {children} + + ); } -PressableWithoutFocus.propTypes = propTypes; -PressableWithoutFocus.defaultProps = defaultProps; +PressableWithoutFocus.displayName = 'PressableWithoutFocus'; export default PressableWithoutFocus; From 56ca43fcccecf0e14cf38c497261ca8ae853ee7f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 10:31:01 +0100 Subject: [PATCH 27/46] Migrate PressableWithoutFeedback --- .../Pressable/PressableWithoutFeedback.tsx | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/components/Pressable/PressableWithoutFeedback.tsx b/src/components/Pressable/PressableWithoutFeedback.tsx index 92e704550dec..c3b780e63cfd 100644 --- a/src/components/Pressable/PressableWithoutFeedback.tsx +++ b/src/components/Pressable/PressableWithoutFeedback.tsx @@ -1,23 +1,21 @@ -import React from 'react'; -import _ from 'underscore'; +import React, {ForwardedRef} from 'react'; +import {View} from 'react-native'; import GenericPressable from './GenericPressable'; -import GenericPressableProps from './GenericPressable/PropTypes'; +import PressableProps from './GenericPressable/types'; -const omittedProps = ['pressStyle', 'hoverStyle', 'focusStyle', 'activeStyle', 'disabledStyle', 'screenReaderActiveStyle', 'shouldUseHapticsOnPress', 'shouldUseHapticsOnLongPress']; - -const PressableWithoutFeedback = React.forwardRef((props, ref) => { - const propsWithoutStyling = _.omit(props, omittedProps); +function PressableWithoutFeedback( + {pressStyle, hoverStyle, focusStyle, disabledStyle, screenReaderActiveStyle, shouldUseHapticsOnPress, shouldUseHapticsOnLongPress, ...rest}: PressableProps, + ref: ForwardedRef, +) { return ( ); -}); +} PressableWithoutFeedback.displayName = 'PressableWithoutFeedback'; -PressableWithoutFeedback.propTypes = _.omit(GenericPressableProps.pressablePropTypes, omittedProps); -PressableWithoutFeedback.defaultProps = _.omit(GenericPressableProps.defaultProps, omittedProps); -export default PressableWithoutFeedback; +export default React.forwardRef(PressableWithoutFeedback); From 384cb529e99973d9c32478a8292252c99be883a4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 10:40:12 +0100 Subject: [PATCH 28/46] Migrate PressableWithFeedback --- .../Pressable/PressableWithFeedback.tsx | 91 +++++++++---------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/src/components/Pressable/PressableWithFeedback.tsx b/src/components/Pressable/PressableWithFeedback.tsx index ad29204bb018..5d7f7c110ea7 100644 --- a/src/components/Pressable/PressableWithFeedback.tsx +++ b/src/components/Pressable/PressableWithFeedback.tsx @@ -1,95 +1,90 @@ -import propTypes from 'prop-types'; -import React, {forwardRef, useState} from 'react'; -import _ from 'underscore'; +import React, {ForwardedRef, forwardRef, useState} from 'react'; +import {StyleProp, View, ViewStyle} from 'react-native'; +import {AnimatedStyle} from 'react-native-reanimated'; import OpacityView from '@components/OpacityView'; import variables from '@styles/variables'; import GenericPressable from './GenericPressable'; -import GenericPressablePropTypes from './GenericPressable/PropTypes'; +import PressableProps from './GenericPressable/types'; -const omittedProps = ['wrapperStyle', 'needsOffscreenAlphaCompositing']; +type PressableWithFeedbackProps = PressableProps & { + /** Style for the wrapper view */ + wrapperStyle?: StyleProp>; -const PressableWithFeedbackPropTypes = { - ...GenericPressablePropTypes.pressablePropTypes, /** * Determines what opacity value should be applied to the underlaying view when Pressable is pressed. * To disable dimming, pass 1 as pressDimmingValue * @default variables.pressDimValue */ - pressDimmingValue: propTypes.number, + pressDimmingValue?: number; + /** * Determines what opacity value should be applied to the underlaying view when pressable is hovered. * To disable dimming, pass 1 as hoverDimmingValue * @default variables.hoverDimValue */ - hoverDimmingValue: propTypes.number, - /** - * Used to locate this view from native classes. - */ - nativeID: propTypes.string, + hoverDimmingValue?: number; /** Whether the view needs to be rendered offscreen (for Android only) */ - needsOffscreenAlphaCompositing: propTypes.bool, -}; - -const PressableWithFeedbackDefaultProps = { - ...GenericPressablePropTypes.defaultProps, - pressDimmingValue: variables.pressDimValue, - hoverDimmingValue: variables.hoverDimValue, - nativeID: '', - wrapperStyle: [], - needsOffscreenAlphaCompositing: false, + needsOffscreenAlphaCompositing?: boolean; }; -const PressableWithFeedback = forwardRef((props, ref) => { - const propsWithoutWrapperProps = _.omit(props, omittedProps); +function PressableWithFeedback( + { + children, + wrapperStyle = [], + needsOffscreenAlphaCompositing = false, + pressDimmingValue = variables.pressDimValue, + hoverDimmingValue = variables.hoverDimValue, + ...rest + }: PressableWithFeedbackProps, + ref: ForwardedRef, +) { const [isPressed, setIsPressed] = useState(false); const [isHovered, setIsHovered] = useState(false); return ( { + {...rest} + disabled={rest.disabled} + onHoverIn={(event) => { setIsHovered(true); - if (props.onHoverIn) { - props.onHoverIn(); + if (rest.onHoverIn) { + rest.onHoverIn(event); } }} - onHoverOut={() => { + onHoverOut={(event) => { setIsHovered(false); - if (props.onHoverOut) { - props.onHoverOut(); + if (rest.onHoverOut) { + rest.onHoverOut(event); } }} - onPressIn={() => { + onPressIn={(event) => { setIsPressed(true); - if (props.onPressIn) { - props.onPressIn(); + if (rest.onPressIn) { + rest.onPressIn(event); } }} - onPressOut={() => { + onPressOut={(event) => { setIsPressed(false); - if (props.onPressOut) { - props.onPressOut(); + if (rest.onPressOut) { + rest.onPressOut(event); } }} > - {(state) => (_.isFunction(props.children) ? props.children(state) : props.children)} + {(state) => (typeof children === 'function' ? children(state) : children)} ); -}); +} PressableWithFeedback.displayName = 'PressableWithFeedback'; -PressableWithFeedback.propTypes = PressableWithFeedbackPropTypes; -PressableWithFeedback.defaultProps = PressableWithFeedbackDefaultProps; -export default PressableWithFeedback; +export default forwardRef(PressableWithFeedback); From 831c3f9586bf90ad97736d8d37422e7257132908 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 12:46:21 +0100 Subject: [PATCH 29/46] Migrate PressableWithDelayToggle --- src/components/OpacityView.js | 2 +- .../Pressable/PressableWithDelayToggle.tsx | 122 ++++++++---------- src/libs/getButtonState.ts | 6 +- 3 files changed, 57 insertions(+), 73 deletions(-) diff --git a/src/components/OpacityView.js b/src/components/OpacityView.js index ebd261916e65..582251d569c1 100644 --- a/src/components/OpacityView.js +++ b/src/components/OpacityView.js @@ -21,7 +21,7 @@ const propTypes = { * @default [] */ // eslint-disable-next-line react/forbid-prop-types - style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), + style: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.bool]), /** * The value to use for the opacity when the view is dimmed diff --git a/src/components/Pressable/PressableWithDelayToggle.tsx b/src/components/Pressable/PressableWithDelayToggle.tsx index 7113afff8bdc..388c5b3660b6 100644 --- a/src/components/Pressable/PressableWithDelayToggle.tsx +++ b/src/components/Pressable/PressableWithDelayToggle.tsx @@ -1,8 +1,8 @@ -import PropTypes from 'prop-types'; -import React from 'react'; +import React, {ForwardedRef, forwardRef} from 'react'; +import {Text as RNText, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; +import {SvgProps} from 'react-native-svg'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; -import refPropTypes from '@components/refPropTypes'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import useThrottledButtonState from '@hooks/useThrottledButtonState'; @@ -10,68 +10,61 @@ import getButtonState from '@libs/getButtonState'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; import variables from '@styles/variables'; +import PressableProps from './GenericPressable/types'; import PressableWithoutFeedback from './PressableWithoutFeedback'; -const propTypes = { - /** Ref passed to the component by React.forwardRef (do not pass from parent) */ - innerRef: refPropTypes, - +type PressableWithDelayToggleProps = PressableProps & { /** The text to display */ - text: PropTypes.string, + text: string; /** The text to display once the pressable is pressed */ - textChecked: PropTypes.string, + textChecked: string; /** The tooltip text to display */ - tooltipText: PropTypes.string, + tooltipText: string; /** The tooltip text to display once the pressable is pressed */ - tooltipTextChecked: PropTypes.string, + tooltipTextChecked: string; /** Styles to apply to the container */ - // eslint-disable-next-line react/forbid-prop-types - styles: PropTypes.arrayOf(PropTypes.object), + styles?: StyleProp; - /** Styles to apply to the text */ - // eslint-disable-next-line react/forbid-prop-types - textStyles: PropTypes.arrayOf(PropTypes.object), + // /** Styles to apply to the text */ + textStyles?: StyleProp; /** Styles to apply to the icon */ - // eslint-disable-next-line react/forbid-prop-types - iconStyles: PropTypes.arrayOf(PropTypes.object), - - /** Callback to be called on onPress */ - onPress: PropTypes.func.isRequired, + iconStyles?: StyleProp; /** The icon to display */ - icon: PropTypes.func, + icon?: React.FC; /** The icon to display once the pressable is pressed */ - iconChecked: PropTypes.func, + iconChecked?: React.FC; /** * Should be set to `true` if this component is being rendered inline in * another `Text`. This is due to limitations in RN regarding the * vertical text alignment of non-Text elements */ - inline: PropTypes.bool, -}; - -const defaultProps = { - text: '', - textChecked: '', - tooltipText: '', - tooltipTextChecked: '', - styles: [], - textStyles: [], - iconStyles: [], - icon: null, - inline: true, - iconChecked: Expensicons.Checkmark, - innerRef: () => {}, + inline?: boolean; }; -function PressableWithDelayToggle(props) { +function PressableWithDelayToggle( + { + iconChecked = Expensicons.Checkmark, + inline = true, + onPress, + text, + textChecked, + tooltipText, + tooltipTextChecked, + styles: pressableStyle, + textStyles, + iconStyles, + icon, + }: PressableWithDelayToggleProps, + ref: ForwardedRef, +) { const [isActive, temporarilyDisableInteractions] = useThrottledButtonState(); const updatePressState = () => { @@ -79,54 +72,58 @@ function PressableWithDelayToggle(props) { return; } temporarilyDisableInteractions(); - props.onPress(); + onPress(); }; // Due to limitations in RN regarding the vertical text alignment of non-Text elements, // for elements that are supposed to be inline, we need to use a Text element instead // of a Pressable - const PressableView = props.inline ? Text : PressableWithoutFeedback; - const tooltipText = !isActive ? props.tooltipTextChecked : props.tooltipText; + const PressableView = inline ? Text : PressableWithoutFeedback; + const tooltipTexts = !isActive ? tooltipTextChecked : tooltipText; const labelText = ( - {!isActive && props.textChecked ? props.textChecked : props.text} + {!isActive && textChecked ? textChecked : text}   ); return ( <> - {props.inline && labelText} + {inline && labelText} {({hovered, pressed}) => ( <> - {!props.inline && labelText} - {props.icon && ( + {!inline && labelText} + {icon && ( )} @@ -138,17 +135,6 @@ function PressableWithDelayToggle(props) { ); } -PressableWithDelayToggle.propTypes = propTypes; -PressableWithDelayToggle.defaultProps = defaultProps; - -const PressableWithDelayToggleWithRef = React.forwardRef((props, ref) => ( - -)); - -PressableWithDelayToggleWithRef.displayName = 'PressableWithDelayToggleWithRef'; +PressableWithDelayToggle.displayName = 'PressableWithDelayToggle'; -export default PressableWithDelayToggleWithRef; +export default forwardRef(PressableWithDelayToggle); diff --git a/src/libs/getButtonState.ts b/src/libs/getButtonState.ts index 6b89e1b7d383..fe593b9f613e 100644 --- a/src/libs/getButtonState.ts +++ b/src/libs/getButtonState.ts @@ -1,12 +1,10 @@ import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; -type GetButtonState = (isActive: boolean, isPressed: boolean, isComplete: boolean, isDisabled: boolean, isInteractive: boolean) => ValueOf; - /** * Get the string representation of a button's state. */ -const getButtonState: GetButtonState = (isActive = false, isPressed = false, isComplete = false, isDisabled = false, isInteractive = true) => { +function getButtonState(isActive = false, isPressed = false, isComplete = false, isDisabled = false, isInteractive = true): ValueOf { if (!isInteractive) { return CONST.BUTTON_STATES.DEFAULT; } @@ -28,6 +26,6 @@ const getButtonState: GetButtonState = (isActive = false, isPressed = false, isC } return CONST.BUTTON_STATES.DEFAULT; -}; +} export default getButtonState; From 5e961ba6667201f5f2945c6728adb9c0103726ce Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 13:23:14 +0100 Subject: [PATCH 30/46] Remove excessive optional chaining --- .../Pressable/GenericPressable/BaseGenericPressable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx index a64fd9909f0d..2da089b94661 100644 --- a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx +++ b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx @@ -83,7 +83,7 @@ function GenericPressable( HapticFeedback.longPress(); } if (ref && 'current' in ref) { - ref?.current?.blur(); + ref.current?.blur(); } onLongPress(event); @@ -104,7 +104,7 @@ function GenericPressable( HapticFeedback.press(); } if (ref && 'current' in ref) { - ref?.current?.blur(); + ref.current?.blur(); } onPress(event); From 7e6357ea60a2c708bddbb5df6073aa299eaeab0d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 13:35:17 +0100 Subject: [PATCH 31/46] Revert accessibilityHint change in index.native.tsx --- src/components/Pressable/GenericPressable/index.native.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Pressable/GenericPressable/index.native.tsx b/src/components/Pressable/GenericPressable/index.native.tsx index fbc53551f435..5bed0f488063 100644 --- a/src/components/Pressable/GenericPressable/index.native.tsx +++ b/src/components/Pressable/GenericPressable/index.native.tsx @@ -8,6 +8,7 @@ function NativeGenericPressable(props: PressableProps, ref: ForwardedRef) Date: Thu, 2 Nov 2023 13:51:06 +0100 Subject: [PATCH 32/46] Improve accessibilityLabel for pressable --- .../Pressable/GenericPressable/types.ts | 232 ++++++++++-------- .../Pressable/PressableWithDelayToggle.tsx | 2 +- 2 files changed, 132 insertions(+), 102 deletions(-) diff --git a/src/components/Pressable/GenericPressable/types.ts b/src/components/Pressable/GenericPressable/types.ts index 2e8857224646..35616cb600a3 100644 --- a/src/components/Pressable/GenericPressable/types.ts +++ b/src/components/Pressable/GenericPressable/types.ts @@ -12,106 +12,136 @@ type Shortcut = { modifiers: string[]; }; -type PressableProps = RNPressableProps & { - /** - * onPress callback - */ - onPress: (event?: GestureResponderEvent | KeyboardEvent) => void; - - /** - * Specifies keyboard shortcut to trigger onPressHandler - * @example {shortcutKey: 'a', modifiers: ['ctrl', 'shift'], descriptionKey: 'keyboardShortcut.description'} - */ - keyboardShortcut?: Shortcut; - - /** - * Specifies if haptic feedback should be used on press - * @default false - */ - shouldUseHapticsOnPress?: boolean; - - /** - * Specifies if haptic feedback should be used on long press - * @default false - */ - shouldUseHapticsOnLongPress?: boolean; - - /** - * style for when the component is disabled. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.isDisabled ? 'red' : 'blue'}) - */ - disabledStyle?: StylePropWithFunction; - - /** - * style for when the component is hovered. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.hovered ? 'red' : 'blue'}) - */ - hoverStyle?: StylePropWithFunction; - - /** - * style for when the component is focused. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.focused ? 'red' : 'blue'}) - */ - focusStyle?: StylePropWithFunction; - - /** - * style for when the component is pressed. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.pressed ? 'red' : 'blue'}) - */ - pressStyle?: StylePropWithFunction; - - /** - * style for when the component is active and the screen reader is on. - * Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) - * @default {} - * @example {backgroundColor: 'red'} - * @example state => ({backgroundColor: state.isScreenReaderActive ? 'red' : 'blue'}) - */ - screenReaderActiveStyle?: StylePropWithFunction; - - /** - * Specifies if the component should be accessible when the screen reader is on - * @default 'all' - * @example 'all' - the component is accessible regardless of screen reader state - * @example 'active' - the component is accessible only when the screen reader is on - * @example 'disabled' - the component is not accessible when the screen reader is on - */ - enableInScreenReaderStates?: ValueOf; - - /** - * Specifies which component should be focused after interacting with this component - */ - nextFocusRef?: ElementRef> & RefObject; - - /** - * Specifies the accessibility label for the component - * @example 'Search' - * @example 'Close' - */ - accessibilityLabel: string; - - /** - * Specifies the accessibility hint for the component - * @example 'Double tap to open' - */ - accessibilityHint?: string; - - /** - * Specifies if the component should calculate its hitSlop automatically - * @default true - */ - shouldUseAutoHitSlop?: boolean; - - /** Turns off drag area for the component */ - noDragArea?: boolean; -}; +type RequiredAccessibilityLabel = + | { + /** + * When true, indicates that the view is an accessibility element. + * By default, all the touchable elements are accessible. + */ + accessible?: true | undefined; + + /** + * Specifies the accessibility label for the component + * @example 'Search' + * @example 'Close' + */ + accessibilityLabel: string; + } + | { + /** + * When false, indicates that the view is not an accessibility element. + */ + accessible: false; + + /** + * Specifies the accessibility label for the component + * @example 'Search' + * @example 'Close' + */ + accessibilityLabel?: string; + }; + +type PressableProps = RNPressableProps & + RequiredAccessibilityLabel & { + /** + * onPress callback + */ + onPress: (event?: GestureResponderEvent | KeyboardEvent) => void; + + /** + * Specifies keyboard shortcut to trigger onPressHandler + * @example {shortcutKey: 'a', modifiers: ['ctrl', 'shift'], descriptionKey: 'keyboardShortcut.description'} + */ + keyboardShortcut?: Shortcut; + + /** + * Specifies if haptic feedback should be used on press + * @default false + */ + shouldUseHapticsOnPress?: boolean; + + /** + * Specifies if haptic feedback should be used on long press + * @default false + */ + shouldUseHapticsOnLongPress?: boolean; + + /** + * style for when the component is disabled. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.isDisabled ? 'red' : 'blue'}) + */ + disabledStyle?: StylePropWithFunction; + + /** + * style for when the component is hovered. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.hovered ? 'red' : 'blue'}) + */ + hoverStyle?: StylePropWithFunction; + + /** + * style for when the component is focused. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.focused ? 'red' : 'blue'}) + */ + focusStyle?: StylePropWithFunction; + + /** + * style for when the component is pressed. Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.pressed ? 'red' : 'blue'}) + */ + pressStyle?: StylePropWithFunction; + + /** + * style for when the component is active and the screen reader is on. + * Can be a function that receives the component's state (active, disabled, hover, focus, pressed, isScreenReaderActive) + * @default {} + * @example {backgroundColor: 'red'} + * @example state => ({backgroundColor: state.isScreenReaderActive ? 'red' : 'blue'}) + */ + screenReaderActiveStyle?: StylePropWithFunction; + + /** + * Specifies if the component should be accessible when the screen reader is on + * @default 'all' + * @example 'all' - the component is accessible regardless of screen reader state + * @example 'active' - the component is accessible only when the screen reader is on + * @example 'disabled' - the component is not accessible when the screen reader is on + */ + enableInScreenReaderStates?: ValueOf; + + /** + * Specifies which component should be focused after interacting with this component + */ + nextFocusRef?: ElementRef> & RefObject; + + /** + * Specifies the accessibility label for the component + * @example 'Search' + * @example 'Close' + */ + accessibilityLabel?: string; + + /** + * Specifies the accessibility hint for the component + * @example 'Double tap to open' + */ + accessibilityHint?: string; + + /** + * Specifies if the component should calculate its hitSlop automatically + * @default true + */ + shouldUseAutoHitSlop?: boolean; + + /** Turns off drag area for the component */ + noDragArea?: boolean; + }; export default PressableProps; diff --git a/src/components/Pressable/PressableWithDelayToggle.tsx b/src/components/Pressable/PressableWithDelayToggle.tsx index 388c5b3660b6..316adc25076d 100644 --- a/src/components/Pressable/PressableWithDelayToggle.tsx +++ b/src/components/Pressable/PressableWithDelayToggle.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-native-a11y/has-valid-accessibility-descriptors */ import React, {ForwardedRef, forwardRef} from 'react'; import {Text as RNText, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; import {SvgProps} from 'react-native-svg'; @@ -109,7 +110,6 @@ function PressableWithDelayToggle( From edd4afa21f81cd4bd81bbbe1987c45181b090e4b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 14:01:47 +0100 Subject: [PATCH 33/46] Update stylePropTypes and use it in OpacityView --- src/components/FixedFooter.tsx | 2 +- src/components/OpacityView.js | 4 ++-- src/styles/stylePropTypes.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/FixedFooter.tsx b/src/components/FixedFooter.tsx index afda41f16d06..61529f16b3bb 100644 --- a/src/components/FixedFooter.tsx +++ b/src/components/FixedFooter.tsx @@ -11,7 +11,7 @@ type FixedFooterProps = { }; function FixedFooter({style = [], children}: FixedFooterProps) { - return {children}; + return {children}; } FixedFooter.displayName = 'FixedFooter'; diff --git a/src/components/OpacityView.js b/src/components/OpacityView.js index 582251d569c1..be42c22feaa2 100644 --- a/src/components/OpacityView.js +++ b/src/components/OpacityView.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import shouldRenderOffscreen from '@libs/shouldRenderOffscreen'; +import stylePropTypes from '@styles/stylePropTypes'; import * as StyleUtils from '@styles/StyleUtils'; import variables from '@styles/variables'; @@ -20,8 +21,7 @@ const propTypes = { * Array of style objects * @default [] */ - // eslint-disable-next-line react/forbid-prop-types - style: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.bool]), + style: stylePropTypes, /** * The value to use for the opacity when the view is dimmed diff --git a/src/styles/stylePropTypes.js b/src/styles/stylePropTypes.js index b97decba621c..961846f8065a 100644 --- a/src/styles/stylePropTypes.js +++ b/src/styles/stylePropTypes.js @@ -1,3 +1,3 @@ import PropTypes from 'prop-types'; -export default PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object), PropTypes.func]); +export default PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object), PropTypes.func, PropTypes.bool]); From 9a5efebf00fdec759ec2549554c3f6ac8de0d634 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 14:04:28 +0100 Subject: [PATCH 34/46] Fix a typo --- src/components/FixedFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FixedFooter.tsx b/src/components/FixedFooter.tsx index 61529f16b3bb..afda41f16d06 100644 --- a/src/components/FixedFooter.tsx +++ b/src/components/FixedFooter.tsx @@ -11,7 +11,7 @@ type FixedFooterProps = { }; function FixedFooter({style = [], children}: FixedFooterProps) { - return {children}; + return {children}; } FixedFooter.displayName = 'FixedFooter'; From 1071d374614bbb578838ba27daeaf7d43fbd459a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 2 Nov 2023 14:43:14 +0100 Subject: [PATCH 35/46] Fix prettier --- src/styles/StyleUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 7a2135ead3cd..85a9bc7110bd 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -749,7 +749,7 @@ function parseStyleAsArray(styleParam: T | T[]): T[] { * Parse style function and return Styles object */ function parseStyleFromFunction(style: ParsableStyle, state: PressableStateCallbackType): StyleProp { - return typeof style === 'function' ? style(state) : style + return typeof style === 'function' ? style(state) : style; } /** From 28a5209a78b4f5d47efdfae797681f2d669f4a31 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 3 Nov 2023 11:51:05 +0100 Subject: [PATCH 36/46] Fix aria-keyshortcuts --- .../Pressable/GenericPressable/BaseGenericPressable.tsx | 2 +- src/types/modules/react-native.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx index 2da089b94661..1576fe18da54 100644 --- a/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx +++ b/src/components/Pressable/GenericPressable/BaseGenericPressable.tsx @@ -146,7 +146,7 @@ function GenericPressable( ...rest.accessibilityState, }} aria-disabled={isDisabled} - aria-keyshortcuts={keyboardShortcut?.modifiers.map((modifier) => `${modifier}+${keyboardShortcut.shortcutKey}`) ?? []} + aria-keyshortcuts={keyboardShortcut && `${keyboardShortcut.modifiers.join('')}+${keyboardShortcut.shortcutKey}`} // ios-only form of inputs onMagicTap={!isDisabled ? onPressHandler : undefined} onAccessibilityTap={!isDisabled ? onPressHandler : undefined} diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 0c9d7ec7ff91..ec857af2eceb 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -35,7 +35,7 @@ declare module 'react-native' { 'aria-haspopup'?: 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | false; 'aria-hidden'?: boolean; 'aria-invalid'?: boolean; - 'aria-keyshortcuts'?: string[]; + 'aria-keyshortcuts'?: string; 'aria-label'?: string; 'aria-labelledby'?: idRef; 'aria-level'?: number; From 73beb391c44ed09e0399cf54a393651e23fbe93a Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 3 Nov 2023 20:31:09 +0200 Subject: [PATCH 37/46] babel.config - disable console logging on native prod builds But do keep error and warn logs --- babel.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/babel.config.js b/babel.config.js index 7de6926c850d..29825d80fd2e 100644 --- a/babel.config.js +++ b/babel.config.js @@ -78,6 +78,11 @@ const metro = { }, ], ], + env: { + production: { + plugins: ['transform-remove-console', {exclude: ['error', 'warn']}], + }, + }, }; /* From 7f6ef194c60f6b0451598aa5aec33a51c363b652 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 3 Nov 2023 20:32:08 +0200 Subject: [PATCH 38/46] babel.config - don't try to disable console logging on production webpack builds --- babel.config.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/babel.config.js b/babel.config.js index 29825d80fd2e..ab8efa583f35 100644 --- a/babel.config.js +++ b/babel.config.js @@ -17,16 +17,8 @@ const defaultPlugins = [ ]; const webpack = { - env: { - production: { - presets: defaultPresets, - plugins: [...defaultPlugins, 'transform-remove-console'], - }, - development: { - presets: defaultPresets, - plugins: defaultPlugins, - }, - }, + presets: defaultPresets, + plugins: defaultPlugins, }; const metro = { From 9579930dccbfe4f144d99dcaf87457f2da062114 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 3 Nov 2023 20:33:27 +0200 Subject: [PATCH 39/46] babel.config - print some env and build info for more insights --- babel.config.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/babel.config.js b/babel.config.js index ab8efa583f35..64a433936eb7 100644 --- a/babel.config.js +++ b/babel.config.js @@ -99,11 +99,19 @@ if (process.env.CAPTURE_METRICS === 'true') { ]); } -module.exports = ({caller}) => { +module.exports = (api) => { + console.debug('babel.config.js'); + console.debug(' - api.version:', api.version); + console.debug(' - api.env:', api.env()); + console.debug(' - process.env.NODE_ENV:', process.env.NODE_ENV); + console.debug(' - process.env.BABEL_ENV:', process.env.BABEL_ENV); + // For `react-native` (iOS/Android) caller will be "metro" // For `webpack` (Web) caller will be "@babel-loader" // For jest, it will be babel-jest // For `storybook` there won't be any config at all so we must give default argument of an empty object - const runningIn = caller((args = {}) => args.name); + const runningIn = api.caller((args = {}) => args.name); + console.debug(' - running in: ', runningIn); + return ['metro', 'babel-jest'].includes(runningIn) ? metro : webpack; }; From 8d2d2f2806ba0752db5e27cbbf1e81cb572cb8c1 Mon Sep 17 00:00:00 2001 From: Nam Le Date: Mon, 6 Nov 2023 22:31:27 +0700 Subject: [PATCH 40/46] fix use taskReportId --- src/libs/actions/ReportActions.ts | 5 +++-- src/types/onyx/ReportAction.ts | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 64e69def0583..d7ff96fc6c2e 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -26,8 +26,9 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction) { } // Delete the failed task report too - if (reportAction.childReportID) { - Report.deleteReport(reportAction.childReportID); + const taskReportID = reportAction.message?.[0]?.taskReportID; + if (taskReportID) { + Report.deleteReport(taskReportID); } return; } diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 19908273ad3d..842898c1a2bd 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -43,6 +43,9 @@ type Message = { moderationDecision?: Decision; translationKey?: string; + + /** ID of a task report */ + taskReportID?: string }; type Person = { From ce5e88f9c25ba0bec12c1e8e73035df1817a4adc Mon Sep 17 00:00:00 2001 From: Nam Le Date: Mon, 6 Nov 2023 22:36:31 +0700 Subject: [PATCH 41/46] fix lint --- src/types/onyx/ReportAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 842898c1a2bd..66622f4b29ea 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -45,7 +45,7 @@ type Message = { translationKey?: string; /** ID of a task report */ - taskReportID?: string + taskReportID?: string; }; type Person = { From b58b9126ee7efd872aef15b72e36617dcd1e87b9 Mon Sep 17 00:00:00 2001 From: Prince Mendiratta Date: Tue, 7 Nov 2023 13:29:39 +0530 Subject: [PATCH 42/46] feat: fix fatal crash on address page Signed-off-by: Prince Mendiratta --- src/pages/settings/Profile/PersonalDetails/AddressPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js index e44b00920544..d7c4b16270db 100644 --- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js +++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js @@ -212,7 +212,7 @@ function AddressPage({privatePersonalDetails, route}) { Date: Tue, 7 Nov 2023 08:39:17 +0000 Subject: [PATCH 43/46] [ImgBot] Optimize images *Total -- 620.30kb -> 587.82kb (5.24%) /assets/images/bell.svg -- 0.78kb -> 0.64kb (18.09%) /assets/images/home-background--android.svg -- 619.52kb -> 587.18kb (5.22%) Signed-off-by: ImgBotApp --- assets/images/bell.svg | 7 +- assets/images/home-background--android.svg | 6556 +------------------- 2 files changed, 2 insertions(+), 6561 deletions(-) diff --git a/assets/images/bell.svg b/assets/images/bell.svg index 6ba600dc695b..5a6b411185a9 100644 --- a/assets/images/bell.svg +++ b/assets/images/bell.svg @@ -1,6 +1 @@ - - - - - + \ No newline at end of file diff --git a/assets/images/home-background--android.svg b/assets/images/home-background--android.svg index 507aecf04836..2b72b6ccabe9 100644 --- a/assets/images/home-background--android.svg +++ b/assets/images/home-background--android.svg @@ -1,6555 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file From d0806683ca8f0c9f54e3c1091b2acfc7b6bdf405 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 7 Nov 2023 09:46:17 +0100 Subject: [PATCH 44/46] fix: displaying avatars in LHNOptionList --- src/components/LHNOptionsList/LHNOptionsList.js | 4 ++-- src/components/LHNOptionsList/OptionRowLHNData.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.js b/src/components/LHNOptionsList/LHNOptionsList.js index 3986773aca87..ebd00d4706d8 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.js +++ b/src/components/LHNOptionsList/LHNOptionsList.js @@ -144,7 +144,7 @@ function LHNOptionsList({ '', )}`; const itemComment = draftComments[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] || ''; - const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForAccountIDs(itemFullReport.participantAccountIDs, personalDetails)); + const participantsPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(itemFullReport.participantAccountIDs, personalDetails); return ( Date: Tue, 7 Nov 2023 12:06:18 +0000 Subject: [PATCH 45/46] Update version to 1.3.96-1 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index c7974572e665..6b284a13e4cd 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -90,8 +90,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001039600 - versionName "1.3.96-0" + versionCode 1001039601 + versionName "1.3.96-1" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 94d2986fd111..61b91d63200e 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.3.96.0 + 1.3.96.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 9478336965cf..d22058077cc9 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.3.96.0 + 1.3.96.1 diff --git a/package-lock.json b/package-lock.json index b7c9f13085c4..ac6e29333271 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.96-0", + "version": "1.3.96-1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.96-0", + "version": "1.3.96-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 229545f34252..3c38db5ef09c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.96-0", + "version": "1.3.96-1", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 377648bdea7bdb8e7d5e7a11b3c16f192d3a01dd Mon Sep 17 00:00:00 2001 From: OSBotify Date: Tue, 7 Nov 2023 12:11:26 +0000 Subject: [PATCH 46/46] Update version to 1.3.96-2 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6b284a13e4cd..7bf56a7a1177 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -90,8 +90,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001039601 - versionName "1.3.96-1" + versionCode 1001039602 + versionName "1.3.96-2" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 61b91d63200e..e31b05dcece6 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.3.96.1 + 1.3.96.2 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index d22058077cc9..1b3695499183 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.3.96.1 + 1.3.96.2 diff --git a/package-lock.json b/package-lock.json index ac6e29333271..b9eb50615924 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.96-1", + "version": "1.3.96-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.96-1", + "version": "1.3.96-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 3c38db5ef09c..f10ec0d81ee9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.96-1", + "version": "1.3.96-2", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",