From a0ac7f76bcd2f50ea1b7a4985775d500db596c66 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 21 May 2024 22:20:49 +0300 Subject: [PATCH 001/136] open existing group chat if it exists on split expense --- src/libs/ReportUtils.ts | 4 ++-- src/libs/actions/IOU.ts | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b8e4c448fdc2..88b7c642b0ee 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5195,7 +5195,7 @@ function shouldReportBeInOptionList({ /** * Attempts to find a report in onyx with the provided list of participants. Does not include threads, task, expense, room, and policy expense chat. */ -function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = allReports): OnyxEntry { +function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = allReports, shouldIncludeGroupChats = false): OnyxEntry { const sortedNewParticipantList = newParticipantList.sort(); return ( Object.values(reports ?? {}).find((report) => { @@ -5209,7 +5209,7 @@ function getChatByParticipants(newParticipantList: number[], reports: OnyxCollec isMoneyRequestReport(report) || isChatRoom(report) || isPolicyExpenseChat(report) || - isGroupChat(report) + (isGroupChat(report) && !shouldIncludeGroupChats) ) { return false; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 2464dbff7dbd..006547044ce7 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3662,11 +3662,9 @@ function getOrCreateOptimisticSplitChatReport(existingSplitChatReportID: string, // Check if the report is available locally if we do have one let existingSplitChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`]; - // If we do not have one locally then we will search for a chat with the same participants (only for 1:1 chats). - const shouldGetOrCreateOneOneDM = participants.length < 2; const allParticipantsAccountIDs = [...participantAccountIDs, currentUserAccountID]; - if (!existingSplitChatReport && shouldGetOrCreateOneOneDM) { - existingSplitChatReport = ReportUtils.getChatByParticipants(allParticipantsAccountIDs); + if (!existingSplitChatReport) { + existingSplitChatReport = ReportUtils.getChatByParticipants(allParticipantsAccountIDs, undefined, participantAccountIDs.length > 1); } // We found an existing chat report we are done... From 061ba9de5fc6293af543d92780a81cfb0ae341a5 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 00:07:15 +0700 Subject: [PATCH 002/136] remove empty object --- src/components/AddPaymentMethodMenu.tsx | 7 ++-- src/components/AttachmentModal.tsx | 3 +- .../HTMLRenderers/MentionReportRenderer.tsx | 3 +- src/components/KYCWall/types.ts | 3 +- .../LHNOptionsList/LHNOptionsList.tsx | 2 +- .../LHNOptionsList/OptionRowLHNData.tsx | 2 +- src/components/LHNOptionsList/types.ts | 3 +- src/components/LocaleContextProvider.tsx | 2 +- .../Reactions/ReactionTooltipContent.tsx | 2 +- .../MoneyRequestPreviewContent.tsx | 8 ++-- src/components/SettlementButton.tsx | 9 ++-- .../withCurrentUserPersonalDetails.tsx | 6 +-- src/hooks/useCurrentUserPersonalDetails.ts | 10 +---- src/libs/DistanceRequestUtils.ts | 3 +- src/libs/NextStepUtils.ts | 13 ++---- src/libs/OptionsListUtils.ts | 17 ++++---- src/libs/PersonalDetailsUtils.ts | 3 +- src/libs/PolicyUtils.ts | 6 +-- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.ts | 42 +++++++++---------- src/libs/getReportPolicyID.ts | 7 ++-- .../ReportActionCompose.tsx | 2 +- 22 files changed, 65 insertions(+), 90 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.tsx b/src/components/AddPaymentMethodMenu.tsx index ac9657694500..93786cd9c437 100644 --- a/src/components/AddPaymentMethodMenu.tsx +++ b/src/components/AddPaymentMethodMenu.tsx @@ -11,7 +11,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {AnchorPosition} from '@src/styles'; import type {Report, Session} from '@src/types/onyx'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Expensicons from './Icon/Expensicons'; import type {PaymentMethod} from './KYCWall/types'; import PopoverMenu from './PopoverMenu'; @@ -32,7 +31,7 @@ type AddPaymentMethodMenuProps = AddPaymentMethodMenuOnyxProps & { onItemSelected: (paymentMethod: PaymentMethod) => void; /** The IOU/Expense report we are paying */ - iouReport?: OnyxEntry | EmptyObject; + iouReport?: OnyxEntry; /** Anchor position for the AddPaymentMenu. */ anchorPosition: AnchorPosition; @@ -65,9 +64,9 @@ function AddPaymentMethodMenu({ // Users can choose to pay with business bank account in case of Expense reports or in case of P2P IOU report // which then starts a bottom up flow and creates a Collect workspace where the payer is an admin and payee is an employee. - const isIOUReport = ReportUtils.isIOUReport(iouReport ?? {}); + const isIOUReport = ReportUtils.isIOUReport(iouReport ?? null); const canUseBusinessBankAccount = - ReportUtils.isExpenseReport(iouReport ?? {}) || (isIOUReport && !ReportActionsUtils.hasRequestFromCurrentAccount(iouReport?.reportID ?? '', session?.accountID ?? 0)); + ReportUtils.isExpenseReport(iouReport ?? null) || (isIOUReport && !ReportActionsUtils.hasRequestFromCurrentAccount(iouReport?.reportID ?? '', session?.accountID ?? 0)); const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || isIOUReport; diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index af7f482198bb..8943262d3a5f 100644 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -26,7 +26,6 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type ModalType from '@src/types/utils/ModalType'; import AttachmentCarousel from './Attachments/AttachmentCarousel'; @@ -99,7 +98,7 @@ type AttachmentModalProps = AttachmentModalOnyxProps & { headerTitle?: string; /** The report that has this attachment */ - report?: OnyxEntry | EmptyObject; + report?: OnyxEntry; /** Optional callback to fire when we want to do something after modal show. */ onModalShow?: () => void; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 345bd338f365..261e01af4227 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -16,7 +16,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type MentionReportOnyxProps = { @@ -28,7 +27,7 @@ type MentionReportRendererProps = MentionReportOnyxProps & CustomRendererProps value.replace(CONST.UNICODE.LTR, '').replace('#', ''); -const getMentionDetails = (htmlAttributeReportID: string, currentReport: OnyxEntry | EmptyObject, reports: OnyxCollection, tnode: TText | TPhrasing) => { +const getMentionDetails = (htmlAttributeReportID: string, currentReport: OnyxEntry, reports: OnyxCollection, tnode: TText | TPhrasing) => { let reportID: string | undefined; let mentionDisplayText: string; diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index 53ed00e04143..568f2a15903f 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -7,7 +7,6 @@ import type {Route} from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; type Source = ValueOf; @@ -45,7 +44,7 @@ type KYCWallProps = { chatReportID?: string; /** The IOU/Expense report we are paying */ - iouReport?: OnyxEntry | EmptyObject; + iouReport?: OnyxEntry; /** Where the popover should be positioned relative to the anchor points. */ anchorAlignment?: AnchorAlignment; diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 6405e3026b1a..5ae35b7adb3c 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -123,7 +123,7 @@ function LHNOptionsList({ if (lastReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { lastReportActionTransactionID = lastReportAction.originalMessage?.IOUTransactionID ?? ''; } - const lastReportActionTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${lastReportActionTransactionID}`] ?? {}; + const lastReportActionTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${lastReportActionTransactionID}`]; return ( ; @@ -84,7 +83,7 @@ type OptionRowLHNDataProps = { transaction: OnyxEntry; /** The transaction linked to the report's last action */ - lastReportActionTransaction?: OnyxEntry; + lastReportActionTransaction?: OnyxEntry; /** Whether a report contains a draft */ hasDraftComment: boolean; diff --git a/src/components/LocaleContextProvider.tsx b/src/components/LocaleContextProvider.tsx index eb7d9324d2ab..22c5a7631241 100644 --- a/src/components/LocaleContextProvider.tsx +++ b/src/components/LocaleContextProvider.tsx @@ -73,7 +73,7 @@ const LocaleContext = createContext({ preferredLocale: CONST.LOCALES.DEFAULT, }); -function LocaleContextProvider({preferredLocale, currentUserPersonalDetails = {}, children}: LocaleContextProviderProps) { +function LocaleContextProvider({preferredLocale, currentUserPersonalDetails, children}: LocaleContextProviderProps) { const locale = preferredLocale ?? CONST.LOCALES.DEFAULT; const selectedTimezone = useMemo(() => currentUserPersonalDetails?.timezone?.selected, [currentUserPersonalDetails]); diff --git a/src/components/Reactions/ReactionTooltipContent.tsx b/src/components/Reactions/ReactionTooltipContent.tsx index 198eba1f969c..8f469b01272c 100644 --- a/src/components/Reactions/ReactionTooltipContent.tsx +++ b/src/components/Reactions/ReactionTooltipContent.tsx @@ -23,7 +23,7 @@ type ReactionTooltipContentProps = Pick PersonalDetailsUtils.getPersonalDetailsByIDs(accountIDs, currentUserPersonalDetails.accountID, true), [currentUserPersonalDetails.accountID, accountIDs]); diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 8a3cf039a322..d0ae956246b2 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -3,6 +3,7 @@ import truncate from 'lodash/truncate'; import React, {useMemo} from 'react'; import {View} from 'react-native'; import type {GestureResponderEvent} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import ConfirmedRoute from '@components/ConfirmedRoute'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -36,7 +37,6 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import type {IOUMessage} from '@src/types/onyx/OriginalMessage'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {MoneyRequestPreviewProps, PendingMessageProps} from './types'; @@ -214,10 +214,8 @@ function MoneyRequestPreviewContent({ }; const getDisplayDeleteAmountText = (): string => { - const iouOriginalMessage: IOUMessage | EmptyObject = action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? action.originalMessage : {}; - const {amount = 0, currency = CONST.CURRENCY.USD} = iouOriginalMessage; - - return CurrencyUtils.convertToDisplayString(amount, currency); + const iouOriginalMessage: OnyxEntry = action?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? action.originalMessage : null; + return CurrencyUtils.convertToDisplayString(iouOriginalMessage?.amount, iouOriginalMessage?.currency); }; const displayAmount = isDeleted ? getDisplayDeleteAmountText() : getDisplayAmountText(); diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index f56c4dd1a863..f0a5224220d0 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -17,7 +17,6 @@ import type {ButtonSizeValue} from '@src/styles/utils/types'; import type {LastPaymentMethod, Policy, Report} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import type {PaymentType} from './ButtonWithDropdownMenu/types'; import * as Expensicons from './Icon/Expensicons'; @@ -55,7 +54,7 @@ type SettlementButtonProps = SettlementButtonOnyxProps & { chatReportID?: string; /** The IOU/Expense report we are paying */ - iouReport?: OnyxEntry | EmptyObject; + iouReport?: OnyxEntry; /** Should we show the payment options? */ shouldHidePaymentOptions?: boolean; @@ -123,7 +122,7 @@ function SettlementButton({ enablePaymentsRoute, // The "iouReport" and "nvpLastPaymentMethod" objects needs to be stable to prevent the "useMemo" // hook from being recreated unnecessarily, hence the use of CONST.EMPTY_ARRAY and CONST.EMPTY_OBJECT - iouReport = CONST.EMPTY_OBJECT, + iouReport = null, nvpLastPaymentMethod = CONST.EMPTY_OBJECT, isDisabled = false, isLoading = false, @@ -150,7 +149,7 @@ function SettlementButton({ const session = useSession(); const chatReport = ReportUtils.getReport(chatReportID); const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); - const shouldShowPaywithExpensifyOption = !isPaidGroupPolicy || (!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport as OnyxEntry)); + const shouldShowPaywithExpensifyOption = !isPaidGroupPolicy || (!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport)); const shouldShowPayElsewhereOption = !isPaidGroupPolicy || policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL; const paymentButtonOptions = useMemo(() => { const buttonOptions = []; @@ -222,7 +221,7 @@ function SettlementButton({ if (confirmApproval) { confirmApproval(); } else { - IOU.approveMoneyRequest(iouReport ?? {}); + IOU.approveMoneyRequest(iouReport); } return; } diff --git a/src/components/withCurrentUserPersonalDetails.tsx b/src/components/withCurrentUserPersonalDetails.tsx index 8bbaf1c9305c..91fd388eabf1 100644 --- a/src/components/withCurrentUserPersonalDetails.tsx +++ b/src/components/withCurrentUserPersonalDetails.tsx @@ -4,10 +4,8 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import getComponentDisplayName from '@libs/getComponentDisplayName'; import type {PersonalDetails} from '@src/types/onyx'; -type CurrentUserPersonalDetails = PersonalDetails | Record; - type HOCProps = { - currentUserPersonalDetails: CurrentUserPersonalDetails; + currentUserPersonalDetails: PersonalDetails; }; type WithCurrentUserPersonalDetailsProps = HOCProps; @@ -32,4 +30,4 @@ export default function ; - function useCurrentUserPersonalDetails() { const session = useSession(); - const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; + const personalDetails = usePersonalDetails(); const accountID = session?.accountID ?? 0; const accountPersonalDetails = personalDetails?.[accountID]; - const currentUserPersonalDetails: CurrentUserPersonalDetails = useMemo( - () => (accountPersonalDetails ? {...accountPersonalDetails, accountID} : {}) as CurrentUserPersonalDetails, - [accountPersonalDetails, accountID], - ); + const currentUserPersonalDetails: PersonalDetails = useMemo(() => ({...accountPersonalDetails, accountID}), [accountPersonalDetails, accountID]); return currentUserPersonalDetails; } diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 9cb48534214e..5c47458bdc14 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -7,7 +7,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {LastSelectedDistanceRates, Report} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; import * as ReportUtils from './ReportUtils'; @@ -48,7 +47,7 @@ const METERS_TO_MILES = 0.000621371; // There are approximately 0.000621371 mile * @returns [currency] - The currency associated with the rate. * @returns [unit] - The unit of measurement for the distance. */ -function getDefaultMileageRate(policy: OnyxEntry | EmptyObject): MileageRate | null { +function getDefaultMileageRate(policy: OnyxEntry): MileageRate | null { if (!policy?.customUnits) { return null; } diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index daa2015b5cd0..8d7c5d21d74c 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -1,14 +1,13 @@ import {format, lastDayOfMonth, setDate} from 'date-fns'; import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; -import type {OnyxCollection} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Report, ReportNextStep} from '@src/types/onyx'; import type {Message} from '@src/types/onyx/ReportNextStep'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import DateUtils from './DateUtils'; import EmailUtils from './EmailUtils'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; @@ -73,16 +72,12 @@ type BuildNextStepParameters = { * @param parameters.isPaidWithExpensify - Whether a report has been paid with Expensify or outside * @returns nextStep */ -function buildNextStep( - report: Report | EmptyObject, - predictedNextStatus: ValueOf, - {isPaidWithExpensify}: BuildNextStepParameters = {}, -): ReportNextStep | null { +function buildNextStep(report: OnyxEntry, predictedNextStatus: ValueOf, {isPaidWithExpensify}: BuildNextStepParameters = {}): ReportNextStep | null { if (!ReportUtils.isExpenseReport(report)) { return null; } - const {policyID = '', ownerAccountID = -1, managerID = -1} = report; + const {policyID = '', ownerAccountID = -1, managerID = -1} = report ?? {}; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? ({} as Policy); const {harvesting, preventSelfApproval, autoReportingFrequency, autoReportingOffset} = policy; const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, ownerAccountID); @@ -282,7 +277,7 @@ function buildNextStep( accountID: currentUserAccountID, email: currentUserEmail, }, - report as Report, + report, ) ) { optimisticNextStep = { diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index f321c10c686e..722c46948642 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -34,7 +34,6 @@ import type { import type {Participant} from '@src/types/onyx/IOU'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import times from '@src/utils/times'; import Timing from './actions/Timing'; @@ -2050,23 +2049,23 @@ function getShareLogOptions(options: OptionList, searchValue = '', betas: Beta[] /** * Build the IOUConfirmation options for showing the payee personalDetail */ -function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails | EmptyObject, amountText?: string): PayeePersonalDetails { - const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login ?? ''); +function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: OnyxEntry, amountText?: string): PayeePersonalDetails { + const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? ''); return { text: PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, formattedLogin), alternateText: formattedLogin || PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, '', false), icons: [ { - source: personalDetail.avatar ?? FallbackAvatar, - name: personalDetail.login ?? '', + source: personalDetail?.avatar ?? FallbackAvatar, + name: personalDetail?.login ?? '', type: CONST.ICON_TYPE_AVATAR, - id: personalDetail.accountID, + id: personalDetail?.accountID, }, ], descriptiveText: amountText ?? '', - login: personalDetail.login ?? '', - accountID: personalDetail.accountID, - keyForList: String(personalDetail.accountID), + login: personalDetail?.login ?? '', + accountID: personalDetail?.accountID ?? 0, + keyForList: String(personalDetail?.accountID), }; } diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index d58ac4d5218c..5ec494f51779 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -1,7 +1,6 @@ import Str from 'expensify-common/lib/str'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import type {CurrentUserPersonalDetails} from '@components/withCurrentUserPersonalDetails'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx'; @@ -272,7 +271,7 @@ function createDisplayName(login: string, passedPersonalDetails: Pick): PolicyEmployee[] { /** * Returns the policy of the report */ -function getPolicy(policyID: string | undefined): Policy | EmptyObject { +function getPolicy(policyID: string | undefined): OnyxEntry { if (!allPolicies || !policyID) { - return {}; + return null; } - return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; + return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; } /** Return active policies where current user is an admin */ diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8a59958c208b..50138241aae6 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6292,7 +6292,7 @@ function isHoldCreator(transaction: OnyxEntry, reportID: string): b /** * Get all held transactions of a iouReport */ -function getAllHeldTransactions(iouReportID: string): Transaction[] { +function getAllHeldTransactions(iouReportID?: string): Transaction[] { const transactions = TransactionUtils.getAllReportTransactions(iouReportID); return transactions.filter((transaction) => TransactionUtils.isOnHold(transaction)); } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 08ce01263a83..29d0cdf95ae4 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6078,7 +6078,7 @@ function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chat return chatReport?.invoiceReceiver?.accountID === userAccountID; } - return PolicyUtils.getPolicy(chatReport?.invoiceReceiver?.policyID).role === CONST.POLICY.ROLE.ADMIN; + return PolicyUtils.getPolicy(chatReport?.invoiceReceiver?.policyID)?.role === CONST.POLICY.ROLE.ADMIN; } const isPayer = ReportUtils.isPayer( @@ -6112,20 +6112,20 @@ function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObj }); } -function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full?: boolean) { - const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - let total = expenseReport.total ?? 0; - const hasHeldExpenses = ReportUtils.hasHeldExpenses(expenseReport.reportID); - if (hasHeldExpenses && !full && !!expenseReport.unheldTotal) { - total = expenseReport.unheldTotal; +function approveMoneyRequest(expenseReport: OnyxEntry, full?: boolean) { + const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport?.reportID}`] ?? null; + let total = expenseReport?.total ?? 0; + const hasHeldExpenses = ReportUtils.hasHeldExpenses(expenseReport?.reportID); + if (hasHeldExpenses && !full && !!expenseReport?.unheldTotal) { + total = expenseReport?.unheldTotal; } - const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport.currency ?? '', expenseReport.reportID); + const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? ''); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.APPROVED); - const chatReport = ReportUtils.getReport(expenseReport.chatReportID); + const chatReport = ReportUtils.getReport(expenseReport?.chatReportID); const optimisticReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport?.reportID}`, value: { [optimisticApprovedReportAction.reportActionID]: { ...(optimisticApprovedReportAction as OnyxTypes.ReportAction), @@ -6135,7 +6135,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full }; const optimisticIOUReportData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.reportID}`, value: { ...expenseReport, lastMessageText: optimisticApprovedReportAction.message?.[0]?.text, @@ -6158,7 +6158,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full const optimisticNextStepData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport?.reportID}`, value: optimisticNextStep, }; const optimisticData: OnyxUpdate[] = [optimisticIOUReportData, optimisticReportActionsData, optimisticNextStepData, optimisticChatReportData]; @@ -6166,7 +6166,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport?.reportID}`, value: { [optimisticApprovedReportAction.reportActionID]: { pendingAction: null, @@ -6175,7 +6175,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.reportID}`, value: { pendingFields: { partial: null, @@ -6187,7 +6187,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport?.reportID}`, value: { [optimisticApprovedReportAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), @@ -6196,7 +6196,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.chatReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.chatReportID}`, value: { hasOutstandingChildRequest: chatReport?.hasOutstandingChildRequest, pendingFields: { @@ -6206,14 +6206,14 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport?.reportID}`, value: currentNextStep, }, ]; // Clear hold reason of all transactions if we approve all requests if (full && hasHeldExpenses) { - const heldTransactions = ReportUtils.getAllHeldTransactions(expenseReport.reportID); + const heldTransactions = ReportUtils.getAllHeldTransactions(expenseReport?.reportID); heldTransactions.forEach((heldTransaction) => { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -6237,7 +6237,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full } const parameters: ApproveMoneyRequestParams = { - reportID: expenseReport.reportID, + reportID: expenseReport?.reportID ?? '', approvedReportActionID: optimisticApprovedReportAction.reportActionID, full, }; @@ -6251,7 +6251,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { const policy = PolicyUtils.getPolicy(expenseReport.policyID); const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); - const adminAccountID = policy.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails.accountID : undefined; + const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails.accountID : undefined; const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED); @@ -6374,7 +6374,7 @@ function cancelPayment(expenseReport: OnyxTypes.Report, chatReport: OnyxTypes.Re const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(expenseReport.reportID, -(expenseReport.total ?? 0), expenseReport.currency ?? ''); const policy = PolicyUtils.getPolicy(chatReport.policyID); const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE; - const approvalMode = policy.approvalMode ?? CONST.POLICY.APPROVAL_MODE.BASIC; + const approvalMode = policy?.approvalMode ?? CONST.POLICY.APPROVAL_MODE.BASIC; let stateNum: ValueOf = CONST.REPORT.STATE_NUM.SUBMITTED; let statusNum: ValueOf = CONST.REPORT.STATUS_NUM.SUBMITTED; if (!isFree) { diff --git a/src/libs/getReportPolicyID.ts b/src/libs/getReportPolicyID.ts index 12124f24fbe7..8751ce640b6f 100644 --- a/src/libs/getReportPolicyID.ts +++ b/src/libs/getReportPolicyID.ts @@ -2,7 +2,6 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; let allReports: OnyxCollection; Onyx.connect({ @@ -14,12 +13,12 @@ Onyx.connect({ /** * Get the report given a reportID */ -function getReport(reportID: string | undefined): OnyxEntry | EmptyObject { +function getReport(reportID: string | undefined): OnyxEntry { if (!allReports) { - return {}; + return null; } - return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? {}; + return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; } /** diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index adf358ab416d..d0ede3f6eb3b 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -104,7 +104,7 @@ const willBlurTextInputOnTapOutside = willBlurTextInputOnTapOutsideFunc(); function ReportActionCompose({ blockedFromConcierge, - currentUserPersonalDetails = {}, + currentUserPersonalDetails, disabled = false, isComposerFullSize = false, onSubmit, From 5024f05740ea997a7e89e5ee8c22dda7bdd38f89 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 00:50:17 +0700 Subject: [PATCH 003/136] remove EmptyObject in PolicyUtils --- src/components/ConnectionLayout.tsx | 4 ++-- src/components/SelectionScreen.tsx | 4 ++-- src/libs/PolicyUtils.ts | 19 +++++++++---------- src/libs/ReportUtils.ts | 27 ++++++++++++--------------- src/libs/actions/IOU.ts | 4 ++-- 5 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/components/ConnectionLayout.tsx b/src/components/ConnectionLayout.tsx index bcb2a0833086..dc0d7fbc5ac7 100644 --- a/src/components/ConnectionLayout.tsx +++ b/src/components/ConnectionLayout.tsx @@ -94,8 +94,8 @@ function ConnectionLayout({ }: ConnectionLayoutProps) { const {translate} = useLocalize(); - const policy = PolicyUtils.getPolicy(policyID ?? ''); - const isConnectionEmpty = isEmpty(policy.connections?.[connectionName]); + const policy = PolicyUtils.getPolicy(policyID); + const isConnectionEmpty = isEmpty(policy?.connections?.[connectionName]); const renderSelectionContent = useMemo( () => ( diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx index c8c290b562b6..3c51f17948bd 100644 --- a/src/components/SelectionScreen.tsx +++ b/src/components/SelectionScreen.tsx @@ -76,8 +76,8 @@ function SelectionScreen({ }: SelectionScreenProps) { const {translate} = useLocalize(); - const policy = PolicyUtils.getPolicy(policyID ?? ''); - const isConnectionEmpty = isEmpty(policy.connections?.[connectionName]); + const policy = PolicyUtils.getPolicy(policyID); + const isConnectionEmpty = isEmpty(policy?.connections?.[connectionName]); return ( | EmptyObject): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; +const isPolicyAdmin = (policy: OnyxEntry): boolean => policy?.role === CONST.POLICY.ROLE.ADMIN; /** * Checks if the policy is a free group policy. */ -const isFreeGroupPolicy = (policy: OnyxEntry | EmptyObject): boolean => policy?.type === CONST.POLICY.TYPE.FREE; +const isFreeGroupPolicy = (policy: OnyxEntry): boolean => policy?.type === CONST.POLICY.TYPE.FREE; const isPolicyEmployee = (policyID: string, policies: OnyxCollection): boolean => Object.values(policies ?? {}).some((policy) => policy?.id === policyID); @@ -267,7 +266,7 @@ function isPendingDeletePolicy(policy: OnyxEntry): boolean { return policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; } -function isPaidGroupPolicy(policy: OnyxEntry | EmptyObject): boolean { +function isPaidGroupPolicy(policy: OnyxEntry): boolean { return policy?.type === CONST.POLICY.TYPE.TEAM || policy?.type === CONST.POLICY.TYPE.CORPORATE; } @@ -284,14 +283,14 @@ function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry | EmptyObject): boolean { +function isInstantSubmitEnabled(policy: OnyxEntry): boolean { return policy?.type === CONST.POLICY.TYPE.FREE || (policy?.autoReporting === true && policy?.autoReportingFrequency === CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT); } /** * Checks if policy's approval mode is "optional", a.k.a. "Submit & Close" */ -function isSubmitAndClose(policy: OnyxEntry | EmptyObject): boolean { +function isSubmitAndClose(policy: OnyxEntry): boolean { return policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL; } @@ -334,7 +333,7 @@ function canEditTaxRate(policy: Policy, taxID: string): boolean { return policy.taxRates?.defaultExternalID !== taxID; } -function isPolicyFeatureEnabled(policy: OnyxEntry | EmptyObject, featureName: PolicyFeatureName): boolean { +function isPolicyFeatureEnabled(policy: OnyxEntry, featureName: PolicyFeatureName): boolean { if (featureName === CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED) { return Boolean(policy?.tax?.trackingEnabled); } @@ -342,7 +341,7 @@ function isPolicyFeatureEnabled(policy: OnyxEntry | EmptyObject, feature return Boolean(policy?.[featureName]); } -function getApprovalWorkflow(policy: OnyxEntry | EmptyObject): ValueOf { +function getApprovalWorkflow(policy: OnyxEntry): ValueOf { if (policy?.type === CONST.POLICY.TYPE.PERSONAL) { return CONST.POLICY.APPROVAL_MODE.OPTIONAL; } @@ -350,14 +349,14 @@ function getApprovalWorkflow(policy: OnyxEntry | EmptyObject): ValueOf | EmptyObject): string { +function getDefaultApprover(policy: OnyxEntry): string { return policy?.approver ?? policy?.owner ?? ''; } /** * Returns the accountID to whom the given employeeAccountID submits reports to in the given Policy. */ -function getSubmitToAccountID(policy: OnyxEntry | EmptyObject, employeeAccountID: number): number { +function getSubmitToAccountID(policy: OnyxEntry, employeeAccountID: number): number { const employeeLogin = getLoginsByAccountIDs([employeeAccountID])[0]; const defaultApprover = getDefaultApprover(policy); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 19575be659b7..451ffd3df096 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -656,11 +656,11 @@ function getRootParentReport(report: OnyxEntry | undefined | EmptyObject /** * Returns the policy of the report */ -function getPolicy(policyID: string | undefined): Policy | EmptyObject { +function getPolicy(policyID: string | undefined): OnyxEntry { if (!allPolicies || !policyID) { - return {}; + return null; } - return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; + return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; } /** @@ -1254,7 +1254,7 @@ function isJoinRequestInAdminRoom(report: OnyxEntry): boolean { // since they are not a part of the company, and should not action it on their behalf. if (report.policyID) { const policy = getPolicy(report.policyID); - if (!PolicyUtils.isExpensifyTeam(policy.owner) && PolicyUtils.isExpensifyTeam(currentUserPersonalDetails?.login)) { + if (!PolicyUtils.isExpensifyTeam(policy?.owner) && PolicyUtils.isExpensifyTeam(currentUserPersonalDetails?.login)) { return false; } } @@ -2623,8 +2623,8 @@ function canEditMoneyRequest(reportAction: OnyxEntry): boolean { return isProcessingReport(moneyRequestReport) && isRequestor; } - const policy = getPolicy(moneyRequestReport?.policyID ?? ''); - const isAdmin = policy.role === CONST.POLICY.ROLE.ADMIN; + const policy = getPolicy(moneyRequestReport?.policyID); + const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; const isManager = currentUserAccountID === moneyRequestReport?.managerID; if (isInvoiceReport(moneyRequestReport) && isManager) { @@ -2679,8 +2679,8 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxEntry, field } if ((fieldToEdit === CONST.EDIT_REQUEST_FIELD.AMOUNT || fieldToEdit === CONST.EDIT_REQUEST_FIELD.CURRENCY) && TransactionUtils.isDistanceRequest(transaction)) { - const policy = getPolicy(moneyRequestReport?.reportID ?? ''); - const isAdmin = isExpenseReport(moneyRequestReport) && policy.role === CONST.POLICY.ROLE.ADMIN; + const policy = getPolicy(moneyRequestReport?.reportID); + const isAdmin = isExpenseReport(moneyRequestReport) && policy?.role === CONST.POLICY.ROLE.ADMIN; const isManager = isExpenseReport(moneyRequestReport) && currentUserAccountID === moneyRequestReport?.managerID; return isAdmin || isManager; @@ -3677,7 +3677,7 @@ function getHumanReadableStatus(statusNum: number): string { * If after all replacements the formula is empty, the original formula is returned. * See {@link https://help.expensify.com/articles/expensify-classic/insights-and-custom-reporting/Custom-Templates} */ -function populateOptimisticReportFormula(formula: string, report: OptimisticExpenseReport, policy: Policy | EmptyObject): string { +function populateOptimisticReportFormula(formula: string, report: OptimisticExpenseReport, policy: OnyxEntry): string { const createdDate = report.lastVisibleActionCreated ? new Date(report.lastVisibleActionCreated) : undefined; const result = formula // We don't translate because the server response is always in English @@ -3685,7 +3685,7 @@ function populateOptimisticReportFormula(formula: string, report: OptimisticExpe .replaceAll('{report:startdate}', createdDate ? format(createdDate, CONST.DATE.FNS_FORMAT_STRING) : '') .replaceAll('{report:total}', report.total !== undefined ? CurrencyUtils.convertToDisplayString(Math.abs(report.total), report.currency).toString() : '') .replaceAll('{report:currency}', report.currency ?? '') - .replaceAll('{report:policyname}', policy.name ?? '') + .replaceAll('{report:policyname}', policy?.name ?? '') .replaceAll('{report:created}', createdDate ? format(createdDate, CONST.DATE.FNS_DATE_TIME_FORMAT_STRING) : '') .replaceAll('{report:created:yyyy-MM-dd}', createdDate ? format(createdDate, CONST.DATE.FNS_FORMAT_STRING) : '') .replaceAll('{report:status}', report.statusNum !== undefined ? getHumanReadableStatus(report.statusNum) : '') @@ -3776,7 +3776,7 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa function getIOUSubmittedMessage(report: OnyxEntry) { const policy = getPolicy(report?.policyID); - if (report?.ownerAccountID !== currentUserAccountID && policy.role === CONST.POLICY.ROLE.ADMIN) { + if (report?.ownerAccountID !== currentUserAccountID && policy?.role === CONST.POLICY.ROLE.ADMIN) { const ownerPersonalDetail = getPersonalDetailsForAccountID(report?.ownerAccountID ?? 0); const ownerDisplayName = `${ownerPersonalDetail.displayName ?? ''}${ownerPersonalDetail.displayName !== ownerPersonalDetail.login ? ` (${ownerPersonalDetail.login})` : ''}`; @@ -6537,11 +6537,8 @@ function isReportOwner(report: OnyxEntry): boolean { function isAllowedToApproveExpenseReport(report: OnyxEntry, approverAccountID?: number): boolean { const policy = getPolicy(report?.policyID); - const {preventSelfApproval} = policy; - const isOwner = (approverAccountID ?? currentUserAccountID) === report?.ownerAccountID; - - return !(preventSelfApproval && isOwner); + return !(policy?.preventSelfApproval && isOwner); } function isAllowedToSubmitDraftExpenseReport(report: OnyxEntry): boolean { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 29d0cdf95ae4..388ffa716364 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6034,7 +6034,7 @@ function sendMoneyWithWallet(report: OnyxEntry, amount: number Report.notifyNewAction(params.chatReportID, managerID); } -function canApproveIOU(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry | EmptyObject) { +function canApproveIOU(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry) { if (isEmptyObject(chatReport)) { return false; } @@ -6061,7 +6061,7 @@ function canApproveIOU(iouReport: OnyxEntry | EmptyObject, cha return isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled && !isArchivedReport; } -function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry | EmptyObject) { +function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); const isChatReportArchived = ReportUtils.isArchivedRoom(chatReport); From ded2bce727d0e6a1bfd9a01736f3245cb1a2198d Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 01:21:30 +0700 Subject: [PATCH 004/136] remove EmptyObject in ReportActionsUtils --- src/libs/ReportActionsUtils.ts | 25 +++++++++++----------- src/libs/ReportUtils.ts | 18 ++++++++-------- src/libs/actions/Policy/Policy.ts | 6 +++--- src/pages/settings/Profile/ProfilePage.tsx | 4 ++-- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 1b2b03e7e2c4..3fabc2983c4e 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -22,7 +22,6 @@ import type { import type Report from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActionMessageJSON, ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import DateUtils from './DateUtils'; import * as Environment from './Environment/Environment'; @@ -121,7 +120,7 @@ function isReversedTransaction(reportAction: OnyxEntry 0; } -function isPendingRemove(reportAction: OnyxEntry | EmptyObject): boolean { +function isPendingRemove(reportAction: OnyxEntry): boolean { if (isEmptyObject(reportAction)) { return false; } @@ -148,7 +147,7 @@ function isModifiedExpenseAction(reportAction: OnyxEntry | ReportA * We are in the process of deprecating reportAction.originalMessage and will be setting the db version of "message" to reportAction.message in the future see: https://github.com/Expensify/App/issues/39797 * In the interim, we must check to see if we have an object or array for the reportAction.message, if we have an array we will use the originalMessage as this means we have not yet migrated. */ -function getWhisperedTo(reportAction: OnyxEntry | EmptyObject): number[] { +function getWhisperedTo(reportAction: OnyxEntry): number[] { const originalMessage = reportAction?.originalMessage; const message = reportAction?.message; @@ -163,7 +162,7 @@ function getWhisperedTo(reportAction: OnyxEntry | EmptyObject): nu return []; } -function isWhisperAction(reportAction: OnyxEntry | EmptyObject): boolean { +function isWhisperAction(reportAction: OnyxEntry): boolean { return getWhisperedTo(reportAction).length > 0; } @@ -216,11 +215,11 @@ function isThreadParentMessage(reportAction: OnyxEntry, reportID: * * @deprecated Use Onyx.connect() or withOnyx() instead */ -function getParentReportAction(report: OnyxEntry | EmptyObject): ReportAction | EmptyObject { +function getParentReportAction(report: OnyxEntry): OnyxEntry { if (!report?.parentReportID || !report.parentReportActionID) { - return {}; + return null; } - return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`]?.[report.parentReportActionID] ?? {}; + return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`]?.[report.parentReportActionID] ?? null; } /** @@ -238,7 +237,7 @@ function isSentMoneyReportAction(reportAction: OnyxEntry | EmptyObject): boolean { +function isTransactionThread(parentReportAction: OnyxEntry): boolean { return ( parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && (parentReportAction.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.CREATE || @@ -1176,9 +1175,9 @@ function isReportActionUnread(reportAction: OnyxEntry, lastReadTim * Check whether the current report action of the report is unread or not * */ -function isCurrentActionUnread(report: Report | EmptyObject, reportAction: ReportAction): boolean { - const lastReadTime = report.lastReadTime ?? ''; - const sortedReportActions = getSortedReportActions(Object.values(getAllReportActions(report.reportID))); +function isCurrentActionUnread(report: OnyxEntry, reportAction: ReportAction): boolean { + const lastReadTime = report?.lastReadTime ?? ''; + const sortedReportActions = getSortedReportActions(Object.values(getAllReportActions(report?.reportID ?? ''))); const currentActionIndex = sortedReportActions.findIndex((action) => action.reportActionID === reportAction.reportActionID); if (currentActionIndex === -1) { return false; @@ -1205,14 +1204,14 @@ function isActionableJoinRequestPending(reportID: string): boolean { return !!findPendingRequest; } -function isApprovedOrSubmittedReportAction(action: OnyxEntry | EmptyObject) { +function isApprovedOrSubmittedReportAction(action: OnyxEntry) { return [CONST.REPORT.ACTIONS.TYPE.APPROVED, CONST.REPORT.ACTIONS.TYPE.SUBMITTED].some((type) => type === action?.actionName); } /** * Gets the text version of the message in a report action */ -function getReportActionMessageText(reportAction: OnyxEntry | EmptyObject): string { +function getReportActionMessageText(reportAction: OnyxEntry): string { return reportAction?.message?.reduce((acc, curr) => `${acc}${curr?.text}`, '') ?? ''; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 451ffd3df096..f7ccd1dbbd65 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1940,11 +1940,11 @@ function getIcons( const parentReportAction = ReportActionsUtils.getParentReportAction(report); const workspaceIcon = getWorkspaceIcon(report, policy); const memberIcon = { - source: personalDetails?.[parentReportAction.actorAccountID ?? -1]?.avatar ?? FallbackAvatar, - id: parentReportAction.actorAccountID, + source: personalDetails?.[parentReportAction?.actorAccountID ?? -1]?.avatar ?? FallbackAvatar, + id: parentReportAction?.actorAccountID, type: CONST.ICON_TYPE_AVATAR, - name: personalDetails?.[parentReportAction.actorAccountID ?? -1]?.displayName ?? '', - fallbackIcon: personalDetails?.[parentReportAction.actorAccountID ?? -1]?.fallbackIcon, + name: personalDetails?.[parentReportAction?.actorAccountID ?? -1]?.displayName ?? '', + fallbackIcon: personalDetails?.[parentReportAction?.actorAccountID ?? -1]?.fallbackIcon, }; return [memberIcon, workspaceIcon]; @@ -1952,14 +1952,14 @@ function getIcons( if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); - const actorAccountID = parentReportAction.actorAccountID; + const actorAccountID = parentReportAction?.actorAccountID; const actorDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(allPersonalDetails?.[actorAccountID ?? -1], '', false); const actorIcon = { id: actorAccountID, source: personalDetails?.[actorAccountID ?? -1]?.avatar ?? FallbackAvatar, name: actorDisplayName, type: CONST.ICON_TYPE_AVATAR, - fallbackIcon: personalDetails?.[parentReportAction.actorAccountID ?? -1]?.fallbackIcon, + fallbackIcon: personalDetails?.[parentReportAction?.actorAccountID ?? -1]?.fallbackIcon, }; if (isWorkspaceThread(report)) { @@ -3057,7 +3057,7 @@ function isChangeLogObject(originalMessage?: ChangeLog): ChangeLog | undefined { * @param parentReportAction * @param parentReportActionMessage */ -function getAdminRoomInvitedParticipants(parentReportAction: ReportAction | Record, parentReportActionMessage: string) { +function getAdminRoomInvitedParticipants(parentReportAction: OnyxEntry, parentReportActionMessage: string) { if (!parentReportAction?.originalMessage) { return parentReportActionMessage || Localize.translateLocal('parentReportAction.deletedMessage'); } @@ -3108,7 +3108,7 @@ function getInvoicePayerName(report: OnyxEntry): string { /** * Get the report action message for a report action. */ -function getReportActionMessage(reportAction: ReportAction | EmptyObject, parentReportID?: string) { +function getReportActionMessage(reportAction: OnyxEntry, parentReportID?: string) { if (isEmptyObject(reportAction)) { return ''; } @@ -6414,7 +6414,7 @@ function getAllAncestorReportActions(report: Report | null | undefined): Ancesto break; } - const isParentReportActionUnread = ReportActionsUtils.isCurrentActionUnread(parentReport ?? {}, parentReportAction); + const isParentReportActionUnread = ReportActionsUtils.isCurrentActionUnread(parentReport, parentReportAction); allAncestors.push({ report: currentReport, reportAction: parentReportAction, diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index fc2a8ad7970e..0f00edc7d2c6 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -3012,12 +3012,12 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[reportPreview.reportActionID]: null}, + value: {[reportPreview?.reportActionID ?? '']: null}, }); failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[reportPreview.reportActionID]: reportPreview}, + value: {[reportPreview?.reportActionID ?? '']: reportPreview}, }); // To optimistically remove the GBR from the DM we need to update the hasOutstandingChildRequest param to false @@ -3059,7 +3059,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, - value: {[reportPreview.reportActionID]: null}, + value: {[reportPreview?.reportActionID ?? '']: null}, }); // Create the MOVED report action and add it to the DM chat which indicates to the user where the report has been moved diff --git a/src/pages/settings/Profile/ProfilePage.tsx b/src/pages/settings/Profile/ProfilePage.tsx index 4c5ed88e6898..ae252da6fff6 100755 --- a/src/pages/settings/Profile/ProfilePage.tsx +++ b/src/pages/settings/Profile/ProfilePage.tsx @@ -26,7 +26,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {LoginList, PersonalDetails, PrivatePersonalDetails} from '@src/types/onyx'; +import type {LoginList, PrivatePersonalDetails} from '@src/types/onyx'; type ProfilePageOnyxProps = { loginList: OnyxEntry; @@ -102,7 +102,7 @@ function ProfilePage({ ]; useEffect(() => { - App.openProfile(currentUserPersonalDetails as PersonalDetails); + App.openProfile(currentUserPersonalDetails); }, [currentUserPersonalDetails]); const privateOptions = [ From a3ed5745d49fa2d9da8df4e6baf43b8a2fb14f12 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 02:06:12 +0700 Subject: [PATCH 005/136] remove EmptyObject in Task --- src/libs/ReportUtils.ts | 89 +++++++++---------- src/libs/WorkspacesSettingsUtils.ts | 6 +- src/libs/actions/IOU.ts | 46 +++++----- src/libs/actions/Policy/Policy.ts | 4 +- src/libs/actions/Task.ts | 6 +- .../home/report/ReportActionItemSingle.tsx | 2 +- 6 files changed, 75 insertions(+), 78 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f7ccd1dbbd65..f00c38319d68 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -53,7 +53,6 @@ import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference, Participants, PendingChatMember, Participant as ReportParticipant} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions, ReportPreviewAction} from '@src/types/onyx/ReportAction'; import type {Comment, Receipt, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import * as IOU from './actions/IOU'; @@ -596,7 +595,7 @@ function getCurrentUserDisplayNameOrEmail(): string | undefined { return currentUserPersonalDetails?.displayName ?? currentUserEmail; } -function getChatType(report: OnyxEntry | Participant | EmptyObject): ValueOf | undefined { +function getChatType(report: OnyxEntry | Participant): ValueOf | undefined { return report?.chatType; } @@ -626,20 +625,20 @@ function isDraftReport(reportID: string | undefined): boolean { /** * Returns the parentReport if the given report is a thread */ -function getParentReport(report: OnyxEntry | EmptyObject): OnyxEntry | EmptyObject { +function getParentReport(report: OnyxEntry): OnyxEntry { if (!report?.parentReportID) { - return {}; + return null; } - return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? {}; + return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? null; } /** * Returns the root parentReport if the given report is nested. * Uses recursion to iterate any depth of nested reports. */ -function getRootParentReport(report: OnyxEntry | undefined | EmptyObject): OnyxEntry | EmptyObject { +function getRootParentReport(report: OnyxEntry | undefined): OnyxEntry { if (!report) { - return {}; + return null; } // Returns the current report as the root report, because it does not have a parentReportID @@ -674,7 +673,7 @@ function getPolicyType(report: OnyxEntry, policies: OnyxCollection | undefined | EmptyObject, returnEmptyIfNotFound = false, policy: OnyxEntry | undefined = undefined): string { +function getPolicyName(report: OnyxEntry | undefined, returnEmptyIfNotFound = false, policy: OnyxEntry | undefined = undefined): string { const noPolicyFound = returnEmptyIfNotFound ? '' : Localize.translateLocal('workspace.common.unavailable'); if (isEmptyObject(report)) { return noPolicyFound; @@ -706,25 +705,25 @@ function getReportParticipantsTitle(accountIDs: number[]): string { /** * Checks if a report is a chat report. */ -function isChatReport(report: OnyxEntry | EmptyObject): boolean { +function isChatReport(report: OnyxEntry): boolean { return report?.type === CONST.REPORT.TYPE.CHAT; } -function isInvoiceReport(report: OnyxEntry | EmptyObject): boolean { +function isInvoiceReport(report: OnyxEntry): boolean { return report?.type === CONST.REPORT.TYPE.INVOICE; } /** * Checks if a report is an Expense report. */ -function isExpenseReport(report: OnyxEntry | EmptyObject): boolean { +function isExpenseReport(report: OnyxEntry): boolean { return report?.type === CONST.REPORT.TYPE.EXPENSE; } /** * Checks if a report is an IOU report using report or reportID */ -function isIOUReport(reportOrID: OnyxEntry | string | EmptyObject): boolean { +function isIOUReport(reportOrID: OnyxEntry | string): boolean { const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return report?.type === CONST.REPORT.TYPE.IOU; } @@ -732,7 +731,7 @@ function isIOUReport(reportOrID: OnyxEntry | string | EmptyObject): bool /** * Checks if a report is an IOU report using report */ -function isIOUReportUsingReport(report: OnyxEntry | EmptyObject): report is Report { +function isIOUReportUsingReport(report: OnyxEntry): report is Report { return report?.type === CONST.REPORT.TYPE.IOU; } /** @@ -749,7 +748,7 @@ function isTaskReport(report: OnyxEntry): boolean { * There's another situation where you don't have access to the parentReportAction (because it was created in a chat you don't have access to) * In this case, we have added the key to the report itself */ -function isCanceledTaskReport(report: OnyxEntry | EmptyObject = {}, parentReportAction: OnyxEntry | EmptyObject = {}): boolean { +function isCanceledTaskReport(report: OnyxEntry, parentReportAction: OnyxEntry = null): boolean { if (!isEmptyObject(parentReportAction) && (parentReportAction?.message?.[0]?.isDeletedParentAction ?? false)) { return true; } @@ -766,7 +765,7 @@ function isCanceledTaskReport(report: OnyxEntry | EmptyObject = {}, pare * * @param parentReportAction - The parent report action of the report (Used to check if the task has been canceled) */ -function isOpenTaskReport(report: OnyxEntry, parentReportAction: OnyxEntry | EmptyObject = {}): boolean { +function isOpenTaskReport(report: OnyxEntry, parentReportAction: OnyxEntry = null): boolean { return ( isTaskReport(report) && !isCanceledTaskReport(report, parentReportAction) && report?.stateNum === CONST.REPORT.STATE_NUM.OPEN && report?.statusNum === CONST.REPORT.STATUS_NUM.OPEN ); @@ -789,7 +788,7 @@ function isReportManager(report: OnyxEntry): boolean { /** * Checks if the supplied report has been approved */ -function isReportApproved(reportOrID: OnyxEntry | string | EmptyObject): boolean { +function isReportApproved(reportOrID: OnyxEntry | string): boolean { const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return report?.stateNum === CONST.REPORT.STATE_NUM.APPROVED && report?.statusNum === CONST.REPORT.STATUS_NUM.APPROVED; } @@ -797,7 +796,7 @@ function isReportApproved(reportOrID: OnyxEntry | string | EmptyObject): /** * Checks if the supplied report is an expense report in Open state and status. */ -function isOpenExpenseReport(report: OnyxEntry | EmptyObject): boolean { +function isOpenExpenseReport(report: OnyxEntry): boolean { return isExpenseReport(report) && report?.stateNum === CONST.REPORT.STATE_NUM.OPEN && report?.statusNum === CONST.REPORT.STATUS_NUM.OPEN; } @@ -827,7 +826,7 @@ function isSettled(reportID: string | undefined): boolean { if (!allReports || !reportID) { return false; } - const report: Report | EmptyObject = allReports[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? {}; + const report = allReports[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; if (isEmptyObject(report) || report.isWaitingOnBankAccount) { return false; } @@ -897,7 +896,7 @@ function isUserCreatedPolicyRoom(report: OnyxEntry): boolean { /** * Whether the provided report is a Policy Expense chat. */ -function isPolicyExpenseChat(report: OnyxEntry | Participant | EmptyObject): boolean { +function isPolicyExpenseChat(report: OnyxEntry | Participant): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || (report?.isPolicyExpenseChat ?? false); } @@ -967,7 +966,7 @@ function isPaidGroupPolicyExpenseReport(report: OnyxEntry): boolean { /** * Checks if the supplied report is an invoice report in Open state and status. */ -function isOpenInvoiceReport(report: OnyxEntry | EmptyObject): boolean { +function isOpenInvoiceReport(report: OnyxEntry): boolean { return isInvoiceReport(report) && report?.statusNum === CONST.REPORT.STATUS_NUM.OPEN; } @@ -1102,7 +1101,7 @@ function sortReportsByLastRead(reports: Array>, reportMetadata /** * Returns true if report is still being processed */ -function isProcessingReport(report: OnyxEntry | EmptyObject): boolean { +function isProcessingReport(report: OnyxEntry): boolean { return report?.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && report?.statusNum === CONST.REPORT.STATUS_NUM.SUBMITTED; } @@ -1227,7 +1226,7 @@ function isClosedExpenseReportWithNoExpenses(report: OnyxEntry): boolean /** * Whether the provided report is an archived room */ -function isArchivedRoom(report: OnyxEntry | EmptyObject, reportNameValuePairs?: OnyxEntry | EmptyObject): boolean { +function isArchivedRoom(report: OnyxEntry, reportNameValuePairs?: OnyxEntry): boolean { if (reportNameValuePairs) { return reportNameValuePairs.isArchived; } @@ -1238,7 +1237,7 @@ function isArchivedRoom(report: OnyxEntry | EmptyObject, reportNameValue /** * Whether the provided report is a closed report */ -function isClosedReport(report: OnyxEntry | EmptyObject): boolean { +function isClosedReport(report: OnyxEntry): boolean { return report?.statusNum === CONST.REPORT.STATUS_NUM.CLOSED; } @@ -1405,7 +1404,7 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { /** * Checks if a report is an IOU or expense report. */ -function isMoneyRequestReport(reportOrID: OnyxEntry | EmptyObject | string): boolean { +function isMoneyRequestReport(reportOrID: OnyxEntry | string): boolean { const report = typeof reportOrID === 'object' ? reportOrID : allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null; return isIOUReport(report) || isExpenseReport(report); } @@ -2177,11 +2176,7 @@ function getReimbursementQueuedActionMessage(reportAction: OnyxEntry, - report: OnyxEntry | EmptyObject, - isLHNPreview = false, -): string { +function getReimbursementDeQueuedActionMessage(reportAction: OnyxEntry, report: OnyxEntry, isLHNPreview = false): string { const originalMessage = reportAction?.originalMessage as ReimbursementDeQueuedMessage | undefined; const amount = originalMessage?.amount; const currency = originalMessage?.currency; @@ -2260,7 +2255,7 @@ function getLastVisibleMessage(reportID: string | undefined, actionsToMerge: Rep * * @param [parentReportAction] - The parent report action of the report (Used to check if the task has been canceled) */ -function isWaitingForAssigneeToCompleteTask(report: OnyxEntry, parentReportAction: OnyxEntry | EmptyObject = {}): boolean { +function isWaitingForAssigneeToCompleteTask(report: OnyxEntry, parentReportAction: OnyxEntry): boolean { return isTaskReport(report) && isReportManager(report) && isOpenTaskReport(report, parentReportAction); } @@ -2283,7 +2278,7 @@ function isUnreadWithMention(reportOrOption: OnyxEntry | OptionData): bo * @param option (report or optionItem) * @param parentReportAction (the report action the current report is a thread of) */ -function requiresAttentionFromCurrentUser(optionOrReport: OnyxEntry | OptionData, parentReportAction: EmptyObject | OnyxEntry = {}) { +function requiresAttentionFromCurrentUser(optionOrReport: OnyxEntry | OptionData, parentReportAction: OnyxEntry = null) { if (!optionOrReport) { return false; } @@ -2755,14 +2750,14 @@ function hasMissingSmartscanFields(iouReportID: string): boolean { * * NOTE: This method is only meant to be used inside this action file. Do not export and use it elsewhere. Use withOnyx or Onyx.connect() instead. */ -function getLinkedTransaction(reportAction: OnyxEntry): Transaction | EmptyObject { +function getLinkedTransaction(reportAction: OnyxEntry): OnyxEntry { let transactionID = ''; if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { transactionID = (reportAction?.originalMessage as IOUMessage)?.IOUTransactionID ?? ''; } - return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {}; + return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? null; } /** @@ -2818,13 +2813,13 @@ function getTransactionReportName(reportAction: OnyxEntry | EmptyObject, - iouReportAction: OnyxEntry | EmptyObject = {}, + report: OnyxEntry, + iouReportAction: OnyxEntry = null, shouldConsiderScanningReceiptOrPendingRoute = false, isPreviewMessageForParentChatReport = false, policy: OnyxEntry = null, isForListPreview = false, - originalReportAction: OnyxEntry | EmptyObject = iouReportAction, + originalReportAction: OnyxEntry = iouReportAction, ): string { const reportActionMessage = iouReportAction?.message?.[0]?.html ?? ''; @@ -3942,7 +3937,7 @@ function buildOptimisticIOUReportAction( receipt: Receipt = {}, isOwnPolicyExpenseChat = false, created = DateUtils.getDBTime(), - linkedExpenseReportAction: ReportAction | EmptyObject = {}, + linkedExpenseReportAction: OnyxEntry = null, ): OptimisticIOUReportAction { const IOUReportID = iouReportID || generateReportID(); @@ -4977,7 +4972,7 @@ function buildOptimisticMoneyRequestEntities( isPersonalTrackingExpense?: boolean, existingTransactionThreadReportID?: string, linkedTrackedExpenseReportAction?: ReportAction, -): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction | EmptyObject] { +): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction | null] { const createdActionForChat = buildOptimisticCreatedReportAction(payeeEmail); // The `CREATED` action must be optimistically generated before the IOU action so that it won't appear after the IOU action in the chat. @@ -5003,7 +4998,7 @@ function buildOptimisticMoneyRequestEntities( // Create optimistic transactionThread and the `CREATED` action for it, if existingTransactionThreadReportID is undefined const transactionThread = buildTransactionThread(iouAction, iouReport, existingTransactionThreadReportID); - const createdActionForTransactionThread = existingTransactionThreadReportID ? {} : buildOptimisticCreatedReportAction(payeeEmail); + const createdActionForTransactionThread = existingTransactionThreadReportID ? null : buildOptimisticCreatedReportAction(payeeEmail); // The IOU action and the transactionThread are co-dependent as parent-child, so we need to link them together iouAction.childReportID = existingTransactionThreadReportID ?? transactionThread.reportID; @@ -5351,7 +5346,7 @@ function getAllPolicyReports(policyID: string): Array> { /** * Returns true if Chronos is one of the chat participants (1:1) */ -function chatIncludesChronos(report: OnyxEntry | EmptyObject): boolean { +function chatIncludesChronos(report: OnyxEntry): boolean { const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number); return participantAccountIDs.includes(CONST.ACCOUNT_ID.CHRONOS); } @@ -5852,7 +5847,7 @@ function getAddWorkspaceRoomOrChatReportErrors(report: OnyxEntry): Error /** * Return true if the expense report is marked for deletion. */ -function isMoneyRequestReportPendingDeletion(report: OnyxEntry | EmptyObject): boolean { +function isMoneyRequestReportPendingDeletion(report: OnyxEntry): boolean { if (!isMoneyRequestReport(report)) { return false; } @@ -6481,7 +6476,7 @@ function getAllAncestorReportActionIDs(report: Report | null | undefined, includ * @param lastVisibleActionCreated Last visible action created of the child report * @param type The type of action in the child report */ -function getOptimisticDataForParentReportAction(reportID: string, lastVisibleActionCreated: string, type: string): Array { +function getOptimisticDataForParentReportAction(reportID: string, lastVisibleActionCreated: string, type: string): Array { const report = getReport(reportID); if (!report || isEmptyObject(report)) { @@ -6495,13 +6490,13 @@ function getOptimisticDataForParentReportAction(reportID: string, lastVisibleAct const ancestorReport = getReport(ancestors.reportIDs[index]); if (!ancestorReport || isEmptyObject(ancestorReport)) { - return {} as EmptyObject; + return null; } const ancestorReportAction = ReportActionsUtils.getReportAction(ancestorReport.reportID, ancestors.reportActionsIDs[index]); if (!ancestorReportAction || isEmptyObject(ancestorReportAction)) { - return {} as EmptyObject; + return null; } return { @@ -6514,7 +6509,7 @@ function getOptimisticDataForParentReportAction(reportID: string, lastVisibleAct }); } -function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry | EmptyObject): boolean { +function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry): boolean { if (isEmptyObject(policy)) { return false; } @@ -6723,7 +6718,7 @@ function createDraftTransactionAndNavigateToParticipantSelector(transactionID: s /** * @returns the object to update `report.hasOutstandingChildRequest` */ -function getOutstandingChildRequest(iouReport: OnyxEntry | EmptyObject): OutstandingChildRequest { +function getOutstandingChildRequest(iouReport: OnyxEntry): OutstandingChildRequest { if (!iouReport || isEmptyObject(iouReport)) { return {}; } @@ -6757,7 +6752,7 @@ function canReportBeMentionedWithinPolicy(report: OnyxEntry, policyID: s } function shouldShowMerchantColumn(transactions: Transaction[]) { - return transactions.some((transaction) => isExpenseReport(allReports?.[transaction.reportID] ?? {})); + return transactions.some((transaction) => isExpenseReport(allReports?.[transaction.reportID] ?? null)); } export { diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index 6c57c2a6f99d..74c5125855d4 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -4,7 +4,7 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, ReimbursementAccount, Report, ReportActions} from '@src/types/onyx'; +import type {Policy, ReimbursementAccount, Report, ReportAction, ReportActions} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import * as CurrencyUtils from './CurrencyUtils'; import type {Phrase, PhraseParameters} from './Localize'; @@ -66,10 +66,10 @@ const getBrickRoadForPolicy = (report: Report, altReportActions?: OnyxCollection } // To determine if the report requires attention from the current user, we need to load the parent report action - let itemParentReportAction = {}; + let itemParentReportAction: OnyxEntry = null; if (report.parentReportID) { const itemParentReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`] ?? {}; - itemParentReportAction = report.parentReportActionID ? itemParentReportActions[report.parentReportActionID] : {}; + itemParentReportAction = report.parentReportActionID ? itemParentReportActions[report.parentReportActionID] : null; } const reportOption = {...report, isUnread: ReportUtils.isUnread(report), isUnreadWithMention: ReportUtils.isUnreadWithMention(report)}; const shouldShowGreenDotIndicator = ReportUtils.requiresAttentionFromCurrentUser(reportOption, itemParentReportAction); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 388ffa716364..d657ba27f170 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -462,7 +462,7 @@ function buildOnyxDataForMoneyRequest( optimisticPolicyRecentlyUsedTags: OnyxTypes.RecentlyUsedTags, isNewChatReport: boolean, transactionThreadReport: OptimisticChatReport | EmptyObject, - transactionThreadCreatedReportAction: OptimisticCreatedReportAction | EmptyObject, + transactionThreadCreatedReportAction: OptimisticCreatedReportAction | null, shouldCreateNewMoneyRequestReport: boolean, policy?: OnyxEntry, policyTagList?: OnyxEntry, @@ -850,7 +850,7 @@ function buildOnyxDataForInvoice( optimisticPolicyRecentlyUsedTags: OnyxTypes.RecentlyUsedTags, isNewChatReport: boolean, transactionThreadReport: OptimisticChatReport, - transactionThreadCreatedReportAction: OptimisticCreatedReportAction | EmptyObject, + transactionThreadCreatedReportAction: OptimisticCreatedReportAction | null, policy?: OnyxEntry, policyTagList?: OnyxEntry, policyCategories?: OnyxEntry, @@ -907,7 +907,7 @@ function buildOnyxDataForInvoice( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, value: { - [transactionThreadCreatedReportAction.reportActionID]: transactionThreadCreatedReportAction, + [transactionThreadCreatedReportAction?.reportActionID ?? '']: transactionThreadCreatedReportAction, }, }, // Remove the temporary transaction used during the creation flow @@ -1037,7 +1037,7 @@ function buildOnyxDataForInvoice( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, value: { - [transactionThreadCreatedReportAction.reportActionID]: { + [transactionThreadCreatedReportAction?.reportActionID ?? '']: { pendingAction: null, errors: null, }, @@ -1124,7 +1124,7 @@ function buildOnyxDataForInvoice( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, value: { - [transactionThreadCreatedReportAction.reportActionID]: { + [transactionThreadCreatedReportAction?.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateInvoiceFailureMessage', false, errorKey), }, }, @@ -1159,7 +1159,7 @@ function buildOnyxDataForTrackExpense( iouAction: OptimisticIOUReportAction, reportPreviewAction: OnyxEntry, transactionThreadReport: OptimisticChatReport | EmptyObject, - transactionThreadCreatedReportAction: OptimisticCreatedReportAction | EmptyObject, + transactionThreadCreatedReportAction: OptimisticCreatedReportAction | null, shouldCreateNewMoneyRequestReport: boolean, policy?: OnyxEntry, policyTagList?: OnyxEntry, @@ -1493,7 +1493,7 @@ function buildOnyxDataForTrackExpense( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, value: { - [transactionThreadCreatedReportAction.reportActionID]: { + [transactionThreadCreatedReportAction?.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }, @@ -2020,7 +2020,7 @@ function getMoneyRequestInformation( optimisticPolicyRecentlyUsedTags, isNewChatReport, optimisticTransactionThread ?? {}, - optimisticCreatedActionForTransactionThread ?? {}, + optimisticCreatedActionForTransactionThread, shouldCreateNewMoneyRequestReport, policy, policyTagList, @@ -2230,7 +2230,7 @@ function getTrackExpenseInformation( iouAction, reportPreviewAction, optimisticTransactionThread ?? {}, - (optimisticCreatedActionForTransactionThread as OptimisticCreatedReportAction) ?? {}, // Add type assertion here + optimisticCreatedActionForTransactionThread, shouldCreateNewMoneyRequestReport, policy, policyTagList, @@ -2249,7 +2249,7 @@ function getTrackExpenseInformation( createdIOUReportActionID: shouldCreateNewMoneyRequestReport ? optimisticCreatedActionForIOUReport.reportActionID : '0', reportPreviewAction: reportPreviewAction ?? undefined, transactionThreadReportID: optimisticTransactionThread.reportID, - createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, + createdReportActionIDForThread: optimisticCreatedActionForTransactionThread?.reportActionID ?? '0', actionableWhisperReportActionIDParam: actionableTrackExpenseWhisper?.reportActionID ?? '', onyxData: { optimisticData: [...optimisticData, ...trackExpenseOnyxData[0]], @@ -2527,9 +2527,9 @@ function getUpdateMoneyRequestParams( // Step 4: Compute the IOU total and update the report preview message (and report header) so LHN amount owed is correct. const diff = calculateDiffAmount(iouReport, updatedTransaction, transaction); - let updatedMoneyRequestReport: OnyxTypes.Report | EmptyObject; + let updatedMoneyRequestReport: OnyxEntry; if (!iouReport) { - updatedMoneyRequestReport = {}; + updatedMoneyRequestReport = null; } else if ((ReportUtils.isExpenseReport(iouReport) || ReportUtils.isInvoiceReport(iouReport)) && typeof iouReport.total === 'number') { // For expense report, the amount is negative, so we should subtract total from diff updatedMoneyRequestReport = { @@ -2543,7 +2543,9 @@ function getUpdateMoneyRequestParams( updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID ?? -1, diff, TransactionUtils.getCurrency(transaction), false, true); } - updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, transactionDetails?.currency); + if (updatedMoneyRequestReport) { + updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, transactionDetails?.currency); + } optimisticData.push( { @@ -4142,7 +4144,7 @@ function createSplitsAndOnyxData( createdIOUReportActionID: oneOnOneCreatedActionForIOU.reportActionID, reportPreviewReportActionID: oneOnOneReportPreviewAction.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, - createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, + createdReportActionIDForThread: optimisticCreatedActionForTransactionThread?.reportActionID, }; splits.push(individualSplit); @@ -4829,7 +4831,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA createdIOUReportActionID: oneOnOneCreatedActionForIOU.reportActionID, reportPreviewReportActionID: oneOnOneReportPreviewAction.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, - createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, + createdReportActionIDForThread: optimisticCreatedActionForTransactionThread?.reportActionID, }); optimisticData.push(...oneOnOneOptimisticData); @@ -5644,7 +5646,7 @@ function getSendMoneyParams( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTransactionThread.reportID}`, value: { - [optimisticCreatedActionForTransactionThread.reportActionID]: optimisticCreatedActionForTransactionThread, + [optimisticCreatedActionForTransactionThread?.reportActionID ?? '']: optimisticCreatedActionForTransactionThread, }, }; @@ -5729,7 +5731,7 @@ function getSendMoneyParams( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTransactionThread.reportID}`, value: { - [optimisticCreatedActionForTransactionThread.reportActionID]: { + [optimisticCreatedActionForTransactionThread?.reportActionID ?? '']: { pendingAction: null, }, }, @@ -5757,7 +5759,7 @@ function getSendMoneyParams( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTransactionThread.reportID}`, value: { - [optimisticCreatedActionForTransactionThread.reportActionID]: { + [optimisticCreatedActionForTransactionThread?.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }, @@ -5835,7 +5837,7 @@ function getSendMoneyParams( reportPreviewReportActionID: reportPreviewAction.reportActionID, createdIOUReportActionID: optimisticCreatedActionForIOUReport.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, - createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, + createdReportActionIDForThread: optimisticCreatedActionForTransactionThread?.reportActionID ?? '0', }, optimisticData, successData, @@ -6034,7 +6036,7 @@ function sendMoneyWithWallet(report: OnyxEntry, amount: number Report.notifyNewAction(params.chatReportID, managerID); } -function canApproveIOU(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry) { +function canApproveIOU(iouReport: OnyxEntry, chatReport: OnyxEntry, policy: OnyxEntry) { if (isEmptyObject(chatReport)) { return false; } @@ -6061,7 +6063,7 @@ function canApproveIOU(iouReport: OnyxEntry | EmptyObject, cha return isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled && !isArchivedReport; } -function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry) { +function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry, policy: OnyxEntry) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); const isChatReportArchived = ReportUtils.isArchivedRoom(chatReport); @@ -6101,7 +6103,7 @@ function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chat ); } -function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObject, excludedIOUReportID: string): boolean { +function hasIOUToApproveOrPay(chatReport: OnyxEntry, excludedIOUReportID: string): boolean { const chatReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`] ?? {}; return Object.values(chatReportActions).some((action) => { diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 0f00edc7d2c6..006b8c0e5598 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -2686,7 +2686,7 @@ function dismissAddedWithPrimaryLoginMessages(policyID: string) { * * @returns policyID of the workspace we have created */ -function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string | undefined { +function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): string | undefined { // This flow only works for IOU reports if (!ReportUtils.isIOUReportUsingReport(iouReport)) { return; @@ -3047,7 +3047,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string message: [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - text: ReportUtils.getReportPreviewMessage(expenseReport, {}, false, false, newWorkspace), + text: ReportUtils.getReportPreviewMessage(expenseReport, null, false, false, newWorkspace), }, ], created: DateUtils.getDBTime(), diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 55d898d3d4f3..fe749b9d0ea1 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -814,11 +814,11 @@ function getParentReportAction(report: OnyxEntry): ReportActio /** * Returns the parentReport if the given report is a thread */ -function getParentReport(report: OnyxEntry | EmptyObject): OnyxEntry | EmptyObject { +function getParentReport(report: OnyxEntry | EmptyObject): OnyxEntry { if (!report?.parentReportID) { - return {}; + return null; } - return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? {}; + return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? null; } /** diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index f71db06c2d44..07a4636cf50e 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -85,7 +85,7 @@ function ReportActionItemSingle({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing let actorHint = (login || (displayName ?? '')).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && iouReport, [action?.actionName, iouReport]); - const isInvoiceReport = ReportUtils.isInvoiceReport(iouReport ?? {}); + const isInvoiceReport = ReportUtils.isInvoiceReport(iouReport ?? null); const isWorkspaceActor = isInvoiceReport || (ReportUtils.isPolicyExpenseChat(report) && (!actorAccountID || displayAllActors)); let avatarSource = avatar; let avatarId: number | string | undefined = actorAccountID; From 16b1e368ae30477abc89cd07762c3110b4d0962c Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 02:38:21 +0700 Subject: [PATCH 006/136] remove EmptyObject in Navigation, Report, Task, User and Welcome actions --- src/libs/Navigation/Navigation.ts | 4 ++-- src/libs/Navigation/dismissModalWithReport.ts | 8 ++++---- src/libs/actions/Report.ts | 17 ++++++++--------- src/libs/actions/Task.ts | 3 +-- src/libs/actions/User.ts | 3 +-- src/libs/actions/Welcome.ts | 3 +-- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 3fa3e0c5c317..7a4254a96b4f 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -1,6 +1,7 @@ import {findFocusedRoute} from '@react-navigation/core'; import type {EventArg, NavigationContainerEventMap} from '@react-navigation/native'; import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native'; +import type {OnyxEntry} from 'react-native-onyx'; import Log from '@libs/Log'; import * as ReportUtils from '@libs/ReportUtils'; import {getReport} from '@libs/ReportUtils'; @@ -10,7 +11,6 @@ import type {HybridAppRoute, Route} from '@src/ROUTES'; import ROUTES, {HYBRID_APP_ROUTES} from '@src/ROUTES'; import {PROTECTED_SCREENS} from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import originalCloseRHPFlow from './closeRHPFlow'; import originalDismissModal from './dismissModal'; import originalDismissModalWithReport from './dismissModalWithReport'; @@ -68,7 +68,7 @@ const closeRHPFlow = (ref = navigationRef) => originalCloseRHPFlow(ref); // Re-exporting the dismissModalWithReport here to fill in default value for navigationRef. The dismissModalWithReport isn't defined in this file to avoid cyclic dependencies. // This method is needed because it allows to dismiss the modal and then open the report. Within this method is checked whether the report belongs to a specific workspace. Sometimes the report we want to check, hasn't been added to the Onyx yet. // Then we can pass the report as a param without getting it from the Onyx. -const dismissModalWithReport = (report: Report | EmptyObject, ref = navigationRef) => originalDismissModalWithReport(report, ref); +const dismissModalWithReport = (report: OnyxEntry, ref = navigationRef) => originalDismissModalWithReport(report, ref); /** Method for finding on which index in stack we are. */ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number | undefined { diff --git a/src/libs/Navigation/dismissModalWithReport.ts b/src/libs/Navigation/dismissModalWithReport.ts index c0405c2c9da0..c63e4ba1367e 100644 --- a/src/libs/Navigation/dismissModalWithReport.ts +++ b/src/libs/Navigation/dismissModalWithReport.ts @@ -2,6 +2,7 @@ import {getActionFromState} from '@react-navigation/core'; import type {NavigationContainerRef} from '@react-navigation/native'; import {StackActions} from '@react-navigation/native'; import {findLastIndex} from 'lodash'; +import type {OnyxEntry} from 'react-native-onyx'; import Log from '@libs/Log'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; @@ -9,7 +10,6 @@ import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type {Report} from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import getPolicyIDFromState from './getPolicyIDFromState'; import getStateFromPath from './getStateFromPath'; @@ -25,7 +25,7 @@ import type {RootStackParamList, StackNavigationAction, State} from './types'; * * @param targetReportID - The reportID to navigate to after dismissing the modal */ -function dismissModalWithReport(targetReport: Report | EmptyObject, navigationRef: NavigationContainerRef) { +function dismissModalWithReport(targetReport: OnyxEntry, navigationRef: NavigationContainerRef) { if (!navigationRef.isReady()) { return; } @@ -44,8 +44,8 @@ function dismissModalWithReport(targetReport: Report | EmptyObject, navigationRe case SCREENS.REPORT_AVATAR: case SCREENS.CONCIERGE: // If we are not in the target report, we need to navigate to it after dismissing the modal - if (targetReport.reportID !== getTopmostReportId(state)) { - const reportState = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReport.reportID)); + if (targetReport?.reportID !== getTopmostReportId(state)) { + const reportState = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReport?.reportID ?? '')); const policyID = getPolicyIDFromState(state as State); const policyMemberAccountIDs = getPolicyEmployeeAccountIDs(policyID); const shouldOpenAllWorkspace = isEmptyObject(targetReport) ? true : !doesReportBelongToWorkspace(targetReport, policyMemberAccountIDs, policyID); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 88c33c9d98ad..44046f2cf85d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -99,7 +99,6 @@ import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage import type {NotificationPreference, Participants, Participant as ReportParticipant, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CachedPDFPaths from './CachedPDFPaths'; import * as Modal from './Modal'; @@ -974,8 +973,8 @@ function navigateToAndOpenReport( optimisticReportID?: string, isGroupChat = false, ) { - let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; - let chat: OnyxEntry | EmptyObject = {}; + let newChat: ReportUtils.OptimisticChatReport | null = null; + let chat: OnyxEntry = null; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); // If we are not creating a new Group Chat then we are creating a 1:1 DM and will look for an existing chat @@ -994,12 +993,12 @@ function navigateToAndOpenReport( const report = isEmptyObject(chat) ? newChat : chat; // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server - openReport(report.reportID, '', userLogins, newChat, undefined, undefined, undefined, avatarFile); + openReport(report?.reportID ?? '', '', userLogins, newChat ?? {}, undefined, undefined, undefined, avatarFile); if (shouldDismissModal) { Navigation.dismissModalWithReport(report); } else { Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME}); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID)); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report?.reportID ?? '')); } } @@ -1009,7 +1008,7 @@ function navigateToAndOpenReport( * @param participantAccountIDs of user logins to start a chat report with. */ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) { - let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; + let newChat: ReportUtils.OptimisticChatReport | null = null; const chat = ReportUtils.getChatByParticipants([...participantAccountIDs, currentUserAccountID]); if (!chat) { newChat = ReportUtils.buildOptimisticChatReport([...participantAccountIDs, currentUserAccountID]); @@ -1017,7 +1016,7 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) const report = chat ?? newChat; // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server - openReport(report.reportID, '', [], newChat, '0', false, participantAccountIDs); + openReport(report?.reportID ?? '', '', [], newChat ?? {}, '0', false, participantAccountIDs); Navigation.dismissModalWithReport(report); } @@ -1602,7 +1601,7 @@ function updateNotificationPreference( navigate: boolean, parentReportID?: string, parentReportActionID?: string, - report: OnyxEntry | EmptyObject = {}, + report: OnyxEntry = null, ) { if (previousValue === newValue) { if (navigate && !isEmptyObject(report) && report.reportID) { @@ -1648,7 +1647,7 @@ function updateNotificationPreference( } } -function updateRoomVisibility(reportID: string, previousValue: RoomVisibility | undefined, newValue: RoomVisibility, navigate: boolean, report: OnyxEntry | EmptyObject = {}) { +function updateRoomVisibility(reportID: string, previousValue: RoomVisibility | undefined, newValue: RoomVisibility, navigate: boolean, report: OnyxEntry = null) { if (previousValue === newValue) { if (navigate && !isEmptyObject(report) && report.reportID) { ReportUtils.goBackToDetailsPage(report); diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index fe749b9d0ea1..f13ae0303d37 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -22,7 +22,6 @@ import type {Icon} from '@src/types/onyx/OnyxCommon'; import type {ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Report from './Report'; type OptimisticReport = Pick; @@ -814,7 +813,7 @@ function getParentReportAction(report: OnyxEntry): ReportActio /** * Returns the parentReport if the given report is a thread */ -function getParentReport(report: OnyxEntry | EmptyObject): OnyxEntry { +function getParentReport(report: OnyxEntry): OnyxEntry { if (!report?.parentReportID) { return null; } diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index f7e90f775b65..3212aff71122 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -44,7 +44,6 @@ import type {Status} from '@src/types/onyx/PersonalDetails'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OriginalMessage} from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; import * as Link from './Link'; import * as Report from './Report'; @@ -60,7 +59,7 @@ Onyx.connect({ }, }); -let myPersonalDetails: OnyxEntry | EmptyObject = {}; +let myPersonalDetails: OnyxEntry; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index 119b7da42e21..c4b6d86794a3 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -4,7 +4,6 @@ import type {OnboardingPurposeType} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type Onboarding from '@src/types/onyx/Onboarding'; import type OnyxPolicy from '@src/types/onyx/Policy'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; let onboarding: Onboarding | [] | undefined; let hasDismissedModal: boolean | undefined; @@ -110,7 +109,7 @@ Onyx.connect({ }, }); -const allPolicies: OnyxCollection | EmptyObject = {}; +const allPolicies: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY, callback: (val, key) => { From 2243993909ac216fbd290034039ddffa184666e6 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 03:01:25 +0700 Subject: [PATCH 007/136] remove EmptyObject in several components --- src/libs/actions/Policy/Policy.ts | 45 +++++++++++---------- src/pages/ProfilePage.tsx | 12 +++--- src/pages/home/report/ReportActionsList.tsx | 7 ++-- src/pages/home/report/ReportFooter.tsx | 9 ++--- src/types/onyx/ReportAction.ts | 3 +- 5 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 006b8c0e5598..f94482e018df 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -82,7 +82,6 @@ import type {ErrorFields, Errors, PendingAction} from '@src/types/onyx/OnyxCommo import type {OriginalMessageJoinPolicyChangeLog} from '@src/types/onyx/OriginalMessage'; import type {Attributes, CompanyAddress, CustomUnit, Rate, Unit} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { @@ -222,11 +221,11 @@ function isCurrencySupportedForDirectReimbursement(currency: string) { /** * Returns the policy of the report */ -function getPolicy(policyID: string | undefined): Policy | EmptyObject { +function getPolicy(policyID: string | undefined): OnyxEntry { if (!allPolicies || !policyID) { - return {}; + return null; } - return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; + return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; } /** @@ -461,11 +460,11 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReporting: policy.autoReporting ?? null, + autoReporting: policy?.autoReporting ?? null, harvesting: { - enabled: policy.harvesting?.enabled ?? null, + enabled: policy?.harvesting?.enabled ?? null, }, - autoReportingFrequency: policy.autoReportingFrequency ?? null, + autoReportingFrequency: policy?.autoReportingFrequency ?? null, pendingFields: {autoReporting: null}, errorFields: {autoReporting: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingErrorMessage')}, }, @@ -506,7 +505,7 @@ function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReportingFrequency: policy.autoReportingFrequency ?? null, + autoReportingFrequency: policy?.autoReportingFrequency ?? null, pendingFields: {autoReportingFrequency: null}, errorFields: {autoReportingFrequency: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingFrequencyErrorMessage')}, }, @@ -547,7 +546,7 @@ function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingO onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReportingOffset: policy.autoReportingOffset ?? null, + autoReportingOffset: policy?.autoReportingOffset ?? null, pendingFields: {autoReportingOffset: null}, errorFields: {autoReportingOffset: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.monthlyOffsetErrorMessage')}, }, @@ -592,8 +591,8 @@ function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMo onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - approver: policy.approver ?? null, - approvalMode: policy.approvalMode ?? null, + approver: policy?.approver ?? null, + approvalMode: policy?.approvalMode ?? null, pendingFields: {approvalMode: null}, errorFields: {approvalMode: ErrorUtils.getMicroSecondOnyxError('workflowsApprovalPage.genericErrorMessage')}, }, @@ -652,7 +651,7 @@ function setWorkspacePayer(policyID: string, reimburserEmail: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, + achAccount: {reimburser: policy?.achAccount?.reimburser ?? null}, errorFields: {reimburser: ErrorUtils.getMicroSecondOnyxError('workflowsPayerPage.genericErrorMessage')}, pendingFields: {reimburser: null}, }, @@ -711,8 +710,8 @@ function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueO key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { isLoadingWorkspaceReimbursement: false, - reimbursementChoice: policy.reimbursementChoice ?? null, - achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, + reimbursementChoice: policy?.reimbursementChoice ?? null, + achAccount: {reimburser: policy?.achAccount?.reimburser ?? null}, errorFields: {reimbursementChoice: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, pendingFields: {reimbursementChoice: null}, }, @@ -800,9 +799,11 @@ function removeMembers(accountIDs: number[], policyID: string) { const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); const emailList = accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).filter((login) => !!login) as string[]; - const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); + const optimisticClosedReportActions = workspaceChats.map(() => + ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy?.name ?? '', CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY), + ); - const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policy.id, policy.name, accountIDs); + const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policy?.id ?? '', policy?.name ?? '', accountIDs); const optimisticMembersState: OnyxCollection = {}; const successMembersState: OnyxCollection = {}; @@ -849,7 +850,7 @@ function removeMembers(accountIDs: number[], policyID: string) { value: { statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, - oldPolicyName: policy.name, + oldPolicyName: policy?.name ?? '', pendingChatMembers, }, }); @@ -885,7 +886,7 @@ function removeMembers(accountIDs: number[], policyID: string) { const remainingLogins = employeeListEmails.filter((email) => !emailList.includes(email)); const invitedPrimaryToSecondaryLogins: Record = {}; - if (policy.primaryLoginsInvited) { + if (policy?.primaryLoginsInvited) { Object.keys(policy.primaryLoginsInvited).forEach((key) => (invitedPrimaryToSecondaryLogins[policy.primaryLoginsInvited?.[key] ?? ''] = key)); } @@ -3551,10 +3552,10 @@ function enablePolicyWorkflows(policyID: string, enabled: boolean) { areWorkflowsEnabled: !enabled, ...(!enabled ? { - approvalMode: policy.approvalMode, - autoReporting: policy.autoReporting, - harvesting: policy.harvesting, - reimbursementChoice: policy.reimbursementChoice, + approvalMode: policy?.approvalMode ?? null, + autoReporting: policy?.autoReporting ?? null, + harvesting: policy?.harvesting ?? null, + reimbursementChoice: policy?.reimbursementChoice ?? null, } : {}), pendingFields: { diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 7ce9bcf47c9c..3618918be489 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -36,7 +36,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetails, Report} from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type ProfilePageProps = StackScreenProps; @@ -44,7 +43,8 @@ type ProfilePageProps = StackScreenProps { +const getPhoneNumber = (details: OnyxEntry): string | undefined => { + const {login = '', displayName = ''} = details ?? {}; // If the user hasn't set a displayName, it is set to their phone number const parsedPhoneNumber = parsePhoneNumber(displayName); @@ -92,7 +92,7 @@ function ProfilePage({route}: ProfilePageProps) { const {translate, formatPhoneNumber} = useLocalize(); const accountID = Number(route.params?.accountID ?? 0); const isCurrentUser = session?.accountID === accountID; - const details: PersonalDetails | EmptyObject = personalDetails?.[accountID] ?? (ValidationUtils.isValidAccountRoute(accountID) ? {} : {accountID: 0}); + const details: OnyxEntry = personalDetails?.[accountID] ?? (ValidationUtils.isValidAccountRoute(accountID) ? null : {accountID: 0}); const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(details, undefined, undefined, isCurrentUser); const fallbackIcon = details?.fallbackIcon ?? ''; @@ -114,7 +114,7 @@ function ProfilePage({route}: ProfilePageProps) { const phoneNumber = getPhoneNumber(details); const phoneOrEmail = isSMSLogin ? getPhoneNumber(details) : login; - const hasAvatar = Boolean(details.avatar); + const hasAvatar = Boolean(details?.avatar); const isLoading = Boolean(personalDetailsMetadata?.[accountID]?.isLoading) || isEmptyObject(details); const statusEmojiCode = details?.status?.emojiCode ?? ''; @@ -158,7 +158,7 @@ function ProfilePage({route}: ProfilePageProps) { - + {isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : login} diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index c700fea4fb85..ea305830d770 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -29,7 +29,6 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import FloatingMessageCounter from './FloatingMessageCounter'; import getInitialNumToRender from './getInitialNumReportActionsToRender'; import ListBoundaryLoader from './ListBoundaryLoader'; @@ -607,7 +606,7 @@ function ReportActionsList({ [isLoadingNewerReportActions, styles.chatContentScrollView, styles.chatContentScrollViewWithHeaderLoader, canShowHeader], ); - const lastReportAction: OnyxTypes.ReportAction | EmptyObject = useMemo(() => sortedReportActions.at(-1) ?? {}, [sortedReportActions]); + const lastReportAction: OnyxTypes.ReportAction | null = useMemo(() => sortedReportActions.at(-1) ?? null, [sortedReportActions]); const retryLoadOlderChatsError = useCallback(() => { loadOlderChats(true); @@ -628,12 +627,12 @@ function ReportActionsList({ type={CONST.LIST_COMPONENTS.FOOTER} isLoadingOlderReportActions={isLoadingOlderReportActions} isLoadingInitialReportActions={isLoadingInitialReportActions} - lastReportActionName={lastReportAction.actionName} + lastReportActionName={lastReportAction?.actionName} hasError={hasLoadingOlderReportActionsError} onRetry={retryLoadOlderChatsError} /> ); - }, [isLoadingInitialReportActions, isLoadingOlderReportActions, lastReportAction.actionName, isOffline, hasLoadingOlderReportActionsError, retryLoadOlderChatsError]); + }, [isLoadingInitialReportActions, isLoadingOlderReportActions, lastReportAction?.actionName, isOffline, hasLoadingOlderReportActionsError, retryLoadOlderChatsError]); const onLayoutInner = useCallback( (event: LayoutChangeEvent) => { diff --git a/src/pages/home/report/ReportFooter.tsx b/src/pages/home/report/ReportFooter.tsx index ac56fe916bc9..39824b6622b6 100644 --- a/src/pages/home/report/ReportFooter.tsx +++ b/src/pages/home/report/ReportFooter.tsx @@ -21,7 +21,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import type {PendingAction} from '@src/types/onyx/OnyxCommon'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ReportActionCompose from './ReportActionCompose/ReportActionCompose'; import SystemChatReportFooterMessage from './SystemChatReportFooterMessage'; @@ -118,18 +117,18 @@ function ReportFooter({ const mention = match[1] ? match[1].trim() : undefined; const mentionWithDomain = ReportUtils.addDomainToShortMention(mention ?? '') ?? mention; - let assignee: OnyxTypes.PersonalDetails | EmptyObject = {}; + let assignee: OnyxEntry = null; let assigneeChatReport; if (mentionWithDomain) { - assignee = Object.values(allPersonalDetails).find((value) => value?.login === mentionWithDomain) ?? {}; - if (!Object.keys(assignee).length) { + assignee = Object.values(allPersonalDetails).find((value) => value?.login === mentionWithDomain) ?? null; + if (!Object.keys(assignee ?? {}).length) { const assigneeAccountID = UserUtils.generateAccountID(mentionWithDomain); const optimisticDataForNewAssignee = Task.setNewOptimisticAssignee(mentionWithDomain, assigneeAccountID); assignee = optimisticDataForNewAssignee.assignee; assigneeChatReport = optimisticDataForNewAssignee.assigneeReport; } } - Task.createTaskAndNavigate(report.reportID, title, '', assignee?.login ?? '', assignee.accountID, assigneeChatReport, report.policyID); + Task.createTaskAndNavigate(report.reportID, title, '', assignee?.login ?? '', assignee?.accountID, assigneeChatReport, report.policyID); return true; }, [allPersonalDetails, report.policyID, report.reportID], diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index d7333feb8650..7ae3eb472959 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -4,7 +4,6 @@ import type {AvatarSource} from '@libs/UserUtils'; import type CONST from '@src/CONST'; import type ONYXKEYS from '@src/ONYXKEYS'; import type CollectionDataSet from '@src/types/utils/CollectionDataSet'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import type * as OnyxCommon from './OnyxCommon'; import type {Decision, OriginalMessageModifiedExpense, OriginalMessageReportPreview, Reaction} from './OriginalMessage'; import type OriginalMessage from './OriginalMessage'; @@ -191,7 +190,7 @@ type ReportActionBase = OnyxCommon.OnyxValueWithOfflineFeedback<{ isFirstItem?: boolean; /** Informations about attachments of report action */ - attachmentInfo?: FileObject | EmptyObject; + attachmentInfo?: FileObject; /** Receipt tied to report action */ receipt?: Receipt; From beb4150076575e9fde7d4c928867670f8ce3dfec Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 03:08:39 +0700 Subject: [PATCH 008/136] remove EmptyObject in API --- src/libs/API/types.ts | 23 +++++++++++------------ src/libs/actions/MapboxToken.ts | 4 ++-- src/libs/actions/PaymentMethods.ts | 14 +++++--------- src/libs/actions/PersonalDetails.ts | 2 +- src/libs/actions/Session/index.ts | 2 +- src/libs/actions/Travel.ts | 2 +- src/libs/actions/User.ts | 2 +- src/libs/actions/Wallet.ts | 6 +++--- tests/unit/APITest.ts | 2 +- 9 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 40deec85bc47..6a623246ad75 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -1,6 +1,5 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import type * as Parameters from './parameters'; import type SignInUserParams from './parameters/SignInUserParams'; import type UpdateBeneficialOwnersForBankAccountParams from './parameters/UpdateBeneficialOwnersForBankAccountParams'; @@ -253,7 +252,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_AUTOMATIC_TIMEZONE]: Parameters.UpdateAutomaticTimezoneParams; [WRITE_COMMANDS.UPDATE_SELECTED_TIMEZONE]: Parameters.UpdateSelectedTimezoneParams; [WRITE_COMMANDS.UPDATE_USER_AVATAR]: Parameters.UpdateUserAvatarParams; - [WRITE_COMMANDS.DELETE_USER_AVATAR]: EmptyObject; + [WRITE_COMMANDS.DELETE_USER_AVATAR]: null; [WRITE_COMMANDS.REFER_TEACHERS_UNITE_VOLUNTEER]: Parameters.ReferTeachersUniteVolunteerParams; [WRITE_COMMANDS.ADD_SCHOOL_PRINCIPAL]: Parameters.AddSchoolPrincipalParams; [WRITE_COMMANDS.CLOSE_ACCOUNT]: Parameters.CloseAccountParams; @@ -269,7 +268,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SET_CONTACT_METHOD_AS_DEFAULT]: Parameters.SetContactMethodAsDefaultParams; [WRITE_COMMANDS.UPDATE_THEME]: Parameters.UpdateThemeParams; [WRITE_COMMANDS.UPDATE_STATUS]: Parameters.UpdateStatusParams; - [WRITE_COMMANDS.CLEAR_STATUS]: EmptyObject; + [WRITE_COMMANDS.CLEAR_STATUS]: null; [WRITE_COMMANDS.UPDATE_PERSONAL_DETAILS_FOR_WALLET]: Parameters.UpdatePersonalDetailsForWalletParams; [WRITE_COMMANDS.VERIFY_IDENTITY]: Parameters.VerifyIdentityParams; [WRITE_COMMANDS.ACCEPT_WALLET_TERMS]: Parameters.AcceptWalletTermsParams; @@ -284,8 +283,8 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SIGN_IN_USER_WITH_LINK]: Parameters.SignInUserWithLinkParams; [WRITE_COMMANDS.REQUEST_UNLINK_VALIDATION_LINK]: Parameters.RequestUnlinkValidationLinkParams; [WRITE_COMMANDS.UNLINK_LOGIN]: Parameters.UnlinkLoginParams; - [WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH]: EmptyObject; - [WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH]: EmptyObject; + [WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH]: null; + [WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH]: null; [WRITE_COMMANDS.TWO_FACTOR_AUTH_VALIDATE]: Parameters.ValidateTwoFactorAuthParams; [WRITE_COMMANDS.ADD_COMMENT]: Parameters.AddCommentOrAttachementParams; [WRITE_COMMANDS.ADD_ATTACHMENT]: Parameters.AddCommentOrAttachementParams; @@ -433,7 +432,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.CATEGORIZE_TRACKED_EXPENSE]: Parameters.CategorizeTrackedExpenseParams; [WRITE_COMMANDS.SHARE_TRACKED_EXPENSE]: Parameters.ShareTrackedExpenseParams; [WRITE_COMMANDS.LEAVE_POLICY]: Parameters.LeavePolicyParams; - [WRITE_COMMANDS.ACCEPT_SPOTNANA_TERMS]: EmptyObject; + [WRITE_COMMANDS.ACCEPT_SPOTNANA_TERMS]: null; [WRITE_COMMANDS.SEND_INVOICE]: Parameters.SendInvoiceParams; [WRITE_COMMANDS.MARK_AS_CASH]: Parameters.MarkAsCashParams; }; @@ -492,9 +491,9 @@ type ReadCommandParameters = { [READ_COMMANDS.SYNC_POLICY_TO_XERO]: Parameters.SyncPolicyToXeroParams; [READ_COMMANDS.OPEN_REIMBURSEMENT_ACCOUNT_PAGE]: Parameters.OpenReimbursementAccountPageParams; [READ_COMMANDS.OPEN_WORKSPACE_VIEW]: Parameters.OpenWorkspaceViewParams; - [READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN]: EmptyObject; - [READ_COMMANDS.OPEN_PAYMENTS_PAGE]: EmptyObject; - [READ_COMMANDS.OPEN_PERSONAL_DETAILS]: EmptyObject; + [READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN]: null; + [READ_COMMANDS.OPEN_PAYMENTS_PAGE]: null; + [READ_COMMANDS.OPEN_PERSONAL_DETAILS]: null; [READ_COMMANDS.OPEN_PUBLIC_PROFILE_PAGE]: Parameters.OpenPublicProfilePageParams; [READ_COMMANDS.OPEN_PLAID_BANK_LOGIN]: Parameters.OpenPlaidBankLoginParams; [READ_COMMANDS.OPEN_PLAID_BANK_ACCOUNT_SELECTOR]: Parameters.OpenPlaidBankAccountSelectorParams; @@ -509,9 +508,9 @@ type ReadCommandParameters = { [READ_COMMANDS.GET_ROUTE]: Parameters.GetRouteParams; [READ_COMMANDS.GET_ROUTE_FOR_DRAFT]: Parameters.GetRouteParams; [READ_COMMANDS.GET_STATEMENT_PDF]: Parameters.GetStatementPDFParams; - [READ_COMMANDS.OPEN_ONFIDO_FLOW]: EmptyObject; - [READ_COMMANDS.OPEN_INITIAL_SETTINGS_PAGE]: EmptyObject; - [READ_COMMANDS.OPEN_ENABLE_PAYMENTS_PAGE]: EmptyObject; + [READ_COMMANDS.OPEN_ONFIDO_FLOW]: null; + [READ_COMMANDS.OPEN_INITIAL_SETTINGS_PAGE]: null; + [READ_COMMANDS.OPEN_ENABLE_PAYMENTS_PAGE]: null; [READ_COMMANDS.BEGIN_SIGNIN]: Parameters.BeginSignInParams; [READ_COMMANDS.SIGN_IN_WITH_SHORT_LIVED_AUTH_TOKEN]: Parameters.SignInWithShortLivedAuthTokenParams; [READ_COMMANDS.SIGN_IN_WITH_SUPPORT_AUTH_TOKEN]: Parameters.SignInWithSupportAuthTokenParams; diff --git a/src/libs/actions/MapboxToken.ts b/src/libs/actions/MapboxToken.ts index 3b98f79698ba..db3999c6f75d 100644 --- a/src/libs/actions/MapboxToken.ts +++ b/src/libs/actions/MapboxToken.ts @@ -39,7 +39,7 @@ const setExpirationTimer = () => { return; } console.debug(`[MapboxToken] Fetching a new token after waiting ${REFRESH_INTERVAL / 1000 / 60} minutes`); - API.read(READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN, {}, {}); + API.read(READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN, null, {}); }, REFRESH_INTERVAL); }; @@ -52,7 +52,7 @@ const clearToken = () => { }; const fetchToken = () => { - API.read(READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN, {}, {}); + API.read(READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN, null, {}); isCurrentlyFetchingToken = true; }; diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index c5a74bdc6ace..a33a36d575c5 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -63,15 +63,11 @@ function openWalletPage() { }, ]; - return API.read( - READ_COMMANDS.OPEN_PAYMENTS_PAGE, - {}, - { - optimisticData, - successData, - failureData, - }, - ); + return API.read(READ_COMMANDS.OPEN_PAYMENTS_PAGE, null, { + optimisticData, + successData, + failureData, + }); } function getMakeDefaultPaymentOnyxData( diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index b9cea5c9447c..e93ba560dc64 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -397,7 +397,7 @@ function deleteAvatar() { }, ]; - API.write(WRITE_COMMANDS.DELETE_USER_AVATAR, {}, {optimisticData, failureData}); + API.write(WRITE_COMMANDS.DELETE_USER_AVATAR, null, {optimisticData, failureData}); } /** diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 303517558206..7174199e9301 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -859,7 +859,7 @@ function toggleTwoFactorAuth(enable: boolean) { }, ]; - API.write(enable ? WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH : WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH, {}, {optimisticData, successData, failureData}); + API.write(enable ? WRITE_COMMANDS.ENABLE_TWO_FACTOR_AUTH : WRITE_COMMANDS.DISABLE_TWO_FACTOR_AUTH, null, {optimisticData, successData, failureData}); } function validateTwoFactorAuth(twoFactorAuthCode: string) { diff --git a/src/libs/actions/Travel.ts b/src/libs/actions/Travel.ts index 02affae0fe67..f13a978d4e74 100644 --- a/src/libs/actions/Travel.ts +++ b/src/libs/actions/Travel.ts @@ -19,7 +19,7 @@ function acceptSpotnanaTerms() { }, ]; - API.write(WRITE_COMMANDS.ACCEPT_SPOTNANA_TERMS, {}, {successData}); + API.write(WRITE_COMMANDS.ACCEPT_SPOTNANA_TERMS, null, {successData}); } // eslint-disable-next-line import/prefer-default-export diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 3212aff71122..a68f20557dae 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -951,7 +951,7 @@ function clearCustomStatus() { }, }, ]; - API.write(WRITE_COMMANDS.CLEAR_STATUS, {}, {optimisticData}); + API.write(WRITE_COMMANDS.CLEAR_STATUS, null, {optimisticData}); } /** diff --git a/src/libs/actions/Wallet.ts b/src/libs/actions/Wallet.ts index 045cc34f39ef..19d44c2406fe 100644 --- a/src/libs/actions/Wallet.ts +++ b/src/libs/actions/Wallet.ts @@ -49,7 +49,7 @@ function openOnfidoFlow() { }, ]; - API.read(READ_COMMANDS.OPEN_ONFIDO_FLOW, {}, {optimisticData, finallyData}); + API.read(READ_COMMANDS.OPEN_ONFIDO_FLOW, null, {optimisticData, finallyData}); } function setAdditionalDetailsQuestions(questions: WalletAdditionalQuestionDetails[] | null, idNumber?: string) { @@ -212,14 +212,14 @@ function acceptWalletTerms(parameters: AcceptWalletTermsParams) { * Fetches data when the user opens the InitialSettingsPage */ function openInitialSettingsPage() { - API.read(READ_COMMANDS.OPEN_INITIAL_SETTINGS_PAGE, {}); + API.read(READ_COMMANDS.OPEN_INITIAL_SETTINGS_PAGE, null); } /** * Fetches data when the user opens the EnablePaymentsPage */ function openEnablePaymentsPage() { - API.read(READ_COMMANDS.OPEN_ENABLE_PAYMENTS_PAGE, {}); + API.read(READ_COMMANDS.OPEN_ENABLE_PAYMENTS_PAGE, null); } function updateCurrentStep(currentStep: ValueOf) { diff --git a/tests/unit/APITest.ts b/tests/unit/APITest.ts index 07633b19802b..d2498c16c16a 100644 --- a/tests/unit/APITest.ts +++ b/tests/unit/APITest.ts @@ -538,7 +538,7 @@ describe('APITests', () => { // WHEN we make a request that should be retried, one that should not, and another that should API.write('MockCommandOne' as WriteCommand, {}); - API.read('MockCommandTwo' as ReadCommand, {}); + API.read('MockCommandTwo' as ReadCommand, null); API.write('MockCommandThree' as WriteCommand, {}); // THEN the retryable requests should immediately be added to the persisted requests From 7a2d0d4e2de37c6127a7751f8c098aa08fe73a88 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 03:41:13 +0700 Subject: [PATCH 009/136] remove EmptyObject in IOU --- src/libs/actions/IOU.ts | 169 +++++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 82 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d657ba27f170..84d95d675755 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -52,13 +52,12 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant, Split} from '@src/types/onyx/IOU'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; -import type {IOUMessage, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type {IOUMessage, OriginalMessageReportPreview, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {ReportPreviewAction} from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, ReceiptSource, Routes, SplitShares, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CachedPDFPaths from './CachedPDFPaths'; import * as Category from './Policy/Category'; @@ -237,11 +236,11 @@ Onyx.connect({ }, }); -let currentUserPersonalDetails: OnyxTypes.PersonalDetails | EmptyObject = {}; +let currentUserPersonalDetails: OnyxEntry; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { - currentUserPersonalDetails = value?.[userAccountID] ?? {}; + currentUserPersonalDetails = value?.[userAccountID] ?? null; }, }); @@ -293,7 +292,10 @@ function getReportPreviewAction(chatReportID: string, iouReportID: string): Onyx // Find the report preview action from the chat report return ( Object.values(reportActions).find( - (reportAction) => reportAction && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && reportAction.originalMessage.linkedReportID === iouReportID, + (reportAction) => + reportAction && + reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && + (reportAction as ReportAction & OriginalMessageReportPreview).originalMessage.linkedReportID === iouReportID, ) ?? null ); } @@ -332,13 +334,13 @@ function initMoneyRequest(reportID: string, policy: OnyxEntry, amount: 0, comment, created, - currency: currentUserPersonalDetails.localCurrencyCode ?? CONST.CURRENCY.USD, + currency: currentUserPersonalDetails?.localCurrencyCode ?? CONST.CURRENCY.USD, iouRequestType, reportID, transactionID: newTransactionID, isFromGlobalCreate, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, - splitPayerAccountIDs: [currentUserPersonalDetails.accountID], + splitPayerAccountIDs: currentUserPersonalDetails ? [currentUserPersonalDetails.accountID] : undefined, }); } @@ -461,7 +463,7 @@ function buildOnyxDataForMoneyRequest( optimisticPolicyRecentlyUsedCategories: string[], optimisticPolicyRecentlyUsedTags: OnyxTypes.RecentlyUsedTags, isNewChatReport: boolean, - transactionThreadReport: OptimisticChatReport | EmptyObject, + transactionThreadReport: OptimisticChatReport | null, transactionThreadCreatedReportAction: OptimisticCreatedReportAction | null, shouldCreateNewMoneyRequestReport: boolean, policy?: OnyxEntry, @@ -550,7 +552,7 @@ function buildOnyxDataForMoneyRequest( }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.reportID}`, value: { ...transactionThreadReport, pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, @@ -567,7 +569,7 @@ function buildOnyxDataForMoneyRequest( if (!isEmptyObject(transactionThreadCreatedReportAction)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: transactionThreadCreatedReportAction, }, @@ -659,7 +661,7 @@ function buildOnyxDataForMoneyRequest( }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.reportID}`, value: { participants: redundantParticipants, pendingFields: null, @@ -716,7 +718,7 @@ function buildOnyxDataForMoneyRequest( if (!isEmptyObject(transactionThreadCreatedReportAction)) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: { pendingAction: null, @@ -758,7 +760,7 @@ function buildOnyxDataForMoneyRequest( }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.reportID}`, value: { pendingFields: null, errorFields: existingTransactionThreadReport @@ -808,7 +810,7 @@ function buildOnyxDataForMoneyRequest( if (!isEmptyObject(transactionThreadCreatedReportAction)) { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), @@ -1158,7 +1160,7 @@ function buildOnyxDataForTrackExpense( iouCreatedAction: OptimisticCreatedReportAction, iouAction: OptimisticIOUReportAction, reportPreviewAction: OnyxEntry, - transactionThreadReport: OptimisticChatReport | EmptyObject, + transactionThreadReport: OptimisticChatReport | null, transactionThreadCreatedReportAction: OptimisticCreatedReportAction | null, shouldCreateNewMoneyRequestReport: boolean, policy?: OnyxEntry, @@ -1293,7 +1295,7 @@ function buildOnyxDataForTrackExpense( }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.reportID}`, value: { ...transactionThreadReport, pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, @@ -1310,7 +1312,7 @@ function buildOnyxDataForTrackExpense( if (!isEmptyObject(transactionThreadCreatedReportAction)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: transactionThreadCreatedReportAction, }, @@ -1370,7 +1372,7 @@ function buildOnyxDataForTrackExpense( successData.push( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.reportID}`, value: { pendingFields: null, errorFields: null, @@ -1389,7 +1391,7 @@ function buildOnyxDataForTrackExpense( if (!isEmptyObject(transactionThreadCreatedReportAction)) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`, value: { [transactionThreadCreatedReportAction.reportActionID]: { pendingAction: null, @@ -1468,7 +1470,7 @@ function buildOnyxDataForTrackExpense( }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.reportID}`, value: { pendingFields: null, errorFields: existingTransactionThreadReport @@ -1491,7 +1493,7 @@ function buildOnyxDataForTrackExpense( }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`, value: { [transactionThreadCreatedReportAction?.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), @@ -1838,7 +1840,7 @@ function getSendInvoiceInformation( * it creates optimistic versions of them and uses those instead */ function getMoneyRequestInformation( - parentChatReport: OnyxEntry | EmptyObject, + parentChatReport: OnyxEntry, participant: Participant, comment: string, amount: number, @@ -2053,7 +2055,7 @@ function getMoneyRequestInformation( * it creates optimistic versions of them and uses those instead */ function getTrackExpenseInformation( - parentChatReport: OnyxEntry | EmptyObject, + parentChatReport: OnyxEntry, participant: Participant, comment: string, amount: number, @@ -2074,7 +2076,7 @@ function getTrackExpenseInformation( moneyRequestReportID = '', linkedTrackedExpenseReportAction?: OnyxTypes.ReportAction, existingTransactionID?: string, -): TrackExpenseInformation | EmptyObject { +): TrackExpenseInformation | null { const optimisticData: OnyxUpdate[] = []; const successData: OnyxUpdate[] = []; const failureData: OnyxUpdate[] = []; @@ -2092,7 +2094,7 @@ function getTrackExpenseInformation( // If we still don't have a report, it likely doesn't exist, and we will early return here as it should not happen // Maybe later, we can build an optimistic selfDM chat. if (!chatReport) { - return {}; + return null; } // Check if the report is a draft @@ -3217,7 +3219,7 @@ function categorizeTrackedExpense( linkedTrackedExpenseReportID: string, transactionThreadReportID: string, reportPreviewReportActionID: string, - onyxData: OnyxData, + onyxData: OnyxData | undefined, amount: number, currency: string, comment: string, @@ -3231,7 +3233,7 @@ function categorizeTrackedExpense( receipt?: Receipt, createdWorkspaceParams?: CreateWorkspaceParams, ) { - const {optimisticData, successData, failureData} = onyxData; + const {optimisticData, successData, failureData} = onyxData ?? {}; const { optimisticData: moveTransactionOptimisticData, @@ -3294,7 +3296,7 @@ function shareTrackedExpense( linkedTrackedExpenseReportID: string, transactionThreadReportID: string, reportPreviewReportActionID: string, - onyxData: OnyxData, + onyxData: OnyxData | undefined, amount: number, currency: string, comment: string, @@ -3308,7 +3310,7 @@ function shareTrackedExpense( receipt?: Receipt, createdWorkspaceParams?: CreateWorkspaceParams, ) { - const {optimisticData, successData, failureData} = onyxData; + const {optimisticData, successData, failureData} = onyxData ?? {}; const { optimisticData: moveTransactionOptimisticData, @@ -3409,7 +3411,7 @@ function requestMoney( createdReportActionIDForThread, onyxData, } = getMoneyRequestInformation( - isMovingTransactionFromTrackExpense ? {} : currentChatReport, + isMovingTransactionFromTrackExpense ? null : currentChatReport, participant, comment, amount, @@ -3598,30 +3600,31 @@ function trackExpense( createdReportActionIDForThread, actionableWhisperReportActionIDParam, onyxData, - } = getTrackExpenseInformation( - currentChatReport, - participant, - comment, - amount, - currency, - currentCreated, - merchant, - receipt, - category, - tag, - taxCode, - taxAmount, - billable, - policy, - policyTagList, - policyCategories, - payeeEmail, - payeeAccountID, - moneyRequestReportID, - linkedTrackedExpenseReportAction, - isMovingTransactionFromTrackExpense ? (linkedTrackedExpenseReportAction?.originalMessage as IOUMessage)?.IOUTransactionID : undefined, - ); - const activeReportID = isMoneyRequestReport ? report.reportID : chatReport.reportID; + } = + getTrackExpenseInformation( + currentChatReport, + participant, + comment, + amount, + currency, + currentCreated, + merchant, + receipt, + category, + tag, + taxCode, + taxAmount, + billable, + policy, + policyTagList, + policyCategories, + payeeEmail, + payeeAccountID, + moneyRequestReportID, + linkedTrackedExpenseReportAction, + isMovingTransactionFromTrackExpense ? (linkedTrackedExpenseReportAction?.originalMessage as IOUMessage)?.IOUTransactionID : undefined, + ) ?? {}; + const activeReportID = isMoneyRequestReport ? report.reportID : chatReport?.reportID; switch (action) { case CONST.IOU.ACTION.CATEGORIZE: { @@ -3629,15 +3632,15 @@ function trackExpense( return; } categorizeTrackedExpense( - chatReport.policyID ?? '', - transaction.transactionID, - iouAction.reportActionID, + chatReport?.policyID ?? '', + transaction?.transactionID ?? '', + iouAction?.reportActionID ?? '', iouReport?.reportID ?? '', createdIOUReportActionID ?? '', actionableWhisperReportActionID, linkedTrackedExpenseReportAction, linkedTrackedExpenseReportID, - transactionThreadReportID, + transactionThreadReportID ?? '', reportPreviewAction?.reportActionID ?? '', onyxData, amount, @@ -3660,15 +3663,15 @@ function trackExpense( return; } shareTrackedExpense( - chatReport.policyID ?? '', - transaction.transactionID, - iouAction.reportActionID, + chatReport?.policyID ?? '', + transaction?.transactionID ?? '', + iouAction?.reportActionID ?? '', iouReport?.reportID ?? '', createdIOUReportActionID ?? '', actionableWhisperReportActionID, linkedTrackedExpenseReportAction, linkedTrackedExpenseReportID, - transactionThreadReportID, + transactionThreadReportID ?? '', reportPreviewAction?.reportActionID ?? '', onyxData, amount, @@ -3694,10 +3697,10 @@ function trackExpense( created: currentCreated, merchant, iouReportID: iouReport?.reportID, - chatReportID: chatReport.reportID, - transactionID: transaction.transactionID, - reportActionID: iouAction.reportActionID, - createdChatReportActionID, + chatReportID: chatReport?.reportID ?? '', + transactionID: transaction?.transactionID ?? '', + reportActionID: iouAction?.reportActionID ?? '', + createdChatReportActionID: createdChatReportActionID ?? '', createdIOUReportActionID, reportPreviewReportActionID: reportPreviewAction?.reportActionID, receipt, @@ -3709,8 +3712,8 @@ function trackExpense( billable, // This needs to be a string of JSON because of limitations with the fetch() API and nested objects receiptGpsPoints: gpsPoints ? JSON.stringify(gpsPoints) : undefined, - transactionThreadReportID, - createdReportActionIDForThread, + transactionThreadReportID: transactionThreadReportID ?? '', + createdReportActionIDForThread: createdReportActionIDForThread ?? '', waypoints: validWaypoints ? JSON.stringify(validWaypoints) : undefined, }; if (actionableWhisperReportActionIDParam) { @@ -3725,7 +3728,7 @@ function trackExpense( Navigation.navigate(ROUTES.ROOM_INVITE.getRoute(activeReportID ?? '', CONST.IOU.SHARE.ROLE.ACCOUNTANT)); } - Report.notifyNewAction(activeReportID, payeeAccountID); + Report.notifyNewAction(activeReportID ?? '', payeeAccountID); } function getOrCreateOptimisticSplitChatReport(existingSplitChatReportID: string, participants: Participant[], participantAccountIDs: number[], currentUserAccountID: number) { @@ -4722,7 +4725,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA // In case this is still the optimistic accountID saved in the splits array, return early as we cannot know // if there is an existing chat between the split creator and this participant // Instead, we will rely on Auth generating the report IDs and the user won't see any optimistic chats or reports created - const participantPersonalDetails: OnyxTypes.PersonalDetails | EmptyObject = allPersonalDetails[participant?.accountID ?? -1] ?? {}; + const participantPersonalDetails: OnyxEntry = allPersonalDetails[participant?.accountID ?? -1]; if (!participantPersonalDetails || participantPersonalDetails.isOptimisticPersonalDetail) { splits.push({ email: participant.email, @@ -4731,7 +4734,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA } } - let oneOnOneChatReport: OnyxTypes.Report | null; + let oneOnOneChatReport: OnyxEntry; let isNewOneOnOneChatReport = false; if (isPolicyExpenseChat) { // The workspace chat reportID is saved in the splits array when starting a split expense with a workspace @@ -5244,10 +5247,12 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor iouReportLastMessageText.length === 0 && !ReportActionsUtils.isDeletedParentAction(lastVisibleAction) && (!transactionThreadID || shouldDeleteTransactionThread); // STEP 4: Update the iouReport and reportPreview with new totals and messages if it wasn't deleted - let updatedIOUReport: OnyxTypes.Report | null; + let updatedIOUReport: OnyxEntry; const currency = TransactionUtils.getCurrency(transaction); - const updatedReportPreviewAction: OnyxTypes.ReportAction | EmptyObject = {...reportPreviewAction}; - updatedReportPreviewAction.pendingAction = shouldDeleteIOUReport ? CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE : CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; + const updatedReportPreviewAction: OnyxEntry = reportPreviewAction; + if (updatedReportPreviewAction) { + updatedReportPreviewAction.pendingAction = shouldDeleteIOUReport ? CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE : CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; + } if (iouReport && ReportUtils.isExpenseReport(iouReport)) { updatedIOUReport = {...iouReport}; @@ -5341,7 +5346,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, ); - if (!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0) { + if (!shouldDeleteIOUReport && updatedReportPreviewAction?.childMoneyRequestCount === 0) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, @@ -5470,7 +5475,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }); } - if (!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0) { + if (!shouldDeleteIOUReport && updatedReportPreviewAction?.childMoneyRequestCount === 0) { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, @@ -5527,7 +5532,7 @@ function deleteTrackExpense(chatReportID: string, transactionID: string, reportA * @param recipient - The user receiving the money */ function getSendMoneyParams( - report: OnyxEntry | EmptyObject, + report: OnyxEntry, amount: number, currency: string, comment: string, @@ -5653,7 +5658,7 @@ function getSendMoneyParams( const successData: OnyxUpdate[] = []; // Add optimistic personal details for recipient - let optimisticPersonalDetailListData: OnyxUpdate | EmptyObject = {}; + let optimisticPersonalDetailListData: OnyxUpdate | null = null; const optimisticPersonalDetailListAction = isNewChat ? { [recipientAccountID]: { @@ -6063,7 +6068,7 @@ function canApproveIOU(iouReport: OnyxEntry, chatReport: OnyxE return isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled && !isArchivedReport; } -function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry, policy: OnyxEntry) { +function canIOUBePaid(iouReport: OnyxEntry, chatReport: OnyxEntry, policy: OnyxEntry) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); const isChatReportArchived = ReportUtils.isArchivedRoom(chatReport); @@ -6251,9 +6256,9 @@ function submitReport(expenseReport: OnyxTypes.Report) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; const parentReport = ReportUtils.getReport(expenseReport.parentReportID); const policy = PolicyUtils.getPolicy(expenseReport.policyID); - const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID; + const isCurrentUserManager = currentUserPersonalDetails?.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); - const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails.accountID : undefined; + const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails?.accountID : undefined; const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED); @@ -6564,7 +6569,7 @@ function replaceReceipt(transactionID: string, file: File, source: string) { function setMoneyRequestParticipantsFromReport(transactionID: string, report: OnyxEntry): Participant[] { // If the report is iou or expense report, we should get the chat report to set participant for request money const chatReport = ReportUtils.isMoneyRequestReport(report) ? ReportUtils.getReport(report?.chatReportID) : report; - const currentUserAccountID = currentUserPersonalDetails.accountID; + const currentUserAccountID = currentUserPersonalDetails?.accountID; const shouldAddAsReport = !isEmptyObject(chatReport) && ReportUtils.isSelfDM(chatReport); let participants: Participant[] = []; From decf834db265600098262da07c6263d22e351680 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 03:51:20 +0700 Subject: [PATCH 010/136] remove Record --- src/components/PopoverProvider/types.ts | 3 ++- src/hooks/useKeyboardShortcut.ts | 3 ++- src/libs/ModifiedExpenseMessage.ts | 2 +- src/libs/ReportActionsUtils.ts | 2 +- src/libs/actions/Task.ts | 16 ++++++++-------- .../migrations/CheckForPreviousReportActionID.ts | 3 ++- .../withReportAndReportActionOrNotFound.tsx | 4 ++-- src/types/onyx/OriginalMessage.ts | 3 ++- 8 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index 5022aee0f843..c532905a3a4f 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -1,6 +1,7 @@ import type {ReactNode, RefObject} from 'react'; // eslint-disable-next-line no-restricted-imports import type {Text, View} from 'react-native'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; type PopoverContextProps = { children: ReactNode; @@ -8,7 +9,7 @@ type PopoverContextProps = { type PopoverContextValue = { onOpen?: (popoverParams: AnchorRef) => void; - popover?: AnchorRef | Record | null; + popover?: AnchorRef | EmptyObject | null; close: (anchorRef?: RefObject) => void; isOpen: boolean; }; diff --git a/src/hooks/useKeyboardShortcut.ts b/src/hooks/useKeyboardShortcut.ts index 6bf8b2c52bc3..7339aa0b9e1f 100644 --- a/src/hooks/useKeyboardShortcut.ts +++ b/src/hooks/useKeyboardShortcut.ts @@ -3,6 +3,7 @@ import type {GestureResponderEvent} from 'react-native'; import type {ValueOf} from 'type-fest'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import CONST from '@src/CONST'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; type Shortcut = ValueOf; type KeyboardShortcutConfig = { @@ -26,7 +27,7 @@ type KeyboardShortcutConfig = { * Register a keyboard shortcut handler. * Recommendation: To ensure stability, wrap the `callback` function with the useCallback hook before using it with this hook. */ -export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: GestureResponderEvent | KeyboardEvent) => void, config: KeyboardShortcutConfig | Record = {}) { +export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: GestureResponderEvent | KeyboardEvent) => void, config: KeyboardShortcutConfig | EmptyObject = {}) { const { captureOnInputs = true, shouldBubble = false, diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 2df75030ac19..1991c4549f84 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -105,7 +105,7 @@ function getForDistanceRequest(newDistance: string, oldDistance: string, newAmou * ModifiedExpense::getNewDotComment in Web-Expensify should match this. * If we change this function be sure to update the backend as well. */ -function getForReportAction(reportID: string | undefined, reportAction: OnyxEntry | ReportAction | Record): string { +function getForReportAction(reportID: string | undefined, reportAction: OnyxEntry): string { if (reportAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE) { return ''; } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 3fabc2983c4e..4c0b89a82a6a 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -139,7 +139,7 @@ function isReportActionSubmitted(reportAction: OnyxEntry): boolean return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.SUBMITTED; } -function isModifiedExpenseAction(reportAction: OnyxEntry | ReportAction | Record): boolean { +function isModifiedExpenseAction(reportAction: OnyxEntry): boolean { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE; } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index f13ae0303d37..ef258bc153a9 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -802,12 +802,12 @@ function getShareDestination(reportID: string, reports: OnyxCollection): ReportAction | Record { +function getParentReportAction(report: OnyxEntry): OnyxEntry { // If the report is not a thread report, then it won't have a parent and an empty object can be returned. if (!report?.parentReportID || !report.parentReportActionID) { - return {}; + return null; } - return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`]?.[report.parentReportActionID] ?? {}; + return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`]?.[report.parentReportActionID] ?? null; } /** @@ -837,7 +837,7 @@ function deleteTask(report: OnyxEntry) { const shouldDeleteTaskReport = !ReportActionsUtils.doesReportHaveVisibleActions(report.reportID ?? ''); const optimisticReportAction: Partial = { pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - previousMessage: parentReportAction.message, + previousMessage: parentReportAction?.message, message: [ { translationKey: '', @@ -852,7 +852,7 @@ function deleteTask(report: OnyxEntry) { linkMetadata: [], }; const optimisticReportActions = { - [parentReportAction.reportActionID]: optimisticReportAction, + [parentReportAction?.reportActionID ?? '']: optimisticReportAction, }; const optimisticData: OnyxUpdate[] = [ @@ -918,7 +918,7 @@ function deleteTask(report: OnyxEntry) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReport?.reportID}`, value: { - [parentReportAction.reportActionID]: { + [parentReportAction?.reportActionID ?? '']: { pendingAction: null, }, }, @@ -945,7 +945,7 @@ function deleteTask(report: OnyxEntry) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReport?.reportID}`, value: { - [parentReportAction.reportActionID]: { + [parentReportAction?.reportActionID ?? '']: { pendingAction: null, }, }, @@ -986,7 +986,7 @@ function getTaskAssigneeAccountID(taskReport: OnyxEntry): numb } const reportAction = getParentReportAction(taskReport); - return reportAction.childManagerAccountID; + return reportAction?.childManagerAccountID; } /** diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index 7e4bbe9ffb3e..7a2a294af859 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -3,6 +3,7 @@ import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; function getReportActionsFromOnyx(): Promise> { return new Promise((resolve) => { @@ -60,6 +61,6 @@ export default function (): Promise { onyxData[onyxKey] = {}; }); - return Onyx.multiSet(onyxData as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}`, Record>); + return Onyx.multiSet(onyxData as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}`, EmptyObject>); }); } diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx index e5e203fb5030..3b6dca6cf8cf 100644 --- a/src/pages/home/report/withReportAndReportActionOrNotFound.tsx +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.tsx @@ -53,11 +53,11 @@ export default function > { function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { const getReportAction = useCallback(() => { - let reportAction: OnyxTypes.ReportAction | Record | undefined = props.reportActions?.[`${props.route.params.reportActionID}`]; + let reportAction: OnyxEntry = props.reportActions?.[`${props.route.params.reportActionID}`] ?? null; // Handle threads if needed if (!reportAction?.reportActionID) { - reportAction = props?.parentReportAction ?? {}; + reportAction = props?.parentReportAction ?? null; } return reportAction; diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index b079a64ebb4b..5d8a7ad113a7 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -1,6 +1,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; type PaymentMethodType = DeepValueOf; @@ -331,7 +332,7 @@ type OriginalMessageMoved = { type OriginalMessageMergedWithCashTransaction = { actionName: typeof CONST.REPORT.ACTIONS.TYPE.MERGED_WITH_CASH_TRANSACTION; - originalMessage: Record; // No data is sent with this action + originalMessage: EmptyObject; // No data is sent with this action }; type OriginalMessageDismissedViolation = { From bbc5694ba41f722dc309547f729f99559f6d8d03 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 31 May 2024 04:14:39 +0700 Subject: [PATCH 011/136] fix receipt type --- .../CategorizeTrackedExpenseParams.ts | 2 +- .../parameters/ShareTrackedExpenseParams.ts | 2 +- src/libs/API/parameters/TrackExpenseParams.ts | 2 +- src/libs/TransactionUtils.ts | 2 +- src/libs/actions/IOU.ts | 32 +++++++++---------- .../step/IOURequestStepConfirmation.tsx | 4 +-- src/types/onyx/Transaction.ts | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts b/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts index 78eb0adecc5e..12a5075477f5 100644 --- a/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts +++ b/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts @@ -16,7 +16,7 @@ type CategorizeTrackedExpenseParams = { reportPreviewReportActionID: string; category?: string; tag?: string; - receipt?: Receipt; + receipt: Receipt | null; taxCode: string; taxAmount: number; billable?: boolean; diff --git a/src/libs/API/parameters/ShareTrackedExpenseParams.ts b/src/libs/API/parameters/ShareTrackedExpenseParams.ts index cee4bc40d9ac..c1f07d496baa 100644 --- a/src/libs/API/parameters/ShareTrackedExpenseParams.ts +++ b/src/libs/API/parameters/ShareTrackedExpenseParams.ts @@ -16,7 +16,7 @@ type ShareTrackedExpenseParams = { reportPreviewReportActionID: string; category?: string; tag?: string; - receipt?: Receipt; + receipt: Receipt | null; taxCode: string; taxAmount: number; billable?: boolean; diff --git a/src/libs/API/parameters/TrackExpenseParams.ts b/src/libs/API/parameters/TrackExpenseParams.ts index d5e1de2e625b..458cc8b7976f 100644 --- a/src/libs/API/parameters/TrackExpenseParams.ts +++ b/src/libs/API/parameters/TrackExpenseParams.ts @@ -15,7 +15,7 @@ type TrackExpenseParams = { createdChatReportActionID: string; createdIOUReportActionID?: string; reportPreviewReportActionID?: string; - receipt?: Receipt; + receipt: Receipt | null; receiptState?: ValueOf; category?: string; tag?: string; diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 79e73f1585d2..414011303eec 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -97,7 +97,7 @@ function buildOptimisticTransaction( source = '', originalTransactionID = '', merchant = '', - receipt: Receipt = {}, + receipt: OnyxEntry = null, filename = '', existingTransactionID: string | null = null, category = '', diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 84d95d675755..26945be3e940 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 { +function getReceiptError(receipt: OnyxEntry, filename?: string, isScanRequest = true, errorKey?: number): Errors | ErrorFields { return isEmptyObject(receipt) || !isScanRequest ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage', false, errorKey) : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source?.toString() ?? '', filename: filename ?? ''}, errorKey); @@ -776,7 +776,7 @@ function buildOnyxDataForMoneyRequest( value: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest, errorKey), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, isScanRequest, errorKey), pendingAction: null, pendingFields: clearedPendingFields, }, @@ -790,7 +790,7 @@ function buildOnyxDataForMoneyRequest( [iouCreatedAction.reportActionID]: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest, errorKey), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, isScanRequest, errorKey), }, [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), @@ -800,7 +800,7 @@ function buildOnyxDataForMoneyRequest( [iouAction.reportActionID]: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest, errorKey), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, isScanRequest, errorKey), }, }), }, @@ -1115,7 +1115,7 @@ function buildOnyxDataForInvoice( [iouCreatedAction.reportActionID]: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, false, errorKey), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, false, errorKey), }, [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateInvoiceFailureMessage'), @@ -1428,7 +1428,7 @@ function buildOnyxDataForTrackExpense( [iouCreatedAction.reportActionID]: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, isScanRequest), }, [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), @@ -1438,7 +1438,7 @@ function buildOnyxDataForTrackExpense( [iouAction.reportActionID]: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, isScanRequest), }, }), }, @@ -1452,7 +1452,7 @@ function buildOnyxDataForTrackExpense( [iouAction.reportActionID]: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, isScanRequest), }, }, }); @@ -1486,7 +1486,7 @@ function buildOnyxDataForTrackExpense( value: { // Disabling this line since transaction.filename can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), + errors: getReceiptError(transaction.receipt ?? null, transaction.filename || transaction.receipt?.filename, isScanRequest), pendingAction: null, pendingFields: clearedPendingFields, }, @@ -1706,7 +1706,7 @@ function getSendInvoiceInformation( transaction: OnyxEntry, currentUserAccountID: number, invoiceChatReport?: OnyxEntry, - receipt?: Receipt, + receipt: OnyxEntry = null, policy?: OnyxEntry, policyTagList?: OnyxEntry, policyCategories?: OnyxEntry, @@ -2062,7 +2062,7 @@ function getTrackExpenseInformation( currency: string, created: string, merchant: string, - receipt: Receipt | undefined, + receipt: OnyxEntry, category: string | undefined, tag: string | undefined, taxCode: string | undefined, @@ -3230,7 +3230,7 @@ function categorizeTrackedExpense( taxCode = '', taxAmount = 0, billable?: boolean, - receipt?: Receipt, + receipt: Receipt | null = null, createdWorkspaceParams?: CreateWorkspaceParams, ) { const {optimisticData, successData, failureData} = onyxData ?? {}; @@ -3307,7 +3307,7 @@ function shareTrackedExpense( taxCode = '', taxAmount = 0, billable?: boolean, - receipt?: Receipt, + receipt: Receipt | null = null, createdWorkspaceParams?: CreateWorkspaceParams, ) { const {optimisticData, successData, failureData} = onyxData ?? {}; @@ -3509,13 +3509,13 @@ function sendInvoice( currentUserAccountID: number, transaction: OnyxEntry, invoiceChatReport?: OnyxEntry, - receiptFile?: Receipt, + receiptFile?: Receipt | null, policy?: OnyxEntry, policyTagList?: OnyxEntry, policyCategories?: OnyxEntry, ) { const {senderWorkspaceID, receiver, invoiceRoom, createdChatReportActionID, invoiceReportID, reportPreviewReportActionID, transactionID, transactionThreadReportID, onyxData} = - getSendInvoiceInformation(transaction, currentUserAccountID, invoiceChatReport, receiptFile, policy, policyTagList, policyCategories); + getSendInvoiceInformation(transaction, currentUserAccountID, invoiceChatReport, receiptFile ?? null, policy, policyTagList, policyCategories); let parameters: SendInvoiceParams = { senderWorkspaceID, @@ -3565,7 +3565,7 @@ function trackExpense( payeeAccountID: number, participant: Participant, comment: string, - receipt?: Receipt, + receipt: Receipt | null = null, category?: string, tag?: string, taxCode = '', diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index b4eb9f1082ed..73a3080dbe53 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -80,7 +80,7 @@ function IOURequestStepConfirmation({ const {translate} = useLocalize(); const {windowWidth} = useWindowDimensions(); const {isOffline} = useNetwork(); - const [receiptFile, setReceiptFile] = useState(); + const [receiptFile, setReceiptFile] = useState>(); const receiptFilename = transaction?.filename; const receiptPath = transaction?.receipt?.source; @@ -254,7 +254,7 @@ function IOURequestStepConfirmation({ ); const trackExpense = useCallback( - (selectedParticipants: Participant[], trimmedComment: string, receiptObj?: Receipt, gpsPoints?: IOU.GpsPoint) => { + (selectedParticipants: Participant[], trimmedComment: string, receiptObj?: OnyxEntry, gpsPoints?: IOU.GpsPoint) => { if (!report || !transaction) { return; } diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 460bd279048b..b504f1e2eabd 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -183,7 +183,7 @@ type Transaction = OnyxCommon.OnyxValueWithOfflineFeedback< participants?: Participant[]; /** The receipt object associated with the transaction */ - receipt?: Receipt; + receipt?: Receipt | null; /** The iouReportID associated with the transaction */ reportID: string; From 6c09eb70483c2b8c3c89e2d29ace8095817a4476 Mon Sep 17 00:00:00 2001 From: James Dean Date: Fri, 31 May 2024 08:51:38 -0700 Subject: [PATCH 012/136] Update en.ts minor copy updates based on the convo here: https://expensify.slack.com/archives/C03U7DCU4/p1717106264594009 --- src/languages/en.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 8d1b24211583..85800e899821 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1253,7 +1253,7 @@ export default { }, priorityModePage: { priorityMode: 'Priority mode', - explainerText: 'Choose whether to show all chats by default sorted with most recent with pinned items at the top, or #focus on unread pinned items, sorted alphabetically.', + explainerText: 'Choose whether to #focus on unread and pinned chats only, or show everything with the most recent and pinned chats at the top.', priorityModes: { default: { label: 'Most recent', From 233aa5fcf4a4b97b839bb62674e2f2a66028c88a Mon Sep 17 00:00:00 2001 From: James Dean Date: Tue, 4 Jun 2024 09:36:46 -0700 Subject: [PATCH 013/136] Update es.ts --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 29f85edfc93e..4c02590d702d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1252,7 +1252,7 @@ export default { priorityModePage: { priorityMode: 'Modo prioridad', explainerText: - 'Elige si deseas mostrar por defecto todos los chats ordenados desde el más reciente y con los elementos anclados en la parte superior, o elige el modo #concentración, con los elementos no leídos anclados en la parte superior y ordenados alfabéticamente.', + 'Elige #concentración si deseas enfocarte sólo en los chats no leídos y en los anclados, o mostrarlo todo con los chats más recientes y los anclados en la parte superior.', priorityModes: { default: { label: 'Más recientes', From 1aff409fa45bd06300d8a973924b43b5fa964057 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 6 Jun 2024 17:27:31 +0700 Subject: [PATCH 014/136] remove EmptyObject in PopoverProvider and useKeyboardShortcut --- src/components/PopoverProvider/index.native.tsx | 4 ++-- src/components/PopoverProvider/index.tsx | 2 +- src/components/PopoverProvider/types.ts | 3 +-- src/hooks/useKeyboardShortcut.ts | 5 ++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/PopoverProvider/index.native.tsx b/src/components/PopoverProvider/index.native.tsx index b13909945bef..d58322fafe63 100644 --- a/src/components/PopoverProvider/index.native.tsx +++ b/src/components/PopoverProvider/index.native.tsx @@ -3,7 +3,7 @@ import type {PopoverContextProps, PopoverContextValue} from './types'; const PopoverContext = React.createContext({ onOpen: () => {}, - popover: {}, + popover: null, close: () => {}, isOpen: false, }); @@ -13,7 +13,7 @@ function PopoverContextProvider(props: PopoverContextProps) { () => ({ onOpen: () => {}, close: () => {}, - popover: {}, + popover: null, isOpen: false, }), [], diff --git a/src/components/PopoverProvider/index.tsx b/src/components/PopoverProvider/index.tsx index cc6c84477525..82f3c6c7d61a 100644 --- a/src/components/PopoverProvider/index.tsx +++ b/src/components/PopoverProvider/index.tsx @@ -6,7 +6,7 @@ import type {AnchorRef, PopoverContextProps, PopoverContextValue} from './types' const PopoverContext = createContext({ onOpen: () => {}, - popover: {}, + popover: null, close: () => {}, isOpen: false, }); diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index c532905a3a4f..b3d21e9ed5d9 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -1,7 +1,6 @@ import type {ReactNode, RefObject} from 'react'; // eslint-disable-next-line no-restricted-imports import type {Text, View} from 'react-native'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; type PopoverContextProps = { children: ReactNode; @@ -9,7 +8,7 @@ type PopoverContextProps = { type PopoverContextValue = { onOpen?: (popoverParams: AnchorRef) => void; - popover?: AnchorRef | EmptyObject | null; + popover?: AnchorRef | null; close: (anchorRef?: RefObject) => void; isOpen: boolean; }; diff --git a/src/hooks/useKeyboardShortcut.ts b/src/hooks/useKeyboardShortcut.ts index 7339aa0b9e1f..87a16fcbfa5d 100644 --- a/src/hooks/useKeyboardShortcut.ts +++ b/src/hooks/useKeyboardShortcut.ts @@ -3,7 +3,6 @@ import type {GestureResponderEvent} from 'react-native'; import type {ValueOf} from 'type-fest'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import CONST from '@src/CONST'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; type Shortcut = ValueOf; type KeyboardShortcutConfig = { @@ -27,7 +26,7 @@ type KeyboardShortcutConfig = { * Register a keyboard shortcut handler. * Recommendation: To ensure stability, wrap the `callback` function with the useCallback hook before using it with this hook. */ -export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: GestureResponderEvent | KeyboardEvent) => void, config: KeyboardShortcutConfig | EmptyObject = {}) { +export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: GestureResponderEvent | KeyboardEvent) => void, config?: KeyboardShortcutConfig) { const { captureOnInputs = true, shouldBubble = false, @@ -41,7 +40,7 @@ export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: G // This flag is used to prevent auto submit form when press enter key on selection modal. shouldStopPropagation = false, - } = config; + } = config ?? {}; useEffect(() => { if (!isActive) { From 7fadca4c3ac81122778c4e535764ea7ceec806de Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 6 Jun 2024 17:36:33 +0700 Subject: [PATCH 015/136] remove EmptyObject in Report actions --- src/libs/actions/Report.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8b27bcaa290f..51175e38efd2 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -745,7 +745,7 @@ function openReport( reportID: string, reportActionID?: string, participantLoginList: string[] = [], - newReportObject: Partial = {}, + newReportObject: ReportUtils.OptimisticChatReport | null = null, parentReportActionID = '0', isFromDeepLink = false, participantAccountIDList: number[] = [], @@ -821,8 +821,8 @@ function openReport( if (ReportUtils.isGroupChat(newReportObject)) { parameters.chatType = CONST.REPORT.CHAT_TYPE.GROUP; parameters.groupChatAdminLogins = currentUserEmail; - parameters.optimisticAccountIDList = Object.keys(newReportObject.participants ?? {}).join(','); - parameters.reportName = newReportObject.reportName ?? ''; + parameters.optimisticAccountIDList = Object.keys(newReportObject?.participants ?? {}).join(','); + parameters.reportName = newReportObject?.reportName ?? ''; // If we have an avatar then include it with the parameters if (avatar) { @@ -990,7 +990,7 @@ function navigateToAndOpenReport( const report = isEmptyObject(chat) ? newChat : chat; // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server - openReport(report?.reportID ?? '', '', userLogins, newChat ?? {}, undefined, undefined, undefined, avatarFile); + openReport(report?.reportID ?? '', '', userLogins, newChat, undefined, undefined, undefined, avatarFile); if (shouldDismissModal) { Navigation.dismissModalWithReport(report); } else { @@ -1013,7 +1013,7 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) const report = chat ?? newChat; // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server - openReport(report?.reportID ?? '', '', [], newChat ?? {}, '0', false, participantAccountIDs); + openReport(report?.reportID ?? '', '', [], newChat, '0', false, participantAccountIDs); Navigation.dismissModalWithReport(report); } @@ -2487,7 +2487,7 @@ function openReportFromDeepLink(url: string, shouldNavigate = true) { if (reportID && !isAuthenticated) { // Call the OpenReport command to check in the server if it's a public room. If so, we'll open it as an anonymous user - openReport(reportID, '', [], {}, '0', true); + openReport(reportID, '', [], null, '0', true); // Show the sign-in page if the app is offline if (networkStatus === CONST.NETWORK.NETWORK_STATUS.OFFLINE) { From 66e25d3720c5aa284ccb0b1cd20cd6fb78619d36 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 6 Jun 2024 19:56:39 +0700 Subject: [PATCH 016/136] resolve minor comments --- src/hooks/useKeyboardShortcut.ts | 4 ++-- src/libs/OptionsListUtils.ts | 2 +- src/libs/actions/IOU.ts | 7 ++----- src/libs/actions/Policy/Policy.ts | 12 ++++++------ .../migrations/CheckForPreviousReportActionID.ts | 4 ++-- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/hooks/useKeyboardShortcut.ts b/src/hooks/useKeyboardShortcut.ts index 87a16fcbfa5d..1c5bbc426ef2 100644 --- a/src/hooks/useKeyboardShortcut.ts +++ b/src/hooks/useKeyboardShortcut.ts @@ -26,7 +26,7 @@ type KeyboardShortcutConfig = { * Register a keyboard shortcut handler. * Recommendation: To ensure stability, wrap the `callback` function with the useCallback hook before using it with this hook. */ -export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: GestureResponderEvent | KeyboardEvent) => void, config?: KeyboardShortcutConfig) { +export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: GestureResponderEvent | KeyboardEvent) => void, config: KeyboardShortcutConfig = {}) { const { captureOnInputs = true, shouldBubble = false, @@ -40,7 +40,7 @@ export default function useKeyboardShortcut(shortcut: Shortcut, callback: (e?: G // This flag is used to prevent auto submit form when press enter key on selection modal. shouldStopPropagation = false, - } = config ?? {}; + } = config; useEffect(() => { if (!isActive) { diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 95822ebf083c..7379c64a2fcb 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -2103,7 +2103,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: OnyxEn ], descriptiveText: amountText ?? '', login: personalDetail?.login ?? '', - accountID: personalDetail?.accountID ?? 0, + accountID: personalDetail?.accountID ?? -1, keyForList: String(personalDetail?.accountID), }; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 23ae79c50cac..0e1c29cbe423 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -52,7 +52,7 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant, Split} from '@src/types/onyx/IOU'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; -import type {IOUMessage, OriginalMessageReportPreview, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type {IOUMessage, PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {ReportPreviewAction} from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; @@ -292,10 +292,7 @@ function getReportPreviewAction(chatReportID: string, iouReportID: string): Onyx // Find the report preview action from the chat report return ( Object.values(reportActions).find( - (reportAction) => - reportAction && - reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && - (reportAction as ReportAction & OriginalMessageReportPreview).originalMessage.linkedReportID === iouReportID, + (reportAction) => reportAction && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && reportAction.originalMessage.linkedReportID === iouReportID, ) ?? null ); } diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index da3cad3933b9..86ccf4881940 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -583,8 +583,8 @@ function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMo onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - approver: policy?.approver ?? null, - approvalMode: policy?.approvalMode ?? null, + approver: policy?.approver, + approvalMode: policy?.approvalMode, pendingFields: {approvalMode: null}, errorFields: {approvalMode: ErrorUtils.getMicroSecondOnyxError('workflowsApprovalPage.genericErrorMessage')}, }, @@ -3470,10 +3470,10 @@ function enablePolicyWorkflows(policyID: string, enabled: boolean) { areWorkflowsEnabled: !enabled, ...(!enabled ? { - approvalMode: policy?.approvalMode ?? null, - autoReporting: policy?.autoReporting ?? null, - harvesting: policy?.harvesting ?? null, - reimbursementChoice: policy?.reimbursementChoice ?? null, + approvalMode: policy?.approvalMode, + autoReporting: policy?.autoReporting, + harvesting: policy?.harvesting, + reimbursementChoice: policy?.reimbursementChoice, } : {}), pendingFields: { diff --git a/src/libs/migrations/CheckForPreviousReportActionID.ts b/src/libs/migrations/CheckForPreviousReportActionID.ts index 7a2a294af859..83658ff961c0 100644 --- a/src/libs/migrations/CheckForPreviousReportActionID.ts +++ b/src/libs/migrations/CheckForPreviousReportActionID.ts @@ -3,7 +3,7 @@ import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import type {ReportActionsCollectionDataSet} from '@src/types/onyx/ReportAction'; function getReportActionsFromOnyx(): Promise> { return new Promise((resolve) => { @@ -61,6 +61,6 @@ export default function (): Promise { onyxData[onyxKey] = {}; }); - return Onyx.multiSet(onyxData as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}`, EmptyObject>); + return Onyx.multiSet(onyxData as ReportActionsCollectionDataSet); }); } From 8230dbe8fb8d3a596f8237616c7a768bbc3acb9f Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 6 Jun 2024 20:26:38 +0700 Subject: [PATCH 017/136] fix lint --- src/libs/PolicyUtils.ts | 8 ++++---- src/libs/TransactionUtils.ts | 2 +- src/pages/iou/request/step/IOURequestStepDistanceRate.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 6af994ffaafb..0af41a28e349 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -7,7 +7,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, PolicyCategories, PolicyEmployeeList, PolicyTagList, PolicyTags, TaxRate} from '@src/types/onyx'; -import type {PolicyFeatureName, Rate, Tenant} from '@src/types/onyx/Policy'; +import type {CustomUnit, PolicyFeatureName, Rate, Tenant} from '@src/types/onyx/Policy'; import type PolicyEmployee from '@src/types/onyx/PolicyEmployee'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Navigation from './Navigation/Navigation'; @@ -92,16 +92,16 @@ function getNumericValue(value: number | string, toLocaleDigit: (arg: string) => /** * Retrieves the distance custom unit object for the given policy */ -function getCustomUnit(policy: OnyxEntry) { +function getCustomUnit(policy: OnyxEntry): CustomUnit | undefined { return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); } /** * Retrieves custom unit rate object from the given customUnitRateID */ -function getCustomUnitRate(policy: OnyxEntry | EmptyObject, customUnitRateID: string): Rate | EmptyObject { +function getCustomUnitRate(policy: OnyxEntry, customUnitRateID: string): Rate | undefined { const distanceUnit = getCustomUnit(policy); - return distanceUnit?.rates[customUnitRateID] ?? {}; + return distanceUnit?.rates[customUnitRateID]; } function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => string): string { diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 5150c4c7cfb6..694d3cb000cb 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -699,7 +699,7 @@ function getRateID(transaction: OnyxEntry): string | undefined { * If it is distance request, then returns the tax code corresponding to the custom unit rate * Else returns policy default tax rate if transaction is in policy default currency, otherwise foreign default tax rate */ -function getDefaultTaxCode(policy: OnyxEntry, transaction: OnyxEntry, currency?: string | undefined) { +function getDefaultTaxCode(policy: OnyxEntry, transaction: OnyxEntry, currency?: string | undefined): string | undefined { if (isDistanceRequest(transaction)) { const customUnitRateID = getRateID(transaction) ?? ''; const customUnitRate = getCustomUnitRate(policy, customUnitRateID); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index f8879a3f4250..e584f6611941 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -78,7 +78,7 @@ function IOURequestStepDistanceRate({ function selectDistanceRate(customUnitRateID: string) { if (shouldShowTax) { const policyCustomUnitRate = getCustomUnitRate(policy, customUnitRateID); - const taxRateExternalID = policyCustomUnitRate.attributes?.taxRateExternalID ?? ''; + const taxRateExternalID = policyCustomUnitRate?.attributes?.taxRateExternalID ?? ''; const taxableAmount = DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, TransactionUtils.getDistance(transaction)); const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? ''; const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount)); From 8e8919be9b70dd2680275b57f77c2200a4b5cd74 Mon Sep 17 00:00:00 2001 From: Florent De Neve Date: Tue, 11 Jun 2024 16:45:23 +0200 Subject: [PATCH 018/136] Remove policy.submitsTo as it does not exist anymore --- src/types/onyx/Policy.ts | 3 --- tests/utils/LHNTestUtils.tsx | 1 - tests/utils/collections/policies.ts | 1 - 3 files changed, 5 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index ad8575a81829..fa2209672b53 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -786,9 +786,6 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback< /** When the monthly scheduled submit should happen */ autoReportingOffset?: AutoReportingOffset; - /** The accountID of manager who the employee submits their expenses to on paid policies */ - submitsTo?: number; - /** The employee list of the policy */ employeeList?: OnyxTypes.PolicyEmployeeList; diff --git a/tests/utils/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx index 89c31d92843e..925ce6030c09 100644 --- a/tests/utils/LHNTestUtils.tsx +++ b/tests/utils/LHNTestUtils.tsx @@ -236,7 +236,6 @@ function getFakePolicy(id = '1', name = 'Workspace-Test-001'): Policy { }, autoReportingOffset: 1, preventSelfApproval: true, - submitsTo: 123456, defaultBillable: false, disabledFields: {defaultBillable: true, reimbursable: false}, approvalMode: 'BASIC', diff --git a/tests/utils/collections/policies.ts b/tests/utils/collections/policies.ts index 5507c9e75436..d34a2f6474b5 100644 --- a/tests/utils/collections/policies.ts +++ b/tests/utils/collections/policies.ts @@ -15,7 +15,6 @@ export default function createRandomPolicy(index: number): Policy { }, autoReportingOffset: 1, preventSelfApproval: randBoolean(), - submitsTo: index, outputCurrency: randCurrencyCode(), role: rand(Object.values(CONST.POLICY.ROLE)), owner: randEmail(), From 61859a628652201881635cdcc890cc8d86b71cea Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Wed, 12 Jun 2024 16:24:47 +0300 Subject: [PATCH 019/136] Add billing currency --- src/CONST.ts | 1 + src/ONYXKEYS.ts | 3 + src/ROUTES.ts | 2 + src/SCREENS.ts | 2 + .../PaymentCardChangeCurrencyForm.tsx | 113 ++++++++++++++++++ .../AddPaymentCard/PaymentCardForm.tsx | 33 ++--- src/languages/en.ts | 11 ++ src/languages/es.ts | 11 ++ .../parameters/UpdateBillingCurrencyParams.ts | 9 ++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + .../ModalStackNavigators/index.tsx | 3 + .../CENTRAL_PANE_TO_RHP_MAPPING.ts | 7 +- .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 8 ++ src/libs/Navigation/types.ts | 1 + src/libs/actions/PaymentMethods.ts | 70 ++++++++++- .../PaymentCard/ChangeCurrency/index.tsx | 44 +++++++ .../CardSection/CardSectionActions/index.tsx | 4 +- .../CardSectionDataEmpty/index.tsx | 10 +- .../ChangeBillingCurrency/index.tsx | 45 +++++++ src/types/form/AddDebitCardForm.ts | 3 + src/types/form/ChangeBillingCurrencyForm.ts | 20 ++++ src/types/form/index.ts | 1 + src/types/onyx/Fund.ts | 3 +- 25 files changed, 379 insertions(+), 29 deletions(-) create mode 100644 src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx create mode 100644 src/libs/API/parameters/UpdateBillingCurrencyParams.ts create mode 100644 src/pages/settings/PaymentCard/ChangeCurrency/index.tsx create mode 100644 src/pages/settings/Subscription/PaymentCard/ChangeBillingCurrency/index.tsx create mode 100644 src/types/form/ChangeBillingCurrencyForm.ts diff --git a/src/CONST.ts b/src/CONST.ts index b0e3ab8c3af4..c26d9cbf21de 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -590,6 +590,7 @@ const CONST = { ONFIDO_TERMS_OF_SERVICE_URL: 'https://onfido.com/terms-of-service/', LIST_OF_RESTRICTED_BUSINESSES: 'https://community.expensify.com/discussion/6191/list-of-restricted-businesses', TRAVEL_TERMS_URL: `${USE_EXPENSIFY_URL}/travelterms`, + PRICING: `https://www.expensify.com/pricing`, // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0d22d3714fe6..084a1fbccd07 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -450,6 +450,8 @@ const ONYXKEYS = { SETTINGS_STATUS_SET_CLEAR_AFTER_FORM_DRAFT: 'settingsStatusSetClearAfterFormDraft', SETTINGS_STATUS_CLEAR_DATE_FORM: 'settingsStatusClearDateForm', SETTINGS_STATUS_CLEAR_DATE_FORM_DRAFT: 'settingsStatusClearDateFormDraft', + CHANGE_BILLING_CURRENCY_FORM: 'changeBillingCurrencyForm', + CHANGE_BILLING_CURRENCY_FORM_DRAFT: 'changeBillingCurrencyFormDraft', PRIVATE_NOTES_FORM: 'privateNotesForm', PRIVATE_NOTES_FORM_DRAFT: 'privateNotesFormDraft', I_KNOW_A_TEACHER_FORM: 'iKnowTeacherForm', @@ -524,6 +526,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.WAYPOINT_FORM]: FormTypes.WaypointForm; [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM]: FormTypes.SettingsStatusSetForm; [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM]: FormTypes.SettingsStatusClearDateForm; + [ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM]: FormTypes.ChangeBillingCurrencyForm; [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM]: FormTypes.SettingsStatusSetClearAfterForm; [ONYXKEYS.FORMS.PRIVATE_NOTES_FORM]: FormTypes.PrivateNotesForm; [ONYXKEYS.FORMS.I_KNOW_A_TEACHER_FORM]: FormTypes.IKnowTeacherForm; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ed20d388bb87..85a320ac2e06 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -95,6 +95,7 @@ const ROUTES = { WORKSPACE_SWITCHER: 'workspace-switcher', SETTINGS: 'settings', SETTINGS_PROFILE: 'settings/profile', + SETTINGS_CHANGE_CURRENCY: 'settings/add-payment-card/change-currency', SETTINGS_SHARE_CODE: 'settings/shareCode', SETTINGS_DISPLAY_NAME: 'settings/profile/display-name', SETTINGS_TIMEZONE: 'settings/profile/timezone', @@ -104,6 +105,7 @@ const ROUTES = { SETTINGS_SUBSCRIPTION: 'settings/subscription', SETTINGS_SUBSCRIPTION_SIZE: 'settings/subscription/subscription-size', SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD: 'settings/subscription/add-payment-card', + SETTINGS_SUBSCRIPTION_CHANGE_BILLING_CURRENCY: 'settings/subscription/change-billing-currency', SETTINGS_SUBSCRIPTION_DISABLE_AUTO_RENEW_SURVEY: 'settings/subscription/disable-auto-renew-survey', SETTINGS_PRIORITY_MODE: 'settings/preferences/priority-mode', SETTINGS_LANGUAGE: 'settings/preferences/language', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 9f7277a0ad0f..a8e2023f5cd9 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -41,6 +41,7 @@ const SCREENS = { SAVE_THE_WORLD: 'Settings_TeachersUnite', APP_DOWNLOAD_LINKS: 'Settings_App_Download_Links', ADD_DEBIT_CARD: 'Settings_Add_Debit_Card', + ADD_PAYMENT_CARD_CHANGE_CURRENCY: 'Settings_Add_Payment_Card_Change_Currency', ADD_BANK_ACCOUNT: 'Settings_Add_Bank_Account', ADD_BANK_ACCOUNT_REFACTOR: 'Settings_Add_Bank_Account_Refactor', CLOSE: 'Settings_Close', @@ -109,6 +110,7 @@ const SCREENS = { SIZE: 'Settings_Subscription_Size', ADD_PAYMENT_CARD: 'Settings_Subscription_Add_Payment_Card', DISABLE_AUTO_RENEW_SURVEY: 'Settings_Subscription_DisableAutoRenewSurvey', + CHANGE_BILLING_CURRENCY: 'Settings_Subscription_Change_Billing_Currency', }, }, SAVE_THE_WORLD: { diff --git a/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx b/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx new file mode 100644 index 000000000000..d0c291225802 --- /dev/null +++ b/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx @@ -0,0 +1,113 @@ +import React, {useCallback, useState} from 'react'; +import type {ValueOf} from 'type-fest'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import Hoverable from '@components/Hoverable'; +import * as Expensicons from '@components/Icon/Expensicons'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import TextLink from '@components/TextLink'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import INPUT_IDS from '@src/types/form/ChangeBillingCurrencyForm'; +import PaymentCardCurrencyModal from './PaymentCardCurrencyModal'; + +type PaymentCardFormProps = { + initialCurrency?: ValueOf; + isSecurityCodeRequired?: boolean; + changeBillingCurrency: (currency?: ValueOf, values?: FormOnyxValues) => void; +}; + +const REQUIRED_FIELDS = [INPUT_IDS.SECURITY_CODE]; + +function PaymentCardChangeCurrencyForm({changeBillingCurrency, isSecurityCodeRequired, initialCurrency}: PaymentCardFormProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const [isCurrencyModalVisible, setIsCurrencyModalVisible] = useState(false); + const [currency, setCurrency] = useState>(initialCurrency ?? CONST.CURRENCY.USD); + + const validate = (formValues: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(formValues, REQUIRED_FIELDS); + + if (formValues.securityCode && !ValidationUtils.isValidSecurityCode(formValues.securityCode)) { + errors.securityCode = 'billingCurrency.error.securityCode'; + } + + return errors; + }; + + const showCurrenciesModal = useCallback(() => { + setIsCurrencyModalVisible(true); + }, []); + + const changeCurrency = useCallback((newCurrency: keyof typeof CONST.CURRENCY) => { + setCurrency(newCurrency); + setIsCurrencyModalVisible(false); + }, []); + + return ( + changeBillingCurrency(currency, formData)} + submitButtonText={translate('common.save')} + scrollContextEnabled + style={[styles.mh5, styles.flexGrow1]} + > + + {(isHovered) => ( + + )} + + + {!!isSecurityCodeRequired && ( + + )} + + } + currentCurrency={currency} + onCurrencyChange={changeCurrency} + onClose={() => setIsCurrencyModalVisible(false)} + /> + + {`${translate('billingCurrency.note')}`}{' '} + {`${translate('billingCurrency.noteLink')}`}{' '} + {`${translate('billingCurrency.notDetails')}`} + + + ); +} + +PaymentCardChangeCurrencyForm.displayName = 'PaymentCardChangeCurrencyForm'; + +export default PaymentCardChangeCurrencyForm; diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 61e9a2d1860a..5fcf3c2dd6cf 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -1,7 +1,8 @@ import {useRoute} from '@react-navigation/native'; -import React, {useCallback, useRef, useState} from 'react'; +import React, {useCallback, useRef} from 'react'; import type {ReactNode} from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AddressSearch from '@components/AddressSearch'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; @@ -18,13 +19,13 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ValidationUtils from '@libs/ValidationUtils'; +import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/AddDebitCardForm'; -import PaymentCardCurrencyModal from './PaymentCardCurrencyModal'; type PaymentCardFormProps = { shouldShowPaymentCardForm?: boolean; @@ -129,15 +130,14 @@ function PaymentCardForm({ headerContent, }: PaymentCardFormProps) { const styles = useThemeStyles(); + const [data] = useOnyx(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM); + const {translate} = useLocalize(); const route = useRoute(); const label = CARD_LABELS[isDebitCard ? CARD_TYPES.DEBIT_CARD : CARD_TYPES.PAYMENT_CARD]; const cardNumberRef = useRef(null); - const [isCurrencyModalVisible, setIsCurrencyModalVisible] = useState(false); - const [currency, setCurrency] = useState(CONST.CURRENCY.USD); - const validate = (formValues: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(formValues, REQUIRED_FIELDS); @@ -172,13 +172,8 @@ function PaymentCardForm({ return errors; }; - const showCurrenciesModal = useCallback(() => { - setIsCurrencyModalVisible(true); - }, []); - - const changeCurrency = useCallback((newCurrency: keyof typeof CONST.CURRENCY) => { - setCurrency(newCurrency); - setIsCurrencyModalVisible(false); + const openCurrenciesSelectScreen = useCallback(() => { + Navigation.navigate(ROUTES.SETTINGS_CHANGE_CURRENCY); }, []); if (!shouldShowPaymentCardForm) { @@ -191,7 +186,7 @@ function PaymentCardForm({ addPaymentCard(formData, currency)} + onSubmit={addPaymentCard} submitButtonText={submitButtonText} scrollContextEnabled style={[styles.mh5, styles.flexGrow1]} @@ -279,8 +274,8 @@ function PaymentCardForm({ aria-label={translate('common.currency')} role={CONST.ROLE.COMBOBOX} icon={Expensicons.ArrowRight} - onPress={showCurrenciesModal} - value={currency} + onPress={openCurrenciesSelectScreen} + value={data?.currency ?? CONST.CURRENCY.USD} containerStyles={[styles.mt5]} inputStyle={isHovered && styles.cursorPointer} hideFocusedState @@ -302,14 +297,6 @@ function PaymentCardForm({ /> )} - - } - currentCurrency={currency} - onCurrencyChange={changeCurrency} - onClose={() => setIsCurrencyModalVisible(false)} - /> {footerContent} diff --git a/src/languages/en.ts b/src/languages/en.ts index 76e4d5d5a143..5d104eebb189 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1051,6 +1051,17 @@ export default { genericFailureMessage: "Private notes couldn't be saved.", }, }, + billingCurrency: { + error: { + securityCode: 'Please enter a valid security code.', + }, + securityCode: 'Security code', + changePaymentCurrency: 'Change payment currency', + paymentCurrency: 'Payment currency', + note: 'Note: Changing your payment currency can impact how much you’ll pay for Expensify. Refer to our', + noteLink: 'pricing page', + notDetails: 'for full details.', + }, addDebitCardPage: { addADebitCard: 'Add a debit card', nameOnCard: 'Name on card', diff --git a/src/languages/es.ts b/src/languages/es.ts index d03e13d1a9ff..69b7d6c78b29 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1048,6 +1048,17 @@ export default { genericFailureMessage: 'Las notas privadas no han podido ser guardadas.', }, }, + billingCurrency: { + error: { + securityCode: 'Por favor, introduce un código de seguridad válido.', + }, + securityCode: 'Código de seguridad', + changePaymentCurrency: 'Cambiar la moneda de pago', + paymentCurrency: 'Moneda de pago', + note: 'Nota: Cambiar tu moneda de pago puede afectar cuánto pagarás por Expensify. Consulta nuestra', + noteLink: 'página de precios', + notDetails: 'para conocer todos los detalles.', + }, addDebitCardPage: { addADebitCard: 'Añadir una tarjeta de débito', nameOnCard: 'Nombre en la tarjeta', diff --git a/src/libs/API/parameters/UpdateBillingCurrencyParams.ts b/src/libs/API/parameters/UpdateBillingCurrencyParams.ts new file mode 100644 index 000000000000..957b377a2e95 --- /dev/null +++ b/src/libs/API/parameters/UpdateBillingCurrencyParams.ts @@ -0,0 +1,9 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type UpdateBillingCurrencyParams = { + currency: ValueOf; + cardCVV: string; +}; + +export default UpdateBillingCurrencyParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index b853f134b315..17d73538f58d 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -97,6 +97,7 @@ export type {default as UpdateRoomVisibilityParams} from './UpdateRoomVisibility export type {default as UpdateReportWriteCapabilityParams} from './UpdateReportWriteCapabilityParams'; export type {default as AddWorkspaceRoomParams} from './AddWorkspaceRoomParams'; export type {default as UpdatePolicyRoomNameParams} from './UpdatePolicyRoomNameParams'; +export type {default as UpdateBillingCurrencyParams} from './UpdateBillingCurrencyParams'; export type {default as AddEmojiReactionParams} from './AddEmojiReactionParams'; export type {default as RemoveEmojiReactionParams} from './RemoveEmojiReactionParams'; export type {default as LeaveRoomParams} from './LeaveRoomParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 6b34e04e1937..8eafacda915c 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -159,6 +159,7 @@ const WRITE_COMMANDS = { UPDATE_MONEY_REQUEST_DESCRIPTION: 'UpdateMoneyRequestDescription', UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY: 'UpdateMoneyRequestAmountAndCurrency', HOLD_MONEY_REQUEST: 'HoldRequest', + UPDATE_BILLING_CARD_CURRENCY: 'UpdateBillingCardCurrency', UNHOLD_MONEY_REQUEST: 'UnHoldRequest', UPDATE_DISTANCE_REQUEST: 'UpdateDistanceRequest', REQUEST_MONEY: 'RequestMoney', @@ -435,6 +436,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_ENABLED]: Parameters.SetPolicyDistanceRatesEnabledParams; [WRITE_COMMANDS.DELETE_POLICY_DISTANCE_RATES]: Parameters.DeletePolicyDistanceRatesParams; [WRITE_COMMANDS.DISMISS_TRACK_EXPENSE_ACTIONABLE_WHISPER]: Parameters.DismissTrackExpenseActionableWhisperParams; + [WRITE_COMMANDS.UPDATE_BILLING_CARD_CURRENCY]: Parameters.UpdateBillingCurrencyParams; [WRITE_COMMANDS.CONVERT_TRACKED_EXPENSE_TO_REQUEST]: Parameters.ConvertTrackedExpenseToRequestParams; [WRITE_COMMANDS.CATEGORIZE_TRACKED_EXPENSE]: Parameters.CategorizeTrackedExpenseParams; [WRITE_COMMANDS.SHARE_TRACKED_EXPENSE]: Parameters.ShareTrackedExpenseParams; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 807c938e21dd..6b2dd3a9ef2c 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -29,6 +29,7 @@ import type { TravelNavigatorParamList, WalletStatementNavigatorParamList, } from '@navigation/types'; +import ChangeCurrency from '@pages/settings/PaymentCard/ChangeCurrency'; import type {ThemeStyles} from '@styles/index'; import type {Screen} from '@src/SCREENS'; import SCREENS from '@src/SCREENS'; @@ -331,6 +332,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/taxes/WorkspaceCreateTaxPage').default as React.ComponentType, [SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default as React.ComponentType, [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD]: () => require('../../../../pages/settings/Subscription/PaymentCard/AddPaymentCard').default as React.ComponentType, + [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: () => require('../../../../pages/settings/Subscription/PaymentCard/ChangeBillingCurrency').default as React.ComponentType, + [SCREENS.SETTINGS.ADD_PAYMENT_CARD_CHANGE_CURRENCY]: () => require('../../../../pages/settings/PaymentCard/ChangeCurrency').default as React.ComponentType, }); const EnablePaymentsStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts index c4858d3141f1..2402dd01e00c 100755 --- a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts @@ -39,7 +39,12 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial> = [SCREENS.SETTINGS.SAVE_THE_WORLD]: [SCREENS.I_KNOW_A_TEACHER, SCREENS.INTRO_SCHOOL_PRINCIPAL, SCREENS.I_AM_A_TEACHER], [SCREENS.SETTINGS.TROUBLESHOOT]: [SCREENS.SETTINGS.CONSOLE], [SCREENS.SEARCH.CENTRAL_PANE]: [SCREENS.SEARCH.REPORT_RHP], - [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD, SCREENS.SETTINGS.SUBSCRIPTION.SIZE, SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY], + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [ + SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD, + SCREENS.SETTINGS.SUBSCRIPTION.SIZE, + SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY, + SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY, + ], }; export default CENTRAL_PANE_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index f91d290639ff..ab8647c5b8ff 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -11,6 +11,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.OWNER_CHANGE_CHECK, SCREENS.WORKSPACE.OWNER_CHANGE_SUCCESS, SCREENS.WORKSPACE.OWNER_CHANGE_ERROR, + SCREENS.WORKSPACE.OWNER_CHANGE_ERROR, ], [SCREENS.WORKSPACE.WORKFLOWS]: [ SCREENS.WORKSPACE.WORKFLOWS_APPROVER, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index bb002ec2c01f..a2b7da387112 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -130,6 +130,14 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD, exact: true, }, + [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: { + path: ROUTES.SETTINGS_SUBSCRIPTION_CHANGE_BILLING_CURRENCY, + exact: true, + }, + [SCREENS.SETTINGS.ADD_PAYMENT_CARD_CHANGE_CURRENCY]: { + path: ROUTES.SETTINGS_CHANGE_CURRENCY, + exact: true, + }, [SCREENS.SETTINGS.PREFERENCES.THEME]: { path: ROUTES.SETTINGS_THEME, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5597e7ce00da..c100a9355b03 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -250,6 +250,7 @@ type SettingsNavigatorParamList = { tagName: string; }; [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD]: undefined; + [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: undefined; [SCREENS.WORKSPACE.TAXES_SETTINGS]: { policyID: string; }; diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index c12f7a042659..f4c330711ae6 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -5,7 +5,14 @@ import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; -import type {AddPaymentCardParams, DeletePaymentCardParams, MakeDefaultPaymentMethodParams, PaymentCardParams, TransferWalletBalanceParams} from '@libs/API/parameters'; +import { + AddPaymentCardParams, + DeletePaymentCardParams, + MakeDefaultPaymentMethodParams, + PaymentCardParams, + TransferWalletBalanceParams, + UpdateBillingCurrencyParams, +} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as CardUtils from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -273,6 +280,17 @@ function clearDebitCardFormErrorAndSubmit() { [INPUT_IDS.ADDRESS_ZIP_CODE]: '', [INPUT_IDS.ADDRESS_STATE]: '', [INPUT_IDS.ACCEPT_TERMS]: '', + [INPUT_IDS.CURRENCY]: CONST.CURRENCY.USD, + }); +} + +/** + * Set currency for payments + * + */ +function setPaymentMethodCurrency(currency: ValueOf) { + Onyx.merge(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, { + [INPUT_IDS.CURRENCY]: currency, }); } @@ -425,6 +443,54 @@ function deletePaymentCard(fundID: number) { }); } +/** + * Call the API to change billing currency. + * + */ +function updateBillingCurrency(currency: ValueOf, cardCVV: string) { + const parameters: UpdateBillingCurrencyParams = { + cardCVV, + currency, + }; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: 'merge', + key: ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM, + value: { + isLoading: true, + errors: null, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: 'merge', + key: ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM, + value: { + isLoading: false, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: 'merge', + key: ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM, + value: { + isLoading: false, + }, + }, + ]; + + API.write(WRITE_COMMANDS.UPDATE_BILLING_CARD_CURRENCY, parameters, { + optimisticData, + successData, + failureData, + }); +} + export { deletePaymentCard, addPaymentCard, @@ -440,8 +506,10 @@ export { saveWalletTransferAccountTypeAndID, saveWalletTransferMethodType, hasPaymentMethodError, + updateBillingCurrency, clearDeletePaymentMethodError, clearAddPaymentMethodError, clearWalletError, + setPaymentMethodCurrency, clearWalletTermsError, }; diff --git a/src/pages/settings/PaymentCard/ChangeCurrency/index.tsx b/src/pages/settings/PaymentCard/ChangeCurrency/index.tsx new file mode 100644 index 000000000000..d57bdda4cdfb --- /dev/null +++ b/src/pages/settings/PaymentCard/ChangeCurrency/index.tsx @@ -0,0 +1,44 @@ +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import PaymentCardChangeCurrencyForm from '@components/AddPaymentCard/PaymentCardChangeCurrencyForm'; +import type {FormOnyxValues} from '@components/Form/types'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@navigation/Navigation'; +import * as PaymentMethods from '@userActions/PaymentMethods'; +import type CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function ChangeCurrency() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const [debitCardForm] = useOnyx(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM); + + const changeCurrency = useCallback((currency?: ValueOf) => { + if (currency) { + PaymentMethods.setPaymentMethodCurrency(currency); + } + + Navigation.goBack(); + }, []); + + return ( + + + + + + + ); +} + +ChangeCurrency.displayName = 'ChangeCurrency'; + +export default ChangeCurrency; diff --git a/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx b/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx index eb40977c25a9..0347d88abf52 100644 --- a/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx @@ -5,8 +5,10 @@ import ThreeDotsMenu from '@components/ThreeDotsMenu'; import type ThreeDotsMenuProps from '@components/ThreeDotsMenu/types'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import Navigation from '@navigation/Navigation'; import type {AnchorPosition} from '@styles/index'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; const anchorAlignment = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, @@ -29,7 +31,7 @@ function CardSectionActions() { { icon: Expensicons.MoneyCircle, text: translate('subscription.cardSection.changeCurrency'), - onSelected: () => {}, // TODO: update with navigation to "change currency" screen (https://github.com/Expensify/App/issues/38621) + onSelected: () => Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_CHANGE_BILLING_CURRENCY), }, ], [translate], diff --git a/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx b/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx index 13131841c53d..9f51718f0fee 100644 --- a/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx @@ -1,16 +1,22 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import Button from '@components/Button'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@navigation/Navigation'; +import ROUTES from '@src/ROUTES'; function CardSectionDataEmpty() { const {translate} = useLocalize(); const styles = useThemeStyles(); + const openAddPaymentCardScreen = useCallback(() => { + Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD); + }, []); + return (