From f7275e04e6d5aa07438760e51f1532fd165423df Mon Sep 17 00:00:00 2001 From: Robert Kozik Date: Fri, 19 Apr 2024 11:11:35 +0200 Subject: [PATCH 001/228] introduce OnyxAwareParser --- src/libs/OnyxAwareParser.ts | 39 +++++++++++++++++++ .../report/ContextMenu/ContextMenuActions.tsx | 4 +- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/libs/OnyxAwareParser.ts diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts new file mode 100644 index 000000000000..4a8d0045abe8 --- /dev/null +++ b/src/libs/OnyxAwareParser.ts @@ -0,0 +1,39 @@ +import ONYXKEYS from "@src/ONYXKEYS"; +import ExpensiMark from "expensify-common/lib/ExpensiMark"; +import Onyx from "react-native-onyx"; + +const parser = new ExpensiMark(); + +const reportIDToNameMap: Record = {}; +const accountIDToNameMap: Record = {}; + +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + callback: (report) => { + if (!report) { + return; + } + + reportIDToNameMap[report.reportID] = report.reportName ?? ''; + }, +}) + +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (personalDetailsList) => { + Object.values(personalDetailsList ?? {}).forEach((personalDetails) => { + if (!personalDetails) { + return; + } + + accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? ''; + }); + }, +}) + + +function parseHtmlToMarkdown(html: string): string { + return parser.htmlToMarkdown(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); +}; + +export default parseHtmlToMarkdown; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index b5cb9d911ef5..efe728148887 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -30,6 +30,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type {Beta, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; +import parseHtmlToMarkdown from '@libs/OnyxAwareParser'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; @@ -232,8 +233,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } const editAction = () => { if (!draftMessage) { - const parser = new ExpensiMark(); - Report.saveReportActionDraft(reportID, reportAction, parser.htmlToMarkdown(getActionHtml(reportAction))); + Report.saveReportActionDraft(reportID, reportAction, parseHtmlToMarkdown(getActionHtml(reportAction))); } else { Report.deleteReportActionDraft(reportID, reportAction); } From 7b3fe65b72567ec3bb7f516f531263b57a5914f8 Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 19 Apr 2024 15:06:04 +0200 Subject: [PATCH 002/228] `parser.htmlTo` -> `parseHtmlTo` --- package-lock.json | 6 +++--- package.json | 2 +- src/hooks/useCopySelectionHelper.ts | 7 +++---- src/hooks/useHtmlPaste/index.ts | 5 ++--- src/libs/OnyxAwareParser.ts | 19 +++++++++++-------- src/libs/ReportUtils.ts | 10 ++++------ src/libs/actions/Report.ts | 5 +++-- .../PrivateNotes/PrivateNotesEditPage.tsx | 7 +++---- src/pages/RoomDescriptionPage.tsx | 5 ++--- .../report/ContextMenu/ContextMenuActions.tsx | 8 +++----- .../ComposerWithSuggestions.tsx | 5 ++--- .../report/ReportActionItemMessageEdit.tsx | 5 ++--- src/pages/tasks/NewTaskDescriptionPage.tsx | 3 ++- src/pages/tasks/NewTaskDetailsPage.tsx | 5 +++-- src/pages/tasks/TaskDescriptionPage.tsx | 5 +++-- .../workspace/WorkspaceInviteMessagePage.tsx | 3 ++- .../WorkspaceProfileDescriptionPage.tsx | 3 ++- src/pages/workspace/taxes/NamePage.tsx | 6 ++---- 18 files changed, 53 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52a975e3a83e..80484c07a6fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", + "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", @@ -20213,8 +20213,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", - "integrity": "sha512-zz0/y0apISP1orxXEQOgn+Uod45O4wVypwwtaqcDPV4dH1tC3i4L98NoLSZvLn7Y17EcceSkfN6QCEsscgFTDQ==", + "resolved": "git+ssh://git@github.com/software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", + "integrity": "sha512-Ns7qkMuJ4SeLj0lrj3i+KqHBzjlym8baDlS7CUIqq2tuNXkgxwO4D+5d6U3ooLOf0CyWb56KaGy5TOTFqpJDZA==", "license": "MIT", "dependencies": { "classnames": "2.5.0", diff --git a/package.json b/package.json index 1cef7d94fcba..50029f0e22c3 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", + "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", diff --git a/src/hooks/useCopySelectionHelper.ts b/src/hooks/useCopySelectionHelper.ts index be7830dc6170..ed379bfcf2e6 100644 --- a/src/hooks/useCopySelectionHelper.ts +++ b/src/hooks/useCopySelectionHelper.ts @@ -1,7 +1,7 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import {useEffect} from 'react'; import Clipboard from '@libs/Clipboard'; import KeyboardShortcut from '@libs/KeyboardShortcut'; +import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser'; import SelectionScraper from '@libs/SelectionScraper'; import CONST from '@src/CONST'; @@ -10,12 +10,11 @@ function copySelectionToClipboard() { if (!selection) { return; } - const parser = new ExpensiMark(); if (!Clipboard.canSetHtml()) { - Clipboard.setString(parser.htmlToMarkdown(selection)); + Clipboard.setString(parseHtmlToMarkdown(selection)); return; } - Clipboard.setHtml(selection, parser.htmlToText(selection)); + Clipboard.setHtml(selection, parseHtmlToText(selection)); } export default function useCopySelectionHelper() { diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index 925a3db518ae..82a52dbb0fef 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -1,6 +1,6 @@ import {useNavigation} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import {useCallback, useEffect} from 'react'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import type UseHtmlPaste from './types'; const insertByCommand = (text: string) => { @@ -62,8 +62,7 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi */ const handlePastedHTML = useCallback( (html: string) => { - const parser = new ExpensiMark(); - paste(parser.htmlToMarkdown(html)); + paste(parseHtmlToMarkdown(html)); }, [paste], ); diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts index 4a8d0045abe8..853f94d7189f 100644 --- a/src/libs/OnyxAwareParser.ts +++ b/src/libs/OnyxAwareParser.ts @@ -1,6 +1,6 @@ -import ONYXKEYS from "@src/ONYXKEYS"; -import ExpensiMark from "expensify-common/lib/ExpensiMark"; -import Onyx from "react-native-onyx"; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; const parser = new ExpensiMark(); @@ -16,7 +16,7 @@ Onyx.connect({ reportIDToNameMap[report.reportID] = report.reportName ?? ''; }, -}) +}); Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, @@ -29,11 +29,14 @@ Onyx.connect({ accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? ''; }); }, -}) - +}); function parseHtmlToMarkdown(html: string): string { return parser.htmlToMarkdown(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); -}; +} + +function parseHtmlToText(html: string): string { + return parser.htmlToText(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); +} -export default parseHtmlToMarkdown; +export {parseHtmlToMarkdown, parseHtmlToText}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a15e1937dbe2..42c14c820f0d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -69,6 +69,7 @@ import ModifiedExpenseMessage from './ModifiedExpenseMessage'; import linkingConfig from './Navigation/linkingConfig'; import Navigation from './Navigation/Navigation'; import * as NumberUtils from './NumberUtils'; +import {parseHtmlToText} from './OnyxAwareParser'; import Permissions from './Permissions'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as PhoneNumber from './PhoneNumber'; @@ -3174,8 +3175,7 @@ function getReportDescriptionText(report: Report): string { return ''; } - const parser = new ExpensiMark(); - return parser.htmlToText(report.description); + return parseHtmlToText(report.description); } function getPolicyDescriptionText(policy: OnyxEntry): string { @@ -3183,12 +3183,10 @@ function getPolicyDescriptionText(policy: OnyxEntry): string { return ''; } - const parser = new ExpensiMark(); - return parser.htmlToText(policy.description); + return parseHtmlToText(policy.description); } function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, actorAccountID?: number): OptimisticReportAction { - const parser = new ExpensiMark(); const commentText = getParsedComment(text ?? ''); const isAttachmentOnly = file && !text; const isTextOnly = text && !file; @@ -3200,7 +3198,7 @@ function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, textForNewComment = CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML; } else if (isTextOnly) { htmlForNewComment = commentText; - textForNewComment = parser.htmlToText(htmlForNewComment); + textForNewComment = parseHtmlToText(htmlForNewComment); } else { htmlForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; textForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index a4afff17d972..c4c140e0a85a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -57,6 +57,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import LocalNotification from '@libs/Notification/LocalNotification'; +import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; @@ -1331,7 +1332,7 @@ function editReportComment(reportID: string, originalReportAction: OnyxEntry ReportActions.getDraftPrivateNote(report.reportID).trim() || parser.htmlToMarkdown(report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '').trim(), + () => ReportActions.getDraftPrivateNote(report.reportID).trim() || parseHtmlToMarkdown(report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '').trim(), ); /** @@ -93,7 +92,7 @@ function PrivateNotesEditPage({route, personalDetailsList, report}: PrivateNotes const originalNote = report?.privateNotes?.[Number(route.params.accountID)]?.note ?? ''; let editedNote = ''; if (privateNote.trim() !== originalNote.trim()) { - editedNote = ReportActions.handleUserDeletedLinksInHtml(privateNote.trim(), parser.htmlToMarkdown(originalNote).trim()); + editedNote = ReportActions.handleUserDeletedLinksInHtml(privateNote.trim(), parseHtmlToMarkdown(originalNote).trim()); ReportActions.updatePrivateNotes(report.reportID, Number(route.params.accountID), editedNote); } diff --git a/src/pages/RoomDescriptionPage.tsx b/src/pages/RoomDescriptionPage.tsx index 9d26de1da9f1..4641ed8c1c4f 100644 --- a/src/pages/RoomDescriptionPage.tsx +++ b/src/pages/RoomDescriptionPage.tsx @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection} from 'react-native-onyx'; @@ -13,6 +12,7 @@ import TextInput from '@components/TextInput'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as ReportUtils from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import * as Report from '@userActions/Report'; @@ -31,8 +31,7 @@ type RoomDescriptionPageProps = { function RoomDescriptionPage({report, policies}: RoomDescriptionPageProps) { const styles = useThemeStyles(); - const parser = new ExpensiMark(); - const [description, setDescription] = useState(() => parser.htmlToMarkdown(report?.description ?? '')); + const [description, setDescription] = useState(() => parseHtmlToMarkdown(report?.description ?? '')); const reportDescriptionInputRef = useRef(null); const focusTimeoutRef = useRef | null>(null); const {translate} = useLocalize(); diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index bd3bedb2a83c..db096e26cd30 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -1,4 +1,3 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import type {MutableRefObject} from 'react'; import React from 'react'; @@ -19,6 +18,7 @@ import getAttachmentDetails from '@libs/fileDownload/getAttachmentDetails'; import * as Localize from '@libs/Localize'; import ModifiedExpenseMessage from '@libs/ModifiedExpenseMessage'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -30,7 +30,6 @@ import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type {Beta, ReportAction, ReportActionReactions, Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; -import parseHtmlToMarkdown from '@libs/OnyxAwareParser'; import type {ContextMenuAnchor} from './ReportActionContextMenu'; import {hideContextMenu, showDeleteModal} from './ReportActionContextMenu'; @@ -42,11 +41,10 @@ function getActionHtml(reportAction: OnyxEntry): string { /** Sets the HTML string to Clipboard */ function setClipboardMessage(content: string) { - const parser = new ExpensiMark(); if (!Clipboard.canSetHtml()) { - Clipboard.setString(parser.htmlToMarkdown(content)); + Clipboard.setString(parseHtmlToMarkdown(content)); } else { - const plainText = parser.htmlToText(content); + const plainText = parseHtmlToText(content); Clipboard.setHtml(content, plainText); } } diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 8f42da5a1575..6fc09fb1b15b 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -1,5 +1,4 @@ import {useIsFocused, useNavigation} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef, MutableRefObject, RefAttributes, RefObject} from 'react'; import React, {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; @@ -35,6 +34,7 @@ import * as EmojiUtils from '@libs/EmojiUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; import getPlatform from '@libs/getPlatform'; import * as KeyDownListener from '@libs/KeyboardShortcut/KeyDownPressListener'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -536,8 +536,7 @@ function ComposerWithSuggestions( ) { event.preventDefault(); if (lastReportAction) { - const parser = new ExpensiMark(); - Report.saveReportActionDraft(reportID, lastReportAction, parser.htmlToMarkdown(lastReportAction.message?.at(-1)?.html ?? '')); + Report.saveReportActionDraft(reportID, lastReportAction, parseHtmlToMarkdown(lastReportAction.message?.at(-1)?.html ?? '')); } } }, diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index fc3c92434fc4..cc1d2789a09a 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -1,4 +1,3 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashDebounce from 'lodash/debounce'; import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; @@ -28,6 +27,7 @@ import * as EmojiUtils from '@libs/EmojiUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; import type {Selection} from '@libs/focusComposerWithDelay/types'; import focusEditAfterCancelDelete from '@libs/focusEditAfterCancelDelete'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import onyxSubscribe from '@libs/onyxSubscribe'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; @@ -117,8 +117,7 @@ function ReportActionItemMessageEdit( const isCommentPendingSaved = useRef(false); useEffect(() => { - const parser = new ExpensiMark(); - const originalMessage = parser.htmlToMarkdown(action.message?.[0]?.html ?? ''); + const originalMessage = parseHtmlToMarkdown(action.message?.[0]?.html ?? ''); if ( ReportActionsUtils.isDeletedAction(action) || Boolean(action.message && draftMessage === originalMessage) || diff --git a/src/pages/tasks/NewTaskDescriptionPage.tsx b/src/pages/tasks/NewTaskDescriptionPage.tsx index e33241c52f4a..4bca46213671 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.tsx +++ b/src/pages/tasks/NewTaskDescriptionPage.tsx @@ -16,6 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {NewTaskNavigatorParamList} from '@libs/Navigation/types'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import * as TaskActions from '@userActions/Task'; import CONST from '@src/CONST'; @@ -77,7 +78,7 @@ function NewTaskDescriptionPage({task}: NewTaskDescriptionPageProps) { { setTaskTitle(task?.title ?? ''); - setTaskDescription(parser.htmlToMarkdown(parser.replace(task?.description ?? ''))); + setTaskDescription(parseHtmlToMarkdown(parser.replace(task?.description ?? ''))); }, [task]); const validate = (values: FormOnyxValues): FormInputErrors => { @@ -132,7 +133,7 @@ function NewTaskDetailsPage({task}: NewTaskDetailsPageProps) { autoGrowHeight shouldSubmitForm containerStyles={styles.autoGrowHeightMultilineInput} - defaultValue={parser.htmlToMarkdown(parser.replace(taskDescription))} + defaultValue={parseHtmlToMarkdown(parser.replace(taskDescription))} value={taskDescription} onValueChange={setTaskDescription} isMarkdownEnabled diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index 9abe15a5bb80..63bff1073698 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -16,6 +16,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as ReportUtils from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; @@ -46,7 +47,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti const submit = useCallback( (values: FormOnyxValues) => { - if (parser.htmlToMarkdown(parser.replace(values.description)) !== parser.htmlToMarkdown(parser.replace(report?.description ?? '')) && !isEmptyObject(report)) { + if (parseHtmlToMarkdown(parser.replace(values.description)) !== parseHtmlToMarkdown(parser.replace(report?.description ?? '')) && !isEmptyObject(report)) { // Set the description of the report in the store and then call EditTask API // to update the description of the report on the server Task.editTask(report, {description: values.description}); @@ -109,7 +110,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti name={INPUT_IDS.DESCRIPTION} label={translate('newTaskPage.descriptionOptional')} accessibilityLabel={translate('newTaskPage.descriptionOptional')} - defaultValue={parser.htmlToMarkdown((report && parser.replace(report?.description ?? '')) || '')} + defaultValue={parseHtmlToMarkdown((report && parser.replace(report?.description ?? '')) || '')} ref={(element: AnimatedTextInputRef) => { if (!element) { return; diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index 30d66662b996..56e2d5a2c257 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -23,6 +23,7 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; @@ -93,7 +94,7 @@ function WorkspaceInviteMessagePage({ useEffect(() => { if (!isEmptyObject(invitedEmailsToAccountIDsDraft)) { - setWelcomeNote(parser.htmlToMarkdown(getDefaultWelcomeNote())); + setWelcomeNote(parseHtmlToMarkdown(getDefaultWelcomeNote())); return; } Navigation.goBack(ROUTES.WORKSPACE_INVITE.getRoute(route.params.policyID), true); diff --git a/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx b/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx index 8086f2414e42..52066d8313d6 100644 --- a/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx +++ b/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx @@ -12,6 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -27,7 +28,7 @@ function WorkspaceProfileDescriptionPage({policy}: Props) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [description, setDescription] = useState(() => - parser.htmlToMarkdown( + parseHtmlToMarkdown( // policy?.description can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing policy?.description || diff --git a/src/pages/workspace/taxes/NamePage.tsx b/src/pages/workspace/taxes/NamePage.tsx index 5c77295a9664..3e5902b140ac 100644 --- a/src/pages/workspace/taxes/NamePage.tsx +++ b/src/pages/workspace/taxes/NamePage.tsx @@ -1,5 +1,4 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useState} from 'react'; import {View} from 'react-native'; import FormProvider from '@components/Form/FormProvider'; @@ -14,6 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {renamePolicyTax, validateTaxName} from '@libs/actions/TaxRate'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as PolicyUtils from '@libs/PolicyUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; @@ -29,8 +29,6 @@ import INPUT_IDS from '@src/types/form/WorkspaceTaxNameForm'; type NamePageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps; -const parser = new ExpensiMark(); - function NamePage({ route: { params: {policyID, taxID}, @@ -42,7 +40,7 @@ function NamePage({ const currentTaxRate = PolicyUtils.getTaxByID(policy, taxID); const {inputCallbackRef} = useAutoFocusInput(); - const [name, setName] = useState(() => parser.htmlToMarkdown(currentTaxRate?.name ?? '')); + const [name, setName] = useState(() => parseHtmlToMarkdown(currentTaxRate?.name ?? '')); const goBack = useCallback(() => Navigation.goBack(ROUTES.WORKSPACE_TAX_EDIT.getRoute(policyID ?? '', taxID)), [policyID, taxID]); From baf2a8c18cd41f58b7fdd098a48b53d022e074fa Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 19 Apr 2024 15:21:18 +0200 Subject: [PATCH 003/228] add fallback when displayName does not exist --- src/libs/OnyxAwareParser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts index 853f94d7189f..586b914216be 100644 --- a/src/libs/OnyxAwareParser.ts +++ b/src/libs/OnyxAwareParser.ts @@ -14,7 +14,7 @@ Onyx.connect({ return; } - reportIDToNameMap[report.reportID] = report.reportName ?? ''; + reportIDToNameMap[report.reportID] = report.reportName ?? report.displayName ?? report.reportID; }, }); @@ -26,7 +26,7 @@ Onyx.connect({ return; } - accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? ''; + accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? String(personalDetails.accountID); }); }, }); From 63a70982f9293675e1ce66070b9f1744b03d41b7 Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 19 Apr 2024 15:25:32 +0200 Subject: [PATCH 004/228] bring back removed trim --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index c4c140e0a85a..734078078126 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1332,7 +1332,7 @@ function editReportComment(reportID: string, originalReportAction: OnyxEntry Date: Tue, 23 Apr 2024 13:00:15 +0200 Subject: [PATCH 005/228] update expensify-common --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c30f6c1a74c..17bb60a22ef1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#9a68635cdcef4c81593c0f816a007bc9c707d46a", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", @@ -20202,8 +20202,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", - "integrity": "sha512-Ns7qkMuJ4SeLj0lrj3i+KqHBzjlym8baDlS7CUIqq2tuNXkgxwO4D+5d6U3ooLOf0CyWb56KaGy5TOTFqpJDZA==", + "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#9a68635cdcef4c81593c0f816a007bc9c707d46a", + "integrity": "sha512-9BHjM3kZs7/dil0oykEQFkEhXjVD5liTttmO7ZYtPZkl4j6g97mubY2p9lYpWwpkWckUfvU7nGuZQjahw9xSFA==", "license": "MIT", "dependencies": { "classnames": "2.5.0", diff --git a/package.json b/package.json index 571b1638e266..39c6132711cc 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com:software-mansion-labs/expensify-common.git#7596ae07c9cd5a6b5265897034898b4526d681dc", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#9a68635cdcef4c81593c0f816a007bc9c707d46a", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", From 46a1f6e72cd3b3b069cdb1fcd72e845c63ee3716 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 29 Apr 2024 14:04:57 +0200 Subject: [PATCH 006/228] fix failing typecheck --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cb9f449c14f9..ad683903fd5c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3324,7 +3324,7 @@ function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, textForNewComment = parseHtmlToText(htmlForNewComment); } else { htmlForNewComment = `${commentText}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; - textForNewComment = `${parser.htmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; + textForNewComment = `${parseHtmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`; } const isAttachment = !text && file !== undefined; From 65ae4fc83b29659687fbe6dc35cbcac4146289d0 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 8 May 2024 18:46:01 +0800 Subject: [PATCH 007/228] set the initial value of the status bar --- web/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/index.html b/web/index.html index 115803573bbd..013d87ec64a4 100644 --- a/web/index.html +++ b/web/index.html @@ -4,7 +4,7 @@ New Expensify - + From 4400aca0b2948f8a2945e16335975152851df5b6 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 10 May 2024 12:13:24 +0200 Subject: [PATCH 008/228] Removing compose.ts --- src/components/LocaleContextProvider.tsx | 12 +++---- src/components/MapView/MapView.tsx | 8 ++--- src/components/TestToolMenu.tsx | 8 ++--- .../withReportAndReportActionOrNotFound.tsx | 8 ++--- .../WorkspaceRateAndUnitPage/InitialPage.tsx | 31 +++++++++---------- .../WorkspaceRateAndUnitPage/RatePage.tsx | 12 +++---- .../WorkspaceRateAndUnitPage/UnitPage.tsx | 13 +++----- .../withPolicyAndFullscreenLoading.tsx | 8 ++--- 8 files changed, 42 insertions(+), 58 deletions(-) diff --git a/src/components/LocaleContextProvider.tsx b/src/components/LocaleContextProvider.tsx index eb7d9324d2ab..e0e30d14d2a2 100644 --- a/src/components/LocaleContextProvider.tsx +++ b/src/components/LocaleContextProvider.tsx @@ -2,7 +2,6 @@ import React, {createContext, useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import compose from '@libs/compose'; import DateUtils from '@libs/DateUtils'; import * as LocaleDigitUtils from '@libs/LocaleDigitUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; @@ -125,18 +124,17 @@ function LocaleContextProvider({preferredLocale, currentUserPersonalDetails = {} return {children}; } -const Provider = compose( +const Provider = withCurrentUserPersonalDetails( withOnyx({ preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, selector: (preferredLocale) => preferredLocale, }, - }), - withCurrentUserPersonalDetails, -)(LocaleContextProvider); + })(LocaleContextProvider), +); Provider.displayName = 'withOnyx(LocaleContextProvider)'; -export {Provider as LocaleContextProvider, LocaleContext}; +export {LocaleContext, Provider as LocaleContextProvider}; -export type {LocaleContextProps, Locale}; +export type {Locale, LocaleContextProps}; diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index b1021350435c..05a7cd221c1e 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -6,7 +6,6 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import useThemeStyles from '@hooks/useThemeStyles'; import setUserLocation from '@libs/actions/UserLocation'; -import compose from '@libs/compose'; import getCurrentPosition from '@libs/getCurrentPosition'; import CONST from '@src/CONST'; import useLocalize from '@src/hooks/useLocalize'; @@ -196,11 +195,10 @@ const MapView = forwardRef( }, ); -export default compose( +export default memo( withOnyx({ userLocation: { key: ONYXKEYS.USER_LOCATION, }, - }), - memo, -)(MapView); + })(MapView), +); diff --git a/src/components/TestToolMenu.tsx b/src/components/TestToolMenu.tsx index e4d33957f7f1..85a2a975fc76 100644 --- a/src/components/TestToolMenu.tsx +++ b/src/components/TestToolMenu.tsx @@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ApiUtils from '@libs/ApiUtils'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as Network from '@userActions/Network'; import * as Session from '@userActions/Session'; @@ -109,11 +108,10 @@ function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) { TestToolMenu.displayName = 'TestToolMenu'; -export default compose( +export default withNetwork()( withOnyx({ user: { key: ONYXKEYS.USER, }, - }), - withNetwork(), -)(TestToolMenu); + })(TestToolMenu), +); diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index e5e203fb5030..123f7b12cb22 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -7,7 +7,6 @@ import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import withWindowDimensions from '@components/withWindowDimensions'; import type {WindowDimensionsProps} from '@components/withWindowDimensions/types'; -import compose from '@libs/compose'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import type {FlagCommentNavigatorParamList, SplitDetailsNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; @@ -103,7 +102,7 @@ export default function , OnyxProps>({ report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, @@ -138,9 +137,8 @@ export default function ({ - // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM - reimbursementAccount: { - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - }, - workspaceRateAndUnit: { - key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, - }, - }), - withPolicy, - withNetwork(), -)(WorkspaceRateAndUnitPage); +export default withNetwork()( + withPolicy( + withOnyx({ + // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM + reimbursementAccount: { + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + }, + workspaceRateAndUnit: { + key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, + }, + })(WorkspaceRateAndUnitPage), + ), +); diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx index 8685cd3b1aee..e265e261b6b8 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx @@ -1,17 +1,16 @@ import React, {useCallback, useEffect, useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import AmountForm from '@components/AmountForm'; import FormProvider from '@components/Form/FormProvider'; import InputWrapperWithRef from '@components/Form/InputWrapper'; import type {FormOnyxValues} from '@components/Form/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import {validateRateValue} from '@libs/PolicyDistanceRatesUtils'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -99,11 +98,10 @@ function WorkspaceRatePage(props: WorkspaceRatePageProps) { WorkspaceRatePage.displayName = 'WorkspaceRatePage'; -export default compose( +export default withPolicy( withOnyx({ workspaceRateAndUnit: { key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, }, - }), - withPolicy, -)(WorkspaceRatePage); + })(WorkspaceRatePage), +); diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx index 1d30c068e30d..6337fa2c461e 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx @@ -1,15 +1,14 @@ import React, {useEffect, useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import Text from '@components/Text'; import type {UnitItemType} from '@components/UnitPicker'; import UnitPicker from '@components/UnitPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; -import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -70,12 +69,10 @@ function WorkspaceUnitPage(props: WorkspaceUnitPageProps) { } WorkspaceUnitPage.displayName = 'WorkspaceUnitPage'; - -export default compose( +export default withPolicy( withOnyx({ workspaceRateAndUnit: { key: ONYXKEYS.WORKSPACE_RATE_AND_UNIT, }, - }), - withPolicy, -)(WorkspaceUnitPage); + })(WorkspaceUnitPage), +); diff --git a/src/pages/workspace/withPolicyAndFullscreenLoading.tsx b/src/pages/workspace/withPolicyAndFullscreenLoading.tsx index 161320441843..2467136a382b 100644 --- a/src/pages/workspace/withPolicyAndFullscreenLoading.tsx +++ b/src/pages/workspace/withPolicyAndFullscreenLoading.tsx @@ -4,7 +4,6 @@ import React, {forwardRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import compose from '@libs/compose'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList} from '@src/types/onyx'; import type {WithPolicyOnyxProps, WithPolicyProps} from './withPolicy'; @@ -49,7 +48,7 @@ export default function withPolicyAndFullscreenLoading, WithPolicyAndFullscreenLoadingOnyxProps>({ isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, @@ -57,9 +56,8 @@ export default function withPolicyAndFullscreenLoading Date: Mon, 13 May 2024 15:33:53 +0200 Subject: [PATCH 009/228] remove leftovers --- src/pages/tasks/TaskDescriptionPage.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index 4a9a7f870230..7b3dd8813540 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -30,8 +29,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; type TaskDescriptionPageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps; -const parser = new ExpensiMark(); - function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescriptionPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -48,7 +45,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti const submit = useCallback( (values: FormOnyxValues) => { - if (values.description !== parseHtmlToMarkdown(parser.replace(report?.description ?? '')) && !isEmptyObject(report)) { + if (values.description !== parseHtmlToMarkdown(report?.description ?? '') && !isEmptyObject(report)) { // Set the description of the report in the store and then call EditTask API // to update the description of the report on the server Task.editTask(report, {description: values.description}); From 7556c6027f6529a49f688b40f98d0a24968f2ff0 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 13 May 2024 16:09:38 +0200 Subject: [PATCH 010/228] removing compose.ts --- src/libs/compose.ts | 71 --------------------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 src/libs/compose.ts diff --git a/src/libs/compose.ts b/src/libs/compose.ts deleted file mode 100644 index dadc586d0f0d..000000000000 --- a/src/libs/compose.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-types */ -/* eslint-disable import/export */ - -/** - * This is a utility function taken directly from Redux. (We don't want to add Redux as a dependency) - * It enables functional composition, useful for the chaining/composition of HOCs. - * - * For example, instead of: - * - * export default hoc1(config1, hoc2(config2, hoc3(config3)))(Component); - * - * Use this instead: - * - * export default compose( - * hoc1(config1), - * hoc2(config2), - * hoc3(config3), - * )(Component) - */ -export default function compose(): (a: R) => R; - -export default function compose(f: F): F; - -/* two functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2; - -/* three functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (...args: A) => R3; - -/* four functions */ -export default function compose(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (...args: A) => R4; - -/* five functions */ -export default function compose( - f1: (...args: A) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, -): (...args: A) => R5; - -/* six functions */ -export default function compose( - f1: (...args: A) => R1, - f2: (a: R1) => R2, - f3: (a: R2) => R3, - f4: (a: R3) => R4, - f5: (a: R4) => R5, - f6: (a: R5) => R6, -): (...args: A) => R6; - -/* rest */ -export default function compose(f1: (a: unknown) => R, ...funcs: Function[]): (...args: unknown[]) => R; - -export default function compose(...funcs: Function[]): Function { - if (funcs.length === 0) { - // infer the argument type so it is usable in inference down the line - return (arg: T) => arg; - } - - if (funcs.length === 1) { - return funcs[0]; - } - - return funcs.reduce( - (a, b) => - (...args: unknown[]) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - a(b(...args)), - ); -} From dafa5a4f22b9412b3f6ce64a69e796a427c3563d Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 14 May 2024 18:11:30 +0200 Subject: [PATCH 011/228] replace htmlToText in `OptionRowLHN` --- src/components/LHNOptionsList/OptionRowLHN.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index b665c305f5cf..fe234eb629a4 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import React, {useCallback, useRef, useState} from 'react'; import type {GestureResponderEvent, ViewStyle} from 'react-native'; import {StyleSheet, View} from 'react-native'; @@ -20,6 +19,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import DateUtils from '@libs/DateUtils'; import DomUtils from '@libs/DomUtils'; +import {parseHtmlToText} from '@libs/OnyxAwareParser'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; @@ -29,8 +29,6 @@ import CONST from '@src/CONST'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {OptionRowLHNProps} from './types'; -const parser = new ExpensiMark(); - function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, optionItem, viewMode = 'default', style, onLayout = () => {}, hasDraftComment}: OptionRowLHNProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -228,7 +226,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti numberOfLines={1} accessibilityLabel={translate('accessibilityHints.lastChatMessagePreview')} > - {parser.htmlToText(optionItem.alternateText)} + {parseHtmlToText(optionItem.alternateText)} ) : null} From 3a2f068f2f0d6ead4a085f695e083265f03c36a9 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Fri, 24 May 2024 01:47:53 +0700 Subject: [PATCH 012/228] ordered mention suggestions --- .../ReportActionCompose/SuggestionMention.tsx | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 05e1163da200..cef1c0990c59 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -1,7 +1,8 @@ import Str from 'expensify-common/lib/str'; +import lodashMapValues from 'lodash/mapValues'; import lodashSortBy from 'lodash/sortBy'; import type {ForwardedRef} from 'react'; -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {useOnyx} from 'react-native-onyx'; import type {OnyxCollection} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -9,11 +10,13 @@ import type {Mention} from '@components/MentionSuggestions'; import MentionSuggestions from '@components/MentionSuggestions'; import {usePersonalDetails} from '@components/OnyxProvider'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; +import useCurrentReportID from '@hooks/useCurrentReportID'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebounce from '@hooks/useDebounce'; import useLocalize from '@hooks/useLocalize'; import * as LoginUtils from '@libs/LoginUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; +import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import * as UserUtils from '@libs/UserUtils'; @@ -21,7 +24,7 @@ import {isValidRoomName} from '@libs/ValidationUtils'; import * as ReportUserActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetailsList, Report} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, Report} from '@src/types/onyx'; import type {SuggestionsRef} from './ReportActionCompose'; import type {SuggestionProps} from './Suggestions'; @@ -46,6 +49,14 @@ const defaultSuggestionsValues: SuggestionValues = { prefixType: '', }; +type SuggestionPersonalDetailsList = Record< + string, + | (PersonalDetails & { + weight: number; + }) + | null +>; + function SuggestionMention( {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused, isGroupPolicyReport, policyID}: SuggestionProps, ref: ForwardedRef, @@ -59,6 +70,36 @@ function SuggestionMention( const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const isMentionSuggestionsMenuVisible = !!suggestionValues.suggestedMentions.length && suggestionValues.shouldShowSuggestionMenu; + const currentReportID = useCurrentReportID(); + const currentReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${currentReportID?.currentReportID}`] ?? null; + // Smaller weight means higher order in suggestion list + const getPersonalDetailsWeight = useCallback( + (detail: PersonalDetails, policyEmployeeAccountIDs: number[]): number => { + if (ReportUtils.isReportParticipant(detail.accountID, currentReport)) { + return 0; + } + if (policyEmployeeAccountIDs.includes(detail.accountID)) { + return 1; + } + return 2; + }, + [currentReport], + ); + const weightedPersonalDetails: PersonalDetailsList | SuggestionPersonalDetailsList = useMemo(() => { + const policyEmployeeAccountIDs = getPolicyEmployeeAccountIDs(policyID); + if (!ReportUtils.isGroupChat(currentReport) && !ReportUtils.doesReportBelongToWorkspace(currentReport, policyEmployeeAccountIDs, policyID)) { + return personalDetails; + } + return lodashMapValues(personalDetails, (detail) => + detail + ? { + ...detail, + weight: getPersonalDetailsWeight(detail, policyEmployeeAccountIDs), + } + : null, + ); + }, [policyID, currentReport, personalDetails, getPersonalDetailsWeight]); + const [highlightedMentionIndex, setHighlightedMentionIndex] = useArrowKeyFocusManager({ isActive: isMentionSuggestionsMenuVisible, maxIndex: suggestionValues.suggestedMentions.length - 1, @@ -191,7 +232,7 @@ function SuggestionMention( ); const getUserMentionOptions = useCallback( - (personalDetailsParam: PersonalDetailsList, searchValue = ''): Mention[] => { + (personalDetailsParam: PersonalDetailsList | SuggestionPersonalDetailsList, searchValue = ''): Mention[] => { const suggestions = []; if (CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT.includes(searchValue.toLowerCase())) { @@ -232,8 +273,7 @@ function SuggestionMention( return true; }); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing cannot be used if left side can be empty string - const sortedPersonalDetails = lodashSortBy(filteredPersonalDetails, (detail) => detail?.displayName || detail?.login); + const sortedPersonalDetails = lodashSortBy(filteredPersonalDetails, ['weight', 'displayName', 'login']); sortedPersonalDetails.slice(0, CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS - suggestions.length).forEach((detail) => { suggestions.push({ text: formatLoginPrivateDomain(PersonalDetailsUtils.getDisplayNameOrDefault(detail), detail?.login), @@ -320,7 +360,7 @@ function SuggestionMention( }; if (isMentionCode(suggestionWord) && prefixType === '@') { - const suggestions = getUserMentionOptions(personalDetails, prefix); + const suggestions = getUserMentionOptions(weightedPersonalDetails, prefix); nextState.suggestedMentions = suggestions; nextState.shouldShowSuggestionMenu = !!suggestions.length; } @@ -340,7 +380,7 @@ function SuggestionMention( })); setHighlightedMentionIndex(0); }, - [isComposerFocused, value, isGroupPolicyReport, setHighlightedMentionIndex, resetSuggestions, getUserMentionOptions, personalDetails, getRoomMentionOptions, reports], + [isComposerFocused, value, isGroupPolicyReport, setHighlightedMentionIndex, resetSuggestions, getUserMentionOptions, weightedPersonalDetails, getRoomMentionOptions, reports], ); useEffect(() => { From ce9d3895206bb74f6ebf8f750fc580f8820c19c1 Mon Sep 17 00:00:00 2001 From: dominictb Date: Mon, 27 May 2024 09:42:19 +0700 Subject: [PATCH 013/228] fix: keep the Android keyboard visible when pasting in Composer component env: Android Chrome Signed-off-by: dominictb --- src/components/Composer/index.tsx | 2 +- src/hooks/useHtmlPaste/index.ts | 11 +++++++---- src/hooks/useHtmlPaste/types.ts | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index f7bf277050a2..a7635db58472 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -250,7 +250,7 @@ function Composer( // eslint-disable-next-line react-hooks/exhaustive-deps }, [isComposerFullSize]); - useHtmlPaste(textInput, handlePaste, true); + useHtmlPaste(textInput, handlePaste, true, false); useEffect(() => { if (typeof ref === 'function') { diff --git a/src/hooks/useHtmlPaste/index.ts b/src/hooks/useHtmlPaste/index.ts index 925a3db518ae..b6ee5ab122fc 100644 --- a/src/hooks/useHtmlPaste/index.ts +++ b/src/hooks/useHtmlPaste/index.ts @@ -18,8 +18,7 @@ const insertAtCaret = (target: HTMLElement, text: string) => { // Move caret to the end of the newly inserted text node. range.setStart(node, node.length); range.setEnd(node, node.length); - selection.removeAllRanges(); - selection.addRange(range); + selection.setBaseAndExtent(range.startContainer, range.startOffset, range.endContainer, range.endOffset); // Dispatch paste event to simulate real browser behavior target.dispatchEvent(new Event('paste', {bubbles: true})); @@ -30,7 +29,7 @@ const insertAtCaret = (target: HTMLElement, text: string) => { } }; -const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeListenerOnScreenBlur = false) => { +const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeListenerOnScreenBlur = false, shouldRefocusAfterPaste = true) => { const navigation = useNavigation(); /** @@ -47,7 +46,11 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi } // Pointer will go out of sight when a large paragraph is pasted on the web. Refocusing the input keeps the cursor in view. - textInputRef.current?.blur(); + // If shouldRefocusAfterPaste = false, we want to call `focus()` only as it won't change the focus state of the input since it is already focused + if (shouldRefocusAfterPaste) { + textInputRef.current?.blur(); + } + textInputRef.current?.focus(); // eslint-disable-next-line no-empty } catch (e) {} diff --git a/src/hooks/useHtmlPaste/types.ts b/src/hooks/useHtmlPaste/types.ts index 305ebe5fbd0f..f2dd41eb2488 100644 --- a/src/hooks/useHtmlPaste/types.ts +++ b/src/hooks/useHtmlPaste/types.ts @@ -5,6 +5,7 @@ type UseHtmlPaste = ( textInputRef: MutableRefObject<(HTMLTextAreaElement & TextInput) | TextInput | null>, preHtmlPasteCallback?: (event: ClipboardEvent) => boolean, removeListenerOnScreenBlur?: boolean, + shouldRefocusAfterPaste?: boolean, ) => void; export default UseHtmlPaste; From 6f730812c3d81f109873ef037c9f81a895ac3a6e Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 28 May 2024 16:12:07 +0200 Subject: [PATCH 014/228] Reversing dependencies order --- src/components/MapView/MapView.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 05a7cd221c1e..fa885160d881 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -195,10 +195,8 @@ const MapView = forwardRef( }, ); -export default memo( - withOnyx({ - userLocation: { - key: ONYXKEYS.USER_LOCATION, - }, - })(MapView), -); +export default withOnyx({ + userLocation: { + key: ONYXKEYS.USER_LOCATION, + }, +})(memo(MapView)); From 3539dbd0dfa689b16296d75466b96f8e5c013545 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 28 May 2024 23:23:23 +0800 Subject: [PATCH 015/228] update splashBG color --- src/libs/getSplashBackgroundColor/index.native.ts | 7 ------- src/libs/getSplashBackgroundColor/index.ts | 7 ------- src/styles/theme/themes/dark.ts | 3 +-- src/styles/theme/themes/light.ts | 3 +-- 4 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 src/libs/getSplashBackgroundColor/index.native.ts delete mode 100644 src/libs/getSplashBackgroundColor/index.ts diff --git a/src/libs/getSplashBackgroundColor/index.native.ts b/src/libs/getSplashBackgroundColor/index.native.ts deleted file mode 100644 index 1d21b7a004e1..000000000000 --- a/src/libs/getSplashBackgroundColor/index.native.ts +++ /dev/null @@ -1,7 +0,0 @@ -import colors from '@styles/theme/colors'; - -function getSplashBackgroundColor() { - return colors.green400; -} - -export default getSplashBackgroundColor; diff --git a/src/libs/getSplashBackgroundColor/index.ts b/src/libs/getSplashBackgroundColor/index.ts deleted file mode 100644 index 97484d49163e..000000000000 --- a/src/libs/getSplashBackgroundColor/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import colors from '@styles/theme/colors'; - -function getSplashBackgroundColor() { - return colors.productDark100; -} - -export default getSplashBackgroundColor; diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index f0493a815747..73d7fa969214 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -1,4 +1,3 @@ -import getSplashBackgroundColor from '@libs/getSplashBackgroundColor'; import colors from '@styles/theme/colors'; import type {ThemeColors} from '@styles/theme/types'; import CONST from '@src/CONST'; @@ -7,7 +6,7 @@ import SCREENS from '@src/SCREENS'; const darkTheme = { // Figma keys appBG: colors.productDark100, - splashBG: getSplashBackgroundColor(), + splashBG: colors.green400, highlightBG: colors.productDark200, border: colors.productDark400, borderLighter: colors.productDark400, diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index cf490a90a7f7..16339c70bafe 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -1,4 +1,3 @@ -import getSplashBackgroundColor from '@libs/getSplashBackgroundColor'; import colors from '@styles/theme/colors'; import type {ThemeColors} from '@styles/theme/types'; import CONST from '@src/CONST'; @@ -7,7 +6,7 @@ import SCREENS from '@src/SCREENS'; const lightTheme = { // Figma keys appBG: colors.productLight100, - splashBG: getSplashBackgroundColor(), + splashBG: colors.green400, highlightBG: colors.productLight200, border: colors.productLight400, borderLighter: colors.productLight400, From 21f55c7be031f3b2024482e5a2f895cda2a08dd7 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 28 May 2024 23:23:36 +0800 Subject: [PATCH 016/228] don't update status bar color on mount --- src/components/CustomStatusBarAndBackground/index.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/CustomStatusBarAndBackground/index.tsx b/src/components/CustomStatusBarAndBackground/index.tsx index 524c8a3903e0..356fbd3726a3 100644 --- a/src/components/CustomStatusBarAndBackground/index.tsx +++ b/src/components/CustomStatusBarAndBackground/index.tsx @@ -114,11 +114,6 @@ function CustomStatusBarAndBackground({isNested = false}: CustomStatusBarAndBack [prevIsRootStatusBarEnabled, isRootStatusBarEnabled, statusBarAnimation, statusBarStyle, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle], ); - useEffect(() => { - updateStatusBarAppearance({backgroundColor: theme.appBG}); - // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this to run on first render - }, []); - useEffect(() => { didForceUpdateStatusBarRef.current = false; }, [isRootStatusBarEnabled]); From ef67dad60c342a14c2c4eee89ddb8b02195bb83c Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Thu, 30 May 2024 02:12:46 +0530 Subject: [PATCH 017/228] Align inline icons with text in Android LHN --- .../LHNOptionsList/LHNOptionsList.tsx | 60 ++++++++++++------- src/styles/index.ts | 8 +++ 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 6405e3026b1a..d04b40002302 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -69,36 +69,52 @@ function LHNOptionsList({ const emptyLHNSubtitle = useMemo( () => ( - + {translate('common.emptyLHN.subtitleText1')} - + + + {translate('common.emptyLHN.subtitleText2')} - + + + {translate('common.emptyLHN.subtitleText3')} ), - [theme, styles.alignItemsCenter, styles.textAlignCenter, translate], + [styles.emptyLHNDetailsContainer, styles.mh1, styles.textAlignCenter, theme.icon, theme.placeholderText, translate], ); /** diff --git a/src/styles/index.ts b/src/styles/index.ts index 5627adb01834..0d1bac489fa0 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4939,6 +4939,14 @@ const styles = (theme: ThemeColors) => reportListItemActionButtonMargin: { marginLeft: variables.searchTypeColumnWidth, }, + + emptyLHNDetailsContainer: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + flexWrap: 'wrap', + textAlign: 'center', + }, } satisfies Styles); type ThemeStyles = ReturnType; From f4659e14d9db2ff2af079655346c220bbe471d51 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 30 May 2024 04:16:23 +0700 Subject: [PATCH 018/228] fix LHN - Thread chat with violation is not removed from LHN --- src/libs/ReportUtils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a6fe52f4c7b2..6c9259ec6939 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5265,7 +5265,10 @@ function shouldReportBeInOptionList({ if (isSelfDM(report)) { return includeSelfDM; } - + const parentReportAction = ReportActionsUtils.getParentReportAction(report); + if (parentReportAction && ReportActionsUtils.isPendingRemove(parentReportAction) && ReportActionsUtils.isThreadParentMessage(parentReportAction, report?.reportID ?? '')) { + return false; + } return true; } From 715d9f37752dcc3e5a37835f7737f5d3ed2ea15a Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 30 May 2024 04:26:01 +0700 Subject: [PATCH 019/228] fix lint --- src/libs/ReportUtils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6c9259ec6939..96a50d1ead89 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5266,7 +5266,12 @@ function shouldReportBeInOptionList({ return includeSelfDM; } const parentReportAction = ReportActionsUtils.getParentReportAction(report); - if (parentReportAction && ReportActionsUtils.isPendingRemove(parentReportAction) && ReportActionsUtils.isThreadParentMessage(parentReportAction, report?.reportID ?? '')) { + if ( + parentReportAction && + !isEmptyObject(parentReportAction) && + ReportActionsUtils.isPendingRemove(parentReportAction) && + ReportActionsUtils.isThreadParentMessage(parentReportAction, report?.reportID ?? '') + ) { return false; } return true; From 81f47de231fcd02b1dd3dc02809db7440ca9d0d8 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Thu, 30 May 2024 20:32:12 +0530 Subject: [PATCH 020/228] Update --- src/styles/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 0d1bac489fa0..900954b744c4 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4941,10 +4941,10 @@ const styles = (theme: ThemeColors) => }, emptyLHNDetailsContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - flexWrap: 'wrap', + ...flex.flexRow, + ...flex.alignItemsCenter, + ...flex.justifyContentCenter, + ...flex.flexWrap, textAlign: 'center', }, } satisfies Styles); From b37cc7042c25ff2e9bbbee550875b5f7c763e5d9 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 31 May 2024 12:47:50 +0530 Subject: [PATCH 021/228] Update --- .../LHNOptionsList/LHNOptionsList.tsx | 20 +++++++------------ src/styles/index.ts | 8 -------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index d04b40002302..5dd606897c40 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -69,12 +69,10 @@ function LHNOptionsList({ const emptyLHNSubtitle = useMemo( () => ( - + {translate('common.emptyLHN.subtitleText1')} @@ -88,10 +86,8 @@ function LHNOptionsList({ additionalStyles={styles.mh1} /> {translate('common.emptyLHN.subtitleText2')} @@ -105,10 +101,8 @@ function LHNOptionsList({ additionalStyles={styles.mh1} /> {translate('common.emptyLHN.subtitleText3')} diff --git a/src/styles/index.ts b/src/styles/index.ts index 900954b744c4..5627adb01834 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4939,14 +4939,6 @@ const styles = (theme: ThemeColors) => reportListItemActionButtonMargin: { marginLeft: variables.searchTypeColumnWidth, }, - - emptyLHNDetailsContainer: { - ...flex.flexRow, - ...flex.alignItemsCenter, - ...flex.justifyContentCenter, - ...flex.flexWrap, - textAlign: 'center', - }, } satisfies Styles); type ThemeStyles = ReturnType; From 49006c9d06266db51d1586ae58a3cbff2c44650b Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 31 May 2024 16:18:41 +0530 Subject: [PATCH 022/228] Update --- src/components/LHNOptionsList/LHNOptionsList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 5dd606897c40..3f3c97fec09d 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -108,7 +108,7 @@ function LHNOptionsList({ ), - [styles.emptyLHNDetailsContainer, styles.mh1, styles.textAlignCenter, theme.icon, theme.placeholderText, translate], + [styles.alignItemsCenter, styles.flexRow, styles.justifyContentCenter, styles.flexWrap, styles.textAlignCenter, styles.mh1, theme.icon, theme.placeholderText, translate], ); /** From 1bd5fa13e18dbf57f77215bb4d40409fb7d21d1d Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:34:16 +0530 Subject: [PATCH 023/228] Update --- .../LHNOptionsList/LHNOptionsList.tsx | 31 ++++++-------- src/components/TextBlock.tsx | 40 +++++++++++++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 src/components/TextBlock.tsx diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 3f3c97fec09d..edd037bd7d68 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -10,7 +10,6 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import LottieAnimations from '@components/LottieAnimations'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; -import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import usePrevious from '@hooks/usePrevious'; @@ -24,6 +23,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import OptionRowLHNData from './OptionRowLHNData'; import type {LHNOptionsListOnyxProps, LHNOptionsListProps, RenderItemProps} from './types'; +import TextBlock from "@components/TextBlock"; const keyExtractor = (item: string) => `report_${item}`; @@ -70,42 +70,37 @@ function LHNOptionsList({ const emptyLHNSubtitle = useMemo( () => ( - - {translate('common.emptyLHN.subtitleText1')} - + textStyles={[styles.textAlignCenter, styles.textNormal]} + text={translate('common.emptyLHN.subtitleText1')} + /> - - {translate('common.emptyLHN.subtitleText2')} - + textStyles={[styles.textAlignCenter, styles.textNormal]} + text={translate('common.emptyLHN.subtitleText2')} + /> - - {translate('common.emptyLHN.subtitleText3')} - + textStyles={[styles.textAlignCenter, styles.textNormal]} + text={translate('common.emptyLHN.subtitleText3')} + /> ), [styles.alignItemsCenter, styles.flexRow, styles.justifyContentCenter, styles.flexWrap, styles.textAlignCenter, styles.mh1, theme.icon, theme.placeholderText, translate], diff --git a/src/components/TextBlock.tsx b/src/components/TextBlock.tsx new file mode 100644 index 000000000000..d9cc99e8a738 --- /dev/null +++ b/src/components/TextBlock.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import type { StyleProp, TextStyle } from 'react-native'; +import { Text } from 'react-native'; +import useTheme from "@hooks/useTheme"; + +type TextWordProps = { + /** The color of the text */ + color?: string; + + /** Styles to apply to each text word */ + textStyles?: StyleProp; + + /** The full text to be split into words */ + text: string; +}; + +function TextWord({ color, textStyles, text }: TextWordProps) { + const theme = useTheme(); + const words = text.split(' '); + + return ( + <> + {words.map((word, index) => ( + + {word} + + ))} + + ); +} + +TextWord.displayName = 'TextWord'; + +export default TextWord; From 33b2b1fe3dfebcf4c53eabb07b71b61da51fc6d9 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:46:11 +0530 Subject: [PATCH 024/228] Lint --- src/components/LHNOptionsList/LHNOptionsList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index edd037bd7d68..80cabb9fb761 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -21,9 +21,9 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import TextBlock from "@components/TextBlock"; import OptionRowLHNData from './OptionRowLHNData'; import type {LHNOptionsListOnyxProps, LHNOptionsListProps, RenderItemProps} from './types'; -import TextBlock from "@components/TextBlock"; const keyExtractor = (item: string) => `report_${item}`; From 7f995841bd44c46289ef61131411931ba506d619 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:21:45 +0530 Subject: [PATCH 025/228] Update --- .../LHNOptionsList/LHNOptionsList.tsx | 5 +--- src/components/TextBlock.tsx | 30 +++++++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 80cabb9fb761..05b7e150fc53 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -10,6 +10,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import LottieAnimations from '@components/LottieAnimations'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; +import TextBlock from '@components/TextBlock'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import usePrevious from '@hooks/usePrevious'; @@ -21,7 +22,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import TextBlock from "@components/TextBlock"; import OptionRowLHNData from './OptionRowLHNData'; import type {LHNOptionsListOnyxProps, LHNOptionsListProps, RenderItemProps} from './types'; @@ -72,7 +72,6 @@ function LHNOptionsList({ diff --git a/src/components/TextBlock.tsx b/src/components/TextBlock.tsx index d9cc99e8a738..0770bd0891cc 100644 --- a/src/components/TextBlock.tsx +++ b/src/components/TextBlock.tsx @@ -1,9 +1,10 @@ -import React from 'react'; -import type { StyleProp, TextStyle } from 'react-native'; -import { Text } from 'react-native'; -import useTheme from "@hooks/useTheme"; +import React, {memo, useMemo} from 'react'; +import type {StyleProp, TextStyle} from 'react-native'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Text from './Text'; -type TextWordProps = { +type TextBlockProps = { /** The color of the text */ color?: string; @@ -14,19 +15,18 @@ type TextWordProps = { text: string; }; -function TextWord({ color, textStyles, text }: TextWordProps) { +function TextBlock({color, textStyles, text}: TextBlockProps) { const theme = useTheme(); - const words = text.split(' '); + const styles = useThemeStyles(); + + const words = useMemo(() => text.match(/(\S+\s*)/g) ?? [], [text]); return ( <> - {words.map((word, index) => ( + {words.map((word) => ( {word} @@ -35,6 +35,6 @@ function TextWord({ color, textStyles, text }: TextWordProps) { ); } -TextWord.displayName = 'TextWord'; +TextBlock.displayName = 'TextBlock'; -export default TextWord; +export default memo(TextBlock); From 7dae918fa165f583e5818f1dc4fa690bf54b99c1 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 00:07:57 +0700 Subject: [PATCH 026/228] get rid of missing translation FormHelpMessage --- src/components/AddressForm.tsx | 6 ++-- src/components/AmountPicker/types.ts | 3 +- src/components/CheckboxWithLabel.tsx | 3 +- src/components/CountrySelector.tsx | 3 +- src/components/DotIndicatorMessage.tsx | 2 +- src/components/FormAlertWithSubmitButton.tsx | 3 +- src/components/FormAlertWrapper.tsx | 3 +- src/components/FormHelpMessage.tsx | 7 ++-- src/components/MagicCodeInput.tsx | 3 +- src/components/MenuItem.tsx | 5 ++- src/components/MessagesRow.tsx | 4 +-- .../MoneyRequestConfirmationList.tsx | 31 ++++------------ src/components/PDFView/PDFPasswordForm.tsx | 2 +- src/components/Picker/types.ts | 3 +- src/components/RadioButtonWithLabel.tsx | 3 +- src/components/RadioButtons.tsx | 3 +- src/components/SingleChoiceQuestion.tsx | 3 +- src/components/StateSelector.tsx | 3 +- .../TextInput/BaseTextInput/types.ts | 5 ++- src/components/TextPicker/types.ts | 3 +- src/components/TimePicker/TimePicker.tsx | 6 ++-- src/components/ValuePicker/types.ts | 3 +- src/libs/DateUtils.ts | 4 +-- src/libs/ErrorUtils.ts | 21 ++++++----- src/libs/ValidationUtils.ts | 10 +++--- src/libs/actions/TaxRate.ts | 5 +-- src/pages/EditReportFieldDate.tsx | 4 +-- .../EnablePayments/AdditionalDetailsStep.tsx | 14 ++++---- .../FeesAndTerms/substeps/TermsStep.tsx | 2 +- .../PersonalInfo/substeps/DateOfBirthStep.tsx | 28 +++++++-------- src/pages/EnablePayments/TermsStep.tsx | 2 +- .../BaseOnboardingPurpose.tsx | 10 +++--- .../AddressFormFields.tsx | 6 ++-- .../DateOfBirthUBO.tsx | 4 +-- .../substeps/ConfirmationBusiness.tsx | 20 +++++------ .../substeps/IncorporationDateBusiness.tsx | 24 ++++++------- .../substeps/ConfirmAgreements.tsx | 35 +++++++++---------- .../PersonalInfo/substeps/DateOfBirth.tsx | 28 +++++++-------- src/pages/Travel/TravelTerms.tsx | 2 +- src/pages/iou/MoneyRequestAmountForm.tsx | 9 +++-- .../MoneyRequestParticipantsSelector.tsx | 2 +- .../TwoFactorAuth/Steps/CodesStep.tsx | 2 +- .../Wallet/ActivatePhysicalCardPage.tsx | 2 +- .../settings/Wallet/AddDebitCardPage.tsx | 14 ++++---- .../settings/Wallet/ReportCardLostPage.tsx | 4 +-- src/pages/signin/LoginForm/BaseLoginForm.tsx | 11 +++--- .../ValidateCodeForm/BaseValidateCodeForm.tsx | 4 +-- src/pages/tasks/NewTaskPage.tsx | 6 ++-- src/pages/workspace/WorkspaceInvitePage.tsx | 2 +- src/pages/workspace/WorkspaceMembersPage.tsx | 2 +- src/pages/workspace/WorkspaceNewRoomPage.tsx | 10 +++--- src/stories/Form.stories.tsx | 5 ++- src/types/onyx/OnyxCommon.ts | 4 +-- 53 files changed, 181 insertions(+), 222 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index 9ad4643e834a..bd3ad7ea0888 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -96,7 +96,7 @@ function AddressForm({ // Check "State" dropdown is a valid state if selected Country is USA if (values.country === CONST.COUNTRY.US && !values.state) { - errors.state = 'common.error.fieldRequired'; + errors.state = translate('common.error.fieldRequired'); } // Add "Field required" errors if any required field is empty @@ -106,7 +106,7 @@ function AddressForm({ return; } - errors[fieldKey] = 'common.error.fieldRequired'; + errors[fieldKey] = translate('common.error.fieldRequired'); }); // If no country is selected, default value is an empty string and there's no related regex data so we default to an empty object @@ -131,7 +131,7 @@ function AddressForm({ } return errors; - }, []); + }, [translate]); return ( string); /** Form Error description */ - errorText?: MaybePhraseKey; + errorText?: string; /** Callback to call when the input changes */ onInputChange?: (value: string | undefined) => void; diff --git a/src/components/CheckboxWithLabel.tsx b/src/components/CheckboxWithLabel.tsx index dd169576186e..db62aa9e1441 100644 --- a/src/components/CheckboxWithLabel.tsx +++ b/src/components/CheckboxWithLabel.tsx @@ -3,7 +3,6 @@ import React, {useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import variables from '@styles/variables'; import Checkbox from './Checkbox'; import FormHelpMessage from './FormHelpMessage'; @@ -41,7 +40,7 @@ type CheckboxWithLabelProps = RequiredLabelProps & { style?: StyleProp; /** Error text to display */ - errorText?: MaybePhraseKey; + errorText?: string; /** Value for checkbox. This prop is intended to be set by FormProvider only */ value?: boolean; diff --git a/src/components/CountrySelector.tsx b/src/components/CountrySelector.tsx index 002c0c6d4b0a..62fdc85687e1 100644 --- a/src/components/CountrySelector.tsx +++ b/src/components/CountrySelector.tsx @@ -4,7 +4,6 @@ import type {ForwardedRef} from 'react'; import type {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; @@ -13,7 +12,7 @@ import MenuItemWithTopDescription from './MenuItemWithTopDescription'; type CountrySelectorProps = { /** Form error text. e.g when no country is selected */ - errorText?: MaybePhraseKey; + errorText?: string; /** Callback called when the country changes. */ onInputChange?: (value?: string) => void; diff --git a/src/components/DotIndicatorMessage.tsx b/src/components/DotIndicatorMessage.tsx index 3f72bbf429aa..f1aa8243b327 100644 --- a/src/components/DotIndicatorMessage.tsx +++ b/src/components/DotIndicatorMessage.tsx @@ -23,7 +23,7 @@ type DotIndicatorMessageProps = { * timestamp: 'message', * } */ - messages: Record; + messages: Record; /** The type of message, 'error' shows a red dot, 'success' shows a green dot */ type: 'error' | 'success'; diff --git a/src/components/FormAlertWithSubmitButton.tsx b/src/components/FormAlertWithSubmitButton.tsx index 137012478549..cd177a1d77a3 100644 --- a/src/components/FormAlertWithSubmitButton.tsx +++ b/src/components/FormAlertWithSubmitButton.tsx @@ -2,13 +2,12 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import Button from './Button'; import FormAlertWrapper from './FormAlertWrapper'; type FormAlertWithSubmitButtonProps = { /** Error message to display above button */ - message?: MaybePhraseKey; + message?: string; /** Whether the button is disabled */ isDisabled?: boolean; diff --git a/src/components/FormAlertWrapper.tsx b/src/components/FormAlertWrapper.tsx index d8b379208a29..525182070095 100644 --- a/src/components/FormAlertWrapper.tsx +++ b/src/components/FormAlertWrapper.tsx @@ -4,7 +4,6 @@ import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import type Network from '@src/types/onyx/Network'; import FormHelpMessage from './FormHelpMessage'; import {withNetwork} from './OnyxProvider'; @@ -29,7 +28,7 @@ type FormAlertWrapperProps = { isMessageHtml?: boolean; /** Error message to display above button */ - message?: MaybePhraseKey; + message?: string; /** Props to detect online status */ network: Network; diff --git a/src/components/FormHelpMessage.tsx b/src/components/FormHelpMessage.tsx index 4f1d784788bf..01a5a1eaf3a8 100644 --- a/src/components/FormHelpMessage.tsx +++ b/src/components/FormHelpMessage.tsx @@ -4,14 +4,13 @@ import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Localize from '@libs/Localize'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import Text from './Text'; type FormHelpMessageProps = { /** Error or hint text. Ignored when children is not empty */ - message?: Localize.MaybePhraseKey; + message?: string; /** Children to render next to dot indicator */ children?: React.ReactNode; @@ -33,8 +32,6 @@ function FormHelpMessage({message = '', children, isError = true, style, shouldS return null; } - const translatedMessage = Localize.translateIfPhraseKey(message); - return ( {isError && shouldShowRedDotIndicator && ( @@ -44,7 +41,7 @@ function FormHelpMessage({message = '', children, isError = true, style, shouldS /> )} - {children ?? {translatedMessage}} + {children ?? {message}} ); diff --git a/src/components/MagicCodeInput.tsx b/src/components/MagicCodeInput.tsx index deff56a534ee..6239243cb5ab 100644 --- a/src/components/MagicCodeInput.tsx +++ b/src/components/MagicCodeInput.tsx @@ -7,7 +7,6 @@ import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Browser from '@libs/Browser'; -import type {MaybePhraseKey} from '@libs/Localize'; import * as ValidationUtils from '@libs/ValidationUtils'; import CONST from '@src/CONST'; import FormHelpMessage from './FormHelpMessage'; @@ -33,7 +32,7 @@ type MagicCodeInputProps = { shouldDelayFocus?: boolean; /** Error text to display */ - errorText?: MaybePhraseKey; + errorText?: string; /** Specifies autocomplete hints for the system, so it can provide autofill */ autoComplete: AutoCompleteVariant; diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index b6f378763659..dd3cf462f89e 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -14,7 +14,6 @@ import ControlSelection from '@libs/ControlSelection'; import convertToLTR from '@libs/convertToLTR'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getButtonState from '@libs/getButtonState'; -import type {MaybePhraseKey} from '@libs/Localize'; import type {AvatarSource} from '@libs/UserUtils'; import variables from '@styles/variables'; import * as Session from '@userActions/Session'; @@ -151,13 +150,13 @@ type MenuItemBaseProps = { shouldShowDescriptionOnTop?: boolean; /** Error to display at the bottom of the component */ - errorText?: MaybePhraseKey; + errorText?: string; /** Any additional styles to pass to error text. */ errorTextStyle?: StyleProp; /** Hint to display at the bottom of the component */ - hintText?: MaybePhraseKey; + hintText?: string; /** Should the error text red dot indicator be shown */ shouldShowRedDotIndicator?: boolean; diff --git a/src/components/MessagesRow.tsx b/src/components/MessagesRow.tsx index 7c764ec94fcd..2aead5da334c 100644 --- a/src/components/MessagesRow.tsx +++ b/src/components/MessagesRow.tsx @@ -4,9 +4,7 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import type * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; -import type {ReceiptError} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import DotIndicatorMessage from './DotIndicatorMessage'; import Icon from './Icon'; @@ -16,7 +14,7 @@ import Tooltip from './Tooltip'; type MessagesRowProps = { /** The messages to display */ - messages: Record; + messages: Record; /** The type of message, 'error' shows a red dot, 'success' shows a green dot */ type: 'error' | 'success'; diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index b18a98b13304..31fcdaa4843e 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -347,11 +347,11 @@ function MoneyRequestConfirmationList({ useEffect(() => { if (shouldDisplayFieldError && hasSmartScanFailed) { - setFormError('iou.receiptScanningFailed'); + setFormError(translate('iou.receiptScanningFailed')); return; } if (shouldDisplayFieldError && didConfirmSplit) { - setFormError('iou.error.genericSmartscanFailureMessage'); + setFormError(translate('iou.error.genericSmartscanFailureMessage')); return; } // reset the form error whenever the screen gains or loses focus @@ -431,7 +431,7 @@ function MoneyRequestConfirmationList({ const shares: number[] = Object.values(splitSharesMap).map((splitShare) => splitShare?.amount ?? 0); const sumOfShares = shares?.reduce((prev, current): number => prev + current, 0); if (sumOfShares !== iouAmount) { - setFormError('iou.error.invalidSplit'); + setFormError(translate('iou.error.invalidSplit')); return; } @@ -441,7 +441,7 @@ function MoneyRequestConfirmationList({ // A split must have at least two participants with amounts bigger than 0 if (participantsWithAmount.length === 1) { - setFormError('iou.error.invalidSplitParticipants'); + setFormError(translate('iou.error.invalidSplitParticipants')); return; } @@ -698,11 +698,11 @@ function MoneyRequestConfirmationList({ return; } if (!isEditingSplitBill && isMerchantRequired && (isMerchantEmpty || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null)))) { - setFormError('iou.error.invalidMerchant'); + setFormError(translate('iou.error.invalidMerchant')); return; } if (iouCategory.length > CONST.API_TRANSACTION_CATEGORY_MAX_LENGTH) { - setFormError('iou.error.invalidCategoryLength'); + setFormError(translate('iou.error.invalidCategoryLength')); return; } @@ -738,24 +738,7 @@ function MoneyRequestConfirmationList({ onConfirm?.(selectedParticipants); } }, - [ - selectedParticipants, - isMerchantRequired, - isMerchantEmpty, - shouldDisplayFieldError, - transaction, - iouType, - onSendMoney, - iouCurrencyCode, - isDistanceRequest, - iouCategory, - isDistanceRequestWithPendingRoute, - iouAmount, - isEditingSplitBill, - formError, - setFormError, - onConfirm, - ], + [selectedParticipants, isEditingSplitBill, isMerchantRequired, isMerchantEmpty, shouldDisplayFieldError, transaction, iouCategory.length, formError, iouType, setFormError, translate, onSendMoney, iouCurrencyCode, isDistanceRequest, isDistanceRequestWithPendingRoute, iouAmount, onConfirm], ); const footerContent = useMemo(() => { diff --git a/src/components/PDFView/PDFPasswordForm.tsx b/src/components/PDFView/PDFPasswordForm.tsx index e1ef83c9b60d..e571c2830b3c 100644 --- a/src/components/PDFView/PDFPasswordForm.tsx +++ b/src/components/PDFView/PDFPasswordForm.tsx @@ -47,7 +47,7 @@ function PDFPasswordForm({isFocused, isPasswordInvalid = false, shouldShowLoadin const errorText = useMemo(() => { if (isPasswordInvalid) { - return 'attachmentView.passwordIncorrect'; + return translate('attachmentView.passwordIncorrect'); } if (validationErrorText) { return validationErrorText; diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts index d935ebe8fdc5..a6a942f41f26 100644 --- a/src/components/Picker/types.ts +++ b/src/components/Picker/types.ts @@ -1,6 +1,5 @@ import type {ChangeEvent, Component, ReactElement} from 'react'; import type {MeasureLayoutOnSuccessCallback, NativeMethods, StyleProp, ViewStyle} from 'react-native'; -import type {MaybePhraseKey} from '@libs/Localize'; type MeasureLayoutOnFailCallback = () => void; @@ -59,7 +58,7 @@ type BasePickerProps = { placeholder?: PickerPlaceholder; /** Error text to display */ - errorText?: MaybePhraseKey; + errorText?: string; /** Customize the BasePicker container */ containerStyles?: StyleProp; diff --git a/src/components/RadioButtonWithLabel.tsx b/src/components/RadioButtonWithLabel.tsx index 9b93d7900772..94265c19645d 100644 --- a/src/components/RadioButtonWithLabel.tsx +++ b/src/components/RadioButtonWithLabel.tsx @@ -3,7 +3,6 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import FormHelpMessage from './FormHelpMessage'; import * as Pressables from './Pressable'; import RadioButton from './RadioButton'; @@ -29,7 +28,7 @@ type RadioButtonWithLabelProps = { hasError?: boolean; /** Error text to display */ - errorText?: MaybePhraseKey; + errorText?: string; }; const PressableWithFeedback = Pressables.PressableWithFeedback; diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index 2030ce8f0bfd..07e8fe38f772 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -3,7 +3,6 @@ import type {ForwardedRef} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import FormHelpMessage from './FormHelpMessage'; import RadioButtonWithLabel from './RadioButtonWithLabel'; @@ -24,7 +23,7 @@ type RadioButtonsProps = { onPress: (value: string) => void; /** Potential error text provided by a form InputWrapper */ - errorText?: MaybePhraseKey; + errorText?: string; /** Style for radio button */ radioButtonStyle?: StyleProp; diff --git a/src/components/SingleChoiceQuestion.tsx b/src/components/SingleChoiceQuestion.tsx index 3ff844dd80e9..c2dc72438e43 100644 --- a/src/components/SingleChoiceQuestion.tsx +++ b/src/components/SingleChoiceQuestion.tsx @@ -3,14 +3,13 @@ import React, {forwardRef} from 'react'; // eslint-disable-next-line no-restricted-imports import type {Text as RNText} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import type {Choice} from './RadioButtons'; import RadioButtons from './RadioButtons'; import Text from './Text'; type SingleChoiceQuestionProps = { prompt: string; - errorText?: MaybePhraseKey; + errorText?: string; possibleAnswers: Choice[]; currentQuestionIndex: number; onInputChange: (value: string) => void; diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx index 8cae007679ff..0733cb519f2c 100644 --- a/src/components/StateSelector.tsx +++ b/src/components/StateSelector.tsx @@ -6,7 +6,6 @@ import type {View} from 'react-native'; import useGeographicalStateFromRoute from '@hooks/useGeographicalStateFromRoute'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {MaybePhraseKey} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -17,7 +16,7 @@ type State = keyof typeof COMMON_CONST.STATES; type StateSelectorProps = { /** Form error text. e.g when no state is selected */ - errorText?: MaybePhraseKey; + errorText?: string; /** Current selected state */ value?: State | ''; diff --git a/src/components/TextInput/BaseTextInput/types.ts b/src/components/TextInput/BaseTextInput/types.ts index e8e2d5ab352d..7a46cca693e3 100644 --- a/src/components/TextInput/BaseTextInput/types.ts +++ b/src/components/TextInput/BaseTextInput/types.ts @@ -1,6 +1,5 @@ import type {GestureResponderEvent, StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; -import type {MaybePhraseKey} from '@libs/Localize'; import type IconAsset from '@src/types/utils/IconAsset'; type CustomBaseTextInputProps = { @@ -20,7 +19,7 @@ type CustomBaseTextInputProps = { placeholder?: string; /** Error text to display */ - errorText?: MaybePhraseKey; + errorText?: string; /** Icon to display in right side of text input */ icon?: IconAsset | null; @@ -68,7 +67,7 @@ type CustomBaseTextInputProps = { hideFocusedState?: boolean; /** Hint text to display below the TextInput */ - hint?: MaybePhraseKey; + hint?: string; /** Prefix character */ prefixCharacter?: string; diff --git a/src/components/TextPicker/types.ts b/src/components/TextPicker/types.ts index 179d16a07262..cbdc1ed21efe 100644 --- a/src/components/TextPicker/types.ts +++ b/src/components/TextPicker/types.ts @@ -1,6 +1,5 @@ import type {MenuItemBaseProps} from '@components/MenuItem'; import type {BaseTextInputProps} from '@components/TextInput/BaseTextInput/types'; -import type {MaybePhraseKey} from '@libs/Localize'; type TextProps = Exclude; @@ -30,7 +29,7 @@ type TextPickerProps = { placeholder?: string; /** Form Error description */ - errorText?: MaybePhraseKey; + errorText?: string; /** Callback to call when the input changes */ onInputChange?: (value: string | undefined) => void; diff --git a/src/components/TimePicker/TimePicker.tsx b/src/components/TimePicker/TimePicker.tsx index aecaf74dc4a3..8905abd370fe 100644 --- a/src/components/TimePicker/TimePicker.tsx +++ b/src/components/TimePicker/TimePicker.tsx @@ -135,15 +135,15 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}}: Tim const hour = parseInt(hourStr, 10); if (hour === 0) { setError(true); - setErrorMessage('common.error.invalidTimeRange'); + setErrorMessage(translate('common.error.invalidTimeRange')); return false; } const isValid = DateUtils.isTimeAtLeastOneMinuteInFuture({timeString, dateTimeString: defaultValue}); setError(!isValid); - setErrorMessage('common.error.invalidTimeShouldBeFuture'); + setErrorMessage(translate('common.error.invalidTimeShouldBeFuture')); return isValid; }, - [hours, minutes, amPmValue, defaultValue], + [hours, minutes, amPmValue, defaultValue, translate], ); const resetHours = () => { diff --git a/src/components/ValuePicker/types.ts b/src/components/ValuePicker/types.ts index b9c2c89948d9..b57c9d32061a 100644 --- a/src/components/ValuePicker/types.ts +++ b/src/components/ValuePicker/types.ts @@ -1,5 +1,4 @@ import type {ListItem} from '@components/SelectionList/types'; -import type {MaybePhraseKey} from '@libs/Localize'; type ValuePickerListItem = ListItem & { value?: string; @@ -51,7 +50,7 @@ type ValuePickerProps = { placeholder?: string; /** Form Error description */ - errorText?: MaybePhraseKey; + errorText?: string; /** Callback to call when the input changes */ onInputChange?: (value: string | undefined) => void; diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 8bd37ddd698d..75ae7ae3f043 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -648,7 +648,7 @@ const getDayValidationErrorKey = (inputDate: Date): string => { } if (isAfter(startOfDay(new Date()), startOfDay(inputDate))) { - return 'common.error.invalidDateShouldBeFuture'; + return Localize.translateLocal('common.error.invalidDateShouldBeFuture'); } return ''; }; @@ -662,7 +662,7 @@ const getDayValidationErrorKey = (inputDate: Date): string => { const getTimeValidationErrorKey = (inputTime: Date): string => { const timeNowPlusOneMinute = addMinutes(new Date(), 1); if (isBefore(inputTime, timeNowPlusOneMinute)) { - return 'common.error.invalidTimeShouldBeFuture'; + return Localize.translateLocal('common.error.invalidTimeShouldBeFuture'); } return ''; }; diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 3487f05b9c05..af27dd5801f3 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -1,11 +1,11 @@ import mapValues from 'lodash/mapValues'; import CONST from '@src/CONST'; -import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; +import type {TranslationFlatObject} from '@src/languages/types'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type Response from '@src/types/onyx/Response'; import type {ReceiptError} from '@src/types/onyx/Transaction'; import DateUtils from './DateUtils'; -import * as Localize from './Localize'; +import type * as Localize from './Localize'; function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatObject { switch (response.jsonCode) { @@ -53,15 +53,15 @@ function getMicroSecondOnyxErrorObject(error: Errors, errorKey?: number): ErrorF } // We can assume that if error is a string, it has already been translated because it is server error -function getErrorMessageWithTranslationData(error: Localize.MaybePhraseKey): Localize.MaybePhraseKey { - return typeof error === 'string' ? [error, {isTranslated: true}] : error; +function getErrorMessageWithTranslationData(error: string): string { + return error; } type OnyxDataWithErrors = { errors?: Errors | null; }; -function getLatestErrorMessage(onyxData: TOnyxData | null): Localize.MaybePhraseKey { +function getLatestErrorMessage(onyxData: TOnyxData | null): string { const errors = onyxData?.errors ?? {}; if (Object.keys(errors).length === 0) { @@ -69,7 +69,7 @@ function getLatestErrorMessage(onyxData: T } const key = Object.keys(errors).sort().reverse()[0]; - return getErrorMessageWithTranslationData(errors[key]); + return getErrorMessageWithTranslationData(errors[key] ?? ''); } function getLatestErrorMessageField(onyxData: TOnyxData): Errors { @@ -148,21 +148,20 @@ function getErrorsWithTranslationData(errors: Localize.MaybePhraseKey | Errors): * @param errors - An object containing current errors in the form * @param message - Message to assign to the inputID errors */ -function addErrorMessage(errors: Errors, inputID?: string | null, message?: TKey | Localize.MaybePhraseKey) { +function addErrorMessage(errors: Errors, inputID?: string | null, message?: string) { if (!message || !inputID) { return; } const errorList = errors; const error = errorList[inputID]; - const translatedMessage = Localize.translateIfPhraseKey(message); if (!error) { - errorList[inputID] = [translatedMessage, {isTranslated: true}]; + errorList[inputID] = [message]; } else if (typeof error === 'string') { - errorList[inputID] = [`${error}\n${translatedMessage}`, {isTranslated: true}]; + errorList[inputID] = [`${error}\n${message}`]; } else if (Array.isArray(error)) { - error[0] = `${error[0]}\n${translatedMessage}`; + error[0] = `${error[0]}\n${message}`; } } diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 13fe326c2c1c..7fe73526a1a5 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -11,7 +11,7 @@ import type {OnyxFormKey} from '@src/ONYXKEYS'; import type {Report, TaxRates} from '@src/types/onyx'; import * as CardUtils from './CardUtils'; import DateUtils from './DateUtils'; -import type {MaybePhraseKey} from './Localize'; +import * as Localize from './Localize'; import * as LoginUtils from './LoginUtils'; import {parsePhoneNumber} from './PhoneNumber'; import StringUtils from './StringUtils'; @@ -112,7 +112,7 @@ function getFieldRequiredErrors(values: FormOnyxVal return; } - errors[fieldKey] = 'common.error.fieldRequired'; + errors[fieldKey] = Localize.translateLocal('common.error.fieldRequired'); }); return errors; @@ -191,7 +191,7 @@ function meetsMaximumAgeRequirement(date: string): boolean { /** * Validate that given date is in a specified range of years before now. */ -function getAgeRequirementError(date: string, minimumAge: number, maximumAge: number): MaybePhraseKey { +function getAgeRequirementError(date: string, minimumAge: number, maximumAge: number): Localize.MaybePhraseKey { const currentDate = startOfDay(new Date()); const testDate = parse(date, CONST.DATE.FNS_FORMAT_STRING, currentDate); @@ -222,14 +222,14 @@ function getDatePassedError(inputDate: string): string { // If input date is not valid, return an error if (!isValid(parsedDate)) { - return 'common.error.dateInvalid'; + return Localize.translateLocal('common.error.dateInvalid'); } // Clear time for currentDate so comparison is based solely on the date currentDate.setHours(0, 0, 0, 0); if (parsedDate < currentDate) { - return 'common.error.dateInvalid'; + return Localize.translateLocal('common.error.dateInvalid'); } return ''; diff --git a/src/libs/actions/TaxRate.ts b/src/libs/actions/TaxRate.ts index f8425cd0c40c..fd7fbe4e7d86 100644 --- a/src/libs/actions/TaxRate.ts +++ b/src/libs/actions/TaxRate.ts @@ -12,6 +12,7 @@ import INPUT_IDS from '@src/types/form/WorkspaceNewTaxForm'; import type {Policy, TaxRate, TaxRates} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type {OnyxData} from '@src/types/onyx/Request'; +import { translateLocal } from '@libs/Localize'; let allPolicies: OnyxCollection; Onyx.connect({ @@ -39,7 +40,7 @@ const validateTaxName = (policy: Policy, values: FormOnyxValues) => { const errors: FormInputErrors = {}; if (isRequired && value[fieldKey].trim() === '') { - errors[fieldKey] = 'common.error.fieldRequired'; + errors[fieldKey] = translate('common.error.fieldRequired'); } return errors; }, - [fieldKey, isRequired], + [fieldKey, isRequired, translate], ); return ( diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.tsx b/src/pages/EnablePayments/AdditionalDetailsStep.tsx index 4756db4d43ec..cc41738fa581 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.tsx +++ b/src/pages/EnablePayments/AdditionalDetailsStep.tsx @@ -76,32 +76,32 @@ function AdditionalDetailsStep({walletAdditionalDetails = DEFAULT_WALLET_ADDITIO if (values.dob) { if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.dob'; + errors.dob = translate('bankAccount.error.dob'); } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.age'; + errors.dob = translate('bankAccount.error.age'); } } if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = 'bankAccount.error.addressStreet'; + errors.addressStreet = translate('bankAccount.error.addressStreet'); } if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = 'bankAccount.error.zipCode'; + errors.addressZipCode = translate('bankAccount.error.zipCode'); } if (values.phoneNumber && !ValidationUtils.isValidUSPhone(values.phoneNumber, true)) { - errors.phoneNumber = 'bankAccount.error.phoneNumber'; + errors.phoneNumber = translate('bankAccount.error.phoneNumber'); } // walletAdditionalDetails stores errors returned by the server. If the server returns an SSN error // then the user needs to provide the full 9 digit SSN. if (walletAdditionalDetails?.errorCode === CONST.WALLET.ERROR.SSN) { if (values.ssn && !ValidationUtils.isValidSSNFullNine(values.ssn)) { - errors.ssn = 'additionalDetailsStep.ssnFull9Error'; + errors.ssn = translate('additionalDetailsStep.ssnFull9Error'); } } else if (values.ssn && !ValidationUtils.isValidSSNLastFour(values.ssn)) { - errors.ssn = 'bankAccount.error.ssnLast4'; + errors.ssn = translate('bankAccount.error.ssnLast4'); } return errors; diff --git a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx index fe17ea7e1afb..261613ba9318 100644 --- a/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx +++ b/src/pages/EnablePayments/FeesAndTerms/substeps/TermsStep.tsx @@ -48,7 +48,7 @@ function TermsStep() { const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); - const errorMessage = error ? 'common.error.acceptTerms' : ErrorUtils.getLatestErrorMessage(walletTerms ?? {}) ?? ''; + const errorMessage = error ? translate('common.error.acceptTerms') : ErrorUtils.getLatestErrorMessage(walletTerms ?? {}) ?? ''; const toggleDisclosure = () => { setHasAcceptedDisclosure(!hasAcceptedDisclosure); diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx index cfd0f4c5e3f7..6d3a1ebe9d89 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx @@ -19,20 +19,6 @@ import INPUT_IDS from '@src/types/form/WalletAdditionalDetailsForm'; const PERSONAL_INFO_DOB_KEY = INPUT_IDS.PERSONAL_INFO_STEP.DOB; const STEP_FIELDS = [PERSONAL_INFO_DOB_KEY]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.dob) { - if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.dob'; - } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.age'; - } - } - - return errors; -}; - const minDate = subYears(new Date(), CONST.DATE_BIRTH.MAX_AGE); const maxDate = subYears(new Date(), CONST.DATE_BIRTH.MIN_AGE_FOR_PAYMENT); @@ -40,6 +26,20 @@ function DateOfBirthStep({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.dob) { + if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.dob'); + } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.age'); + } + } + + return errors; + }; + const [walletAdditionalDetails] = useOnyx(ONYXKEYS.WALLET_ADDITIONAL_DETAILS); const dobDefaultValue = walletAdditionalDetails?.[PERSONAL_INFO_DOB_KEY] ?? walletAdditionalDetails?.[PERSONAL_INFO_DOB_KEY] ?? ''; diff --git a/src/pages/EnablePayments/TermsStep.tsx b/src/pages/EnablePayments/TermsStep.tsx index 916a5200a2e0..b2afb8b49fd9 100644 --- a/src/pages/EnablePayments/TermsStep.tsx +++ b/src/pages/EnablePayments/TermsStep.tsx @@ -58,7 +58,7 @@ function TermsStep(props: TermsStepProps) { const [error, setError] = useState(false); const {translate} = useLocalize(); - const errorMessage = error ? 'common.error.acceptTerms' : ErrorUtils.getLatestErrorMessage(props.walletTerms ?? {}) ?? ''; + const errorMessage = error ? translate('common.error.acceptTerms') : ErrorUtils.getLatestErrorMessage(props.walletTerms ?? {}) ?? ''; const toggleDisclosure = () => { setHasAcceptedDisclosure(!hasAcceptedDisclosure); diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index 31ff883834cc..04281eef0d2e 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -83,7 +83,7 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on Navigation.navigate(ROUTES.ONBOARDING_PERSONAL_DETAILS); }, [selectedPurpose]); - const [errorMessage, setErrorMessage] = useState<'onboarding.purpose.errorSelection' | 'onboarding.purpose.errorContinue' | ''>(''); + const [errorMessage, setErrorMessage] = useState(''); const menuItems: MenuItemProps[] = Object.values(CONST.ONBOARDING_CHOICES).map((choice) => { const translationKey = `onboarding.purpose.${choice}` as const; @@ -110,11 +110,11 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on const handleOuterClick = useCallback(() => { if (!selectedPurpose) { - setErrorMessage('onboarding.purpose.errorSelection'); + setErrorMessage(translate('onboarding.purpose.errorSelection')); } else { - setErrorMessage('onboarding.purpose.errorContinue'); + setErrorMessage(translate('onboarding.purpose.errorContinue')); } - }, [selectedPurpose]); + }, [selectedPurpose, setErrorMessage, translate]); const onboardingLocalRef = useRef(null); useImperativeHandle(isFocused ? OnboardingRefManager.ref : onboardingLocalRef, () => ({handleOuterClick}), [handleOuterClick]); @@ -147,7 +147,7 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on buttonText={translate('common.continue')} onSubmit={() => { if (!selectedPurpose) { - setErrorMessage('onboarding.purpose.errorSelection'); + setErrorMessage(translate('onboarding.purpose.errorSelection')); return; } setErrorMessage(''); diff --git a/src/pages/ReimbursementAccount/AddressFormFields.tsx b/src/pages/ReimbursementAccount/AddressFormFields.tsx index 48af00cd4925..2133c4c5bf2e 100644 --- a/src/pages/ReimbursementAccount/AddressFormFields.tsx +++ b/src/pages/ReimbursementAccount/AddressFormFields.tsx @@ -52,7 +52,7 @@ function AddressFormFields({shouldSaveDraft = false, defaultValues, values, erro value={values?.street} defaultValue={defaultValues?.street} onInputChange={onFieldChange} - errorText={errors?.street ? 'bankAccount.error.addressStreet' : ''} + errorText={errors?.street ? translate('bankAccount.error.addressStreet') : ''} renamedInputKeys={inputKeys} maxInputLength={CONST.FORM_CHARACTER_LIMIT} isLimitedToUSA @@ -68,7 +68,7 @@ function AddressFormFields({shouldSaveDraft = false, defaultValues, values, erro value={values?.city} defaultValue={defaultValues?.city} onChangeText={(value) => onFieldChange?.({city: value})} - errorText={errors?.city ? 'bankAccount.error.addressCity' : ''} + errorText={errors?.city ? translate('bankAccount.error.addressCity') : ''} containerStyles={styles.mt6} /> @@ -94,7 +94,7 @@ function AddressFormFields({shouldSaveDraft = false, defaultValues, values, erro value={values?.zipCode} defaultValue={defaultValues?.zipCode} onChangeText={(value) => onFieldChange?.({zipCode: value})} - errorText={errors?.zipCode ? 'bankAccount.error.zipCode' : ''} + errorText={errors?.zipCode ? translate('bankAccount.error.zipCode') : ''} maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} hint={['common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}]} containerStyles={styles.mt3} diff --git a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/DateOfBirthUBO.tsx b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/DateOfBirthUBO.tsx index f563692f0ae9..2173d19887ad 100644 --- a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/DateOfBirthUBO.tsx +++ b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/DateOfBirthUBO.tsx @@ -40,9 +40,9 @@ function DateOfBirthUBO({reimbursementAccountDraft, onNext, isEditing, beneficia if (values[dobInputID]) { if (!ValidationUtils.isValidPastDate(values[dobInputID]) || !ValidationUtils.meetsMaximumAgeRequirement(values[dobInputID])) { - errors[dobInputID] = 'bankAccount.error.dob'; + errors[dobInputID] = translate('bankAccount.error.dob'); } else if (!ValidationUtils.meetsMinimumAgeRequirement(values[dobInputID])) { - errors[dobInputID] = 'bankAccount.error.age'; + errors[dobInputID] = translate('bankAccount.error.age'); } } diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx index 6311a63a4059..5e0cd2f700c7 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx @@ -37,16 +37,6 @@ type States = keyof typeof COMMON_CONST.STATES; const BUSINESS_INFO_STEP_KEYS = INPUT_IDS.BUSINESS_INFO_STEP; const BUSINESS_INFO_STEP_INDEXES = CONST.REIMBURSEMENT_ACCOUNT.SUBSTEP_INDEX.BUSINESS_INFO; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS]); - - if (!values.hasNoConnectionToCannabis) { - errors.hasNoConnectionToCannabis = 'bankAccount.error.restrictedBusiness'; - } - - return errors; -}; - function ConfirmCompanyLabel() { const {translate} = useLocalize(); @@ -62,6 +52,16 @@ function ConfirmationBusiness({reimbursementAccount, reimbursementAccountDraft, const {translate} = useLocalize(); const styles = useThemeStyles(); + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, [BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS]); + + if (!values.hasNoConnectionToCannabis) { + errors.hasNoConnectionToCannabis = translate('bankAccount.error.restrictedBusiness'); + } + + return errors; + }; + const values = useMemo(() => getSubstepValues(BUSINESS_INFO_STEP_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); const defaultCheckboxState = reimbursementAccountDraft?.[BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS] ?? false; diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx index 8fecbe46164f..14a6d03e0670 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx @@ -29,22 +29,22 @@ type IncorporationDateBusinessProps = IncorporationDateBusinessOnyxProps & SubSt const COMPANY_INCORPORATION_DATE_KEY = INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_DATE; const STEP_FIELDS = [COMPANY_INCORPORATION_DATE_KEY]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { - errors.incorporationDate = 'common.error.dateInvalid'; - } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { - errors.incorporationDate = 'bankAccount.error.incorporationDateFuture'; - } - - return errors; -}; - function IncorporationDateBusiness({reimbursementAccount, reimbursementAccountDraft, onNext, isEditing}: IncorporationDateBusinessProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { + errors.incorporationDate = translate('common.error.dateInvalid'); + } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { + errors.incorporationDate = translate('bankAccount.error.incorporationDateFuture'); + } + + return errors; + }; + const defaultCompanyIncorporationDate = reimbursementAccount?.achData?.incorporationDate ?? reimbursementAccountDraft?.incorporationDate ?? ''; const handleSubmit = useReimbursementAccountStepFormSubmit({ diff --git a/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx b/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx index 03a178f186ee..94d006d72f26 100644 --- a/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx +++ b/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx @@ -30,24 +30,6 @@ const STEP_FIELDS = [ INPUT_IDS.COMPLETE_VERIFICATION.CERTIFY_TRUE_INFORMATION, ]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (!ValidationUtils.isRequiredFulfilled(values.acceptTermsAndConditions)) { - errors.acceptTermsAndConditions = 'common.error.acceptTerms'; - } - - if (!ValidationUtils.isRequiredFulfilled(values.certifyTrueInformation)) { - errors.certifyTrueInformation = 'completeVerificationStep.certifyTrueAndAccurateError'; - } - - if (!ValidationUtils.isRequiredFulfilled(values.isAuthorizedToUseBankAccount)) { - errors.isAuthorizedToUseBankAccount = 'completeVerificationStep.isAuthorizedToUseBankAccountError'; - } - - return errors; -}; - function IsAuthorizedToUseBankAccountLabel() { const {translate} = useLocalize(); return {translate('completeVerificationStep.isAuthorizedToUseBankAccount')}; @@ -76,6 +58,23 @@ function ConfirmAgreements({onNext, reimbursementAccount}: ConfirmAgreementsProp certifyTrueInformation: reimbursementAccount?.achData?.certifyTrueInformation ?? false, acceptTermsAndConditions: reimbursementAccount?.achData?.acceptTermsAndConditions ?? false, }; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (!ValidationUtils.isRequiredFulfilled(values.acceptTermsAndConditions)) { + errors.acceptTermsAndConditions = translate('common.error.acceptTerms'); + } + + if (!ValidationUtils.isRequiredFulfilled(values.certifyTrueInformation)) { + errors.certifyTrueInformation = translate('completeVerificationStep.certifyTrueAndAccurateError'); + } + + if (!ValidationUtils.isRequiredFulfilled(values.isAuthorizedToUseBankAccount)) { + errors.isAuthorizedToUseBankAccount = translate('completeVerificationStep.isAuthorizedToUseBankAccountError'); + } + + return errors; + }; return ( ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.dob) { - if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.dob'; - } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.age'; - } - } - - return errors; -}; - function DateOfBirth({reimbursementAccount, reimbursementAccountDraft, onNext, isEditing}: DateOfBirthProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.dob) { + if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.dob'); + } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.age'); + } + } + + return errors; + }; + const dobDefaultValue = reimbursementAccount?.achData?.[PERSONAL_INFO_DOB_KEY] ?? reimbursementAccountDraft?.[PERSONAL_INFO_DOB_KEY] ?? ''; const minDate = subYears(new Date(), CONST.DATE_BIRTH.MAX_AGE); diff --git a/src/pages/Travel/TravelTerms.tsx b/src/pages/Travel/TravelTerms.tsx index 468ca9b8082a..9f26284848a4 100644 --- a/src/pages/Travel/TravelTerms.tsx +++ b/src/pages/Travel/TravelTerms.tsx @@ -23,7 +23,7 @@ function TravelTerms() { const [hasAcceptedTravelTerms, setHasAcceptedTravelTerms] = useState(false); const [error, setError] = useState(false); - const errorMessage = error ? 'travel.termsAndConditions.error' : ''; + const errorMessage = error ? translate('travel.termsAndConditions.error') : ''; const toggleTravelTerms = () => { setHasAcceptedTravelTerms(!hasAcceptedTravelTerms); diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index 46bd34006550..07037a09df11 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -16,7 +16,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import type {MaybePhraseKey} from '@libs/Localize'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types'; @@ -98,7 +97,7 @@ function MoneyRequestAmountForm( const textInput = useRef(null); const moneyRequestAmountInput = useRef(null); - const [formError, setFormError] = useState(''); + const [formError, setFormError] = useState(''); const [shouldUpdateSelection, setShouldUpdateSelection] = useState(true); const isFocused = useIsFocused(); @@ -209,12 +208,12 @@ function MoneyRequestAmountForm( // Skip the check for tax amount form as 0 is a valid input const currentAmount = moneyRequestAmountInput.current?.getAmount() ?? ''; if (!currentAmount.length || (!isTaxAmountForm && isAmountInvalid(currentAmount))) { - setFormError('iou.error.invalidAmount'); + setFormError(translate('iou.error.invalidAmount')); return; } if (isTaxAmountInvalid(currentAmount, taxAmount, isTaxAmountForm)) { - setFormError(['iou.error.invalidTaxAmount', {amount: formattedTaxAmount}]); + setFormError([translate('iou.error.invalidTaxAmount'), {amount: formattedTaxAmount}]); return; } @@ -225,7 +224,7 @@ function MoneyRequestAmountForm( onSubmitButtonPress({amount: currentAmount, currency, paymentMethod: iouPaymentType}); }, - [taxAmount, onSubmitButtonPress, currency, formattedTaxAmount, initializeAmount], + [taxAmount, initializeAmount, onSubmitButtonPress, currency, translate, formattedTaxAmount], ); const buttonText: string = useMemo(() => { diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 1d9aec1ea60d..3e8cffd3869a 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -309,7 +309,7 @@ function MoneyRequestParticipantsSelector({participants = [], onFinish, onPartic )} diff --git a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx index 13d03fd557e9..b44f5d9e0610 100644 --- a/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/Steps/CodesStep.tsx @@ -135,7 +135,7 @@ function CodesStep({account, backTo}: CodesStepProps) { text={translate('common.next')} onPress={() => { if (!account?.codesAreCopied) { - setError('twoFactorAuth.errorStepCodes'); + setError(translate('twoFactorAuth.errorStepCodes')); return; } setStep(CONST.TWO_FACTOR_AUTH_STEPS.VERIFY); diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx index 2710cadf94e4..bd6fa17bff34 100644 --- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx @@ -105,7 +105,7 @@ function ActivatePhysicalCardPage({ activateCardCodeInputRef.current?.blur(); if (lastFourDigits.replace(CONST.MAGIC_CODE_EMPTY_CHAR, '').length !== LAST_FOUR_DIGITS_LENGTH) { - setFormError('activateCardPage.error.thatDidntMatch'); + setFormError(translate('activateCardPage.error.thatDidntMatch')); return; } if (inactiveCard?.cardID === undefined) { diff --git a/src/pages/settings/Wallet/AddDebitCardPage.tsx b/src/pages/settings/Wallet/AddDebitCardPage.tsx index 0beb3c16018d..b3bc1d839727 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.tsx +++ b/src/pages/settings/Wallet/AddDebitCardPage.tsx @@ -89,31 +89,31 @@ function DebitCardPage({formData}: DebitCardPageProps) { const errors = ValidationUtils.getFieldRequiredErrors(values, REQUIRED_FIELDS); if (values.nameOnCard && !ValidationUtils.isValidLegalName(values.nameOnCard)) { - errors.nameOnCard = 'addDebitCardPage.error.invalidName'; + errors.nameOnCard = translate('addDebitCardPage.error.invalidName'); } if (values.cardNumber && !ValidationUtils.isValidDebitCard(values.cardNumber.replace(/ /g, ''))) { - errors.cardNumber = 'addDebitCardPage.error.debitCardNumber'; + errors.cardNumber = translate('addDebitCardPage.error.debitCardNumber'); } if (values.expirationDate && !ValidationUtils.isValidExpirationDate(values.expirationDate)) { - errors.expirationDate = 'addDebitCardPage.error.expirationDate'; + errors.expirationDate = translate('addDebitCardPage.error.expirationDate'); } if (values.securityCode && !ValidationUtils.isValidSecurityCode(values.securityCode)) { - errors.securityCode = 'addDebitCardPage.error.securityCode'; + errors.securityCode = translate('addDebitCardPage.error.securityCode'); } if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = 'addDebitCardPage.error.addressStreet'; + errors.addressStreet = translate('addDebitCardPage.error.addressStreet'); } if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = 'addDebitCardPage.error.addressZipCode'; + errors.addressZipCode = translate('addDebitCardPage.error.addressZipCode'); } if (!values.acceptTerms) { - errors.acceptTerms = 'common.error.acceptTerms'; + errors.acceptTerms = translate('common.error.acceptTerms'); } return errors; diff --git a/src/pages/settings/Wallet/ReportCardLostPage.tsx b/src/pages/settings/Wallet/ReportCardLostPage.tsx index 11790bd44cb6..813f09af74d5 100644 --- a/src/pages/settings/Wallet/ReportCardLostPage.tsx +++ b/src/pages/settings/Wallet/ReportCardLostPage.tsx @@ -183,7 +183,7 @@ function ReportCardLostPage({ @@ -201,7 +201,7 @@ function ReportCardLostPage({ diff --git a/src/pages/signin/LoginForm/BaseLoginForm.tsx b/src/pages/signin/LoginForm/BaseLoginForm.tsx index 4286a2603341..9e4b3e487718 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.tsx +++ b/src/pages/signin/LoginForm/BaseLoginForm.tsx @@ -31,7 +31,6 @@ import * as CloseAccount from '@userActions/CloseAccount'; import * as Session from '@userActions/Session'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {CloseAccountForm} from '@src/types/form'; import type {Account, Credentials} from '@src/types/onyx'; @@ -59,7 +58,7 @@ function BaseLoginForm({account, credentials, closeAccount, blurOnSubmit = false const {translate} = useLocalize(); const input = useRef(null); const [login, setLogin] = useState(() => Str.removeSMSDomain(credentials?.login ?? '')); - const [formError, setFormError] = useState(); + const [formError, setFormError] = useState(); const prevIsVisible = usePrevious(isVisible); const firstBlurred = useRef(false); const isFocused = useIsFocused(); @@ -73,7 +72,7 @@ function BaseLoginForm({account, credentials, closeAccount, blurOnSubmit = false (value: string) => { const loginTrim = value.trim(); if (!loginTrim) { - setFormError('common.pleaseEnterEmailOrPhoneNumber'); + setFormError(translate('common.pleaseEnterEmailOrPhoneNumber')); return false; } @@ -82,9 +81,9 @@ function BaseLoginForm({account, credentials, closeAccount, blurOnSubmit = false if (!Str.isValidEmail(loginTrim) && !parsedPhoneNumber.possible) { if (ValidationUtils.isNumericWithSpecialChars(loginTrim)) { - setFormError('common.error.phoneNumber'); + setFormError(translate('common.error.phoneNumber')); } else { - setFormError('loginForm.error.invalidFormatEmailLogin'); + setFormError(translate('loginForm.error.invalidFormatEmailLogin')); } return false; } @@ -92,7 +91,7 @@ function BaseLoginForm({account, credentials, closeAccount, blurOnSubmit = false setFormError(undefined); return true; }, - [setFormError], + [setFormError, translate], ); /** diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx index 488d3d48eae9..7aaa5a03576b 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -323,7 +323,7 @@ function BaseValidateCodeForm({account, credentials, session, autoComplete, isUs onChangeText={(text) => onTextInput(text, 'twoFactorAuthCode')} onFulfill={validateAndSubmitForm} maxLength={CONST.TFA_CODE_LENGTH} - errorText={formError?.twoFactorAuthCode ?? ''} + errorText={formError?.twoFactorAuthCode ? translate(formError?.twoFactorAuthCode) : ''} hasError={hasError} autoFocus key="twoFactorAuthCode" @@ -356,7 +356,7 @@ function BaseValidateCodeForm({account, credentials, session, autoComplete, isUs value={validateCode} onChangeText={(text) => onTextInput(text, 'validateCode')} onFulfill={validateAndSubmitForm} - errorText={formError?.validateCode ?? ''} + errorText={formError?.validateCode ? translate(formError?.validateCode) : ''} hasError={hasError} autoFocus key="validateCode" diff --git a/src/pages/tasks/NewTaskPage.tsx b/src/pages/tasks/NewTaskPage.tsx index d038e8260418..9fdb0ae67a65 100644 --- a/src/pages/tasks/NewTaskPage.tsx +++ b/src/pages/tasks/NewTaskPage.tsx @@ -99,17 +99,17 @@ function NewTaskPage({task, reports, personalDetails}: NewTaskPageProps) { // the response const onSubmit = () => { if (!task?.title && !task?.shareDestination) { - setErrorMessage('newTaskPage.confirmError'); + setErrorMessage(translate('newTaskPage.confirmError')); return; } if (!task.title) { - setErrorMessage('newTaskPage.pleaseEnterTaskName'); + setErrorMessage(translate('newTaskPage.pleaseEnterTaskName')); return; } if (!task.shareDestination) { - setErrorMessage('newTaskPage.pleaseEnterTaskDestination'); + setErrorMessage(translate('newTaskPage.pleaseEnterTaskDestination')); return; } diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index 9142361a531e..f1dbbf1047f4 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -277,7 +277,7 @@ function WorkspaceInvitePage({route, betas, invitedEmailsToAccountIDsDraft, poli isAlertVisible={shouldShowAlertPrompt} buttonText={translate('common.next')} onSubmit={inviteUser} - message={[policy?.alertMessage ?? '', {isTranslated: true}]} + message={policy?.alertMessage ?? ''} containerStyles={[styles.flexReset, styles.flexGrow0, styles.flexShrink0, styles.flexBasisAuto]} enabledWhenOffline disablePressOnEnter diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index c537ee50d19b..26c87bd0ed4f 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -411,7 +411,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, Policy.dismissAddedWithPrimaryLoginMessages(policyID)} /> diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index 382c1dde6d47..cf5d3c1fb3aa 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -169,18 +169,18 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { // We error if the user doesn't enter a room name or left blank - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.pleaseEnterRoomName'); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.pleaseEnterRoomName')); } else if (values.roomName !== CONST.POLICY.ROOM_PREFIX && !ValidationUtils.isValidRoomName(values.roomName)) { // We error if the room name has invalid characters - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomNameInvalidError'); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomNameInvalidError')); } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'roomName', ['newRoomPage.roomNameReservedError', {reservedName: values.roomName}]); + ErrorUtils.addErrorMessage(errors, 'roomName', [translate('newRoomPage.roomNameReservedError'), {reservedName: values.roomName}]); } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, values.policyID ?? '')) { // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomAlreadyExistsError')); } else if (values.roomName.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'roomName', ['common.error.characterLimitExceedCounter', {length: values.roomName.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'roomName', [translate('common.error.characterLimitExceedCounter'), {length: values.roomName.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); } const descriptionLength = ReportUtils.getCommentLength(values.reportDescription); diff --git a/src/stories/Form.stories.tsx b/src/stories/Form.stories.tsx index f4e89f6766f0..ab29612b0556 100644 --- a/src/stories/Form.stories.tsx +++ b/src/stories/Form.stories.tsx @@ -12,7 +12,6 @@ import Picker from '@components/Picker'; import StateSelector from '@components/StateSelector'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; -import type {MaybePhraseKey} from '@libs/Localize'; import NetworkConnection from '@libs/NetworkConnection'; import * as ValidationUtils from '@libs/ValidationUtils'; import * as FormActions from '@userActions/FormActions'; @@ -58,7 +57,7 @@ function Template(props: FormProviderProps) { FormActions.setDraftValues(props.formID, props.draftValues); if (props.formState?.error) { - FormActions.setErrors(props.formID, {error: props.formState.error as MaybePhraseKey}); + FormActions.setErrors(props.formID, {error: props.formState.error as string}); } else { FormActions.clearErrors(props.formID); } @@ -172,7 +171,7 @@ function WithNativeEventHandler(props: FormProviderProps) { FormActions.setDraftValues(props.formID, props.draftValues); if (props.formState?.error) { - FormActions.setErrors(props.formID, {error: props.formState.error as MaybePhraseKey}); + FormActions.setErrors(props.formID, {error: props.formState.error as string}); } else { FormActions.clearErrors(props.formID); } diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index 8b96a89a2a1b..37a06f20ca63 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -1,7 +1,7 @@ import type {ValueOf} from 'type-fest'; -import type {MaybePhraseKey} from '@libs/Localize'; import type {AvatarSource} from '@libs/UserUtils'; import type CONST from '@src/CONST'; +import type { MaybePhraseKey } from '@libs/Localize'; type PendingAction = ValueOf | null; @@ -19,7 +19,7 @@ type OnyxValueWithOfflineFeedback = keyof TO type ErrorFields = Record; -type Errors = Record; +type Errors = Record; type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; From 7010adfa191473171f573d8cdb742e20a62107fb Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 13:02:54 +0700 Subject: [PATCH 027/228] Fix translate error for DotIndicatorMessage --- src/components/AddressForm.tsx | 73 ++++++++++--------- src/components/DotIndicatorMessage.tsx | 11 ++- src/components/MessagesRow.tsx | 3 +- .../MoneyRequestConfirmationList.tsx | 20 ++++- src/components/OfflineWithFeedback.tsx | 10 +-- src/components/PDFView/PDFPasswordForm.tsx | 2 +- src/libs/ErrorUtils.ts | 24 +++--- src/libs/actions/IOU.ts | 4 +- src/libs/actions/TaxRate.ts | 2 +- .../PersonalInfo/substeps/DateOfBirthStep.tsx | 4 +- .../substeps/ConfirmationBusiness.tsx | 2 +- .../substeps/IncorporationDateBusiness.tsx | 4 +- .../substeps/ConfirmAgreements.tsx | 8 +- .../PersonalInfo/substeps/DateOfBirth.tsx | 4 +- .../MoneyRequestParticipantsSelector.tsx | 2 +- .../request/step/IOURequestStepDistance.tsx | 2 +- .../Contacts/ContactMethodDetailsPage.tsx | 2 +- .../Wallet/ActivatePhysicalCardPage.tsx | 2 +- .../settings/Wallet/ReportCardLostPage.tsx | 4 +- src/pages/signin/LoginForm/BaseLoginForm.tsx | 2 +- src/pages/workspace/WorkspaceNewRoomPage.tsx | 15 ++-- src/types/onyx/OnyxCommon.ts | 1 - 22 files changed, 107 insertions(+), 94 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index bd3ad7ea0888..dc5c47ad182c 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -88,50 +88,53 @@ function AddressForm({ * @returns - An object containing the errors for each inputID */ - const validator = useCallback((values: FormOnyxValues): Errors => { - const errors: Errors & { - zipPostCode?: string | string[]; - } = {}; - const requiredFields = ['addressLine1', 'city', 'country', 'state'] as const; - - // Check "State" dropdown is a valid state if selected Country is USA - if (values.country === CONST.COUNTRY.US && !values.state) { - errors.state = translate('common.error.fieldRequired'); - } - - // Add "Field required" errors if any required field is empty - requiredFields.forEach((fieldKey) => { - const fieldValue = values[fieldKey] ?? ''; - if (ValidationUtils.isRequiredFulfilled(fieldValue)) { - return; + const validator = useCallback( + (values: FormOnyxValues): Errors => { + const errors: Errors & { + zipPostCode?: string | string[]; + } = {}; + const requiredFields = ['addressLine1', 'city', 'country', 'state'] as const; + + // Check "State" dropdown is a valid state if selected Country is USA + if (values.country === CONST.COUNTRY.US && !values.state) { + errors.state = translate('common.error.fieldRequired'); } - errors[fieldKey] = translate('common.error.fieldRequired'); - }); + // Add "Field required" errors if any required field is empty + requiredFields.forEach((fieldKey) => { + const fieldValue = values[fieldKey] ?? ''; + if (ValidationUtils.isRequiredFulfilled(fieldValue)) { + return; + } + + errors[fieldKey] = translate('common.error.fieldRequired'); + }); - // If no country is selected, default value is an empty string and there's no related regex data so we default to an empty object - const countryRegexDetails = (values.country ? CONST.COUNTRY_ZIP_REGEX_DATA?.[values.country] : {}) as CountryZipRegex; + // If no country is selected, default value is an empty string and there's no related regex data so we default to an empty object + const countryRegexDetails = (values.country ? CONST.COUNTRY_ZIP_REGEX_DATA?.[values.country] : {}) as CountryZipRegex; - // The postal code system might not exist for a country, so no regex either for them. - const countrySpecificZipRegex = countryRegexDetails?.regex; - const countryZipFormat = countryRegexDetails?.samples ?? ''; + // The postal code system might not exist for a country, so no regex either for them. + const countrySpecificZipRegex = countryRegexDetails?.regex; + const countryZipFormat = countryRegexDetails?.samples ?? ''; - ErrorUtils.addErrorMessage(errors, 'firstName', 'bankAccount.error.firstName'); + ErrorUtils.addErrorMessage(errors, 'firstName', 'bankAccount.error.firstName'); - if (countrySpecificZipRegex) { - if (!countrySpecificZipRegex.test(values.zipPostCode?.trim().toUpperCase())) { - if (ValidationUtils.isRequiredFulfilled(values.zipPostCode?.trim())) { - errors.zipPostCode = ['privatePersonalDetails.error.incorrectZipFormat', countryZipFormat]; - } else { - errors.zipPostCode = 'common.error.fieldRequired'; + if (countrySpecificZipRegex) { + if (!countrySpecificZipRegex.test(values.zipPostCode?.trim().toUpperCase())) { + if (ValidationUtils.isRequiredFulfilled(values.zipPostCode?.trim())) { + errors.zipPostCode = ['privatePersonalDetails.error.incorrectZipFormat', countryZipFormat]; + } else { + errors.zipPostCode = 'common.error.fieldRequired'; + } } + } else if (!CONST.GENERIC_ZIP_CODE_REGEX.test(values?.zipPostCode?.trim()?.toUpperCase() ?? '')) { + errors.zipPostCode = 'privatePersonalDetails.error.incorrectZipFormat'; } - } else if (!CONST.GENERIC_ZIP_CODE_REGEX.test(values?.zipPostCode?.trim()?.toUpperCase() ?? '')) { - errors.zipPostCode = 'privatePersonalDetails.error.incorrectZipFormat'; - } - return errors; - }, [translate]); + return errors; + }, + [translate], + ); return ( ; + messages: Record; /** The type of message, 'error' shows a red dot, 'success' shows a green dot */ type: 'error' | 'success'; @@ -45,12 +44,12 @@ function DotIndicatorMessage({messages = {}, style, type, textStyles}: DotIndica } // Fetch the keys, sort them, and map through each key to get the corresponding message - const sortedMessages: Array = Object.keys(messages) + const sortedMessages: Array = Object.keys(messages) .sort() - .map((key) => messages[key]); - + .map((key) => messages[key]) + .filter((message): message is string | ReceiptError => message !== null); // Removing duplicates using Set and transforming the result into an array - const uniqueMessages: Array = [...new Set(sortedMessages)].map((message) => (isReceiptError(message) ? message : Localize.translateIfPhraseKey(message))); + const uniqueMessages: Array = [...new Set(sortedMessages)].map((message) => message); const isErrorMessage = type === 'error'; diff --git a/src/components/MessagesRow.tsx b/src/components/MessagesRow.tsx index 2aead5da334c..6a5be5db07bc 100644 --- a/src/components/MessagesRow.tsx +++ b/src/components/MessagesRow.tsx @@ -5,6 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; +import type {ReceiptError} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import DotIndicatorMessage from './DotIndicatorMessage'; import Icon from './Icon'; @@ -14,7 +15,7 @@ import Tooltip from './Tooltip'; type MessagesRowProps = { /** The messages to display */ - messages: Record; + messages: Record; /** The type of message, 'error' shows a red dot, 'success' shows a green dot */ type: 'error' | 'success'; diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 31fcdaa4843e..c745ca34251d 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -738,7 +738,25 @@ function MoneyRequestConfirmationList({ onConfirm?.(selectedParticipants); } }, - [selectedParticipants, isEditingSplitBill, isMerchantRequired, isMerchantEmpty, shouldDisplayFieldError, transaction, iouCategory.length, formError, iouType, setFormError, translate, onSendMoney, iouCurrencyCode, isDistanceRequest, isDistanceRequestWithPendingRoute, iouAmount, onConfirm], + [ + selectedParticipants, + isEditingSplitBill, + isMerchantRequired, + isMerchantEmpty, + shouldDisplayFieldError, + transaction, + iouCategory.length, + formError, + iouType, + setFormError, + translate, + onSendMoney, + iouCurrencyCode, + isDistanceRequest, + isDistanceRequestWithPendingRoute, + iouAmount, + onConfirm, + ], ); const footerContent = useMemo(() => { diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index 1ff3ee2ed737..70354c4a4676 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -5,8 +5,6 @@ import {View} from 'react-native'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; -import type {MaybePhraseKey} from '@libs/Localize'; import mapChildrenFlat from '@libs/mapChildrenFlat'; import shouldRenderOffscreen from '@libs/shouldRenderOffscreen'; import type {AllStyles} from '@styles/utils/types'; @@ -63,10 +61,6 @@ type OfflineWithFeedbackProps = ChildrenProps & { type StrikethroughProps = Partial & {style: Array}; -function isMaybePhraseKeyType(message: unknown): message is MaybePhraseKey { - return typeof message === 'string' || Array.isArray(message); -} - function OfflineWithFeedback({ pendingAction, canDismissError = true, @@ -90,8 +84,8 @@ function OfflineWithFeedback({ // Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages. const errorEntries = Object.entries(errors ?? {}); - const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, MaybePhraseKey | ReceiptError] => errorEntry[1] !== null); - const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => (isMaybePhraseKeyType(error) ? ErrorUtils.getErrorMessageWithTranslationData(error) : error)); + const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string | ReceiptError] => errorEntry[1] !== null); + const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => error); const hasErrorMessages = !isEmptyObject(errorMessages); const isOfflinePendingAction = !!isOffline && !!pendingAction; diff --git a/src/components/PDFView/PDFPasswordForm.tsx b/src/components/PDFView/PDFPasswordForm.tsx index e571c2830b3c..e212d79b27d6 100644 --- a/src/components/PDFView/PDFPasswordForm.tsx +++ b/src/components/PDFView/PDFPasswordForm.tsx @@ -53,7 +53,7 @@ function PDFPasswordForm({isFocused, isPasswordInvalid = false, shouldShowLoadin return validationErrorText; } return ''; - }, [isPasswordInvalid, validationErrorText]); + }, [isPasswordInvalid, validationErrorText, translate]); useEffect(() => { if (!isFocused) { diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index af27dd5801f3..2019d5d2daf5 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -1,11 +1,11 @@ import mapValues from 'lodash/mapValues'; import CONST from '@src/CONST'; -import type {TranslationFlatObject} from '@src/languages/types'; +import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type Response from '@src/types/onyx/Response'; import type {ReceiptError} from '@src/types/onyx/Transaction'; import DateUtils from './DateUtils'; -import type * as Localize from './Localize'; +import * as Localize from './Localize'; function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatObject { switch (response.jsonCode) { @@ -40,8 +40,8 @@ function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatO * Method used to get an error object with microsecond as the key. * @param error - error key or message to be saved */ -function getMicroSecondOnyxError(error: string, isTranslated = false, errorKey?: number): Errors { - return {[errorKey ?? DateUtils.getMicroseconds()]: error && [error, {isTranslated}]}; +function getMicroSecondOnyxError(error: TranslationPaths, _isTranslated = false, errorKey?: number): Errors { + return {[errorKey ?? DateUtils.getMicroseconds()]: Localize.translateLocal(error)}; } /** @@ -53,8 +53,8 @@ function getMicroSecondOnyxErrorObject(error: Errors, errorKey?: number): ErrorF } // We can assume that if error is a string, it has already been translated because it is server error -function getErrorMessageWithTranslationData(error: string): string { - return error; +function getErrorMessageWithTranslationData(error: string | null): string { + return error ?? ''; } type OnyxDataWithErrors = { @@ -130,12 +130,12 @@ function getLatestErrorFieldForAnyField; Onyx.connect({ diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx index 6d3a1ebe9d89..7c4f5f43f2a1 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx @@ -28,7 +28,7 @@ function DateOfBirthStep({onNext, isEditing}: SubStepProps) { const validate = (values: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - + if (values.dob) { if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { errors.dob = translate('bankAccount.error.dob'); @@ -36,7 +36,7 @@ function DateOfBirthStep({onNext, isEditing}: SubStepProps) { errors.dob = translate('bankAccount.error.age'); } } - + return errors; }; diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx index 5e0cd2f700c7..fa660c98d8bc 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx @@ -54,7 +54,7 @@ function ConfirmationBusiness({reimbursementAccount, reimbursementAccountDraft, const validate = (values: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(values, [BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS]); - + if (!values.hasNoConnectionToCannabis) { errors.hasNoConnectionToCannabis = translate('bankAccount.error.restrictedBusiness'); } diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx index 14a6d03e0670..58a1feb710c2 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx @@ -35,13 +35,13 @@ function IncorporationDateBusiness({reimbursementAccount, reimbursementAccountDr const validate = (values: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - + if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { errors.incorporationDate = translate('common.error.dateInvalid'); } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { errors.incorporationDate = translate('bankAccount.error.incorporationDateFuture'); } - + return errors; }; diff --git a/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx b/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx index 94d006d72f26..1e2f004b9c8d 100644 --- a/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx +++ b/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx @@ -60,19 +60,19 @@ function ConfirmAgreements({onNext, reimbursementAccount}: ConfirmAgreementsProp }; const validate = (values: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - + if (!ValidationUtils.isRequiredFulfilled(values.acceptTermsAndConditions)) { errors.acceptTermsAndConditions = translate('common.error.acceptTerms'); } - + if (!ValidationUtils.isRequiredFulfilled(values.certifyTrueInformation)) { errors.certifyTrueInformation = translate('completeVerificationStep.certifyTrueAndAccurateError'); } - + if (!ValidationUtils.isRequiredFulfilled(values.isAuthorizedToUseBankAccount)) { errors.isAuthorizedToUseBankAccount = translate('completeVerificationStep.isAuthorizedToUseBankAccountError'); } - + return errors; }; diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx index 3c3ed7cee480..c47139af8a90 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx @@ -38,7 +38,7 @@ function DateOfBirth({reimbursementAccount, reimbursementAccountDraft, onNext, i const validate = (values: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - + if (values.dob) { if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { errors.dob = translate('bankAccount.error.dob'); @@ -46,7 +46,7 @@ function DateOfBirth({reimbursementAccount, reimbursementAccountDraft, onNext, i errors.dob = translate('bankAccount.error.age'); } } - + return errors; }; diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 3e8cffd3869a..d3afa0ad3a36 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -309,7 +309,7 @@ function MoneyRequestParticipantsSelector({participants = [], onFinish, onPartic )} diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index b486510ead0a..e2f665c52cae 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -326,7 +326,7 @@ function IOURequestStepDistance({ return {duplicateWaypointsError: translate('iou.error.duplicateWaypointsErrorMessage')} as Errors; } if (atLeastTwoDifferentWaypointsError) { - return {atLeastTwoDifferentWaypointsError: 'iou.error.atLeastTwoDifferentWaypoints'} as Errors; + return {atLeastTwoDifferentWaypointsError: translate('iou.error.atLeastTwoDifferentWaypoints')} as Errors; } return {}; }; diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx index 8aaeb7151563..0e392bb8b3c0 100644 --- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx +++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx @@ -231,7 +231,7 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { type="success" style={[themeStyles.mb3]} // eslint-disable-next-line @typescript-eslint/naming-convention - messages={{0: ['contacts.enterMagicCode', {contactMethod: formattedContactMethod}]}} + messages={{0: translate('contacts.enterMagicCode', {contactMethod: formattedContactMethod})}} /> ; diff --git a/src/pages/settings/Wallet/ReportCardLostPage.tsx b/src/pages/settings/Wallet/ReportCardLostPage.tsx index 813f09af74d5..4a84f0294d78 100644 --- a/src/pages/settings/Wallet/ReportCardLostPage.tsx +++ b/src/pages/settings/Wallet/ReportCardLostPage.tsx @@ -183,7 +183,7 @@ function ReportCardLostPage({ @@ -201,7 +201,7 @@ function ReportCardLostPage({ diff --git a/src/pages/signin/LoginForm/BaseLoginForm.tsx b/src/pages/signin/LoginForm/BaseLoginForm.tsx index 9e4b3e487718..097bb843402a 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.tsx +++ b/src/pages/signin/LoginForm/BaseLoginForm.tsx @@ -265,7 +265,7 @@ function BaseLoginForm({account, credentials, closeAccount, blurOnSubmit = false style={[styles.mv2]} type="success" // eslint-disable-next-line @typescript-eslint/naming-convention,@typescript-eslint/prefer-nullish-coalescing - messages={{0: closeAccount?.success ? [closeAccount.success, {isTranslated: true}] : account?.message || ''}} + messages={{0: closeAccount?.success ? closeAccount.success : account?.message || ''}} /> )} { diff --git a/src/pages/workspace/WorkspaceNewRoomPage.tsx b/src/pages/workspace/WorkspaceNewRoomPage.tsx index cf5d3c1fb3aa..436edd0f1c89 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.tsx +++ b/src/pages/workspace/WorkspaceNewRoomPage.tsx @@ -175,20 +175,21 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomNameInvalidError')); } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'roomName', [translate('newRoomPage.roomNameReservedError'), {reservedName: values.roomName}]); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomNameReservedError', {reservedName: values.roomName})); } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, values.policyID ?? '')) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomAlreadyExistsError')); } else if (values.roomName.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'roomName', [translate('common.error.characterLimitExceedCounter'), {length: values.roomName.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('common.error.characterLimitExceedCounter', {length: values.roomName.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } const descriptionLength = ReportUtils.getCommentLength(values.reportDescription); if (descriptionLength > CONST.REPORT_DESCRIPTION.MAX_LENGTH) { - ErrorUtils.addErrorMessage(errors, 'reportDescription', [ - 'common.error.characterLimitExceedCounter', - {length: descriptionLength, limit: CONST.REPORT_DESCRIPTION.MAX_LENGTH}, - ]); + ErrorUtils.addErrorMessage( + errors, + 'reportDescription', + translate('common.error.characterLimitExceedCounter', {length: descriptionLength, limit: CONST.REPORT_DESCRIPTION.MAX_LENGTH}), + ); } if (!values.policyID) { @@ -197,7 +198,7 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli return errors; }, - [reports], + [reports, translate], ); const writeCapabilityOptions = useMemo( diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index 37a06f20ca63..98c8066da8f4 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -1,7 +1,6 @@ import type {ValueOf} from 'type-fest'; import type {AvatarSource} from '@libs/UserUtils'; import type CONST from '@src/CONST'; -import type { MaybePhraseKey } from '@libs/Localize'; type PendingAction = ValueOf | null; From 7d39bf56cf5db269ffaabc35961892475cde38be Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 13:37:29 +0700 Subject: [PATCH 028/228] fix test --- tests/actions/IOUTest.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 940d533b9d2b..fcfe94ce2952 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -10,6 +10,7 @@ import * as Report from '@src/libs/actions/Report'; import * as ReportActions from '@src/libs/actions/ReportActions'; import * as User from '@src/libs/actions/User'; import DateUtils from '@src/libs/DateUtils'; +import * as Localize from '@src/libs/Localize'; import Navigation from '@src/libs/Navigation/Navigation'; import * as NumberUtils from '@src/libs/NumberUtils'; import * as PersonalDetailsUtils from '@src/libs/PersonalDetailsUtils'; @@ -769,7 +770,7 @@ describe('actions/IOU', () => { Onyx.disconnect(connectionID); expect(transaction?.pendingAction).toBeFalsy(); expect(transaction?.errors).toBeTruthy(); - expect(Object.values(transaction?.errors ?? {})[0]).toEqual(expect.arrayContaining(['iou.error.genericCreateFailureMessage', {isTranslated: false}])); + expect(Object.values(transaction?.errors ?? {})[0]).toEqual(Localize.translateLocal('iou.error.genericCreateFailureMessage')); resolve(); }, }); From 6177f8b0f07aefbf4b3f350df62503aea0985646 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 13:45:04 +0700 Subject: [PATCH 029/228] fix error utils test --- src/libs/ErrorUtils.ts | 2 +- tests/unit/ErrorUtilsTest.ts | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 2019d5d2daf5..2621aa4ebd72 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -148,7 +148,7 @@ function getErrorsWithTranslationData(errors: Errors): Errors { * @param errors - An object containing current errors in the form * @param message - Message to assign to the inputID errors */ -function addErrorMessage(errors: Errors, inputID?: string | null, message?: string) { +function addErrorMessage(errors: Errors, inputID?: string | null, message?: string | null) { if (!message || !inputID) { return; } diff --git a/tests/unit/ErrorUtilsTest.ts b/tests/unit/ErrorUtilsTest.ts index 9168c1ca12a5..6f615ab20436 100644 --- a/tests/unit/ErrorUtilsTest.ts +++ b/tests/unit/ErrorUtilsTest.ts @@ -6,21 +6,21 @@ describe('ErrorUtils', () => { const errors: Errors = {}; ErrorUtils.addErrorMessage(errors, 'username', 'Username cannot be empty'); - expect(errors).toEqual({username: ['Username cannot be empty', {isTranslated: true}]}); + expect(errors).toEqual({username: 'Username cannot be empty'}); }); test('should append an error message to an existing error message for a given inputID', () => { const errors: Errors = {username: 'Username cannot be empty'}; ErrorUtils.addErrorMessage(errors, 'username', 'Username must be at least 6 characters long'); - expect(errors).toEqual({username: ['Username cannot be empty\nUsername must be at least 6 characters long', {isTranslated: true}]}); + expect(errors).toEqual({username: 'Username cannot be empty\nUsername must be at least 6 characters long'}); }); test('should add an error to input which does not contain any errors yet', () => { const errors: Errors = {username: 'Username cannot be empty'}; ErrorUtils.addErrorMessage(errors, 'password', 'Password cannot be empty'); - expect(errors).toEqual({username: 'Username cannot be empty', password: ['Password cannot be empty', {isTranslated: true}]}); + expect(errors).toEqual({username: 'Username cannot be empty', password: 'Password cannot be empty'}); }); test('should not mutate the errors object when message is empty', () => { @@ -59,10 +59,7 @@ describe('ErrorUtils', () => { ErrorUtils.addErrorMessage(errors, 'username', 'Username must not contain special characters'); expect(errors).toEqual({ - username: [ - 'Username cannot be empty\nUsername must be at least 6 characters long\nUsername must contain at least one letter\nUsername must not contain special characters', - {isTranslated: true}, - ], + username: 'Username cannot be empty\nUsername must be at least 6 characters long\nUsername must contain at least one letter\nUsername must not contain special characters', }); }); }); From 04512137e4405c537ff75664aa6950b4fc43c929 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 13:51:18 +0700 Subject: [PATCH 030/228] complete fix tes --- tests/unit/ErrorUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ErrorUtilsTest.ts b/tests/unit/ErrorUtilsTest.ts index 6f615ab20436..f5517c56156a 100644 --- a/tests/unit/ErrorUtilsTest.ts +++ b/tests/unit/ErrorUtilsTest.ts @@ -50,7 +50,7 @@ describe('ErrorUtils', () => { ErrorUtils.addErrorMessage(errors, 'username', 'Username must be at least 6 characters long'); ErrorUtils.addErrorMessage(errors, 'username', 'Username must contain at least one letter'); - expect(errors).toEqual({username: ['Username cannot be empty\nUsername must be at least 6 characters long\nUsername must contain at least one letter', {isTranslated: true}]}); + expect(errors).toEqual({username: 'Username cannot be empty\nUsername must be at least 6 characters long\nUsername must contain at least one letter'}); }); test('should append multiple error messages to an existing error message for the same inputID', () => { From 7b70d73d6614270502ce87f49a10eb2474f44826 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 14:54:12 +0700 Subject: [PATCH 031/228] fix translation error --- src/libs/ErrorUtils.ts | 4 ++-- src/pages/ChatFinderPage/index.tsx | 2 +- src/pages/iou/request/MoneyRequestParticipantsSelector.tsx | 2 +- tests/actions/IOUTest.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 2621aa4ebd72..cef907a91cfc 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -126,9 +126,9 @@ function getLatestErrorFieldForAnyField option === action); diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index fcfe94ce2952..63417b7ddd3b 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1862,7 +1862,7 @@ describe('actions/IOU', () => { Onyx.disconnect(connectionID); const updatedAction = Object.values(allActions ?? {}).find((reportAction) => !isEmptyObject(reportAction)); expect(updatedAction?.actionName).toEqual('MODIFIEDEXPENSE'); - expect(Object.values(updatedAction?.errors ?? {})).toEqual(expect.arrayContaining([['iou.error.genericEditFailureMessage', {isTranslated: false}]])); + expect(Object.values(updatedAction?.errors ?? {})).toEqual(Localize.translateLocal('iou.error.genericEditFailureMessage')); resolve(); }, }); @@ -2084,7 +2084,7 @@ describe('actions/IOU', () => { callback: (allActions) => { Onyx.disconnect(connectionID); const erroredAction = Object.values(allActions ?? {}).find((action) => !isEmptyObject(action?.errors)); - expect(Object.values(erroredAction?.errors ?? {})).toEqual(expect.arrayContaining([['iou.error.other', {isTranslated: false}]])); + expect(Object.values(erroredAction?.errors ?? {})).toEqual(Localize.translateLocal('iou.error.other')); resolve(); }, }); From 55a90773cb37ac21fd93a8b43a434e3a7c7b132a Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 15:39:24 +0700 Subject: [PATCH 032/228] remove the use of MaybePhraseKey type --- src/components/AddressForm.tsx | 3 +- src/components/AddressSearch/types.ts | 3 +- src/components/Form/types.ts | 3 +- src/components/RoomNameInput/types.ts | 3 +- src/components/SelectionList/types.ts | 3 +- src/libs/ErrorUtils.ts | 9 +- src/libs/OptionsListUtils.ts | 6 +- src/libs/ValidationUtils.ts | 8 +- src/libs/actions/BankAccounts.ts | 4 +- src/libs/actions/IOU.ts | 82 +++++++++---------- src/libs/actions/Policy/Category.ts | 12 +-- src/libs/actions/Policy/Policy.ts | 50 +++++------ src/libs/actions/Policy/Tag.ts | 12 +-- src/libs/actions/Report.ts | 22 ++--- src/libs/actions/Session/index.ts | 4 +- src/libs/actions/SignInRedirect.ts | 2 +- src/libs/actions/Task.ts | 6 +- src/libs/actions/TaxRate.ts | 10 +-- src/libs/actions/User.ts | 10 +-- src/libs/actions/connections/index.ts | 6 +- src/pages/ChatFinderPage/index.tsx | 3 +- .../BaseOnboardingPersonalDetails.tsx | 4 +- .../OnboardingWork/BaseOnboardingWork.tsx | 2 +- .../AddressFormFields.tsx | 2 +- src/pages/iou/MoneyRequestAmountForm.tsx | 2 +- .../MoneyRequestParticipantsSelector.tsx | 3 +- .../step/IOURequestStepDescription.tsx | 26 +++--- .../settings/Profile/DisplayNamePage.tsx | 4 +- .../Profile/PersonalDetails/LegalNamePage.tsx | 17 ++-- src/pages/settings/Report/RoomNamePage.tsx | 6 +- .../settings/Wallet/AddDebitCardPage.tsx | 2 +- src/pages/tasks/NewTaskDescriptionPage.tsx | 6 +- src/pages/tasks/NewTaskDetailsPage.tsx | 8 +- src/pages/tasks/NewTaskTitlePage.tsx | 2 +- src/pages/tasks/TaskDescriptionPage.tsx | 4 +- src/pages/tasks/TaskTitlePage.tsx | 4 +- src/pages/workspace/WorkspaceNamePage.tsx | 4 +- .../WorkspaceProfileDescriptionPage.tsx | 4 +- .../workspace/categories/CategoryForm.tsx | 8 +- .../workspace/tags/WorkspaceCreateTagPage.tsx | 4 +- tests/actions/IOUTest.ts | 2 +- 41 files changed, 198 insertions(+), 177 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index dc5c47ad182c..8601c3fa1e9f 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -3,7 +3,6 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; -import type {MaybePhraseKey} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; import CONST from '@src/CONST'; @@ -77,7 +76,7 @@ function AddressForm({ const zipSampleFormat = (country && (CONST.COUNTRY_ZIP_REGEX_DATA[country] as CountryZipRegex)?.samples) ?? ''; - const zipFormat: MaybePhraseKey = ['common.zipCodeExampleFormat', {zipSampleFormat}]; + const zipFormat = translate('common.zipCodeExampleFormat', {zipSampleFormat}); const isUSAForm = country === CONST.COUNTRY.US; diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts index bc7acf3f7e40..82e4c3c3fc37 100644 --- a/src/components/AddressSearch/types.ts +++ b/src/components/AddressSearch/types.ts @@ -1,7 +1,6 @@ import type {RefObject} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, View, ViewStyle} from 'react-native'; import type {Place} from 'react-native-google-places-autocomplete'; -import type {MaybePhraseKey} from '@libs/Localize'; import type Locale from '@src/types/onyx/Locale'; import type {Address} from '@src/types/onyx/PrivatePersonalDetails'; @@ -35,7 +34,7 @@ type AddressSearchProps = { onBlur?: () => void; /** Error text to display */ - errorText?: MaybePhraseKey; + errorText?: string; /** Hint text to display */ hint?: string; diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index 331f1c943b30..9aa8bc921164 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -18,7 +18,6 @@ import type StateSelector from '@components/StateSelector'; import type TextInput from '@components/TextInput'; import type TextPicker from '@components/TextPicker'; import type ValuePicker from '@components/ValuePicker'; -import type {MaybePhraseKey} from '@libs/Localize'; import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker'; import type {Country} from '@src/CONST'; import type {OnyxFormKey, OnyxValues} from '@src/ONYXKEYS'; @@ -139,7 +138,7 @@ type FormRef = { type InputRefs = Record>; -type FormInputErrors = Partial, MaybePhraseKey>>; +type FormInputErrors = Partial, string | undefined>>; export type { FormProps, diff --git a/src/components/RoomNameInput/types.ts b/src/components/RoomNameInput/types.ts index 80f08a01e472..763d0f3f2668 100644 --- a/src/components/RoomNameInput/types.ts +++ b/src/components/RoomNameInput/types.ts @@ -1,10 +1,9 @@ import type {NativeSyntheticEvent, ReturnKeyTypeOptions, TextInputFocusEventData, TextInputSubmitEditingEventData} from 'react-native'; -import type {MaybePhraseKey} from '@libs/Localize'; type RoomNameInputProps = { value?: string; disabled?: boolean; - errorText?: MaybePhraseKey; + errorText?: string; onChangeText?: (value: string) => void; onSubmitEditing?: (event: NativeSyntheticEvent) => void; onInputChange?: (value: string) => void; diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index b3bee8fb15c5..f0d2c479c9c5 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -1,6 +1,5 @@ import type {MutableRefObject, ReactElement, ReactNode} from 'react'; import type {GestureResponderEvent, InputModeOptions, LayoutChangeEvent, SectionListData, StyleProp, TextInput, TextStyle, ViewStyle} from 'react-native'; -import type {MaybePhraseKey} from '@libs/Localize'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; import type CONST from '@src/CONST'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; @@ -288,7 +287,7 @@ type BaseSelectionListProps = Partial & { textInputPlaceholder?: string; /** Hint for the text input */ - textInputHint?: MaybePhraseKey; + textInputHint?: string; /** Value for the text input */ textInputValue?: string; diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index cef907a91cfc..44630efe9f45 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -40,10 +40,14 @@ function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatO * Method used to get an error object with microsecond as the key. * @param error - error key or message to be saved */ -function getMicroSecondOnyxError(error: TranslationPaths, _isTranslated = false, errorKey?: number): Errors { +function getMicroSecondOnyxErrorWithTranslationKey(error: TranslationPaths, errorKey?: number): Errors { return {[errorKey ?? DateUtils.getMicroseconds()]: Localize.translateLocal(error)}; } +function getMicroSecondOnyxErrorWithMessage(error: string, errorKey?: number): Errors { + return {[errorKey ?? DateUtils.getMicroseconds()]: error}; +} + /** * Method used to get an error object with microsecond as the key and an object as the value. * @param error - error key or message to be saved @@ -189,7 +193,8 @@ export { getLatestErrorMessage, getLatestErrorMessageField, getLatestErrorFieldForAnyField, - getMicroSecondOnyxError, + getMicroSecondOnyxErrorWithTranslationKey, + getMicroSecondOnyxErrorWithMessage, getMicroSecondOnyxErrorObject, isReceiptError, }; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index f321c10c686e..b23ce6f23847 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -532,14 +532,14 @@ function getAllReportErrors(report: OnyxEntry, reportActions: OnyxEntry< const transactionID = parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction?.originalMessage?.IOUTransactionID : null; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; if (TransactionUtils.hasMissingSmartscanFields(transaction ?? null) && !ReportUtils.isSettled(transaction?.reportID)) { - reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage'); + reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericSmartscanFailureMessage'); } } else if ((ReportUtils.isIOUReport(report) || ReportUtils.isExpenseReport(report)) && report?.ownerAccountID === currentUserAccountID) { if (ReportUtils.hasMissingSmartscanFields(report?.reportID ?? '') && !ReportUtils.isSettled(report?.reportID)) { - reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage'); + reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericSmartscanFailureMessage'); } } else if (ReportUtils.hasSmartscanError(Object.values(reportActions ?? {}))) { - reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxError('report.genericSmartscanFailureMessage'); + reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericSmartscanFailureMessage'); } // All error objects related to the report. Each object in the sources contains error messages keyed by microtime const errorSources = { diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 7fe73526a1a5..c9144547833a 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -191,12 +191,12 @@ function meetsMaximumAgeRequirement(date: string): boolean { /** * Validate that given date is in a specified range of years before now. */ -function getAgeRequirementError(date: string, minimumAge: number, maximumAge: number): Localize.MaybePhraseKey { +function getAgeRequirementError(date: string, minimumAge: number, maximumAge: number): string { const currentDate = startOfDay(new Date()); const testDate = parse(date, CONST.DATE.FNS_FORMAT_STRING, currentDate); if (!isValid(testDate)) { - return 'common.error.dateInvalid'; + return Localize.translateLocal('common.error.dateInvalid'); } const maximalDate = subYears(currentDate, minimumAge); @@ -207,10 +207,10 @@ function getAgeRequirementError(date: string, minimumAge: number, maximumAge: nu } if (isSameDay(testDate, maximalDate) || isAfter(testDate, maximalDate)) { - return ['privatePersonalDetails.error.dateShouldBeBefore', {dateString: format(maximalDate, CONST.DATE.FNS_FORMAT_STRING)}]; + return Localize.translateLocal('privatePersonalDetails.error.dateShouldBeBefore', {dateString: format(maximalDate, CONST.DATE.FNS_FORMAT_STRING)}); } - return ['privatePersonalDetails.error.dateShouldBeAfter', {dateString: format(minimalDate, CONST.DATE.FNS_FORMAT_STRING)}]; + return Localize.translateLocal('privatePersonalDetails.error.dateShouldBeAfter', {dateString: format(minimalDate, CONST.DATE.FNS_FORMAT_STRING)}); } /** diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index cddb2c371a60..89d0296994e3 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -163,7 +163,7 @@ function getVBBADataForOnyx(currentStep?: BankAccountStep): OnyxData { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, value: { isLoading: false, - errors: ErrorUtils.getMicroSecondOnyxError('walletPage.addBankAccountFailure'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('walletPage.addBankAccountFailure'), }, }, ], @@ -239,7 +239,7 @@ function addPersonalBankAccount(account: PlaidBankAccount) { key: ONYXKEYS.PERSONAL_BANK_ACCOUNT, value: { isLoading: false, - errors: ErrorUtils.getMicroSecondOnyxError('walletPage.addBankAccountFailure'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('walletPage.addBankAccountFailure'), }, }, ], diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index dfaea862b888..0b4991ec2138 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -444,7 +444,7 @@ function updateDistanceRequestRate(transactionID: string, rateID: string, policy /** Helper function to get the receipt error for expenses, or the generic error if there's no receipt */ function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true, errorKey?: number): Errors | ErrorFields { return isEmptyObject(receipt) || !isScanRequest - ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage', false, errorKey) + ? ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage', errorKey) : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source?.toString() ?? '', filename: filename ?? ''}, errorKey); } @@ -740,7 +740,7 @@ function buildOnyxDataForMoneyRequest( ...(isNewChatReport ? { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, } : {}), @@ -752,7 +752,7 @@ function buildOnyxDataForMoneyRequest( value: { pendingFields: null, errorFields: { - ...(shouldCreateNewMoneyRequestReport ? {createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage')} : {}), + ...(shouldCreateNewMoneyRequestReport ? {createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage')} : {}), }, }, }, @@ -764,7 +764,7 @@ function buildOnyxDataForMoneyRequest( errorFields: existingTransactionThreadReport ? null : { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -791,7 +791,7 @@ function buildOnyxDataForMoneyRequest( errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest, errorKey), }, [iouAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, } : { @@ -811,7 +811,7 @@ function buildOnyxDataForMoneyRequest( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, }); @@ -1072,7 +1072,7 @@ function buildOnyxDataForInvoice( ...(isNewChatReport ? { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, } : {}), @@ -1084,7 +1084,7 @@ function buildOnyxDataForInvoice( value: { pendingFields: null, errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -1093,7 +1093,7 @@ function buildOnyxDataForInvoice( key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, value: { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -1101,7 +1101,7 @@ function buildOnyxDataForInvoice( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, value: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateInvoiceFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateInvoiceFailureMessage'), pendingAction: null, pendingFields: clearedPendingFields, }, @@ -1116,7 +1116,7 @@ function buildOnyxDataForInvoice( errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, false, errorKey), }, [iouAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateInvoiceFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateInvoiceFailureMessage'), }, }, }, @@ -1125,7 +1125,7 @@ function buildOnyxDataForInvoice( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateInvoiceFailureMessage', false, errorKey), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateInvoiceFailureMessage', errorKey), }, }, }, @@ -1413,7 +1413,7 @@ function buildOnyxDataForTrackExpense( value: { pendingFields: null, errorFields: { - ...(shouldCreateNewMoneyRequestReport ? {createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage')} : {}), + ...(shouldCreateNewMoneyRequestReport ? {createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage')} : {}), }, }, }, @@ -1429,7 +1429,7 @@ function buildOnyxDataForTrackExpense( errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), }, [iouAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, } : { @@ -1474,7 +1474,7 @@ function buildOnyxDataForTrackExpense( errorFields: existingTransactionThreadReport ? null : { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -1494,7 +1494,7 @@ function buildOnyxDataForTrackExpense( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, }, @@ -1680,7 +1680,7 @@ function getDeleteTrackExpenseInformation( [reportAction.reportActionID]: { ...reportAction, pendingAction: null, - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericDeleteFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericDeleteFailureMessage'), }, }, }, @@ -2518,7 +2518,7 @@ function getUpdateMoneyRequestParams( value: { [updatedReportAction.reportActionID]: { ...(updatedReportAction as OnyxTypes.ReportAction), - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericEditFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericEditFailureMessage'), }, }, }); @@ -2802,7 +2802,7 @@ function getUpdateTrackExpenseParams( value: { [updatedReportAction.reportActionID]: { ...(updatedReportAction as OnyxTypes.ReportAction), - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericEditFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericEditFailureMessage'), }, }, }); @@ -3130,7 +3130,7 @@ const getConvertTrackedExpenseInformation = ( value: { [modifiedExpenseReportAction.reportActionID]: { ...(modifiedExpenseReportAction as OnyxTypes.ReportAction), - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericEditFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericEditFailureMessage'), }, }, }); @@ -3934,7 +3934,7 @@ function createSplitsAndOnyxData( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, value: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, ]; @@ -3945,7 +3945,7 @@ function createSplitsAndOnyxData( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { [splitIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, }); @@ -3956,7 +3956,7 @@ function createSplitsAndOnyxData( key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, value: { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -3965,7 +3965,7 @@ function createSplitsAndOnyxData( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { [splitIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, }, @@ -4480,7 +4480,7 @@ function startSplitBill({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, value: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, ]; @@ -4502,7 +4502,7 @@ function startSplitBill({ key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, value: { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -4511,7 +4511,7 @@ function startSplitBill({ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { [splitChatCreatedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, [splitIOUReportAction.reportActionID]: { errors: getReceiptError(receipt, filename), @@ -4686,7 +4686,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: { ...unmodifiedTransaction, - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, { @@ -4695,7 +4695,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA value: { [reportAction.reportActionID]: { ...reportAction, - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, }, @@ -5060,7 +5060,7 @@ function editRegularMoneyRequest( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { [updatedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericEditFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericEditFailureMessage'), }, }, }, @@ -5741,7 +5741,7 @@ function getSendMoneyParams( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${optimisticTransaction.transactionID}`, value: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.other'), }, }, { @@ -5749,7 +5749,7 @@ function getSendMoneyParams( key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTransactionThread.reportID}`, value: { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -5758,7 +5758,7 @@ function getSendMoneyParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTransactionThread.reportID}`, value: { [optimisticCreatedActionForTransactionThread.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, }, @@ -5777,7 +5777,7 @@ function getSendMoneyParams( key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, value: { errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + createChat: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -5786,7 +5786,7 @@ function getSendMoneyParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticIOUReport.reportID}`, value: { [optimisticIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericCreateFailureMessage'), }, }, }, @@ -5802,7 +5802,7 @@ function getSendMoneyParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticIOUReport.reportID}`, value: { [optimisticIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.other'), }, }, }); @@ -5950,7 +5950,7 @@ function getPayMoneyRequestParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, value: { [optimisticIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.other'), }, }, }, @@ -6190,7 +6190,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { [optimisticApprovedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.other'), }, }, }, @@ -6344,7 +6344,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { [optimisticSubmittedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.other'), }, }, }); @@ -6432,7 +6432,7 @@ function cancelPayment(expenseReport: OnyxTypes.Report, chatReport: OnyxTypes.Re key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { [optimisticReportAction.reportActionID ?? '']: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.other'), }, }, }, @@ -6505,7 +6505,7 @@ function detachReceipt(transactionID: string) { key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: { ...transaction, - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.receiptDeleteFailureError'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.receiptDeleteFailureError'), }, }, ]; diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index 82bca6dc7c22..ec540e624ffb 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -92,7 +92,7 @@ function buildOptimisticPolicyCategories(policyID: string, categories: readonly (acc, category) => ({ ...acc, [category]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.createFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.categories.createFailureMessage'), pendingAction: null, }, }), @@ -211,7 +211,7 @@ function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Recor acc[key] = { ...policyCategories[key], ...categoriesToUpdate[key], - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.categories.updateFailureMessage'), pendingFields: { enabled: null, }, @@ -327,7 +327,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string ...policyCategoryToUpdate, name: policyCategory.oldName, unencodedName: decodeURIComponent(policyCategory.oldName), - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.categories.updateFailureMessage'), pendingAction: null, }, }, @@ -380,7 +380,7 @@ function setWorkspaceRequiresCategory(policyID: string, requiresCategory: boolea key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { requiresCategory: !requiresCategory, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.categories.updateFailureMessage'), pendingFields: { requiresCategory: null, }, @@ -446,7 +446,7 @@ function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: stri value: categoryNamesToDelete.reduce>>((acc, categoryName) => { acc[categoryName] = { pendingAction: null, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.deleteFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.categories.deleteFailureMessage'), }; return acc; }, {}), @@ -579,7 +579,7 @@ function setPolicyDistanceRatesDefaultCategory(policyID: string, currentCustomUn customUnits: { [currentCustomUnit.customUnitID]: { ...currentCustomUnit, - errorFields: {defaultCategory: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {defaultCategory: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, pendingFields: {defaultCategory: null}, }, }, diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index fc2a8ad7970e..9bdc433b7e71 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -467,7 +467,7 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency }, autoReportingFrequency: policy.autoReportingFrequency ?? null, pendingFields: {autoReporting: null}, - errorFields: {autoReporting: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingErrorMessage')}, + errorFields: {autoReporting: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsDelayedSubmissionPage.autoReportingErrorMessage')}, }, }, ]; @@ -508,7 +508,7 @@ function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf value: { autoReportingFrequency: policy.autoReportingFrequency ?? null, pendingFields: {autoReportingFrequency: null}, - errorFields: {autoReportingFrequency: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingFrequencyErrorMessage')}, + errorFields: {autoReportingFrequency: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsDelayedSubmissionPage.autoReportingFrequencyErrorMessage')}, }, }, ]; @@ -549,7 +549,7 @@ function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingO value: { autoReportingOffset: policy.autoReportingOffset ?? null, pendingFields: {autoReportingOffset: null}, - errorFields: {autoReportingOffset: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.monthlyOffsetErrorMessage')}, + errorFields: {autoReportingOffset: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsDelayedSubmissionPage.monthlyOffsetErrorMessage')}, }, }, ]; @@ -595,7 +595,7 @@ function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMo approver: policy.approver ?? null, approvalMode: policy.approvalMode ?? null, pendingFields: {approvalMode: null}, - errorFields: {approvalMode: ErrorUtils.getMicroSecondOnyxError('workflowsApprovalPage.genericErrorMessage')}, + errorFields: {approvalMode: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsApprovalPage.genericErrorMessage')}, }, }, ]; @@ -653,7 +653,7 @@ function setWorkspacePayer(policyID: string, reimburserEmail: string) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, - errorFields: {reimburser: ErrorUtils.getMicroSecondOnyxError('workflowsPayerPage.genericErrorMessage')}, + errorFields: {reimburser: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workflowsPayerPage.genericErrorMessage')}, pendingFields: {reimburser: null}, }, }, @@ -713,7 +713,7 @@ function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueO isLoadingWorkspaceReimbursement: false, reimbursementChoice: policy.reimbursementChoice ?? null, achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, - errorFields: {reimbursementChoice: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {reimbursementChoice: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, pendingFields: {reimbursementChoice: null}, }, }, @@ -810,7 +810,7 @@ function removeMembers(accountIDs: number[], policyID: string) { emailList.forEach((email) => { optimisticMembersState[email] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; successMembersState[email] = null; - failureMembersState[email] = {errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')}; + failureMembersState[email] = {errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.people.error.genericRemove')}; }); const optimisticData: OnyxUpdate[] = [ @@ -969,7 +969,7 @@ function leaveWorkspace(policyID: string) { pendingAction: policy?.pendingAction, employeeList: { [sessionEmail]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.people.error.genericRemove'), }, }, }, @@ -1075,7 +1075,7 @@ function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newR key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { employeeList: previousEmployeeList, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.editor.genericFailureMessage'), }, }, ]; @@ -1363,7 +1363,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount optimisticMembersState[email] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, role: CONST.POLICY.ROLE.USER}; successMembersState[email] = {pendingAction: null}; failureMembersState[email] = { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericAdd'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.people.error.genericAdd'), }; }); @@ -1441,7 +1441,7 @@ function inviteMemberToWorkspace(policyID: string, inviterEmail: string) { { onyxMethod: Onyx.METHOD.MERGE, key: memberJoinKey, - value: {...failureMembersState, errors: ErrorUtils.getMicroSecondOnyxError('common.genericEditFailureMessage')}, + value: {...failureMembersState, errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, ]; @@ -1535,7 +1535,7 @@ function deleteWorkspaceAvatar(policyID: string) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { errorFields: { - avatarURL: ErrorUtils.getMicroSecondOnyxError('avatarWithImagePicker.deleteWorkspaceError'), + avatarURL: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('avatarWithImagePicker.deleteWorkspaceError'), }, }, }, @@ -1594,7 +1594,7 @@ function updateGeneralSettings(policyID: string, name: string, currencyValue?: s failureRates[rateID] = { ...currentRates[rateID], pendingFields: {currency: null}, - errorFields: {currency: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {currency: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }; } } @@ -1655,7 +1655,7 @@ function updateGeneralSettings(policyID: string, name: string, currencyValue?: s key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { errorFields: { - generalSettings: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), + generalSettings: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.editor.genericFailureMessage'), }, ...(customUnitID && { customUnits: { @@ -1720,7 +1720,7 @@ function updateWorkspaceDescription(policyID: string, description: string, curre key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { errorFields: { - description: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), + description: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.editor.genericFailureMessage'), }, }, }, @@ -1869,7 +1869,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C rates: { [newCustomUnit.rates.customUnitRateID]: { ...currentCustomUnit.rates, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.reimburse.updateCustomUnitError'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.reimburse.updateCustomUnitError'), }, }, }, @@ -2096,7 +2096,7 @@ function buildOptimisticPolicyCategories(policyID: string, categories: readonly (acc, category) => ({ ...acc, [category]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.createFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.categories.createFailureMessage'), pendingAction: null, }, }), @@ -3635,7 +3635,7 @@ function createPolicyDistanceRate(policyID: string, customUnitID: string, custom [customUnitID]: { rates: { [customUnitRate.customUnitRateID ?? '']: { - errors: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), }, }, }, @@ -3753,7 +3753,7 @@ function setPolicyDistanceRatesUnit(policyID: string, currentCustomUnit: CustomU customUnits: { [currentCustomUnit.customUnitID]: { ...currentCustomUnit, - errorFields: {attributes: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {attributes: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, pendingFields: {attributes: null}, }, }, @@ -3799,7 +3799,7 @@ function updatePolicyDistanceRateValue(policyID: string, customUnit: CustomUnit, failureRates[rateID] = { ...currentRates[rateID], pendingFields: {rate: null}, - errorFields: {rate: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {rate: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }; } } @@ -3870,7 +3870,7 @@ function setPolicyDistanceRatesEnabled(policyID: string, customUnit: CustomUnit, failureRates[rateID] = { ...currentRates[rateID], pendingFields: {enabled: null}, - errorFields: {enabled: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {enabled: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }; } } @@ -3941,7 +3941,7 @@ function deletePolicyDistanceRates(policyID: string, customUnit: CustomUnit, rat failureRates[rateID] = { ...currentRates[rateID], pendingAction: null, - errors: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), }; } else { optimisticRates[rateID] = currentRates[rateID]; @@ -4037,7 +4037,7 @@ function setPolicyCustomTaxName(policyID: string, customTaxName: string) { taxRates: { name: originalCustomTaxName, pendingFields: {name: null}, - errorFields: {name: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {name: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, }, }, @@ -4089,7 +4089,7 @@ function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { taxRates: { defaultExternalID: originalDefaultExternalID, pendingFields: {defaultExternalID: null}, - errorFields: {defaultExternalID: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {defaultExternalID: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, }, }, @@ -4141,7 +4141,7 @@ function setForeignCurrencyDefault(policyID: string, taxCode: string) { taxRates: { foreignTaxDefault: originalDefaultForeignCurrencyID, pendingFields: {foreignTaxDefault: null}, - errorFields: {foreignTaxDefault: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + errorFields: {foreignTaxDefault: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, }, }, diff --git a/src/libs/actions/Policy/Tag.ts b/src/libs/actions/Policy/Tag.ts index 7760f29d6703..512300591fd1 100644 --- a/src/libs/actions/Policy/Tag.ts +++ b/src/libs/actions/Policy/Tag.ts @@ -160,7 +160,7 @@ function createPolicyTag(policyID: string, tagName: string) { [policyTag.name]: { tags: { [newTagName]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.tags.genericFailureMessage'), }, }, }, @@ -242,7 +242,7 @@ function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record>>>((acc, tagName) => { - acc[tagName] = {pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.deleteFailureMessage')}; + acc[tagName] = {pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.tags.deleteFailureMessage')}; return acc; }, {}), }, @@ -416,7 +416,7 @@ function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: [newTagName]: null, [oldTagName]: { ...oldTag, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.tags.genericFailureMessage'), }, }, }, @@ -514,7 +514,7 @@ function renamePolicyTaglist(policyID: string, policyTagListName: {oldName: stri value: { errors: { [oldName]: oldName, - [newName]: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + [newName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.tags.genericFailureMessage'), }, [newName]: null, [oldName]: oldPolicyTags, @@ -566,7 +566,7 @@ function setPolicyRequiresTag(policyID: string, requiresTag: boolean) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { requiresTag: !requiresTag, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.tags.genericFailureMessage'), pendingFields: { requiresTag: null, }, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 33da5f6336a0..0af60ee812d6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -569,7 +569,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { failureReportActions[actionKey] = { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style ...(action as OptimisticAddCommentReportAction), - errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), }; }); @@ -1746,7 +1746,7 @@ function updateReportName(reportID: string, value: string, previousValue: string reportName: null, }, errorFields: { - reportName: ErrorUtils.getMicroSecondOnyxError('report.genericUpdateReporNameEditFailureMessage'), + reportName: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericUpdateReporNameEditFailureMessage'), }, }, }, @@ -1828,7 +1828,7 @@ function updateReportField(reportID: string, reportField: PolicyReportField, pre [fieldKey]: null, }, errorFields: { - [fieldKey]: ErrorUtils.getMicroSecondOnyxError('report.genericUpdateReportFieldFailureMessage'), + [fieldKey]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericUpdateReportFieldFailureMessage'), }, }, }, @@ -1897,7 +1897,7 @@ function deleteReportField(reportID: string, reportField: PolicyReportField) { [fieldKey]: null, }, errorFields: { - [fieldKey]: ErrorUtils.getMicroSecondOnyxError('report.genericUpdateReportFieldFailureMessage'), + [fieldKey]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericUpdateReportFieldFailureMessage'), }, }, }, @@ -2074,7 +2074,7 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { key: `${ONYXKEYS.COLLECTION.REPORT}${policyReport.reportID}`, value: { errorFields: { - addWorkspaceRoom: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + addWorkspaceRoom: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericCreateReportFailureMessage'), }, }, }, @@ -2755,7 +2755,7 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: InvitedEmails } return { ...pendingChatMember, - errors: ErrorUtils.getMicroSecondOnyxError('roomMembersPage.error.genericAdd'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('roomMembersPage.error.genericAdd'), }; }) ?? null, }, @@ -3047,7 +3047,7 @@ const updatePrivateNotes = (reportID: string, accountID: number, note: string) = value: { privateNotes: { [accountID]: { - errors: ErrorUtils.getMicroSecondOnyxError('privateNotes.error.genericFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('privateNotes.error.genericFailureMessage'), }, }, }, @@ -3271,7 +3271,7 @@ function completeOnboarding( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: { [taskReportAction.reportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), } as ReportAction, }, }, @@ -3353,10 +3353,10 @@ function completeOnboarding( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: { [introductionCommentAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), } as ReportAction, [textCommentAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), } as ReportAction, }, }, @@ -3394,7 +3394,7 @@ function completeOnboarding( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: { [videoCommentAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), } as ReportAction, }, }); diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 303517558206..b2a433864723 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -392,7 +392,7 @@ function signInAttemptState(): OnyxData { value: { isLoading: false, loadingForm: null, - errors: ErrorUtils.getMicroSecondOnyxError('loginForm.cannotGetAccountDetails'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('loginForm.cannotGetAccountDetails'), }, }, ], @@ -673,7 +673,7 @@ function clearAccountMessages() { } function setAccountError(error: string) { - Onyx.merge(ONYXKEYS.ACCOUNT, {errors: ErrorUtils.getMicroSecondOnyxError(error, true)}); + Onyx.merge(ONYXKEYS.ACCOUNT, {errors: ErrorUtils.getMicroSecondOnyxErrorWithMessage(error)}); } // It's necessary to throttle requests to reauthenticate since calling this multiple times will cause Pusher to diff --git a/src/libs/actions/SignInRedirect.ts b/src/libs/actions/SignInRedirect.ts index c6d4ffcaa30f..e72a4b011074 100644 --- a/src/libs/actions/SignInRedirect.ts +++ b/src/libs/actions/SignInRedirect.ts @@ -34,7 +34,7 @@ function clearStorageAndRedirect(errorMessage?: string): Promise { } // `Onyx.clear` reinitializes the Onyx instance with initial values so use `Onyx.merge` instead of `Onyx.set` - Onyx.merge(ONYXKEYS.SESSION, {errors: ErrorUtils.getMicroSecondOnyxError(errorMessage)}); + Onyx.merge(ONYXKEYS.SESSION, {errors: ErrorUtils.getMicroSecondOnyxErrorWithMessage(errorMessage)}); }); } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 55d898d3d4f3..143b30ac6406 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -269,7 +269,7 @@ function createTaskAndNavigate( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, value: { [optimisticAddCommentReport.reportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('task.genericCreateTaskFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('task.genericCreateTaskFailureMessage'), }, }, }); @@ -347,7 +347,7 @@ function completeTask(taskReport: OnyxEntry) { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReportID}`, value: { [completedTaskReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('task.messages.error'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('task.messages.error'), }, }, }, @@ -416,7 +416,7 @@ function reopenTask(taskReport: OnyxEntry) { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReportID}`, value: { [reopenedTaskReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('task.messages.error'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('task.messages.error'), }, }, }, diff --git a/src/libs/actions/TaxRate.ts b/src/libs/actions/TaxRate.ts index cdf5bd490c29..b4624a48fc7b 100644 --- a/src/libs/actions/TaxRate.ts +++ b/src/libs/actions/TaxRate.ts @@ -124,7 +124,7 @@ function createPolicyTax(policyID: string, taxRate: TaxRate) { taxRates: { taxes: { [taxRate.code]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.taxes.error.createFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.taxes.error.createFailureMessage'), }, }, }, @@ -232,7 +232,7 @@ function setPolicyTaxesEnabled(policyID: string, taxesIDsToUpdate: string[], isE acc[taxID] = { isDisabled: !!originalTaxes[taxID].isDisabled, pendingFields: {isDisabled: null}, - errorFields: {isDisabled: ErrorUtils.getMicroSecondOnyxError('workspace.taxes.error.updateFailureMessage')}, + errorFields: {isDisabled: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.taxes.error.updateFailureMessage')}, }; return acc; }, {}), @@ -313,7 +313,7 @@ function deletePolicyTaxes(policyID: string, taxesToDelete: string[]) { taxes: taxesToDelete.reduce((acc, taxID) => { acc[taxID] = { pendingAction: null, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.taxes.error.deleteFailureMessage'), + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.taxes.error.deleteFailureMessage'), }; return acc; }, {}), @@ -377,7 +377,7 @@ function updatePolicyTaxValue(policyID: string, taxID: string, taxValue: number) [taxID]: { value: originalTaxRate.value, pendingFields: {value: null}, - errorFields: {value: ErrorUtils.getMicroSecondOnyxError('workspace.taxes.error.updateFailureMessage')}, + errorFields: {value: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.taxes.error.updateFailureMessage')}, }, }, }, @@ -439,7 +439,7 @@ function renamePolicyTax(policyID: string, taxID: string, newName: string) { [taxID]: { name: originalTaxRate.name, pendingFields: {name: null}, - errorFields: {name: ErrorUtils.getMicroSecondOnyxError('workspace.taxes.error.updateFailureMessage')}, + errorFields: {name: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.taxes.error.updateFailureMessage')}, }, }, }, diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index f7e90f775b65..67c9a38afb35 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -156,7 +156,7 @@ function requestContactMethodValidateCode(contactMethod: string) { [contactMethod]: { validateCodeSent: false, errorFields: { - validateCodeSent: ErrorUtils.getMicroSecondOnyxError('contacts.genericFailureMessages.requestContactMethodValidateCode'), + validateCodeSent: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('contacts.genericFailureMessages.requestContactMethodValidateCode'), }, pendingFields: { validateCodeSent: null, @@ -239,7 +239,7 @@ function deleteContactMethod(contactMethod: string, loginList: Record [settingName, null])), - errorFields: Object.fromEntries(Object.keys(configUpdate).map((settingName) => [settingName, ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')])), + errorFields: Object.fromEntries( + Object.keys(configUpdate).map((settingName) => [settingName, ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')]), + ), }, }, }, diff --git a/src/pages/ChatFinderPage/index.tsx b/src/pages/ChatFinderPage/index.tsx index 35417cf4ecd4..9ec8a24d23c3 100644 --- a/src/pages/ChatFinderPage/index.tsx +++ b/src/pages/ChatFinderPage/index.tsx @@ -13,7 +13,6 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useDismissedReferralBanners from '@hooks/useDismissedReferralBanners'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import type {MaybePhraseKey} from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; import type {RootStackParamList} from '@libs/Navigation/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; @@ -59,7 +58,7 @@ function ChatFinderPage({betas, isSearchingForReports, navigation}: ChatFinderPa shouldInitialize: isScreenTransitionEnd, }); - const offlineMessage: MaybePhraseKey = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; + const offlineMessage: string = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const [, debouncedSearchValueInServer, setSearchValueInServer] = useDebouncedState('', 500); diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 2260fc872270..a7a78aa9048c 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -96,7 +96,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat if (!ValidationUtils.isValidDisplayName(values.firstName)) { ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.hasInvalidCharacter'); } else if (values.firstName.length > CONST.DISPLAY_NAME.MAX_LENGTH) { - ErrorUtils.addErrorMessage(errors, 'firstName', ['common.error.characterLimitExceedCounter', {length: values.firstName.length, limit: CONST.DISPLAY_NAME.MAX_LENGTH}]); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('common.error.characterLimitExceedCounter', {length: values.firstName.length, limit: CONST.DISPLAY_NAME.MAX_LENGTH})); } if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.containsReservedWord'); @@ -106,7 +106,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat if (!ValidationUtils.isValidDisplayName(values.lastName)) { ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.hasInvalidCharacter'); } else if (values.lastName.length > CONST.DISPLAY_NAME.MAX_LENGTH) { - ErrorUtils.addErrorMessage(errors, 'lastName', ['common.error.characterLimitExceedCounter', {length: values.lastName.length, limit: CONST.DISPLAY_NAME.MAX_LENGTH}]); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('common.error.characterLimitExceedCounter', {length: values.lastName.length, limit: CONST.DISPLAY_NAME.MAX_LENGTH})); } if (ValidationUtils.doesContainReservedWord(values.lastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.containsReservedWord'); diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index b0e01d0c8caa..cdaa264682d1 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -63,7 +63,7 @@ function BaseOnboardingWork({shouldUseNativeStyles, onboardingPurposeSelected, o } else if ([...work].length > CONST.TITLE_CHARACTER_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 // code units. - ErrorUtils.addErrorMessage(errors, 'work', ['common.error.characterLimitExceedCounter', {length: [...work].length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'work', translate('common.error.characterLimitExceedCounter', {length: [...work].length, limit: CONST.TITLE_CHARACTER_LIMIT})); } return errors; diff --git a/src/pages/ReimbursementAccount/AddressFormFields.tsx b/src/pages/ReimbursementAccount/AddressFormFields.tsx index 2133c4c5bf2e..c586c71f3d47 100644 --- a/src/pages/ReimbursementAccount/AddressFormFields.tsx +++ b/src/pages/ReimbursementAccount/AddressFormFields.tsx @@ -96,7 +96,7 @@ function AddressFormFields({shouldSaveDraft = false, defaultValues, values, erro onChangeText={(value) => onFieldChange?.({zipCode: value})} errorText={errors?.zipCode ? translate('bankAccount.error.zipCode') : ''} maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} - hint={['common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}]} + hint={translate('common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples})} containerStyles={styles.mt3} /> diff --git a/src/pages/iou/MoneyRequestAmountForm.tsx b/src/pages/iou/MoneyRequestAmountForm.tsx index 07037a09df11..01a0565c4f96 100644 --- a/src/pages/iou/MoneyRequestAmountForm.tsx +++ b/src/pages/iou/MoneyRequestAmountForm.tsx @@ -213,7 +213,7 @@ function MoneyRequestAmountForm( } if (isTaxAmountInvalid(currentAmount, taxAmount, isTaxAmountForm)) { - setFormError([translate('iou.error.invalidTaxAmount'), {amount: formattedTaxAmount}]); + setFormError(translate('iou.error.invalidTaxAmount', {amount: formattedTaxAmount})); return; } diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 2000652d9511..113ece3eca35 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -19,7 +19,6 @@ import usePermissions from '@hooks/usePermissions'; import useScreenWrapperTranstionStatus from '@hooks/useScreenWrapperTransitionStatus'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import type {MaybePhraseKey} from '@libs/Localize'; import type {Options} from '@libs/OptionsListUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as Policy from '@userActions/Policy/Policy'; @@ -65,7 +64,7 @@ function MoneyRequestParticipantsSelector({participants = [], onFinish, onPartic shouldInitialize: didScreenTransitionEnd, }); - const offlineMessage: MaybePhraseKey = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; + const offlineMessage: string = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; const isIOUSplit = iouType === CONST.IOU.TYPE.SPLIT; const isCategorizeOrShareAction = [CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].some((option) => option === action); diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index e8388a92e602..cbee039e2b28 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -94,18 +94,22 @@ function IOURequestStepDescription({ /** * @returns - An object containing the errors for each inputID */ - const validate = useCallback((values: FormOnyxValues): FormInputErrors => { - const errors = {}; - - if (values.moneyRequestComment.length > CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'moneyRequestComment', [ - 'common.error.characterLimitExceedCounter', - {length: values.moneyRequestComment.length, limit: CONST.DESCRIPTION_LIMIT}, - ]); - } + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = {}; + + if (values.moneyRequestComment.length > CONST.DESCRIPTION_LIMIT) { + ErrorUtils.addErrorMessage( + errors, + 'moneyRequestComment', + translate('common.error.characterLimitExceedCounter', {length: values.moneyRequestComment.length, limit: CONST.DESCRIPTION_LIMIT}), + ); + } - return errors; - }, []); + return errors; + }, + [translate], + ); const navigateBack = () => { Navigation.goBack(backTo); diff --git a/src/pages/settings/Profile/DisplayNamePage.tsx b/src/pages/settings/Profile/DisplayNamePage.tsx index 3ab443b23561..830c06c9a254 100644 --- a/src/pages/settings/Profile/DisplayNamePage.tsx +++ b/src/pages/settings/Profile/DisplayNamePage.tsx @@ -49,7 +49,7 @@ function DisplayNamePage({isLoadingApp = true, currentUserPersonalDetails}: Disp if (!ValidationUtils.isValidDisplayName(values.firstName)) { ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.hasInvalidCharacter'); } else if (values.firstName.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'firstName', ['common.error.characterLimitExceedCounter', {length: values.firstName.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('common.error.characterLimitExceedCounter', {length: values.firstName.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.containsReservedWord'); @@ -59,7 +59,7 @@ function DisplayNamePage({isLoadingApp = true, currentUserPersonalDetails}: Disp if (!ValidationUtils.isValidDisplayName(values.lastName)) { ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.hasInvalidCharacter'); } else if (values.lastName.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'lastName', ['common.error.characterLimitExceedCounter', {length: values.lastName.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('common.error.characterLimitExceedCounter', {length: values.lastName.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } if (ValidationUtils.doesContainReservedWord(values.lastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.containsReservedWord'); diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx index 085c70b47937..1242618d1407 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx @@ -49,10 +49,11 @@ function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNameP } else if (!values.legalFirstName) { errors.legalFirstName = 'common.error.fieldRequired'; } else if (values.legalFirstName.length > CONST.LEGAL_NAME.MAX_LENGTH) { - ErrorUtils.addErrorMessage(errors, 'legalFirstName', [ - 'common.error.characterLimitExceedCounter', - {length: values.legalFirstName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}, - ]); + ErrorUtils.addErrorMessage( + errors, + 'legalFirstName', + translate('common.error.characterLimitExceedCounter', {length: values.legalFirstName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}), + ); } if (ValidationUtils.doesContainReservedWord(values.legalFirstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { ErrorUtils.addErrorMessage(errors, 'legalFirstName', 'personalDetails.error.containsReservedWord'); @@ -65,7 +66,11 @@ function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNameP } else if (!values.legalLastName) { errors.legalLastName = 'common.error.fieldRequired'; } else if (values.legalLastName.length > CONST.LEGAL_NAME.MAX_LENGTH) { - ErrorUtils.addErrorMessage(errors, 'legalLastName', ['common.error.characterLimitExceedCounter', {length: values.legalLastName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}]); + ErrorUtils.addErrorMessage( + errors, + 'legalLastName', + translate('common.error.characterLimitExceedCounter', {length: values.legalLastName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}), + ); } if (ValidationUtils.doesContainReservedWord(values.legalLastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { ErrorUtils.addErrorMessage(errors, 'legalLastName', 'personalDetails.error.containsReservedWord'); @@ -73,7 +78,7 @@ function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNameP } return errors; - }, []); + }, [translate]); return ( CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'roomName', ['common.error.characterLimitExceedCounter', {length: values.roomName.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('common.error.characterLimitExceedCounter', {length: values.roomName.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } return errors; }, - [report, reports], + [report, reports, translate], ); return ( diff --git a/src/pages/settings/Wallet/AddDebitCardPage.tsx b/src/pages/settings/Wallet/AddDebitCardPage.tsx index b3bc1d839727..a5085b3d2315 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.tsx +++ b/src/pages/settings/Wallet/AddDebitCardPage.tsx @@ -199,7 +199,7 @@ function DebitCardPage({formData}: DebitCardPageProps) { role={CONST.ROLE.PRESENTATION} inputMode={CONST.INPUT_MODE.NUMERIC} maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} - hint={['common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples}]} + hint={translate('common.zipCodeExampleFormat', {zipSampleFormat: CONST.COUNTRY_ZIP_REGEX_DATA.US.samples})} containerStyles={[styles.mt4]} /> diff --git a/src/pages/tasks/NewTaskDescriptionPage.tsx b/src/pages/tasks/NewTaskDescriptionPage.tsx index 93ad5e63f3d7..2f186149f9ae 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.tsx +++ b/src/pages/tasks/NewTaskDescriptionPage.tsx @@ -49,7 +49,11 @@ function NewTaskDescriptionPage({task}: NewTaskDescriptionPageProps) { const errors = {}; if (values.taskDescription.length > CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'taskDescription', ['common.error.characterLimitExceedCounter', {length: values.taskDescription.length, limit: CONST.DESCRIPTION_LIMIT}]); + ErrorUtils.addErrorMessage( + errors, + 'taskDescription', + translate('common.error.characterLimitExceedCounter', {length: values.taskDescription.length, limit: CONST.DESCRIPTION_LIMIT}), + ); } return errors; diff --git a/src/pages/tasks/NewTaskDetailsPage.tsx b/src/pages/tasks/NewTaskDetailsPage.tsx index 52fdbf5523a2..ae269b1dbc81 100644 --- a/src/pages/tasks/NewTaskDetailsPage.tsx +++ b/src/pages/tasks/NewTaskDetailsPage.tsx @@ -58,10 +58,14 @@ function NewTaskDetailsPage({task}: NewTaskDetailsPageProps) { // We error if the user doesn't enter a task name ErrorUtils.addErrorMessage(errors, 'taskTitle', 'newTaskPage.pleaseEnterTaskName'); } else if (values.taskTitle.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'taskTitle', ['common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } if (values.taskDescription.length > CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'taskDescription', ['common.error.characterLimitExceedCounter', {length: values.taskDescription.length, limit: CONST.DESCRIPTION_LIMIT}]); + ErrorUtils.addErrorMessage( + errors, + 'taskDescription', + translate('common.error.characterLimitExceedCounter', {length: values.taskDescription.length, limit: CONST.DESCRIPTION_LIMIT}), + ); } return errors; diff --git a/src/pages/tasks/NewTaskTitlePage.tsx b/src/pages/tasks/NewTaskTitlePage.tsx index 582d2a5c6500..0ff6c178d24a 100644 --- a/src/pages/tasks/NewTaskTitlePage.tsx +++ b/src/pages/tasks/NewTaskTitlePage.tsx @@ -42,7 +42,7 @@ function NewTaskTitlePage({task}: NewTaskTitlePageProps) { // We error if the user doesn't enter a task name ErrorUtils.addErrorMessage(errors, 'taskTitle', 'newTaskPage.pleaseEnterTaskName'); } else if (values.taskTitle.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'taskTitle', ['common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } return errors; diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index c48746c81239..dbceacd1e97a 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -39,11 +39,11 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti const errors = {}; if (values?.description && values.description?.length > CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'description', ['common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'description', translate('common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT})); } return errors; - }, []); + }, [translate]); const submit = useCallback( (values: FormOnyxValues) => { diff --git a/src/pages/tasks/TaskTitlePage.tsx b/src/pages/tasks/TaskTitlePage.tsx index 99558efaeb03..918d9d624f5b 100644 --- a/src/pages/tasks/TaskTitlePage.tsx +++ b/src/pages/tasks/TaskTitlePage.tsx @@ -35,11 +35,11 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) if (!title) { ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, 'newTaskPage.pleaseEnterTaskName'); } else if (title.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, ['common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } return errors; - }, []); + }, [translate]); const submit = useCallback( (values: FormOnyxValues) => { diff --git a/src/pages/workspace/WorkspaceNamePage.tsx b/src/pages/workspace/WorkspaceNamePage.tsx index 6eea77962334..173543043a6b 100644 --- a/src/pages/workspace/WorkspaceNamePage.tsx +++ b/src/pages/workspace/WorkspaceNamePage.tsx @@ -47,11 +47,11 @@ function WorkspaceNamePage({policy}: Props) { } else if ([...name].length > CONST.TITLE_CHARACTER_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 // code units. - ErrorUtils.addErrorMessage(errors, 'name', ['common.error.characterLimitExceedCounter', {length: [...name].length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'name', translate('common.error.characterLimitExceedCounter', {length: [...name].length, limit: CONST.TITLE_CHARACTER_LIMIT})); } return errors; - }, []); + }, [translate]); return ( CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'description', ['common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'description', translate('common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT})); } return errors; - }, []); + }, [translate]); const submit = useCallback( (values: FormOnyxValues) => { diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx index 397677ab749a..e045889ff6b6 100644 --- a/src/pages/workspace/categories/CategoryForm.tsx +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -48,12 +48,16 @@ function CategoryForm({onSubmit, policyCategories, categoryName, validateEdit}: errors.categoryName = 'workspace.categories.invalidCategoryName'; } else if ([...newCategoryName].length > CONST.CATEGORY_NAME_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. - ErrorUtils.addErrorMessage(errors, 'categoryName', ['common.error.characterLimitExceedCounter', {length: [...newCategoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}]); + ErrorUtils.addErrorMessage( + errors, + 'categoryName', + translate('common.error.characterLimitExceedCounter', {length: [...newCategoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}), + ); } return errors; }, - [policyCategories], + [policyCategories, translate], ); const submit = useCallback( diff --git a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx index 0ed373f135e4..408bf4eb2366 100644 --- a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx +++ b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx @@ -49,12 +49,12 @@ function CreateTagPage({route, policyTags}: CreateTagPageProps) { errors.tagName = 'workspace.tags.existingTagError'; } else if ([...tagName].length > CONST.TAG_NAME_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. - ErrorUtils.addErrorMessage(errors, 'tagName', ['common.error.characterLimitExceedCounter', {length: [...tagName].length, limit: CONST.TAG_NAME_LIMIT}]); + ErrorUtils.addErrorMessage(errors, 'tagName', translate('common.error.characterLimitExceedCounter', {length: [...tagName].length, limit: CONST.TAG_NAME_LIMIT})); } return errors; }, - [policyTags], + [policyTags, translate], ); const createTag = useCallback( diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 63417b7ddd3b..164e51393e21 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1862,7 +1862,7 @@ describe('actions/IOU', () => { Onyx.disconnect(connectionID); const updatedAction = Object.values(allActions ?? {}).find((reportAction) => !isEmptyObject(reportAction)); expect(updatedAction?.actionName).toEqual('MODIFIEDEXPENSE'); - expect(Object.values(updatedAction?.errors ?? {})).toEqual(Localize.translateLocal('iou.error.genericEditFailureMessage')); + expect(Object.values(updatedAction?.errors ?? {})[0]).toEqual(Localize.translateLocal('iou.error.genericEditFailureMessage')); resolve(); }, }); From 20e829fe0f9d92937b11fb9517613605d6c9be5e Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 15:48:25 +0700 Subject: [PATCH 033/228] fix test --- .../Profile/PersonalDetails/LegalNamePage.tsx | 71 ++++++++++--------- src/pages/tasks/TaskDescriptionPage.tsx | 17 +++-- src/pages/tasks/TaskTitlePage.tsx | 21 +++--- src/pages/workspace/WorkspaceNamePage.tsx | 27 +++---- .../WorkspaceProfileDescriptionPage.tsx | 17 +++-- tests/actions/IOUTest.ts | 2 +- tests/unit/ValidationUtilsTest.ts | 11 ++- 7 files changed, 93 insertions(+), 73 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx index 1242618d1407..83f8456f5fd3 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx @@ -40,45 +40,48 @@ function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNameP const legalFirstName = privatePersonalDetails?.legalFirstName ?? ''; const legalLastName = privatePersonalDetails?.legalLastName ?? ''; - const validate = useCallback((values: FormOnyxValues) => { - const errors: Errors = {}; + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: Errors = {}; - if (typeof values.legalFirstName === 'string') { - if (!ValidationUtils.isValidLegalName(values.legalFirstName)) { - ErrorUtils.addErrorMessage(errors, 'legalFirstName', 'privatePersonalDetails.error.hasInvalidCharacter'); - } else if (!values.legalFirstName) { - errors.legalFirstName = 'common.error.fieldRequired'; - } else if (values.legalFirstName.length > CONST.LEGAL_NAME.MAX_LENGTH) { - ErrorUtils.addErrorMessage( - errors, - 'legalFirstName', - translate('common.error.characterLimitExceedCounter', {length: values.legalFirstName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}), - ); + if (typeof values.legalFirstName === 'string') { + if (!ValidationUtils.isValidLegalName(values.legalFirstName)) { + ErrorUtils.addErrorMessage(errors, 'legalFirstName', 'privatePersonalDetails.error.hasInvalidCharacter'); + } else if (!values.legalFirstName) { + errors.legalFirstName = 'common.error.fieldRequired'; + } else if (values.legalFirstName.length > CONST.LEGAL_NAME.MAX_LENGTH) { + ErrorUtils.addErrorMessage( + errors, + 'legalFirstName', + translate('common.error.characterLimitExceedCounter', {length: values.legalFirstName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}), + ); + } + if (ValidationUtils.doesContainReservedWord(values.legalFirstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { + ErrorUtils.addErrorMessage(errors, 'legalFirstName', 'personalDetails.error.containsReservedWord'); + } } - if (ValidationUtils.doesContainReservedWord(values.legalFirstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'legalFirstName', 'personalDetails.error.containsReservedWord'); - } - } - if (typeof values.legalLastName === 'string') { - if (!ValidationUtils.isValidLegalName(values.legalLastName)) { - ErrorUtils.addErrorMessage(errors, 'legalLastName', 'privatePersonalDetails.error.hasInvalidCharacter'); - } else if (!values.legalLastName) { - errors.legalLastName = 'common.error.fieldRequired'; - } else if (values.legalLastName.length > CONST.LEGAL_NAME.MAX_LENGTH) { - ErrorUtils.addErrorMessage( - errors, - 'legalLastName', - translate('common.error.characterLimitExceedCounter', {length: values.legalLastName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}), - ); - } - if (ValidationUtils.doesContainReservedWord(values.legalLastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'legalLastName', 'personalDetails.error.containsReservedWord'); + if (typeof values.legalLastName === 'string') { + if (!ValidationUtils.isValidLegalName(values.legalLastName)) { + ErrorUtils.addErrorMessage(errors, 'legalLastName', 'privatePersonalDetails.error.hasInvalidCharacter'); + } else if (!values.legalLastName) { + errors.legalLastName = 'common.error.fieldRequired'; + } else if (values.legalLastName.length > CONST.LEGAL_NAME.MAX_LENGTH) { + ErrorUtils.addErrorMessage( + errors, + 'legalLastName', + translate('common.error.characterLimitExceedCounter', {length: values.legalLastName.length, limit: CONST.LEGAL_NAME.MAX_LENGTH}), + ); + } + if (ValidationUtils.doesContainReservedWord(values.legalLastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { + ErrorUtils.addErrorMessage(errors, 'legalLastName', 'personalDetails.error.containsReservedWord'); + } } - } - return errors; - }, [translate]); + return errors; + }, + [translate], + ); return ( ): FormInputErrors => { - const errors = {}; + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = {}; - if (values?.description && values.description?.length > CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'description', translate('common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT})); - } + if (values?.description && values.description?.length > CONST.DESCRIPTION_LIMIT) { + ErrorUtils.addErrorMessage(errors, 'description', translate('common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT})); + } - return errors; - }, [translate]); + return errors; + }, + [translate], + ); const submit = useCallback( (values: FormOnyxValues) => { diff --git a/src/pages/tasks/TaskTitlePage.tsx b/src/pages/tasks/TaskTitlePage.tsx index 918d9d624f5b..70ced7a81072 100644 --- a/src/pages/tasks/TaskTitlePage.tsx +++ b/src/pages/tasks/TaskTitlePage.tsx @@ -29,17 +29,20 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) const styles = useThemeStyles(); const {translate} = useLocalize(); - const validate = useCallback(({title}: FormOnyxValues): FormInputErrors => { - const errors: FormInputErrors = {}; + const validate = useCallback( + ({title}: FormOnyxValues): FormInputErrors => { + const errors: FormInputErrors = {}; - if (!title) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, 'newTaskPage.pleaseEnterTaskName'); - } else if (title.length > CONST.TITLE_CHARACTER_LIMIT) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT})); - } + if (!title) { + ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, 'newTaskPage.pleaseEnterTaskName'); + } else if (title.length > CONST.TITLE_CHARACTER_LIMIT) { + ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT})); + } - return errors; - }, [translate]); + return errors; + }, + [translate], + ); const submit = useCallback( (values: FormOnyxValues) => { diff --git a/src/pages/workspace/WorkspaceNamePage.tsx b/src/pages/workspace/WorkspaceNamePage.tsx index 173543043a6b..03d4e7107c1c 100644 --- a/src/pages/workspace/WorkspaceNamePage.tsx +++ b/src/pages/workspace/WorkspaceNamePage.tsx @@ -38,20 +38,23 @@ function WorkspaceNamePage({policy}: Props) { [policy], ); - const validate = useCallback((values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const name = values.name.trim(); + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const name = values.name.trim(); - if (!ValidationUtils.isRequiredFulfilled(name)) { - errors.name = 'workspace.editor.nameIsRequiredError'; - } else if ([...name].length > CONST.TITLE_CHARACTER_LIMIT) { - // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 - // code units. - ErrorUtils.addErrorMessage(errors, 'name', translate('common.error.characterLimitExceedCounter', {length: [...name].length, limit: CONST.TITLE_CHARACTER_LIMIT})); - } + if (!ValidationUtils.isRequiredFulfilled(name)) { + errors.name = 'workspace.editor.nameIsRequiredError'; + } else if ([...name].length > CONST.TITLE_CHARACTER_LIMIT) { + // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 + // code units. + ErrorUtils.addErrorMessage(errors, 'name', translate('common.error.characterLimitExceedCounter', {length: [...name].length, limit: CONST.TITLE_CHARACTER_LIMIT})); + } - return errors; - }, [translate]); + return errors; + }, + [translate], + ); return ( ) => { - const errors = {}; + const validate = useCallback( + (values: FormOnyxValues) => { + const errors = {}; - if (values.description.length > CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage(errors, 'description', translate('common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT})); - } + if (values.description.length > CONST.DESCRIPTION_LIMIT) { + ErrorUtils.addErrorMessage(errors, 'description', translate('common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT})); + } - return errors; - }, [translate]); + return errors; + }, + [translate], + ); const submit = useCallback( (values: FormOnyxValues) => { diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 164e51393e21..de0e4556ba5e 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2084,7 +2084,7 @@ describe('actions/IOU', () => { callback: (allActions) => { Onyx.disconnect(connectionID); const erroredAction = Object.values(allActions ?? {}).find((action) => !isEmptyObject(action?.errors)); - expect(Object.values(erroredAction?.errors ?? {})).toEqual(Localize.translateLocal('iou.error.other')); + expect(Object.values(erroredAction?.errors ?? {})[0]).toEqual(Localize.translateLocal('iou.error.other')); resolve(); }, }); diff --git a/tests/unit/ValidationUtilsTest.ts b/tests/unit/ValidationUtilsTest.ts index fc5dacee2cf2..3bd083bc07e4 100644 --- a/tests/unit/ValidationUtilsTest.ts +++ b/tests/unit/ValidationUtilsTest.ts @@ -1,4 +1,5 @@ import {addDays, format, startOfDay, subYears} from 'date-fns'; +import * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; import * as ValidationUtils from '@src/libs/ValidationUtils'; @@ -186,19 +187,23 @@ describe('ValidationUtils', () => { test('Should return an error message for a date before the minimum age requirement', () => { const invalidDate: string = format(subYears(new Date(), 17), CONST.DATE.FNS_FORMAT_STRING); // Date of birth 17 years ago const error = ValidationUtils.getAgeRequirementError(invalidDate, 18, 150); - expect(error).toEqual(['privatePersonalDetails.error.dateShouldBeBefore', {dateString: format(startOfDay(subYears(new Date(), 18)), CONST.DATE.FNS_FORMAT_STRING)}]); + expect(error).toEqual( + Localize.translateLocal('privatePersonalDetails.error.dateShouldBeBefore', {dateString: format(startOfDay(subYears(new Date(), 18)), CONST.DATE.FNS_FORMAT_STRING)}), + ); }); test('Should return an error message for a date after the maximum age requirement', () => { const invalidDate: string = format(subYears(new Date(), 160), CONST.DATE.FNS_FORMAT_STRING); // Date of birth 160 years ago const error = ValidationUtils.getAgeRequirementError(invalidDate, 18, 150); - expect(error).toEqual(['privatePersonalDetails.error.dateShouldBeAfter', {dateString: format(startOfDay(subYears(new Date(), 150)), CONST.DATE.FNS_FORMAT_STRING)}]); + expect(error).toEqual( + Localize.translateLocal('privatePersonalDetails.error.dateShouldBeAfter', {dateString: format(startOfDay(subYears(new Date(), 150)), CONST.DATE.FNS_FORMAT_STRING)}), + ); }); test('Should return an error message for an invalid date', () => { const invalidDate = '2023-07-32'; // Invalid date const error = ValidationUtils.getAgeRequirementError(invalidDate, 18, 150); - expect(error).toBe('common.error.dateInvalid'); + expect(error).toBe(Localize.translateLocal('common.error.dateInvalid')); }); }); From a7fb5482db646a8e41a4be8254d851172343b183 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 16:04:33 +0700 Subject: [PATCH 034/228] fix lint --- src/components/AddPlaidBankAccount.tsx | 1 + src/components/Form/FormProvider.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/AddPlaidBankAccount.tsx b/src/components/AddPlaidBankAccount.tsx index 366f14ec9780..26444b1a48c4 100644 --- a/src/components/AddPlaidBankAccount.tsx +++ b/src/components/AddPlaidBankAccount.tsx @@ -177,6 +177,7 @@ function AddPlaidBankAccount({ })); const {icon, iconSize, iconStyles} = getBankIcon({styles}); const plaidErrors = plaidData?.errors; + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style const plaidDataErrorMessage = !isEmptyObject(plaidErrors) ? (Object.values(plaidErrors)[0] as string) : ''; const bankName = plaidData?.bankName; diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index 3d20f910dca0..bd65d3046e18 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -256,6 +256,7 @@ function FormProvider( (Object.keys(errorFields) .sort() .map((key) => errorFields[key]) + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style .at(-1) as string) ?? ''; const inputRef = inputProps.ref; From 6d3a233ef472f09fc30f01ce115d07a5f9309b28 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 16:22:02 +0700 Subject: [PATCH 035/228] fix test --- src/components/Form/FormProvider.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index bd65d3046e18..536af18d85b3 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -256,8 +256,7 @@ function FormProvider( (Object.keys(errorFields) .sort() .map((key) => errorFields[key]) - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - .at(-1) as string) ?? ''; + .at(-1)) ?? ''; const inputRef = inputProps.ref; From 0c4cabc755963e8ccae139198d6f7e9e830d6248 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 3 Jun 2024 16:31:03 +0700 Subject: [PATCH 036/228] fix prettier --- src/components/Form/FormProvider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index 536af18d85b3..16ecc3805d2d 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -253,10 +253,10 @@ function FormProvider( const errorFields = formState?.errorFields?.[inputID] ?? {}; const fieldErrorMessage = - (Object.keys(errorFields) + Object.keys(errorFields) .sort() .map((key) => errorFields[key]) - .at(-1)) ?? ''; + .at(-1) ?? ''; const inputRef = inputProps.ref; From 22c18c3a6ccbbdf3cfd5f7463ed53c0ed3bc5375 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:25:15 +0200 Subject: [PATCH 037/228] feat: add loading state to the SettlementButton --- src/components/MoneyReportHeader.tsx | 5 ++++- src/components/ReportActionItem/ReportPreview.tsx | 5 ++++- src/libs/ReportUtils.ts | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index aad801a7c259..4f6e10337469 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -59,6 +60,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const styles = useThemeStyles(); const theme = useTheme(); + const {isOffline} = useNetwork(); const [isDeleteRequestModalVisible, setIsDeleteRequestModalVisible] = useState(false); const {translate} = useLocalize(); const {windowWidth} = useWindowDimensions(); @@ -228,7 +230,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea shouldDisableApproveButton={shouldDisableApproveButton} style={[styles.pv2]} formattedAmount={!ReportUtils.hasOnlyHeldExpenses(moneyRequestReport.reportID) ? displayedAmount : ''} - isDisabled={!canAllowSettlement} + isDisabled={isOffline && !canAllowSettlement} + isLoading={!isOffline && !canAllowSettlement} /> )} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index be3b104018db..52c987ca78ff 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -13,6 +13,7 @@ import SettlementButton from '@components/SettlementButton'; import {showContextMenuForReport} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -106,6 +107,7 @@ function ReportPreview({ const styles = useThemeStyles(); const {translate} = useLocalize(); const {canUseViolations} = usePermissions(); + const {isOffline} = useNetwork(); const {hasMissingSmartscanFields, areAllRequestsBeingSmartScanned, hasOnlyTransactionsWithPendingRoutes, hasNonReimbursableTransactions} = useMemo( () => ({ @@ -382,7 +384,8 @@ function ReportPreview({ horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }} - isDisabled={!canAllowSettlement} + isDisabled={isOffline && !canAllowSettlement} + isLoading={!isOffline && !canAllowSettlement} /> )} {shouldShowSubmitButton && ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 690cfdafd92d..07a5ef9941f7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6343,8 +6343,9 @@ function hasUpdatedTotal(report: OnyxEntry, policy: OnyxEntry): const hasPendingTransaction = transactions.some((transaction) => !!transaction.pendingAction); const hasTransactionWithDifferentCurrency = transactions.some((transaction) => transaction.currency !== report.currency); const hasDifferentWorkspaceCurrency = report.pendingFields?.createChat && isExpenseReport(report) && report.currency !== policy?.outputCurrency; + const hasOptimisticHeldExpense = hasHeldExpenses(report.reportID) && report?.unheldTotal === undefined; - return !(hasPendingTransaction && (hasTransactionWithDifferentCurrency || hasDifferentWorkspaceCurrency)) && !(hasHeldExpenses(report.reportID) && report?.unheldTotal === undefined); + return !(hasPendingTransaction && (hasTransactionWithDifferentCurrency || hasDifferentWorkspaceCurrency)) && !hasOptimisticHeldExpense; } /** From f29281c39395a8378d87b52a3d040ee9b68b14cd Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 00:05:08 +0700 Subject: [PATCH 038/228] add missing form helper error translation --- src/libs/PolicyDistanceRatesUtils.ts | 5 ++- src/libs/actions/BankAccounts.ts | 3 +- src/pages/EditReportFieldText.tsx | 4 +- .../PersonalInfo/substeps/AddressStep.tsx | 28 +++++++------- .../PersonalInfo/substeps/FullNameStep.tsx | 24 ++++++------ .../PersonalInfo/substeps/PhoneNumberStep.tsx | 17 +++++---- .../substeps/SocialSecurityNumberStep.tsx | 19 +++++----- .../BaseOnboardingPersonalDetails.tsx | 10 ++--- .../OnboardingWork/BaseOnboardingWork.tsx | 2 +- .../BankInfo/substeps/Manual.tsx | 37 ++++++++++--------- .../AddressUBO.tsx | 4 +- .../SocialSecurityNumberUBO.tsx | 2 +- .../BusinessInfo/substeps/AddressBusiness.tsx | 32 ++++++++-------- .../BusinessInfo/substeps/NameBusiness.tsx | 20 +++++----- .../substeps/PhoneNumberBusiness.tsx | 20 +++++----- .../BusinessInfo/substeps/TaxIdBusiness.tsx | 21 +++++------ .../BusinessInfo/substeps/WebsiteBusiness.tsx | 20 +++++----- .../components/BankAccountValidationForm.tsx | 2 +- .../PersonalInfo/substeps/Address.tsx | 28 +++++++------- .../PersonalInfo/substeps/FullName.tsx | 25 ++++++------- .../substeps/SocialSecurityNumber.tsx | 19 +++++----- .../IntroSchoolPrincipalPage.tsx | 14 +++---- src/pages/TeachersUnite/KnowATeacherPage.tsx | 12 +++--- src/pages/iou/HoldReasonPage.tsx | 6 +-- .../request/step/IOURequestStepMerchant.tsx | 4 +- .../request/step/IOURequestStepWaypoint.tsx | 4 +- .../ExitSurvey/ExitSurveyReasonPage.tsx | 2 +- .../ExitSurvey/ExitSurveyResponsePage.tsx | 2 +- .../Profile/Contacts/NewContactMethodPage.tsx | 6 +-- .../settings/Profile/DisplayNamePage.tsx | 8 ++-- .../Profile/PersonalDetails/LegalNamePage.tsx | 10 ++--- src/pages/settings/Report/RoomNamePage.tsx | 6 +-- .../settings/Security/CloseAccountPage.tsx | 2 +- .../Wallet/Card/GetPhysicalCardName.tsx | 8 ++-- .../Wallet/Card/GetPhysicalCardPhone.tsx | 4 +- src/pages/tasks/NewTaskDetailsPage.tsx | 2 +- src/pages/tasks/NewTaskTitlePage.tsx | 2 +- src/pages/tasks/TaskTitlePage.tsx | 2 +- .../workspace/WorkspaceInviteMessagePage.tsx | 2 +- src/pages/workspace/WorkspaceNamePage.tsx | 2 +- .../workspace/categories/CategoryForm.tsx | 6 +-- .../workspace/categories/EditCategoryPage.tsx | 6 +-- .../members/WorkspaceOwnerPaymentCardForm.tsx | 12 +++--- src/pages/workspace/tags/EditTagPage.tsx | 6 +-- .../workspace/tags/WorkspaceCreateTagPage.tsx | 4 +- .../workspace/tags/WorkspaceEditTagsPage.tsx | 16 ++++---- .../WorkspaceTaxesSettingsCustomTaxName.tsx | 19 ++++++---- 47 files changed, 259 insertions(+), 250 deletions(-) diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index db739cc3b8c7..11ee74bab232 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -4,6 +4,7 @@ import type ONYXKEYS from '@src/ONYXKEYS'; import type {Rate} from '@src/types/onyx/Policy'; import * as CurrencyUtils from './CurrencyUtils'; import getPermittedDecimalSeparator from './getPermittedDecimalSeparator'; +import * as Localize from './Localize'; import * as MoneyRequestUtils from './MoneyRequestUtils'; import * as NumberUtils from './NumberUtils'; @@ -17,9 +18,9 @@ function validateRateValue(values: FormOnyxValues, currency: stri // Allow one more decimal place for accuracy const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CurrencyUtils.getCurrencyDecimals(currency) + 1}})?$`, 'i'); if (!rateValueRegex.test(parsedRate) || parsedRate === '') { - errors.rate = 'workspace.reimburse.invalidRateError'; + errors.rate = Localize.translateLocal('workspace.reimburse.invalidRateError'); } else if (NumberUtils.parseFloatAnyLocale(parsedRate) <= 0) { - errors.rate = 'workspace.reimburse.lowRateError'; + errors.rate = Localize.translateLocal('workspace.reimburse.lowRateError'); } return errors; } diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index 89d0296994e3..3912572418c2 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -14,6 +14,7 @@ import type { } from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; +import * as Localize from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -532,7 +533,7 @@ function validatePlaidSelection(values: FormOnyxValues): Form const errorFields: FormInputErrors = {}; if (!values.selectedPlaidAccountID) { - errorFields.selectedPlaidAccountID = 'bankAccount.error.youNeedToSelectAnOption'; + errorFields.selectedPlaidAccountID = Localize.translateLocal('bankAccount.error.youNeedToSelectAnOption'); } return errorFields; diff --git a/src/pages/EditReportFieldText.tsx b/src/pages/EditReportFieldText.tsx index 0649d59cd2ee..d619eb52b695 100644 --- a/src/pages/EditReportFieldText.tsx +++ b/src/pages/EditReportFieldText.tsx @@ -36,11 +36,11 @@ function EditReportFieldTextPage({fieldName, onSubmit, fieldValue, isRequired, f (values: FormOnyxValues) => { const errors: FormInputErrors = {}; if (isRequired && values[fieldKey].trim() === '') { - errors[fieldKey] = 'common.error.fieldRequired'; + errors[fieldKey] = translate('common.error.fieldRequired'); } return errors; }, - [fieldKey, isRequired], + [fieldKey, isRequired, translate], ); return ( diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx index ad490fa2d7c9..22347f1b3bff 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx @@ -25,20 +25,6 @@ const INPUT_KEYS = { const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.STREET, PERSONAL_INFO_STEP_KEY.CITY, PERSONAL_INFO_STEP_KEY.STATE, PERSONAL_INFO_STEP_KEY.ZIP_CODE]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = 'bankAccount.error.addressStreet'; - } - - if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = 'bankAccount.error.zipCode'; - } - - return errors; -}; - function AddressStep({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -52,6 +38,20 @@ function AddressStep({onNext, isEditing}: SubStepProps) { zipCode: walletAdditionalDetails?.[PERSONAL_INFO_STEP_KEY.ZIP_CODE] ?? '', }; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { + errors.addressStreet = translate('bankAccount.error.addressStreet'); + } + + if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { + errors.addressZipCode = translate('bankAccount.error.zipCode'); + } + + return errors; + }; + const handleSubmit = useWalletAdditionalDetailsStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx index f63d5ef84879..89240720f533 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx @@ -19,18 +19,6 @@ import INPUT_IDS from '@src/types/form/WalletAdditionalDetailsForm'; const PERSONAL_INFO_STEP_KEY = INPUT_IDS.PERSONAL_INFO_STEP; const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.FIRST_NAME, PERSONAL_INFO_STEP_KEY.LAST_NAME]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.legalFirstName && !ValidationUtils.isValidLegalName(values.legalFirstName)) { - errors.legalFirstName = 'bankAccount.error.firstName'; - } - - if (values.legalLastName && !ValidationUtils.isValidLegalName(values.legalLastName)) { - errors.legalLastName = 'bankAccount.error.lastName'; - } - return errors; -}; - function FullNameStep({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -42,6 +30,18 @@ function FullNameStep({onNext, isEditing}: SubStepProps) { lastName: walletAdditionalDetails?.[PERSONAL_INFO_STEP_KEY.LAST_NAME] ?? '', }; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + if (values.legalFirstName && !ValidationUtils.isValidLegalName(values.legalFirstName)) { + errors.legalFirstName = translate('bankAccount.error.firstName'); + } + + if (values.legalLastName && !ValidationUtils.isValidLegalName(values.legalLastName)) { + errors.legalLastName = translate('bankAccount.error.lastName'); + } + return errors; + }; + const handleSubmit = useWalletAdditionalDetailsStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx index 86b6a40948fc..a5326504a1c6 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx @@ -19,14 +19,6 @@ import INPUT_IDS from '@src/types/form/WalletAdditionalDetailsForm'; const PERSONAL_INFO_STEP_KEY = INPUT_IDS.PERSONAL_INFO_STEP; const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.PHONE_NUMBER]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.phoneNumber && !ValidationUtils.isValidUSPhone(values.phoneNumber, true)) { - errors.phoneNumber = 'bankAccount.error.phoneNumber'; - } - return errors; -}; function PhoneNumberStep({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -35,6 +27,15 @@ function PhoneNumberStep({onNext, isEditing}: SubStepProps) { const defaultPhoneNumber = walletAdditionalDetails?.[PERSONAL_INFO_STEP_KEY.PHONE_NUMBER] ?? ''; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.phoneNumber && !ValidationUtils.isValidUSPhone(values.phoneNumber, true)) { + errors.phoneNumber = translate('bankAccount.error.phoneNumber'); + } + return errors; + }; + const handleSubmit = useWalletAdditionalDetailsStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx index 4c366c5a1873..cb3f2fa263f7 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx @@ -19,19 +19,20 @@ import INPUT_IDS from '@src/types/form/WalletAdditionalDetailsForm'; const PERSONAL_INFO_STEP_KEY = INPUT_IDS.PERSONAL_INFO_STEP; const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.SSN_LAST_4]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.ssn && !ValidationUtils.isValidSSNLastFour(values.ssn)) { - errors.ssn = 'bankAccount.error.ssnLast4'; - } - - return errors; -}; function SocialSecurityNumberStep({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.ssn && !ValidationUtils.isValidSSNLastFour(values.ssn)) { + errors.ssn = translate('bankAccount.error.ssnLast4'); + } + + return errors; + }; + const [walletAdditionalDetails] = useOnyx(ONYXKEYS.WALLET_ADDITIONAL_DETAILS); const defaultSsnLast4 = walletAdditionalDetails?.[PERSONAL_INFO_STEP_KEY.SSN_LAST_4] ?? ''; diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index a7a78aa9048c..7a5fa8fe013a 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -91,25 +91,25 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat // First we validate the first name field if (values.firstName.replace(CONST.REGEX.ANY_SPACE, '').length === 0) { - ErrorUtils.addErrorMessage(errors, 'firstName', 'onboarding.error.requiredFirstName'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('onboarding.error.requiredFirstName')); } if (!ValidationUtils.isValidDisplayName(values.firstName)) { - ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.hasInvalidCharacter'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('personalDetails.error.hasInvalidCharacter')); } else if (values.firstName.length > CONST.DISPLAY_NAME.MAX_LENGTH) { ErrorUtils.addErrorMessage(errors, 'firstName', translate('common.error.characterLimitExceedCounter', {length: values.firstName.length, limit: CONST.DISPLAY_NAME.MAX_LENGTH})); } if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.containsReservedWord'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('personalDetails.error.containsReservedWord')); } // Then we validate the last name field if (!ValidationUtils.isValidDisplayName(values.lastName)) { - ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.hasInvalidCharacter'); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('personalDetails.error.hasInvalidCharacter')); } else if (values.lastName.length > CONST.DISPLAY_NAME.MAX_LENGTH) { ErrorUtils.addErrorMessage(errors, 'lastName', translate('common.error.characterLimitExceedCounter', {length: values.lastName.length, limit: CONST.DISPLAY_NAME.MAX_LENGTH})); } if (ValidationUtils.doesContainReservedWord(values.lastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.containsReservedWord'); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('personalDetails.error.containsReservedWord')); } return errors; diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index cdaa264682d1..9b8824300d30 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -59,7 +59,7 @@ function BaseOnboardingWork({shouldUseNativeStyles, onboardingPurposeSelected, o const work = values.work.trim(); if (!ValidationUtils.isRequiredFulfilled(work)) { - errors.work = 'workspace.editor.nameIsRequiredError'; + errors.work = translate('workspace.editor.nameIsRequiredError'); } else if ([...work].length > CONST.TITLE_CHARACTER_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 // code units. diff --git a/src/pages/ReimbursementAccount/BankInfo/substeps/Manual.tsx b/src/pages/ReimbursementAccount/BankInfo/substeps/Manual.tsx index 3f58d7a2073e..b85ca99b2935 100644 --- a/src/pages/ReimbursementAccount/BankInfo/substeps/Manual.tsx +++ b/src/pages/ReimbursementAccount/BankInfo/substeps/Manual.tsx @@ -37,25 +37,28 @@ function Manual({reimbursementAccount, onNext}: ManualProps) { [BANK_INFO_STEP_KEYS.ACCOUNT_NUMBER]: reimbursementAccount?.achData?.[BANK_INFO_STEP_KEYS.ACCOUNT_NUMBER] ?? '', }; - const validate = useCallback((values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - const routingNumber = values.routingNumber?.trim(); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const routingNumber = values.routingNumber?.trim(); - if ( - values.accountNumber && - !CONST.BANK_ACCOUNT.REGEX.US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) && - !CONST.BANK_ACCOUNT.REGEX.MASKED_US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) - ) { - errors.accountNumber = 'bankAccount.error.accountNumber'; - } else if (values.accountNumber && values.accountNumber === routingNumber) { - errors.accountNumber = 'bankAccount.error.routingAndAccountNumberCannotBeSame'; - } - if (routingNumber && (!CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber))) { - errors.routingNumber = 'bankAccount.error.routingNumber'; - } + if ( + values.accountNumber && + !CONST.BANK_ACCOUNT.REGEX.US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) && + !CONST.BANK_ACCOUNT.REGEX.MASKED_US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) + ) { + errors.accountNumber = translate('bankAccount.error.accountNumber'); + } else if (values.accountNumber && values.accountNumber === routingNumber) { + errors.accountNumber = translate('bankAccount.error.routingAndAccountNumberCannotBeSame'); + } + if (routingNumber && (!CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber))) { + errors.routingNumber = translate('bankAccount.error.routingNumber'); + } - return errors; - }, []); + return errors; + }, + [translate], + ); const shouldDisableInputs = !!(reimbursementAccount?.achData?.bankAccountID ?? ''); diff --git a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx index c49f26abf80c..284a27108a48 100644 --- a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx +++ b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/AddressUBO.tsx @@ -47,11 +47,11 @@ function AddressUBO({reimbursementAccountDraft, onNext, isEditing, beneficialOwn const errors = ValidationUtils.getFieldRequiredErrors(values, stepFields); if (values[inputKeys.street] && !ValidationUtils.isValidAddress(values[inputKeys.street])) { - errors[inputKeys.street] = 'bankAccount.error.addressStreet'; + errors[inputKeys.street] = translate('bankAccount.error.addressStreet'); } if (values[inputKeys.zipCode] && !ValidationUtils.isValidZipCode(values[inputKeys.zipCode])) { - errors[inputKeys.zipCode] = 'bankAccount.error.zipCode'; + errors[inputKeys.zipCode] = translate('bankAccount.error.zipCode'); } return errors; diff --git a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/SocialSecurityNumberUBO.tsx b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/SocialSecurityNumberUBO.tsx index 7c0c5ac0c057..d7814816cdb9 100644 --- a/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/SocialSecurityNumberUBO.tsx +++ b/src/pages/ReimbursementAccount/BeneficialOwnerInfo/substeps/BeneficialOwnerDetailsFormSubsteps/SocialSecurityNumberUBO.tsx @@ -36,7 +36,7 @@ function SocialSecurityNumberUBO({reimbursementAccountDraft, onNext, isEditing, const validate = (values: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(values, stepFields); if (values[ssnLast4InputID] && !ValidationUtils.isValidSSNLastFour(values[ssnLast4InputID])) { - errors[ssnLast4InputID] = 'bankAccount.error.ssnLast4'; + errors[ssnLast4InputID] = translate('bankAccount.error.ssnLast4'); } return errors; }; diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx index cca9e5619d59..8454a82969e7 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/AddressBusiness.tsx @@ -32,27 +32,27 @@ const INPUT_KEYS = { const STEP_FIELDS = [COMPANY_BUSINESS_INFO_KEY.STREET, COMPANY_BUSINESS_INFO_KEY.CITY, COMPANY_BUSINESS_INFO_KEY.STATE, COMPANY_BUSINESS_INFO_KEY.ZIP_CODE]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); +function AddressBusiness({reimbursementAccount, onNext, isEditing}: AddressBusinessProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); - if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = 'bankAccount.error.addressStreet'; - } + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.addressCity && !ValidationUtils.isValidAddress(values.addressCity)) { - errors.addressCity = 'bankAccount.error.addressCity'; - } + if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { + errors.addressStreet = translate('bankAccount.error.addressStreet'); + } - if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = 'bankAccount.error.zipCode'; - } + if (values.addressCity && !ValidationUtils.isValidAddress(values.addressCity)) { + errors.addressCity = translate('bankAccount.error.addressCity'); + } - return errors; -}; + if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { + errors.addressZipCode = translate('bankAccount.error.zipCode'); + } -function AddressBusiness({reimbursementAccount, onNext, isEditing}: AddressBusinessProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); + return errors; + }; const defaultValues = { street: reimbursementAccount?.achData?.addressStreet ?? '', diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx index 377fbf9782ae..712f42578f36 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx @@ -26,16 +26,6 @@ type NameBusinessProps = NameBusinessOnyxProps & SubStepProps; const COMPANY_NAME_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_NAME; const STEP_FIELDS = [COMPANY_NAME_KEY]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.companyName && !ValidationUtils.isValidCompanyName(values.companyName)) { - errors.companyName = 'bankAccount.error.companyName'; - } - - return errors; -}; - function NameBusiness({reimbursementAccount, onNext, isEditing}: NameBusinessProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -45,6 +35,16 @@ function NameBusiness({reimbursementAccount, onNext, isEditing}: NameBusinessPro const shouldDisableCompanyName = !!(bankAccountID && defaultCompanyName && reimbursementAccount?.achData?.state !== 'SETUP'); + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.companyName && !ValidationUtils.isValidCompanyName(values.companyName)) { + errors.companyName = translate('bankAccount.error.companyName'); + } + + return errors; + }; + const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx index 018922610080..b22424d194f9 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx @@ -26,21 +26,21 @@ type PhoneNumberBusinessProps = PhoneNumberBusinessOnyxProps & SubStepProps; const COMPANY_PHONE_NUMBER_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_PHONE; const STEP_FIELDS = [COMPANY_PHONE_NUMBER_KEY]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { - errors.companyPhone = 'bankAccount.error.phoneNumber'; - } - - return errors; -}; - function PhoneNumberBusiness({reimbursementAccount, onNext, isEditing}: PhoneNumberBusinessProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const defaultCompanyPhoneNumber = reimbursementAccount?.achData?.companyPhone ?? ''; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { + errors.companyPhone = translate('bankAccount.error.phoneNumber'); + } + + return errors; + }; + const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx index cabf0fc01576..6fba24b901e3 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx @@ -25,17 +25,6 @@ type TaxIdBusinessProps = TaxIdBusinessOnyxProps & SubStepProps; const COMPANY_TAX_ID_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_TAX_ID; const STEP_FIELDS = [COMPANY_TAX_ID_KEY]; - -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { - errors.companyTaxID = 'bankAccount.error.taxID'; - } - - return errors; -}; - function TaxIdBusiness({reimbursementAccount, onNext, isEditing}: TaxIdBusinessProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -43,6 +32,16 @@ function TaxIdBusiness({reimbursementAccount, onNext, isEditing}: TaxIdBusinessP const bankAccountID = reimbursementAccount?.achData?.bankAccountID ?? 0; const shouldDisableCompanyTaxID = !!(bankAccountID && defaultCompanyTaxId && reimbursementAccount?.achData?.state !== 'SETUP'); + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { + errors.companyTaxID = translate('bankAccount.error.taxID'); + } + + return errors; + }; + const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx index e06c4d9d575e..7115e1a82158 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx @@ -34,16 +34,6 @@ type WebsiteBusinessProps = WebsiteBusinessOnyxProps & SubStepProps; const COMPANY_WEBSITE_KEY = INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_WEBSITE; const STEP_FIELDS = [COMPANY_WEBSITE_KEY]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.website && !ValidationUtils.isValidWebsite(values.website)) { - errors.website = 'bankAccount.error.website'; - } - - return errors; -}; - function WebsiteBusiness({reimbursementAccount, user, session, onNext, isEditing}: WebsiteBusinessProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -51,6 +41,16 @@ function WebsiteBusiness({reimbursementAccount, user, session, onNext, isEditing const defaultWebsiteExample = useMemo(() => getDefaultCompanyWebsite(session, user), [session, user]); const defaultCompanyWebsite = reimbursementAccount?.achData?.website ?? defaultWebsiteExample; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.website && !ValidationUtils.isValidWebsite(values.website)) { + errors.website = translate('bankAccount.error.website'); + } + + return errors; + }; + const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx b/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx index a83e18d119c9..9bb4b4a56bfe 100644 --- a/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx +++ b/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx @@ -73,7 +73,7 @@ function BankAccountValidationForm({requiresTwoFactorAuth, reimbursementAccount, if (ValidationUtils.isRequiredFulfilled(filteredValue.toString())) { return; } - errors[key as keyof AmountValues] = 'common.error.invalidAmount'; + errors[key as keyof AmountValues] = translate('common.error.invalidAmount'); }); return errors; diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx index a0ec8220ce3e..e75978b77298 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/Address.tsx @@ -34,20 +34,6 @@ const INPUT_KEYS = { const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.STREET, PERSONAL_INFO_STEP_KEY.CITY, PERSONAL_INFO_STEP_KEY.STATE, PERSONAL_INFO_STEP_KEY.ZIP_CODE]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { - errors.requestorAddressStreet = 'bankAccount.error.addressStreet'; - } - - if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { - errors.requestorAddressZipCode = 'bankAccount.error.zipCode'; - } - - return errors; -}; - function Address({reimbursementAccount, onNext, isEditing}: AddressProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -59,6 +45,20 @@ function Address({reimbursementAccount, onNext, isEditing}: AddressProps) { zipCode: reimbursementAccount?.achData?.[PERSONAL_INFO_STEP_KEY.ZIP_CODE] ?? '', }; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { + errors.requestorAddressStreet = translate('bankAccount.error.addressStreet'); + } + + if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { + errors.requestorAddressZipCode = translate('bankAccount.error.zipCode'); + } + + return errors; + }; + const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx index 1d225c1c32f2..79da5ea4c707 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx @@ -27,19 +27,6 @@ type FullNameProps = FullNameOnyxProps & SubStepProps; const PERSONAL_INFO_STEP_KEY = INPUT_IDS.PERSONAL_INFO_STEP; const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.FIRST_NAME, PERSONAL_INFO_STEP_KEY.LAST_NAME]; - -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.firstName && !ValidationUtils.isValidLegalName(values.firstName)) { - errors.firstName = 'bankAccount.error.firstName'; - } - - if (values.lastName && !ValidationUtils.isValidLegalName(values.lastName)) { - errors.lastName = 'bankAccount.error.lastName'; - } - return errors; -}; - function FullName({reimbursementAccount, onNext, isEditing}: FullNameProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -49,6 +36,18 @@ function FullName({reimbursementAccount, onNext, isEditing}: FullNameProps) { lastName: reimbursementAccount?.achData?.[PERSONAL_INFO_STEP_KEY.LAST_NAME] ?? '', }; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + if (values.firstName && !ValidationUtils.isValidLegalName(values.firstName)) { + errors.firstName = translate('bankAccount.error.firstName'); + } + + if (values.lastName && !ValidationUtils.isValidLegalName(values.lastName)) { + errors.lastName = translate('bankAccount.error.lastName'); + } + return errors; + }; + const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx index e647fd768fb1..d33a0ac6243a 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx @@ -28,21 +28,22 @@ type SocialSecurityNumberProps = SocialSecurityNumberOnyxProps & SubStepProps; const PERSONAL_INFO_STEP_KEY = INPUT_IDS.PERSONAL_INFO_STEP; const STEP_FIELDS = [PERSONAL_INFO_STEP_KEY.SSN_LAST_4]; -const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - - if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { - errors.ssnLast4 = 'bankAccount.error.ssnLast4'; - } - - return errors; -}; function SocialSecurityNumber({reimbursementAccount, onNext, isEditing}: SocialSecurityNumberProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const defaultSsnLast4 = reimbursementAccount?.achData?.[PERSONAL_INFO_STEP_KEY.SSN_LAST_4] ?? ''; + const validate = (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + + if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { + errors.ssnLast4 = translate('bankAccount.error.ssnLast4'); + } + + return errors; + }; + const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx b/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx index 6e8c3b340f4e..00bc9a1e73c9 100644 --- a/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx +++ b/src/pages/TeachersUnite/IntroSchoolPrincipalPage.tsx @@ -50,27 +50,27 @@ function IntroSchoolPrincipalPage(props: IntroSchoolPrincipalPageProps) { const errors: FormInputErrors = {}; if (!values.firstName || !ValidationUtils.isValidPersonName(values.firstName)) { - ErrorUtils.addErrorMessage(errors, 'firstName', 'bankAccount.error.firstName'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('bankAccount.error.firstName')); } if (!values.lastName || !ValidationUtils.isValidPersonName(values.lastName)) { - ErrorUtils.addErrorMessage(errors, 'lastName', 'bankAccount.error.lastName'); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('bankAccount.error.lastName')); } if (!values.partnerUserID) { - ErrorUtils.addErrorMessage(errors, 'partnerUserID', 'teachersUnitePage.error.enterEmail'); + ErrorUtils.addErrorMessage(errors, 'partnerUserID', translate('teachersUnitePage.error.enterEmail')); } if (values.partnerUserID && props.loginList?.[values.partnerUserID.toLowerCase()]) { - ErrorUtils.addErrorMessage(errors, 'partnerUserID', 'teachersUnitePage.error.tryDifferentEmail'); + ErrorUtils.addErrorMessage(errors, 'partnerUserID', translate('teachersUnitePage.error.tryDifferentEmail')); } if (values.partnerUserID && !Str.isValidEmail(values.partnerUserID)) { - ErrorUtils.addErrorMessage(errors, 'partnerUserID', 'teachersUnitePage.error.enterValidEmail'); + ErrorUtils.addErrorMessage(errors, 'partnerUserID', translate('teachersUnitePage.error.enterValidEmail')); } if (values.partnerUserID && LoginUtils.isEmailPublicDomain(values.partnerUserID)) { - ErrorUtils.addErrorMessage(errors, 'partnerUserID', 'teachersUnitePage.error.tryDifferentEmail'); + ErrorUtils.addErrorMessage(errors, 'partnerUserID', translate('teachersUnitePage.error.tryDifferentEmail')); } return errors; }, - [props.loginList], + [props.loginList, translate], ); return ( diff --git a/src/pages/TeachersUnite/KnowATeacherPage.tsx b/src/pages/TeachersUnite/KnowATeacherPage.tsx index cb58009c40f0..54de95f81828 100644 --- a/src/pages/TeachersUnite/KnowATeacherPage.tsx +++ b/src/pages/TeachersUnite/KnowATeacherPage.tsx @@ -59,24 +59,24 @@ function KnowATeacherPage(props: KnowATeacherPageProps) { const validateIfNumber = LoginUtils.validateNumber(phoneLogin); if (!values.firstName || !ValidationUtils.isValidPersonName(values.firstName)) { - ErrorUtils.addErrorMessage(errors, 'firstName', 'bankAccount.error.firstName'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('bankAccount.error.firstName')); } if (!values.lastName || !ValidationUtils.isValidPersonName(values.lastName)) { - ErrorUtils.addErrorMessage(errors, 'lastName', 'bankAccount.error.lastName'); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('bankAccount.error.lastName')); } if (!values.partnerUserID) { - ErrorUtils.addErrorMessage(errors, 'partnerUserID', 'teachersUnitePage.error.enterPhoneEmail'); + ErrorUtils.addErrorMessage(errors, 'partnerUserID', translate('teachersUnitePage.error.enterPhoneEmail')); } if (values.partnerUserID && props.loginList?.[validateIfNumber || values.partnerUserID.toLowerCase()]) { - ErrorUtils.addErrorMessage(errors, 'partnerUserID', 'teachersUnitePage.error.tryDifferentEmail'); + ErrorUtils.addErrorMessage(errors, 'partnerUserID', translate('teachersUnitePage.error.tryDifferentEmail')); } if (values.partnerUserID && !(validateIfNumber || Str.isValidEmail(values.partnerUserID))) { - ErrorUtils.addErrorMessage(errors, 'partnerUserID', 'contacts.genericFailureMessages.invalidContactMethod'); + ErrorUtils.addErrorMessage(errors, 'partnerUserID', translate('contacts.genericFailureMessages.invalidContactMethod')); } return errors; }, - [props.loginList], + [props.loginList, translate], ); return ( diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index 18b290a81ea4..a17ed6df11d3 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -73,20 +73,20 @@ function HoldReasonPage({route}: HoldReasonPageProps) { const errors: FormInputErrors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.COMMENT]); if (!values.comment) { - errors.comment = 'common.error.fieldRequired'; + errors.comment = translate('common.error.fieldRequired'); } // We have extra isWorkspaceRequest condition since, for 1:1 requests, canEditMoneyRequest will rightly return false // as we do not allow requestee to edit fields like description and amount. // But, we still want the requestee to be able to put the request on hold if (!ReportUtils.canEditMoneyRequest(parentReportAction) && isWorkspaceRequest) { const formErrors = {}; - ErrorUtils.addErrorMessage(formErrors, 'reportModified', 'common.error.requestModified'); + ErrorUtils.addErrorMessage(formErrors, 'reportModified', translate('common.error.requestModified')); FormActions.setErrors(ONYXKEYS.FORMS.MONEY_REQUEST_HOLD_FORM, formErrors); } return errors; }, - [parentReportAction, isWorkspaceRequest], + [parentReportAction, isWorkspaceRequest, translate], ); useEffect(() => { diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index fc7d39b49089..bd5ec0eed4c3 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -73,12 +73,12 @@ function IOURequestStepMerchant({ const errors: FormInputErrors = {}; if (isMerchantRequired && !value.moneyRequestMerchant) { - errors.moneyRequestMerchant = 'common.error.fieldRequired'; + errors.moneyRequestMerchant = translate('common.error.fieldRequired'); } return errors; }, - [isMerchantRequired], + [isMerchantRequired, translate], ); const updateMerchant = (value: FormOnyxValues) => { diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx index 6c0eae98fb85..158cc8ceca85 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx @@ -90,13 +90,13 @@ function IOURequestStepWaypoint({ const errors = {}; const waypointValue = values[`waypoint${pageIndex}`] ?? ''; if (isOffline && waypointValue !== '' && !ValidationUtils.isValidAddress(waypointValue)) { - ErrorUtils.addErrorMessage(errors, `waypoint${pageIndex}`, 'bankAccount.error.address'); + ErrorUtils.addErrorMessage(errors, `waypoint${pageIndex}`, translate('bankAccount.error.address')); } // If the user is online, and they are trying to save a value without using the autocomplete, show an error message instructing them to use a selected address instead. // That enables us to save the address with coordinates when it is selected if (!isOffline && waypointValue !== '' && waypointAddress !== waypointValue) { - ErrorUtils.addErrorMessage(errors, `waypoint${pageIndex}`, 'distance.error.selectSuggestedAddress'); + ErrorUtils.addErrorMessage(errors, `waypoint${pageIndex}`, translate('distance.error.selectSuggestedAddress')); } return errors; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index dbaf330803c1..d9f53faa62d0 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -60,7 +60,7 @@ function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) { validate={() => { const errors: Errors = {}; if (!reason) { - errors[INPUT_IDS.REASON] = 'common.error.fieldRequired'; + errors[INPUT_IDS.REASON] = translate('common.error.fieldRequired'); } return errors; }} diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 62183b0ebc26..64e8159334d9 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -110,7 +110,7 @@ function ExitSurveyResponsePage({draftResponse, route, navigation}: ExitSurveyRe validate={() => { const errors: Errors = {}; if (!draftResponse?.trim()) { - errors[INPUT_IDS.RESPONSE] = 'common.error.fieldRequired'; + errors[INPUT_IDS.RESPONSE] = translate('common.error.fieldRequired'); } return errors; }} diff --git a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx index 20e12f71664e..4366f8ff284d 100644 --- a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx +++ b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx @@ -57,15 +57,15 @@ function NewContactMethodPage({loginList, route}: NewContactMethodPageProps) { const errors = {}; if (!values.phoneOrEmail) { - ErrorUtils.addErrorMessage(errors, 'phoneOrEmail', 'contacts.genericFailureMessages.contactMethodRequired'); + ErrorUtils.addErrorMessage(errors, 'phoneOrEmail', translate('contacts.genericFailureMessages.contactMethodRequired')); } if (!!values.phoneOrEmail && !(validateIfnumber || Str.isValidEmail(values.phoneOrEmail))) { - ErrorUtils.addErrorMessage(errors, 'phoneOrEmail', 'contacts.genericFailureMessages.invalidContactMethod'); + ErrorUtils.addErrorMessage(errors, 'phoneOrEmail', translate('contacts.genericFailureMessages.invalidContactMethod')); } if (!!values.phoneOrEmail && loginList?.[validateIfnumber || values.phoneOrEmail.toLowerCase()]) { - ErrorUtils.addErrorMessage(errors, 'phoneOrEmail', 'contacts.genericFailureMessages.enteredMethodIsAlreadySubmited'); + ErrorUtils.addErrorMessage(errors, 'phoneOrEmail', translate('contacts.genericFailureMessages.enteredMethodIsAlreadySubmited')); } return errors; diff --git a/src/pages/settings/Profile/DisplayNamePage.tsx b/src/pages/settings/Profile/DisplayNamePage.tsx index 830c06c9a254..e338fc16b0ee 100644 --- a/src/pages/settings/Profile/DisplayNamePage.tsx +++ b/src/pages/settings/Profile/DisplayNamePage.tsx @@ -47,22 +47,22 @@ function DisplayNamePage({isLoadingApp = true, currentUserPersonalDetails}: Disp // First we validate the first name field if (!ValidationUtils.isValidDisplayName(values.firstName)) { - ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.hasInvalidCharacter'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('personalDetails.error.hasInvalidCharacter')); } else if (values.firstName.length > CONST.TITLE_CHARACTER_LIMIT) { ErrorUtils.addErrorMessage(errors, 'firstName', translate('common.error.characterLimitExceedCounter', {length: values.firstName.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.containsReservedWord'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('personalDetails.error.containsReservedWord')); } // Then we validate the last name field if (!ValidationUtils.isValidDisplayName(values.lastName)) { - ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.hasInvalidCharacter'); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('personalDetails.error.hasInvalidCharacter')); } else if (values.lastName.length > CONST.TITLE_CHARACTER_LIMIT) { ErrorUtils.addErrorMessage(errors, 'lastName', translate('common.error.characterLimitExceedCounter', {length: values.lastName.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } if (ValidationUtils.doesContainReservedWord(values.lastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'lastName', 'personalDetails.error.containsReservedWord'); + ErrorUtils.addErrorMessage(errors, 'lastName', translate('personalDetails.error.containsReservedWord')); } return errors; }; diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx index 83f8456f5fd3..c5e725450264 100644 --- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.tsx @@ -46,9 +46,9 @@ function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNameP if (typeof values.legalFirstName === 'string') { if (!ValidationUtils.isValidLegalName(values.legalFirstName)) { - ErrorUtils.addErrorMessage(errors, 'legalFirstName', 'privatePersonalDetails.error.hasInvalidCharacter'); + ErrorUtils.addErrorMessage(errors, 'legalFirstName', translate('privatePersonalDetails.error.hasInvalidCharacter')); } else if (!values.legalFirstName) { - errors.legalFirstName = 'common.error.fieldRequired'; + errors.legalFirstName = translate('common.error.fieldRequired'); } else if (values.legalFirstName.length > CONST.LEGAL_NAME.MAX_LENGTH) { ErrorUtils.addErrorMessage( errors, @@ -57,13 +57,13 @@ function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNameP ); } if (ValidationUtils.doesContainReservedWord(values.legalFirstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'legalFirstName', 'personalDetails.error.containsReservedWord'); + ErrorUtils.addErrorMessage(errors, 'legalFirstName', translate('personalDetails.error.containsReservedWord')); } } if (typeof values.legalLastName === 'string') { if (!ValidationUtils.isValidLegalName(values.legalLastName)) { - ErrorUtils.addErrorMessage(errors, 'legalLastName', 'privatePersonalDetails.error.hasInvalidCharacter'); + ErrorUtils.addErrorMessage(errors, 'legalLastName', translate('privatePersonalDetails.error.hasInvalidCharacter')); } else if (!values.legalLastName) { errors.legalLastName = 'common.error.fieldRequired'; } else if (values.legalLastName.length > CONST.LEGAL_NAME.MAX_LENGTH) { @@ -74,7 +74,7 @@ function LegalNamePage({privatePersonalDetails, isLoadingApp = true}: LegalNameP ); } if (ValidationUtils.doesContainReservedWord(values.legalLastName, CONST.DISPLAY_NAME.RESERVED_NAMES)) { - ErrorUtils.addErrorMessage(errors, 'legalLastName', 'personalDetails.error.containsReservedWord'); + ErrorUtils.addErrorMessage(errors, 'legalLastName', translate('personalDetails.error.containsReservedWord')); } } diff --git a/src/pages/settings/Report/RoomNamePage.tsx b/src/pages/settings/Report/RoomNamePage.tsx index 91451307aed7..67068182c8a5 100644 --- a/src/pages/settings/Report/RoomNamePage.tsx +++ b/src/pages/settings/Report/RoomNamePage.tsx @@ -53,16 +53,16 @@ function RoomNamePage({report, policy, reports}: RoomNamePageProps) { if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { // We error if the user doesn't enter a room name or left blank - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.pleaseEnterRoomName'); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.pleaseEnterRoomName')); } else if (!ValidationUtils.isValidRoomName(values.roomName)) { // We error if the room name has invalid characters - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomNameInvalidError'); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomNameInvalidError')); } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomNameReservedError', {reservedName: values.roomName})); } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, report?.policyID ?? '')) { // The room name can't be set to one that already exists on the policy - ErrorUtils.addErrorMessage(errors, 'roomName', 'newRoomPage.roomAlreadyExistsError'); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomAlreadyExistsError')); } else if (values.roomName.length > CONST.TITLE_CHARACTER_LIMIT) { ErrorUtils.addErrorMessage(errors, 'roomName', translate('common.error.characterLimitExceedCounter', {length: values.roomName.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } diff --git a/src/pages/settings/Security/CloseAccountPage.tsx b/src/pages/settings/Security/CloseAccountPage.tsx index 0fc0f5b7790a..d7a8087edb57 100644 --- a/src/pages/settings/Security/CloseAccountPage.tsx +++ b/src/pages/settings/Security/CloseAccountPage.tsx @@ -72,7 +72,7 @@ function CloseAccountPage({session}: CloseAccountPageProps) { const errors = ValidationUtils.getFieldRequiredErrors(values, ['phoneOrEmail']); if (values.phoneOrEmail && userEmailOrPhone && sanitizePhoneOrEmail(userEmailOrPhone) !== sanitizePhoneOrEmail(values.phoneOrEmail)) { - errors.phoneOrEmail = 'closeAccountPage.enterYourDefaultContactMethod'; + errors.phoneOrEmail = translate('closeAccountPage.enterYourDefaultContactMethod'); } return errors; }; diff --git a/src/pages/settings/Wallet/Card/GetPhysicalCardName.tsx b/src/pages/settings/Wallet/Card/GetPhysicalCardName.tsx index 1667aa8d36d3..deaa05350c7f 100644 --- a/src/pages/settings/Wallet/Card/GetPhysicalCardName.tsx +++ b/src/pages/settings/Wallet/Card/GetPhysicalCardName.tsx @@ -43,15 +43,15 @@ function GetPhysicalCardName({ const errors: OnValidateResult = {}; if (values?.legalFirstName && !ValidationUtils.isValidLegalName(values.legalFirstName)) { - errors.legalFirstName = 'privatePersonalDetails.error.hasInvalidCharacter'; + errors.legalFirstName = translate('privatePersonalDetails.error.hasInvalidCharacter'); } else if (!values?.legalFirstName) { - errors.legalFirstName = 'common.error.fieldRequired'; + errors.legalFirstName = translate('common.error.fieldRequired'); } if (values?.legalLastName && !ValidationUtils.isValidLegalName(values.legalLastName)) { - errors.legalLastName = 'privatePersonalDetails.error.hasInvalidCharacter'; + errors.legalLastName = translate('privatePersonalDetails.error.hasInvalidCharacter'); } else if (!values?.legalLastName) { - errors.legalLastName = 'common.error.fieldRequired'; + errors.legalLastName = translate('common.error.fieldRequired'); } return errors; diff --git a/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.tsx b/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.tsx index f709de4b51c3..56d5a29a3203 100644 --- a/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.tsx +++ b/src/pages/settings/Wallet/Card/GetPhysicalCardPhone.tsx @@ -42,9 +42,9 @@ function GetPhysicalCardPhone({ const errors: OnValidateResult = {}; if (!LoginUtils.validateNumber(phoneNumberToValidate)) { - errors.phoneNumber = 'common.error.phoneNumber'; + errors.phoneNumber = translate('common.error.phoneNumber'); } else if (!phoneNumberToValidate) { - errors.phoneNumber = 'common.error.fieldRequired'; + errors.phoneNumber = translate('common.error.fieldRequired'); } return errors; diff --git a/src/pages/tasks/NewTaskDetailsPage.tsx b/src/pages/tasks/NewTaskDetailsPage.tsx index ae269b1dbc81..3824bebde1e9 100644 --- a/src/pages/tasks/NewTaskDetailsPage.tsx +++ b/src/pages/tasks/NewTaskDetailsPage.tsx @@ -56,7 +56,7 @@ function NewTaskDetailsPage({task}: NewTaskDetailsPageProps) { if (!values.taskTitle) { // We error if the user doesn't enter a task name - ErrorUtils.addErrorMessage(errors, 'taskTitle', 'newTaskPage.pleaseEnterTaskName'); + ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('newTaskPage.pleaseEnterTaskName')); } else if (values.taskTitle.length > CONST.TITLE_CHARACTER_LIMIT) { ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } diff --git a/src/pages/tasks/NewTaskTitlePage.tsx b/src/pages/tasks/NewTaskTitlePage.tsx index 0ff6c178d24a..cdc9fc27596a 100644 --- a/src/pages/tasks/NewTaskTitlePage.tsx +++ b/src/pages/tasks/NewTaskTitlePage.tsx @@ -40,7 +40,7 @@ function NewTaskTitlePage({task}: NewTaskTitlePageProps) { if (!values.taskTitle) { // We error if the user doesn't enter a task name - ErrorUtils.addErrorMessage(errors, 'taskTitle', 'newTaskPage.pleaseEnterTaskName'); + ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('newTaskPage.pleaseEnterTaskName')); } else if (values.taskTitle.length > CONST.TITLE_CHARACTER_LIMIT) { ErrorUtils.addErrorMessage(errors, 'taskTitle', translate('common.error.characterLimitExceedCounter', {length: values.taskTitle.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } diff --git a/src/pages/tasks/TaskTitlePage.tsx b/src/pages/tasks/TaskTitlePage.tsx index 70ced7a81072..e99b0b3da67a 100644 --- a/src/pages/tasks/TaskTitlePage.tsx +++ b/src/pages/tasks/TaskTitlePage.tsx @@ -34,7 +34,7 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) const errors: FormInputErrors = {}; if (!title) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, 'newTaskPage.pleaseEnterTaskName'); + ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('newTaskPage.pleaseEnterTaskName')); } else if (title.length > CONST.TITLE_CHARACTER_LIMIT) { ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT})); } diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index ffc7299eb1f0..add00da9643a 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -125,7 +125,7 @@ function WorkspaceInviteMessagePage({ const validate = (): FormInputErrors => { const errorFields: FormInputErrors = {}; if (isEmptyObject(invitedEmailsToAccountIDsDraft)) { - errorFields.welcomeMessage = 'workspace.inviteMessage.inviteNoMembersError'; + errorFields.welcomeMessage = translate('workspace.inviteMessage.inviteNoMembersError'); } return errorFields; }; diff --git a/src/pages/workspace/WorkspaceNamePage.tsx b/src/pages/workspace/WorkspaceNamePage.tsx index 03d4e7107c1c..eee99a912d82 100644 --- a/src/pages/workspace/WorkspaceNamePage.tsx +++ b/src/pages/workspace/WorkspaceNamePage.tsx @@ -44,7 +44,7 @@ function WorkspaceNamePage({policy}: Props) { const name = values.name.trim(); if (!ValidationUtils.isRequiredFulfilled(name)) { - errors.name = 'workspace.editor.nameIsRequiredError'; + errors.name = translate('workspace.editor.nameIsRequiredError'); } else if ([...name].length > CONST.TITLE_CHARACTER_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 // code units. diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx index ae1822515fa5..efe457729e36 100644 --- a/src/pages/workspace/categories/CategoryForm.tsx +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -40,11 +40,11 @@ function CategoryForm({onSubmit, policyCategories, categoryName, validateEdit}: const newCategoryName = values.categoryName.trim(); if (!ValidationUtils.isRequiredFulfilled(newCategoryName)) { - errors.categoryName = 'workspace.categories.categoryRequiredError'; + errors.categoryName = translate('workspace.categories.categoryRequiredError'); } else if (policyCategories?.[newCategoryName]) { - errors.categoryName = 'workspace.categories.existingCategoryError'; + errors.categoryName = translate('workspace.categories.existingCategoryError'); } else if (newCategoryName === CONST.INVALID_CATEGORY_NAME) { - errors.categoryName = 'workspace.categories.invalidCategoryName'; + errors.categoryName = translate('workspace.categories.invalidCategoryName'); } else if ([...newCategoryName].length > CONST.CATEGORY_NAME_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. ErrorUtils.addErrorMessage( diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index 7b37375272d2..224233fff318 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -37,14 +37,14 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const newCategoryName = values.categoryName.trim(); if (!newCategoryName) { - errors.categoryName = 'workspace.categories.categoryRequiredError'; + errors.categoryName = translate('workspace.categories.categoryRequiredError'); } else if (policyCategories?.[newCategoryName] && currentCategoryName !== newCategoryName) { - errors.categoryName = 'workspace.categories.existingCategoryError'; + errors.categoryName = translate('workspace.categories.existingCategoryError'); } return errors; }, - [policyCategories, currentCategoryName], + [policyCategories, currentCategoryName, translate], ); const editCategory = useCallback( diff --git a/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx b/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx index 1a2f32449c41..8684792be806 100644 --- a/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx @@ -76,27 +76,27 @@ function WorkspaceOwnerPaymentCardForm({policy}: WorkspaceOwnerPaymentCardFormPr const errors = ValidationUtils.getFieldRequiredErrors(formValues, REQUIRED_FIELDS); if (formValues.nameOnCard && !ValidationUtils.isValidLegalName(formValues.nameOnCard)) { - errors.nameOnCard = 'addDebitCardPage.error.invalidName'; + errors.nameOnCard = translate('addDebitCardPage.error.invalidName'); } if (formValues.cardNumber && !ValidationUtils.isValidDebitCard(formValues.cardNumber.replace(/ /g, ''))) { - errors.cardNumber = 'addDebitCardPage.error.debitCardNumber'; + errors.cardNumber = translate('addDebitCardPage.error.debitCardNumber'); } if (formValues.expirationDate && !ValidationUtils.isValidExpirationDate(formValues.expirationDate)) { - errors.expirationDate = 'addDebitCardPage.error.expirationDate'; + errors.expirationDate = translate('addDebitCardPage.error.expirationDate'); } if (formValues.securityCode && !ValidationUtils.isValidSecurityCode(formValues.securityCode)) { - errors.securityCode = 'addDebitCardPage.error.securityCode'; + errors.securityCode = translate('addDebitCardPage.error.securityCode'); } if (formValues.addressStreet && !ValidationUtils.isValidAddress(formValues.addressStreet)) { - errors.addressStreet = 'addDebitCardPage.error.addressStreet'; + errors.addressStreet = translate('addDebitCardPage.error.addressStreet'); } if (formValues.addressZipCode && !ValidationUtils.isValidZipCode(formValues.addressZipCode)) { - errors.addressZipCode = 'addDebitCardPage.error.addressZipCode'; + errors.addressZipCode = translate('addDebitCardPage.error.addressZipCode'); } return errors; diff --git a/src/pages/workspace/tags/EditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx index a6b3b5dd6657..f2269bbb8c53 100644 --- a/src/pages/workspace/tags/EditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -43,14 +43,14 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { const tagName = values.tagName.trim(); const {tags} = PolicyUtils.getTagList(policyTags, route.params.orderWeight); if (!ValidationUtils.isRequiredFulfilled(tagName)) { - errors.tagName = 'workspace.tags.tagRequiredError'; + errors.tagName = translate('workspace.tags.tagRequiredError'); } else if (tags?.[tagName] && currentTagName !== tagName) { - errors.tagName = 'workspace.tags.existingTagError'; + errors.tagName = translate('workspace.tags.existingTagError'); } return errors; }, - [route.params.orderWeight, currentTagName, policyTags], + [policyTags, route.params.orderWeight, currentTagName, translate], ); const editTag = useCallback( diff --git a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx index 408bf4eb2366..1214507480ae 100644 --- a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx +++ b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx @@ -44,9 +44,9 @@ function CreateTagPage({route, policyTags}: CreateTagPageProps) { const {tags} = PolicyUtils.getTagList(policyTags, 0); if (!ValidationUtils.isRequiredFulfilled(tagName)) { - errors.tagName = 'workspace.tags.tagRequiredError'; + errors.tagName = translate('workspace.tags.tagRequiredError'); } else if (tags?.[tagName]) { - errors.tagName = 'workspace.tags.existingTagError'; + errors.tagName = translate('workspace.tags.existingTagError'); } else if ([...tagName].length > CONST.TAG_NAME_LIMIT) { // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. ErrorUtils.addErrorMessage(errors, 'tagName', translate('common.error.characterLimitExceedCounter', {length: [...tagName].length, limit: CONST.TAG_NAME_LIMIT})); diff --git a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx index f0b3ada7f2e5..91883cc989b0 100644 --- a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx @@ -30,20 +30,20 @@ type WorkspaceEditTagsPageOnyxProps = { type WorkspaceEditTagsPageProps = WorkspaceEditTagsPageOnyxProps & StackScreenProps; -const validateTagName = (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - if (!values[INPUT_IDS.POLICY_TAGS_NAME] && values[INPUT_IDS.POLICY_TAGS_NAME].trim() === '') { - errors[INPUT_IDS.POLICY_TAGS_NAME] = 'common.error.fieldRequired'; - } - return errors; -}; - function WorkspaceEditTagsPage({route, policyTags}: WorkspaceEditTagsPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const taglistName = useMemo(() => PolicyUtils.getTagListName(policyTags, route.params.orderWeight), [policyTags, route.params.orderWeight]); const {inputCallbackRef} = useAutoFocusInput(); + const validateTagName = (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + if (!values[INPUT_IDS.POLICY_TAGS_NAME] && values[INPUT_IDS.POLICY_TAGS_NAME].trim() === '') { + errors[INPUT_IDS.POLICY_TAGS_NAME] = translate('common.error.fieldRequired'); + } + return errors; + }; + const updateTaglistName = useCallback( (values: FormOnyxValues) => { if (values[INPUT_IDS.POLICY_TAGS_NAME] !== taglistName) { diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx index dbade9a82aa2..f24a388011ab 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx @@ -36,16 +36,19 @@ function WorkspaceTaxesSettingsCustomTaxName({ const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); - const validate = useCallback((values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const customTaxName = values[INPUT_IDS.NAME]; + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const customTaxName = values[INPUT_IDS.NAME]; - if (!ValidationUtils.isRequiredFulfilled(customTaxName)) { - errors.name = 'workspace.taxes.error.customNameRequired'; - } + if (!ValidationUtils.isRequiredFulfilled(customTaxName)) { + errors.name = translate('workspace.taxes.error.customNameRequired'); + } - return errors; - }, []); + return errors; + }, + [translate], + ); const submit = ({name}: WorkspaceTaxCustomName) => { setPolicyCustomTaxName(policyID, name); From 9a5608750bab8c0fded409e94637cd5d506d76eb Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 4 Jun 2024 02:08:17 +0530 Subject: [PATCH 039/228] Update --- src/components/LHNOptionsList/LHNOptionsList.tsx | 9 ++++++--- src/components/TextBlock.tsx | 7 ++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 05b7e150fc53..4cac95ed9527 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -71,7 +71,8 @@ function LHNOptionsList({ () => ( diff --git a/src/components/TextBlock.tsx b/src/components/TextBlock.tsx index 0770bd0891cc..5d1414d95eba 100644 --- a/src/components/TextBlock.tsx +++ b/src/components/TextBlock.tsx @@ -16,17 +16,14 @@ type TextBlockProps = { }; function TextBlock({color, textStyles, text}: TextBlockProps) { - const theme = useTheme(); - const styles = useThemeStyles(); - const words = useMemo(() => text.match(/(\S+\s*)/g) ?? [], [text]); return ( <> {words.map((word) => ( {word} From 267cf232b2f9dc94f5555568e7d15ce97c5e04e5 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Jun 2024 15:31:01 +0700 Subject: [PATCH 040/228] fix: Workspace - Wrong members number is displayed --- src/libs/actions/Policy/Policy.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 4d918352ba91..7ffbe8ccb355 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1321,6 +1321,9 @@ function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: I }, isOptimisticReport: false, pendingChatMembers: null, + participants: { + [accountID]: allPersonalDetails && allPersonalDetails[accountID] ? {} : null, + }, }, }); workspaceMembersChats.onyxSuccessData.push({ From b5ccc54e52ea634818c9ba08e4f419021f2d61a9 Mon Sep 17 00:00:00 2001 From: Shridhar Goel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:47:17 +0530 Subject: [PATCH 041/228] Update LHNOptionsList.tsx --- src/components/LHNOptionsList/LHNOptionsList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 4cac95ed9527..f906b8efb35b 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -103,7 +103,7 @@ function LHNOptionsList({ /> ), - [styles.alignItemsCenter, styles.flexRow, styles.justifyContentCenter, styles.flexWrap, styles.textAlignCenter, styles.mh1, theme.icon, theme.placeholderText, translate], + [styles.alignItemsCenter, styles.flexRow, styles.justifyContentCenter, styles.flexWrap, styles.textAlignCenter, styles.mh1, theme.icon, theme.textSupporting, styles.textNormal, translate], ); /** From e307c86e8a18df915c599c0128c2efb05bb12909 Mon Sep 17 00:00:00 2001 From: Shridhar Goel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:47:33 +0530 Subject: [PATCH 042/228] Update TextBlock.tsx --- src/components/TextBlock.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/TextBlock.tsx b/src/components/TextBlock.tsx index 5d1414d95eba..ea7899b1c533 100644 --- a/src/components/TextBlock.tsx +++ b/src/components/TextBlock.tsx @@ -1,7 +1,5 @@ import React, {memo, useMemo} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; import Text from './Text'; type TextBlockProps = { From a72b9310fb501802316f267a223d7a546fdf9332 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:22:40 +0530 Subject: [PATCH 043/228] Update --- src/components/LHNOptionsList/LHNOptionsList.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index f906b8efb35b..27325be54168 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -103,7 +103,18 @@ function LHNOptionsList({ /> ), - [styles.alignItemsCenter, styles.flexRow, styles.justifyContentCenter, styles.flexWrap, styles.textAlignCenter, styles.mh1, theme.icon, theme.textSupporting, styles.textNormal, translate], + [ + styles.alignItemsCenter, + styles.flexRow, + styles.justifyContentCenter, + styles.flexWrap, + styles.textAlignCenter, + styles.mh1, + theme.icon, + theme.textSupporting, + styles.textNormal, + translate, + ], ); /** From a8fbfa6ae5e5d11879015680fb9b9d3dc739df27 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 4 Jun 2024 13:04:00 +0200 Subject: [PATCH 044/228] use `personalDetails.login` as account name --- src/libs/OnyxAwareParser.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts index 586b914216be..3366de44e76c 100644 --- a/src/libs/OnyxAwareParser.ts +++ b/src/libs/OnyxAwareParser.ts @@ -26,16 +26,18 @@ Onyx.connect({ return; } - accountIDToNameMap[personalDetails.accountID] = personalDetails.displayName ?? String(personalDetails.accountID); + accountIDToNameMap[personalDetails.accountID] = personalDetails.login ?? String(personalDetails.accountID); }); }, }); function parseHtmlToMarkdown(html: string): string { + // TODO: change `reportIdToName` to `reportIDToName` (changes in expensify-common) return parser.htmlToMarkdown(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); } function parseHtmlToText(html: string): string { + // TODO: change `reportIdToName` to `reportIDToName` (changes in expensify-common) return parser.htmlToText(html, {reportIdToName: reportIDToNameMap, accountIDToName: accountIDToNameMap}); } From 25cecda20fff07594b6f7ca437284d31ffeae0e8 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 4 Jun 2024 16:55:02 +0200 Subject: [PATCH 045/228] e2e: terminate app only when all network requests were finished --- src/libs/E2E/tests/appStartTimeTest.e2e.ts | 2 + src/libs/E2E/tests/chatOpeningTest.e2e.ts | 11 +++-- src/libs/E2E/tests/linkingTest.e2e.ts | 2 + .../E2E/tests/openChatFinderPageTest.e2e.ts | 11 +++-- src/libs/E2E/tests/reportTypingTest.e2e.ts | 5 ++- src/libs/E2E/utils/NetworkInterceptor.ts | 40 +++++++++++++++++++ tests/e2e/config.ts | 1 - tests/e2e/testRunner.ts | 2 +- 8 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/libs/E2E/tests/appStartTimeTest.e2e.ts b/src/libs/E2E/tests/appStartTimeTest.e2e.ts index 321fc3773d51..456c62b24ac9 100644 --- a/src/libs/E2E/tests/appStartTimeTest.e2e.ts +++ b/src/libs/E2E/tests/appStartTimeTest.e2e.ts @@ -3,6 +3,7 @@ import type {PerformanceEntry} from 'react-native-performance'; import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; +import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Performance from '@libs/Performance'; const test = () => { @@ -30,6 +31,7 @@ const test = () => { }), ), ) + .then(waitForActiveRequestsToBeEmpty) .then(() => { console.debug('[E2E] Done, exiting…'); E2EClient.submitTestDone(); diff --git a/src/libs/E2E/tests/chatOpeningTest.e2e.ts b/src/libs/E2E/tests/chatOpeningTest.e2e.ts index 8e43c4ece564..c8887cab535d 100644 --- a/src/libs/E2E/tests/chatOpeningTest.e2e.ts +++ b/src/libs/E2E/tests/chatOpeningTest.e2e.ts @@ -5,6 +5,7 @@ import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; import getPromiseWithResolve from '@libs/E2E/utils/getPromiseWithResolve'; +import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import CONST from '@src/CONST'; @@ -29,11 +30,13 @@ const test = (config: NativeConfig) => { const [renderChatPromise, renderChatResolve] = getPromiseWithResolve(); const [chatTTIPromise, chatTTIResolve] = getPromiseWithResolve(); - Promise.all([renderChatPromise, chatTTIPromise]).then(() => { - console.debug(`[E2E] Submitting!`); + Promise.all([renderChatPromise, chatTTIPromise]) + .then(waitForActiveRequestsToBeEmpty) + .then(() => { + console.debug(`[E2E] Submitting!`); - E2EClient.submitTestDone(); - }); + E2EClient.submitTestDone(); + }); Performance.subscribeToMeasurements((entry) => { if (entry.name === CONST.TIMING.SIDEBAR_LOADED) { diff --git a/src/libs/E2E/tests/linkingTest.e2e.ts b/src/libs/E2E/tests/linkingTest.e2e.ts index c48ffb5a8057..8a1980a8b28a 100644 --- a/src/libs/E2E/tests/linkingTest.e2e.ts +++ b/src/libs/E2E/tests/linkingTest.e2e.ts @@ -6,6 +6,7 @@ import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; +import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import CONST from '@src/CONST'; @@ -51,6 +52,7 @@ const test = (config: NativeConfig) => { name: 'Comment linking', duration: entry.duration, }) + .then(waitForActiveRequestsToBeEmpty) .then(() => { console.debug('[E2E] Test completed successfully, exiting…'); E2EClient.submitTestDone(); diff --git a/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts b/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts index 4ac7995b914f..1837d31f7110 100644 --- a/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts +++ b/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts @@ -3,6 +3,7 @@ import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; import getPromiseWithResolve from '@libs/E2E/utils/getPromiseWithResolve'; +import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import CONST from '@src/CONST'; @@ -25,11 +26,13 @@ const test = () => { const [openSearchPagePromise, openSearchPageResolve] = getPromiseWithResolve(); const [loadSearchOptionsPromise, loadSearchOptionsResolve] = getPromiseWithResolve(); - Promise.all([openSearchPagePromise, loadSearchOptionsPromise]).then(() => { - console.debug(`[E2E] Submitting!`); + Promise.all([openSearchPagePromise, loadSearchOptionsPromise]) + .then(waitForActiveRequestsToBeEmpty) + .then(() => { + console.debug(`[E2E] Submitting!`); - E2EClient.submitTestDone(); - }); + E2EClient.submitTestDone(); + }); Performance.subscribeToMeasurements((entry) => { if (entry.name === CONST.TIMING.SIDEBAR_LOADED) { diff --git a/src/libs/E2E/tests/reportTypingTest.e2e.ts b/src/libs/E2E/tests/reportTypingTest.e2e.ts index 817bda941611..fec5daa246fe 100644 --- a/src/libs/E2E/tests/reportTypingTest.e2e.ts +++ b/src/libs/E2E/tests/reportTypingTest.e2e.ts @@ -5,6 +5,7 @@ import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import waitForKeyboard from '@libs/E2E/actions/waitForKeyboard'; import E2EClient from '@libs/E2E/client'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; +import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import {getRerenderCount, resetRerenderCount} from '@pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e'; @@ -54,7 +55,9 @@ const test = (config: NativeConfig) => { branch: Config.E2E_BRANCH, name: 'Composer typing rerender count', renderCount: rerenderCount, - }).then(E2EClient.submitTestDone); + }) + .then(waitForActiveRequestsToBeEmpty) + .then(E2EClient.submitTestDone); }, 3000); }) .catch((error) => { diff --git a/src/libs/E2E/utils/NetworkInterceptor.ts b/src/libs/E2E/utils/NetworkInterceptor.ts index 3a4a48f7db53..ad23afeb0c3b 100644 --- a/src/libs/E2E/utils/NetworkInterceptor.ts +++ b/src/libs/E2E/utils/NetworkInterceptor.ts @@ -1,4 +1,5 @@ /* eslint-disable @lwc/lwc/no-async-await */ +import {DeviceEventEmitter} from 'react-native'; import type {NetworkCacheEntry, NetworkCacheMap} from '@libs/E2E/types'; const LOG_TAG = `[E2E][NetworkInterceptor]`; @@ -93,6 +94,33 @@ function hashFetchArgs(args: Parameters) { return `${url}${JSON.stringify(headers)}`; } +let activeRequestsCount = 0; + +const ACTIVE_REQUESTS_QUEUE_IS_EMPTY_EVENT = 'activeRequestsQueueIsEmpty'; + +/** + * Assures that ongoing network requests are empty. **Highly desirable** to call this function before closing the app. + * Otherwise if some requests are persisted - they will be executed on the next app start. And it can lead to a situation + * where we can have `N * M` requests (where `N` is the number of app run per test and `M` is the number of test suites) + * and such big amount of requests can lead to a situation, where first app run (in test suite to cache network requests) + * may be blocked by spinners and lead to unbelievable big time execution, which eventually will be bigger than timeout and + * will lead to a test failure. + */ +function waitForActiveRequestsToBeEmpty(): Promise { + console.debug('Waiting for requests queue to be empty...', activeRequestsCount); + + if (activeRequestsCount === 0) { + return Promise.resolve(); + } + + return new Promise((resolve) => { + const subscription = DeviceEventEmitter.addListener(ACTIVE_REQUESTS_QUEUE_IS_EMPTY_EVENT, () => { + subscription.remove(); + resolve(); + }); + }); +} + /** * Install a network interceptor by overwriting the global fetch function: * - Overwrites fetch globally with a custom implementation @@ -145,6 +173,8 @@ export default function installNetworkInterceptor( console.debug('!!! Missed cache hit for url:', url); } + activeRequestsCount++; + return originalFetch(...args) .then(async (res) => { if (networkCache != null) { @@ -166,6 +196,16 @@ export default function installNetworkInterceptor( .then((res) => { console.debug(LOG_TAG, 'Network cache updated!'); return res; + }) + .finally(() => { + console.debug('Active requests count:', activeRequestsCount); + + activeRequestsCount--; + + if (activeRequestsCount === 0) { + DeviceEventEmitter.emit(ACTIVE_REQUESTS_QUEUE_IS_EMPTY_EVENT); + } }); }; } +export {waitForActiveRequestsToBeEmpty}; diff --git a/tests/e2e/config.ts b/tests/e2e/config.ts index 4120019de692..62f73c454631 100644 --- a/tests/e2e/config.ts +++ b/tests/e2e/config.ts @@ -68,7 +68,6 @@ export default { TESTS_CONFIG: { [TEST_NAMES.AppStartTime]: { name: TEST_NAMES.AppStartTime, - warmupRuns: 1, // ... any additional config you might need }, [TEST_NAMES.OpenChatFinderPage]: { diff --git a/tests/e2e/testRunner.ts b/tests/e2e/testRunner.ts index fcf93d525f8e..fc9cb069acad 100644 --- a/tests/e2e/testRunner.ts +++ b/tests/e2e/testRunner.ts @@ -184,7 +184,7 @@ const runTests = async (): Promise => { // by default we do 2 warmups: // - first warmup to pass a login flow // - second warmup to pass an actual flow and cache network requests - const iterations = test.warmupRuns ?? 2; + const iterations = 2; for (let i = 0; i < iterations; i++) { // Warmup the main app: await runTestIteration(config.MAIN_APP_PACKAGE, `[MAIN] ${warmupText}. Iteration ${i + 1}/${iterations}`); From ba60fea1b49ab0f2eafd8cdb95c5b9de16e1a921 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 5 Jun 2024 01:34:42 +0700 Subject: [PATCH 046/228] clean code --- src/components/AddressForm.tsx | 8 ++--- .../MoneyRequestConfirmationList.tsx | 25 +++++++++------ src/components/PDFView/PDFPasswordForm.tsx | 2 +- src/libs/OptionsListUtils.ts | 2 +- .../PersonalInfo/substeps/AddressStep.tsx | 25 ++++++++------- .../PersonalInfo/substeps/DateOfBirthStep.tsx | 25 ++++++++------- .../PersonalInfo/substeps/FullNameStep.tsx | 25 ++++++++------- .../PersonalInfo/substeps/PhoneNumberStep.tsx | 19 +++++++----- .../substeps/SocialSecurityNumberStep.tsx | 19 +++++++----- .../substeps/ConfirmationBusiness.tsx | 19 +++++++----- .../substeps/IncorporationDateBusiness.tsx | 23 ++++++++------ .../BusinessInfo/substeps/NameBusiness.tsx | 19 +++++++----- .../substeps/PhoneNumberBusiness.tsx | 19 +++++++----- .../BusinessInfo/substeps/TaxIdBusiness.tsx | 19 +++++++----- .../BusinessInfo/substeps/WebsiteBusiness.tsx | 20 ++++++------ .../substeps/ConfirmAgreements.tsx | 31 ++++++++++--------- .../PersonalInfo/substeps/Address.tsx | 25 ++++++++------- .../PersonalInfo/substeps/DateOfBirth.tsx | 25 ++++++++------- .../PersonalInfo/substeps/FullName.tsx | 25 ++++++++------- .../substeps/SocialSecurityNumber.tsx | 19 +++++++----- .../Profile/Contacts/NewContactMethodPage.tsx | 2 +- .../BaseTwoFactorAuthForm.tsx | 8 +++-- .../workspace/tags/WorkspaceEditTagsPage.tsx | 17 +++++----- 23 files changed, 239 insertions(+), 182 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index 8601c3fa1e9f..006550664f93 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -116,18 +116,18 @@ function AddressForm({ const countrySpecificZipRegex = countryRegexDetails?.regex; const countryZipFormat = countryRegexDetails?.samples ?? ''; - ErrorUtils.addErrorMessage(errors, 'firstName', 'bankAccount.error.firstName'); + ErrorUtils.addErrorMessage(errors, 'firstName', translate('bankAccount.error.firstName')); if (countrySpecificZipRegex) { if (!countrySpecificZipRegex.test(values.zipPostCode?.trim().toUpperCase())) { if (ValidationUtils.isRequiredFulfilled(values.zipPostCode?.trim())) { - errors.zipPostCode = ['privatePersonalDetails.error.incorrectZipFormat', countryZipFormat]; + errors.zipPostCode = [translate('privatePersonalDetails.error.incorrectZipFormat', countryZipFormat)]; } else { - errors.zipPostCode = 'common.error.fieldRequired'; + errors.zipPostCode = translate('common.error.fieldRequired'); } } } else if (!CONST.GENERIC_ZIP_CODE_REGEX.test(values?.zipPostCode?.trim()?.toUpperCase() ?? '')) { - errors.zipPostCode = 'privatePersonalDetails.error.incorrectZipFormat'; + errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat'); } return errors; diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 9f01d63cc439..a073b021207d 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -35,6 +35,7 @@ import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; import * as IOU from '@userActions/IOU'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; @@ -322,7 +323,7 @@ function MoneyRequestConfirmationList({ const previousTransactionCurrency = usePrevious(transaction?.currency); const isFocused = useIsFocused(); - const [formError, debouncedFormError, setFormError] = useDebouncedState(''); + const [formError, debouncedFormError, setFormError] = useDebouncedState(''); const [didConfirm, setDidConfirm] = useState(false); const [didConfirmSplit, setDidConfirmSplit] = useState(false); @@ -349,8 +350,12 @@ function MoneyRequestConfirmationList({ const isCategoryRequired = !!policy?.requiresCategory; useEffect(() => { + if (shouldDisplayFieldError && hasSmartScanFailed) { + setFormError('iou.receiptScanningFailed'); + return; + } if (shouldDisplayFieldError && didConfirmSplit) { - setFormError(translate('iou.error.genericSmartscanFailureMessage')); + setFormError('iou.error.genericSmartscanFailureMessage'); return; } @@ -435,7 +440,7 @@ function MoneyRequestConfirmationList({ const shares: number[] = Object.values(splitSharesMap).map((splitShare) => splitShare?.amount ?? 0); const sumOfShares = shares?.reduce((prev, current): number => prev + current, 0); if (sumOfShares !== iouAmount) { - setFormError(translate('iou.error.invalidSplit')); + setFormError('iou.error.invalidSplit'); return; } @@ -445,7 +450,7 @@ function MoneyRequestConfirmationList({ // A split must have at least two participants with amounts bigger than 0 if (participantsWithAmount.length === 1) { - setFormError(translate('iou.error.invalidSplitParticipants')); + setFormError('iou.error.invalidSplitParticipants'); return; } @@ -702,11 +707,11 @@ function MoneyRequestConfirmationList({ return; } if (!isEditingSplitBill && isMerchantRequired && (isMerchantEmpty || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null)))) { - setFormError(translate('iou.error.invalidMerchant')); + setFormError('iou.error.invalidMerchant'); return; } if (iouCategory.length > CONST.API_TRANSACTION_CATEGORY_MAX_LENGTH) { - setFormError(translate('iou.error.invalidCategoryLength')); + setFormError('iou.error.invalidCategoryLength'); return; } if (iouType !== CONST.IOU.TYPE.PAY) { @@ -751,7 +756,6 @@ function MoneyRequestConfirmationList({ formError, iouType, setFormError, - translate, onSendMoney, iouCurrencyCode, isDistanceRequest, @@ -808,7 +812,7 @@ function MoneyRequestConfirmationList({ )} @@ -825,10 +829,11 @@ function MoneyRequestConfirmationList({ policyID, splitOrRequestOptions, formError, - debouncedFormError, - shouldShowReadOnlySplits, styles.ph1, styles.mb2, + shouldShowReadOnlySplits, + debouncedFormError, + translate, ]); // An intermediate structure that helps us classify the fields as "primary" and "supplementary". diff --git a/src/components/PDFView/PDFPasswordForm.tsx b/src/components/PDFView/PDFPasswordForm.tsx index e212d79b27d6..4b6491addbef 100644 --- a/src/components/PDFView/PDFPasswordForm.tsx +++ b/src/components/PDFView/PDFPasswordForm.tsx @@ -90,7 +90,7 @@ function PDFPasswordForm({isFocused, isPasswordInvalid = false, shouldShowLoadin return true; } if (!password) { - setValidationErrorText('attachmentView.passwordRequired'); + setValidationErrorText(translate('attachmentView.passwordRequired')); } return false; }; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 7cc0b49a88d9..6f475af474ec 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -539,7 +539,7 @@ function getAllReportErrors(report: OnyxEntry, reportActions: OnyxEntry< } } else if ((ReportUtils.isIOUReport(report) || ReportUtils.isExpenseReport(report)) && report?.ownerAccountID === currentUserAccountID) { if (ReportUtils.shouldShowRBRForMissingSmartscanFields(report?.reportID ?? '') && !ReportUtils.isSettled(report?.reportID)) { - reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericSmartscanFailureMessage'); + reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericSmartscanFailureMessage'); } } else if (ReportUtils.hasSmartscanError(Object.values(reportActions ?? {}))) { reportActionErrors.smartscan = ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericSmartscanFailureMessage'); diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx index 22347f1b3bff..56703fb4b5c4 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/AddressStep.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -38,19 +38,22 @@ function AddressStep({onNext, isEditing}: SubStepProps) { zipCode: walletAdditionalDetails?.[PERSONAL_INFO_STEP_KEY.ZIP_CODE] ?? '', }; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { - errors.addressStreet = translate('bankAccount.error.addressStreet'); - } + if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { + errors.addressStreet = translate('bankAccount.error.addressStreet'); + } - if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = translate('bankAccount.error.zipCode'); - } + if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { + errors.addressZipCode = translate('bankAccount.error.zipCode'); + } - return errors; - }; + return errors; + }, + [translate], + ); const handleSubmit = useWalletAdditionalDetailsStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx index 7c4f5f43f2a1..d476fdcc5c86 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/DateOfBirthStep.tsx @@ -1,5 +1,5 @@ import {subYears} from 'date-fns'; -import React from 'react'; +import React, {useCallback} from 'react'; import {useOnyx} from 'react-native-onyx'; import DatePicker from '@components/DatePicker'; import FormProvider from '@components/Form/FormProvider'; @@ -26,19 +26,22 @@ function DateOfBirthStep({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.dob) { - if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { - errors.dob = translate('bankAccount.error.dob'); - } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { - errors.dob = translate('bankAccount.error.age'); + if (values.dob) { + if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.dob'); + } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.age'); + } } - } - return errors; - }; + return errors; + }, + [translate], + ); const [walletAdditionalDetails] = useOnyx(ONYXKEYS.WALLET_ADDITIONAL_DETAILS); diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx index 89240720f533..b40fb2202943 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/FullNameStep.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -30,17 +30,20 @@ function FullNameStep({onNext, isEditing}: SubStepProps) { lastName: walletAdditionalDetails?.[PERSONAL_INFO_STEP_KEY.LAST_NAME] ?? '', }; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.legalFirstName && !ValidationUtils.isValidLegalName(values.legalFirstName)) { - errors.legalFirstName = translate('bankAccount.error.firstName'); - } + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + if (values.legalFirstName && !ValidationUtils.isValidLegalName(values.legalFirstName)) { + errors.legalFirstName = translate('bankAccount.error.firstName'); + } - if (values.legalLastName && !ValidationUtils.isValidLegalName(values.legalLastName)) { - errors.legalLastName = translate('bankAccount.error.lastName'); - } - return errors; - }; + if (values.legalLastName && !ValidationUtils.isValidLegalName(values.legalLastName)) { + errors.legalLastName = translate('bankAccount.error.lastName'); + } + return errors; + }, + [translate], + ); const handleSubmit = useWalletAdditionalDetailsStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx index a5326504a1c6..60bfa431ca78 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/PhoneNumberStep.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -27,14 +27,17 @@ function PhoneNumberStep({onNext, isEditing}: SubStepProps) { const defaultPhoneNumber = walletAdditionalDetails?.[PERSONAL_INFO_STEP_KEY.PHONE_NUMBER] ?? ''; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.phoneNumber && !ValidationUtils.isValidUSPhone(values.phoneNumber, true)) { - errors.phoneNumber = translate('bankAccount.error.phoneNumber'); - } - return errors; - }; + if (values.phoneNumber && !ValidationUtils.isValidUSPhone(values.phoneNumber, true)) { + errors.phoneNumber = translate('bankAccount.error.phoneNumber'); + } + return errors; + }, + [translate], + ); const handleSubmit = useWalletAdditionalDetailsStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx b/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx index cb3f2fa263f7..bdaa3fe98f67 100644 --- a/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx +++ b/src/pages/EnablePayments/PersonalInfo/substeps/SocialSecurityNumberStep.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -23,15 +23,18 @@ function SocialSecurityNumberStep({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.ssn && !ValidationUtils.isValidSSNLastFour(values.ssn)) { - errors.ssn = translate('bankAccount.error.ssnLast4'); - } + if (values.ssn && !ValidationUtils.isValidSSNLastFour(values.ssn)) { + errors.ssn = translate('bankAccount.error.ssnLast4'); + } - return errors; - }; + return errors; + }, + [translate], + ); const [walletAdditionalDetails] = useOnyx(ONYXKEYS.WALLET_ADDITIONAL_DETAILS); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx index fa660c98d8bc..fb6a246ce84e 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/ConfirmationBusiness.tsx @@ -1,5 +1,5 @@ import type {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; -import React, {useMemo} from 'react'; +import React, {useCallback, useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; @@ -52,15 +52,18 @@ function ConfirmationBusiness({reimbursementAccount, reimbursementAccountDraft, const {translate} = useLocalize(); const styles = useThemeStyles(); - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, [BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS]); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, [BUSINESS_INFO_STEP_KEYS.HAS_NO_CONNECTION_TO_CANNABIS]); - if (!values.hasNoConnectionToCannabis) { - errors.hasNoConnectionToCannabis = translate('bankAccount.error.restrictedBusiness'); - } + if (!values.hasNoConnectionToCannabis) { + errors.hasNoConnectionToCannabis = translate('bankAccount.error.restrictedBusiness'); + } - return errors; - }; + return errors; + }, + [translate], + ); const values = useMemo(() => getSubstepValues(BUSINESS_INFO_STEP_KEYS, reimbursementAccountDraft, reimbursementAccount), [reimbursementAccount, reimbursementAccountDraft]); diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx index 58a1feb710c2..274e087cc415 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/IncorporationDateBusiness.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import DatePicker from '@components/DatePicker'; @@ -33,17 +33,20 @@ function IncorporationDateBusiness({reimbursementAccount, reimbursementAccountDr const {translate} = useLocalize(); const styles = useThemeStyles(); - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { - errors.incorporationDate = translate('common.error.dateInvalid'); - } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { - errors.incorporationDate = translate('bankAccount.error.incorporationDateFuture'); - } + if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { + errors.incorporationDate = translate('common.error.dateInvalid'); + } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { + errors.incorporationDate = translate('bankAccount.error.incorporationDateFuture'); + } - return errors; - }; + return errors; + }, + [translate], + ); const defaultCompanyIncorporationDate = reimbursementAccount?.achData?.incorporationDate ?? reimbursementAccountDraft?.incorporationDate ?? ''; diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx index 712f42578f36..d7be083df3a0 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/NameBusiness.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -35,15 +35,18 @@ function NameBusiness({reimbursementAccount, onNext, isEditing}: NameBusinessPro const shouldDisableCompanyName = !!(bankAccountID && defaultCompanyName && reimbursementAccount?.achData?.state !== 'SETUP'); - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.companyName && !ValidationUtils.isValidCompanyName(values.companyName)) { - errors.companyName = translate('bankAccount.error.companyName'); - } + if (values.companyName && !ValidationUtils.isValidCompanyName(values.companyName)) { + errors.companyName = translate('bankAccount.error.companyName'); + } - return errors; - }; + return errors; + }, + [translate], + ); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx index b22424d194f9..e0c83737b6f8 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/PhoneNumberBusiness.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -31,15 +31,18 @@ function PhoneNumberBusiness({reimbursementAccount, onNext, isEditing}: PhoneNum const styles = useThemeStyles(); const defaultCompanyPhoneNumber = reimbursementAccount?.achData?.companyPhone ?? ''; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { - errors.companyPhone = translate('bankAccount.error.phoneNumber'); - } + if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { + errors.companyPhone = translate('bankAccount.error.phoneNumber'); + } - return errors; - }; + return errors; + }, + [translate], + ); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx index 6fba24b901e3..8e3ec6d5ec6e 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TaxIdBusiness.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -32,15 +32,18 @@ function TaxIdBusiness({reimbursementAccount, onNext, isEditing}: TaxIdBusinessP const bankAccountID = reimbursementAccount?.achData?.bankAccountID ?? 0; const shouldDisableCompanyTaxID = !!(bankAccountID && defaultCompanyTaxId && reimbursementAccount?.achData?.state !== 'SETUP'); - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { - errors.companyTaxID = translate('bankAccount.error.taxID'); - } + if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { + errors.companyTaxID = translate('bankAccount.error.taxID'); + } - return errors; - }; + return errors; + }, + [translate], + ); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx index 7115e1a82158..541e47f6cb10 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/WebsiteBusiness.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo} from 'react'; +import React, {useCallback, useEffect, useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -41,16 +41,18 @@ function WebsiteBusiness({reimbursementAccount, user, session, onNext, isEditing const defaultWebsiteExample = useMemo(() => getDefaultCompanyWebsite(session, user), [session, user]); const defaultCompanyWebsite = reimbursementAccount?.achData?.website ?? defaultWebsiteExample; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.website && !ValidationUtils.isValidWebsite(values.website)) { - errors.website = translate('bankAccount.error.website'); - } - - return errors; - }; + if (values.website && !ValidationUtils.isValidWebsite(values.website)) { + errors.website = translate('bankAccount.error.website'); + } + return errors; + }, + [translate], + ); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, onNext, diff --git a/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx b/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx index 1e2f004b9c8d..71d7a80ab58b 100644 --- a/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx +++ b/src/pages/ReimbursementAccount/CompleteVerification/substeps/ConfirmAgreements.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; @@ -58,23 +58,26 @@ function ConfirmAgreements({onNext, reimbursementAccount}: ConfirmAgreementsProp certifyTrueInformation: reimbursementAccount?.achData?.certifyTrueInformation ?? false, acceptTermsAndConditions: reimbursementAccount?.achData?.acceptTermsAndConditions ?? false, }; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (!ValidationUtils.isRequiredFulfilled(values.acceptTermsAndConditions)) { - errors.acceptTermsAndConditions = translate('common.error.acceptTerms'); - } + if (!ValidationUtils.isRequiredFulfilled(values.acceptTermsAndConditions)) { + errors.acceptTermsAndConditions = translate('common.error.acceptTerms'); + } - if (!ValidationUtils.isRequiredFulfilled(values.certifyTrueInformation)) { - errors.certifyTrueInformation = translate('completeVerificationStep.certifyTrueAndAccurateError'); - } + if (!ValidationUtils.isRequiredFulfilled(values.certifyTrueInformation)) { + errors.certifyTrueInformation = translate('completeVerificationStep.certifyTrueAndAccurateError'); + } - if (!ValidationUtils.isRequiredFulfilled(values.isAuthorizedToUseBankAccount)) { - errors.isAuthorizedToUseBankAccount = translate('completeVerificationStep.isAuthorizedToUseBankAccountError'); - } + if (!ValidationUtils.isRequiredFulfilled(values.isAuthorizedToUseBankAccount)) { + errors.isAuthorizedToUseBankAccount = translate('completeVerificationStep.isAuthorizedToUseBankAccountError'); + } - return errors; - }; + return errors; + }, + [translate], + ); return ( ): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { - errors.requestorAddressStreet = translate('bankAccount.error.addressStreet'); - } + if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { + errors.requestorAddressStreet = translate('bankAccount.error.addressStreet'); + } - if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { - errors.requestorAddressZipCode = translate('bankAccount.error.zipCode'); - } + if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { + errors.requestorAddressZipCode = translate('bankAccount.error.zipCode'); + } - return errors; - }; + return errors; + }, + [translate], + ); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx index c47139af8a90..6fe391bbe957 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/DateOfBirth.tsx @@ -1,5 +1,5 @@ import {subYears} from 'date-fns'; -import React from 'react'; +import React, {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import DatePicker from '@components/DatePicker'; @@ -36,19 +36,22 @@ function DateOfBirth({reimbursementAccount, reimbursementAccountDraft, onNext, i const {translate} = useLocalize(); const styles = useThemeStyles(); - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.dob) { - if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { - errors.dob = translate('bankAccount.error.dob'); - } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { - errors.dob = translate('bankAccount.error.age'); + if (values.dob) { + if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.dob'); + } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { + errors.dob = translate('bankAccount.error.age'); + } } - } - return errors; - }; + return errors; + }, + [translate], + ); const dobDefaultValue = reimbursementAccount?.achData?.[PERSONAL_INFO_DOB_KEY] ?? reimbursementAccountDraft?.[PERSONAL_INFO_DOB_KEY] ?? ''; diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx index 79da5ea4c707..bab467707fd4 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/FullName.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -36,17 +36,20 @@ function FullName({reimbursementAccount, onNext, isEditing}: FullNameProps) { lastName: reimbursementAccount?.achData?.[PERSONAL_INFO_STEP_KEY.LAST_NAME] ?? '', }; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.firstName && !ValidationUtils.isValidLegalName(values.firstName)) { - errors.firstName = translate('bankAccount.error.firstName'); - } + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + if (values.firstName && !ValidationUtils.isValidLegalName(values.firstName)) { + errors.firstName = translate('bankAccount.error.firstName'); + } - if (values.lastName && !ValidationUtils.isValidLegalName(values.lastName)) { - errors.lastName = translate('bankAccount.error.lastName'); - } - return errors; - }; + if (values.lastName && !ValidationUtils.isValidLegalName(values.lastName)) { + errors.lastName = translate('bankAccount.error.lastName'); + } + return errors; + }, + [translate], + ); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx b/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx index d33a0ac6243a..390880fa65f2 100644 --- a/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx +++ b/src/pages/ReimbursementAccount/PersonalInfo/substeps/SocialSecurityNumber.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -34,15 +34,18 @@ function SocialSecurityNumber({reimbursementAccount, onNext, isEditing}: SocialS const defaultSsnLast4 = reimbursementAccount?.achData?.[PERSONAL_INFO_STEP_KEY.SSN_LAST_4] ?? ''; - const validate = (values: FormOnyxValues): FormInputErrors => { - const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { - errors.ssnLast4 = translate('bankAccount.error.ssnLast4'); - } + if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { + errors.ssnLast4 = translate('bankAccount.error.ssnLast4'); + } - return errors; - }; + return errors; + }, + [translate], + ); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, diff --git a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx index 4366f8ff284d..72358e909446 100644 --- a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx +++ b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx @@ -74,7 +74,7 @@ function NewContactMethodPage({loginList, route}: NewContactMethodPageProps) { // the loginList gets updated, causing this function to run again. // https://github.com/Expensify/App/issues/20610 // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [translate], ); const onBackButtonPress = useCallback(() => { diff --git a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx index 33484c2aee35..cf226655ce32 100644 --- a/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx +++ b/src/pages/settings/Security/TwoFactorAuth/TwoFactorAuthForm/BaseTwoFactorAuthForm.tsx @@ -3,6 +3,7 @@ import type {ForwardedRef, RefAttributes} from 'react'; import {withOnyx} from 'react-native-onyx'; import type {AutoCompleteVariant, MagicCodeInputHandle} from '@components/MagicCodeInput'; import MagicCodeInput from '@components/MagicCodeInput'; +import useLocalize from '@hooks/useLocalize'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import * as Session from '@userActions/Session'; @@ -14,6 +15,7 @@ type BaseTwoFactorAuthFormProps = BaseTwoFactorAuthFormOnyxProps & { }; function BaseTwoFactorAuthForm({account, autoComplete}: BaseTwoFactorAuthFormProps, ref: ForwardedRef) { + const {translate} = useLocalize(); const [formError, setFormError] = useState<{twoFactorAuthCode?: string}>({}); const [twoFactorAuthCode, setTwoFactorAuthCode] = useState(''); const inputRef = useRef(null); @@ -41,18 +43,18 @@ function BaseTwoFactorAuthForm({account, autoComplete}: BaseTwoFactorAuthFormPro inputRef.current.blur(); } if (!twoFactorAuthCode.trim()) { - setFormError({twoFactorAuthCode: 'twoFactorAuthForm.error.pleaseFillTwoFactorAuth'}); + setFormError({twoFactorAuthCode: translate('twoFactorAuthForm.error.pleaseFillTwoFactorAuth')}); return; } if (!ValidationUtils.isValidTwoFactorCode(twoFactorAuthCode)) { - setFormError({twoFactorAuthCode: 'twoFactorAuthForm.error.incorrect2fa'}); + setFormError({twoFactorAuthCode: translate('twoFactorAuthForm.error.incorrect2fa')}); return; } setFormError({}); Session.validateTwoFactorAuth(twoFactorAuthCode); - }, [twoFactorAuthCode]); + }, [twoFactorAuthCode, translate]); useImperativeHandle(ref, () => ({ validateAndSubmitForm() { diff --git a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx index 91883cc989b0..feae955b10fb 100644 --- a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx @@ -36,13 +36,16 @@ function WorkspaceEditTagsPage({route, policyTags}: WorkspaceEditTagsPageProps) const taglistName = useMemo(() => PolicyUtils.getTagListName(policyTags, route.params.orderWeight), [policyTags, route.params.orderWeight]); const {inputCallbackRef} = useAutoFocusInput(); - const validateTagName = (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - if (!values[INPUT_IDS.POLICY_TAGS_NAME] && values[INPUT_IDS.POLICY_TAGS_NAME].trim() === '') { - errors[INPUT_IDS.POLICY_TAGS_NAME] = translate('common.error.fieldRequired'); - } - return errors; - }; + const validateTagName = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + if (!values[INPUT_IDS.POLICY_TAGS_NAME] && values[INPUT_IDS.POLICY_TAGS_NAME].trim() === '') { + errors[INPUT_IDS.POLICY_TAGS_NAME] = translate('common.error.fieldRequired'); + } + return errors; + }, + [translate], + ); const updateTaglistName = useCallback( (values: FormOnyxValues) => { From 03af563416663b39f6c6fae4551f9b51a6db8939 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:18:40 +0300 Subject: [PATCH 047/228] add trip data type --- src/types/onyx/Report.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index e388ce2cd330..f6eb9f70464e 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -203,6 +203,13 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< fieldList?: Record; permissions?: Array>; + + /** The trip data for a trip room */ + tripData?: { + startDate: string; + endDate: string; + tripId: string; + }; }, PolicyReportField['fieldID'] >; From 5b0fd9266bd20c0649c0121e805dbfd5412b7ce4 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:19:01 +0300 Subject: [PATCH 048/228] get trip id from transaction --- src/libs/ReportUtils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d82fb224784b..c5c57f1cb62b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6638,6 +6638,10 @@ function getTripTransactions(tripRoomReportID: string | undefined): Transaction[ return tripTransactionReportIDs.flatMap((reportID) => TransactionUtils.getAllReportTransactions(reportID)); } +function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry | undefined | null): string | undefined { + return getReport(transactionParentReport?.parentReportID)?.tripData?.tripId; +} + /** * Checks if report contains actions with errors */ @@ -7075,6 +7079,7 @@ export { updateReportPreview, temporary_getMoneyRequestOptions, getTripTransactions, + getTripIDFromTransactionParentReport, buildOptimisticInvoiceReport, getInvoiceChatByParticipants, shouldShowMerchantColumn, From b2d1aa3152d02b142b6e7bf9dad451be2d4df40b Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:19:19 +0300 Subject: [PATCH 049/228] get trip id and should show trip details view --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 2987638c759d..c24617676f90 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -171,6 +171,8 @@ function MoneyRequestView({ const shouldShowBillable = isPolicyExpenseChat && (!!transactionBillable || !(policy?.disabledFields?.defaultBillable ?? true)); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); + const shouldShowViewTripDetails = TransactionUtils.hasReservationList(transaction); + const tripID = ReportUtils.getTripIDFromTransactionParentReport(parentReport); const {getViolationsForField} = useViolations(transactionViolations ?? []); const hasViolations = useCallback( From aa695fe3aeb07dfb16024e04e496ab5c3b250e8e Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 5 Jun 2024 05:25:32 +0700 Subject: [PATCH 050/228] fix comment --- src/libs/ReportUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 329510baf38a..f342ee61f85c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5275,14 +5275,16 @@ function shouldReportBeInOptionList({ return includeSelfDM; } const parentReportAction = ReportActionsUtils.getParentReportAction(report); + + // Hide chat threads where the parent message is pending removal if ( - parentReportAction && !isEmptyObject(parentReportAction) && ReportActionsUtils.isPendingRemove(parentReportAction) && ReportActionsUtils.isThreadParentMessage(parentReportAction, report?.reportID ?? '') ) { return false; } + return true; } From d2f6dad16a67e05d4467d6df5da4e2d973007be1 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:27:29 +0300 Subject: [PATCH 051/228] add menu item for trip details --- src/components/ReportActionItem/MoneyRequestView.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index c24617676f90..626b46e8d144 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -4,6 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import ConfirmedRoute from '@components/ConfirmedRoute'; import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {useSession} from '@components/OnyxProvider'; @@ -551,6 +552,13 @@ function MoneyRequestView({ /> )} + {shouldShowViewTripDetails && ( + + )} {shouldShowBillable && ( From 285135379e68e0eb704e2936f691b5468ac41f7c Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:27:39 +0300 Subject: [PATCH 052/228] add luggage icon --- src/components/ReportActionItem/MoneyRequestView.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 626b46e8d144..ca0faa4af499 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -555,6 +555,7 @@ function MoneyRequestView({ {shouldShowViewTripDetails && ( From c4f451267037bc8a58adcd47efefe7dbe59ea07b Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:28:16 +0300 Subject: [PATCH 053/228] add suitcase icon --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index ca0faa4af499..2cc00e32aab0 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -555,7 +555,7 @@ function MoneyRequestView({ {shouldShowViewTripDetails && ( From a408b6667a7f94fe3dc51e20a0e7075ed7a169b3 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:37:52 +0300 Subject: [PATCH 054/228] fix type tripid --- src/types/onyx/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index f6eb9f70464e..2c45156f40ec 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -208,7 +208,7 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< tripData?: { startDate: string; endDate: string; - tripId: string; + tripID: string; }; }, PolicyReportField['fieldID'] From fb7fdec2d4f9027ebbc296acdbc443100ef7be36 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:38:06 +0300 Subject: [PATCH 055/228] fix type tripid --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c5c57f1cb62b..4df0939db0bb 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6639,7 +6639,7 @@ function getTripTransactions(tripRoomReportID: string | undefined): Transaction[ } function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry | undefined | null): string | undefined { - return getReport(transactionParentReport?.parentReportID)?.tripData?.tripId; + return getReport(transactionParentReport?.parentReportID)?.tripData?.tripID; } /** From 68ba31dbfd87f3ce4a51bfefb1799b7b8159c266 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:42:08 +0300 Subject: [PATCH 056/228] add trip id url --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index bc8f627630a3..634e750bf0e0 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3306,6 +3306,7 @@ const CONST = { CONCIERGE_TRAVEL_URL: 'https://community.expensify.com/discussion/7066/introducing-concierge-travel', BOOK_TRAVEL_DEMO_URL: 'https://calendly.com/d/ck2z-xsh-q97/expensify-travel-demo-travel-page', + TRIP_ID_URL: (tripID: string) => `https://travel.expensify.com/trips/${tripID}`, SCREEN_READER_STATES: { ALL: 'all', ACTIVE: 'active', From 09af1940a5d7147dedfaa5d46decac86d6deb932 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:42:26 +0300 Subject: [PATCH 057/228] navigate to trip id url --- src/components/ReportActionItem/MoneyRequestView.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 2cc00e32aab0..62033c7ea3cd 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -1,5 +1,5 @@ import React, {useCallback, useMemo} from 'react'; -import {View} from 'react-native'; +import {Linking, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import ConfirmedRoute from '@components/ConfirmedRoute'; @@ -175,6 +175,13 @@ function MoneyRequestView({ const shouldShowViewTripDetails = TransactionUtils.hasReservationList(transaction); const tripID = ReportUtils.getTripIDFromTransactionParentReport(parentReport); + const navigateToTripID = () => { + if (!tripID) { + return; + } + Linking.openURL(CONST.TRIP_ID_URL(tripID)); + }; + const {getViolationsForField} = useViolations(transactionViolations ?? []); const hasViolations = useCallback( (field: ViolationField, data?: OnyxTypes.TransactionViolation['data']): boolean => !!canUseViolations && getViolationsForField(field, data).length > 0, @@ -552,12 +559,13 @@ function MoneyRequestView({ /> )} - {shouldShowViewTripDetails && ( + {shouldShowViewTripDetails && tripID && ( )} {shouldShowBillable && ( From 69cf344a59178eb4b58414150066dbd5cde45622 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Wed, 5 Jun 2024 01:47:53 +0300 Subject: [PATCH 058/228] handle staging url --- src/CONST.ts | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 634e750bf0e0..cfa5e52b57e0 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3306,7 +3306,7 @@ const CONST = { CONCIERGE_TRAVEL_URL: 'https://community.expensify.com/discussion/7066/introducing-concierge-travel', BOOK_TRAVEL_DEMO_URL: 'https://calendly.com/d/ck2z-xsh-q97/expensify-travel-demo-travel-page', - TRIP_ID_URL: (tripID: string) => `https://travel.expensify.com/trips/${tripID}`, + TRIP_ID_URL: (tripID: string, isProduction: boolean) => (isProduction ? `https://travel.expensify.com/trips/${tripID}` : `https://staging.travel.expensify.com/trips/${tripID}`), SCREEN_READER_STATES: { ALL: 'all', ACTIVE: 'active', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 62033c7ea3cd..1a6b3742bb41 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -13,6 +13,7 @@ import ReceiptEmptyState from '@components/ReceiptEmptyState'; import Switch from '@components/Switch'; import Text from '@components/Text'; import ViolationMessages from '@components/ViolationMessages'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; @@ -100,6 +101,8 @@ function MoneyRequestView({ const session = useSession(); const {isOffline} = useNetwork(); const {translate, toLocaleDigit} = useLocalize(); + const {isProduction} = useEnvironment(); + const parentReportAction = parentReportActions?.[report.parentReportActionID ?? ''] ?? null; const isTrackExpense = ReportUtils.isTrackExpenseReport(report); const {canUseViolations, canUseP2PDistanceRequests} = usePermissions(isTrackExpense ? CONST.IOU.TYPE.TRACK : undefined); @@ -179,7 +182,7 @@ function MoneyRequestView({ if (!tripID) { return; } - Linking.openURL(CONST.TRIP_ID_URL(tripID)); + Linking.openURL(CONST.TRIP_ID_URL(tripID, isProduction)); }; const {getViolationsForField} = useViolations(transactionViolations ?? []); From f985b2406af66b5d51d92690fc3063de0a30f0e7 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Wed, 5 Jun 2024 10:38:14 +0200 Subject: [PATCH 059/228] chore: changes after review --- src/libs/E2E/client.ts | 3 ++- src/libs/E2E/tests/appStartTimeTest.e2e.ts | 2 -- src/libs/E2E/tests/chatOpeningTest.e2e.ts | 11 ++++------- src/libs/E2E/tests/linkingTest.e2e.ts | 2 -- src/libs/E2E/tests/openChatFinderPageTest.e2e.ts | 11 ++++------- src/libs/E2E/tests/reportTypingTest.e2e.ts | 5 +---- 6 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/libs/E2E/client.ts b/src/libs/E2E/client.ts index 5aa999267ead..7a0259de7eef 100644 --- a/src/libs/E2E/client.ts +++ b/src/libs/E2E/client.ts @@ -1,6 +1,7 @@ import Config from '../../../tests/e2e/config'; import Routes from '../../../tests/e2e/server/routes'; import type {NetworkCacheMap, TestConfig, TestResult} from './types'; +import {waitForActiveRequestsToBeEmpty} from './utils/NetworkInterceptor'; type NativeCommandPayload = { text: string; @@ -57,7 +58,7 @@ const submitTestResults = (testResult: TestResult): Promise => { }); }; -const submitTestDone = () => fetch(`${SERVER_ADDRESS}${Routes.testDone}`, defaultRequestInit); +const submitTestDone = () => waitForActiveRequestsToBeEmpty().then(() => fetch(`${SERVER_ADDRESS}${Routes.testDone}`, defaultRequestInit)); let currentActiveTestConfig: TestConfig | null = null; diff --git a/src/libs/E2E/tests/appStartTimeTest.e2e.ts b/src/libs/E2E/tests/appStartTimeTest.e2e.ts index 456c62b24ac9..321fc3773d51 100644 --- a/src/libs/E2E/tests/appStartTimeTest.e2e.ts +++ b/src/libs/E2E/tests/appStartTimeTest.e2e.ts @@ -3,7 +3,6 @@ import type {PerformanceEntry} from 'react-native-performance'; import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; -import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Performance from '@libs/Performance'; const test = () => { @@ -31,7 +30,6 @@ const test = () => { }), ), ) - .then(waitForActiveRequestsToBeEmpty) .then(() => { console.debug('[E2E] Done, exiting…'); E2EClient.submitTestDone(); diff --git a/src/libs/E2E/tests/chatOpeningTest.e2e.ts b/src/libs/E2E/tests/chatOpeningTest.e2e.ts index c8887cab535d..8e43c4ece564 100644 --- a/src/libs/E2E/tests/chatOpeningTest.e2e.ts +++ b/src/libs/E2E/tests/chatOpeningTest.e2e.ts @@ -5,7 +5,6 @@ import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; import getPromiseWithResolve from '@libs/E2E/utils/getPromiseWithResolve'; -import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import CONST from '@src/CONST'; @@ -30,13 +29,11 @@ const test = (config: NativeConfig) => { const [renderChatPromise, renderChatResolve] = getPromiseWithResolve(); const [chatTTIPromise, chatTTIResolve] = getPromiseWithResolve(); - Promise.all([renderChatPromise, chatTTIPromise]) - .then(waitForActiveRequestsToBeEmpty) - .then(() => { - console.debug(`[E2E] Submitting!`); + Promise.all([renderChatPromise, chatTTIPromise]).then(() => { + console.debug(`[E2E] Submitting!`); - E2EClient.submitTestDone(); - }); + E2EClient.submitTestDone(); + }); Performance.subscribeToMeasurements((entry) => { if (entry.name === CONST.TIMING.SIDEBAR_LOADED) { diff --git a/src/libs/E2E/tests/linkingTest.e2e.ts b/src/libs/E2E/tests/linkingTest.e2e.ts index 8a1980a8b28a..c48ffb5a8057 100644 --- a/src/libs/E2E/tests/linkingTest.e2e.ts +++ b/src/libs/E2E/tests/linkingTest.e2e.ts @@ -6,7 +6,6 @@ import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; -import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import CONST from '@src/CONST'; @@ -52,7 +51,6 @@ const test = (config: NativeConfig) => { name: 'Comment linking', duration: entry.duration, }) - .then(waitForActiveRequestsToBeEmpty) .then(() => { console.debug('[E2E] Test completed successfully, exiting…'); E2EClient.submitTestDone(); diff --git a/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts b/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts index 1837d31f7110..4ac7995b914f 100644 --- a/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts +++ b/src/libs/E2E/tests/openChatFinderPageTest.e2e.ts @@ -3,7 +3,6 @@ import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import E2EClient from '@libs/E2E/client'; import getPromiseWithResolve from '@libs/E2E/utils/getPromiseWithResolve'; -import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import CONST from '@src/CONST'; @@ -26,13 +25,11 @@ const test = () => { const [openSearchPagePromise, openSearchPageResolve] = getPromiseWithResolve(); const [loadSearchOptionsPromise, loadSearchOptionsResolve] = getPromiseWithResolve(); - Promise.all([openSearchPagePromise, loadSearchOptionsPromise]) - .then(waitForActiveRequestsToBeEmpty) - .then(() => { - console.debug(`[E2E] Submitting!`); + Promise.all([openSearchPagePromise, loadSearchOptionsPromise]).then(() => { + console.debug(`[E2E] Submitting!`); - E2EClient.submitTestDone(); - }); + E2EClient.submitTestDone(); + }); Performance.subscribeToMeasurements((entry) => { if (entry.name === CONST.TIMING.SIDEBAR_LOADED) { diff --git a/src/libs/E2E/tests/reportTypingTest.e2e.ts b/src/libs/E2E/tests/reportTypingTest.e2e.ts index fec5daa246fe..817bda941611 100644 --- a/src/libs/E2E/tests/reportTypingTest.e2e.ts +++ b/src/libs/E2E/tests/reportTypingTest.e2e.ts @@ -5,7 +5,6 @@ import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import waitForKeyboard from '@libs/E2E/actions/waitForKeyboard'; import E2EClient from '@libs/E2E/client'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; -import {waitForActiveRequestsToBeEmpty} from '@libs/E2E/utils/NetworkInterceptor'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import {getRerenderCount, resetRerenderCount} from '@pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e'; @@ -55,9 +54,7 @@ const test = (config: NativeConfig) => { branch: Config.E2E_BRANCH, name: 'Composer typing rerender count', renderCount: rerenderCount, - }) - .then(waitForActiveRequestsToBeEmpty) - .then(E2EClient.submitTestDone); + }).then(E2EClient.submitTestDone); }, 3000); }) .catch((error) => { From d7f1ae6a7fcbbcc89074265fb8e7981f7b17864f Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 6 Jun 2024 15:19:08 +0700 Subject: [PATCH 060/228] fix: empty category illustration is not center aligned --- src/components/WorkspaceEmptyStateSection.tsx | 20 +++++++++++++++++-- .../request/step/IOURequestStepCategory.tsx | 10 ++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/components/WorkspaceEmptyStateSection.tsx b/src/components/WorkspaceEmptyStateSection.tsx index e1ee7a1b66c3..6a00aa4bf5eb 100644 --- a/src/components/WorkspaceEmptyStateSection.tsx +++ b/src/components/WorkspaceEmptyStateSection.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -15,14 +16,29 @@ type WorkspaceEmptyStateSectionProps = { /** The icon to display along with the title */ icon: IconAsset; + + /** Additional style for container */ + containerStyle?: StyleProp; + + /** Whether to apply card style to container */ + shouldStyleAsCard?: boolean; }; -function WorkspaceEmptyStateSection({icon, subtitle, title}: WorkspaceEmptyStateSectionProps) { +function WorkspaceEmptyStateSection({icon, subtitle, title, containerStyle, shouldStyleAsCard = true}: WorkspaceEmptyStateSectionProps) { const styles = useThemeStyles(); const {shouldUseNarrowLayout} = useResponsiveLayout(); return ( - + -