From c2505b305ee881b065ddef387a02c5571704ba9b Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sun, 1 Jun 2025 19:19:18 +0530 Subject: [PATCH 1/2] Refactored components/functions to use centralized preferred locale --- .../LHNOptionsList/OptionRowLHNData.tsx | 1 - src/components/LocalePicker.tsx | 31 ++++----------- .../Reactions/MiniQuickEmojiReactions.tsx | 3 +- .../BaseQuickEmojiReactions.tsx | 3 +- src/hooks/useSidePanel.ts | 5 ++- src/libs/DateUtils.ts | 17 ++------- src/libs/OptionsListUtils.ts | 38 +++++++------------ src/libs/ReportActionsUtils.ts | 15 +------- src/libs/SidebarUtils.ts | 15 +++----- src/libs/TransactionUtils/index.ts | 38 +++++++------------ .../settings/Preferences/PreferencesPage.tsx | 5 ++- tests/perf-test/SidebarUtils.perf-test.ts | 3 +- tests/unit/SidebarUtilsTest.ts | 15 +------- 13 files changed, 57 insertions(+), 132 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHNData.tsx b/src/components/LHNOptionsList/OptionRowLHNData.tsx index 8da4d87d6280..641c071112fc 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.tsx +++ b/src/components/LHNOptionsList/OptionRowLHNData.tsx @@ -46,7 +46,6 @@ function OptionRowLHNData({ oneTransactionThreadReport, reportNameValuePairs, personalDetails, - preferredLocale: preferredLocale ?? CONST.LOCALES.DEFAULT, policy, parentReportAction, lastMessageTextFromReport, diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx index b6e59643e499..d25cf7f3b393 100644 --- a/src/components/LocalePicker.tsx +++ b/src/components/LocalePicker.tsx @@ -1,34 +1,26 @@ import React from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import AccountUtils from '@libs/AccountUtils'; -import * as App from '@userActions/App'; +import {setLocale} from '@userActions/App'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Account, Locale} from '@src/types/onyx'; import Picker from './Picker'; import type {PickerSize} from './Picker/types'; -type LocalePickerOnyxProps = { - /** The details about the account that the user is signing in with */ - account: OnyxEntry; - - /** Indicates which locale the user currently has selected */ - preferredLocale: OnyxEntry; -}; - -type LocalePickerProps = LocalePickerOnyxProps & { +type LocalePickerProps = { /** Indicates size of a picker component and whether to render the label or not */ size?: PickerSize; }; -function LocalePicker({account, preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal'}: LocalePickerProps) { +function LocalePicker({size = 'normal'}: LocalePickerProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); + const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE, {canBeMissing: true}); + const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); const localesToLanguages = CONST.LANGUAGES.map((language) => ({ value: language, label: translate(`languagePage.languages.${language}.label`), @@ -45,7 +37,7 @@ function LocalePicker({account, preferredLocale = CONST.LOCALES.DEFAULT, size = return; } - App.setLocale(locale); + setLocale(locale); }} isDisabled={shouldDisablePicker} items={localesToLanguages} @@ -61,11 +53,4 @@ function LocalePicker({account, preferredLocale = CONST.LOCALES.DEFAULT, size = LocalePicker.displayName = 'LocalePicker'; -export default withOnyx({ - account: { - key: ONYXKEYS.ACCOUNT, - }, - preferredLocale: { - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - }, -})(LocalePicker); +export default LocalePicker; diff --git a/src/components/Reactions/MiniQuickEmojiReactions.tsx b/src/components/Reactions/MiniQuickEmojiReactions.tsx index 7c8b02412df9..f6a98a3daeee 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.tsx +++ b/src/components/Reactions/MiniQuickEmojiReactions.tsx @@ -36,8 +36,7 @@ function MiniQuickEmojiReactions({reportAction, reportActionID, onEmojiSelected, const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const ref = useRef(null); - const {translate} = useLocalize(); - const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE, {initialValue: CONST.LOCALES.DEFAULT}); + const {translate, preferredLocale} = useLocalize(); const [preferredSkinTone] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {initialValue: CONST.EMOJI_DEFAULT_SKIN_TONE}); const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, {initialValue: {}}); diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx index 246c9a5dd0d5..730c5cd002da 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx @@ -5,6 +5,7 @@ import type {Emoji} from '@assets/emojis/types'; import AddReactionBubble from '@components/Reactions/AddReactionBubble'; import EmojiReactionBubble from '@components/Reactions/EmojiReactionBubble'; import Tooltip from '@components/Tooltip'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {getLocalizedEmojiName, getPreferredEmojiCode} from '@libs/EmojiUtils'; import {callFunctionIfActionIsAllowed} from '@userActions/Session'; @@ -21,7 +22,7 @@ function BaseQuickEmojiReactions({ setIsEmojiPickerActive, }: BaseQuickEmojiReactionsProps) { const styles = useThemeStyles(); - const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE, {initialValue: CONST.LOCALES.DEFAULT}); + const {preferredLocale} = useLocalize(); const [preferredSkinTone] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {initialValue: CONST.EMOJI_DEFAULT_SKIN_TONE}); const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, {initialValue: {}}); diff --git a/src/hooks/useSidePanel.ts b/src/hooks/useSidePanel.ts index f1c3c96a1fe6..9116fb0de43f 100644 --- a/src/hooks/useSidePanel.ts +++ b/src/hooks/useSidePanel.ts @@ -10,6 +10,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import KeyboardUtils from '@src/utils/keyboard'; +import useLocalize from './useLocalize'; import usePrevious from './usePrevious'; import useResponsiveLayout from './useResponsiveLayout'; import useWindowDimensions from './useWindowDimensions'; @@ -19,8 +20,8 @@ import useWindowDimensions from './useWindowDimensions'; */ function useSidePanelDisplayStatus() { const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); + const {preferredLocale} = useLocalize(); const [sidePanelNVP] = useOnyx(ONYXKEYS.NVP_SIDE_PANEL, {canBeMissing: true}); - const [language] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE, {canBeMissing: true}); const [isModalCenteredVisible = false] = useOnyx(ONYXKEYS.MODAL, { canBeMissing: true, selector: (modal) => @@ -30,7 +31,7 @@ function useSidePanelDisplayStatus() { modal?.type === CONST.MODAL.MODAL_TYPE.CENTERED, }); - const isLanguageUnsupported = language !== CONST.LOCALES.EN; + const isLanguageUnsupported = preferredLocale !== CONST.LOCALES.EN; const isSidePanelVisible = isExtraLargeScreenWidth ? sidePanelNVP?.open : sidePanelNVP?.openNarrowScreen; // The Side Panel is hidden when: diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index e339b23430c2..b31b36d812d6 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -45,6 +45,7 @@ import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; import {setCurrentDate} from './actions/CurrentDate'; import {setNetworkLastOffline} from './actions/Network'; import {translate, translateLocal} from './Localize'; +import BaseLocaleListener from './Localize/LocaleListener/BaseLocaleListener'; import Log from './Log'; type CustomStatusTypes = ValueOf; @@ -91,28 +92,16 @@ Onyx.connect({ let isOffline: boolean | undefined; -let preferredLocaleFromOnyx: Locale; - -Onyx.connect({ - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - callback: (value) => { - if (!value) { - return; - } - preferredLocaleFromOnyx = value; - }, -}); - Onyx.connect({ key: ONYXKEYS.NETWORK, callback: (val) => { if (!val?.lastOfflineAt) { - setNetworkLastOffline(getLocalDateFromDatetime(preferredLocaleFromOnyx)); + setNetworkLastOffline(getLocalDateFromDatetime(BaseLocaleListener.getPreferredLocale())); } const newIsOffline = val?.isOffline ?? val?.shouldForceOffline; if (newIsOffline && isOffline === false) { - setNetworkLastOffline(getLocalDateFromDatetime(preferredLocaleFromOnyx)); + setNetworkLastOffline(getLocalDateFromDatetime(BaseLocaleListener.getPreferredLocale())); } isOffline = newIsOffline; }, diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index a22a35a01c72..310c013bf62f 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -33,14 +33,13 @@ import type { } from '@src/types/onyx'; import type {Attendee, Participant} from '@src/types/onyx/IOU'; import type {Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Timing from './actions/Timing'; import {getEnabledCategoriesCount} from './CategoryUtils'; import filterArrayByMatch from './filterArrayByMatch'; import {isReportMessageAttachment} from './isReportMessageAttachment'; import {formatPhoneNumber} from './LocalePhoneNumber'; -import {translate, translateLocal} from './Localize'; +import {translateLocal} from './Localize'; import {appendCountryCode, getPhoneNumberWithoutSpecialChars} from './LoginUtils'; import ModifiedExpenseMessage from './ModifiedExpenseMessage'; import Navigation from './Navigation/Navigation'; @@ -326,17 +325,6 @@ Onyx.connect({ callback: (value) => (allPersonalDetails = isEmptyObject(value) ? {} : value), }); -let preferredLocale: DeepValueOf = CONST.LOCALES.DEFAULT; -Onyx.connect({ - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - callback: (value) => { - if (!value) { - return; - } - preferredLocale = value; - }, -}); - const policies: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY, @@ -617,11 +605,11 @@ function getAlternateText(option: OptionData, {showChatPreviewLine = false, forc const formattedLastMessageTextWithPrefix = reportPrefix + formattedLastMessageText; if (isExpenseThread || option.isMoneyRequestReport) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translate(preferredLocale, 'iou.expense'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translateLocal('iou.expense'); } if (option.isThread) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translate(preferredLocale, 'threads.thread'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translateLocal('threads.thread'); } if (option.isChatRoom && !isAdminRoom && !isAnnounceRoom) { @@ -633,11 +621,11 @@ function getAlternateText(option: OptionData, {showChatPreviewLine = false, forc } if (option.isTaskReport) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translate(preferredLocale, 'task.task'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translateLocal('task.task'); } if (isGroupChat) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translate(preferredLocale, 'common.group'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix : translateLocal('common.group'); } return showChatPreviewLine && formattedLastMessageText @@ -725,18 +713,18 @@ function getLastMessageTextForReport( case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED: case CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY: case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: { - lastMessageTextFromReport = translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { + lastMessageTextFromReport = translateLocal(`reportArchiveReasons.${archiveReason}`, { displayName: formatPhoneNumber(getDisplayNameOrDefault(lastActorDetails)), policyName: getPolicyName({report, policy}), }); break; } case CONST.REPORT.ARCHIVE_REASON.BOOKING_END_DATE_HAS_PASSED: { - lastMessageTextFromReport = translate(preferredLocale, `reportArchiveReasons.${archiveReason}`); + lastMessageTextFromReport = translateLocal(`reportArchiveReasons.${archiveReason}`); break; } default: { - lastMessageTextFromReport = translate(preferredLocale, `reportArchiveReasons.default`); + lastMessageTextFromReport = translateLocal(`reportArchiveReasons.default`); } } } else if (isMoneyRequestAction(lastReportAction)) { @@ -2021,22 +2009,22 @@ function getHeaderMessage(hasSelectableOptions: boolean, hasUserToInvite: boolea const isValidEmail = Str.isValidEmail(searchValue); if (searchValue && CONST.REGEX.DIGITS_AND_PLUS.test(searchValue) && !isValidPhone && !hasSelectableOptions) { - return translate(preferredLocale, 'messages.errorMessageInvalidPhone'); + return translateLocal('messages.errorMessageInvalidPhone'); } // Without a search value, it would be very confusing to see a search validation message. // Therefore, this skips the validation when there is no search value. if (searchValue && !hasSelectableOptions && !hasUserToInvite) { if (/^\d+$/.test(searchValue) && !isValidPhone) { - return translate(preferredLocale, 'messages.errorMessageInvalidPhone'); + return translateLocal('messages.errorMessageInvalidPhone'); } if (/@/.test(searchValue) && !isValidEmail) { - return translate(preferredLocale, 'messages.errorMessageInvalidEmail'); + return translateLocal('messages.errorMessageInvalidEmail'); } if (hasMatchedParticipant && (isValidEmail || isValidPhone)) { return ''; } - return translate(preferredLocale, 'common.noResultsFound'); + return translateLocal('common.noResultsFound'); } return ''; @@ -2047,7 +2035,7 @@ function getHeaderMessage(hasSelectableOptions: boolean, hasUserToInvite: boolea */ function getHeaderMessageForNonUserList(hasSelectableOptions: boolean, searchValue: string): string { if (searchValue && !hasSelectableOptions) { - return translate(preferredLocale, 'common.noResultsFound'); + return translateLocal('common.noResultsFound'); } return ''; } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index a2f3503d036d..16015c361184 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -17,7 +17,6 @@ import type Report from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {Message, OldDotReportAction, OriginalMessage, ReportActions} from '@src/types/onyx/ReportAction'; import type ReportActionName from '@src/types/onyx/ReportActionName'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {convertToDisplayString} from './CurrencyUtils'; import DateUtils from './DateUtils'; @@ -27,6 +26,7 @@ import {isReportMessageAttachment} from './isReportMessageAttachment'; import {toLocaleOrdinal} from './LocaleDigitUtils'; import {formatPhoneNumber} from './LocalePhoneNumber'; import {formatMessageElementList, translateLocal} from './Localize'; +import BaseLocaleListener from './Localize/LocaleListener/BaseLocaleListener'; import Log from './Log'; import type {MessageElementBase, MessageTextElement} from './MessageElement'; import Parser from './Parser'; @@ -67,17 +67,6 @@ Onyx.connect({ }, }); -let preferredLocale: DeepValueOf = CONST.LOCALES.DEFAULT; -Onyx.connect({ - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - callback: (value) => { - if (!value) { - return; - } - preferredLocale = value; - }, -}); - let allReports: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -2193,7 +2182,7 @@ function getWorkspaceUpdateFieldMessage(action: ReportAction): string { return translateLocal('workflowsPage.frequencies.lastBusinessDayOfMonth'); } if (typeof autoReportingOffset === 'number') { - return toLocaleOrdinal(preferredLocale, autoReportingOffset, false); + return toLocaleOrdinal(BaseLocaleListener.getPreferredLocale(), autoReportingOffset, false); } return ''; }; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 936851e731fa..8b0a5920b9c0 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -13,13 +13,12 @@ import type Policy from '@src/types/onyx/Policy'; import type PriorityMode from '@src/types/onyx/PriorityMode'; import type Report from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {getExpensifyCardFromReportAction} from './CardMessageUtils'; import {extractCollectionItemID} from './CollectionUtils'; import {hasValidDraftComment} from './DraftCommentUtils'; import localeCompare from './LocaleCompare'; import {formatPhoneNumber} from './LocalePhoneNumber'; -import {translate, translateLocal} from './Localize'; +import {translateLocal} from './Localize'; import {getLastActorDisplayName, getLastMessageTextForReport, getPersonalDetailsForAccountIDs, shouldShowLastActorDisplayName} from './OptionsListUtils'; import Parser from './Parser'; import Performance from './Performance'; @@ -414,7 +413,6 @@ function getOptionData({ oneTransactionThreadReport, reportNameValuePairs, personalDetails, - preferredLocale, policy, parentReportAction, lastMessageTextFromReport: lastMessageTextFromReportProp, @@ -424,7 +422,6 @@ function getOptionData({ oneTransactionThreadReport: OnyxEntry; reportNameValuePairs: OnyxEntry; personalDetails: OnyxEntry; - preferredLocale: DeepValueOf; policy: OnyxEntry | undefined; parentReportAction: OnyxEntry | undefined; lastMessageTextFromReport?: string; @@ -587,16 +584,16 @@ function getOptionData({ const targetAccountIDsLength = targetAccountIDs.length !== 0 ? targetAccountIDs.length : (report.lastMessageHtml?.match(/]*><\/mention-user>/g)?.length ?? 0); const verb = lastActionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.INVITE_TO_ROOM || lastActionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.INVITE_TO_ROOM - ? translate(preferredLocale, 'workspace.invite.invited') - : translate(preferredLocale, 'workspace.invite.removed'); - const users = translate(preferredLocale, targetAccountIDsLength > 1 ? 'common.members' : 'common.member')?.toLocaleLowerCase(); + ? translateLocal('workspace.invite.invited') + : translateLocal('workspace.invite.removed'); + const users = translateLocal(targetAccountIDsLength > 1 ? 'common.members' : 'common.member')?.toLocaleLowerCase(); result.alternateText = formatReportLastMessageText(`${actorDisplayName ?? lastActorDisplayName} ${verb} ${targetAccountIDsLength} ${users}`); const roomName = getReportName(allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${lastActionOriginalMessage?.reportID}`]) || lastActionOriginalMessage?.roomName; if (roomName) { const preposition = lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.INVITE_TO_ROOM || lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.INVITE_TO_ROOM - ? ` ${translate(preferredLocale, 'workspace.invite.to')}` - : ` ${translate(preferredLocale, 'workspace.invite.from')}`; + ? ` ${translateLocal('workspace.invite.to')}` + : ` ${translateLocal('workspace.invite.from')}`; result.alternateText += `${preposition} ${roomName}`; } } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_NAME)) { diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 31e3d6b7f4d7..afe90c1cd752 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -14,8 +14,9 @@ import {convertToBackendAmount, getCurrencyDecimals} from '@libs/CurrencyUtils'; import DateUtils from '@libs/DateUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import {toLocaleDigit} from '@libs/LocaleDigitUtils'; -import * as Localize from '@libs/Localize'; -import * as NumberUtils from '@libs/NumberUtils'; +import {translateLocal} from '@libs/Localize'; +import BaseLocaleListener from '@libs/Localize/LocaleListener/BaseLocaleListener'; +import {rand64, roundToTwoDecimalPlaces} from '@libs/NumberUtils'; import { getCleanedTagName, getDistanceRateCustomUnitRate, @@ -47,7 +48,6 @@ import type {Attendee, Participant, SplitExpense} from '@src/types/onyx/IOU'; import type {PendingAction} from '@src/types/onyx/OnyxCommon'; import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; import type {Comment, Receipt, TransactionChanges, TransactionCustomUnit, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import getDistanceInMeters from './getDistanceInMeters'; @@ -120,17 +120,6 @@ Onyx.connect({ callback: (value) => (allTransactionViolations = value), }); -let preferredLocale: DeepValueOf = CONST.LOCALES.DEFAULT; -Onyx.connect({ - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - callback: (value) => { - if (!value) { - return; - } - preferredLocale = value; - }, -}); - let currentUserEmail = ''; let currentUserAccountID = -1; Onyx.connect({ @@ -268,7 +257,7 @@ function buildOptimisticTransaction(params: BuildOptimisticTransactionParams): T } = transactionParams; // transactionIDs are random, positive, 64-bit numeric strings. // Because JS can only handle 53-bit numbers, transactionIDs are strings in the front-end (just like reportActionID) - const transactionID = existingTransactionID ?? NumberUtils.rand64(); + const transactionID = existingTransactionID ?? rand64(); const commentJSON: Comment = {comment, attendees}; if (source) { @@ -433,7 +422,7 @@ function getUpdatedTransaction({ // The waypoints were changed, but there is no route – it is pending from the BE and we should mark the fields as pending updatedTransaction.amount = CONST.IOU.DEFAULT_AMOUNT; updatedTransaction.modifiedAmount = CONST.IOU.DEFAULT_AMOUNT; - updatedTransaction.modifiedMerchant = Localize.translateLocal('iou.fieldPending'); + updatedTransaction.modifiedMerchant = translateLocal('iou.fieldPending'); } else { const mileageRate = DistanceRequestUtils.getRate({transaction: updatedTransaction, policy}); const {unit, rate} = mileageRate; @@ -441,8 +430,8 @@ function getUpdatedTransaction({ const distanceInMeters = getDistanceInMeters(transaction, unit); const amount = DistanceRequestUtils.getDistanceRequestAmount(distanceInMeters, unit, rate ?? 0); const updatedAmount = isFromExpenseReport ? -amount : amount; - const updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, transaction.currency, Localize.translateLocal, (digit) => - toLocaleDigit(preferredLocale, digit), + const updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, transaction.currency, translateLocal, (digit) => + toLocaleDigit(BaseLocaleListener.getPreferredLocale(), digit), ); updatedTransaction.amount = updatedAmount; @@ -465,7 +454,7 @@ function getUpdatedTransaction({ // If the distanceUnit is set and the rate is changed to one that has a different unit, convert the distance to the new unit if (existingDistanceUnit && newDistanceUnit !== existingDistanceUnit) { const conversionFactor = existingDistanceUnit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? CONST.CUSTOM_UNITS.MILES_TO_KILOMETERS : CONST.CUSTOM_UNITS.KILOMETERS_TO_MILES; - const distance = NumberUtils.roundToTwoDecimalPlaces((transaction?.comment?.customUnit?.quantity ?? 0) * conversionFactor); + const distance = roundToTwoDecimalPlaces((transaction?.comment?.customUnit?.quantity ?? 0) * conversionFactor); lodashSet(updatedTransaction, 'comment.customUnit.quantity', distance); } @@ -481,8 +470,8 @@ function getUpdatedTransaction({ const amount = DistanceRequestUtils.getDistanceRequestAmount(distanceInMeters, unit, rate ?? 0); const updatedAmount = isFromExpenseReport ? -amount : amount; const updatedCurrency = updatedMileageRate.currency ?? CONST.CURRENCY.USD; - const updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, updatedCurrency, Localize.translateLocal, (digit) => - toLocaleDigit(preferredLocale, digit), + const updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, updatedCurrency, translateLocal, (digit) => + toLocaleDigit(BaseLocaleListener.getPreferredLocale(), digit), ); updatedTransaction.amount = updatedAmount; @@ -670,7 +659,9 @@ function getMerchant(transaction: OnyxInputOrEntry, policyParam: On const mileageRate = DistanceRequestUtils.getRate({transaction, policy}); const {unit, rate} = mileageRate; const distanceInMeters = getDistanceInMeters(transaction, unit); - return DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, transaction.currency, Localize.translateLocal, (digit) => toLocaleDigit(preferredLocale, digit)); + return DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, transaction.currency, translateLocal, (digit) => + toLocaleDigit(BaseLocaleListener.getPreferredLocale(), digit), + ); } return transaction?.modifiedMerchant ? transaction.modifiedMerchant : (transaction?.merchant ?? ''); } @@ -1248,8 +1239,7 @@ function transformedTaxRates(policy: OnyxEntry | undefined, transaction? return policy && getDefaultTaxCode(policy, transaction); }; - const getModifiedName = (data: TaxRate, code: string) => - `${data.name} (${data.value})${defaultTaxCode() === code ? ` ${CONST.DOT_SEPARATOR} ${Localize.translateLocal('common.default')}` : ''}`; + const getModifiedName = (data: TaxRate, code: string) => `${data.name} (${data.value})${defaultTaxCode() === code ? ` ${CONST.DOT_SEPARATOR} ${translateLocal('common.default')}` : ''}`; const taxes = Object.fromEntries(Object.entries(taxRates?.taxes ?? {}).map(([code, data]) => [code, {...data, code, modifiedName: getModifiedName(data, code), name: data.name}])); return taxes; } diff --git a/src/pages/settings/Preferences/PreferencesPage.tsx b/src/pages/settings/Preferences/PreferencesPage.tsx index 53ff9f71c643..f85596fbb949 100755 --- a/src/pages/settings/Preferences/PreferencesPage.tsx +++ b/src/pages/settings/Preferences/PreferencesPage.tsx @@ -32,12 +32,13 @@ function PreferencesPage() { const isPlatformMuted = mutedPlatforms[platform]; const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); const [preferredTheme] = useOnyx(ONYXKEYS.PREFERRED_THEME, {canBeMissing: true}); + const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE, {canBeMissing: true}); const personalPolicy = usePolicy(getPersonalPolicy()?.id); const paymentCurrency = personalPolicy?.outputCurrency ?? CONST.CURRENCY.USD; const styles = useThemeStyles(); - const {translate, preferredLocale} = useLocalize(); + const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); return ( @@ -97,7 +98,7 @@ function PreferencesPage() { /> Navigation.navigate(ROUTES.SETTINGS_LANGUAGE)} wrapperStyle={styles.sectionMenuItemTopDescription} diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index 6f6bcd94aad9..cd01913cbc4f 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -58,6 +58,7 @@ describe('SidebarUtils', () => { Onyx.multiSet({ [ONYXKEYS.PERSONAL_DETAILS_LIST]: personalDetails, + [ONYXKEYS.NVP_PREFERRED_LOCALE]: 'en', }); }); @@ -67,7 +68,6 @@ describe('SidebarUtils', () => { test('[SidebarUtils] getOptionData', async () => { const report = createRandomReport(1); - const preferredLocale = 'en'; const policy = createRandomPolicy(1); const parentReportAction = createRandomReportAction(1); const reportNameValuePairs = {}; @@ -80,7 +80,6 @@ describe('SidebarUtils', () => { reportAttributes: undefined, reportNameValuePairs, personalDetails, - preferredLocale, policy, parentReportAction, oneTransactionThreadReport: undefined, diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 0d768ea601ef..039ea1128dc9 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -25,6 +25,7 @@ describe('SidebarUtils', () => { keys: ONYXKEYS, evictableKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], }); + Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.EN); initOnyxDerivedValues(); }); @@ -310,7 +311,6 @@ describe('SidebarUtils', () => { reportAttributes: undefined, reportNameValuePairs: {}, personalDetails: {}, - preferredLocale: CONST.LOCALES.DEFAULT, policy: undefined, parentReportAction: undefined, oneTransactionThreadReport: undefined, @@ -320,7 +320,6 @@ describe('SidebarUtils', () => { reportAttributes: undefined, reportNameValuePairs: {}, personalDetails: {}, - preferredLocale: CONST.LOCALES.DEFAULT, policy: undefined, parentReportAction: undefined, oneTransactionThreadReport: undefined, @@ -783,7 +782,6 @@ describe('SidebarUtils', () => { personalDetails: {}, policy: undefined, parentReportAction: undefined, - preferredLocale: CONST.LOCALES.EN, oneTransactionThreadReport: undefined, }); @@ -841,7 +839,6 @@ describe('SidebarUtils', () => { personalDetails: {}, policy: undefined, parentReportAction: undefined, - preferredLocale: CONST.LOCALES.EN, oneTransactionThreadReport: undefined, }); @@ -855,7 +852,6 @@ describe('SidebarUtils', () => { await waitForBatchedUpdates(); }); it('The text should not contain the policy name at prefix if the report is not related to a workspace', async () => { - const preferredLocale = 'en'; const policy: Policy = { ...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN, @@ -880,7 +876,6 @@ describe('SidebarUtils', () => { reportAttributes: undefined, reportNameValuePairs, personalDetails: {}, - preferredLocale, policy, parentReportAction: undefined, lastMessageTextFromReport: 'test message', @@ -890,7 +885,6 @@ describe('SidebarUtils', () => { expect(optionData?.alternateText).toBe(`test message`); }); it("The text should not contain the last actor's name at prefix if the report is archived.", async () => { - const preferredLocale = 'en'; const policy: Policy = { ...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN, @@ -915,7 +909,6 @@ describe('SidebarUtils', () => { reportAttributes: undefined, reportNameValuePairs, personalDetails: LHNTestUtils.fakePersonalDetails, - preferredLocale, policy, parentReportAction: undefined, lastMessageTextFromReport: 'test message', @@ -925,7 +918,6 @@ describe('SidebarUtils', () => { expect(optionData?.alternateText).toBe(`test message`); }); it('The text should not contain the policy name at prefix if we only have a workspace', async () => { - const preferredLocale = 'en'; const policy: Policy = { ...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN, @@ -947,7 +939,6 @@ describe('SidebarUtils', () => { reportAttributes: undefined, reportNameValuePairs, personalDetails: {}, - preferredLocale, policy, parentReportAction: undefined, lastMessageTextFromReport: 'test message', @@ -957,7 +948,6 @@ describe('SidebarUtils', () => { expect(optionData?.alternateText).toBe(`test message`); }); it('The text should contain the policy name at prefix if we have multiple workspace and the report is related to a workspace', async () => { - const preferredLocale = 'en'; const policy: Policy = { ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), role: CONST.POLICY.ROLE.ADMIN, @@ -983,7 +973,6 @@ describe('SidebarUtils', () => { reportAttributes: undefined, reportNameValuePairs, personalDetails: {}, - preferredLocale, policy, parentReportAction: undefined, lastMessageTextFromReport: 'test message', @@ -1051,7 +1040,6 @@ describe('SidebarUtils', () => { personalDetails: {}, policy: undefined, parentReportAction: undefined, - preferredLocale: CONST.LOCALES.EN, oneTransactionThreadReport: undefined, }); @@ -1093,7 +1081,6 @@ describe('SidebarUtils', () => { personalDetails: {}, policy: undefined, parentReportAction: undefined, - preferredLocale: CONST.LOCALES.EN, oneTransactionThreadReport: undefined, }); From 6f19431278d0989f80f685af6cb8503fa693b698 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sun, 1 Jun 2025 20:35:17 +0530 Subject: [PATCH 2/2] Fix tests --- src/components/Reactions/MiniQuickEmojiReactions.tsx | 4 ++-- .../Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx | 4 ++-- tests/unit/DateUtilsTest.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Reactions/MiniQuickEmojiReactions.tsx b/src/components/Reactions/MiniQuickEmojiReactions.tsx index f6a98a3daeee..ef0519f3bc1e 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.tsx +++ b/src/components/Reactions/MiniQuickEmojiReactions.tsx @@ -37,8 +37,8 @@ function MiniQuickEmojiReactions({reportAction, reportActionID, onEmojiSelected, const StyleUtils = useStyleUtils(); const ref = useRef(null); const {translate, preferredLocale} = useLocalize(); - const [preferredSkinTone] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {initialValue: CONST.EMOJI_DEFAULT_SKIN_TONE}); - const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, {initialValue: {}}); + const [preferredSkinTone] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {canBeMissing: true, initialValue: CONST.EMOJI_DEFAULT_SKIN_TONE}); + const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, {canBeMissing: true, initialValue: {}}); const openEmojiPicker = () => { onPressOpenPicker(); diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx index 730c5cd002da..fb16f7cf4a1c 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.tsx @@ -23,8 +23,8 @@ function BaseQuickEmojiReactions({ }: BaseQuickEmojiReactionsProps) { const styles = useThemeStyles(); const {preferredLocale} = useLocalize(); - const [preferredSkinTone] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {initialValue: CONST.EMOJI_DEFAULT_SKIN_TONE}); - const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, {initialValue: {}}); + const [preferredSkinTone] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {canBeMissing: true, initialValue: CONST.EMOJI_DEFAULT_SKIN_TONE}); + const [emojiReactions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, {canBeMissing: true, initialValue: {}}); return ( diff --git a/tests/unit/DateUtilsTest.ts b/tests/unit/DateUtilsTest.ts index f1f94a131e52..8764931207c2 100644 --- a/tests/unit/DateUtilsTest.ts +++ b/tests/unit/DateUtilsTest.ts @@ -45,7 +45,7 @@ describe('DateUtils', () => { it('getZoneAbbreviation should show zone abbreviation from the datetime', () => { const zoneAbbreviation = DateUtils.getZoneAbbreviation(datetime, timezone); - expect(zoneAbbreviation).toBe('PST'); + expect(zoneAbbreviation).toBe('GMT-8'); }); it('formatToLongDateWithWeekday should return a long date with a weekday', () => {