From e69dd460c76176e324a374bf6905fafa18f2d5cc Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jul 2024 16:09:51 +0530 Subject: [PATCH 001/180] Added pluralization system for lang translations --- src/components/MoneyReportHeader.tsx | 4 +- src/components/ProcessMoneyReportHoldMenu.tsx | 2 +- src/components/ReceiptAudit.tsx | 2 +- .../ExportWithDropdownMenu.tsx | 2 +- .../ReportActionItem/ReportPreview.tsx | 13 +- .../Search/SearchListWithHeader.tsx | 4 +- src/components/Search/SearchPageHeader.tsx | 2 +- src/languages/en.ts | 108 ++++++++++------ src/languages/es.ts | 115 +++++++++++------- src/languages/types.ts | 63 ++++++++-- src/libs/Localize/index.ts | 37 +++++- src/libs/PolicyUtils.ts | 5 +- src/libs/ReportActionsUtils.ts | 2 +- src/pages/ReportDetailsPage.tsx | 4 +- src/pages/ReportParticipantsPage.tsx | 2 +- src/pages/Search/SearchSelectedNarrow.tsx | 2 +- .../ReportActionCompose.tsx | 12 +- .../home/report/ReportDetailsExportPage.tsx | 2 +- .../FloatingActionButtonAndPopover.tsx | 2 +- src/pages/wallet/WalletStatementPage.tsx | 2 +- src/pages/workspace/WorkspaceMembersPage.tsx | 2 +- .../intacct/ExistingConnectionsPage.tsx | 4 +- .../intacct/import/SageIntacctImportPage.tsx | 2 +- .../NetSuiteExistingConnectionsPage.tsx | 4 +- .../categories/WorkspaceCategoriesPage.tsx | 2 +- .../PolicyDistanceRateDetailsPage.tsx | 2 +- .../distanceRates/PolicyDistanceRatesPage.tsx | 10 +- .../ReportFieldsListValuesPage.tsx | 2 +- .../WorkspaceReportFieldsPage.tsx | 2 +- .../workspace/tags/WorkspaceTagsPage.tsx | 2 +- .../workspace/tags/WorkspaceViewTagsPage.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesPage.tsx | 2 +- 32 files changed, 273 insertions(+), 148 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 1acee187c67b..18a8c0b9e1a1 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -387,12 +387,12 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea /> )} setIsDeleteRequestModalVisible(false)} onModalHide={() => ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current)} - prompt={translate('iou.deleteConfirmation')} + prompt={translate('iou.deleteConfirmation', undefined, 1)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index 872464d8a5b0..ffc1150a454b 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -76,7 +76,7 @@ function ProcessMoneyReportHoldMenu({ if (nonHeldAmount) { return translate(isApprove ? 'iou.confirmApprovalAmount' : 'iou.confirmPayAmount'); } - return translate(isApprove ? 'iou.confirmApprovalAllHoldAmount' : 'iou.confirmPayAllHoldAmount', {transactionCount}); + return translate(isApprove ? 'iou.confirmApprovalAllHoldAmount' : 'iou.confirmPayAllHoldAmount', undefined, transactionCount); }, [nonHeldAmount, transactionCount, translate, isApprove]); return ( diff --git a/src/components/ReceiptAudit.tsx b/src/components/ReceiptAudit.tsx index bb704def1836..1057736621cd 100644 --- a/src/components/ReceiptAudit.tsx +++ b/src/components/ReceiptAudit.tsx @@ -22,7 +22,7 @@ function ReceiptAudit({notes, shouldShowAuditResult}: ReceiptAuditProps) { let auditText = ''; if (notes.length > 0 && shouldShowAuditResult) { - auditText = translate('iou.receiptIssuesFound', notes.length); + auditText = translate('iou.receiptIssuesFound', undefined, notes.length); } else if (!notes.length && shouldShowAuditResult) { auditText = translate('common.verified'); } diff --git a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx index a13c0a266689..2906bee5758f 100644 --- a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx +++ b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx @@ -118,7 +118,7 @@ function ExportWithDropdownMenu({policy, report, connectionName}: ExportWithDrop title={translate('workspace.exportAgainModal.title')} onConfirm={confirmExport} onCancel={() => setModalStatus(null)} - prompt={translate('workspace.exportAgainModal.description', report?.reportName ?? '', connectionName)} + prompt={translate('workspace.exportAgainModal.description', {reportName: report?.reportName ?? '', connectionName})} confirmText={translate('workspace.exportAgainModal.confirmText')} cancelText={translate('workspace.exportAgainModal.cancelText')} isVisible={!!modalStatus} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index ae6ace23c64e..c794113d0f50 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -328,11 +328,14 @@ function ReportPreview({ return {supportText: formattedMerchant}; } return { - supportText: translate('iou.expenseCount', { - count: numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests, - scanningReceipts: numberOfScanningReceipts, - pendingReceipts: numberOfPendingRequests, - }), + supportText: translate( + 'iou.expenseCount', + { + scanningReceipts: numberOfScanningReceipts, + pendingReceipts: numberOfPendingRequests, + }, + numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests, + ), }; }, [formattedMerchant, formattedDescription, moneyRequestComment, translate, numberOfRequests, numberOfScanningReceipts, numberOfPendingRequests]); diff --git a/src/components/Search/SearchListWithHeader.tsx b/src/components/Search/SearchListWithHeader.tsx index bd4b843bbd60..edfa55a53b26 100644 --- a/src/components/Search/SearchListWithHeader.tsx +++ b/src/components/Search/SearchListWithHeader.tsx @@ -213,8 +213,8 @@ function SearchListWithHeader( isVisible={deleteExpensesConfirmModalVisible} onConfirm={handleDeleteExpenses} onCancel={handleOnCancelConfirmModal} - title={translate('iou.deleteExpense', {count: selectedTransactionsToDelete.length})} - prompt={translate('iou.deleteConfirmation', {count: selectedTransactionsToDelete.length})} + title={translate('iou.deleteExpense', undefined, selectedTransactionsToDelete.length)} + prompt={translate('iou.deleteConfirmation', undefined, selectedTransactionsToDelete.length)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index bd1e053b1c7e..0f4843b52a58 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -221,7 +221,7 @@ function SearchPageHeader({ shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTransactionsKeys.length})} + customText={translate('workspace.common.selected', undefined, selectedTransactionsKeys.length)} options={headerButtonsOptions} isSplitButton={false} /> diff --git a/src/languages/en.ts b/src/languages/en.ts index b49ad50421a4..5e3b9241aae9 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1,4 +1,4 @@ -import {CONST as COMMON_CONST, Str} from 'expensify-common'; +import {CONST as COMMON_CONST} from 'expensify-common'; import {startCase} from 'lodash'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; @@ -17,25 +17,25 @@ import type { ChangePolicyParams, ChangeTypeParams, CharacterLimitParams, - ConfirmHoldExpenseParams, ConfirmThatParams, + CustomersOrJobsLabelTranslationParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, DelegateSubmitParams, DeleteActionParams, DeleteConfirmationParams, - DeleteExpenseTranslationParams, DidSplitAmountMessageParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnterMagicCodeParams, + ExportAgainModalDescriptionTranslationParams, ExportedToIntegrationParams, FormattedMaxLengthParams, ForwardedParams, GoBackMessageParams, GoToRoomParams, InstantSummaryParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, @@ -66,6 +66,7 @@ import type { ReportArchiveReasonsMergedParams, ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, + ReportIntegrationMessageTranslationParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, @@ -80,6 +81,7 @@ import type { SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, + StatementPageTitleTranslationParams, StepCounterParams, StripePaidParams, TaskCreatedActionParams, @@ -496,16 +498,14 @@ export default { sendAttachment: 'Send attachment', addAttachment: 'Add attachment', writeSomething: 'Write something...', - conciergePlaceholderOptions: [ - 'Ask for help!', - 'Ask me anything!', - 'Ask me to book travel!', - 'Ask me what I can do!', - 'Ask me how to pay people!', - 'Ask me how to send an invoice!', - 'Ask me how to scan a receipt!', - 'Ask me how to get a free corporate card!', - ], + conciergePlaceholderOptions: (isSmallScreenWidth: boolean) => { + // If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions + const options = ['Ask for help!', 'Ask me anything!', 'Ask me to book travel!', 'Ask me what I can do!', 'Ask me how to pay people!']; + if (!isSmallScreenWidth) { + options.push('Ask me how to send an invoice!', 'Ask me how to scan a receipt!', 'Ask me how to get a free corporate card!'); + } + return options[Math.floor(Math.random() * options.length)]; + }, blockedFromConcierge: 'Communication is barred', fileUploadFailed: 'Upload failed. File is not supported.', localTime: ({user, time}: LocalTimeParams) => `It's ${time} for ${user}`, @@ -654,7 +654,7 @@ export default { splitBill: 'Split expense', splitScan: 'Split receipt', splitDistance: 'Split distance', - paySomeone: (name: string) => `Pay ${name ?? 'someone'}`, + paySomeone: ({name}: PaySomeoneParams) => `Pay ${name ?? 'someone'}`, assignTask: 'Assign task', header: 'Quick action', trackManual: 'Track expense', @@ -700,7 +700,10 @@ export default { receiptScanning: 'Receipt scanning...', receiptScanInProgress: 'Receipt scan in progress', receiptScanInProgressDescription: 'Receipt scan in progress. Check back later or enter the details now.', - receiptIssuesFound: (count: number) => `${count === 1 ? 'Issue' : 'Issues'} found`, + receiptIssuesFound: () => ({ + one: `Issue found`, + other: () => `Issues found`, + }), fieldPending: 'Pending...', defaultRate: 'Default rate', receiptMissingDetails: 'Receipt missing details', @@ -710,12 +713,19 @@ export default { receiptStatusText: "Only you can see this receipt when it's scanning. Check back later or enter the details now.", receiptScanningFailed: 'Receipt scanning failed. Please enter the details manually.', transactionPendingDescription: 'Transaction pending. It may take a few days to post.', - expenseCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => - `${count} ${Str.pluralize('expense', 'expenses', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${ - pendingReceipts > 0 ? `, ${pendingReceipts} pending` : '' - }`, - deleteExpense: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `Delete ${Str.pluralize('expense', 'expenses', count)}`, - deleteConfirmation: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `Are you sure that you want to delete ${Str.pluralize('this expense', 'these expenses', count)}?`, + expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ + zero: `0 expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, + one: `1 expense${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, + other: (count: number) => `${count} expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, + }), + deleteExpense: () => ({ + one: `Delete expense`, + other: () => `Delete expenses`, + }), + deleteConfirmation: () => ({ + one: `Are you sure that you want to delete this expense?`, + other: () => `Are you sure that you want to delete these expenses?`, + }), settledExpensify: 'Paid', settledElsewhere: 'Paid elsewhere', individual: 'Individual', @@ -808,12 +818,18 @@ export default { keepAll: 'Keep all', confirmApprove: 'Confirm approval amount', confirmApprovalAmount: 'Approve only compliant expenses, or approve the entire report.', - confirmApprovalAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('This expense is', 'These expenses are', transactionCount)} on hold. Do you want to approve anyway?`, + confirmApprovalAllHoldAmount: () => ({ + zero: `This expense is on hold. Do you want to approve anyway?`, + one: `This expense is on hold. Do you want to approve anyway?`, + other: () => `These expenses are on hold. Do you want to approve anyway?`, + }), confirmPay: 'Confirm payment amount', confirmPayAmount: "Pay what's not on hold, or pay the entire report.", - confirmPayAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('This expense is', 'These expenses are', transactionCount)} on hold. Do you want to pay anyway?`, + confirmPayAllHoldAmount: () => ({ + zero: `This expense is on hold. Do you want to pay anyway?`, + one: `This expense is on hold. Do you want to pay anyway?`, + other: () => `These expenses are on hold. Do you want to pay anyway?`, + }), payOnly: 'Pay only', approveOnly: 'Approve only', holdEducationalTitle: 'This expense is on', @@ -2040,7 +2056,10 @@ export default { testTransactions: 'Test transactions', issueAndManageCards: 'Issue and manage cards', reconcileCards: 'Reconcile cards', - selected: ({selectedNumber}) => `${selectedNumber} selected`, + selected: () => ({ + one: `1 selected`, + other: (count: number) => `${count} selected`, + }), settlementFrequency: 'Settlement frequency', deleteConfirmation: 'Are you sure you want to delete this workspace?', unavailable: 'Unavailable workspace', @@ -2075,7 +2094,7 @@ export default { createNewConnection: 'Create new connection', reuseExistingConnection: 'Reuse existing connection', existingConnections: 'Existing connections', - lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Last synced ${formattedDate}`, + lastSyncDate: ({connectionName, formattedDate}: LastSyncDateTranslationParams) => `${connectionName} - Last synced ${formattedDate}`, }, qbo: { importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.', @@ -2507,7 +2526,7 @@ export default { importJobs: 'Import projects', customers: 'customers', jobs: 'projects', - label: (importFields: string[], importType: string) => `${importFields.join(' and ')}, ${importType}`, + label: ({importFields, importType}: CustomersOrJobsLabelTranslationParams) => `${importFields.join(' and ')}, ${importType}`, }, importTaxDescription: 'Import tax groups from NetSuite.', importCustomFields: { @@ -2629,7 +2648,10 @@ export default { addAUserDefinedDimension: 'Add a user-defined dimension', detailedInstructionsLink: 'View detailed instructions', detailedInstructionsRestOfSentence: ' on adding user-defined dimensions.', - userDimensionsAdded: (dimensionsCount: number) => `${dimensionsCount} ${Str.pluralize('UDD', `UDDs`, dimensionsCount)} added`, + userDimensionsAdded: () => ({ + one: `1 UDD added`, + other: (count: number) => `${count} UDDs added`, + }), mappingTitle: (mappingName: SageIntacctMappingName): string => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: @@ -3279,9 +3301,18 @@ export default { rate: 'Rate', addRate: 'Add rate', trackTax: 'Track tax', - deleteRates: ({count}: DistanceRateOperationsParams) => `Delete ${Str.pluralize('rate', 'rates', count)}`, - enableRates: ({count}: DistanceRateOperationsParams) => `Enable ${Str.pluralize('rate', 'rates', count)}`, - disableRates: ({count}: DistanceRateOperationsParams) => `Disable ${Str.pluralize('rate', 'rates', count)}`, + deleteRates: () => ({ + one: `Delete 1 rate`, + other: (count: number) => `Delete ${count} rates`, + }), + enableRates: () => ({ + one: `Enable 1 rate`, + other: (count: number) => `Enable ${count} rates`, + }), + disableRates: () => ({ + one: `Disable 1 rate`, + other: (count: number) => `Disable ${count} rates`, + }), enableRate: 'Enable rate', status: 'Status', unit: 'Unit', @@ -3289,7 +3320,10 @@ export default { changePromptMessage: ' to make that change.', defaultCategory: 'Default category', deleteDistanceRate: 'Delete distance rate', - areYouSureDelete: ({count}: DistanceRateOperationsParams) => `Are you sure you want to delete ${Str.pluralize('this rate', 'these rates', count)}?`, + areYouSureDelete: () => ({ + one: `Are you sure you want to delete 1 rate?`, + other: (count: number) => `Are you sure you want to delete ${count} rates?`, + }), }, editor: { descriptionInputLabel: 'Description', @@ -3376,7 +3410,7 @@ export default { }, exportAgainModal: { title: 'Careful!', - description: (reportName: string, connectionName: ConnectionName) => + description: ({reportName, connectionName}: ExportAgainModalDescriptionTranslationParams) => `The following reports have already been exported to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}:\n\n${reportName}\n\nAre you sure you want to export them again?`, confirmText: 'Yes, export again', cancelText: 'Cancel', @@ -3530,7 +3564,7 @@ export default { deleteConfirmation: 'Are you sure you want to delete this task?', }, statementPage: { - title: (year, monthName) => `${monthName} ${year} statement`, + title: ({year, monthName}: StatementPageTitleTranslationParams) => `${monthName} ${year} statement`, generatingPDF: "We're generating your PDF right now. Please check back soon!", }, keyboardShortcutsPage: { @@ -3688,7 +3722,7 @@ export default { pending: ({label}: ExportedToIntegrationParams) => `started exporting this report to ${label}...`, }, forwarded: ({amount, currency}: ForwardedParams) => `approved ${currency}${amount}`, - integrationsMessage: (errorMessage: string, label: string) => `failed to export this report to ${label} ("${errorMessage}").`, + integrationsMessage: ({errorMessage, label}: ReportIntegrationMessageTranslationParams) => `failed to export this report to ${label} ("${errorMessage}").`, managerAttachReceipt: `added a receipt`, managerDetachReceipt: `removed a receipt`, markedReimbursed: ({amount, currency}: MarkedReimbursedParams) => `paid ${currency}${amount} elsewhere`, diff --git a/src/languages/es.ts b/src/languages/es.ts index ff06d4f4f97e..1ce4ac35586b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1,4 +1,3 @@ -import {Str} from 'expensify-common'; import CONST from '@src/CONST'; import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type { @@ -15,26 +14,26 @@ import type { ChangePolicyParams, ChangeTypeParams, CharacterLimitParams, - ConfirmHoldExpenseParams, ConfirmThatParams, + CustomersOrJobsLabelTranslationParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, DelegateSubmitParams, DeleteActionParams, DeleteConfirmationParams, - DeleteExpenseTranslationParams, DidSplitAmountMessageParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnglishTranslation, EnterMagicCodeParams, + ExportAgainModalDescriptionTranslationParams, ExportedToIntegrationParams, FormattedMaxLengthParams, ForwardedParams, GoBackMessageParams, GoToRoomParams, InstantSummaryParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, @@ -65,6 +64,7 @@ import type { ReportArchiveReasonsMergedParams, ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, + ReportIntegrationMessageTranslationParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, @@ -79,6 +79,7 @@ import type { SignUpNewFaceCodeParams, SizeExceededParams, SplitAmountParams, + StatementPageTitleTranslationParams, StepCounterParams, StripePaidParams, TaskCreatedActionParams, @@ -487,16 +488,13 @@ export default { sendAttachment: 'Enviar adjunto', addAttachment: 'Añadir archivo adjunto', writeSomething: 'Escribe algo...', - conciergePlaceholderOptions: [ - '¡Pide ayuda!', - '¡Pregúntame lo que sea!', - '¡Pídeme que te reserve un viaje!', - '¡Pregúntame qué puedo hacer!', - '¡Pregúntame cómo pagar a la gente!', - '¡Pregúntame cómo enviar una factura!', - '¡Pregúntame cómo escanear un recibo!', - '¡Pregúntame cómo obtener una tarjeta de crédito corporativa gratis!', - ], + conciergePlaceholderOptions: (isSmallScreenWidth: boolean) => { + const options = ['¡Pide ayuda!', '¡Pregúntame lo que sea!', '¡Pídeme que te reserve un viaje!', '¡Pregúntame qué puedo hacer!', '¡Pregúntame cómo pagar a la gente!']; + if (!isSmallScreenWidth) { + options.push('¡Pregúntame cómo enviar una factura!', '¡Pregúntame cómo escanear un recibo!', '¡Pregúntame cómo obtener una tarjeta de crédito corporativa gratis!'); + } + return options[Math.floor(Math.random() * options.length)]; + }, blockedFromConcierge: 'Comunicación no permitida', fileUploadFailed: 'Subida fallida. El archivo no es compatible.', localTime: ({user, time}: LocalTimeParams) => `Son las ${time} para ${user}`, @@ -649,7 +647,7 @@ export default { splitBill: 'Dividir gasto', splitScan: 'Dividir recibo', splitDistance: 'Dividir distancia', - paySomeone: (name: string) => `Pagar a ${name ?? 'alguien'}`, + paySomeone: ({name}: PaySomeoneParams) => `Pagar a ${name ?? 'alguien'}`, assignTask: 'Assignar tarea', header: 'Acción rápida', trackManual: 'Crear gasto', @@ -692,7 +690,10 @@ export default { pendingMatchWithCreditCardDescription: 'Recibo pendiente de adjuntar con la transacción de la tarjeta. Márcalo como efectivo para cancelar.', markAsCash: 'Marcar como efectivo', routePending: 'Ruta pendiente...', - receiptIssuesFound: (count: number) => `${count === 1 ? 'Problema encontrado' : 'Problemas encontrados'}`, + receiptIssuesFound: () => ({ + one: `Problema encontrado`, + other: () => `Problemas encontrados`, + }), fieldPending: 'Pendiente...', receiptScanning: 'Escaneando recibo...', receiptScanInProgress: 'Escaneado de recibo en proceso', @@ -705,13 +706,19 @@ export default { receiptStatusText: 'Solo tú puedes ver este recibo cuando se está escaneando. Vuelve más tarde o introduce los detalles ahora.', receiptScanningFailed: 'El escaneo de recibo ha fallado. Introduce los detalles manualmente.', transactionPendingDescription: 'Transacción pendiente. Puede tardar unos días en contabilizarse.', - expenseCount: ({count, scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => - `${count} ${Str.pluralize('gasto', 'gastos', count)}${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${ - pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : '' - }`, - - deleteExpense: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `Eliminar ${Str.pluralize('gasto', 'gastos', count)}`, - deleteConfirmation: ({count}: DeleteExpenseTranslationParams = {count: 1}) => `¿Estás seguro de que quieres eliminar ${Str.pluralize('esta solicitud', 'estas solicitudes', count)}?`, + expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ + zero: `0 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, + one: `1 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, + other: (count: number) => `${count} gastos${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, + }), + deleteExpense: () => ({ + one: `Eliminar gasto`, + other: () => `Eliminar gastos`, + }), + deleteConfirmation: () => ({ + one: `¿Estás seguro de que quieres eliminar esta solicitud?`, + other: () => `¿Estás seguro de que quieres eliminar estas solicitudes?`, + }), settledExpensify: 'Pagado', settledElsewhere: 'Pagado de otra forma', individual: 'Individual', @@ -804,20 +811,18 @@ export default { keepAll: 'Mantener todos', confirmApprove: 'Confirmar importe a aprobar', confirmApprovalAmount: 'Aprueba sólo los gastos conformes, o aprueba todo el informe.', - confirmApprovalAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('Este gasto está bloqueado', 'Estos gastos están bloqueados', transactionCount)}. ¿Quieres ${Str.pluralize( - 'aprobar', - 'aprobarlos', - transactionCount, - )} de todos modos?`, + confirmApprovalAllHoldAmount: () => ({ + zero: `Ningún gasto está bloqueado. ¿Quieres aprobar todo el informe?`, + one: `Este gasto está bloqueado. ¿Quieres aprobarlo de todos modos?`, + other: () => `Estos gastos están bloqueados. ¿Quieres aprobarlos de todos modos?`, + }), confirmPay: 'Confirmar importe de pago', confirmPayAmount: 'Paga lo que no está bloqueado, o paga el informe completo.', - confirmPayAllHoldAmount: ({transactionCount}: ConfirmHoldExpenseParams) => - `${Str.pluralize('Este gasto está bloqueado', 'Estos gastos están bloqueados', transactionCount)}. ¿Quieres ${Str.pluralize( - 'pagar', - 'pagarlo', - transactionCount, - )} de todos modos?`, + confirmPayAllHoldAmount: () => ({ + zero: `Ningún gasto está bloqueado. ¿Quieres pagar todo el informe?`, + one: `Este gasto está bloqueado. ¿Quieres pagarlo de todos modos?`, + other: () => `Estos gastos están bloqueados. ¿Quieres pagarlos de todos modos?`, + }), payOnly: 'Solo pagar', approveOnly: 'Solo aprobar', hold: 'Bloquear', @@ -2074,7 +2079,10 @@ export default { testTransactions: 'Transacciones de prueba', issueAndManageCards: 'Emitir y gestionar tarjetas', reconcileCards: 'Reconciliar tarjetas', - selected: ({selectedNumber}) => `${selectedNumber} seleccionados`, + selected: () => ({ + one: `1 seleccionado`, + other: (count: number) => `${count} seleccionados`, + }), settlementFrequency: 'Frecuencia de liquidación', deleteConfirmation: '¿Estás seguro de que quieres eliminar este espacio de trabajo?', unavailable: 'Espacio de trabajo no disponible', @@ -2110,7 +2118,7 @@ export default { createNewConnection: 'Crear una nueva conexión', reuseExistingConnection: 'Reutilizar la conexión existente', existingConnections: 'Conexiones existentes', - lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Última sincronización ${formattedDate}`, + lastSyncDate: ({connectionName, formattedDate}: LastSyncDateTranslationParams) => `${connectionName} - Última sincronización ${formattedDate}`, }, qbo: { importDescription: 'Elige que configuraciónes de codificación son importadas desde QuickBooks Online a Expensify.', @@ -2556,7 +2564,7 @@ export default { importJobs: 'Importar proyectos', customers: 'clientes', jobs: 'proyectos', - label: (importFields: string[], importType: string) => `${importFields.join(' y ')}, ${importType}`, + label: ({importFields, importType}: CustomersOrJobsLabelTranslationParams) => `${importFields.join(' y ')}, ${importType}`, }, importTaxDescription: 'Importar grupos de impuestos desde NetSuite.', importCustomFields: { @@ -2678,7 +2686,10 @@ export default { addAUserDefinedDimension: 'Añadir una dimensión definida por el usuario', detailedInstructionsLink: 'Ver instrucciones detalladas', detailedInstructionsRestOfSentence: ' para añadir dimensiones definidas por el usuario.', - userDimensionsAdded: (dimensionsCount: number) => `${dimensionsCount} ${Str.pluralize('UDD', `UDDs`, dimensionsCount)} añadido`, + userDimensionsAdded: () => ({ + one: `1 UDD añadido`, + other: (count: number) => `${count} UDDs añadido`, + }), mappingTitle: (mappingName: SageIntacctMappingName): string => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: @@ -3333,9 +3344,18 @@ export default { rate: 'Tasa', addRate: 'Agregar tasa', trackTax: 'Impuesto de seguimiento', - deleteRates: ({count}: DistanceRateOperationsParams) => `Eliminar ${Str.pluralize('tasa', 'tasas', count)}`, - enableRates: ({count}: DistanceRateOperationsParams) => `Activar ${Str.pluralize('tasa', 'tasas', count)}`, - disableRates: ({count}: DistanceRateOperationsParams) => `Desactivar ${Str.pluralize('tasa', 'tasas', count)}`, + deleteRates: () => ({ + one: `Eliminar 1 tasa`, + other: (count: number) => `Eliminar ${count} tasas`, + }), + enableRates: () => ({ + one: `Activar 1 tasa`, + other: (count: number) => `Activar ${count} tasas`, + }), + disableRates: () => ({ + one: `Desactivar 1 tasa`, + other: (count: number) => `Desactivar ${count} tasas`, + }), enableRate: 'Activar tasa', status: 'Estado', unit: 'Unidad', @@ -3343,7 +3363,10 @@ export default { changePromptMessage: ' para hacer ese cambio.', defaultCategory: 'Categoría predeterminada', deleteDistanceRate: 'Eliminar tasa de distancia', - areYouSureDelete: ({count}: DistanceRateOperationsParams) => `¿Estás seguro de que quieres eliminar ${Str.pluralize('esta tasa', 'estas tasas', count)}?`, + areYouSureDelete: () => ({ + one: `¿Estás seguro de que quieres eliminar 1 tasa?`, + other: (count: number) => `¿Estás seguro de que quieres eliminar ${count} tasas?`, + }), }, editor: { nameInputLabel: 'Nombre', @@ -3432,7 +3455,7 @@ export default { exportAgainModal: { title: '¡Cuidado!', - description: (reportName: string, connectionName: ConnectionName) => + description: ({reportName, connectionName}: ExportAgainModalDescriptionTranslationParams) => `Los siguientes informes ya se han exportado a ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]}:\n\n${reportName}\n\n¿Estás seguro de que deseas exportarlos de nuevo?`, confirmText: 'Sí, exportar de nuevo', cancelText: 'Cancelar', @@ -3587,7 +3610,7 @@ export default { deleteConfirmation: '¿Estás seguro de que quieres eliminar esta tarea?', }, statementPage: { - title: (year, monthName) => `Estado de cuenta de ${monthName} ${year}`, + title: ({year, monthName}: StatementPageTitleTranslationParams) => `Estado de cuenta de ${monthName} ${year}`, generatingPDF: 'Estamos generando tu PDF ahora mismo. ¡Por favor, vuelve más tarde!', }, keyboardShortcutsPage: { @@ -3746,7 +3769,7 @@ export default { pending: ({label}: ExportedToIntegrationParams) => `comenzó a exportar este informe a ${label}...`, }, forwarded: ({amount, currency}: ForwardedParams) => `aprobado ${currency}${amount}`, - integrationsMessage: (errorMessage: string, label: string) => `no se pudo exportar este informe a ${label} ("${errorMessage}").`, + integrationsMessage: ({errorMessage, label}: ReportIntegrationMessageTranslationParams) => `no se pudo exportar este informe a ${label} ("${errorMessage}").`, managerAttachReceipt: `agregó un recibo`, managerDetachReceipt: `quitó un recibo`, markedReimbursed: ({amount, currency}: MarkedReimbursedParams) => `pagó ${currency}${amount} en otro lugar`, diff --git a/src/languages/types.ts b/src/languages/types.ts index 24117f257d8f..62c96beb323e 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -1,5 +1,5 @@ import type {OnyxInputOrEntry, ReportAction} from '@src/types/onyx'; -import type {Unit} from '@src/types/onyx/Policy'; +import type {ConnectionName, Unit} from '@src/types/onyx/Policy'; import type {ViolationDataType} from '@src/types/onyx/TransactionViolation'; import type en from './en'; @@ -96,7 +96,6 @@ type ReportArchiveReasonsPolicyDeletedParams = { }; type RequestCountParams = { - count: number; scanningReceipts: number; pendingReceipts: number; }; @@ -251,9 +250,21 @@ type PaySomeoneParams = {name?: string}; type TaskCreatedActionParams = {title: string}; +type PluralFormPhase = { + zero?: string; + one: string; + two?: string; + few?: (count: number) => string; + many?: (count: number) => string; + other: (count: number) => string; +}; + /* Translation Object types */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -type TranslationBaseValue = string | string[] | ((...args: any[]) => string); +type TranslationPluralPhaseValue = (arg?: any) => PluralFormPhase; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type TranslationSingularFunctionValue = (arg?: any) => string; +type TranslationBaseValue = string | TranslationPluralPhaseValue | TranslationSingularFunctionValue; type TranslationBase = {[key: string]: TranslationBaseValue | TranslationBase}; @@ -261,7 +272,7 @@ type TranslationBase = {[key: string]: TranslationBaseValue | TranslationBase}; // Flattens an object and returns concatenations of all the keys of nested objects type FlattenObject = { // eslint-disable-next-line @typescript-eslint/no-explicit-any - [TKey in keyof TObject]: TObject[TKey] extends (...args: any[]) => any + [TKey in keyof TObject]: TObject[TKey] extends (arg?: any) => any ? `${TPrefix}${TKey & string}` : // eslint-disable-next-line @typescript-eslint/no-explicit-any TObject[TKey] extends any[] @@ -289,6 +300,11 @@ type TranslationFlatObject = { [TKey in TranslationPaths]: TranslateType; }; +type PluralTranslationFlatObject = Pick< + TranslationFlatObject, + {[K in keyof TranslationFlatObject]: TranslationFlatObject[K] extends TranslationPluralPhaseValue ? K : never}[keyof TranslationFlatObject] +>; + type TermsParams = {amount: string}; type ElectronicFundsParams = {percentage: string; amount: string}; @@ -297,12 +313,8 @@ type LogSizeParams = {size: number}; type HeldRequestParams = {comment: string}; -type DistanceRateOperationsParams = {count: number}; - type ReimbursementRateParams = {unit: Unit}; -type ConfirmHoldExpenseParams = {transactionCount: number}; - type ChangeFieldParams = {oldValue?: string; newValue: string; fieldName: string}; type ChangePolicyParams = {fromPolicy: string; toPolicy: string}; @@ -344,8 +356,29 @@ type RemoveMembersWarningPrompt = { ownerName: string; }; -type DeleteExpenseTranslationParams = { - count: number; +type LastSyncDateTranslationParams = { + connectionName: string; + formattedDate: string; +}; + +type CustomersOrJobsLabelTranslationParams = { + importFields: string[]; + importType: string; +}; + +type ExportAgainModalDescriptionTranslationParams = { + reportName: string; + connectionName: ConnectionName; +}; + +type StatementPageTitleTranslationParams = { + year: string | number; + monthName: string; +}; + +type ReportIntegrationMessageTranslationParams = { + errorMessage: string; + label: string; }; export type { @@ -359,14 +392,12 @@ export type { BeginningOfChatHistoryDomainRoomPartOneParams, CanceledRequestParams, CharacterLimitParams, - ConfirmHoldExpenseParams, ConfirmThatParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, DeleteActionParams, DeleteConfirmationParams, DidSplitAmountMessageParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnglishTranslation, @@ -423,9 +454,11 @@ export type { ThreadSentMoneyReportNameParams, ToValidateLoginParams, TransferParams, + PluralFormPhase, TranslationBase, TranslationFlatObject, TranslationPaths, + PluralTranslationFlatObject, UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, @@ -468,5 +501,9 @@ export type { StripePaidParams, UnapprovedParams, RemoveMembersWarningPrompt, - DeleteExpenseTranslationParams, + LastSyncDateTranslationParams, + CustomersOrJobsLabelTranslationParams, + ExportAgainModalDescriptionTranslationParams, + StatementPageTitleTranslationParams, + ReportIntegrationMessageTranslationParams, }; diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index c9eef3170245..f1b705398067 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -6,7 +6,7 @@ import type {MessageElementBase, MessageTextElement} from '@libs/MessageElement' import Config from '@src/CONFIG'; import CONST from '@src/CONST'; import translations from '@src/languages/translations'; -import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; +import type {PluralFormPhase, TranslationFlatObject, TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Locale} from '@src/types/onyx'; import LocaleListener from './LocaleListener'; @@ -45,8 +45,8 @@ function init() { }, {}); } -type PhraseParameters = T extends (...args: infer A) => string ? A : never[]; -type Phrase = TranslationFlatObject[TKey] extends (...args: infer A) => unknown ? (...args: A) => string : string; +type PhraseParameters = T extends (arg: infer A) => string ? [A] : T extends (arg: infer A) => PluralFormPhase ? [A, number] : never[]; +type Phrase = TranslationFlatObject[TKey]; /** * Map to store translated values for each locale. @@ -70,6 +70,23 @@ const translationCache = new Map, Map, Map]>), ); +function handlePluralForm(translatedPhrase: PluralFormPhase, pluralForm: Intl.LDMLPluralRule, count: number) { + switch (pluralForm) { + case 'zero': + return translatedPhrase.zero; + case 'one': + return translatedPhrase.one; + case 'two': + return translatedPhrase.two; + case 'few': + return translatedPhrase.few?.(count); + case 'many': + return translatedPhrase.many?.(count); + default: + return translatedPhrase.other(count); + } +} + /** * Helper function to get the translated string for given * locale and phrase. This function is used to avoid @@ -106,11 +123,21 @@ function getTranslatedPhrase( return valueFromCache; } - const translatedPhrase = translations?.[language]?.[phraseKey] as Phrase; + const translatedPhrase = translations?.[language]?.[phraseKey]; if (translatedPhrase) { if (typeof translatedPhrase === 'function') { - return translatedPhrase(...phraseParameters); + const calledTranslatedPhrase = translatedPhrase(phraseParameters[0]); + if (typeof calledTranslatedPhrase === 'string') { + return calledTranslatedPhrase; + } + const count = phraseParameters[1] ?? 0; + const pluralForm = new Intl.PluralRules(language, {type: 'ordinal'}).select(count); + if (pluralForm in calledTranslatedPhrase) { + return handlePluralForm(calledTranslatedPhrase, pluralForm, count) ?? ''; + } + Log.alert(`Plural form ${pluralForm} is not found for ${phraseKey}, using 'other' form`); + return calledTranslatedPhrase.other(count); } // We set the translated value in the cache only for the phrases without parameters. diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index fe53a7bcd5ce..c3410fbd6516 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -649,7 +649,10 @@ function getCustomersOrJobsLabelNetSuite(policy: Policy | undefined, translate: importFields.push(translate('workspace.netsuite.import.customersOrJobs.jobs')); } - const importedValueLabel = translate(`workspace.netsuite.import.customersOrJobs.label`, importFields, translate(`workspace.accounting.importTypes.${importedValue}`).toLowerCase()); + const importedValueLabel = translate(`workspace.netsuite.import.customersOrJobs.label`, { + importFields, + importType: translate(`workspace.accounting.importTypes.${importedValue}`).toLowerCase(), + }); return importedValueLabel.charAt(0).toUpperCase() + importedValueLabel.slice(1); } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 73784873201a..f4683abf6100 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1248,7 +1248,7 @@ function getMessageOfOldDotReportAction(oldDotAction: PartialReportAction | OldD case CONST.REPORT.ACTIONS.TYPE.INTEGRATIONS_MESSAGE: { const {result, label} = originalMessage; const errorMessage = result?.messages?.join(', ') ?? ''; - return Localize.translateLocal('report.actions.type.integrationsMessage', errorMessage, label); + return Localize.translateLocal('report.actions.type.integrationsMessage', {errorMessage, label}); } case CONST.REPORT.ACTIONS.TYPE.MANAGER_ATTACH_RECEIPT: return Localize.translateLocal('report.actions.type.managerAttachReceipt'); diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 4e82900372b9..5e8c311a79e4 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -763,7 +763,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD shouldEnableNewFocusManagement /> setIsDeleteModalVisible(false)} @@ -779,7 +779,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); } }} - prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation')} + prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', undefined, 1)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 15dd063ec6ee..300122db9be5 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -268,7 +268,7 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic shouldAlwaysShowDropdownMenu pressOnEnter - customText={translate('workspace.common.selected', {selectedNumber: selectedMembers.length})} + customText={translate('workspace.common.selected', undefined, selectedMembers.length)} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} onPress={() => null} options={bulkActionsButtonOptions} diff --git a/src/pages/Search/SearchSelectedNarrow.tsx b/src/pages/Search/SearchSelectedNarrow.tsx index 1cc34d6bf53a..039c54dc609a 100644 --- a/src/pages/Search/SearchSelectedNarrow.tsx +++ b/src/pages/Search/SearchSelectedNarrow.tsx @@ -50,7 +50,7 @@ function SearchSelectedNarrow({options, itemsLength}: SearchSelectedNarrowProps) onPress={openMenu} ref={buttonRef} style={[styles.w100, styles.ph5]} - text={translate('workspace.common.selected', {selectedNumber: itemsLength})} + text={translate('workspace.common.selected', undefined, itemsLength)} isContentCentered iconRight={Expensicons.DownArrow} isDisabled={options.length === 0} diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 6ff163f6ec37..403b7864102f 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -195,13 +195,6 @@ function ReportActionCompose({ const userBlockedFromConcierge = useMemo(() => User.isBlockedFromConcierge(blockedFromConcierge), [blockedFromConcierge]); const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); - // If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions - const conciergePlaceholderRandomIndex = useMemo( - () => Math.floor(Math.random() * (translate('reportActionCompose.conciergePlaceholderOptions').length - (isSmallScreenWidth ? 4 : 1) + 1)), - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - [], - ); - // Placeholder to display in the chat input. const inputPlaceholder = useMemo(() => { if (includesConcierge) { @@ -209,11 +202,12 @@ function ReportActionCompose({ return translate('reportActionCompose.blockedFromConcierge'); } - return translate('reportActionCompose.conciergePlaceholderOptions')[conciergePlaceholderRandomIndex]; + return translate('reportActionCompose.conciergePlaceholderOptions', isSmallScreenWidth); } return translate('reportActionCompose.writeSomething'); - }, [includesConcierge, translate, userBlockedFromConcierge, conciergePlaceholderRandomIndex]); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [includesConcierge, translate, userBlockedFromConcierge]); const focus = () => { if (composerRef.current === null) { diff --git a/src/pages/home/report/ReportDetailsExportPage.tsx b/src/pages/home/report/ReportDetailsExportPage.tsx index 99b7305cc7a9..d6861bd54dd0 100644 --- a/src/pages/home/report/ReportDetailsExportPage.tsx +++ b/src/pages/home/report/ReportDetailsExportPage.tsx @@ -120,7 +120,7 @@ function ReportDetailsExportPage({route}: ReportDetailsExportPageProps) { title={translate('workspace.exportAgainModal.title')} onConfirm={confirmExport} onCancel={() => setModalStatus(null)} - prompt={translate('workspace.exportAgainModal.description', report?.reportName ?? '', connectionName)} + prompt={translate('workspace.exportAgainModal.description', {reportName: report?.reportName ?? '', connectionName})} confirmText={translate('workspace.exportAgainModal.confirmText')} cancelText={translate('workspace.exportAgainModal.cancelText')} isVisible={!!modalStatus} diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index cde3750fc37f..ab18013d6b23 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -212,7 +212,7 @@ function FloatingActionButtonAndPopover( } if (quickAction?.action === CONST.QUICK_ACTIONS.SEND_MONEY && quickActionAvatars.length > 0) { const name: string = ReportUtils.getDisplayNameForParticipant(+(quickActionAvatars[0]?.id ?? -1), true) ?? ''; - return translate('quickAction.paySomeone', name); + return translate('quickAction.paySomeone', {name}); } const titleKey = getQuickActionTitle(quickAction?.action ?? ('' as QuickActionName)); return titleKey ? translate(titleKey) : ''; diff --git a/src/pages/wallet/WalletStatementPage.tsx b/src/pages/wallet/WalletStatementPage.tsx index 0db72877bd2e..54b601a00350 100644 --- a/src/pages/wallet/WalletStatementPage.tsx +++ b/src/pages/wallet/WalletStatementPage.tsx @@ -70,7 +70,7 @@ function WalletStatementPage({walletStatement, route}: WalletStatementPageProps) const year = yearMonth?.substring(0, 4) || getYear(new Date()); const month = yearMonth?.substring(4) || getMonth(new Date()); const monthName = format(new Date(Number(year), Number(month) - 1), CONST.DATE.MONTH_FORMAT); - const title = translate('statementPage.title', year, monthName); + const title = translate('statementPage.title', {year, monthName}); const url = `${CONFIG.EXPENSIFY.EXPENSIFY_URL}statement.php?period=${yearMonth}${themePreference === CONST.THEME.DARK ? '&isDarkMode=true' : ''}`; return ( diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index adbf5a664c82..8b2ca4e09ca1 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -503,7 +503,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, shouldAlwaysShowDropdownMenu pressOnEnter - customText={translate('workspace.common.selected', {selectedNumber: selectedEmployees.length})} + customText={translate('workspace.common.selected', undefined, selectedEmployees.length)} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} onPress={() => null} options={getBulkActionsButtonOptions()} diff --git a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx index c55e46083470..3475d726ba84 100644 --- a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx +++ b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx @@ -30,7 +30,9 @@ function ExistingConnectionsPage({route}: ExistingConnectionsPageProps) { key: policy.id, icon: policy.avatarURL ? policy.avatarURL : ReportUtils.getDefaultWorkspaceAvatar(policy.name), iconType: policy.avatarURL ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_WORKSPACE, - description: date ? translate('workspace.common.lastSyncDate', CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.intacct, date) : translate('workspace.accounting.intacct'), + description: date + ? translate('workspace.common.lastSyncDate', {connectionName: CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.intacct, formattedDate: date}) + : translate('workspace.accounting.intacct'), onPress: () => { copyExistingPolicyConnection(policy.id, policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT); Navigation.dismissModal(); diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx index 543d3f4d0154..958e35c5b572 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -128,7 +128,7 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { 0 - ? translate('workspace.intacct.userDimensionsAdded', sageIntacctConfig?.mappings?.dimensions?.length) + ? translate('workspace.intacct.userDimensionsAdded', undefined, sageIntacctConfig?.mappings?.dimensions?.length) : undefined } description={translate('workspace.intacct.userDefinedDimensions')} diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx index ef98625a64e2..5fdca30600c2 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteExistingConnectionsPage.tsx @@ -30,7 +30,9 @@ function NetSuiteExistingConnectionsPage({route}: ExistingConnectionsPageProps) key: policy.id, icon: policy.avatarURL ? policy.avatarURL : ReportUtils.getDefaultWorkspaceAvatar(policy.name), iconType: policy.avatarURL ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_WORKSPACE, - description: date ? translate('workspace.common.lastSyncDate', CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.netsuite, date) : translate('workspace.accounting.netsuite'), + description: date + ? translate('workspace.common.lastSyncDate', {connectionName: CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY.netsuite, formattedDate: date}) + : translate('workspace.accounting.netsuite'), onPress: () => { copyExistingPolicyConnection(policy.id, policyID, CONST.POLICY.CONNECTIONS.NAME.NETSUITE); Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING.getRoute(policyID)); diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 174251a80d5f..12f76fe15abb 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -224,7 +224,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedCategoriesArray.length})} + customText={translate('workspace.common.selected', undefined, selectedCategoriesArray.length)} options={options} isSplitButton={false} style={[shouldUseNarrowLayout && styles.flexGrow1, shouldUseNarrowLayout && styles.mb3]} diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx index 00204d1e40c7..85e602228149 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx @@ -194,7 +194,7 @@ function PolicyDistanceRateDetailsPage({policy, route}: PolicyDistanceRateDetail isVisible={isDeleteModalVisible} onConfirm={deleteRate} onCancel={() => setIsDeleteModalVisible(false)} - prompt={translate('workspace.distanceRates.areYouSureDelete', {count: 1})} + prompt={translate('workspace.distanceRates.areYouSureDelete', undefined, 1)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx index 1ef1319973a1..4749c3483571 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx @@ -200,7 +200,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const getBulkActionsButtonOptions = () => { const options: Array> = [ { - text: translate('workspace.distanceRates.deleteRates', {count: selectedDistanceRates.length}), + text: translate('workspace.distanceRates.deleteRates', undefined, selectedDistanceRates.length), value: CONST.POLICY.BULK_ACTION_TYPES.DELETE, icon: Expensicons.Trashcan, onSelected: () => (canDisableOrDeleteSelectedRates ? setIsDeleteModalVisible(true) : setIsWarningModalVisible(true)), @@ -210,7 +210,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const enabledRates = selectedDistanceRates.filter((rate) => rate.enabled); if (enabledRates.length > 0) { options.push({ - text: translate('workspace.distanceRates.disableRates', {count: enabledRates.length}), + text: translate('workspace.distanceRates.disableRates', undefined, enabledRates.length), value: CONST.POLICY.BULK_ACTION_TYPES.DISABLE, icon: Expensicons.DocumentSlash, onSelected: () => (canDisableOrDeleteSelectedRates ? disableRates() : setIsWarningModalVisible(true)), @@ -220,7 +220,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const disabledRates = selectedDistanceRates.filter((rate) => !rate.enabled); if (disabledRates.length > 0) { options.push({ - text: translate('workspace.distanceRates.enableRates', {count: disabledRates.length}), + text: translate('workspace.distanceRates.enableRates', undefined, disabledRates.length), value: CONST.POLICY.BULK_ACTION_TYPES.ENABLE, icon: Expensicons.Document, onSelected: enableRates, @@ -257,7 +257,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) shouldAlwaysShowDropdownMenu pressOnEnter - customText={translate('workspace.common.selected', {selectedNumber: selectedDistanceRates.length})} + customText={translate('workspace.common.selected', undefined, selectedDistanceRates.length)} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} onPress={() => null} options={getBulkActionsButtonOptions()} @@ -332,7 +332,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) isVisible={isDeleteModalVisible} onConfirm={deleteRates} onCancel={() => setIsDeleteModalVisible(false)} - prompt={translate('workspace.distanceRates.areYouSureDelete', {count: selectedDistanceRates.length})} + prompt={translate('workspace.distanceRates.areYouSureDelete', undefined, selectedDistanceRates.length)} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx index adbe33397950..0d5a6e617aec 100644 --- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx +++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx @@ -249,7 +249,7 @@ function ReportFieldsListValuesPage({ shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedValuesArray.length})} + customText={translate('workspace.common.selected', undefined, selectedValuesArray.length)} options={options} isSplitButton={false} style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]} diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx index f4e3b01145da..4b231f1f2c5f 100644 --- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx +++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx @@ -164,7 +164,7 @@ function WorkspaceReportFieldsPage({ shouldAlwaysShowDropdownMenu pressOnEnter buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedReportFields.length})} + customText={translate('workspace.common.selected', undefined, selectedReportFields.length)} options={options} isSplitButton={false} style={[shouldUseNarrowLayout && styles.flexGrow1, shouldUseNarrowLayout && styles.mb3]} diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index cf9952720fc9..dc0dab1634b0 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -276,7 +276,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { pressOnEnter isSplitButton={false} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTagsArray.length})} + customText={translate('workspace.common.selected', undefined, selectedTagsArray.length)} options={options} style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]} /> diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx index 9ac1fc7583ae..02a9ba0fe0c2 100644 --- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx @@ -202,7 +202,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) { pressOnEnter isSplitButton={false} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTagsArray.length})} + customText={translate('workspace.common.selected', undefined, selectedTagsArray.length)} options={options} style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]} /> diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index edd2ef4e3a65..f40b2fc054b9 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -235,7 +235,7 @@ function WorkspaceTaxesPage({ onPress={() => {}} options={dropdownMenuOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - customText={translate('workspace.common.selected', {selectedNumber: selectedTaxesIDs.length})} + customText={translate('workspace.common.selected', undefined, selectedTaxesIDs.length)} shouldAlwaysShowDropdownMenu pressOnEnter isSplitButton={false} From e7df141dd5b4a843ddd128da78d06085a67c28be Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jul 2024 16:54:25 +0530 Subject: [PATCH 002/180] Fix lint --- src/components/AddressForm.tsx | 2 +- src/components/ArchivedReportFooter.tsx | 37 +++++++++++++++++++------ tests/unit/TranslateTest.ts | 11 ++------ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index 7ca4cc3273ca..d6ba5813a101 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -126,7 +126,7 @@ function AddressForm({ } } } else if (!CONST.GENERIC_ZIP_CODE_REGEX.test(values?.zipPostCode?.trim()?.toUpperCase() ?? '')) { - errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat'); + errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat', undefined); } return errors; diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 35f5aeecb5a4..7e0121a3ae4f 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -53,14 +53,35 @@ function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}} policyName = lodashEscape(policyName); } - const text = shouldRenderHTML - ? translate(`reportArchiveReasons.${archiveReason}`, { - displayName: `${displayName}`, - oldDisplayName: `${oldDisplayName}`, - policyName: `${policyName}`, - shouldUseYou: actorPersonalDetails?.accountID === getCurrentUserAccountID(), - }) - : translate(`reportArchiveReasons.${archiveReason}`); + let text; + switch (archiveReason) { + case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED: + text = translate(`reportArchiveReasons.${archiveReason}`, { + displayName: `${displayName}`, + }); + break; + case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED: + text = translate(`reportArchiveReasons.${archiveReason}`, { + displayName: `${displayName}`, + oldDisplayName: `${oldDisplayName}`, + }); + break; + case CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY: + text = translate(`reportArchiveReasons.${archiveReason}`, { + displayName: `${displayName}`, + policyName: `${policyName}`, + shouldUseYou: actorPersonalDetails?.accountID === getCurrentUserAccountID(), + }); + break; + case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: + text = translate(`reportArchiveReasons.${archiveReason}`, { + policyName: `${policyName}`, + }); + break; + default: + text = translate(`reportArchiveReasons.${archiveReason}`); + break; + } return ( { expect(Localize.translate(CONST.LOCALES.ES_ES, 'testKey4' as TranslationPaths)).toBe('testKey4'); asMutable(CONFIG).IS_IN_PRODUCTION = ORIGINAL_IS_IN_PRODUCTION; }); - - it('Test when translation value is a function', () => { - const expectedValue = 'With variable Test Variable'; - const testVariable = 'Test Variable'; - // @ts-expect-error - TranslationPaths doesn't include testKeyGroup.testFunction as a valid key - expect(Localize.translate(CONST.LOCALES.EN, 'testKeyGroup.testFunction' as TranslationPaths, {testVariable})).toBe(expectedValue); - }); }); describe('Translation Keys', () => { @@ -126,7 +119,7 @@ describe('flattenObject', () => { none: 'No description', }, content: func, - messages: ['Hello', 'Hi', 'Sup!'], + messages: 'Hello!', }, }, }; @@ -141,7 +134,7 @@ describe('flattenObject', () => { 'complex.report.title.task': 'Task', 'complex.report.description.none': 'No description', 'complex.report.content': func, - 'complex.report.messages': ['Hello', 'Hi', 'Sup!'], + 'complex.report.messages': 'Hello!', }); }); }); From dbc34e6ee66d91bd0c9835a8099315d41827ca18 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:10:43 +0530 Subject: [PATCH 003/180] Apply suggestions from code review Co-authored-by: Jayesh Mangwani <35371050+jayeshmangwani@users.noreply.github.com> --- src/languages/en.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5e3b9241aae9..80e37460ac05 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3302,16 +3302,16 @@ export default { addRate: 'Add rate', trackTax: 'Track tax', deleteRates: () => ({ - one: `Delete 1 rate`, - other: (count: number) => `Delete ${count} rates`, + one: 'Delete rate', + other: () => 'Delete rates', }), enableRates: () => ({ - one: `Enable 1 rate`, - other: (count: number) => `Enable ${count} rates`, + one: 'Enable rate', + other: () => 'Enable rates', }), disableRates: () => ({ - one: `Disable 1 rate`, - other: (count: number) => `Disable ${count} rates`, + one: 'Disable rate', + other: () => 'Disable rates', }), enableRate: 'Enable rate', status: 'Status', @@ -3321,8 +3321,8 @@ export default { defaultCategory: 'Default category', deleteDistanceRate: 'Delete distance rate', areYouSureDelete: () => ({ - one: `Are you sure you want to delete 1 rate?`, - other: (count: number) => `Are you sure you want to delete ${count} rates?`, + one: 'Are you sure you want to delete this rate?', + other: () => 'Are you sure you want to delete these rates?', }), }, editor: { From 7f91f9be8ce90c02b2980225267ed120f45cce7c Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:14:16 +0530 Subject: [PATCH 004/180] Apply suggestions from code review --- src/languages/es.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 1ce4ac35586b..0ae384b5b075 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3345,16 +3345,16 @@ export default { addRate: 'Agregar tasa', trackTax: 'Impuesto de seguimiento', deleteRates: () => ({ - one: `Eliminar 1 tasa`, - other: (count: number) => `Eliminar ${count} tasas`, + one: 'Eliminar tasa', + other: () => 'Eliminar tasas', }), enableRates: () => ({ - one: `Activar 1 tasa`, - other: (count: number) => `Activar ${count} tasas`, + one: 'Activar 1 tasa', + other: () => 'Activar tasas', }), disableRates: () => ({ - one: `Desactivar 1 tasa`, - other: (count: number) => `Desactivar ${count} tasas`, + one: 'Desactivar tasa', + other: () => 'Desactivar tasas', }), enableRate: 'Activar tasa', status: 'Estado', @@ -3364,8 +3364,8 @@ export default { defaultCategory: 'Categoría predeterminada', deleteDistanceRate: 'Eliminar tasa de distancia', areYouSureDelete: () => ({ - one: `¿Estás seguro de que quieres eliminar 1 tasa?`, - other: (count: number) => `¿Estás seguro de que quieres eliminar ${count} tasas?`, + one: '¿Estás seguro de que quieres eliminar esta tasa?', + other: () => '¿Estás seguro de que quieres eliminar estas tasas?', }), }, editor: { From c8dd8d1e696031b4c438c7ac607776c62f3a19ad Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:19:44 +0530 Subject: [PATCH 005/180] Apply suggestions from code review --- .../home/report/ReportActionCompose/ReportActionCompose.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 77e8f366d866..275fe000fe79 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -202,7 +202,7 @@ function ReportActionCompose({ return translate('reportActionCompose.blockedFromConcierge'); } - return translate('reportActionCompose.conciergePlaceholderOptions', isSmallScreenWidth); + return translate('reportActionCompose.conciergePlaceholderOptions', shouldUseNarrowLayout); } return translate('reportActionCompose.writeSomething'); From 3a15ab360099f7427680f2ca6327a84338eea4f2 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 30 Jul 2024 18:11:49 +0530 Subject: [PATCH 006/180] Fixed unused type --- src/languages/types.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/languages/types.ts b/src/languages/types.ts index 62c96beb323e..611deac1e854 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -300,11 +300,6 @@ type TranslationFlatObject = { [TKey in TranslationPaths]: TranslateType; }; -type PluralTranslationFlatObject = Pick< - TranslationFlatObject, - {[K in keyof TranslationFlatObject]: TranslationFlatObject[K] extends TranslationPluralPhaseValue ? K : never}[keyof TranslationFlatObject] ->; - type TermsParams = {amount: string}; type ElectronicFundsParams = {percentage: string; amount: string}; @@ -458,7 +453,6 @@ export type { TranslationBase, TranslationFlatObject, TranslationPaths, - PluralTranslationFlatObject, UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, From fd0b5e7175b1be67111891cc67130ffd3e536419 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 30 Jul 2024 18:27:46 +0530 Subject: [PATCH 007/180] Fixed lint --- src/pages/Search/SearchFiltersDatePage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Search/SearchFiltersDatePage.tsx b/src/pages/Search/SearchFiltersDatePage.tsx index 9ac9973c8ca2..ae11e972dc20 100644 --- a/src/pages/Search/SearchFiltersDatePage.tsx +++ b/src/pages/Search/SearchFiltersDatePage.tsx @@ -47,7 +47,7 @@ function SearchFiltersDatePage() { Date: Tue, 30 Jul 2024 19:19:32 +0530 Subject: [PATCH 008/180] Fixed lint --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index e87a5bb71e3d..f1961dd7b449 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -35,8 +35,8 @@ import type { GoBackMessageParams, GoToRoomParams, InstantSummaryParams, - LastSyncDateTranslationParams, IssueVirtualCardParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, diff --git a/src/languages/es.ts b/src/languages/es.ts index 488cbbca626a..f44850067036 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -33,8 +33,8 @@ import type { GoBackMessageParams, GoToRoomParams, InstantSummaryParams, - LastSyncDateTranslationParams, IssueVirtualCardParams, + LastSyncDateTranslationParams, LocalTimeParams, LoggedInAsParams, LogSizeParams, From 5eed7b5482abfeef2dc723ec9797c04944ce688d Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 1 Aug 2024 18:00:10 +0530 Subject: [PATCH 009/180] Apply suggestions --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index bc91fa1ab428..ec03c97d8948 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -823,7 +823,6 @@ export default { confirmApprove: 'Confirm approval amount', confirmApprovalAmount: 'Approve only compliant expenses, or approve the entire report.', confirmApprovalAllHoldAmount: () => ({ - zero: `This expense is on hold. Do you want to approve anyway?`, one: `This expense is on hold. Do you want to approve anyway?`, other: () => `These expenses are on hold. Do you want to approve anyway?`, }), diff --git a/src/languages/es.ts b/src/languages/es.ts index 3133d6446db2..4a0b72ad237b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -816,7 +816,6 @@ export default { confirmApprove: 'Confirmar importe a aprobar', confirmApprovalAmount: 'Aprueba sólo los gastos conformes, o aprueba todo el informe.', confirmApprovalAllHoldAmount: () => ({ - zero: `Ningún gasto está bloqueado. ¿Quieres aprobar todo el informe?`, one: `Este gasto está bloqueado. ¿Quieres aprobarlo de todos modos?`, other: () => `Estos gastos están bloqueados. ¿Quieres aprobarlos de todos modos?`, }), From 426d50e80e2809e4b6e0b42a643574bf0e51eb53 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 1 Aug 2024 18:01:53 +0530 Subject: [PATCH 010/180] Apply suggestions --- src/components/ArchivedReportFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 7e0121a3ae4f..eeee6c68b73f 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -53,7 +53,7 @@ function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}} policyName = lodashEscape(policyName); } - let text; + let text: string; switch (archiveReason) { case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED: text = translate(`reportArchiveReasons.${archiveReason}`, { From 158b4c9bd0a56333bf6134d5772468447fd37fe0 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 5 Aug 2024 22:45:35 +0530 Subject: [PATCH 011/180] Polyfill plural rules for hermes --- src/languages/es.ts | 2 +- src/libs/IntlPolyfill/index.android.ts | 4 ++++ src/libs/IntlPolyfill/index.ios.ts | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 27d4b40380ed..a2d51f58cd89 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3388,7 +3388,7 @@ export default { other: () => 'Eliminar tasas', }), enableRates: () => ({ - one: 'Activar 1 tasa', + one: 'Activar tasa', other: () => 'Activar tasas', }), disableRates: () => ({ diff --git a/src/libs/IntlPolyfill/index.android.ts b/src/libs/IntlPolyfill/index.android.ts index 7a21ae26bfa4..0f852457e3db 100644 --- a/src/libs/IntlPolyfill/index.android.ts +++ b/src/libs/IntlPolyfill/index.android.ts @@ -11,6 +11,10 @@ const intlPolyfill: IntlPolyfill = () => { require('@formatjs/intl-locale/polyfill'); + require('@formatjs/intl-pluralrules/polyfill'); + require('@formatjs/intl-pluralrules/locale-data/en'); + require('@formatjs/intl-pluralrules/locale-data/es'); + polyfillListFormat(); }; diff --git a/src/libs/IntlPolyfill/index.ios.ts b/src/libs/IntlPolyfill/index.ios.ts index 569b666eb434..0ee189b4d329 100644 --- a/src/libs/IntlPolyfill/index.ios.ts +++ b/src/libs/IntlPolyfill/index.ios.ts @@ -16,6 +16,8 @@ const intlPolyfill: IntlPolyfill = () => { // Required to polyfill NumberFormat on iOS // see: https://github.com/facebook/hermes/issues/1172#issuecomment-1776156538 require('@formatjs/intl-pluralrules/polyfill'); + require('@formatjs/intl-pluralrules/locale-data/en'); + require('@formatjs/intl-pluralrules/locale-data/es'); polyfillNumberFormat(); // Required to polyfill DateTimeFormat on iOS From 2c7a93778e27c7e6faa9b5357abbfc87801a8528 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 5 Aug 2024 22:51:47 +0530 Subject: [PATCH 012/180] Adjusting position of code --- src/libs/IntlPolyfill/index.ios.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/IntlPolyfill/index.ios.ts b/src/libs/IntlPolyfill/index.ios.ts index 0ee189b4d329..3a41790aa8b6 100644 --- a/src/libs/IntlPolyfill/index.ios.ts +++ b/src/libs/IntlPolyfill/index.ios.ts @@ -13,11 +13,12 @@ const intlPolyfill: IntlPolyfill = () => { require('@formatjs/intl-locale/polyfill'); - // Required to polyfill NumberFormat on iOS - // see: https://github.com/facebook/hermes/issues/1172#issuecomment-1776156538 require('@formatjs/intl-pluralrules/polyfill'); require('@formatjs/intl-pluralrules/locale-data/en'); require('@formatjs/intl-pluralrules/locale-data/es'); + + // Required to polyfill NumberFormat on iOS + // see: https://github.com/facebook/hermes/issues/1172#issuecomment-1776156538 polyfillNumberFormat(); // Required to polyfill DateTimeFormat on iOS From 2428dfac03e32a62f735c6d829072764e361d4e1 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Thu, 8 Aug 2024 16:54:27 +0530 Subject: [PATCH 013/180] Applying suggestion --- src/components/AddressForm.tsx | 2 +- src/libs/Localize/index.ts | 2 +- src/pages/Search/SearchFiltersDatePage.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx index d6ba5813a101..7ca4cc3273ca 100644 --- a/src/components/AddressForm.tsx +++ b/src/components/AddressForm.tsx @@ -126,7 +126,7 @@ function AddressForm({ } } } else if (!CONST.GENERIC_ZIP_CODE_REGEX.test(values?.zipPostCode?.trim()?.toUpperCase() ?? '')) { - errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat', undefined); + errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat'); } return errors; diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index f1b705398067..70db13c32851 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -45,7 +45,7 @@ function init() { }, {}); } -type PhraseParameters = T extends (arg: infer A) => string ? [A] : T extends (arg: infer A) => PluralFormPhase ? [A, number] : never[]; +type PhraseParameters = T extends (arg?: infer A) => string ? [A?] : T extends (arg: infer A) => string ? [A] : T extends (arg: infer A) => PluralFormPhase ? [A, number] : never[]; type Phrase = TranslationFlatObject[TKey]; /** diff --git a/src/pages/Search/SearchFiltersDatePage.tsx b/src/pages/Search/SearchFiltersDatePage.tsx index e2a465f6fa9e..4bc95aa21351 100644 --- a/src/pages/Search/SearchFiltersDatePage.tsx +++ b/src/pages/Search/SearchFiltersDatePage.tsx @@ -53,7 +53,7 @@ function SearchFiltersDatePage() { Date: Thu, 8 Aug 2024 21:12:35 +0530 Subject: [PATCH 014/180] Adjusting code --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5bcdfdc1cb14..bccd8bdfba18 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -731,7 +731,6 @@ export default { invalidDomainError: 'You have entered an invalid domain. To continue, please enter a valid domain.', publicDomainError: 'You have entered a public domain. To continue, please enter a private domain.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ - zero: `0 expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, one: `1 expense${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, other: (count: number) => `${count} expenses${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pending` : ''}`, }), diff --git a/src/languages/es.ts b/src/languages/es.ts index bfab43641f85..61cd8baba543 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -724,7 +724,6 @@ export default { invalidDomainError: 'Ha introducido un dominio no válido. Para continuar, introduzca un dominio válido.', publicDomainError: 'Ha introducido un dominio público. Para continuar, introduzca un dominio privado.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => ({ - zero: `0 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, one: `1 gasto${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, other: (count: number) => `${count} gastos${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}${pendingReceipts > 0 ? `, ${pendingReceipts} pendiente` : ''}`, }), From b38931602767fe3bcb2d222957343aefb02886a6 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:10:34 +0530 Subject: [PATCH 015/180] Apply suggestions from code review --- src/libs/IntlPolyfill/index.android.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/IntlPolyfill/index.android.ts b/src/libs/IntlPolyfill/index.android.ts index 84761382b3fd..e6ab02d15c25 100644 --- a/src/libs/IntlPolyfill/index.android.ts +++ b/src/libs/IntlPolyfill/index.android.ts @@ -11,7 +11,7 @@ const intlPolyfill: IntlPolyfill = () => { require('@formatjs/intl-locale/polyfill-force'); - require('@formatjs/intl-pluralrules/polyfill'); + require('@formatjs/intl-pluralrules/polyfill-force'); require('@formatjs/intl-pluralrules/locale-data/en'); require('@formatjs/intl-pluralrules/locale-data/es'); From 6dfb0cd941be2203e0a1b6fc1ae6e04c6439b95b Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:16:54 +0530 Subject: [PATCH 016/180] Apply suggestions from code review --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 495bec63a5d7..71a26d93c623 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -866,7 +866,6 @@ export default { confirmPay: 'Confirm payment amount', confirmPayAmount: "Pay what's not on hold, or pay the entire report.", confirmPayAllHoldAmount: () => ({ - zero: `This expense is on hold. Do you want to pay anyway?`, one: `This expense is on hold. Do you want to pay anyway?`, other: () => `These expenses are on hold. Do you want to pay anyway?`, }), diff --git a/src/languages/es.ts b/src/languages/es.ts index e1187603afd8..92a23c0afb49 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -859,7 +859,6 @@ export default { confirmPay: 'Confirmar importe de pago', confirmPayAmount: 'Paga lo que no está bloqueado, o paga el informe completo.', confirmPayAllHoldAmount: () => ({ - zero: `Ningún gasto está bloqueado. ¿Quieres pagar todo el informe?`, one: `Este gasto está bloqueado. ¿Quieres pagarlo de todos modos?`, other: () => `Estos gastos están bloqueados. ¿Quieres pagarlos de todos modos?`, }), From 895108439ea8f158373287d987b978fa37e5a4ca Mon Sep 17 00:00:00 2001 From: Shubham Agrawal <58412969+shubham1206agra@users.noreply.github.com> Date: Fri, 23 Aug 2024 08:41:56 +0530 Subject: [PATCH 017/180] Apply suggestions from code review --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 16571e7e35e8..4a6b86830587 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -754,7 +754,7 @@ export default { invalidDomainError: 'You have entered an invalid domain. To continue, please enter a valid domain.', publicDomainError: 'You have entered a public domain. To continue, please enter a private domain.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => { - const statusText = []; + const statusText: string[] = []; if (scanningReceipts > 0) { statusText.push(`${scanningReceipts} scanning`); } diff --git a/src/languages/es.ts b/src/languages/es.ts index e670a003648a..5f753962ff34 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -745,7 +745,7 @@ export default { invalidDomainError: 'Ha introducido un dominio no válido. Para continuar, introduzca un dominio válido.', publicDomainError: 'Ha introducido un dominio público. Para continuar, introduzca un dominio privado.', expenseCount: ({scanningReceipts = 0, pendingReceipts = 0}: RequestCountParams) => { - const statusText = []; + const statusText: string[] = []; if (scanningReceipts > 0) { statusText.push(`${scanningReceipts} escaneando`); } From 29124ea861c38f311a46da8b8f79b19ecafefedd Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Fri, 23 Aug 2024 09:22:10 +0530 Subject: [PATCH 018/180] Post merge fixes --- src/languages/en.ts | 17 ++++++++++------- src/languages/es.ts | 17 ++++++++++------- src/languages/types.ts | 19 +++++++++++++++++++ src/libs/ReportActionsUtils.ts | 6 +++--- src/pages/Search/AdvancedSearchFilters.tsx | 5 ++++- .../report/ContextMenu/ContextMenuActions.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- 7 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 4a6b86830587..6bd72a3790f7 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -69,12 +69,14 @@ import type { ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, ReportIntegrationMessageTranslationParams, + ReportMemberRoleParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, + SearchFilterAmountBetweenParams, SetTheDistanceParams, SetTheRequestParams, SettledAfterAddedBankAccountParams, @@ -98,6 +100,7 @@ import type { UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, + UpdateReportMemberRoleParams, UsePlusButtonParams, UserIsAlreadyMemberParams, UserSplitParams, @@ -763,8 +766,8 @@ export default { } return { one: statusText.length > 0 ? `1 expense (${statusText.join(', ')})` : `1 expense`, - other: (count: number) => statusText.length > 0 ? `${count} expenses (${statusText.join(', ')})` : `${count} expenses`, - } + other: (count: number) => (statusText.length > 0 ? `${count} expenses (${statusText.join(', ')})` : `${count} expenses`), + }; }, deleteExpense: () => ({ one: `Delete expense`, @@ -3786,7 +3789,7 @@ export default { amount: { lessThan: (amount?: string) => `Less than ${amount ?? ''}`, greaterThan: (amount?: string) => `Greater than ${amount ?? ''}`, - between: (greaterThan: string, lessThan: string) => `Between ${greaterThan} and ${lessThan}`, + between: ({greaterThan, lessThan}: SearchFilterAmountBetweenParams) => `Between ${greaterThan} and ${lessThan}`, }, }, expenseType: 'Expense type', @@ -3921,10 +3924,10 @@ export default { stripePaid: ({amount, currency}: StripePaidParams) => `paid ${currency}${amount}`, takeControl: `took control`, unapproved: ({amount, currency}: UnapprovedParams) => `unapproved ${currency}${amount}`, - integrationSyncFailed: (label: string, errorMessage: string) => `failed to sync with ${label} ("${errorMessage}")`, - addEmployee: (email: string, role: string) => `added ${email} as ${role === 'user' ? 'member' : 'admin'}`, - updateRole: (email: string, currentRole: string, newRole: string) => `updated the role of ${email} from ${currentRole} to ${newRole}`, - removeMember: (email: string, role: string) => `removed ${role} ${email}`, + integrationSyncFailed: ({label, errorMessage}: ReportIntegrationMessageTranslationParams) => `failed to sync with ${label} ("${errorMessage}")`, + addEmployee: ({email, role}: ReportMemberRoleParams) => `added ${email} as ${role === 'user' ? 'member' : 'admin'}`, + updateRole: ({email, currentRole, newRole}: UpdateReportMemberRoleParams) => `updated the role of ${email} from ${currentRole} to ${newRole}`, + removeMember: ({email, role}: ReportMemberRoleParams) => `removed ${role} ${email}`, }, }, }, diff --git a/src/languages/es.ts b/src/languages/es.ts index 5f753962ff34..a12fd53009c0 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -67,12 +67,14 @@ import type { ReportArchiveReasonsPolicyDeletedParams, ReportArchiveReasonsRemovedFromPolicyParams, ReportIntegrationMessageTranslationParams, + ReportMemberRoleParams, RequestAmountParams, RequestCountParams, RequestedAmountMessageParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, RoomRenamedToParams, + SearchFilterAmountBetweenParams, SetTheDistanceParams, SetTheRequestParams, SettledAfterAddedBankAccountParams, @@ -95,6 +97,7 @@ import type { UntilTimeParams, UpdatedTheDistanceParams, UpdatedTheRequestParams, + UpdateReportMemberRoleParams, UsePlusButtonParams, UserIsAlreadyMemberParams, UserSplitParams, @@ -754,8 +757,8 @@ export default { } return { one: statusText.length > 0 ? `1 gasto (${statusText.join(', ')})` : `1 gasto`, - other: (count: number) => statusText.length > 0 ? `${count} gastos (${statusText.join(', ')})` : `${count} gastos`, - } + other: (count: number) => (statusText.length > 0 ? `${count} gastos (${statusText.join(', ')})` : `${count} gastos`), + }; }, deleteExpense: () => ({ one: `Eliminar gasto`, @@ -3826,7 +3829,7 @@ export default { amount: { lessThan: (amount?: string) => `Menos de ${amount ?? ''}`, greaterThan: (amount?: string) => `Más que ${amount ?? ''}`, - between: (greaterThan: string, lessThan: string) => `Entre ${greaterThan} y ${lessThan}`, + between: ({greaterThan, lessThan}: SearchFilterAmountBetweenParams) => `Entre ${greaterThan} y ${lessThan}`, }, }, expenseType: 'Tipo de gasto', @@ -3962,11 +3965,11 @@ export default { stripePaid: ({amount, currency}: StripePaidParams) => `pagado ${currency}${amount}`, takeControl: `tomó el control`, unapproved: ({amount, currency}: UnapprovedParams) => `no aprobado ${currency}${amount}`, - integrationSyncFailed: (label: string, errorMessage: string) => `no se pudo sincronizar con ${label} ("${errorMessage}")`, - addEmployee: (email: string, role: string) => `agregó a ${email} como ${role === 'user' ? 'miembro' : 'administrador'}`, - updateRole: (email: string, currentRole: string, newRole: string) => + integrationSyncFailed: ({label, errorMessage}: ReportIntegrationMessageTranslationParams) => `no se pudo sincronizar con ${label} ("${errorMessage}")`, + addEmployee: ({email, role}: ReportMemberRoleParams) => `agregó a ${email} como ${role === 'user' ? 'miembro' : 'administrador'}`, + updateRole: ({email, currentRole, newRole}: UpdateReportMemberRoleParams) => `actualicé el rol ${email} de ${currentRole === 'user' ? 'miembro' : 'administrador'} a ${newRole === 'user' ? 'miembro' : 'administrador'}`, - removeMember: (email: string, role: string) => `eliminado ${role === 'user' ? 'miembro' : 'administrador'} ${email}`, + removeMember: ({email, role}: ReportMemberRoleParams) => `eliminado ${role === 'user' ? 'miembro' : 'administrador'} ${email}`, }, }, }, diff --git a/src/languages/types.ts b/src/languages/types.ts index 444a97d26249..b95122979df8 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -386,6 +386,22 @@ type ApprovalWorkflowErrorParams = { name2: string; }; +type SearchFilterAmountBetweenParams = { + greaterThan: string; + lessThan: string; +}; + +type ReportMemberRoleParams = { + email: string; + role: string; +}; + +type UpdateReportMemberRoleParams = { + email: string; + currentRole: string; + newRole: string; +}; + export type { AddressLineParams, AdminCanceledRequestParams, @@ -512,4 +528,7 @@ export type { StatementPageTitleTranslationParams, ReportIntegrationMessageTranslationParams, ApprovalWorkflowErrorParams, + SearchFilterAmountBetweenParams, + ReportMemberRoleParams, + UpdateReportMemberRoleParams, }; diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 5fdfec3fd96b..008a5a4be225 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1613,7 +1613,7 @@ function getPolicyChangeLogAddEmployeeMessage(reportAction: OnyxInputOrEntry): reportAction is ReportAction { @@ -1628,7 +1628,7 @@ function getPolicyChangeLogChangeRoleMessage(reportAction: OnyxInputOrEntry>) { diff --git a/src/pages/Search/AdvancedSearchFilters.tsx b/src/pages/Search/AdvancedSearchFilters.tsx index 5143a2d70008..d0929e33db52 100644 --- a/src/pages/Search/AdvancedSearchFilters.tsx +++ b/src/pages/Search/AdvancedSearchFilters.tsx @@ -69,7 +69,10 @@ function getFilterDisplayTitle(filters: Partial, fiel if (fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { const {lessThan, greaterThan} = filters; if (lessThan && greaterThan) { - return translate('search.filters.amount.between', convertToDisplayStringWithoutCurrency(Number(greaterThan)), convertToDisplayStringWithoutCurrency(Number(lessThan))); + return translate('search.filters.amount.between', { + greaterThan: convertToDisplayStringWithoutCurrency(Number(greaterThan)), + lessThan: convertToDisplayStringWithoutCurrency(Number(lessThan)), + }); } if (lessThan) { return translate('search.filters.amount.lessThan', convertToDisplayStringWithoutCurrency(Number(lessThan))); diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index db7e482a0457..4174557c2e05 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -472,7 +472,7 @@ const ContextMenuActions: ContextMenuAction[] = [ setClipboardMessage(ReportActionsUtils.getPolicyChangeLogDeleteMemberMessage(reportAction)); } else if (ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED)) { const {label, errorMessage} = ReportActionsUtils.getOriginalMessage(reportAction) ?? {label: '', errorMessage: ''}; - setClipboardMessage(Localize.translateLocal('report.actions.type.integrationSyncFailed', label, errorMessage)); + setClipboardMessage(Localize.translateLocal('report.actions.type.integrationSyncFailed', {label, errorMessage})); } else if (content) { setClipboardMessage( content.replace(/()(.*?)(<\/mention-user>)/gi, (match, openTag: string, innerContent: string, closeTag: string): string => { diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index e4dab8518eb2..012fb84b5bc5 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -675,7 +675,7 @@ function ReportActionItem({ children = ; } else if (ReportActionsUtils.isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED)) { const {label, errorMessage} = ReportActionsUtils.getOriginalMessage(action) ?? {label: '', errorMessage: ''}; - children = ; + children = ; } else { const hasBeenFlagged = ![CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING].some((item) => item === moderationDecision) && From c337d6f6d0557ff68f7ceefbb83f2c1d3e931285 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 2 Sep 2024 17:07:23 +0200 Subject: [PATCH 019/180] remove focusing durty --- .../ReportActionCompose.tsx | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 72e89c8de013..f6cf812fd908 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -233,22 +233,8 @@ function ReportActionCompose({ return translate('reportActionCompose.writeSomething'); }, [includesConcierge, translate, userBlockedFromConcierge, conciergePlaceholderRandomIndex]); - const focus = () => { - if (composerRef.current === null) { - return; - } - composerRef.current?.focus(true); - }; - const isKeyboardVisibleWhenShowingModalRef = useRef(false); const isNextModalWillOpenRef = useRef(false); - const restoreKeyboardState = useCallback(() => { - if (!isKeyboardVisibleWhenShowingModalRef.current || isNextModalWillOpenRef.current) { - return; - } - focus(); - isKeyboardVisibleWhenShowingModalRef.current = false; - }, []); const containerRef = useRef(null); const measureContainer = useCallback( @@ -298,8 +284,7 @@ function ReportActionCompose({ const onAttachmentPreviewClose = useCallback(() => { updateShouldShowSuggestionMenuToFalse(); setIsAttachmentPreviewActive(false); - restoreKeyboardState(); - }, [updateShouldShowSuggestionMenuToFalse, restoreKeyboardState]); + }, [updateShouldShowSuggestionMenuToFalse]); /** * Add a new comment to this chat @@ -490,11 +475,18 @@ function ReportActionCompose({ isMenuVisible={isMenuVisible} onTriggerAttachmentPicker={onTriggerAttachmentPicker} raiseIsScrollLikelyLayoutTriggered={raiseIsScrollLikelyLayoutTriggered} + // TODO: Remove onCanceledAttachmentPicker={() => { - isNextModalWillOpenRef.current = false; - restoreKeyboardState(); + // isNextModalWillOpenRef.current = false; + isNextModalWillOpenRef.current = true; + console.debug('[TEST] onCanceledAttachmentPicker'); + // restoreKeyboardState(); + }} + // TODO: Remove + onMenuClosed={() => { + console.debug('[TEST] onMenuClosed'); + // restoreKeyboardState(); }} - onMenuClosed={restoreKeyboardState} onAddActionPressed={onAddActionPressed} onItemSelected={onItemSelected} actionButtonRef={actionButtonRef} @@ -559,11 +551,13 @@ function ReportActionCompose({ {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( { if (isNavigating) { return; } - focus(); + console.debug('[TEST] ReportActionCompose focus 3'); + // focus(); }} onEmojiSelected={(...args) => composerRef.current?.replaceSelectionWithText(...args)} emojiPickerID={report?.reportID} From 6ff977ca0c160baf37ec9181852a0ce063fff4b1 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 3 Sep 2024 13:53:53 +0200 Subject: [PATCH 020/180] clear redundant code --- .../EmojiPicker/EmojiPickerButton.tsx | 4 ++-- .../AttachmentPickerWithMenuItems.tsx | 6 +++--- .../ReportActionCompose.tsx | 20 ------------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 6e0944e5a913..a702a554cee3 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -24,12 +24,12 @@ type EmojiPickerButtonProps = { /** Emoji popup anchor offset shift vertical */ shiftVertical?: number; - onModalHide: EmojiPickerAction.OnModalHideValue; + onModalHide?: EmojiPickerAction.OnModalHideValue; onEmojiSelected: EmojiPickerAction.OnEmojiSelected; }; -function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shiftVertical = 0, onModalHide, onEmojiSelected}: EmojiPickerButtonProps) { +function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shiftVertical = 0, onModalHide = () => {}, onEmojiSelected}: EmojiPickerButtonProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const emojiPopoverAnchor = useRef(null); diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 6e3c3a48de74..ea23759edbf2 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -70,10 +70,10 @@ type AttachmentPickerWithMenuItemsProps = AttachmentPickerWithMenuItemsOnyxProps onTriggerAttachmentPicker: () => void; /** Called when cancelling the attachment picker */ - onCanceledAttachmentPicker: () => void; + onCanceledAttachmentPicker?: () => void; /** Called when the menu with the items is closed after it was open */ - onMenuClosed: () => void; + onMenuClosed?: () => void; /** Called when the add action button is pressed */ onAddActionPressed: () => void; @@ -187,7 +187,7 @@ function AttachmentPickerWithMenuItems({ const onPopoverMenuClose = () => { setMenuVisibility(false); - onMenuClosed(); + onMenuClosed?.(); }; const prevIsFocused = usePrevious(isFocused); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index f6cf812fd908..2632fdf4f719 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -475,18 +475,6 @@ function ReportActionCompose({ isMenuVisible={isMenuVisible} onTriggerAttachmentPicker={onTriggerAttachmentPicker} raiseIsScrollLikelyLayoutTriggered={raiseIsScrollLikelyLayoutTriggered} - // TODO: Remove - onCanceledAttachmentPicker={() => { - // isNextModalWillOpenRef.current = false; - isNextModalWillOpenRef.current = true; - console.debug('[TEST] onCanceledAttachmentPicker'); - // restoreKeyboardState(); - }} - // TODO: Remove - onMenuClosed={() => { - console.debug('[TEST] onMenuClosed'); - // restoreKeyboardState(); - }} onAddActionPressed={onAddActionPressed} onItemSelected={onItemSelected} actionButtonRef={actionButtonRef} @@ -551,14 +539,6 @@ function ReportActionCompose({ {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( { - if (isNavigating) { - return; - } - console.debug('[TEST] ReportActionCompose focus 3'); - // focus(); - }} onEmojiSelected={(...args) => composerRef.current?.replaceSelectionWithText(...args)} emojiPickerID={report?.reportID} shiftVertical={emojiShiftVertical} From 29bd0658236b5c6fcd28857ac965c78cb0fd558f Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 3 Sep 2024 14:41:50 +0200 Subject: [PATCH 021/180] do not focus on web --- .../ComposerWithSuggestions/ComposerWithSuggestions.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 085aa2f7db9a..bbb56eee0a75 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -661,9 +661,7 @@ function ComposerWithSuggestions( if (editFocused) { InputFocus.inputFocusChange(false); - return; } - focus(true); }, [focus, prevIsFocused, editFocused, prevIsModalVisible, isFocused, modal?.isVisible, isNextModalWillOpenRef, shouldAutoFocus]); useEffect(() => { From 4722de5a087b48f28ebc8c49ed9ab4ffe6f265b0 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Tue, 3 Sep 2024 19:24:27 +0530 Subject: [PATCH 022/180] Post merge fixes --- src/languages/en.ts | 6 ++++-- src/languages/es.ts | 6 ++++-- src/pages/workspace/rules/IndividualExpenseRulesSection.tsx | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index b5bb2e89e29b..7ee11c73ca1e 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -20,7 +20,6 @@ import type { ChangeTypeParams, CharacterLimitParams, CompanyCardFeedNameParams, - ConfirmHoldExpenseParams, ConfirmThatParams, CustomersOrJobsLabelTranslationParams, DateShouldBeAfterParams, @@ -3705,7 +3704,10 @@ export default { maxAge: 'Max age', maxExpenseAge: 'Max expense age', maxExpenseAgeDescription: 'Flag spend older than a specific number of days.', - maxExpenseAgeDays: (age: number) => `${age} ${Str.pluralize('day', 'days', age)}`, + maxExpenseAgeDays: () => ({ + one: `1 day`, + other: (count: number) => `${count} days`, + }), billableDefault: 'Billable default', billableDefaultDescription: 'Choose whether cash and credit card expenses should be billable by default. Billable expenses are enabled or disabled in', billable: 'Billable', diff --git a/src/languages/es.ts b/src/languages/es.ts index 84e106476ed0..154f6bf56d58 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -17,7 +17,6 @@ import type { ChangeTypeParams, CharacterLimitParams, CompanyCardFeedNameParams, - ConfirmHoldExpenseParams, ConfirmThatParams, CustomersOrJobsLabelTranslationParams, DateShouldBeAfterParams, @@ -3747,7 +3746,10 @@ export default { maxAge: 'Antigüedad máxima', maxExpenseAge: 'Antigüedad máxima de los gastos', maxExpenseAgeDescription: 'Marca los gastos de más de un número determinado de días.', - maxExpenseAgeDays: (age: number) => `${age} ${Str.pluralize('día', 'días', age)}`, + maxExpenseAgeDays: () => ({ + one: `1 día`, + other: (count: number) => `${count} días`, + }), billableDefault: 'Valor predeterminado facturable', billableDefaultDescription: 'Elige si los gastos en efectivo y con tarjeta de crédito deben ser facturables por defecto. Los gastos facturables se activan o desactivan en', billable: 'Facturable', diff --git a/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx b/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx index 78dbcb3b16c8..a8a7f0652a4d 100644 --- a/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx +++ b/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx @@ -108,7 +108,7 @@ function IndividualExpenseRulesSection({policyID}: IndividualExpenseRulesSection return ''; } - return translate('workspace.rules.individualExpenseRules.maxExpenseAgeDays', policy?.maxExpenseAge); + return translate('workspace.rules.individualExpenseRules.maxExpenseAgeDays', undefined, policy?.maxExpenseAge); }, [policy?.maxExpenseAge, translate]); const billableModeText = translate(`workspace.rules.individualExpenseRules.${policy?.defaultBillable ? 'billable' : 'nonBillable'}`); From a2e0bbf6b5677524ddd60ce2617de75871a6f4c3 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Thu, 5 Sep 2024 02:01:18 +0530 Subject: [PATCH 023/180] add delegate prop to OriginalMessage --- src/types/onyx/OriginalMessage.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index fcab54b470bf..557ec4c39867 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -68,6 +68,9 @@ type OriginalMessageIOU = { /** Collection of accountIDs of users mentioned in message */ whisperedTo?: number[]; + + /** Email of the delegate */ + delegate: string; }; /** Names of moderation decisions */ @@ -124,6 +127,9 @@ type OriginalMessageAddComment = { /** List accountIDs are mentioned in message */ mentionedAccountIDs?: number[]; + + /** Email of the delegate */ + delegate: string; }; /** Model of `actionable mention whisper` report action */ @@ -157,6 +163,9 @@ type OriginalMessageSubmitted = { /** Report ID of the expense */ expenseReportID: string; + + /** Email of the delegate */ + email: string; }; /** Model of `closed` report action */ @@ -430,6 +439,9 @@ type OriginalMessageApproved = { /** Report ID of the expense */ expenseReportID: string; + + /** Email of the delegate */ + delegate: string; }; /** Model of `forwarded` report action */ @@ -489,6 +501,9 @@ type OriginalMessageUnapproved = { /** Report ID of the expense */ expenseReportID: string; + + /** Email of the delegate */ + delegate: string; }; /** From 27d7bcac23167ae490a191c97201872a76bd3233 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Thu, 5 Sep 2024 02:01:29 +0530 Subject: [PATCH 024/180] add delegate prop to OriginalMessage --- src/types/onyx/OriginalMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 557ec4c39867..e55c6a509222 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -165,7 +165,7 @@ type OriginalMessageSubmitted = { expenseReportID: string; /** Email of the delegate */ - email: string; + email:string }; /** Model of `closed` report action */ From deca9750ec4cc76df385ae1c084bb9d241852ded Mon Sep 17 00:00:00 2001 From: Gandalf Date: Thu, 5 Sep 2024 02:20:48 +0530 Subject: [PATCH 025/180] add delegate in original message --- src/libs/ReportUtils.ts | 25 +++++++++++++++++++++---- src/types/onyx/OriginalMessage.ts | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7b226b2e5c8e..2a4a87ff21c4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4091,6 +4091,7 @@ function getPolicyDescriptionText(policy: OnyxEntry): string { } function buildOptimisticAddCommentReportAction( + delegate: string, text?: string, file?: FileObject, actorAccountID?: number, @@ -4136,6 +4137,7 @@ function buildOptimisticAddCommentReportAction( originalMessage: { html: htmlForNewComment, whisperedTo: [], + delegate: delegate, }, isFirstItem: false, isAttachmentOnly, @@ -4197,6 +4199,7 @@ function updateOptimisticParentReportAction(parentReportAction: OnyxEntry, @@ -4547,6 +4553,7 @@ function buildOptimisticIOUReportAction( isOwnPolicyExpenseChat = false, created = DateUtils.getDBTime(), linkedExpenseReportAction?: OnyxEntry, + delegate = '', ): OptimisticIOUReportAction { const IOUReportID = iouReportID || generateReportID(); @@ -4557,6 +4564,7 @@ function buildOptimisticIOUReportAction( IOUTransactionID: transactionID, IOUReportID, type, + delegate, }; if (type === CONST.IOU.REPORT_ACTION_TYPE.PAY) { @@ -4616,11 +4624,12 @@ function buildOptimisticIOUReportAction( /** * Builds an optimistic APPROVED report action with a randomly generated reportActionID. */ -function buildOptimisticApprovedReportAction(amount: number, currency: string, expenseReportID: string): OptimisticApprovedReportAction { +function buildOptimisticApprovedReportAction(amount: number, currency: string, expenseReportID: string, delegate: string): OptimisticApprovedReportAction { const originalMessage = { amount, currency, expenseReportID, + delegate, }; return { @@ -4648,7 +4657,7 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e /** * Builds an optimistic APPROVED report action with a randomly generated reportActionID. */ -function buildOptimisticUnapprovedReportAction(amount: number, currency: string, expenseReportID: string): OptimisticUnapprovedReportAction { +function buildOptimisticUnapprovedReportAction(amount: number, currency: string, expenseReportID: string, delegate: string): OptimisticUnapprovedReportAction { return { actionName: CONST.REPORT.ACTIONS.TYPE.UNAPPROVED, actorAccountID: currentUserAccountID, @@ -4659,6 +4668,7 @@ function buildOptimisticUnapprovedReportAction(amount: number, currency: string, amount, currency, expenseReportID, + delegate, }, message: getIOUReportActionMessage(expenseReportID, CONST.REPORT.ACTIONS.TYPE.UNAPPROVED, Math.abs(amount), '', currency), person: [ @@ -4721,11 +4731,18 @@ function buildOptimisticMovedReportAction(fromPolicyID: string, toPolicyID: stri * Builds an optimistic SUBMITTED report action with a randomly generated reportActionID. * */ -function buildOptimisticSubmittedReportAction(amount: number, currency: string, expenseReportID: string, adminAccountID: number | undefined): OptimisticSubmittedReportAction { +function buildOptimisticSubmittedReportAction( + amount: number, + currency: string, + expenseReportID: string, + adminAccountID: number | undefined, + delegate: string, +): OptimisticSubmittedReportAction { const originalMessage = { amount, currency, expenseReportID, + delegate, }; return { diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index e55c6a509222..557ec4c39867 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -165,7 +165,7 @@ type OriginalMessageSubmitted = { expenseReportID: string; /** Email of the delegate */ - email:string + email: string; }; /** Model of `closed` report action */ From daa0c9cd9574caaa45d4dba561b75767e1a38c8d Mon Sep 17 00:00:00 2001 From: Gandalf Date: Thu, 5 Sep 2024 02:25:34 +0530 Subject: [PATCH 026/180] fix OriginalMessageSubmitted typo --- src/types/onyx/OriginalMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 557ec4c39867..ad6280547284 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -165,7 +165,7 @@ type OriginalMessageSubmitted = { expenseReportID: string; /** Email of the delegate */ - email: string; + delegate: string; }; /** Model of `closed` report action */ From c18f61f9c8b03ef77b85bb6ce4c81f1906082781 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Thu, 5 Sep 2024 02:45:32 +0530 Subject: [PATCH 027/180] update optimistic actions to include delegate email --- src/components/DelegateNoAccessModal.tsx | 38 ++++++++++++++++++++++++ src/hooks/useDelegateUserDetails.ts | 18 +++++++++++ src/libs/actions/IOU.ts | 9 ++++-- src/libs/actions/Report.ts | 13 ++++---- 4 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/components/DelegateNoAccessModal.tsx create mode 100644 src/hooks/useDelegateUserDetails.ts diff --git a/src/components/DelegateNoAccessModal.tsx b/src/components/DelegateNoAccessModal.tsx new file mode 100644 index 000000000000..8bb61b783fd1 --- /dev/null +++ b/src/components/DelegateNoAccessModal.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import useLocalize from '@hooks/useLocalize'; +import CONST from '@src/CONST'; +import ConfirmModal from './ConfirmModal'; +import Text from './Text'; +import TextLink from './TextLink'; + +type DelegateNoAccessModalProps = { + isNoDelegateAccessMenuVisible: boolean; + onConfirm: () => void; + delegatorEmail: string; +}; + +export default function DelegateNoAccessModal({isNoDelegateAccessMenuVisible = false, onConfirm, delegatorEmail = ''}: DelegateNoAccessModalProps) { + const {translate} = useLocalize(); + const basicnoDelegateAccessPromptStart = translate('delegate.notAllowedMessageStart', {accountOwnerEmail: delegatorEmail}); + const basicnoDelegateAccessHyperLinked = translate('delegate.notAllowedMessageHyperLinked'); + const basicnoDelegateAccessPromptEnd = translate('delegate.notAllowedMessageEnd'); + + const delegateNoAccessPrompt = ( + + {basicnoDelegateAccessPromptStart} + {basicnoDelegateAccessHyperLinked} + {basicnoDelegateAccessPromptEnd} + + ); + + return ( + + ); +} \ No newline at end of file diff --git a/src/hooks/useDelegateUserDetails.ts b/src/hooks/useDelegateUserDetails.ts new file mode 100644 index 000000000000..79b10e438b73 --- /dev/null +++ b/src/hooks/useDelegateUserDetails.ts @@ -0,0 +1,18 @@ +import {useOnyx} from 'react-native-onyx'; +import AccountUtils from '@libs/AccountUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; + +function useDelegateUserDetails() { + const currentUserDeatils = useCurrentUserPersonalDetails(); + const [currentUserAccountDetails] = useOnyx(ONYXKEYS.ACCOUNT); + const delegatorEmail = currentUserDeatils?.login; + const delegateEmail = currentUserAccountDetails?.delegatedAccess?.delegate ?? ''; + + return { + delegatorEmail, + delegateEmail + }; +} + +export default useDelegateUserDetails; \ No newline at end of file diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 29d481737790..227970964637 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4,6 +4,7 @@ import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate} import Onyx from 'react-native-onyx'; import type {PartialDeep, SetRequired, ValueOf} from 'type-fest'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; +import useDelegateUserDetails from '@hooks/useDelegateUserDetails' import * as API from '@libs/API'; import type { ApproveMoneyRequestParams, @@ -272,6 +273,8 @@ Onyx.connect({ callback: (value) => (activePolicyID = value), }); +const { delegateEmail } = useDelegateUserDetails() + /** * Find the report preview action from given chat report and iou report */ @@ -6912,7 +6915,7 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?: if (hasHeldExpenses && !full && !!expenseReport?.unheldTotal) { total = expenseReport?.unheldTotal; } - const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1'); + const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1', delegateEmail); const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport?.ownerAccountID ?? -1, expenseReport?.total ?? 0); @@ -7068,7 +7071,7 @@ function unapproveExpenseReport(expenseReport: OnyxEntry) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - const optimisticUnapprovedReportAction = ReportUtils.buildOptimisticUnapprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID); + const optimisticUnapprovedReportAction = ReportUtils.buildOptimisticUnapprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, delegateEmail); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.SUBMITTED); const optimisticReportActionData: OnyxUpdate = { @@ -7162,7 +7165,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { const isCurrentUserManager = currentUserPersonalDetails?.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails?.accountID : undefined; - const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID); + const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID, delegateEmail); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED); const optimisticData: OnyxUpdate[] = !isSubmitAndClosePolicy diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index bd31d3832605..ba6c9ad77c09 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5,6 +5,7 @@ import isEmpty from 'lodash/isEmpty'; import {DeviceEventEmitter, InteractionManager, Linking} from 'react-native'; import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; +import useDelegateUserDetails from '@hooks/useDelegateUserDetails' import type {PartialDeep, ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; @@ -280,6 +281,8 @@ registerPaginationConfig({ isLastItem: (reportAction) => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED, }); +const { delegateEmail } = useDelegateUserDetails() + function clearGroupChat() { Onyx.set(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, null); } @@ -453,7 +456,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { let commandName: typeof WRITE_COMMANDS.ADD_COMMENT | typeof WRITE_COMMANDS.ADD_ATTACHMENT | typeof WRITE_COMMANDS.ADD_TEXT_AND_ATTACHMENT = WRITE_COMMANDS.ADD_COMMENT; if (text && !file) { - const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(text, undefined, undefined, undefined, undefined, reportID); + const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, text, undefined, undefined, undefined, undefined, reportID); reportCommentAction = reportComment.reportAction; reportCommentText = reportComment.commentText; } @@ -462,7 +465,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { // When we are adding an attachment we will call AddAttachment. // It supports sending an attachment with an optional comment and AddComment supports adding a single text comment only. commandName = WRITE_COMMANDS.ADD_ATTACHMENT; - const attachment = ReportUtils.buildOptimisticAddCommentReportAction(text, file, undefined, undefined, undefined, reportID); + const attachment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, text, file, undefined, undefined, undefined, reportID); attachmentAction = attachment.reportAction; } @@ -3340,7 +3343,7 @@ function completeOnboarding( const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Introductory message - const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); + const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; const introductionMessage: AddCommentOrAttachementParams = { reportID: targetChatReportID, @@ -3349,7 +3352,7 @@ function completeOnboarding( }; // Text message - const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID, 1); + const textComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, data.message, undefined, actorAccountID, 1); const textCommentAction: OptimisticAddCommentReportAction = textComment.reportAction; const textMessage: AddCommentOrAttachementParams = { reportID: targetChatReportID, @@ -3360,7 +3363,7 @@ function completeOnboarding( let videoCommentAction: OptimisticAddCommentReportAction | null = null; let videoMessage: AddCommentOrAttachementParams | null = null; if (data.video) { - const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 2); + const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 2); videoCommentAction = videoComment.reportAction; videoMessage = { reportID: targetChatReportID, From f45020ad800eae075e97092c6f00e7475c6b10a7 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Thu, 5 Sep 2024 02:46:36 +0530 Subject: [PATCH 028/180] remove delegate component --- src/components/DelegateNoAccessModal.tsx | 38 ------------------------ 1 file changed, 38 deletions(-) delete mode 100644 src/components/DelegateNoAccessModal.tsx diff --git a/src/components/DelegateNoAccessModal.tsx b/src/components/DelegateNoAccessModal.tsx deleted file mode 100644 index 8bb61b783fd1..000000000000 --- a/src/components/DelegateNoAccessModal.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import useLocalize from '@hooks/useLocalize'; -import CONST from '@src/CONST'; -import ConfirmModal from './ConfirmModal'; -import Text from './Text'; -import TextLink from './TextLink'; - -type DelegateNoAccessModalProps = { - isNoDelegateAccessMenuVisible: boolean; - onConfirm: () => void; - delegatorEmail: string; -}; - -export default function DelegateNoAccessModal({isNoDelegateAccessMenuVisible = false, onConfirm, delegatorEmail = ''}: DelegateNoAccessModalProps) { - const {translate} = useLocalize(); - const basicnoDelegateAccessPromptStart = translate('delegate.notAllowedMessageStart', {accountOwnerEmail: delegatorEmail}); - const basicnoDelegateAccessHyperLinked = translate('delegate.notAllowedMessageHyperLinked'); - const basicnoDelegateAccessPromptEnd = translate('delegate.notAllowedMessageEnd'); - - const delegateNoAccessPrompt = ( - - {basicnoDelegateAccessPromptStart} - {basicnoDelegateAccessHyperLinked} - {basicnoDelegateAccessPromptEnd} - - ); - - return ( - - ); -} \ No newline at end of file From dd7947d069ff499fe94d6859ffe1e62ba1fcfbc9 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Thu, 5 Sep 2024 02:54:00 +0530 Subject: [PATCH 029/180] fix unit test --- tests/actions/IOUTest.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 8a7095af6f7f..dd94fbd76aa5 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -417,6 +417,7 @@ describe('actions/IOU', () => { const comment = 'Giv money plz'; const chatReportID = '1234'; const iouReportID = '5678'; + const delegate = '' let chatReport: OnyxEntry = { reportID: chatReportID, type: CONST.REPORT.TYPE.CHAT, @@ -460,6 +461,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, participantAccountIDs: [RORY_ACCOUNT_ID, CARLOS_ACCOUNT_ID], + delegate: delegate, }, }; let newIOUAction: OnyxEntry>; From 6cb68e38ae3475f66049c1e429abd4f1cd67a27e Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Thu, 5 Sep 2024 22:25:41 +0530 Subject: [PATCH 030/180] create announce room when 3 or more participants --- .../CreateWorkspaceFromIOUPaymentParams.ts | 2 - .../API/parameters/CreateWorkspaceParams.ts | 2 - src/libs/PolicyUtils.ts | 9 +- src/libs/ReportUtils.ts | 110 ++++++++++++++--- src/libs/actions/Policy/Member.ts | 9 +- src/libs/actions/Policy/Policy.ts | 113 ------------------ .../workspace/WorkspaceInviteMessagePage.tsx | 3 +- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 8 files changed, 111 insertions(+), 139 deletions(-) diff --git a/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts b/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts index 761a6c2f5008..a1256f5ad051 100644 --- a/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts +++ b/src/libs/API/parameters/CreateWorkspaceFromIOUPaymentParams.ts @@ -1,13 +1,11 @@ type CreateWorkspaceFromIOUPaymentParams = { policyID: string; - announceChatReportID: string; adminsChatReportID: string; expenseChatReportID: string; ownerEmail: string; makeMeAdmin: boolean; policyName: string; type: string; - announceCreatedReportActionID: string; adminsCreatedReportActionID: string; expenseCreatedReportActionID: string; customUnitID: string; diff --git a/src/libs/API/parameters/CreateWorkspaceParams.ts b/src/libs/API/parameters/CreateWorkspaceParams.ts index c86598b48953..18ef4a0e763f 100644 --- a/src/libs/API/parameters/CreateWorkspaceParams.ts +++ b/src/libs/API/parameters/CreateWorkspaceParams.ts @@ -1,13 +1,11 @@ type CreateWorkspaceParams = { policyID: string; - announceChatReportID: string; adminsChatReportID: string; expenseChatReportID: string; ownerEmail: string; makeMeAdmin: boolean; policyName: string; type: string; - announceCreatedReportActionID: string; adminsCreatedReportActionID: string; expenseCreatedReportActionID: string; customUnitID: string; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 07ce5b1539df..961a8bce41d3 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -218,7 +218,7 @@ const isPolicyOwner = (policy: OnyxInputOrEntry, currentUserAccountID: n * * If includeMemberWithErrors is false, We only return members without errors. Otherwise, the members with errors would immediately be removed before the user has a chance to read the error. */ -function getMemberAccountIDsForWorkspace(employeeList: PolicyEmployeeList | undefined, includeMemberWithErrors = false): MemberEmailsToAccountIDs { +function getMemberAccountIDsForWorkspace(employeeList: PolicyEmployeeList | undefined, includeMemberWithErrors = false, includeMemberWithPendingDelete = true): MemberEmailsToAccountIDs { const members = employeeList ?? {}; const memberEmailsToAccountIDs: MemberEmailsToAccountIDs = {}; Object.keys(members).forEach((email) => { @@ -228,6 +228,13 @@ function getMemberAccountIDsForWorkspace(employeeList: PolicyEmployeeList | unde return; } } + if (!includeMemberWithPendingDelete) { + const member = members?.[email]; + if (member.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + console.log('RETURNING FOR [' + email + ']'); + return; + } + } const personalDetail = getPersonalDetailByEmail(email); if (!personalDetail?.login) { return; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7b226b2e5c8e..dac51f3d0dd0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -323,11 +323,13 @@ type OptimisticTaskReportAction = Pick< | 'linkMetadata' >; +type AnnounceRoomOnyxData = { + onyxOptimisticData: OnyxUpdate[]; + onyxSuccessData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; +}; + type OptimisticWorkspaceChats = { - announceChatReportID: string; - announceChatData: OptimisticChatReport; - announceReportActionData: Record; - announceCreatedReportActionID: string; adminsChatReportID: string; adminsChatData: OptimisticChatReport; adminsReportActionData: Record; @@ -5472,26 +5474,107 @@ function buildOptimisticDismissedViolationReportAction( }; } -function buildOptimisticWorkspaceChats(policyID: string, policyName: string, expenseReportId?: string): OptimisticWorkspaceChats { +function buildOptimisticAnnounceChat(policyID: string, accountIDs: number[]): AnnounceRoomOnyxData { + const announceReport = getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); + const policy = getPolicy(policyID); + const announceRoomOnyxData: AnnounceRoomOnyxData = { + onyxOptimisticData: [], + onyxSuccessData: [], + onyxFailureData: [], + }; + + // Do not create #announce room if the room already exists or if there are less than 3 participants in workspace + if (announceReport || accountIDs.length < 3) { + return announceRoomOnyxData; + } + const announceChatData = buildOptimisticChatReport( - currentUserAccountID ? [currentUserAccountID] : [], + accountIDs, CONST.REPORT.WORKSPACE_CHAT_ROOMS.ANNOUNCE, CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID, CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, false, - policyName, + policy?.name, undefined, undefined, // #announce contains all policy members so notifying always should be opt-in only. CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY, ); - const announceChatReportID = announceChatData.reportID; const announceCreatedAction = buildOptimisticCreatedReportAction(CONST.POLICY.OWNER_EMAIL_FAKE); - const announceReportActionData = { - [announceCreatedAction.reportActionID]: announceCreatedAction, - }; + announceRoomOnyxData.onyxOptimisticData.push( + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatData.reportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...announceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${announceChatData.reportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatData.reportID}`, + value: { + [announceCreatedAction.reportActionID]: announceCreatedAction, + }, + }, + ); + announceRoomOnyxData.onyxSuccessData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatData.reportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + isOptimisticReport: false, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatData.reportID}`, + value: { + [announceCreatedAction.reportActionID]: { + pendingAction: null, + }, + }, + }, + ); + announceRoomOnyxData.onyxFailureData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatData.reportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + isOptimisticReport: false, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatData.reportID}`, + value: { + [announceCreatedAction.reportActionID]: { + pendingAction: null, + }, + }, + }, + ); + return announceRoomOnyxData; +} + +function buildOptimisticWorkspaceChats(policyID: string, policyName: string, expenseReportId?: string): OptimisticWorkspaceChats { const pendingChatMembers = getPendingChatMembers(currentUserAccountID ? [currentUserAccountID] : [], [], CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); const adminsChatData = { ...buildOptimisticChatReport( @@ -5535,10 +5618,6 @@ function buildOptimisticWorkspaceChats(policyID: string, policyName: string, exp }; return { - announceChatReportID, - announceChatData, - announceReportActionData, - announceCreatedReportActionID: announceCreatedAction.reportActionID, adminsChatReportID, adminsChatData, adminsReportActionData, @@ -7812,6 +7891,7 @@ export { buildOptimisticTaskReport, buildOptimisticTaskReportAction, buildOptimisticUnHoldReportAction, + buildOptimisticAnnounceChat, buildOptimisticWorkspaceChats, buildParticipantsFromAccountIDs, buildTransactionThread, diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index 1b881f65c831..1f33c769dca1 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -566,7 +566,7 @@ function clearWorkspaceOwnerChangeFlow(policyID: string) { * Adds members to the specified workspace/policyID * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ -function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs, welcomeNote: string, policyID: string) { +function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs, welcomeNote: string, policyID: string, policyMemberAccountIDs: number[]) { const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const; const logins = Object.keys(invitedEmailsToAccountIDs).map((memberLogin) => PhoneNumber.addSMSDomainIfPhoneNumber(memberLogin)); const accountIDs = Object.values(invitedEmailsToAccountIDs); @@ -575,6 +575,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount const newPersonalDetailsOnyxData = PersonalDetailsUtils.getPersonalDetailsOnyxDataForOptimisticUsers(newLogins, newAccountIDs); const announceRoomMembers = buildAnnounceRoomMembersOnyxData(policyID, accountIDs); + const announceRoomChat = ReportUtils.buildOptimisticAnnounceChat(policyID, [...policyMemberAccountIDs, ...accountIDs]); // create onyx data for policy expense chats for each new member const membersChats = createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs); @@ -601,7 +602,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount }, }, ]; - optimisticData.push(...newPersonalDetailsOnyxData.optimisticData, ...membersChats.onyxOptimisticData, ...announceRoomMembers.onyxOptimisticData); + optimisticData.push(...newPersonalDetailsOnyxData.optimisticData, ...membersChats.onyxOptimisticData, ...announceRoomChat.onyxOptimisticData, ...announceRoomMembers.onyxOptimisticData); const successData: OnyxUpdate[] = [ { @@ -612,7 +613,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount }, }, ]; - successData.push(...newPersonalDetailsOnyxData.finallyData, ...membersChats.onyxSuccessData, ...announceRoomMembers.onyxSuccessData); + successData.push(...newPersonalDetailsOnyxData.finallyData, ...membersChats.onyxSuccessData, ...announceRoomChat.onyxSuccessData, ...announceRoomMembers.onyxSuccessData); const failureData: OnyxUpdate[] = [ { @@ -626,7 +627,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount }, }, ]; - failureData.push(...membersChats.onyxFailureData, ...announceRoomMembers.onyxFailureData); + failureData.push(...membersChats.onyxFailureData, ...announceRoomChat.onyxFailureData, ...announceRoomMembers.onyxFailureData); const params: AddMembersToWorkspaceParams = { employees: JSON.stringify(logins.map((login) => ({email: login}))), diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 81d440a89a49..21f183a2d75d 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1574,10 +1574,6 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName const {customUnits, customUnitID, customUnitRateID, outputCurrency} = buildOptimisticCustomUnits(); const { - announceChatReportID, - announceChatData, - announceReportActionData, - announceCreatedReportActionID, adminsChatReportID, adminsChatData, adminsReportActionData, @@ -1635,26 +1631,6 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName }, }, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...announceChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData, - }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, @@ -1714,26 +1690,6 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName }, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - isOptimisticReport: false, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - [announceCreatedReportActionID]: { - pendingAction: null, - }, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, @@ -1783,16 +1739,6 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: {employeeList: null}, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: null, - }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, @@ -1829,14 +1775,12 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName const params: CreateWorkspaceParams = { policyID, - announceChatReportID, adminsChatReportID, expenseChatReportID, ownerEmail: policyOwnerEmail, makeMeAdmin, policyName: workspaceName, type: CONST.POLICY.TYPE.TEAM, - announceCreatedReportActionID, adminsCreatedReportActionID, expenseCreatedReportActionID, customUnitID, @@ -2208,10 +2152,6 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF const iouReportID = iouReport.reportID; const { - announceChatReportID, - announceChatData, - announceReportActionData, - announceCreatedReportActionID, adminsChatReportID, adminsChatData, adminsReportActionData, @@ -2278,21 +2218,6 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: newWorkspace, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...announceChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData, - }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, @@ -2349,25 +2274,6 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF }, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - [Object.keys(announceChatData)[0]]: { - pendingAction: null, - }, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, @@ -2410,23 +2316,6 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF successData.push(...employeeWorkspaceChat.onyxSuccessData); const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - pendingAction: null, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, @@ -2626,14 +2515,12 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF const params: CreateWorkspaceFromIOUPaymentParams = { policyID, - announceChatReportID, adminsChatReportID, expenseChatReportID: workspaceChatReportID, ownerEmail: '', makeMeAdmin: false, policyName: workspaceName, type: CONST.POLICY.TYPE.TEAM, - announceCreatedReportActionID, adminsCreatedReportActionID, expenseCreatedReportActionID: workspaceChatCreatedReportActionID, customUnitID, diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index df34875f5fa6..39c4f6d4b18e 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -107,8 +107,9 @@ function WorkspaceInviteMessagePage({ const sendInvitation = () => { Keyboard.dismiss(); + const policyMemberAccountIDs = Object.values(PolicyUtils.getMemberAccountIDsForWorkspace(policy?.employeeList, false, false)); // Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details - Member.addMembersToWorkspace(invitedEmailsToAccountIDsDraft ?? {}, `${welcomeNoteSubject}\n\n${welcomeNote}`, route.params.policyID); + Member.addMembersToWorkspace(invitedEmailsToAccountIDsDraft ?? {}, `${welcomeNoteSubject}\n\n${welcomeNote}`, route.params.policyID, policyMemberAccountIDs); debouncedSaveDraft(null); SearchInputManager.searchInput = ''; Navigation.dismissModal(); diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index c0f83662006f..b17a70a308ad 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -354,7 +354,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: fallbackIcon: Expensicons.FallbackWorkspaceAvatar, policyID: policy.id, adminRoom: policyRooms?.[policy.id]?.adminRoom ?? policy.chatReportIDAdmins?.toString(), - announceRoom: policyRooms?.[policy.id]?.announceRoom ?? policy.chatReportIDAnnounce?.toString(), + announceRoom: policyRooms?.[policy.id]?.announceRoom ?? (policy.chatReportIDAnnounce ? policy.chatReportIDAnnounce?.toString() : ''), ownerAccountID: policy.ownerAccountID, role: policy.role, type: policy.type, From e326b2c8f8f37f74fdbcda969b927e06f35e098a Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Thu, 5 Sep 2024 22:28:44 +0530 Subject: [PATCH 031/180] remove console log --- src/libs/PolicyUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 961a8bce41d3..160dd87df0ab 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -231,7 +231,6 @@ function getMemberAccountIDsForWorkspace(employeeList: PolicyEmployeeList | unde if (!includeMemberWithPendingDelete) { const member = members?.[email]; if (member.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - console.log('RETURNING FOR [' + email + ']'); return; } } From dbebceba862d5f54e1387ca0070dd18fd4869927 Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Thu, 5 Sep 2024 23:43:42 +0530 Subject: [PATCH 032/180] jest, lint and typecheck fixes --- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.ts | 4 ---- src/libs/actions/Policy/Policy.ts | 8 ++++---- tests/actions/PolicyTest.ts | 17 +++++------------ 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index dac51f3d0dd0..a522f65db73c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5484,7 +5484,7 @@ function buildOptimisticAnnounceChat(policyID: string, accountIDs: number[]): An }; // Do not create #announce room if the room already exists or if there are less than 3 participants in workspace - if (announceReport || accountIDs.length < 3) { + if (accountIDs.length < 3 || announceReport) { return announceRoomOnyxData; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 29d481737790..85b475b88e36 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3346,8 +3346,6 @@ function categorizeTrackedExpense( receipt, policyExpenseChatReportID: createdWorkspaceParams?.expenseChatReportID, policyExpenseCreatedReportActionID: createdWorkspaceParams?.expenseCreatedReportActionID, - announceChatReportID: createdWorkspaceParams?.announceChatReportID, - announceCreatedReportActionID: createdWorkspaceParams?.announceCreatedReportActionID, adminsChatReportID: createdWorkspaceParams?.adminsChatReportID, adminsCreatedReportActionID: createdWorkspaceParams?.adminsCreatedReportActionID, }; @@ -3423,8 +3421,6 @@ function shareTrackedExpense( receipt, policyExpenseChatReportID: createdWorkspaceParams?.expenseChatReportID, policyExpenseCreatedReportActionID: createdWorkspaceParams?.expenseCreatedReportActionID, - announceChatReportID: createdWorkspaceParams?.announceChatReportID, - announceCreatedReportActionID: createdWorkspaceParams?.announceCreatedReportActionID, adminsChatReportID: createdWorkspaceParams?.adminsChatReportID, adminsCreatedReportActionID: createdWorkspaceParams?.adminsCreatedReportActionID, }; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 21f183a2d75d..34dbb7beac23 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1818,8 +1818,10 @@ function createDraftWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policy const {customUnits, customUnitID, customUnitRateID, outputCurrency} = buildOptimisticCustomUnits(); - const {expenseChatData, announceChatReportID, announceCreatedReportActionID, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, expenseCreatedReportActionID} = - ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); + const {expenseChatData, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, expenseCreatedReportActionID} = ReportUtils.buildOptimisticWorkspaceChats( + policyID, + workspaceName, + ); const optimisticData: OnyxUpdate[] = [ { @@ -1883,14 +1885,12 @@ function createDraftWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policy const params: CreateWorkspaceParams = { policyID, - announceChatReportID, adminsChatReportID, expenseChatReportID, ownerEmail: policyOwnerEmail, makeMeAdmin, policyName: workspaceName, type: CONST.POLICY.TYPE.TEAM, - announceCreatedReportActionID, adminsCreatedReportActionID, expenseCreatedReportActionID, customUnitID, diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index a9af7b9da7d8..82fb52ae1d76 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -35,7 +35,6 @@ describe('actions/Policy', () => { await waitForBatchedUpdates(); let adminReportID; - let announceReportID; let expenseReportID; const policyID = Policy.generatePolicyID(); @@ -75,7 +74,7 @@ describe('actions/Policy', () => { // Three reports should be created: #announce, #admins and expense report const workspaceReports = Object.values(allReports ?? {}).filter((report) => report?.policyID === policyID); - expect(workspaceReports.length).toBe(3); + expect(workspaceReports.length).toBe(2); workspaceReports.forEach((report) => { expect(report?.pendingFields?.addWorkspaceRoom).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); expect(report?.participants).toEqual({[ESH_ACCOUNT_ID]: ESH_PARTICIPANT}); @@ -84,10 +83,6 @@ describe('actions/Policy', () => { adminReportID = report.reportID; break; } - case CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE: { - announceReportID = report.reportID; - break; - } case CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT: { expenseReportID = report.reportID; break; @@ -110,13 +105,12 @@ describe('actions/Policy', () => { // Each of the three reports should have a a `CREATED` action. let adminReportActions: ReportAction[] = Object.values(reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminReportID}`] ?? {}); - let announceReportActions: ReportAction[] = Object.values(reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceReportID}`] ?? {}); let expenseReportActions: ReportAction[] = Object.values(reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReportID}`] ?? {}); - let workspaceReportActions: ReportAction[] = adminReportActions.concat(announceReportActions, expenseReportActions); - [adminReportActions, announceReportActions, expenseReportActions].forEach((actions) => { + let workspaceReportActions: ReportAction[] = adminReportActions.concat(expenseReportActions); + [adminReportActions, expenseReportActions].forEach((actions) => { expect(actions.length).toBe(1); }); - [...adminReportActions, ...announceReportActions, ...expenseReportActions].forEach((reportAction) => { + [...adminReportActions, ...expenseReportActions].forEach((reportAction) => { expect(reportAction.actionName).toBe(CONST.REPORT.ACTIONS.TYPE.CREATED); expect(reportAction.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); expect(reportAction.actorAccountID).toBe(ESH_ACCOUNT_ID); @@ -170,9 +164,8 @@ describe('actions/Policy', () => { // Check if the report action pending action was cleared adminReportActions = Object.values(reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminReportID}`] ?? {}); - announceReportActions = Object.values(reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceReportID}`] ?? {}); expenseReportActions = Object.values(reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReportID}`] ?? {}); - workspaceReportActions = adminReportActions.concat(announceReportActions, expenseReportActions); + workspaceReportActions = adminReportActions.concat(expenseReportActions); workspaceReportActions.forEach((reportAction) => { expect(reportAction.pendingAction).toBeFalsy(); }); From e7162f3a101891bd828777aaaaf1f073d0b93738 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 00:53:20 +0530 Subject: [PATCH 033/180] fix lint --- src/hooks/useDelegateUserDetails.ts | 5 ++--- src/libs/ReportUtils.ts | 4 ++-- src/libs/actions/IOU.ts | 17 +++++++++++++---- src/libs/actions/Report.ts | 7 ++++--- tests/actions/IOUTest.ts | 4 ++-- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/hooks/useDelegateUserDetails.ts b/src/hooks/useDelegateUserDetails.ts index 79b10e438b73..d1542be67463 100644 --- a/src/hooks/useDelegateUserDetails.ts +++ b/src/hooks/useDelegateUserDetails.ts @@ -1,5 +1,4 @@ import {useOnyx} from 'react-native-onyx'; -import AccountUtils from '@libs/AccountUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; @@ -11,8 +10,8 @@ function useDelegateUserDetails() { return { delegatorEmail, - delegateEmail + delegateEmail, }; } -export default useDelegateUserDetails; \ No newline at end of file +export default useDelegateUserDetails; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2a4a87ff21c4..bc82ed723dea 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4137,7 +4137,7 @@ function buildOptimisticAddCommentReportAction( originalMessage: { html: htmlForNewComment, whisperedTo: [], - delegate: delegate, + delegate, }, isFirstItem: false, isAttachmentOnly, @@ -4224,7 +4224,7 @@ function buildOptimisticTaskCommentReportAction( html: ReportActionsUtils.getReportActionHtml(reportAction.reportAction), taskReportID: ReportActionsUtils.getReportActionMessage(reportAction.reportAction)?.taskReportID, whisperedTo: [], - delegate: delegate, + delegate, }; reportAction.reportAction.childReportID = taskReportID; reportAction.reportAction.parentReportID = parentReportID; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 227970964637..4ef052f75957 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4,7 +4,7 @@ import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate} import Onyx from 'react-native-onyx'; import type {PartialDeep, SetRequired, ValueOf} from 'type-fest'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; -import useDelegateUserDetails from '@hooks/useDelegateUserDetails' +import useDelegateUserDetails from '@hooks/useDelegateUserDetails'; import * as API from '@libs/API'; import type { ApproveMoneyRequestParams, @@ -273,8 +273,6 @@ Onyx.connect({ callback: (value) => (activePolicyID = value), }); -const { delegateEmail } = useDelegateUserDetails() - /** * Find the report preview action from given chat report and iou report */ @@ -6915,6 +6913,8 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?: if (hasHeldExpenses && !full && !!expenseReport?.unheldTotal) { total = expenseReport?.unheldTotal; } + const {delegateEmail} = useDelegateUserDetails(); + const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1', delegateEmail); const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport?.ownerAccountID ?? -1, expenseReport?.total ?? 0); @@ -7070,6 +7070,7 @@ function unapproveExpenseReport(expenseReport: OnyxEntry) { } const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; + const {delegateEmail} = useDelegateUserDetails(); const optimisticUnapprovedReportAction = ReportUtils.buildOptimisticUnapprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, delegateEmail); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.SUBMITTED); @@ -7165,7 +7166,15 @@ function submitReport(expenseReport: OnyxTypes.Report) { const isCurrentUserManager = currentUserPersonalDetails?.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails?.accountID : undefined; - const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID, delegateEmail); + const {delegateEmail} = useDelegateUserDetails(); + + const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction( + expenseReport?.total ?? 0, + expenseReport.currency ?? '', + expenseReport.reportID, + adminAccountID, + delegateEmail, + ); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED); const optimisticData: OnyxUpdate[] = !isSubmitAndClosePolicy diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ba6c9ad77c09..1c3abce65372 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5,10 +5,10 @@ import isEmpty from 'lodash/isEmpty'; import {DeviceEventEmitter, InteractionManager, Linking} from 'react-native'; import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import useDelegateUserDetails from '@hooks/useDelegateUserDetails' import type {PartialDeep, ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; +import useDelegateUserDetails from '@hooks/useDelegateUserDetails'; import AccountUtils from '@libs/AccountUtils'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; @@ -281,7 +281,6 @@ registerPaginationConfig({ isLastItem: (reportAction) => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED, }); -const { delegateEmail } = useDelegateUserDetails() function clearGroupChat() { Onyx.set(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, null); @@ -454,6 +453,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { let reportCommentAction: OptimisticAddCommentReportAction | undefined; let attachmentAction: OptimisticAddCommentReportAction | undefined; let commandName: typeof WRITE_COMMANDS.ADD_COMMENT | typeof WRITE_COMMANDS.ADD_ATTACHMENT | typeof WRITE_COMMANDS.ADD_TEXT_AND_ATTACHMENT = WRITE_COMMANDS.ADD_COMMENT; + const {delegateEmail} = useDelegateUserDetails(); if (text && !file) { const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, text, undefined, undefined, undefined, undefined, reportID); @@ -3341,7 +3341,8 @@ function completeOnboarding( const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; - + const {delegateEmail} = useDelegateUserDetails(); + // Introductory message const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index dd94fbd76aa5..7a228fa5af7d 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -417,7 +417,7 @@ describe('actions/IOU', () => { const comment = 'Giv money plz'; const chatReportID = '1234'; const iouReportID = '5678'; - const delegate = '' + const delegate = ''; let chatReport: OnyxEntry = { reportID: chatReportID, type: CONST.REPORT.TYPE.CHAT, @@ -461,7 +461,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, participantAccountIDs: [RORY_ACCOUNT_ID, CARLOS_ACCOUNT_ID], - delegate: delegate, + delegate, }, }; let newIOUAction: OnyxEntry>; From beb642d337a356101360b82f1b070481f00a7821 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 01:16:41 +0530 Subject: [PATCH 034/180] remove usage of hook --- src/hooks/useDelegateUserDetails.ts | 17 ----------------- src/libs/actions/IOU.ts | 12 ++++++++---- src/libs/actions/Report.ts | 13 ++++++++----- 3 files changed, 16 insertions(+), 26 deletions(-) delete mode 100644 src/hooks/useDelegateUserDetails.ts diff --git a/src/hooks/useDelegateUserDetails.ts b/src/hooks/useDelegateUserDetails.ts deleted file mode 100644 index d1542be67463..000000000000 --- a/src/hooks/useDelegateUserDetails.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {useOnyx} from 'react-native-onyx'; -import ONYXKEYS from '@src/ONYXKEYS'; -import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; - -function useDelegateUserDetails() { - const currentUserDeatils = useCurrentUserPersonalDetails(); - const [currentUserAccountDetails] = useOnyx(ONYXKEYS.ACCOUNT); - const delegatorEmail = currentUserDeatils?.login; - const delegateEmail = currentUserAccountDetails?.delegatedAccess?.delegate ?? ''; - - return { - delegatorEmail, - delegateEmail, - }; -} - -export default useDelegateUserDetails; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e01b2b745460..32ebfb1e516f 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4,7 +4,6 @@ import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate} import Onyx from 'react-native-onyx'; import type {PartialDeep, SetRequired, ValueOf} from 'type-fest'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; -import useDelegateUserDetails from '@hooks/useDelegateUserDetails'; import * as API from '@libs/API'; import type { ApproveMoneyRequestParams, @@ -247,6 +246,14 @@ Onyx.connect({ }, }); +let delegateEmail = ''; +Onyx.connect({ + key: ONYXKEYS.ACCOUNT, + callback: (value) => { + delegateEmail = value?.delegatedAccess?.delegate ?? ''; + }, +}); + let quickAction: OnyxEntry = {}; Onyx.connect({ key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, @@ -6915,7 +6922,6 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?: if (hasHeldExpenses && !full && !!expenseReport?.unheldTotal) { total = expenseReport?.unheldTotal; } - const {delegateEmail} = useDelegateUserDetails(); const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1', delegateEmail); @@ -7072,7 +7078,6 @@ function unapproveExpenseReport(expenseReport: OnyxEntry) { } const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - const {delegateEmail} = useDelegateUserDetails(); const optimisticUnapprovedReportAction = ReportUtils.buildOptimisticUnapprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, delegateEmail); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.SUBMITTED); @@ -7168,7 +7173,6 @@ function submitReport(expenseReport: OnyxTypes.Report) { const isCurrentUserManager = currentUserPersonalDetails?.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails?.accountID : undefined; - const {delegateEmail} = useDelegateUserDetails(); const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction( expenseReport?.total ?? 0, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index bdde156bf644..874cc04f9e10 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -8,7 +8,6 @@ import Onyx from 'react-native-onyx'; import type {PartialDeep, ValueOf} from 'type-fest'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; -import useDelegateUserDetails from '@hooks/useDelegateUserDetails'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; import type { @@ -265,6 +264,13 @@ Onyx.connect({ waitForCollectionCallback: true, callback: (value) => (allReportDraftComments = value), }); +let delegateEmail = ''; +Onyx.connect({ + key: ONYXKEYS.ACCOUNT, + callback: (value) => { + delegateEmail = value?.delegatedAccess?.delegate ?? ''; + }, +}); let environmentURL: string; Environment.getEnvironmentURL().then((url: string) => (environmentURL = url)); @@ -280,7 +286,6 @@ registerPaginationConfig({ isLastItem: (reportAction) => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED, }); - function clearGroupChat() { Onyx.set(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, null); } @@ -452,7 +457,6 @@ function addActions(reportID: string, text = '', file?: FileObject) { let reportCommentAction: OptimisticAddCommentReportAction | undefined; let attachmentAction: OptimisticAddCommentReportAction | undefined; let commandName: typeof WRITE_COMMANDS.ADD_COMMENT | typeof WRITE_COMMANDS.ADD_ATTACHMENT | typeof WRITE_COMMANDS.ADD_TEXT_AND_ATTACHMENT = WRITE_COMMANDS.ADD_COMMENT; - const {delegateEmail} = useDelegateUserDetails(); if (text && !file) { const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, text, undefined, undefined, undefined, undefined, reportID); @@ -3323,8 +3327,7 @@ function completeOnboarding( const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; - const {delegateEmail} = useDelegateUserDetails(); - + // Introductory message const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; From b6272e331288c58d1d36e07ad45bf97741ffed83 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 03:24:59 +0530 Subject: [PATCH 035/180] part 1: update reportactionitemsingle to display delegate details --- src/libs/Permissions.ts | 2 +- src/libs/ReportActionsUtils.ts | 6 +++++ .../home/report/ReportActionItemSingle.tsx | 27 ++++++++++++++----- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 0e67dc270518..3ff753dfa067 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -4,7 +4,7 @@ import type {IOUType} from '@src/CONST'; import type Beta from '@src/types/onyx/Beta'; function canUseAllBetas(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.ALL); + return true; } function canUseDefaultRooms(betas: OnyxEntry): boolean { diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 4d126cf9cbf4..378052285c26 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -209,6 +209,11 @@ function getOriginalMessage(reportAction: OnyxInputO return reportAction.originalMessage; } +function getOriginalMessageForDelegate(reportAction: OnyxInputOrEntry){ + + return reportAction?.originalMessage ?? {}; +} + function isExportIntegrationAction(reportAction: OnyxInputOrEntry): boolean { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION; } @@ -1737,6 +1742,7 @@ export { isTransactionThread, isTripPreview, isWhisperAction, + getOriginalMessageForDelegate, isSubmittedAction, isApprovedAction, isForwardedAction, diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 95a7332f0606..c7063cb7af79 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -20,7 +20,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {getReportActionMessage} from '@libs/ReportActionsUtils'; +import {getOriginalMessage, getOriginalMessageForDelegate, getReportActionMessage} from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -30,6 +30,8 @@ import type {Icon} from '@src/types/onyx/OnyxCommon'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import ReportActionItemDate from './ReportActionItemDate'; import ReportActionItemFragment from './ReportActionItemFragment'; +import { getAccountIDsByLogins, getPersonalDetailByEmail } from '@libs/PersonalDetailsUtils'; +import ReportActionItemBasicMessage from './ReportActionItemBasicMessage'; type ReportActionItemSingleProps = Partial & { /** All the data of the action */ @@ -83,7 +85,10 @@ function ReportActionItemSingle({ const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); + const message = getOriginalMessage(action) + const delegateEmail = message?.delegate + console.log('delegateEmail::::', delegateEmail) let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID ?? -1] ?? {}; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -102,15 +107,15 @@ function ReportActionItemSingle({ actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(report); avatarId = report.policyID; - } else if (action?.delegateAccountID && personalDetails[action?.delegateAccountID]) { + } else if (delegateEmail) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. - const delegateDetails = personalDetails[action.delegateAccountID]; + const delegateAccount = getPersonalDetailByEmail(delegateEmail) + const delegateDetails = delegateAccount; const delegateDisplayName = delegateDetails?.displayName; - actorHint = `${delegateDisplayName} (${translate('reportAction.asCopilot')} ${displayName})`; - displayName = actorHint; + displayName = delegateDisplayName ?? ''; avatarSource = delegateDetails?.avatar; - avatarId = action.delegateAccountID; + avatarId = delegateAccount?.accountID; } else if (isReportPreviewAction && isTripRoom) { displayName = report?.reportName ?? ''; } @@ -235,7 +240,16 @@ function ReportActionItemSingle({ const formattedDate = DateUtils.getStatusUntilDate(status?.clearAfter ?? ''); const statusText = status?.text ?? ''; const statusTooltipText = formattedDate ? `${statusText ? `${statusText} ` : ''}(${formattedDate})` : statusText; + const actionTypes = [ + CONST.REPORT.ACTIONS.TYPE.SUBMITTED, + CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, + CONST.REPORT.ACTIONS.TYPE.APPROVED, + CONST.REPORT.ACTIONS.TYPE.HOLD_COMMENT, + CONST.REPORT.ACTIONS.TYPE.HOLD, + CONST.REPORT.ACTIONS.TYPE.UNHOLD, + ]; + return ( ) : null} + {actionTypes.includes(action?.actionName) && ()} {children} From 79192b27d15e72f0c643650eb5f52ef9bd177769 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 03:28:34 +0530 Subject: [PATCH 036/180] Update Permissions.ts --- src/libs/Permissions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 3ff753dfa067..0e67dc270518 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -4,7 +4,7 @@ import type {IOUType} from '@src/CONST'; import type Beta from '@src/types/onyx/Beta'; function canUseAllBetas(betas: OnyxEntry): boolean { - return true; + return !!betas?.includes(CONST.BETAS.ALL); } function canUseDefaultRooms(betas: OnyxEntry): boolean { From 3d136e4fa8ecb70df5ab382258bb8d7db4db73b9 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 12:07:26 +0530 Subject: [PATCH 037/180] part 2: add delegator name --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/pages/home/report/ReportActionItemSingle.tsx | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index c2902dc3d8a5..ca58e53eec0c 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -4600,5 +4600,6 @@ export default { } }, genericError: 'Oops, something went wrong. Please try again.', + onBehalfOfMessage: (delegator: string) => `on behalf of ${delegator}`, }, } satisfies TranslationBase; diff --git a/src/languages/es.ts b/src/languages/es.ts index 9ce98e12b135..7b3baf6a9796 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5117,5 +5117,6 @@ export default { } }, genericError: '¡Ups! Ha ocurrido un error. Por favor, inténtalo de nuevo.', + onBehalfOfMessage: ({delegator}: string) => `on behalf of ${delegator}`, }, } satisfies EnglishTranslation; diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index c7063cb7af79..b2325c367aa1 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -83,14 +83,15 @@ function ReportActionItemSingle({ const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; + const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); const message = getOriginalMessage(action) const delegateEmail = message?.delegate - console.log('delegateEmail::::', delegateEmail) let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID ?? -1] ?? {}; + const accountOwnerDetails = getPersonalDetailByEmail(login ?? '') // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing let actorHint = (login || (displayName ?? '')).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const isTripRoom = ReportUtils.isTripRoom(report); @@ -299,7 +300,7 @@ function ReportActionItemSingle({ ) : null} - {actionTypes.includes(action?.actionName) && ()} + {(delegateEmail && actionTypes.includes(action?.actionName)) && ()} {children} From 9a2f8b72bcc91f2a2405abf30eecdb078ef07521 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 12:09:28 +0530 Subject: [PATCH 038/180] fix spanish translation --- 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 7b3baf6a9796..9492ea36aa90 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5117,6 +5117,6 @@ export default { } }, genericError: '¡Ups! Ha ocurrido un error. Por favor, inténtalo de nuevo.', - onBehalfOfMessage: ({delegator}: string) => `on behalf of ${delegator}`, + onBehalfOfMessage: (delegator: string) => `en nombre de ${delegator}`, }, } satisfies EnglishTranslation; From 4b13c3c194f058adf9f34ebe566d415be821a387 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 12:48:47 +0530 Subject: [PATCH 039/180] add delegator display name --- src/libs/ReportActionsUtils.ts | 3 +-- .../home/report/ReportActionItemSingle.tsx | 20 +++++++++---------- src/styles/index.ts | 10 ++++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 378052285c26..916951f00ea7 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -209,8 +209,7 @@ function getOriginalMessage(reportAction: OnyxInputO return reportAction.originalMessage; } -function getOriginalMessageForDelegate(reportAction: OnyxInputOrEntry){ - +function getOriginalMessageForDelegate(reportAction: OnyxInputOrEntry) { return reportAction?.originalMessage ?? {}; } diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index b2325c367aa1..765febd99d87 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -20,7 +20,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {getOriginalMessage, getOriginalMessageForDelegate, getReportActionMessage} from '@libs/ReportActionsUtils'; +import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; +import {getOriginalMessage, getReportActionMessage} from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -30,8 +31,6 @@ import type {Icon} from '@src/types/onyx/OnyxCommon'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import ReportActionItemDate from './ReportActionItemDate'; import ReportActionItemFragment from './ReportActionItemFragment'; -import { getAccountIDsByLogins, getPersonalDetailByEmail } from '@libs/PersonalDetailsUtils'; -import ReportActionItemBasicMessage from './ReportActionItemBasicMessage'; type ReportActionItemSingleProps = Partial & { /** All the data of the action */ @@ -86,12 +85,12 @@ function ReportActionItemSingle({ const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); - const message = getOriginalMessage(action) - const delegateEmail = message?.delegate + const message = getOriginalMessage(action); + const delegateEmail = message?.delegate; let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID ?? -1] ?? {}; - const accountOwnerDetails = getPersonalDetailByEmail(login ?? '') + const accountOwnerDetails = getPersonalDetailByEmail(login ?? ''); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing let actorHint = (login || (displayName ?? '')).replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const isTripRoom = ReportUtils.isTripRoom(report); @@ -111,7 +110,7 @@ function ReportActionItemSingle({ } else if (delegateEmail) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. - const delegateAccount = getPersonalDetailByEmail(delegateEmail) + const delegateAccount = getPersonalDetailByEmail(delegateEmail); const delegateDetails = delegateAccount; const delegateDisplayName = delegateDetails?.displayName; displayName = delegateDisplayName ?? ''; @@ -248,9 +247,8 @@ function ReportActionItemSingle({ CONST.REPORT.ACTIONS.TYPE.HOLD_COMMENT, CONST.REPORT.ACTIONS.TYPE.HOLD, CONST.REPORT.ACTIONS.TYPE.UNHOLD, + ]; - ]; - return ( ) : null} - {(delegateEmail && actionTypes.includes(action?.actionName)) && ()} + {delegateEmail && actionTypes.includes(action?.actionName) && ( + {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')} + )} {children} diff --git a/src/styles/index.ts b/src/styles/index.ts index 179e886d08ff..43769cbfcea8 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2070,6 +2070,16 @@ const styles = (theme: ThemeColors) => ...wordBreak.breakWord, }, + chatDelegateMessage: { + color: theme.textSupporting, + fontSize: 11, + ...FontUtils.fontFamily.platform.EXP_NEUE, + lineHeight: variables.lineHeightXLarge, + maxWidth: '100%', + ...whiteSpace.preWrap, + ...wordBreak.breakWord, + }, + renderHTMLTitle: { color: theme.text, fontSize: variables.fontSizeNormal, From 1c497dab18cdf453f8667dca76ab363be613ab20 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 13:13:03 +0530 Subject: [PATCH 040/180] fix system message --- src/pages/home/report/ReportActionItemSingle.tsx | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 765febd99d87..9f18b8f18f0c 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -86,7 +86,7 @@ function ReportActionItemSingle({ const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); const message = getOriginalMessage(action); - const delegateEmail = message?.delegate; + const delegateEmail = (message as any)?.delegate; let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID ?? -1] ?? {}; @@ -240,14 +240,6 @@ function ReportActionItemSingle({ const formattedDate = DateUtils.getStatusUntilDate(status?.clearAfter ?? ''); const statusText = status?.text ?? ''; const statusTooltipText = formattedDate ? `${statusText ? `${statusText} ` : ''}(${formattedDate})` : statusText; - const actionTypes = [ - CONST.REPORT.ACTIONS.TYPE.SUBMITTED, - CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, - CONST.REPORT.ACTIONS.TYPE.APPROVED, - CONST.REPORT.ACTIONS.TYPE.HOLD_COMMENT, - CONST.REPORT.ACTIONS.TYPE.HOLD, - CONST.REPORT.ACTIONS.TYPE.UNHOLD, - ]; return ( @@ -298,9 +290,7 @@ function ReportActionItemSingle({ ) : null} - {delegateEmail && actionTypes.includes(action?.actionName) && ( - {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')} - )} + {delegateEmail && {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')}} {children} From 96eb90f4030ad54ac45210bbb9ca16254ac726ea Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 16:41:35 +0530 Subject: [PATCH 041/180] fix grouping of messages --- src/libs/ReportActionsUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 916951f00ea7..8b7a996b2300 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -525,7 +525,7 @@ function findPreviousAction(reportActions: ReportAction[] | undefined, actionInd function isConsecutiveActionMadeByPreviousActor(reportActions: ReportAction[] | undefined, actionIndex: number): boolean { const previousAction = findPreviousAction(reportActions, actionIndex); const currentAction = reportActions?.[actionIndex]; - + // It's OK for there to be no previous action, and in that case, false will be returned // so that the comment isn't grouped if (!currentAction || !previousAction) { @@ -548,7 +548,7 @@ function isConsecutiveActionMadeByPreviousActor(reportActions: ReportAction[] | } // Do not group if the delegate account ID is different - if (previousAction.delegateAccountID !== currentAction.delegateAccountID) { + if (previousAction.originalMessage?.delegate !== currentAction.originalMessage?.delegate) { return false; } From fc3cae1b8cbaed3646174bea2f69460232e18849 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 16:52:13 +0530 Subject: [PATCH 042/180] revert to previous check --- src/libs/ReportActionsUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 8b7a996b2300..f147d283294f 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -548,7 +548,7 @@ function isConsecutiveActionMadeByPreviousActor(reportActions: ReportAction[] | } // Do not group if the delegate account ID is different - if (previousAction.originalMessage?.delegate !== currentAction.originalMessage?.delegate) { + if (previousAction.delegateAccountID !== currentAction.delegateAccountID) { return false; } From 47a098ecf34baf41e64535f1efcc5d3069fc82fa Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 16:53:01 +0530 Subject: [PATCH 043/180] get delegate account details --- src/libs/ReportActionsUtils.ts | 2 +- src/libs/ReportUtils.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index f147d283294f..916951f00ea7 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -525,7 +525,7 @@ function findPreviousAction(reportActions: ReportAction[] | undefined, actionInd function isConsecutiveActionMadeByPreviousActor(reportActions: ReportAction[] | undefined, actionIndex: number): boolean { const previousAction = findPreviousAction(reportActions, actionIndex); const currentAction = reportActions?.[actionIndex]; - + // It's OK for there to be no previous action, and in that case, false will be returned // so that the comment isn't grouped if (!currentAction || !previousAction) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6bc4f98f9a4a..d1bf1767f4ac 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -126,6 +126,7 @@ type OptimisticAddCommentReportAction = Pick< | 'childCommenterCount' | 'childLastVisibleActionCreated' | 'childOldestFourAccountIDs' + | 'delegateAccountID' > & {isOptimisticAction: boolean}; type OptimisticReportAction = { @@ -4107,7 +4108,7 @@ function buildOptimisticAddCommentReportAction( const isAttachmentOnly = file && !text; const isAttachmentWithText = !!text && file !== undefined; const accountID = actorAccountID ?? currentUserAccountID ?? -1; - + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); // Remove HTML from text when applying optimistic offline comment return { commentText, @@ -4144,6 +4145,7 @@ function buildOptimisticAddCommentReportAction( pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, shouldShow: true, isOptimisticAction: true, + delegateAccountID: delegateAccountDetails?.accountID, }, }; } From e48b2b1131ef1c010a4d139084a61088b81bc756 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 17:04:37 +0530 Subject: [PATCH 044/180] add delegateaccountID --- src/libs/ReportUtils.ts | 36 +++++++++++++++++-- .../home/report/ReportActionItemSingle.tsx | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d1bf1767f4ac..ffe970758b56 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -196,12 +196,36 @@ type ReportOfflinePendingActionAndErrors = { type OptimisticApprovedReportAction = Pick< ReportAction, - 'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'isAttachmentOnly' | 'originalMessage' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction' + | 'actionName' + | 'actorAccountID' + | 'automatic' + | 'avatar' + | 'isAttachmentOnly' + | 'originalMessage' + | 'message' + | 'person' + | 'reportActionID' + | 'shouldShow' + | 'created' + | 'pendingAction' + | 'delegateAccountID' >; type OptimisticUnapprovedReportAction = Pick< ReportAction, - 'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'isAttachmentOnly' | 'originalMessage' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction' + | 'actionName' + | 'actorAccountID' + | 'automatic' + | 'avatar' + | 'isAttachmentOnly' + | 'originalMessage' + | 'message' + | 'person' + | 'reportActionID' + | 'shouldShow' + | 'created' + | 'pendingAction' + | 'delegateAccountID' >; type OptimisticSubmittedReportAction = Pick< @@ -219,6 +243,7 @@ type OptimisticSubmittedReportAction = Pick< | 'shouldShow' | 'created' | 'pendingAction' + | 'delegateAccountID' >; type OptimisticHoldReportAction = Pick< @@ -4632,6 +4657,7 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e expenseReportID, delegate, }; + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); return { actionName: CONST.REPORT.ACTIONS.TYPE.APPROVED, @@ -4652,6 +4678,7 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e shouldShow: true, created: DateUtils.getDBTime(), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + delegateAccountID: delegateAccountDetails?.accountID, }; } @@ -4659,6 +4686,7 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e * Builds an optimistic APPROVED report action with a randomly generated reportActionID. */ function buildOptimisticUnapprovedReportAction(amount: number, currency: string, expenseReportID: string, delegate: string): OptimisticUnapprovedReportAction { + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); return { actionName: CONST.REPORT.ACTIONS.TYPE.UNAPPROVED, actorAccountID: currentUserAccountID, @@ -4683,6 +4711,7 @@ function buildOptimisticUnapprovedReportAction(amount: number, currency: string, shouldShow: true, created: DateUtils.getDBTime(), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + delegateAccountID: delegateAccountDetails?.accountID, }; } @@ -4746,6 +4775,8 @@ function buildOptimisticSubmittedReportAction( delegate, }; + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + return { actionName: CONST.REPORT.ACTIONS.TYPE.SUBMITTED, actorAccountID: currentUserAccountID, @@ -4766,6 +4797,7 @@ function buildOptimisticSubmittedReportAction( shouldShow: true, created: DateUtils.getDBTime(), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + delegateAccountID: delegateAccountDetails?.accountID, }; } diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 9f18b8f18f0c..a47c044620eb 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -107,7 +107,7 @@ function ReportActionItemSingle({ actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(report); avatarId = report.policyID; - } else if (delegateEmail) { + } else if (action?.delegateAccountID && personalDetails[action?.delegateAccountID]) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. const delegateAccount = getPersonalDetailByEmail(delegateEmail); From 1b268e8e1500abdc945519e7ff928cba9fe73cc8 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 17:05:55 +0530 Subject: [PATCH 045/180] remove unused util --- src/libs/ReportActionsUtils.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 916951f00ea7..4d126cf9cbf4 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -209,10 +209,6 @@ function getOriginalMessage(reportAction: OnyxInputO return reportAction.originalMessage; } -function getOriginalMessageForDelegate(reportAction: OnyxInputOrEntry) { - return reportAction?.originalMessage ?? {}; -} - function isExportIntegrationAction(reportAction: OnyxInputOrEntry): boolean { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION; } @@ -1741,7 +1737,6 @@ export { isTransactionThread, isTripPreview, isWhisperAction, - getOriginalMessageForDelegate, isSubmittedAction, isApprovedAction, isForwardedAction, From 4b784e707de3bae3aedd12587a7df244cd28df2d Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 17:23:19 +0530 Subject: [PATCH 046/180] cleanup --- src/pages/home/report/ReportActionItemSingle.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index a47c044620eb..14b00ab2e986 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -85,8 +85,9 @@ function ReportActionItemSingle({ const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); - const message = getOriginalMessage(action); - const delegateEmail = (message as any)?.delegate; + const delegatePersonalDetails = personalDetails[action?.delegateAccountID ?? '']; + + const delegateEmail = delegatePersonalDetails?.login; let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID ?? -1] ?? {}; @@ -110,12 +111,9 @@ function ReportActionItemSingle({ } else if (action?.delegateAccountID && personalDetails[action?.delegateAccountID]) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. - const delegateAccount = getPersonalDetailByEmail(delegateEmail); - const delegateDetails = delegateAccount; - const delegateDisplayName = delegateDetails?.displayName; - displayName = delegateDisplayName ?? ''; - avatarSource = delegateDetails?.avatar; - avatarId = delegateAccount?.accountID; + displayName = delegatePersonalDetails?.displayName ?? ''; + avatarSource = delegatePersonalDetails?.avatar; + avatarId = delegatePersonalDetails?.accountID; } else if (isReportPreviewAction && isTripRoom) { displayName = report?.reportName ?? ''; } From 53e5e6136ebdb1e3daacab772deeb6fdc591ed22 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 17:47:44 +0530 Subject: [PATCH 047/180] fix lint --- src/pages/home/report/ReportActionItemSingle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 14b00ab2e986..d69f47fe6a5a 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -21,7 +21,7 @@ import ControlSelection from '@libs/ControlSelection'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; -import {getOriginalMessage, getReportActionMessage} from '@libs/ReportActionsUtils'; +import {getReportActionMessage} from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From d8cccb625a827bafebe420098cd4e5e35921c3b4 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 18:07:17 +0530 Subject: [PATCH 048/180] add delegate AccountID for IOU --- src/libs/ReportUtils.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ffe970758b56..754b32b7d352 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -180,6 +180,7 @@ type OptimisticIOUReportAction = Pick< | 'childReportID' | 'childVisibleActionCount' | 'childCommenterCount' + | 'delegateAccountID' >; type PartialReportAction = OnyxInputOrEntry | Partial | OptimisticIOUReportAction | OptimisticApprovedReportAction | OptimisticSubmittedReportAction | undefined; @@ -4593,6 +4594,8 @@ function buildOptimisticIOUReportAction( delegate, }; + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + if (type === CONST.IOU.REPORT_ACTION_TYPE.PAY) { // In pay someone flow, we store amount, comment, currency in IOUDetails when type = pay if (isSendMoneyFlow) { @@ -4644,6 +4647,7 @@ function buildOptimisticIOUReportAction( shouldShow: true, created, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + delegateAccountID: delegateAccountDetails?.accountID, }; } From c500a0b0b386d39f02b0c8db594e206b0b69c04e Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 6 Sep 2024 18:49:53 +0530 Subject: [PATCH 049/180] Update AvatarWithDelegateAvatar.tsx --- src/pages/home/sidebar/AvatarWithDelegateAvatar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/AvatarWithDelegateAvatar.tsx b/src/pages/home/sidebar/AvatarWithDelegateAvatar.tsx index 0192d3d8423a..05f4c5aec343 100644 --- a/src/pages/home/sidebar/AvatarWithDelegateAvatar.tsx +++ b/src/pages/home/sidebar/AvatarWithDelegateAvatar.tsx @@ -9,7 +9,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ProfileAvatarWithIndicator from './ProfileAvatarWithIndicator'; type AvatarWithDelegateAvatarProps = { - /** Emoji status */ + /** Original account of delegate */ delegateEmail: string; /** Whether the avatar is selected */ From 376502a7ab59f1e16765eee27e3b7c83a8ae5bff Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Tue, 10 Sep 2024 21:07:58 +0530 Subject: [PATCH 050/180] lint fix --- tests/actions/PolicyTest.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 487b9e62f8c9..2ede9f5e5228 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -12,7 +12,6 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; const ESH_EMAIL = 'eshgupta1217@gmail.com'; const ESH_ACCOUNT_ID = 1; -const ESH_PARTICIPANT_ANNOUNCE_ROOM: Participant = {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}; const ESH_PARTICIPANT_ADMINS_ROOM: Participant = {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}; const ESH_PARTICIPANT_EXPENSE_CHAT = {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}; const WORKSPACE_NAME = "Esh's Workspace"; @@ -74,7 +73,7 @@ describe('actions/Policy', () => { }); }); - // Three reports should be created: #announce, #admins and expense report + // Two reports should be created: #admins and expense report const workspaceReports = Object.values(allReports ?? {}).filter((report) => report?.policyID === policyID); expect(workspaceReports.length).toBe(2); workspaceReports.forEach((report) => { From 76ec71a9b7062f4191543f05362c1ac69e2414dc Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Tue, 10 Sep 2024 22:41:40 +0530 Subject: [PATCH 051/180] pass announce params to addmemberstoworkspace --- .../parameters/AddMembersToWorkspaceParams.ts | 2 ++ src/libs/ReportUtils.ts | 20 ++++++++++++++++--- src/libs/actions/Policy/Member.ts | 5 ++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libs/API/parameters/AddMembersToWorkspaceParams.ts b/src/libs/API/parameters/AddMembersToWorkspaceParams.ts index 4e96fd07d301..abfed55e2df3 100644 --- a/src/libs/API/parameters/AddMembersToWorkspaceParams.ts +++ b/src/libs/API/parameters/AddMembersToWorkspaceParams.ts @@ -3,6 +3,8 @@ type AddMembersToWorkspaceParams = { welcomeNote: string; policyID: string; reportCreationData?: string; + announceChatReportID?: string; + announceCreatedReportActionID?: string; }; export default AddMembersToWorkspaceParams; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a7a90f987c82..38c49c7661e3 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -327,6 +327,12 @@ type AnnounceRoomOnyxData = { onyxFailureData: OnyxUpdate[]; }; +type OptimisticAnnounceChat = { + announceChatReportID: string; + announceChatReportActionID: string; + announceChatData: AnnounceRoomOnyxData; +}; + type OptimisticWorkspaceChats = { adminsChatReportID: string; adminsChatData: OptimisticChatReport; @@ -5494,7 +5500,7 @@ function buildOptimisticDismissedViolationReportAction( }; } -function buildOptimisticAnnounceChat(policyID: string, accountIDs: number[]): AnnounceRoomOnyxData { +function buildOptimisticAnnounceChat(policyID: string, accountIDs: number[]): OptimisticAnnounceChat { const announceReport = getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); const policy = getPolicy(policyID); const announceRoomOnyxData: AnnounceRoomOnyxData = { @@ -5505,7 +5511,11 @@ function buildOptimisticAnnounceChat(policyID: string, accountIDs: number[]): An // Do not create #announce room if the room already exists or if there are less than 3 participants in workspace if (accountIDs.length < 3 || announceReport) { - return announceRoomOnyxData; + return { + announceChatReportID: '', + announceChatReportActionID: '', + announceChatData: announceRoomOnyxData, + }; } const announceChatData = buildOptimisticChatReport( @@ -5589,7 +5599,11 @@ function buildOptimisticAnnounceChat(policyID: string, accountIDs: number[]): An }, }, ); - return announceRoomOnyxData; + return { + announceChatReportID: announceChatData.reportID, + announceChatReportActionID: announceCreatedAction.reportActionID, + announceChatData: announceRoomOnyxData, + }; } function buildOptimisticWorkspaceChats(policyID: string, policyName: string, expenseReportId?: string): OptimisticWorkspaceChats { diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index 1f33c769dca1..0dad5632baed 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -575,7 +575,8 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount const newPersonalDetailsOnyxData = PersonalDetailsUtils.getPersonalDetailsOnyxDataForOptimisticUsers(newLogins, newAccountIDs); const announceRoomMembers = buildAnnounceRoomMembersOnyxData(policyID, accountIDs); - const announceRoomChat = ReportUtils.buildOptimisticAnnounceChat(policyID, [...policyMemberAccountIDs, ...accountIDs]); + const optimisticAnnounceChat = ReportUtils.buildOptimisticAnnounceChat(policyID, [...policyMemberAccountIDs, ...accountIDs]); + const announceRoomChat = optimisticAnnounceChat.announceChatData; // create onyx data for policy expense chats for each new member const membersChats = createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs); @@ -631,6 +632,8 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount const params: AddMembersToWorkspaceParams = { employees: JSON.stringify(logins.map((login) => ({email: login}))), + ...(optimisticAnnounceChat.announceChatReportID ? {announceChatReportID: optimisticAnnounceChat.announceChatReportID} : {}), + ...(optimisticAnnounceChat.announceChatReportActionID ? {announceCreatedReportActionID: optimisticAnnounceChat.announceChatReportActionID} : {}), welcomeNote: Parser.replace(welcomeNote), policyID, }; From 3c4b392981e8413e6bc71b96091c039bc4941bba Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 16 Sep 2024 14:49:55 +0200 Subject: [PATCH 052/180] refocus when RHP closed --- .../ComposerWithSuggestions/ComposerWithSuggestions.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 0a8070130053..ddb64435f77a 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -672,7 +672,9 @@ function ComposerWithSuggestions( if (editFocused) { InputFocus.inputFocusChange(false); + return; } + focus(true); }, [focus, prevIsFocused, editFocused, prevIsModalVisible, isFocused, modal?.isVisible, isNextModalWillOpenRef, shouldAutoFocus]); useEffect(() => { From f5d39d8f3a1e3a9a632a4fbee9471a06586c1846 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 16 Sep 2024 14:01:18 -1000 Subject: [PATCH 053/180] Use float instead of int for auditRate percentage --- src/CONST.ts | 2 +- src/libs/actions/Policy/Policy.ts | 5 +++-- src/pages/workspace/rules/ExpenseReportRulesSection.tsx | 2 +- src/pages/workspace/rules/RulesRandomReportAuditPage.tsx | 3 +-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ead9a9eda8ee..20eab24cf856 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2173,7 +2173,7 @@ const CONST = { AUTO_REIMBURSEMENT_MAX_LIMIT_CENTS: 2000000, AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS: 10000, AUTO_APPROVE_REPORTS_UNDER_DEFAULT_CENTS: 10000, - RANDOM_AUDIT_DEFAULT_PERCENTAGE: 5, + RANDOM_AUDIT_DEFAULT_PERCENTAGE: 0.05, AUTO_REPORTING_FREQUENCIES: { INSTANT: 'instant', diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index e30ddaf97e1f..18f5a3495304 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -4248,9 +4248,10 @@ function setPolicyAutomaticApprovalLimit(policyID: string, limit: string) { function setPolicyAutomaticApprovalRate(policyID: string, auditRate: string) { const policy = getPolicy(policyID); const fallbackAuditRate = auditRate === '' ? '0' : auditRate; - const parsedAuditRate = parseInt(fallbackAuditRate, 10); + const parsedAuditRate = parseInt(fallbackAuditRate, 10) / 100; - if (parsedAuditRate === policy?.autoApproval?.auditRate ?? CONST.POLICY.RANDOM_AUDIT_DEFAULT_PERCENTAGE) { + // The auditRate arrives as an int to this method so we will convert it to a float before sending it to the API. + if (parsedAuditRate === (policy?.autoApproval?.auditRate ?? CONST.POLICY.RANDOM_AUDIT_DEFAULT_PERCENTAGE)) { return; } diff --git a/src/pages/workspace/rules/ExpenseReportRulesSection.tsx b/src/pages/workspace/rules/ExpenseReportRulesSection.tsx index 71fdc0a29eeb..d4d51eaeaeeb 100644 --- a/src/pages/workspace/rules/ExpenseReportRulesSection.tsx +++ b/src/pages/workspace/rules/ExpenseReportRulesSection.tsx @@ -133,7 +133,7 @@ function ExpenseReportRulesSection({policyID}: ExpenseReportRulesSectionProps) { Navigation.navigate(ROUTES.RULES_RANDOM_REPORT_AUDIT.getRoute(policyID))} diff --git a/src/pages/workspace/rules/RulesRandomReportAuditPage.tsx b/src/pages/workspace/rules/RulesRandomReportAuditPage.tsx index b127063251d3..ad5e24191ce9 100644 --- a/src/pages/workspace/rules/RulesRandomReportAuditPage.tsx +++ b/src/pages/workspace/rules/RulesRandomReportAuditPage.tsx @@ -32,8 +32,7 @@ function RulesRandomReportAuditPage({route}: RulesRandomReportAuditPageProps) { const styles = useThemeStyles(); const workflowApprovalsUnavailable = PolicyUtils.getWorkflowApprovalsUnavailable(policy); - const defaultValue = policy?.autoApproval?.auditRate ?? CONST.POLICY.RANDOM_AUDIT_DEFAULT_PERCENTAGE; - + const defaultValue = Math.round((policy?.autoApproval?.auditRate ?? CONST.POLICY.RANDOM_AUDIT_DEFAULT_PERCENTAGE) * 100); return ( Date: Mon, 16 Sep 2024 16:08:57 -1000 Subject: [PATCH 054/180] Simplify logic for autoReimbursement --- src/libs/actions/Policy/Policy.ts | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 18f5a3495304..04c8e76f2d07 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -4327,17 +4327,8 @@ function enableAutoApprovalOptions(policyID: string, enabled: boolean) { return; } - const autoApprovalCleanupValues = !enabled - ? { - pendingFields: { - limit: null, - auditRate: null, - }, - } - : {}; - const autoApprovalValues = !enabled ? {auditRate: CONST.POLICY.RANDOM_AUDIT_DEFAULT_PERCENTAGE, limit: CONST.POLICY.AUTO_APPROVE_REPORTS_UNDER_DEFAULT_CENTS} : {}; - const autoApprovalFailureValues = !enabled ? {autoApproval: {limit: policy?.autoApproval?.limit, auditRate: policy?.autoApproval?.auditRate, ...autoApprovalCleanupValues}} : {}; - + const autoApprovalValues = {auditRate: CONST.POLICY.RANDOM_AUDIT_DEFAULT_PERCENTAGE, limit: CONST.POLICY.AUTO_APPROVE_REPORTS_UNDER_DEFAULT_CENTS}; + const autoApprovalFailureValues = {autoApproval: {limit: policy?.autoApproval?.limit, auditRate: policy?.autoApproval?.auditRate, pendingFields: null}}; const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -4363,7 +4354,7 @@ function enableAutoApprovalOptions(policyID: string, enabled: boolean) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoApproval: {...autoApprovalCleanupValues}, + autoApproval: {pendingFields: null}, pendingFields: { shouldShowAutoApprovalOptions: null, }, @@ -4469,6 +4460,7 @@ function setPolicyAutoReimbursementLimit(policyID: string, limit: string) { /** * Call the API to enable auto-payment for the reports in the given policy + * * @param policyID - id of the policy to apply the limit to * @param enabled - whether auto-payment for the reports is enabled in the given policy */ @@ -4479,16 +4471,8 @@ function enablePolicyAutoReimbursementLimit(policyID: string, enabled: boolean) return; } - const autoReimbursementCleanupValues = !enabled - ? { - pendingFields: { - limit: null, - }, - } - : {}; - const autoReimbursementFailureValues = !enabled ? {autoReimbursement: {limit: policy?.autoReimbursement?.limit, ...autoReimbursementCleanupValues}} : {}; - const autoReimbursementValues = !enabled ? {limit: CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS} : {}; - + const autoReimbursementFailureValues = {autoReimbursement: {limit: policy?.autoReimbursement?.limit, pendingFields: null}}; + const autoReimbursementValues = {limit: CONST.POLICY.AUTO_REIMBURSEMENT_DEFAULT_LIMIT_CENTS}; const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -4513,7 +4497,7 @@ function enablePolicyAutoReimbursementLimit(policyID: string, enabled: boolean) onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReimbursement: {...autoReimbursementCleanupValues}, + autoReimbursement: {pendingFields: null}, pendingFields: { shouldShowAutoReimbursementLimitOption: null, }, From f527d62b2927e9d08bf1d87355c592ffe43679da Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 17 Sep 2024 17:12:41 -1000 Subject: [PATCH 055/180] Fix the params for SetPolicyAutoReimbursementLimit as they are incorrect --- src/libs/API/parameters/SetPolicyAutoReimbursementLimit.ts | 2 +- src/libs/actions/Policy/Policy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/API/parameters/SetPolicyAutoReimbursementLimit.ts b/src/libs/API/parameters/SetPolicyAutoReimbursementLimit.ts index 7c6a721e03b0..b743369db926 100644 --- a/src/libs/API/parameters/SetPolicyAutoReimbursementLimit.ts +++ b/src/libs/API/parameters/SetPolicyAutoReimbursementLimit.ts @@ -1,6 +1,6 @@ type SetPolicyAutoReimbursementLimitParams = { policyID: string; - autoReimbursement: {limit: number}; + limit: number; }; export default SetPolicyAutoReimbursementLimitParams; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 04c8e76f2d07..042e5f9ad0ad 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -4447,7 +4447,7 @@ function setPolicyAutoReimbursementLimit(policyID: string, limit: string) { ]; const parameters: SetPolicyAutoReimbursementLimitParams = { - autoReimbursement: {limit: parsedLimit}, + limit: parsedLimit, policyID, }; From 110c6933e87d4108c757435dcc747cd325fbf768 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 18 Sep 2024 12:42:55 +0200 Subject: [PATCH 056/180] focus after emojis --- src/components/EmojiPicker/EmojiPickerButton.tsx | 4 ++-- .../ReportActionCompose/ReportActionCompose.tsx | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPickerButton.tsx b/src/components/EmojiPicker/EmojiPickerButton.tsx index 07badc1799b8..412c6655bdb0 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.tsx +++ b/src/components/EmojiPicker/EmojiPickerButton.tsx @@ -28,12 +28,12 @@ type EmojiPickerButtonProps = { /** Emoji popup anchor offset shift vertical */ shiftVertical?: number; - onModalHide?: EmojiPickerAction.OnModalHideValue; + onModalHide: EmojiPickerAction.OnModalHideValue; onEmojiSelected: EmojiPickerAction.OnEmojiSelected; }; -function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shiftVertical = 0, onPress, onModalHide = () => {}, onEmojiSelected}: EmojiPickerButtonProps) { +function EmojiPickerButton({isDisabled = false, id = '', emojiPickerID = '', shiftVertical = 0, onPress, onModalHide, onEmojiSelected}: EmojiPickerButtonProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const emojiPopoverAnchor = useRef(null); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index b091485443ca..d3dbb7ac8927 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -208,6 +208,13 @@ function ReportActionCompose({ return translate('reportActionCompose.writeSomething'); }, [includesConcierge, translate, userBlockedFromConcierge]); + const focus = () => { + if (composerRef.current === null) { + return; + } + composerRef.current?.focus(true); + }; + const isKeyboardVisibleWhenShowingModalRef = useRef(false); const isNextModalWillOpenRef = useRef(false); @@ -514,6 +521,12 @@ function ReportActionCompose({ {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( { + if (isNavigating) { + return; + } + focus(); + }} onEmojiSelected={(...args) => composerRef.current?.replaceSelectionWithText(...args)} emojiPickerID={report?.reportID} shiftVertical={emojiShiftVertical} From 138290acd9b76c9368ae6fc951f513ced967155c Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 18 Sep 2024 16:49:33 +0200 Subject: [PATCH 057/180] refactor AttachmentPickerWithMenuItems --- .../AttachmentPickerWithMenuItems.tsx | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index ea23759edbf2..526e13b02c01 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentPicker from '@components/AttachmentPicker'; import Icon from '@components/Icon'; @@ -33,12 +33,7 @@ import type * as OnyxTypes from '@src/types/onyx'; type MoneyRequestOptions = Record, PopoverMenuItem>; -type AttachmentPickerWithMenuItemsOnyxProps = { - /** The policy tied to the report */ - policy: OnyxEntry; -}; - -type AttachmentPickerWithMenuItemsProps = AttachmentPickerWithMenuItemsOnyxProps & { +type AttachmentPickerWithMenuItemsProps = { /** The report currently being looked at */ report: OnyxEntry; @@ -97,7 +92,6 @@ type AttachmentPickerWithMenuItemsProps = AttachmentPickerWithMenuItemsOnyxProps */ function AttachmentPickerWithMenuItems({ report, - policy, reportParticipantIDs, displayFileInModal, isFullComposerAvailable, @@ -121,6 +115,9 @@ function AttachmentPickerWithMenuItems({ const {translate} = useLocalize(); const {windowHeight, windowWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, { + initialValue: {} as OnyxTypes.Policy, + }); /** * Returns the list of IOU Options @@ -336,9 +333,4 @@ function AttachmentPickerWithMenuItems({ AttachmentPickerWithMenuItems.displayName = 'AttachmentPickerWithMenuItems'; -export default withOnyx({ - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, - initialValue: {} as OnyxTypes.Policy, - }, -})(AttachmentPickerWithMenuItems); +export default AttachmentPickerWithMenuItems; From 522277ccd4a3f310bee2d0fdca08ec7a56a13084 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 18 Sep 2024 16:52:09 +0200 Subject: [PATCH 058/180] refactor ReportActionCompose --- .../ReportActionCompose.tsx | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index d3dbb7ac8927..f48bb8f9b6a4 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -4,7 +4,7 @@ import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 're import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import {runOnUI, useSharedValue} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; @@ -63,16 +63,7 @@ type SuggestionsRef = { getIsSuggestionsMenuVisible: () => boolean; }; -type ReportActionComposeOnyxProps = { - /** The NVP describing a user's block status */ - blockedFromConcierge: OnyxEntry; - - /** Whether the composer input should be shown */ - shouldShowComposeInput: OnyxEntry; -}; - -type ReportActionComposeProps = ReportActionComposeOnyxProps & - WithCurrentUserPersonalDetailsProps & +type ReportActionComposeProps = WithCurrentUserPersonalDetailsProps & Pick & { /** A method to call when the form is submitted */ onSubmit: (newComment: string) => void; @@ -109,7 +100,6 @@ const willBlurTextInputOnTapOutside = willBlurTextInputOnTapOutsideFunc(); let onSubmitAction = noop; function ReportActionCompose({ - blockedFromConcierge, currentUserPersonalDetails, disabled = false, isComposerFullSize = false, @@ -117,7 +107,6 @@ function ReportActionCompose({ pendingAction, report, reportID, - shouldShowComposeInput = true, isReportReadyForDisplay = true, isEmptyChat, lastReportAction, @@ -133,6 +122,10 @@ function ReportActionCompose({ const actionButtonRef = useRef(null); const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const navigation = useNavigation(); + const [blockedFromConcierge] = useOnyx(ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE); + const [shouldShowComposeInput] = useOnyx(ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, { + initialValue: true, + }); /** * Updates the Highlight state of the composer @@ -558,15 +551,6 @@ function ReportActionCompose({ ReportActionCompose.displayName = 'ReportActionCompose'; -export default withCurrentUserPersonalDetails( - withOnyx({ - blockedFromConcierge: { - key: ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE, - }, - shouldShowComposeInput: { - key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, - }, - })(memo(ReportActionCompose)), -); +export default withCurrentUserPersonalDetails(memo(ReportActionCompose)); export {onSubmitAction}; export type {SuggestionsRef, ComposerRef}; From 36cf24fe4d7dcbb748a3da4f411b4c4bfc6126ed Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 18 Sep 2024 23:31:09 +0700 Subject: [PATCH 059/180] fix ws name and avatar are not updated dynamically --- src/libs/ReportUtils.ts | 2 +- src/pages/home/HeaderView.tsx | 4 +- .../home/report/ReportActionItemCreated.tsx | 57 +++---------------- .../home/report/ReportActionItemSingle.tsx | 6 +- 4 files changed, 16 insertions(+), 53 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f37f3f940516..41b9f922cba9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1955,7 +1955,7 @@ function getWorkspaceIcon(report: OnyxInputOrEntry, policy?: OnyxInputOr const iconFromCache = workSpaceIconsCache.get(cacheKey); // disabling to protect against empty strings // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const policyAvatarURL = report?.policyAvatar || allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatarURL; + const policyAvatarURL = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.avatarURL || report?.policyAvatar; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const policyExpenseChatAvatarSource = policyAvatarURL || getDefaultWorkspaceAvatar(workspaceName); diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 217686c0cad1..17c5fba500fe 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -78,7 +78,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto const isTaskReport = ReportUtils.isTaskReport(report); const reportHeaderData = !isTaskReport && !isChatThread && report.parentReportID ? parentReport : report; // Use sorted display names for the title for group chats on native small screen widths - const title = ReportUtils.getReportName(reportHeaderData, undefined, parentReportAction, personalDetails, invoiceReceiverPolicy); + const title = ReportUtils.getReportName(reportHeaderData, policy, parentReportAction, personalDetails, invoiceReceiverPolicy); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const reportDescription = ReportUtils.getReportDescriptionText(report); @@ -124,7 +124,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto const shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report); const defaultSubscriptSize = ReportUtils.isExpenseRequest(report) ? CONST.AVATAR_SIZE.SMALL_NORMAL : CONST.AVATAR_SIZE.DEFAULT; - const icons = ReportUtils.getIcons(reportHeaderData, personalDetails, null, '', -1, undefined, invoiceReceiverPolicy); + const icons = ReportUtils.getIcons(reportHeaderData, personalDetails, null, '', -1, policy, invoiceReceiverPolicy); const brickRoadIndicator = ReportUtils.hasReportNameError(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; const shouldShowBorderBottom = !isTaskReport || !shouldUseNarrowLayout; const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(report); diff --git a/src/pages/home/report/ReportActionItemCreated.tsx b/src/pages/home/report/ReportActionItemCreated.tsx index ec9f5ea9915f..26d6d7e8f3c2 100644 --- a/src/pages/home/report/ReportActionItemCreated.tsx +++ b/src/pages/home/report/ReportActionItemCreated.tsx @@ -1,34 +1,21 @@ -import lodashIsEqual from 'lodash/isEqual'; import React, {memo} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ReportWelcomeText from '@components/ReportWelcomeText'; import useLocalize from '@hooks/useLocalize'; +import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ReportUtils from '@libs/ReportUtils'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetailsList, Policy, Report} from '@src/types/onyx'; import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground'; -type ReportActionItemCreatedOnyxProps = { - /** The report currently being looked at */ - report: OnyxEntry; - - /** The policy object for the current route */ - policy: OnyxEntry; - - /** Personal details of all the users */ - personalDetails: OnyxEntry; -}; - -type ReportActionItemCreatedProps = ReportActionItemCreatedOnyxProps & { +type ReportActionItemCreatedProps = { /** The id of the report */ reportID: string; @@ -36,7 +23,10 @@ type ReportActionItemCreatedProps = ReportActionItemCreatedOnyxProps & { // eslint-disable-next-line react/no-unused-prop-types policyID: string | undefined; }; -function ReportActionItemCreated({report, personalDetails, policy, reportID}: ReportActionItemCreatedProps) { +function ReportActionItemCreated({policyID, reportID}: ReportActionItemCreatedProps) { + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const policy = usePolicy(policyID); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -47,7 +37,7 @@ function ReportActionItemCreated({report, personalDetails, policy, reportID}: Re return null; } - let icons = ReportUtils.getIcons(report, personalDetails, null, '', -1, undefined, invoiceReceiverPolicy); + let icons = ReportUtils.getIcons(report, personalDetails, null, '', -1, policy, invoiceReceiverPolicy); const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(report); if (ReportUtils.isInvoiceRoom(report) && ReportUtils.isCurrentUserInvoiceReceiver(report)) { @@ -98,33 +88,4 @@ function ReportActionItemCreated({report, personalDetails, policy, reportID}: Re ReportActionItemCreated.displayName = 'ReportActionItemCreated'; -export default withOnyx({ - report: { - key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - }, - - policy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, - - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, -})( - memo( - ReportActionItemCreated, - (prevProps, nextProps) => - prevProps.policy?.name === nextProps.policy?.name && - prevProps.policy?.avatarURL === nextProps.policy?.avatarURL && - prevProps.report?.stateNum === nextProps.report?.stateNum && - prevProps.report?.statusNum === nextProps.report?.statusNum && - prevProps.report?.lastReadTime === nextProps.report?.lastReadTime && - prevProps.report?.description === nextProps.report?.description && - prevProps.personalDetails === nextProps.personalDetails && - prevProps.policy?.description === nextProps.policy?.description && - prevProps.report?.reportName === nextProps.report?.reportName && - prevProps.report?.avatarUrl === nextProps.report?.avatarUrl && - lodashIsEqual(prevProps.report?.invoiceReceiver, nextProps.report?.invoiceReceiver) && - prevProps.report?.errorFields === nextProps.report?.errorFields, - ), -); +export default memo(ReportActionItemCreated); diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 0e58296d39f2..491c2877955a 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -14,6 +14,7 @@ import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; import useLocalize from '@hooks/useLocalize'; +import usePolicy from '@hooks/usePolicy'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -82,6 +83,7 @@ function ReportActionItemSingle({ const {translate} = useLocalize(); const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); + const policy = usePolicy(report.policyID); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); @@ -98,9 +100,9 @@ function ReportActionItemSingle({ let avatarId: number | string | undefined = actorAccountID; if (isWorkspaceActor) { - displayName = ReportUtils.getPolicyName(report); + displayName = ReportUtils.getPolicyName(report, undefined, policy); actorHint = displayName; - avatarSource = ReportUtils.getWorkspaceIcon(report).source; + avatarSource = ReportUtils.getWorkspaceIcon(report, policy).source; avatarId = report.policyID; } else if (action?.delegateAccountID && personalDetails[action?.delegateAccountID]) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their From 0b0f3b20b524a6b28325583bd84add4b088c1dc9 Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Thu, 19 Sep 2024 14:49:22 +0530 Subject: [PATCH 060/180] cleanup --- src/libs/actions/IOU.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e2b374e9829c..67a8910c83c7 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6635,8 +6635,6 @@ function getPayMoneyRequestParams( params, } = Policy.buildPolicyData(currentUserEmail, true, undefined, payerPolicyID); const { - announceChatReportID, - announceCreatedReportActionID, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, @@ -6649,8 +6647,6 @@ function getPayMoneyRequestParams( policyParams = { policyID: payerPolicyID, - announceChatReportID, - announceCreatedReportActionID, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, @@ -7561,8 +7557,6 @@ function payInvoice(paymentMethodType: PaymentMethodType, chatReport: OnyxTypes. params: { reportActionID, policyID, - announceChatReportID, - announceCreatedReportActionID, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, @@ -7588,8 +7582,6 @@ function payInvoice(paymentMethodType: PaymentMethodType, chatReport: OnyxTypes. params = { ...params, policyID, - announceChatReportID, - announceCreatedReportActionID, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, From 568e66c55b42b4fdde143e52c79c59f8cb3a0d39 Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Thu, 19 Sep 2024 15:00:42 +0530 Subject: [PATCH 061/180] prettier fix --- src/libs/actions/IOU.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 67a8910c83c7..bae17d09a2e8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6634,16 +6634,7 @@ function getPayMoneyRequestParams( successData: policySuccessData, params, } = Policy.buildPolicyData(currentUserEmail, true, undefined, payerPolicyID); - const { - adminsChatReportID, - adminsCreatedReportActionID, - expenseChatReportID, - expenseCreatedReportActionID, - customUnitRateID, - customUnitID, - ownerEmail, - policyName, - } = params; + const {adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, expenseCreatedReportActionID, customUnitRateID, customUnitID, ownerEmail, policyName} = params; policyParams = { policyID: payerPolicyID, From bdba824643b7542c00253f0d7e0b2f5d854da9ef Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 20 Sep 2024 15:41:22 +0700 Subject: [PATCH 062/180] fix: Search Save add Onyx optimistic and failure data --- src/ONYXKEYS.ts | 2 +- src/libs/actions/Search.ts | 71 ++++++++++++++++++++++++++++- src/pages/Search/SearchTypeMenu.tsx | 3 +- src/types/onyx/SaveSearch.ts | 6 ++- 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 68a9ca2f8502..e425bc69bcb8 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -860,7 +860,7 @@ type OnyxValuesMapping = { // ONYXKEYS.NVP_TRYNEWDOT is HybridApp onboarding data [ONYXKEYS.NVP_TRYNEWDOT]: OnyxTypes.TryNewDot; - [ONYXKEYS.SAVED_SEARCHES]: OnyxTypes.SaveSearch[]; + [ONYXKEYS.SAVED_SEARCHES]: OnyxTypes.SaveSearch; [ONYXKEYS.RECENTLY_USED_CURRENCIES]: string[]; [ONYXKEYS.ACTIVE_CLIENTS]: string[]; [ONYXKEYS.DEVICE_ID]: string; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index a4f0e59ef976..952763c1d313 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -55,11 +55,78 @@ function saveSearch({queryJSON, name}: {queryJSON: SearchQueryJSON; name?: strin const saveSearchName = name ?? queryJSON?.inputQuery ?? ''; const jsonQuery = JSON.stringify(queryJSON); - API.write(WRITE_COMMANDS.SAVE_SEARCH, {jsonQuery, name: saveSearchName}); + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.SAVED_SEARCHES}`, + value: { + [queryJSON.hash]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + name: saveSearchName, + query: queryJSON.inputQuery, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.SAVED_SEARCHES}`, + value: { + [queryJSON.hash]: null, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.SAVED_SEARCHES}`, + value: { + [queryJSON.hash]: { + pendingAction: null, + }, + }, + }, + ]; + API.write(WRITE_COMMANDS.SAVE_SEARCH, {jsonQuery, name: saveSearchName}, {optimisticData, failureData, successData}); } function deleteSavedSearch(hash: number) { - API.write(WRITE_COMMANDS.DELETE_SAVED_SEARCH, {hash}); + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.SAVED_SEARCHES}`, + value: { + [hash]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }, + }, + }, + ]; + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.SAVED_SEARCHES}`, + value: { + [hash]: null, + }, + }, + ]; + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.SAVED_SEARCHES}`, + value: { + [hash]: { + pendingAction: null, + }, + }, + }, + ]; + + API.write(WRITE_COMMANDS.DELETE_SAVED_SEARCH, {hash}, {optimisticData, failureData, successData}); } function search({queryJSON, offset}: {queryJSON: SearchQueryJSON; offset?: number}) { diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index 7c8af2388f52..e5e44169635d 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -34,7 +34,7 @@ import type IconAsset from '@src/types/utils/IconAsset'; import SavedSearchItemThreeDotMenu from './SavedSearchItemThreeDotMenu'; import SearchTypeMenuNarrow from './SearchTypeMenuNarrow'; -type SavedSearchMenuItem = MenuItemBaseProps & { +type SavedSearchMenuItem = MenuItemWithLink & { key: string; hash: string; query: string; @@ -119,6 +119,7 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { }, rightComponent: , styles: [styles.alignItemsCenter], + pendingAction: item.pendingAction, }; if (!isNarrow) { diff --git a/src/types/onyx/SaveSearch.ts b/src/types/onyx/SaveSearch.ts index d8f8bf32f2a1..129e063f77a3 100644 --- a/src/types/onyx/SaveSearch.ts +++ b/src/types/onyx/SaveSearch.ts @@ -1,13 +1,15 @@ +import type * as OnyxCommon from './OnyxCommon'; + /** * Model of a single saved search */ -type SaveSearchItem = { +type SaveSearchItem = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Name of the saved search */ name: string; /** Query string for the saved search */ query: string; -}; +}>; /** * Model of saved searches From 6e59afcac8920ba482374472e1c679509be5abba Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 20 Sep 2024 15:56:31 +0700 Subject: [PATCH 063/180] fix lint --- src/pages/Search/SearchTypeMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index e5e44169635d..08340fe0279e 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -4,7 +4,6 @@ import {View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {ScrollView as RNScrollView, ScrollViewProps, TextStyle, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; -import type {MenuItemBaseProps} from '@components/MenuItem'; import MenuItem from '@components/MenuItem'; import MenuItemList from '@components/MenuItemList'; import type {MenuItemWithLink} from '@components/MenuItemList'; @@ -179,6 +178,7 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { if (!savedSearches) { return []; } + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return Object.entries(savedSearches).map(([key, item]) => createSavedSearchMenuItem(item as SaveSearchItem, key, shouldUseNarrowLayout)); }; From 9f7e4649a03700be35e91a8867422d1afead7082 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 20 Sep 2024 17:53:55 +0700 Subject: [PATCH 064/180] fix: add offline feedback in narrow --- src/components/PopoverMenu.tsx | 90 ++++++++++++----------- src/pages/Search/SearchTypeMenuNarrow.tsx | 7 +- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 3b074bf772e6..e3a04903f5ca 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -14,6 +14,7 @@ import * as Browser from '@libs/Browser'; import * as Modal from '@userActions/Modal'; import CONST from '@src/CONST'; import type {AnchorPosition} from '@src/styles'; +import type {PendingAction} from '@src/types/onyx/OnyxCommon'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; import FocusableMenuItem from './FocusableMenuItem'; import FocusTrapForModal from './FocusTrap/FocusTrapForModal'; @@ -21,6 +22,7 @@ import * as Expensicons from './Icon/Expensicons'; import type {MenuItemProps} from './MenuItem'; import MenuItem from './MenuItem'; import type BaseModalProps from './Modal/types'; +import OfflineWithFeedback from './OfflineWithFeedback'; import PopoverWithMeasuredContent from './PopoverWithMeasuredContent'; import ScrollView from './ScrollView'; import Text from './Text'; @@ -48,6 +50,8 @@ type PopoverMenuItem = MenuItemProps & { /** Whether to close all modals */ shouldCloseAllModals?: boolean; + + pendingAction?: PendingAction; }; type PopoverModalProps = Pick; @@ -262,49 +266,53 @@ function PopoverMenu({ {renderHeaderText()} {enteredSubMenuIndexes.length > 0 && renderBackButtonItem()} {currentMenuItems.map((item, menuIndex) => ( - selectItem(menuIndex)} - focused={focusedIndex === menuIndex} - displayInDefaultIconColor={item.displayInDefaultIconColor} - shouldShowRightIcon={item.shouldShowRightIcon} - shouldShowRightComponent={item.shouldShowRightComponent} - iconRight={item.iconRight} - rightComponent={item.rightComponent} - shouldPutLeftPaddingWhenNoIcon={item.shouldPutLeftPaddingWhenNoIcon} - label={item.label} - style={{backgroundColor: item.isSelected ? theme.activeComponentBG : undefined}} - isLabelHoverable={item.isLabelHoverable} - floatRightAvatars={item.floatRightAvatars} - floatRightAvatarSize={item.floatRightAvatarSize} - shouldShowSubscriptRightAvatar={item.shouldShowSubscriptRightAvatar} - disabled={item.disabled} - onFocus={() => setFocusedIndex(menuIndex)} - success={item.success} - containerStyle={item.containerStyle} - shouldRenderTooltip={item.shouldRenderTooltip} - tooltipAnchorAlignment={item.tooltipAnchorAlignment} - tooltipShiftHorizontal={item.tooltipShiftHorizontal} - tooltipShiftVertical={item.tooltipShiftVertical} - tooltipWrapperStyle={item.tooltipWrapperStyle} - renderTooltipContent={item.renderTooltipContent} - numberOfLinesTitle={item.numberOfLinesTitle} - interactive={item.interactive} - isSelected={item.isSelected} - badgeText={item.badgeText} - /> + pendingAction={item.pendingAction} + > + selectItem(menuIndex)} + focused={focusedIndex === menuIndex} + displayInDefaultIconColor={item.displayInDefaultIconColor} + shouldShowRightIcon={item.shouldShowRightIcon} + shouldShowRightComponent={item.shouldShowRightComponent} + iconRight={item.iconRight} + rightComponent={item.rightComponent} + shouldPutLeftPaddingWhenNoIcon={item.shouldPutLeftPaddingWhenNoIcon} + label={item.label} + style={{backgroundColor: item.isSelected ? theme.activeComponentBG : undefined}} + isLabelHoverable={item.isLabelHoverable} + floatRightAvatars={item.floatRightAvatars} + floatRightAvatarSize={item.floatRightAvatarSize} + shouldShowSubscriptRightAvatar={item.shouldShowSubscriptRightAvatar} + disabled={item.disabled} + onFocus={() => setFocusedIndex(menuIndex)} + success={item.success} + containerStyle={item.containerStyle} + shouldRenderTooltip={item.shouldRenderTooltip} + tooltipAnchorAlignment={item.tooltipAnchorAlignment} + tooltipShiftHorizontal={item.tooltipShiftHorizontal} + tooltipShiftVertical={item.tooltipShiftVertical} + tooltipWrapperStyle={item.tooltipWrapperStyle} + renderTooltipContent={item.renderTooltipContent} + numberOfLinesTitle={item.numberOfLinesTitle} + interactive={item.interactive} + isSelected={item.isSelected} + badgeText={item.badgeText} + /> + ))} diff --git a/src/pages/Search/SearchTypeMenuNarrow.tsx b/src/pages/Search/SearchTypeMenuNarrow.tsx index 0158a15bfc41..0b55c32c2224 100644 --- a/src/pages/Search/SearchTypeMenuNarrow.tsx +++ b/src/pages/Search/SearchTypeMenuNarrow.tsx @@ -3,7 +3,7 @@ import {Animated, View} from 'react-native'; import type {TextStyle, ViewStyle} from 'react-native'; import Button from '@components/Button'; import Icon from '@components/Icon'; -import type {MenuItemBaseProps} from '@components/MenuItem'; +import type {MenuItemWithLink} from '@components/MenuItemList'; import PopoverMenu from '@components/PopoverMenu'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; @@ -26,7 +26,7 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {SearchTypeMenuItem} from './SearchTypeMenu'; -type SavedSearchMenuItem = MenuItemBaseProps & { +type SavedSearchMenuItem = MenuItemWithLink & { key: string; hash: string; query: string; @@ -121,8 +121,8 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, queryJSON, title, /> ), isSelected: currentSavedSearch?.hash === item.hash, + pendingAction: item.pendingAction, })); - const allMenuItems = []; allMenuItems.push(...popoverMenuItems); @@ -134,7 +134,6 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, queryJSON, title, }); allMenuItems.push(...savedSearchItems); } - return ( Date: Fri, 20 Sep 2024 18:03:34 +0700 Subject: [PATCH 065/180] remove disable eslint --- src/pages/Search/SearchTypeMenu.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index 08340fe0279e..940fbf959582 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -178,8 +178,7 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { if (!savedSearches) { return []; } - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - return Object.entries(savedSearches).map(([key, item]) => createSavedSearchMenuItem(item as SaveSearchItem, key, shouldUseNarrowLayout)); + return Object.entries(savedSearches).map(([key, item]) => createSavedSearchMenuItem(item ?? ({} as SaveSearchItem), key, shouldUseNarrowLayout)); }; const renderSavedSearchesSection = useCallback( From 5429647be60d3693b325c999fd0e4afd5ed65375 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 20 Sep 2024 18:15:43 +0700 Subject: [PATCH 066/180] update type --- src/pages/Search/SearchTypeMenu.tsx | 2 +- src/types/onyx/SaveSearch.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index 940fbf959582..adb680541882 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -178,7 +178,7 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { if (!savedSearches) { return []; } - return Object.entries(savedSearches).map(([key, item]) => createSavedSearchMenuItem(item ?? ({} as SaveSearchItem), key, shouldUseNarrowLayout)); + return Object.entries(savedSearches).map(([key, item]) => createSavedSearchMenuItem(item, key, shouldUseNarrowLayout)); }; const renderSavedSearchesSection = useCallback( diff --git a/src/types/onyx/SaveSearch.ts b/src/types/onyx/SaveSearch.ts index 129e063f77a3..6b3a903b1639 100644 --- a/src/types/onyx/SaveSearch.ts +++ b/src/types/onyx/SaveSearch.ts @@ -14,6 +14,6 @@ type SaveSearchItem = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** * Model of saved searches */ -type SaveSearch = Record; +type SaveSearch = Record; export type {SaveSearch, SaveSearchItem}; From 699edecd6b712490cb7fbc1daa0b68561c3b6fa4 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 20 Sep 2024 18:58:16 +0530 Subject: [PATCH 067/180] fix modified expense report actions --- src/libs/ReportUtils.ts | 29 +++++++++++++++++++++++++---- src/libs/actions/IOU.ts | 16 ++++++++++++---- src/libs/actions/Report.ts | 1 - src/types/onyx/OriginalMessage.ts | 3 +++ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 508e01faca7a..ea9a4d93e277 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -367,7 +367,19 @@ type OptimisticWorkspaceChats = { type OptimisticModifiedExpenseReportAction = Pick< ReportAction, - 'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'isAttachmentOnly' | 'message' | 'originalMessage' | 'person' | 'pendingAction' | 'reportActionID' | 'shouldShow' + | 'actionName' + | 'actorAccountID' + | 'automatic' + | 'avatar' + | 'created' + | 'isAttachmentOnly' + | 'message' + | 'originalMessage' + | 'person' + | 'pendingAction' + | 'reportActionID' + | 'shouldShow' + | 'delegateAccountID' > & {reportID?: string}; type OptimisticTaskReport = Pick< @@ -3475,13 +3487,14 @@ function getReportPreviewMessage( * At the moment, we only allow changing one transaction field at a time. */ function getModifiedExpenseOriginalMessage( + delegate: string, oldTransaction: OnyxInputOrEntry, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, policy: OnyxInputOrEntry, updatedTransaction?: OnyxInputOrEntry, ): OriginalMessageModifiedExpense { - const originalMessage: OriginalMessageModifiedExpense = {}; + const originalMessage: OriginalMessageModifiedExpense = {delegate: delegate}; // Remark: Comment field is the only one which has new/old prefixes for the keys (newComment/ oldComment), // all others have old/- pattern such as oldCreated/created if ('comment' in transactionChanges) { @@ -4948,6 +4961,7 @@ function buildOptimisticActionableTrackExpenseWhisper(iouAction: OptimisticIOURe * Builds an optimistic modified expense action with a randomly generated reportActionID. */ function buildOptimisticModifiedExpenseReportAction( + delegate: string, transactionThread: OnyxInputOrEntry, oldTransaction: OnyxInputOrEntry, transactionChanges: TransactionChanges, @@ -4955,7 +4969,9 @@ function buildOptimisticModifiedExpenseReportAction( policy: OnyxInputOrEntry, updatedTransaction?: OnyxInputOrEntry, ): OptimisticModifiedExpenseReportAction { - const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction); + const originalMessage = getModifiedExpenseOriginalMessage(delegate, oldTransaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + return { actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, actorAccountID: currentUserAccountID, @@ -4983,6 +4999,7 @@ function buildOptimisticModifiedExpenseReportAction( reportActionID: NumberUtils.rand64(), reportID: transactionThread?.reportID, shouldShow: true, + delegateAccountID: delegateAccountDetails?.accountID, }; } @@ -4991,7 +5008,9 @@ function buildOptimisticModifiedExpenseReportAction( * @param transactionThreadID - The reportID of the transaction thread * @param movedToReportID - The reportID of the report the transaction is moved to */ -function buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThreadID: string, movedToReportID: string): OptimisticModifiedExpenseReportAction { +function buildOptimisticMovedTrackedExpenseModifiedReportAction(delegate: string, transactionThreadID: string, movedToReportID: string): OptimisticModifiedExpenseReportAction { + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + return { actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, actorAccountID: currentUserAccountID, @@ -5009,6 +5028,7 @@ function buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThrea ], originalMessage: { movedToReportID, + delegate, }, person: [ { @@ -5021,6 +5041,7 @@ function buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThrea reportActionID: NumberUtils.rand64(), reportID: transactionThreadID, shouldShow: true, + delegateAccountID: delegateAccountDetails?.accountID, }; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 2c3a69bbced8..1a634a17086d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2552,7 +2552,15 @@ function getUpdateMoneyRequestParams( // - we're updating the distance rate while the waypoints are still pending // In these cases, there isn't a valid optimistic mileage data we can use, // and the report action is created on the server with the distance-related response from the MapBox API - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction( + delegateEmail, + transactionThread, + transaction, + transactionChanges, + isFromExpenseReport, + policy, + updatedTransaction, + ); if (!hasPendingWaypoints && !(hasModifiedDistanceRate && TransactionUtils.isFetchingWaypointsFromServer(transaction))) { params.reportActionID = updatedReportAction.reportActionID; @@ -2873,7 +2881,7 @@ function getUpdateTrackExpenseParams( // - we're updating the distance rate while the waypoints are still pending // In these cases, there isn't a valid optimistic mileage data we can use, // and the report action is created on the server with the distance-related response from the MapBox API - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, false, policy); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(delegateEmail, transactionThread, transaction, transactionChanges, false, policy); if (!hasPendingWaypoints && !(hasModifiedDistanceRate && TransactionUtils.isFetchingWaypointsFromServer(transaction))) { params.reportActionID = updatedReportAction.reportActionID; @@ -3239,7 +3247,7 @@ const getConvertTrackedExpenseInformation = ( failureData?.push(...deleteFailureData); // Build modified expense report action with the transaction changes - const modifiedExpenseReportAction = ReportUtils.buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThreadReportID, moneyRequestReportID); + const modifiedExpenseReportAction = ReportUtils.buildOptimisticMovedTrackedExpenseModifiedReportAction(delegateEmail, transactionThreadReportID, moneyRequestReportID); optimisticData?.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -5243,7 +5251,7 @@ function editRegularMoneyRequest( const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); // STEP 2: Build new modified expense report action. - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport, policy); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(delegateEmail, transactionThread, transaction, transactionChanges, isFromExpenseReport, policy); const updatedTransaction = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport) : null; // STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3d66cf592542..2b89b11b0433 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3373,7 +3373,6 @@ function completeOnboarding( let videoCommentAction: OptimisticAddCommentReportAction | null = null; let videoMessage: AddCommentOrAttachementParams | null = null; - if ('video' in data && data.video) { const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 2); videoCommentAction = videoComment.reportAction; diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 8a112edc80c1..11d6c361babb 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -361,6 +361,9 @@ type OriginalMessageModifiedExpense = { /** The ID of moved report */ movedToReportID?: string; + + /** Email of the delegate */ + delegate: string; }; /** Model of `reimbursement queued` report action */ From d5eb8af5bc0cd55b142ba3cfa0788d2a96afbdca Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 20 Sep 2024 19:19:28 +0530 Subject: [PATCH 068/180] remove delegate from original Message (Deprecated) --- src/libs/ReportUtils.ts | 14 +++----------- src/types/onyx/OriginalMessage.ts | 18 ------------------ 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ea9a4d93e277..4f72ef9682d7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3487,14 +3487,13 @@ function getReportPreviewMessage( * At the moment, we only allow changing one transaction field at a time. */ function getModifiedExpenseOriginalMessage( - delegate: string, oldTransaction: OnyxInputOrEntry, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, policy: OnyxInputOrEntry, updatedTransaction?: OnyxInputOrEntry, ): OriginalMessageModifiedExpense { - const originalMessage: OriginalMessageModifiedExpense = {delegate: delegate}; + const originalMessage: OriginalMessageModifiedExpense = {}; // Remark: Comment field is the only one which has new/old prefixes for the keys (newComment/ oldComment), // all others have old/- pattern such as oldCreated/created if ('comment' in transactionChanges) { @@ -4222,7 +4221,6 @@ function buildOptimisticAddCommentReportAction( originalMessage: { html: htmlForNewComment, whisperedTo: [], - delegate, }, isFirstItem: false, isAttachmentOnly, @@ -4311,7 +4309,6 @@ function buildOptimisticTaskCommentReportAction( html: ReportActionsUtils.getReportActionHtml(reportAction.reportAction), taskReportID: ReportActionsUtils.getReportActionMessage(reportAction.reportAction)?.taskReportID, whisperedTo: [], - delegate, }; reportAction.reportAction.childReportID = taskReportID; reportAction.reportAction.parentReportID = parentReportID; @@ -4657,8 +4654,7 @@ function buildOptimisticIOUReportAction( currency, IOUTransactionID: transactionID, IOUReportID, - type, - delegate, + type }; const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); @@ -4726,7 +4722,6 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e amount, currency, expenseReportID, - delegate, }; const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); @@ -4768,7 +4763,6 @@ function buildOptimisticUnapprovedReportAction(amount: number, currency: string, amount, currency, expenseReportID, - delegate, }, message: getIOUReportActionMessage(expenseReportID, CONST.REPORT.ACTIONS.TYPE.UNAPPROVED, Math.abs(amount), '', currency), person: [ @@ -4843,7 +4837,6 @@ function buildOptimisticSubmittedReportAction( amount, currency, expenseReportID, - delegate, }; const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); @@ -4969,7 +4962,7 @@ function buildOptimisticModifiedExpenseReportAction( policy: OnyxInputOrEntry, updatedTransaction?: OnyxInputOrEntry, ): OptimisticModifiedExpenseReportAction { - const originalMessage = getModifiedExpenseOriginalMessage(delegate, oldTransaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction); + const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction); const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); return { @@ -5028,7 +5021,6 @@ function buildOptimisticMovedTrackedExpenseModifiedReportAction(delegate: string ], originalMessage: { movedToReportID, - delegate, }, person: [ { diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 11d6c361babb..c330d009a43d 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -68,9 +68,6 @@ type OriginalMessageIOU = { /** Collection of accountIDs of users mentioned in message */ whisperedTo?: number[]; - - /** Email of the delegate */ - delegate: string; }; /** Names of moderation decisions */ @@ -127,9 +124,6 @@ type OriginalMessageAddComment = { /** List accountIDs are mentioned in message */ mentionedAccountIDs?: number[]; - - /** Email of the delegate */ - delegate: string; }; /** Model of `actionable mention whisper` report action */ @@ -163,9 +157,6 @@ type OriginalMessageSubmitted = { /** Report ID of the expense */ expenseReportID: string; - - /** Email of the delegate */ - delegate: string; }; /** Model of `closed` report action */ @@ -361,9 +352,6 @@ type OriginalMessageModifiedExpense = { /** The ID of moved report */ movedToReportID?: string; - - /** Email of the delegate */ - delegate: string; }; /** Model of `reimbursement queued` report action */ @@ -445,9 +433,6 @@ type OriginalMessageApproved = { /** Report ID of the expense */ expenseReportID: string; - - /** Email of the delegate */ - delegate: string; }; /** Model of `forwarded` report action */ @@ -507,9 +492,6 @@ type OriginalMessageUnapproved = { /** Report ID of the expense */ expenseReportID: string; - - /** Email of the delegate */ - delegate: string; }; /** Model of `Removed From Approval Chain` report action */ From 922953657a54aa33c31d18a70a2ccbd539997ebb Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 20 Sep 2024 19:25:44 +0530 Subject: [PATCH 069/180] fix prettier --- src/libs/ReportUtils.ts | 2 +- tests/actions/IOUTest.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4f72ef9682d7..f5bef248f57e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4654,7 +4654,7 @@ function buildOptimisticIOUReportAction( currency, IOUTransactionID: transactionID, IOUReportID, - type + type, }; const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index ecb3fb4f22cb..662eae8d7b21 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -423,7 +423,6 @@ describe('actions/IOU', () => { const comment = 'Giv money plz'; const chatReportID = '1234'; const iouReportID = '5678'; - const delegate = ''; let chatReport: OnyxEntry = { reportID: chatReportID, type: CONST.REPORT.TYPE.CHAT, @@ -467,7 +466,6 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, participantAccountIDs: [RORY_ACCOUNT_ID, CARLOS_ACCOUNT_ID], - delegate, }, }; let newIOUAction: OnyxEntry>; From d632c5f80e599c2e83d4768d49a7f77c405d2f8e Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 20 Sep 2024 20:26:49 +0530 Subject: [PATCH 070/180] add delegate for task --- src/libs/ReportUtils.ts | 25 ++++++++++++++++++------- src/libs/actions/Task.ts | 22 ++++++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f5bef248f57e..84abd96ab24e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -260,7 +260,7 @@ type OptimisticCancelPaymentReportAction = Pick< type OptimisticEditedTaskReportAction = Pick< ReportAction, - 'reportActionID' | 'actionName' | 'pendingAction' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'shouldShow' | 'message' | 'person' + 'reportActionID' | 'actionName' | 'pendingAction' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'shouldShow' | 'message' | 'person' | 'delegateAccountID' >; type OptimisticClosedReportAction = Pick< @@ -275,7 +275,7 @@ type OptimisticDismissedViolationReportAction = Pick< type OptimisticCreatedReportAction = Pick< ReportAction, - 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'pendingAction' | 'actionName' + 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'pendingAction' | 'actionName' | 'delegateAccountID' >; type OptimisticRenamedReportAction = Pick< @@ -348,6 +348,7 @@ type OptimisticTaskReportAction = Pick< | 'previousMessage' | 'errors' | 'linkMetadata' + | 'delegateAccountID' >; type OptimisticWorkspaceChats = { @@ -4286,6 +4287,7 @@ function updateOptimisticParentReportAction(parentReportAction: OnyxEntry, policy: OnyxEntry { + delegateEmail = value?.delegatedAccess?.delegate ?? ''; + }, +}); + /** * Clears out the task info from the store */ @@ -121,7 +129,7 @@ function createTaskAndNavigate( // Parent ReportAction indicating that a task has been created const optimisticTaskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmail); - const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assigneeAccountID, `task for ${title}`, parentReportID); + const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(delegateEmail, taskReportID, title, assigneeAccountID, `task for ${title}`, parentReportID); optimisticTaskReport.parentReportActionID = optimisticAddCommentReport.reportAction.reportActionID; const currentTime = DateUtils.getDBTimeWithSkew(); @@ -201,6 +209,7 @@ function createTaskAndNavigate( if (assigneeChatReport) { assigneeChatReportOnyxData = ReportUtils.getTaskAssigneeChatOnyxData( + delegateEmail, currentUserAccountID, assigneeAccountID, taskReportID, @@ -327,7 +336,7 @@ function getOutstandingChildTask(taskReport: OnyxEntry) { function completeTask(taskReport: OnyxEntry) { const taskReportID = taskReport?.reportID ?? '-1'; const message = `marked as complete`; - const completedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, message); + const completedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(delegateEmail, taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, message); const parentReport = getParentReport(taskReport); const optimisticData: OnyxUpdate[] = [ { @@ -413,7 +422,7 @@ function completeTask(taskReport: OnyxEntry) { function reopenTask(taskReport: OnyxEntry) { const taskReportID = taskReport?.reportID ?? '-1'; const message = `marked as incomplete`; - const reopenedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED, message); + const reopenedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(delegateEmail, taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED, message); const parentReport = getParentReport(taskReport); const hasOutstandingChildTask = taskReport?.managerID === currentUserAccountID ? true : parentReport?.hasOutstandingChildTask; @@ -493,7 +502,7 @@ function reopenTask(taskReport: OnyxEntry) { function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task) { // Create the EditedReportAction on the task - const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskFieldReportAction({title, description}); + const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskFieldReportAction({title, description}, delegateEmail); // Sometimes title or description is undefined, so we need to check for that, and we provide it to multiple functions const reportName = (title ?? report?.reportName)?.trim(); @@ -570,7 +579,7 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, assigneeEmail: string, assigneeAccountID: number | null = 0, assigneeChatReport?: OnyxEntry) { // Create the EditedReportAction on the task - const editTaskReportAction = ReportUtils.buildOptimisticChangedTaskAssigneeReportAction(assigneeAccountID ?? 0); + const editTaskReportAction = ReportUtils.buildOptimisticChangedTaskAssigneeReportAction(delegateEmail, assigneeAccountID ?? 0); const reportName = report.reportName?.trim(); let assigneeChatReportOnyxData; @@ -675,6 +684,7 @@ function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, as }; assigneeChatReportOnyxData = ReportUtils.getTaskAssigneeChatOnyxData( + delegateEmail, currentUserAccountID, assigneeAccountID, report.reportID, @@ -942,7 +952,7 @@ function deleteTask(report: OnyxEntry) { return; } const message = `deleted task: ${report.reportName}`; - const optimisticCancelReportAction = ReportUtils.buildOptimisticTaskReportAction(report.reportID ?? '-1', CONST.REPORT.ACTIONS.TYPE.TASK_CANCELLED, message); + const optimisticCancelReportAction = ReportUtils.buildOptimisticTaskReportAction(delegateEmail, report.reportID ?? '-1', CONST.REPORT.ACTIONS.TYPE.TASK_CANCELLED, message); const optimisticReportActionID = optimisticCancelReportAction.reportActionID; const parentReportAction = getParentReportAction(report); const parentReport = getParentReport(report); From 80e28ae7b1f103842016c4bcf4ce3d7f63c8495d Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 20 Sep 2024 20:37:49 +0530 Subject: [PATCH 071/180] fix lint --- src/libs/ReportUtils.ts | 2 +- src/libs/actions/Report.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 84abd96ab24e..1f2655efcecc 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5252,7 +5252,7 @@ function buildOptimisticGroupChatReport( * Returns the necessary reportAction onyx data to indicate that the chat has been created optimistically * @param [created] - Action created time */ -function buildOptimisticCreatedReportAction(emailCreatingAction: string, created = DateUtils.getDBTime() ): OptimisticCreatedReportAction { +function buildOptimisticCreatedReportAction(emailCreatingAction: string, created = DateUtils.getDBTime()): OptimisticCreatedReportAction { return { reportActionID: NumberUtils.rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 2b89b11b0433..731ba0bbc5b7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3404,6 +3404,7 @@ function completeOnboarding( ); const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(CONST.EMAIL.CONCIERGE); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( + delegateEmail, currentTask.reportID, task.title, 0, @@ -3415,7 +3416,7 @@ function completeOnboarding( currentTask.parentReportActionID = taskReportAction.reportAction.reportActionID; const completedTaskReportAction = task.autoCompleted - ? ReportUtils.buildOptimisticTaskReportAction(currentTask.reportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, 'marked as complete', actorAccountID, 2) + ? ReportUtils.buildOptimisticTaskReportAction(delegateEmail, currentTask.reportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, 'marked as complete', actorAccountID, 2) : null; return { From 90f28c6e4a5b719a4101f78b6f4fdb7fd4ad143a Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 20 Sep 2024 21:03:48 +0530 Subject: [PATCH 072/180] Do not display delegate message on report preview --- src/pages/home/report/ReportActionItemSingle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index bf802ad2249e..48baadad833f 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -288,7 +288,7 @@ function ReportActionItemSingle({ ) : null} - {delegateEmail && {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')}} + {(delegateEmail && !isReportPreviewAction) && {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')}} {children} From 35b5431ad70716bffc917886b312798e7891dead Mon Sep 17 00:00:00 2001 From: Gandalf Date: Fri, 20 Sep 2024 21:10:47 +0530 Subject: [PATCH 073/180] fix lint --- src/pages/home/report/ReportActionItemSingle.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 48baadad833f..8071c1157a4b 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -288,7 +288,9 @@ function ReportActionItemSingle({ ) : null} - {(delegateEmail && !isReportPreviewAction) && {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')}} + {delegateEmail && !isReportPreviewAction && ( + {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')} + )} {children} From 3c5bb16341e235ebd4c611dc666f9e1309129440 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Sat, 21 Sep 2024 11:12:57 +0530 Subject: [PATCH 074/180] update icon colors for SavedSearchThreeDotsMenu --- src/components/PopoverMenu.tsx | 5 +++++ src/components/ThreeDotsMenu/index.tsx | 2 ++ src/components/ThreeDotsMenu/types.ts | 3 +++ src/pages/Search/SavedSearchItemThreeDotMenu.tsx | 1 + 4 files changed, 11 insertions(+) diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 3b074bf772e6..83012a4b8c6e 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -103,6 +103,9 @@ type PopoverMenuProps = Partial & { /** Whether to show the selected option checkmark */ shouldShowSelectedItemCheck?: boolean; + + /** Is this in the Pane */ + isPaneMenu?: boolean; }; function PopoverMenu({ @@ -128,6 +131,7 @@ function PopoverMenu({ shouldEnableNewFocusManagement, restoreFocusType, shouldShowSelectedItemCheck = false, + isPaneMenu = false }: PopoverMenuProps) { const styles = useThemeStyles(); const theme = useTheme(); @@ -304,6 +308,7 @@ function PopoverMenu({ interactive={item.interactive} isSelected={item.isSelected} badgeText={item.badgeText} + isPaneMenu={isPaneMenu} /> ))} diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index da72135c6035..4df3238c12be 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -37,6 +37,7 @@ function ThreeDotsMenu({ shouldSetModalVisibility = true, disabled = false, modal = {}, + isPaneMenu = false }: ThreeDotsMenuProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -107,6 +108,7 @@ function ThreeDotsMenu({ shouldSetModalVisibility={shouldSetModalVisibility} anchorRef={buttonRef} shouldEnableNewFocusManagement + isPaneMenu={isPaneMenu} /> ); diff --git a/src/components/ThreeDotsMenu/types.ts b/src/components/ThreeDotsMenu/types.ts index 6c3618ffc3ce..71913a5a5a3d 100644 --- a/src/components/ThreeDotsMenu/types.ts +++ b/src/components/ThreeDotsMenu/types.ts @@ -45,6 +45,9 @@ type ThreeDotsMenuProps = ThreeDotsMenuOnyxProps & { /** Should we announce the Modal visibility changes? */ shouldSetModalVisibility?: boolean; + + /** Is this in the Pane */ + isPaneMenu?: boolean; }; export default ThreeDotsMenuProps; diff --git a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx index fdb06828901e..5824842a8bd9 100644 --- a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx +++ b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx @@ -29,6 +29,7 @@ function SavedSearchItemThreeDotMenu({menuItems}: SavedSearchItemThreeDotMenuPro horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }} + isPaneMenu /> ); From d28381cda78f3d3a39f206360e02ee1b1bd43030 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Sat, 21 Sep 2024 14:08:31 +0530 Subject: [PATCH 075/180] fix saved search menu for narrow screen --- src/pages/Search/SearchTypeMenuNarrow.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Search/SearchTypeMenuNarrow.tsx b/src/pages/Search/SearchTypeMenuNarrow.tsx index 0158a15bfc41..8fb5c5b3b0c5 100644 --- a/src/pages/Search/SearchTypeMenuNarrow.tsx +++ b/src/pages/Search/SearchTypeMenuNarrow.tsx @@ -118,6 +118,7 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, queryJSON, title, horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }} + isPaneMenu /> ), isSelected: currentSavedSearch?.hash === item.hash, From fabd84ee2d2b4f2b52f04ec39419fc66b65d4b0a Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Sat, 21 Sep 2024 16:18:45 +0530 Subject: [PATCH 076/180] remove withOnyx and fix linting --- src/components/PopoverMenu.tsx | 2 +- src/components/ThreeDotsMenu/index.tsx | 19 +++++-------------- src/components/ThreeDotsMenu/types.ts | 11 ++--------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 83012a4b8c6e..81bb70a769f0 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -131,7 +131,7 @@ function PopoverMenu({ shouldEnableNewFocusManagement, restoreFocusType, shouldShowSelectedItemCheck = false, - isPaneMenu = false + isPaneMenu = false, }: PopoverMenuProps) { const styles = useThemeStyles(); const theme = useTheme(); diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index 4df3238c12be..9eef7d8b86b5 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -1,7 +1,6 @@ import React, {useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PopoverMenu from '@components/PopoverMenu'; @@ -16,11 +15,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Modal} from '@src/types/onyx'; import type ThreeDotsMenuProps from './types'; -type ThreeDotsMenuOnyxProps = { - /** Details about any modals being used */ - modal: OnyxEntry; -}; - function ThreeDotsMenu({ iconTooltip = 'common.more', icon = Expensicons.ThreeDots, @@ -36,9 +30,10 @@ function ThreeDotsMenu({ shouldOverlay = false, shouldSetModalVisibility = true, disabled = false, - modal = {}, - isPaneMenu = false + isPaneMenu = false, }: ThreeDotsMenuProps) { + const [modal] = useOnyx(ONYXKEYS.MODAL); + const theme = useTheme(); const styles = useThemeStyles(); const [isPopupMenuVisible, setPopupMenuVisible] = useState(false); @@ -116,8 +111,4 @@ function ThreeDotsMenu({ ThreeDotsMenu.displayName = 'ThreeDotsMenu'; -export default withOnyx({ - modal: { - key: ONYXKEYS.MODAL, - }, -})(ThreeDotsMenu); +export default ThreeDotsMenu; diff --git a/src/components/ThreeDotsMenu/types.ts b/src/components/ThreeDotsMenu/types.ts index 71913a5a5a3d..250a72b62135 100644 --- a/src/components/ThreeDotsMenu/types.ts +++ b/src/components/ThreeDotsMenu/types.ts @@ -1,18 +1,11 @@ import type {StyleProp, ViewStyle} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import type {TranslationPaths} from '@src/languages/types'; import type {AnchorPosition} from '@src/styles'; -import type {Modal} from '@src/types/onyx'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; import type IconAsset from '@src/types/utils/IconAsset'; -type ThreeDotsMenuOnyxProps = { - /** Details about any modals being used */ - modal: OnyxEntry; -}; - -type ThreeDotsMenuProps = ThreeDotsMenuOnyxProps & { +type ThreeDotsMenuProps = { /** Tooltip for the popup icon */ iconTooltip?: TranslationPaths; @@ -45,7 +38,7 @@ type ThreeDotsMenuProps = ThreeDotsMenuOnyxProps & { /** Should we announce the Modal visibility changes? */ shouldSetModalVisibility?: boolean; - + /** Is this in the Pane */ isPaneMenu?: boolean; }; From 7769a2261e75399b2a7e67f082d9519ee33b4725 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Sat, 21 Sep 2024 16:23:25 +0530 Subject: [PATCH 077/180] fix eslint error --- src/components/ThreeDotsMenu/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index 9eef7d8b86b5..6553833b63de 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -12,7 +12,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as Browser from '@libs/Browser'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Modal} from '@src/types/onyx'; import type ThreeDotsMenuProps from './types'; function ThreeDotsMenu({ From 15acd10149feeb8321e08b583fc4774027750268 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Mon, 23 Sep 2024 10:36:15 +0200 Subject: [PATCH 078/180] feat: fix reanimated console warnings --- ...eact-native-draggable-flatlist+4.0.1.patch | 94 +++++++++++++++++++ src/App.tsx | 3 - 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 patches/react-native-draggable-flatlist+4.0.1.patch diff --git a/patches/react-native-draggable-flatlist+4.0.1.patch b/patches/react-native-draggable-flatlist+4.0.1.patch new file mode 100644 index 000000000000..348f1aa5de8a --- /dev/null +++ b/patches/react-native-draggable-flatlist+4.0.1.patch @@ -0,0 +1,94 @@ +diff --git a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx +index d7d98c2..2f59c7a 100644 +--- a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx ++++ b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx +@@ -295,7 +295,7 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { + const springTo = placeholderOffset.value - activeCellOffset.value; + touchTranslate.value = withSpring( + springTo, +- animationConfigRef.current, ++ animationConfigRef.value, + () => { + runOnJS(onDragEnd)({ + from: activeIndexAnim.value, +diff --git a/node_modules/react-native-draggable-flatlist/src/context/refContext.tsx b/node_modules/react-native-draggable-flatlist/src/context/refContext.tsx +index ea21575..66c5eed 100644 +--- a/node_modules/react-native-draggable-flatlist/src/context/refContext.tsx ++++ b/node_modules/react-native-draggable-flatlist/src/context/refContext.tsx +@@ -1,14 +1,14 @@ + import React, { useContext } from "react"; + import { useMemo, useRef } from "react"; + import { FlatList } from "react-native-gesture-handler"; +-import Animated, { WithSpringConfig } from "react-native-reanimated"; ++import Animated, { type SharedValue, useSharedValue, WithSpringConfig } from "react-native-reanimated"; + import { DEFAULT_PROPS } from "../constants"; + import { useProps } from "./propsContext"; + import { CellData, DraggableFlatListProps } from "../types"; + + type RefContextValue = { + propsRef: React.MutableRefObject>; +- animationConfigRef: React.MutableRefObject; ++ animationConfigRef: SharedValue; + cellDataRef: React.MutableRefObject>; + keyToIndexRef: React.MutableRefObject>; + containerRef: React.RefObject; +@@ -54,8 +54,8 @@ function useSetupRefs({ + ...DEFAULT_PROPS.animationConfig, + ...animationConfig, + } as WithSpringConfig; +- const animationConfigRef = useRef(animConfig); +- animationConfigRef.current = animConfig; ++ const animationConfigRef = useSharedValue(animConfig); ++ animationConfigRef.value = animConfig; + + const cellDataRef = useRef(new Map()); + const keyToIndexRef = useRef(new Map()); +diff --git a/node_modules/react-native-draggable-flatlist/src/hooks/useCellTranslate.tsx b/node_modules/react-native-draggable-flatlist/src/hooks/useCellTranslate.tsx +index ce4ab68..efea240 100644 +--- a/node_modules/react-native-draggable-flatlist/src/hooks/useCellTranslate.tsx ++++ b/node_modules/react-native-draggable-flatlist/src/hooks/useCellTranslate.tsx +@@ -101,7 +101,7 @@ export function useCellTranslate({ cellIndex, cellSize, cellOffset }: Params) { + ? activeCellSize.value * (isAfterActive ? -1 : 1) + : 0; + +- return withSpring(translationAmt, animationConfigRef.current); ++ return withSpring(translationAmt, animationConfigRef.value); + }, [activeKey, cellIndex]); + + return translate; +diff --git a/node_modules/react-native-draggable-flatlist/src/hooks/useOnCellActiveAnimation.ts b/node_modules/react-native-draggable-flatlist/src/hooks/useOnCellActiveAnimation.ts +index 7c20587..857c7d0 100644 +--- a/node_modules/react-native-draggable-flatlist/src/hooks/useOnCellActiveAnimation.ts ++++ b/node_modules/react-native-draggable-flatlist/src/hooks/useOnCellActiveAnimation.ts +@@ -1,8 +1,9 @@ +-import { useRef } from "react"; +-import Animated, { ++ ++import { + useDerivedValue, + withSpring, + WithSpringConfig, ++ useSharedValue, + } from "react-native-reanimated"; + import { DEFAULT_ANIMATION_CONFIG } from "../constants"; + import { useAnimatedValues } from "../context/animatedValueContext"; +@@ -15,8 +16,8 @@ type Params = { + export function useOnCellActiveAnimation( + { animationConfig }: Params = { animationConfig: {} } + ) { +- const animationConfigRef = useRef(animationConfig); +- animationConfigRef.current = animationConfig; ++ const animationConfigRef = useSharedValue(animationConfig); ++ animationConfigRef.value = animationConfig; + + const isActive = useIsActive(); + +@@ -26,7 +27,7 @@ export function useOnCellActiveAnimation( + const toVal = isActive && isTouchActiveNative.value ? 1 : 0; + return withSpring(toVal, { + ...DEFAULT_ANIMATION_CONFIG, +- ...animationConfigRef.current, ++ ...animationConfigRef.value, + }); + }, [isActive]); + diff --git a/src/App.tsx b/src/App.tsx index 35254fa29b2a..177cc00c7dee 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -50,9 +50,6 @@ LogBox.ignoreLogs([ // the timer is lost. Currently Expensify is using a 30 minutes interval to refresh personal details. // More details here: https://git.io/JJYeb 'Setting a timer for a long period of time', - // We silence this warning for now and will address all the places where it happens separately. - // Then we can remove this line so the problem does not occur in the future. - '[Reanimated] Tried to modify key `current`', ]); const fill = {flex: 1}; From af9e9906014f7def16b4e4a2dcef92becf737d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Mon, 23 Sep 2024 13:58:56 +0200 Subject: [PATCH 079/180] add back qr code download --- .../workspace/WorkspaceProfileSharePage.tsx | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/pages/workspace/WorkspaceProfileSharePage.tsx b/src/pages/workspace/WorkspaceProfileSharePage.tsx index 16a076205ad3..32743bf290de 100644 --- a/src/pages/workspace/WorkspaceProfileSharePage.tsx +++ b/src/pages/workspace/WorkspaceProfileSharePage.tsx @@ -1,12 +1,15 @@ import React, {useMemo, useRef} from 'react'; import {View} from 'react-native'; import type {ImageSourcePropType} from 'react-native'; +import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png'; import ContextMenuItem from '@components/ContextMenuItem'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; import {useSession} from '@components/OnyxProvider'; import QRShare from '@components/QRShare'; -import type {QRShareHandle} from '@components/QRShare/types'; +import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; +import QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; @@ -19,6 +22,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; +import shouldAllowDownloadQRCode from '@libs/shouldAllowDownloadQRCode'; import * as Url from '@libs/Url'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -31,7 +35,7 @@ function WorkspaceProfileSharePage({policy}: WithPolicyProps) { const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const {environmentURL} = useEnvironment(); - const qrCodeRef = useRef(null); + const qrCodeRef = useRef(null); const {shouldUseNarrowLayout} = useResponsiveLayout(); const session = useSession(); @@ -96,21 +100,14 @@ function WorkspaceProfileSharePage({policy}: WithPolicyProps) { - {/* - Right now QR code download button is not shown anymore - This is a temporary measure because right now it's broken because of the Fabric update. - We need to wait for react-native v0.74 to be released so react-native-view-shot gets fixed. - - Please see https://github.com/Expensify/App/issues/40110 to see if it can be re-enabled. - */} - @@ -126,6 +123,15 @@ function WorkspaceProfileSharePage({policy}: WithPolicyProps) { shouldLimitWidth={false} wrapperStyle={themeStyles.sectionMenuItemTopDescription} /> + {shouldAllowDownloadQRCode && ( + qrCodeRef.current?.download?.()} + wrapperStyle={themeStyles.sectionMenuItemTopDescription} + /> + )} From f4b92c2488a64629a700272b7220cc118c0fe900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Mon, 23 Sep 2024 14:11:34 +0200 Subject: [PATCH 080/180] fix lint --- src/pages/workspace/WorkspaceProfileSharePage.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/workspace/WorkspaceProfileSharePage.tsx b/src/pages/workspace/WorkspaceProfileSharePage.tsx index 32743bf290de..21db727f6450 100644 --- a/src/pages/workspace/WorkspaceProfileSharePage.tsx +++ b/src/pages/workspace/WorkspaceProfileSharePage.tsx @@ -7,9 +7,8 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import {useSession} from '@components/OnyxProvider'; -import QRShare from '@components/QRShare'; import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; -import QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; +import type QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; From f87cbac7194400211196e640ccd909dacc04f076 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Mon, 23 Sep 2024 12:24:31 -0700 Subject: [PATCH 081/180] First commit for NewHelp --- help/.gitignore | 5 + help/404.html | 25 +++ help/Gemfile | 33 ++++ help/Gemfile.lock | 182 ++++++++++++++++++ help/_config.yml | 55 ++++++ .../2024-09-23-welcome-to-jekyll.markdown | 29 +++ help/about.markdown | 18 ++ help/index.markdown | 6 + help/robots.txt | 3 + 9 files changed, 356 insertions(+) create mode 100644 help/.gitignore create mode 100644 help/404.html create mode 100644 help/Gemfile create mode 100644 help/Gemfile.lock create mode 100644 help/_config.yml create mode 100644 help/_posts/2024-09-23-welcome-to-jekyll.markdown create mode 100644 help/about.markdown create mode 100644 help/index.markdown create mode 100644 help/robots.txt diff --git a/help/.gitignore b/help/.gitignore new file mode 100644 index 000000000000..f40fbd8ba564 --- /dev/null +++ b/help/.gitignore @@ -0,0 +1,5 @@ +_site +.sass-cache +.jekyll-cache +.jekyll-metadata +vendor diff --git a/help/404.html b/help/404.html new file mode 100644 index 000000000000..086a5c9ea988 --- /dev/null +++ b/help/404.html @@ -0,0 +1,25 @@ +--- +permalink: /404.html +layout: default +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/help/Gemfile b/help/Gemfile new file mode 100644 index 000000000000..2c345b82ae4d --- /dev/null +++ b/help/Gemfile @@ -0,0 +1,33 @@ +source "https://rubygems.org" +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! +gem "jekyll", "~> 4.3.4" +# This is the default theme for new Jekyll sites. You may change this to anything you like. +gem "minima", "~> 2.5" +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +# gem "github-pages", group: :jekyll_plugins +# If you have any plugins, put them here! +group :jekyll_plugins do + gem "jekyll-feed", "~> 0.12" +end + +# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem +# and associated library. +platforms :mingw, :x64_mingw, :mswin, :jruby do + gem "tzinfo", ">= 1", "< 3" + gem "tzinfo-data" +end + +# Performance-booster for watching directories on Windows +gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin] + +# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem +# do not have a Java counterpart. +gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby] diff --git a/help/Gemfile.lock b/help/Gemfile.lock new file mode 100644 index 000000000000..77ac51524535 --- /dev/null +++ b/help/Gemfile.lock @@ -0,0 +1,182 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + bigdecimal (3.1.8) + colorator (1.1.0) + concurrent-ruby (1.3.4) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + eventmachine (1.2.7) + ffi (1.17.0) + ffi (1.17.0-aarch64-linux-gnu) + ffi (1.17.0-aarch64-linux-musl) + ffi (1.17.0-arm-linux-gnu) + ffi (1.17.0-arm-linux-musl) + ffi (1.17.0-arm64-darwin) + ffi (1.17.0-x86-linux-gnu) + ffi (1.17.0-x86-linux-musl) + ffi (1.17.0-x86_64-darwin) + ffi (1.17.0-x86_64-linux-gnu) + ffi (1.17.0-x86_64-linux-musl) + forwardable-extended (2.6.0) + google-protobuf (4.28.2) + bigdecimal + rake (>= 13) + google-protobuf (4.28.2-aarch64-linux) + bigdecimal + rake (>= 13) + google-protobuf (4.28.2-arm64-darwin) + bigdecimal + rake (>= 13) + google-protobuf (4.28.2-x86-linux) + bigdecimal + rake (>= 13) + google-protobuf (4.28.2-x86_64-darwin) + bigdecimal + rake (>= 13) + google-protobuf (4.28.2-x86_64-linux) + bigdecimal + rake (>= 13) + http_parser.rb (0.8.0) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + jekyll (4.3.4) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 1.0) + jekyll-sass-converter (>= 2.0, < 4.0) + jekyll-watch (~> 2.0) + kramdown (~> 2.3, >= 2.3.1) + kramdown-parser-gfm (~> 1.0) + liquid (~> 4.0) + mercenary (>= 0.3.6, < 0.5) + pathutil (~> 0.9) + rouge (>= 3.0, < 5.0) + safe_yaml (~> 1.0) + terminal-table (>= 1.8, < 4.0) + webrick (~> 1.7) + jekyll-feed (0.17.0) + jekyll (>= 3.7, < 5.0) + jekyll-sass-converter (3.0.0) + sass-embedded (~> 1.54) + jekyll-seo-tag (2.8.0) + jekyll (>= 3.8, < 5.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.4.0) + minima (2.5.2) + jekyll (>= 3.5, < 5.0) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (6.0.1) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rexml (3.3.7) + rouge (4.4.0) + safe_yaml (1.0.5) + sass-embedded (1.79.3) + google-protobuf (~> 4.27) + rake (>= 13) + sass-embedded (1.79.3-aarch64-linux-android) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-aarch64-linux-gnu) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-aarch64-linux-musl) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-aarch64-mingw-ucrt) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-arm-linux-androideabi) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-arm-linux-gnueabihf) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-arm-linux-musleabihf) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-arm64-darwin) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-riscv64-linux-android) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-riscv64-linux-gnu) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-riscv64-linux-musl) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86-cygwin) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86-linux-android) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86-linux-gnu) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86-linux-musl) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86-mingw-ucrt) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86_64-cygwin) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86_64-darwin) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86_64-linux-android) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86_64-linux-gnu) + google-protobuf (~> 4.27) + sass-embedded (1.79.3-x86_64-linux-musl) + google-protobuf (~> 4.27) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + unicode-display_width (2.6.0) + webrick (1.8.1) + +PLATFORMS + aarch64-linux + aarch64-linux-android + aarch64-linux-gnu + aarch64-linux-musl + aarch64-mingw-ucrt + arm-linux-androideabi + arm-linux-gnu + arm-linux-gnueabihf + arm-linux-musl + arm-linux-musleabihf + arm64-darwin + riscv64-linux-android + riscv64-linux-gnu + riscv64-linux-musl + ruby + x86-cygwin + x86-linux + x86-linux-android + x86-linux-gnu + x86-linux-musl + x86-mingw-ucrt + x86_64-cygwin + x86_64-darwin + x86_64-linux + x86_64-linux-android + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + http_parser.rb (~> 0.6.0) + jekyll (~> 4.3.4) + jekyll-feed (~> 0.12) + minima (~> 2.5) + tzinfo (>= 1, < 3) + tzinfo-data + wdm (~> 0.1) + +BUNDLED WITH + 2.5.19 diff --git a/help/_config.yml b/help/_config.yml new file mode 100644 index 000000000000..ef7ba7c13913 --- /dev/null +++ b/help/_config.yml @@ -0,0 +1,55 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. +# +# If you need help with YAML syntax, here are some quick references for you: +# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml +# https://learnxinyminutes.com/docs/yaml/ +# +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. + +title: Your awesome title +email: your-email@example.com +description: >- # this means to ignore newlines until "baseurl:" + Write an awesome description for your new site here. You can edit this + line in _config.yml. It will appear in your document head meta (for + Google search results) and in your feed.xml site description. +baseurl: "" # the subpath of your site, e.g. /blog +url: "" # the base hostname & protocol for your site, e.g. http://example.com +twitter_username: jekyllrb +github_username: jekyll + +# Build settings +theme: minima +plugins: + - jekyll-feed + +# Exclude from processing. +# The following items will not be processed, by default. +# Any item listed under the `exclude:` key here will be automatically added to +# the internal "default list". +# +# Excluded items can be processed by explicitly listing the directories or +# their entries' file path in the `include:` list. +# +# exclude: +# - .sass-cache/ +# - .jekyll-cache/ +# - gemfiles/ +# - Gemfile +# - Gemfile.lock +# - node_modules/ +# - vendor/bundle/ +# - vendor/cache/ +# - vendor/gems/ +# - vendor/ruby/ diff --git a/help/_posts/2024-09-23-welcome-to-jekyll.markdown b/help/_posts/2024-09-23-welcome-to-jekyll.markdown new file mode 100644 index 000000000000..c53c7643163e --- /dev/null +++ b/help/_posts/2024-09-23-welcome-to-jekyll.markdown @@ -0,0 +1,29 @@ +--- +layout: post +title: "Welcome to Jekyll!" +date: 2024-09-23 12:16:06 -0700 +categories: jekyll update +--- +You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated. + +Jekyll requires blog post files to be named according to the following format: + +`YEAR-MONTH-DAY-title.MARKUP` + +Where `YEAR` is a four-digit number, `MONTH` and `DAY` are both two-digit numbers, and `MARKUP` is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works. + +Jekyll also offers powerful support for code snippets: + +{% highlight ruby %} +def print_hi(name) + puts "Hi, #{name}" +end +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +{% endhighlight %} + +Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk]. + +[jekyll-docs]: https://jekyllrb.com/docs/home +[jekyll-gh]: https://github.com/jekyll/jekyll +[jekyll-talk]: https://talk.jekyllrb.com/ diff --git a/help/about.markdown b/help/about.markdown new file mode 100644 index 000000000000..8b4e0b28c83e --- /dev/null +++ b/help/about.markdown @@ -0,0 +1,18 @@ +--- +layout: page +title: About +permalink: /about/ +--- + +This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](https://jekyllrb.com/) + +You can find the source code for Minima at GitHub: +[jekyll][jekyll-organization] / +[minima](https://github.com/jekyll/minima) + +You can find the source code for Jekyll at GitHub: +[jekyll][jekyll-organization] / +[jekyll](https://github.com/jekyll/jekyll) + + +[jekyll-organization]: https://github.com/jekyll diff --git a/help/index.markdown b/help/index.markdown new file mode 100644 index 000000000000..06715078416f --- /dev/null +++ b/help/index.markdown @@ -0,0 +1,6 @@ +--- +# Feel free to add content and custom Front Matter to this file. +# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults + +layout: home +--- diff --git a/help/robots.txt b/help/robots.txt new file mode 100644 index 000000000000..6ffbc308f73e --- /dev/null +++ b/help/robots.txt @@ -0,0 +1,3 @@ +User-agent: * +Disallow: / + From bf2b7f9c857b5dd9a8d95695c8f04cfc31e1054e Mon Sep 17 00:00:00 2001 From: David Barrett Date: Mon, 23 Sep 2024 21:14:07 -0700 Subject: [PATCH 082/180] Configured Jekyll action and build steps --- .github/workflows/deployNewHelp.yml | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/deployNewHelp.yml diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml new file mode 100644 index 000000000000..c9061e99861b --- /dev/null +++ b/.github/workflows/deployNewHelp.yml @@ -0,0 +1,68 @@ +on: + # Run on any push to main that has changes to the help directory + push: + branches: + - main + paths: + - 'help/**' + + # Run on any pull request (except PRs against staging or production) that has changes to the docs directory + pull_request: + types: [opened, synchronize] + branches-ignore: [staging, production] + paths: + - 'help/**' + + # Run on any manual trigger + workflow_dispatch: + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "newhelp" + cancel-in-progress: false + +jobs: + build: + env: + IS_PR_FROM_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1.0.13 # Using the latest stable version + with: + source: ./help/ + destination: ./help/_site + + - name: Deploy to Cloudflare Pages + uses: cloudflare/pages-action@v1 # Using the latest stable version + id: deploy + if: env.IS_PR_FROM_FORK != 'true' + with: + apiToken: ${{ secrets.CLOUDFLARE_PAGES_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: newhelp + directory: ./help/_site + + - name: Setup Cloudflare CLI + if: env.IS_PR_FROM_FORK != 'true' + run: pip3 install cloudflare==2.19.0 + + - name: Purge Cloudflare cache + if: env.IS_PR_FROM_FORK != 'true' + run: $HOME/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + env: + CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} + + - name: Leave a comment on the PR + uses: actions-cool/maintain-one-comment@v3.2.0 # Using the latest stable version + if: ${{ github.event_name == 'pull_request' && env.IS_PR_FROM_FORK != 'true' }} + with: + token: ${{ secrets.OS_BOTIFY_TOKEN }} + body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} + From d0bbf5646ae5ad7d4eb63055269b2130afc22d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Tue, 24 Sep 2024 11:39:43 +0200 Subject: [PATCH 083/180] add back the qr code download option on native platforms --- src/pages/ShareCodePage.tsx | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index e11e99a6e852..2a417a9a8174 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -8,8 +8,8 @@ import ContextMenuItem from '@components/ContextMenuItem'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; -import QRShare from '@components/QRShare'; -import type {QRShareHandle} from '@components/QRShare/types'; +import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; +import type QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -18,6 +18,7 @@ import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import Clipboard from '@libs/Clipboard'; +import getPlatform from '@libs/getPlatform'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as Url from '@libs/Url'; @@ -58,7 +59,8 @@ function ShareCodePage({report, policy}: ShareCodePageProps) { const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const {environmentURL} = useEnvironment(); - const qrCodeRef = useRef(null); + const qrCodeRef = useRef(null); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const isReport = !!report?.reportID; @@ -82,6 +84,8 @@ function ShareCodePage({report, policy}: ShareCodePageProps) { }, [report, currentUserPersonalDetails, isReport]); const title = isReport ? ReportUtils.getReportName(report) : currentUserPersonalDetails.displayName ?? ''; + const platform = getPlatform(); + const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID; const urlWithTrailingSlash = Url.addTrailingForwardSlash(environmentURL); const url = isReport ? `${urlWithTrailingSlash}${ROUTES.REPORT_WITH_ID.getRoute(report.reportID)}` @@ -111,24 +115,14 @@ function ShareCodePage({report, policy}: ShareCodePageProps) { /> - {/* - Right now QR code download button is not shown anymore - This is a temporary measure because right now it's broken because of the Fabric update. - We need to wait for react-native v0.74 to be released so react-native-view-shot gets fixed. - - Please see https://github.com/Expensify/App/issues/40110 to see if it can be re-enabled. - */} - @@ -142,6 +136,15 @@ function ShareCodePage({report, policy}: ShareCodePageProps) { onPress={() => Clipboard.setString(url)} shouldLimitWidth={false} /> + {isNative && ( + qrCodeRef.current?.download?.()} + /> + )} Date: Tue, 24 Sep 2024 11:48:01 +0200 Subject: [PATCH 084/180] fix lint --- src/pages/ShareCodePage.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index 2a417a9a8174..0f8edf2c8d63 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -123,6 +123,9 @@ function ShareCodePage({report, policy}: ShareCodePageProps) { logo={isReport ? expensifyLogo : (UserUtils.getAvatarUrl(currentUserPersonalDetails?.avatar, currentUserPersonalDetails?.accountID) as ImageSourcePropType)} logoRatio={isReport ? CONST.QR.EXPENSIFY_LOGO_SIZE_RATIO : CONST.QR.DEFAULT_LOGO_SIZE_RATIO} logoMarginRatio={isReport ? CONST.QR.EXPENSIFY_LOGO_MARGIN_RATIO : CONST.QR.DEFAULT_LOGO_MARGIN_RATIO} + svgLogo={svgLogo} + svgLogoFillColor={svgLogoFillColor} + logoBackgroundColor={logoBackgroundColor} /> From 317115663dc350ddeb6d39e83d13c5d9add332c6 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 15:51:51 +0530 Subject: [PATCH 085/180] fix pay someone limited delegate action --- .../AttachmentPickerWithMenuItems.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 6e3c3a48de74..c6e5a2baffc3 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,5 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -25,8 +25,10 @@ import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as IOU from '@userActions/IOU'; import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; +import DelegateNoAccessModal from '@src/components/DelegateNoAccessModal'; import type {IOUType} from '@src/CONST'; import CONST from '@src/CONST'; +import useDelegateUserDetails from '@src/hooks/useDelegateUserDetails'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; @@ -121,6 +123,8 @@ function AttachmentPickerWithMenuItems({ const {translate} = useLocalize(); const {windowHeight, windowWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {isDelegateAccessRestricted, delegatorEmail} = useDelegateUserDetails(); + const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); /** * Returns the list of IOU Options @@ -149,7 +153,13 @@ function AttachmentPickerWithMenuItems({ [CONST.IOU.TYPE.PAY]: { icon: getIconForAction(CONST.IOU.TYPE.SEND), text: translate('iou.paySomeone', {name: ReportUtils.getPayeeName(report)}), - onSelected: () => selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.PAY, report?.reportID ?? '-1'), false), + onSelected: () => { + if (isDelegateAccessRestricted) { + setIsNoDelegateAccessMenuVisible(true); + return; + } + selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.PAY, report?.reportID ?? '-1'), false); + }, }, [CONST.IOU.TYPE.TRACK]: { icon: getIconForAction(CONST.IOU.TYPE.TRACK), @@ -327,6 +337,11 @@ function AttachmentPickerWithMenuItems({ withoutOverlay anchorRef={actionButtonRef} /> + setIsNoDelegateAccessMenuVisible(false)} + delegatorEmail={delegatorEmail ?? ''} + /> ); }} From 0a745a3c2db5ee53fa92eb39d5b349ace4d47fb0 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 16:06:11 +0530 Subject: [PATCH 086/180] migrate from withOnyx --- .../AttachmentPickerWithMenuItems.tsx | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index c6e5a2baffc3..111e5640e237 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentPicker from '@components/AttachmentPicker'; import Icon from '@components/Icon'; @@ -35,12 +35,7 @@ import type * as OnyxTypes from '@src/types/onyx'; type MoneyRequestOptions = Record, PopoverMenuItem>; -type AttachmentPickerWithMenuItemsOnyxProps = { - /** The policy tied to the report */ - policy: OnyxEntry; -}; - -type AttachmentPickerWithMenuItemsProps = AttachmentPickerWithMenuItemsOnyxProps & { +type AttachmentPickerWithMenuItemsProps = { /** The report currently being looked at */ report: OnyxEntry; @@ -99,7 +94,6 @@ type AttachmentPickerWithMenuItemsProps = AttachmentPickerWithMenuItemsOnyxProps */ function AttachmentPickerWithMenuItems({ report, - policy, reportParticipantIDs, displayFileInModal, isFullComposerAvailable, @@ -125,6 +119,7 @@ function AttachmentPickerWithMenuItems({ const {shouldUseNarrowLayout} = useResponsiveLayout(); const {isDelegateAccessRestricted, delegatorEmail} = useDelegateUserDetails(); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); /** * Returns the list of IOU Options @@ -351,9 +346,4 @@ function AttachmentPickerWithMenuItems({ AttachmentPickerWithMenuItems.displayName = 'AttachmentPickerWithMenuItems'; -export default withOnyx({ - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, - initialValue: {} as OnyxTypes.Policy, - }, -})(AttachmentPickerWithMenuItems); +export default AttachmentPickerWithMenuItems; From fc19a5cfb9930b6e687108c5ea21c434ea65b02c Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 16:16:13 +0530 Subject: [PATCH 087/180] remove commented section --- src/pages/home/report/ReportActionItemSingle.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 8071c1157a4b..cb68d36f60ce 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -109,8 +109,6 @@ function ReportActionItemSingle({ avatarSource = ReportUtils.getWorkspaceIcon(report).source; avatarId = report.policyID; } else if (action?.delegateAccountID && personalDetails[action?.delegateAccountID]) { - // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their - // details. This will be improved upon when the Copilot feature is implemented. displayName = delegatePersonalDetails?.displayName ?? ''; avatarSource = delegatePersonalDetails?.avatar; avatarId = delegatePersonalDetails?.accountID; From e2b43f60bc0e14fd78e13a217fb73e31de6134fc Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 16:24:06 +0530 Subject: [PATCH 088/180] add delegate dependency --- .../ReportActionCompose/AttachmentPickerWithMenuItems.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 111e5640e237..28d985537ff5 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -171,7 +171,7 @@ function AttachmentPickerWithMenuItems({ return ReportUtils.temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs ?? []).map((option) => ({ ...options[option], })); - }, [translate, report, policy, reportParticipantIDs]); + }, [translate, report, policy, reportParticipantIDs, isDelegateAccessRestricted]); /** * Determines if we can show the task option From 24eff7dc864df57c0b52f6645268d581089c5fb4 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 17:04:32 +0530 Subject: [PATCH 089/180] fix offline case for pay expense --- src/libs/actions/IOU.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 1a634a17086d..c83b1bff2f7b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6710,6 +6710,11 @@ function getPayMoneyRequestParams( paymentMethodType, iouReport.reportID, true, + undefined, + undefined, + undefined, + undefined, + delegateEmail, ); // In some instances, the report preview action might not be available to the payer (only whispered to the requestor) From f64cfbd6c29ceb4ff837c1738a8f5375598df365 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 17:53:42 +0530 Subject: [PATCH 090/180] fix offline submission of expense --- src/libs/ReportUtils.ts | 2 ++ src/libs/actions/IOU.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1f2655efcecc..0c11069295b3 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5830,6 +5830,7 @@ function buildOptimisticMoneyRequestEntities( isPersonalTrackingExpense?: boolean, existingTransactionThreadReportID?: string, linkedTrackedExpenseReportAction?: ReportAction, + delegate?: string, ): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction | null] { const createdActionForChat = buildOptimisticCreatedReportAction(payeeEmail); @@ -5851,6 +5852,7 @@ function buildOptimisticMoneyRequestEntities( isOwnPolicyExpenseChat, iouActionCreationTime, linkedTrackedExpenseReportAction, + delegate, ); // Create optimistic transactionThread and the `CREATED` action for it, if existingTransactionThreadReportID is undefined diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c83b1bff2f7b..2ac15a58448d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2136,6 +2136,7 @@ function getMoneyRequestInformation( undefined, linkedTrackedExpenseReportAction?.childReportID, linkedTrackedExpenseReportAction, + delegateEmail, ); let reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : getReportPreviewAction(chatReport.reportID, iouReport.reportID); From d8cda530c0ebfbae9cec7be1daacf08735d16b11 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 24 Sep 2024 11:15:18 +0200 Subject: [PATCH 091/180] Add backTo param handling when opening Report from Search --- src/ROUTES.ts | 14 +++++++---- src/components/PromotedActionsBar.tsx | 2 +- src/components/Search/index.tsx | 8 ++++--- .../SelectionList/Search/ReportListItem.tsx | 4 +++- src/components/SelectionList/types.ts | 5 ++++ .../linkingConfig/getAdaptedStateFromPath.ts | 24 +++++++++---------- src/libs/SearchUtils.ts | 20 ++++++++++++---- 7 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c0ec944b71e1..3d24e3a2dafc 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -59,11 +59,9 @@ const ROUTES = { SEARCH_ADVANCED_FILTERS_IN: 'search/filters/in', SEARCH_REPORT: { route: 'search/view/:reportID/:reportActionID?', - getRoute: (reportID: string, reportActionID?: string) => { - if (reportActionID) { - return `search/view/${reportID}/${reportActionID}` as const; - } - return `search/view/${reportID}` as const; + getRoute: ({reportID, reportActionID, backTo}: {reportID: string; reportActionID?: string; backTo?: string}) => { + const baseRoute = reportActionID ? (`search/view/${reportID}/${reportActionID}` as const) : (`search/view/${reportID}` as const); + return getUrlWithBackToParam(baseRoute, backTo); }, }, TRANSACTION_HOLD_REASON_RHP: 'search/hold', @@ -1552,6 +1550,12 @@ type Route = { type RoutesValidationError = 'Error: One or more routes defined within `ROUTES` have not correctly used `as const` in their `getRoute` function return value.'; +/** + * Represents all routes in the app as a union of literal strings. + * + * If TS throws on this line, it implies that one or more routes defined within `ROUTES` have not correctly used + * `as const` in their `getRoute` function return value. + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars type RouteIsPlainString = AssertTypesNotEqual; diff --git a/src/components/PromotedActionsBar.tsx b/src/components/PromotedActionsBar.tsx index c374259f0447..a2e3566b159d 100644 --- a/src/components/PromotedActionsBar.tsx +++ b/src/components/PromotedActionsBar.tsx @@ -97,7 +97,7 @@ const PromotedActions = { return; } - ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(targetedReportID)); + ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute({reportID: targetedReportID})); }, }), } satisfies PromotedActionsType; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index d1080da19932..d9c8f751ac70 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -227,7 +227,7 @@ function Search({queryJSON}: SearchProps) { } const ListItem = SearchUtils.getListItem(type, status); - const data = SearchUtils.getSections(type, status, searchResults.data, searchResults.search); + const data = SearchUtils.getSections(type, status, searchResults.data, searchResults.search, queryJSON.inputQuery); const sortedData = SearchUtils.getSortedSections(type, status, data, sortBy, sortOrder); const sortedSelectedData = sortedData.map((item) => mapToItemWithSelectionInfo(item, selectedTransactions, canSelectMultiple)); @@ -294,13 +294,15 @@ function Search({queryJSON}: SearchProps) { SearchActions.createTransactionThread(hash, item.transactionID, reportID, item.moneyRequestReportActionID); } + const backTo = ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: queryJSON.inputQuery}); + if (SearchUtils.isReportActionListItemType(item)) { const reportActionID = item.reportActionID; - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(reportID, reportActionID)); + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, reportActionID, backTo})); return; } - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(reportID)); + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, backTo})); }; const fetchMoreResults = () => { diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index a0b96547bcd8..63705bda90f1 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -84,7 +84,9 @@ function ReportListItem({ }; const openReportInRHP = (transactionItem: TransactionListItemType) => { - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(transactionItem.transactionThreadReportID)); + const backTo = ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: reportItem.currentSearchQuery}); + + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: transactionItem.transactionThreadReportID, backTo})); }; if (!reportItem?.reportName && reportItem.transactions.length > 1) { diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 14c83ef25ed4..3fa901073e0e 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -1,5 +1,6 @@ import type {MutableRefObject, ReactElement, ReactNode} from 'react'; import type {GestureResponderEvent, InputModeOptions, LayoutChangeEvent, SectionListData, StyleProp, TextInput, TextStyle, ViewStyle} from 'react-native'; +import type {SearchQueryString} from '@components/Search/types'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; // eslint-disable-next-line no-restricted-imports import type CursorStyles from '@styles/utils/cursor/types'; @@ -233,7 +234,11 @@ type ReportListItemType = ListItem & /** The personal details of the user paying the request */ to: SearchPersonalDetails; + /** List of transactions that belong to this report */ transactions: TransactionListItemType[]; + + /** The current search query that was used to display these report items */ + currentSearchQuery: SearchQueryString; }; type ListItemProps = CommonListItemProps & { diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 2c96e5796309..f92b133d719a 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -114,22 +114,22 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat if (route.params && 'backTo' in route.params && typeof route.params.backTo === 'string') { const stateForBackTo = getStateFromPath(route.params.backTo, config); if (stateForBackTo) { - // eslint-disable-next-line @typescript-eslint/no-shadow - const rhpNavigator = stateForBackTo.routes.find((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); - - const centralPaneOrFullScreenNavigator = stateForBackTo.routes.find( - // eslint-disable-next-line @typescript-eslint/no-shadow - (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR, - ); - // If there is rhpNavigator in the state generated for backTo url, we want to get root route matching to this rhp screen. + const rhpNavigator = stateForBackTo.routes.find((rt) => rt.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); if (rhpNavigator && rhpNavigator.state) { return getMatchingRootRouteForRHPRoute(findFocusedRoute(stateForBackTo) as NavigationPartialRoute); } - // If we know that backTo targets the root route (central pane or full screen) we want to use it. - if (centralPaneOrFullScreenNavigator && centralPaneOrFullScreenNavigator.state) { - return centralPaneOrFullScreenNavigator as NavigationPartialRoute; + // If we know that backTo targets the root route (full screen) we want to use it. + const fullScreenNavigator = stateForBackTo.routes.find((rt) => rt.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR); + if (fullScreenNavigator && fullScreenNavigator.state) { + return fullScreenNavigator as NavigationPartialRoute; + } + + // If we know that backTo targets a central pane screen we want to use it. + const centralPaneScreen = stateForBackTo.routes.find((rt) => isCentralPaneName(rt.name)); + if (centralPaneScreen) { + return centralPaneScreen as NavigationPartialRoute; } } } @@ -191,7 +191,7 @@ function getAdaptedState(state: PartialState if (focusedRHPRoute) { let matchingRootRoute = getMatchingRootRouteForRHPRoute(focusedRHPRoute); const isRHPScreenOpenedFromLHN = focusedRHPRoute?.name && RHP_SCREENS_OPENED_FROM_LHN.includes(focusedRHPRoute?.name as RHPScreenOpenedFromLHN); - // This may happen if this RHP doens't have a route that should be under the overlay defined. + // This may happen if this RHP doesn't have a route that should be under the overlay defined. if (!matchingRootRoute || isRHPScreenOpenedFromLHN) { metainfo.isCentralPaneAndBottomTabMandatory = false; metainfo.isFullScreenNavigatorMandatory = false; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index e178c7dcb77b..fa50fbc0ce47 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -250,7 +250,7 @@ function getIOUReportName(data: OnyxTypes.SearchResults['data'], reportItem: Sea return reportItem.reportName; } -function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']): ReportListItemType[] { +function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], currentSearchQuery: SearchQueryString): ReportListItemType[] { const shouldShowMerchant = getShouldShowMerchant(data); const doesDataContainAPastYearTransaction = shouldShowYear(data); @@ -269,6 +269,7 @@ function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: Onyx from: data.personalDetailsList?.[reportItem.accountID ?? -1], to: reportItem.managerID ? data.personalDetailsList?.[reportItem.managerID] : emptyPersonalDetails, transactions, + currentSearchQuery, reportName: isIOUReport ? getIOUReportName(data, reportItem) : reportItem.reportName, }; } else if (isTransactionEntry(key)) { @@ -311,21 +312,30 @@ function getListItem(type: SearchDataTypes, status: SearchStatus): ListItemType< if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return ChatListItem; } - return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? TransactionListItem : ReportListItem; + if (status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + return TransactionListItem; + } + return ReportListItem; } -function getSections(type: SearchDataTypes, status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']) { +function getSections(type: SearchDataTypes, status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], currentSearchQuery: SearchQueryString) { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return getReportActionsSections(data); } - return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? getTransactionsSections(data, metadata) : getReportSections(data, metadata); + if (status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + return getTransactionsSections(data, metadata); + } + return getReportSections(data, metadata, currentSearchQuery); } function getSortedSections(type: SearchDataTypes, status: SearchStatus, data: ListItemDataType, sortBy?: SearchColumnType, sortOrder?: SortOrder) { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return getSortedReportActionData(data as ReportActionListItemType[]); } - return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? getSortedTransactionData(data as TransactionListItemType[], sortBy, sortOrder) : getSortedReportData(data as ReportListItemType[]); + if (status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + return getSortedTransactionData(data as TransactionListItemType[], sortBy, sortOrder); + } + return getSortedReportData(data as ReportListItemType[]); } function getSortedTransactionData(data: TransactionListItemType[], sortBy?: SearchColumnType, sortOrder?: SortOrder) { From 3b0742c456b25e10d5dc6fa26dc17d7a97246caa Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 18:29:39 +0530 Subject: [PATCH 092/180] fix jest hopefully --- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.ts | 8 +++----- src/pages/home/report/ReportActionsView.tsx | 1 + tests/unit/ReportUtilsTest.ts | 1 + 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b7d597c81153..0bc5f9b37671 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4610,6 +4610,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num * @param delegate - Email of the delegate of the account */ function buildOptimisticIOUReportAction( + delegate = '', type: ValueOf, amount: number, currency: string, @@ -4623,7 +4624,6 @@ function buildOptimisticIOUReportAction( isOwnPolicyExpenseChat = false, created = DateUtils.getDBTime(), linkedExpenseReportAction?: OnyxEntry, - delegate = '', ): OptimisticIOUReportAction { const IOUReportID = iouReportID || generateReportID(); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9a4c4a48ab85..aca25a1af9a0 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4000,6 +4000,7 @@ function createSplitsAndOnyxData( // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat const splitCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); const splitIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( + undefined, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, amount, currency, @@ -4578,6 +4579,7 @@ function startSplitBill({ // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat const splitChatCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); const splitIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( + undefined, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, 0, CONST.CURRENCY.USD, @@ -6702,6 +6704,7 @@ function getPayMoneyRequestParams( } const optimisticIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( + delegateEmail, CONST.IOU.REPORT_ACTION_TYPE.PAY, ReportUtils.isExpenseReport(iouReport) ? -total : total, iouReport.currency ?? '', @@ -6711,11 +6714,6 @@ function getPayMoneyRequestParams( paymentMethodType, iouReport.reportID, true, - undefined, - undefined, - undefined, - undefined, - delegateEmail, ); // In some instances, the report preview action might not be available to the payer (only whispered to the requestor) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 1460942931fc..42302b721059 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -181,6 +181,7 @@ function ReportActionsView({ if (report.total && moneyRequestActions.length < (reportPreviewAction?.childMoneyRequestCount ?? 0) && isEmptyObject(transactionThreadReport)) { const optimisticIOUAction = ReportUtils.buildOptimisticIOUReportAction( + undefined, CONST.IOU.REPORT_ACTION_TYPE.CREATE, 0, CONST.CURRENCY.USD, diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index f8d23e89ca5a..8e86c625b57a 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -766,6 +766,7 @@ describe('ReportUtils', () => { it('should disable thread on split expense actions', () => { const reportAction = ReportUtils.buildOptimisticIOUReportAction( + undefined, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, 50000, CONST.CURRENCY.USD, From d36fd070e893ef7ff9ad203bf73a0801523c6619 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 18:30:26 +0530 Subject: [PATCH 093/180] fix --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0bc5f9b37671..d6114696726d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5817,6 +5817,7 @@ function buildOptimisticMoneyRequestEntities( const createdActionForIOUReport = buildOptimisticCreatedReportAction(payeeEmail, DateUtils.subtractMillisecondsFromDateTime(iouActionCreationTime, 1)); const iouAction = buildOptimisticIOUReportAction( + delegate, type, amount, currency, @@ -5830,7 +5831,6 @@ function buildOptimisticMoneyRequestEntities( isOwnPolicyExpenseChat, iouActionCreationTime, linkedTrackedExpenseReportAction, - delegate, ); // Create optimistic transactionThread and the `CREATED` action for it, if existingTransactionThreadReportID is undefined From d851429d606718e178d213c79a91eb893d19eeaf Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 18:42:31 +0530 Subject: [PATCH 094/180] make delegateEmail accessible in reportUtils itself --- src/libs/ReportUtils.ts | 60 +++++++++------------ src/libs/actions/IOU.ts | 40 +++----------- src/libs/actions/Report.ts | 20 +++---- src/libs/actions/Task.ts | 22 +++----- src/pages/home/report/ReportActionsView.tsx | 1 - tests/unit/ReportUtilsTest.ts | 1 - 6 files changed, 45 insertions(+), 99 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d6114696726d..0da381eaf7b8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -676,6 +676,14 @@ Onyx.connect({ callback: (value) => (onboarding = value), }); +let delegateEmail = ''; +Onyx.connect({ + key: ONYXKEYS.ACCOUNT, + callback: (value) => { + delegateEmail = value?.delegatedAccess?.delegate ?? ''; + }, +}); + function getCurrentUserAvatar(): AvatarSource | undefined { return currentUserPersonalDetails?.avatar; } @@ -4154,7 +4162,6 @@ function getPolicyDescriptionText(policy: OnyxEntry): string { } function buildOptimisticAddCommentReportAction( - delegate: string, text?: string, file?: FileObject, actorAccountID?: number, @@ -4171,7 +4178,7 @@ function buildOptimisticAddCommentReportAction( const isAttachmentOnly = file && !text; const isAttachmentWithText = !!text && file !== undefined; const accountID = actorAccountID ?? currentUserAccountID ?? -1; - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); // Remove HTML from text when applying optimistic offline comment return { commentText, @@ -4262,10 +4269,8 @@ function updateOptimisticParentReportAction(parentReportAction: OnyxEntry, amount: number, currency: string, @@ -4636,7 +4639,7 @@ function buildOptimisticIOUReportAction( type, }; - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); if (type === CONST.IOU.REPORT_ACTION_TYPE.PAY) { // In pay someone flow, we store amount, comment, currency in IOUDetails when type = pay @@ -4696,13 +4699,13 @@ function buildOptimisticIOUReportAction( /** * Builds an optimistic APPROVED report action with a randomly generated reportActionID. */ -function buildOptimisticApprovedReportAction(amount: number, currency: string, expenseReportID: string, delegate: string): OptimisticApprovedReportAction { +function buildOptimisticApprovedReportAction(amount: number, currency: string, expenseReportID: string): OptimisticApprovedReportAction { const originalMessage = { amount, currency, expenseReportID, }; - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { actionName: CONST.REPORT.ACTIONS.TYPE.APPROVED, @@ -4730,8 +4733,8 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e /** * Builds an optimistic APPROVED report action with a randomly generated reportActionID. */ -function buildOptimisticUnapprovedReportAction(amount: number, currency: string, expenseReportID: string, delegate: string): OptimisticUnapprovedReportAction { - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); +function buildOptimisticUnapprovedReportAction(amount: number, currency: string, expenseReportID: string): OptimisticUnapprovedReportAction { + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { actionName: CONST.REPORT.ACTIONS.TYPE.UNAPPROVED, actorAccountID: currentUserAccountID, @@ -4805,20 +4808,14 @@ function buildOptimisticMovedReportAction(fromPolicyID: string, toPolicyID: stri * Builds an optimistic SUBMITTED report action with a randomly generated reportActionID. * */ -function buildOptimisticSubmittedReportAction( - amount: number, - currency: string, - expenseReportID: string, - adminAccountID: number | undefined, - delegate: string, -): OptimisticSubmittedReportAction { +function buildOptimisticSubmittedReportAction(amount: number, currency: string, expenseReportID: string, adminAccountID: number | undefined): OptimisticSubmittedReportAction { const originalMessage = { amount, currency, expenseReportID, }; - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { actionName: CONST.REPORT.ACTIONS.TYPE.SUBMITTED, @@ -4933,7 +4930,6 @@ function buildOptimisticActionableTrackExpenseWhisper(iouAction: OptimisticIOURe * Builds an optimistic modified expense action with a randomly generated reportActionID. */ function buildOptimisticModifiedExpenseReportAction( - delegate: string, transactionThread: OnyxInputOrEntry, oldTransaction: OnyxInputOrEntry, transactionChanges: TransactionChanges, @@ -4942,7 +4938,7 @@ function buildOptimisticModifiedExpenseReportAction( updatedTransaction?: OnyxInputOrEntry, ): OptimisticModifiedExpenseReportAction { const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction); - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, @@ -4980,8 +4976,8 @@ function buildOptimisticModifiedExpenseReportAction( * @param transactionThreadID - The reportID of the transaction thread * @param movedToReportID - The reportID of the report the transaction is moved to */ -function buildOptimisticMovedTrackedExpenseModifiedReportAction(delegate: string, transactionThreadID: string, movedToReportID: string): OptimisticModifiedExpenseReportAction { - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); +function buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThreadID: string, movedToReportID: string): OptimisticModifiedExpenseReportAction { + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, @@ -5075,7 +5071,6 @@ function updateReportPreview( } function buildOptimisticTaskReportAction( - delegate: string, taskReportID: string, actionName: typeof CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED | typeof CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED | typeof CONST.REPORT.ACTIONS.TYPE.TASK_CANCELLED, message = '', @@ -5089,7 +5084,7 @@ function buildOptimisticTaskReportAction( html: message, whisperedTo: [], }; - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { actionName, @@ -5429,7 +5424,7 @@ function buildOptimisticUnHoldReportAction(created = DateUtils.getDBTime()): Opt }; } -function buildOptimisticEditedTaskFieldReportAction({title, description}: Task, delegate: string): OptimisticEditedTaskReportAction { +function buildOptimisticEditedTaskFieldReportAction({title, description}: Task): OptimisticEditedTaskReportAction { // We do not modify title & description in one request, so we need to create a different optimistic action for each field modification let field = ''; let value = ''; @@ -5447,7 +5442,7 @@ function buildOptimisticEditedTaskFieldReportAction({title, description}: Task, } else if (field) { changelog = `removed the ${field}`; } - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { reportActionID: NumberUtils.rand64(), @@ -5476,8 +5471,8 @@ function buildOptimisticEditedTaskFieldReportAction({title, description}: Task, }; } -function buildOptimisticChangedTaskAssigneeReportAction(delegate: string, assigneeAccountID: number): OptimisticEditedTaskReportAction { - const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegate); +function buildOptimisticChangedTaskAssigneeReportAction(assigneeAccountID: number): OptimisticEditedTaskReportAction { + const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); return { reportActionID: NumberUtils.rand64(), @@ -5808,7 +5803,6 @@ function buildOptimisticMoneyRequestEntities( isPersonalTrackingExpense?: boolean, existingTransactionThreadReportID?: string, linkedTrackedExpenseReportAction?: ReportAction, - delegate?: string, ): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction | null] { const createdActionForChat = buildOptimisticCreatedReportAction(payeeEmail); @@ -5817,7 +5811,6 @@ function buildOptimisticMoneyRequestEntities( const createdActionForIOUReport = buildOptimisticCreatedReportAction(payeeEmail, DateUtils.subtractMillisecondsFromDateTime(iouActionCreationTime, 1)); const iouAction = buildOptimisticIOUReportAction( - delegate, type, amount, currency, @@ -6898,7 +6891,6 @@ function canEditRoomVisibility(report: OnyxEntry, policy: OnyxEntry { - delegateEmail = value?.delegatedAccess?.delegate ?? ''; - }, -}); - let quickAction: OnyxEntry = {}; Onyx.connect({ key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, @@ -2136,7 +2128,6 @@ function getMoneyRequestInformation( undefined, linkedTrackedExpenseReportAction?.childReportID, linkedTrackedExpenseReportAction, - delegateEmail, ); let reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : getReportPreviewAction(chatReport.reportID, iouReport.reportID); @@ -2553,15 +2544,7 @@ function getUpdateMoneyRequestParams( // - we're updating the distance rate while the waypoints are still pending // In these cases, there isn't a valid optimistic mileage data we can use, // and the report action is created on the server with the distance-related response from the MapBox API - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction( - delegateEmail, - transactionThread, - transaction, - transactionChanges, - isFromExpenseReport, - policy, - updatedTransaction, - ); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction); if (!hasPendingWaypoints && !(hasModifiedDistanceRate && TransactionUtils.isFetchingWaypointsFromServer(transaction))) { params.reportActionID = updatedReportAction.reportActionID; @@ -2882,7 +2865,7 @@ function getUpdateTrackExpenseParams( // - we're updating the distance rate while the waypoints are still pending // In these cases, there isn't a valid optimistic mileage data we can use, // and the report action is created on the server with the distance-related response from the MapBox API - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(delegateEmail, transactionThread, transaction, transactionChanges, false, policy); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, false, policy); if (!hasPendingWaypoints && !(hasModifiedDistanceRate && TransactionUtils.isFetchingWaypointsFromServer(transaction))) { params.reportActionID = updatedReportAction.reportActionID; @@ -3248,7 +3231,7 @@ const getConvertTrackedExpenseInformation = ( failureData?.push(...deleteFailureData); // Build modified expense report action with the transaction changes - const modifiedExpenseReportAction = ReportUtils.buildOptimisticMovedTrackedExpenseModifiedReportAction(delegateEmail, transactionThreadReportID, moneyRequestReportID); + const modifiedExpenseReportAction = ReportUtils.buildOptimisticMovedTrackedExpenseModifiedReportAction(transactionThreadReportID, moneyRequestReportID); optimisticData?.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -4000,7 +3983,6 @@ function createSplitsAndOnyxData( // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat const splitCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); const splitIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( - undefined, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, amount, currency, @@ -4579,7 +4561,6 @@ function startSplitBill({ // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat const splitChatCreatedReportAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); const splitIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( - undefined, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, 0, CONST.CURRENCY.USD, @@ -5254,7 +5235,7 @@ function editRegularMoneyRequest( const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); // STEP 2: Build new modified expense report action. - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(delegateEmail, transactionThread, transaction, transactionChanges, isFromExpenseReport, policy); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport, policy); const updatedTransaction = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport) : null; // STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct @@ -6704,7 +6685,6 @@ function getPayMoneyRequestParams( } const optimisticIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( - delegateEmail, CONST.IOU.REPORT_ACTION_TYPE.PAY, ReportUtils.isExpenseReport(iouReport) ? -total : total, iouReport.currency ?? '', @@ -7046,7 +7026,7 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?: total = expenseReport?.unheldTotal; } - const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1', delegateEmail); + const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1'); const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport?.ownerAccountID ?? -1, expenseReport?.total ?? 0); @@ -7202,7 +7182,7 @@ function unapproveExpenseReport(expenseReport: OnyxEntry) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - const optimisticUnapprovedReportAction = ReportUtils.buildOptimisticUnapprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, delegateEmail); + const optimisticUnapprovedReportAction = ReportUtils.buildOptimisticUnapprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.SUBMITTED); const optimisticReportActionData: OnyxUpdate = { @@ -7297,13 +7277,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails?.accountID : undefined; - const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction( - expenseReport?.total ?? 0, - expenseReport.currency ?? '', - expenseReport.reportID, - adminAccountID, - delegateEmail, - ); + 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); const optimisticData: OnyxUpdate[] = !isSubmitAndClosePolicy diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ea154d326064..b038f16d003d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -264,13 +264,6 @@ Onyx.connect({ waitForCollectionCallback: true, callback: (value) => (allReportDraftComments = value), }); -let delegateEmail = ''; -Onyx.connect({ - key: ONYXKEYS.ACCOUNT, - callback: (value) => { - delegateEmail = value?.delegatedAccess?.delegate ?? ''; - }, -}); let environmentURL: string; Environment.getEnvironmentURL().then((url: string) => (environmentURL = url)); @@ -459,7 +452,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { let commandName: typeof WRITE_COMMANDS.ADD_COMMENT | typeof WRITE_COMMANDS.ADD_ATTACHMENT | typeof WRITE_COMMANDS.ADD_TEXT_AND_ATTACHMENT = WRITE_COMMANDS.ADD_COMMENT; if (text && !file) { - const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, text, undefined, undefined, undefined, undefined, reportID); + const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(text, undefined, undefined, undefined, undefined, reportID); reportCommentAction = reportComment.reportAction; reportCommentText = reportComment.commentText; } @@ -468,7 +461,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { // When we are adding an attachment we will call AddAttachment. // It supports sending an attachment with an optional comment and AddComment supports adding a single text comment only. commandName = WRITE_COMMANDS.ADD_ATTACHMENT; - const attachment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, text, file, undefined, undefined, undefined, reportID); + const attachment = ReportUtils.buildOptimisticAddCommentReportAction(text, file, undefined, undefined, undefined, reportID); attachmentAction = attachment.reportAction; } @@ -3350,7 +3343,7 @@ function completeOnboarding( const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Introductory message - const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); + const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; const introductionMessage: AddCommentOrAttachementParams = { reportID: targetChatReportID, @@ -3359,7 +3352,7 @@ function completeOnboarding( }; // Text message - const textComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, data.message, undefined, actorAccountID, 1); + const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID, 1); const textCommentAction: OptimisticAddCommentReportAction = textComment.reportAction; const textMessage: AddCommentOrAttachementParams = { reportID: targetChatReportID, @@ -3370,7 +3363,7 @@ function completeOnboarding( let videoCommentAction: OptimisticAddCommentReportAction | null = null; let videoMessage: AddCommentOrAttachementParams | null = null; if ('video' in data && data.video) { - const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(delegateEmail, CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 2); + const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 2); videoCommentAction = videoComment.reportAction; videoMessage = { reportID: targetChatReportID, @@ -3400,7 +3393,6 @@ function completeOnboarding( ); const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(CONST.EMAIL.CONCIERGE); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( - delegateEmail, currentTask.reportID, task.title, 0, @@ -3412,7 +3404,7 @@ function completeOnboarding( currentTask.parentReportActionID = taskReportAction.reportAction.reportActionID; const completedTaskReportAction = task.autoCompleted - ? ReportUtils.buildOptimisticTaskReportAction(delegateEmail, currentTask.reportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, 'marked as complete', actorAccountID, 2) + ? ReportUtils.buildOptimisticTaskReportAction(currentTask.reportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, 'marked as complete', actorAccountID, 2) : null; return { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 7c5df18aa466..41ccfee1786b 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -78,14 +78,6 @@ Onyx.connect({ }, }); -let delegateEmail = ''; -Onyx.connect({ - key: ONYXKEYS.ACCOUNT, - callback: (value) => { - delegateEmail = value?.delegatedAccess?.delegate ?? ''; - }, -}); - /** * Clears out the task info from the store */ @@ -129,7 +121,7 @@ function createTaskAndNavigate( // Parent ReportAction indicating that a task has been created const optimisticTaskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmail); - const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(delegateEmail, taskReportID, title, assigneeAccountID, `task for ${title}`, parentReportID); + const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assigneeAccountID, `task for ${title}`, parentReportID); optimisticTaskReport.parentReportActionID = optimisticAddCommentReport.reportAction.reportActionID; const currentTime = DateUtils.getDBTimeWithSkew(); @@ -209,7 +201,6 @@ function createTaskAndNavigate( if (assigneeChatReport) { assigneeChatReportOnyxData = ReportUtils.getTaskAssigneeChatOnyxData( - delegateEmail, currentUserAccountID, assigneeAccountID, taskReportID, @@ -336,7 +327,7 @@ function getOutstandingChildTask(taskReport: OnyxEntry) { function completeTask(taskReport: OnyxEntry) { const taskReportID = taskReport?.reportID ?? '-1'; const message = `marked as complete`; - const completedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(delegateEmail, taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, message); + const completedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED, message); const parentReport = getParentReport(taskReport); const optimisticData: OnyxUpdate[] = [ { @@ -422,7 +413,7 @@ function completeTask(taskReport: OnyxEntry) { function reopenTask(taskReport: OnyxEntry) { const taskReportID = taskReport?.reportID ?? '-1'; const message = `marked as incomplete`; - const reopenedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(delegateEmail, taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED, message); + const reopenedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(taskReportID, CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED, message); const parentReport = getParentReport(taskReport); const hasOutstandingChildTask = taskReport?.managerID === currentUserAccountID ? true : parentReport?.hasOutstandingChildTask; @@ -502,7 +493,7 @@ function reopenTask(taskReport: OnyxEntry) { function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task) { // Create the EditedReportAction on the task - const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskFieldReportAction({title, description}, delegateEmail); + const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskFieldReportAction({title, description}); // Sometimes title or description is undefined, so we need to check for that, and we provide it to multiple functions const reportName = (title ?? report?.reportName)?.trim(); @@ -579,7 +570,7 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, assigneeEmail: string, assigneeAccountID: number | null = 0, assigneeChatReport?: OnyxEntry) { // Create the EditedReportAction on the task - const editTaskReportAction = ReportUtils.buildOptimisticChangedTaskAssigneeReportAction(delegateEmail, assigneeAccountID ?? 0); + const editTaskReportAction = ReportUtils.buildOptimisticChangedTaskAssigneeReportAction(assigneeAccountID ?? 0); const reportName = report.reportName?.trim(); let assigneeChatReportOnyxData; @@ -684,7 +675,6 @@ function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, as }; assigneeChatReportOnyxData = ReportUtils.getTaskAssigneeChatOnyxData( - delegateEmail, currentUserAccountID, assigneeAccountID, report.reportID, @@ -952,7 +942,7 @@ function deleteTask(report: OnyxEntry) { return; } const message = `deleted task: ${report.reportName}`; - const optimisticCancelReportAction = ReportUtils.buildOptimisticTaskReportAction(delegateEmail, report.reportID ?? '-1', CONST.REPORT.ACTIONS.TYPE.TASK_CANCELLED, message); + const optimisticCancelReportAction = ReportUtils.buildOptimisticTaskReportAction(report.reportID ?? '-1', CONST.REPORT.ACTIONS.TYPE.TASK_CANCELLED, message); const optimisticReportActionID = optimisticCancelReportAction.reportActionID; const parentReportAction = getParentReportAction(report); const parentReport = getParentReport(report); diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 42302b721059..1460942931fc 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -181,7 +181,6 @@ function ReportActionsView({ if (report.total && moneyRequestActions.length < (reportPreviewAction?.childMoneyRequestCount ?? 0) && isEmptyObject(transactionThreadReport)) { const optimisticIOUAction = ReportUtils.buildOptimisticIOUReportAction( - undefined, CONST.IOU.REPORT_ACTION_TYPE.CREATE, 0, CONST.CURRENCY.USD, diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 8e86c625b57a..f8d23e89ca5a 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -766,7 +766,6 @@ describe('ReportUtils', () => { it('should disable thread on split expense actions', () => { const reportAction = ReportUtils.buildOptimisticIOUReportAction( - undefined, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, 50000, CONST.CURRENCY.USD, From 60f097a3091550868753b57c9acea874df3063e6 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 18:44:49 +0530 Subject: [PATCH 095/180] remove whitespaces --- src/libs/actions/IOU.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index eca0a532d9dd..daaa766145ed 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7025,7 +7025,6 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?: if (hasHeldExpenses && !full && !!expenseReport?.unheldTotal) { total = expenseReport?.unheldTotal; } - const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1'); const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport?.ownerAccountID ?? -1, expenseReport?.total ?? 0); @@ -7276,7 +7275,6 @@ function submitReport(expenseReport: OnyxTypes.Report) { const isCurrentUserManager = currentUserPersonalDetails?.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); 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); From e5778d7e1a8a690c38a45dc1e9d57813b5469694 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 19:09:07 +0530 Subject: [PATCH 096/180] Check for delegate report action directly from actions --- src/pages/home/report/ReportActionItemSingle.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index cb68d36f60ce..e93dfa8ea759 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -86,9 +86,6 @@ function ReportActionItemSingle({ const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); const delegatePersonalDetails = personalDetails[action?.delegateAccountID ?? '']; - - const delegateEmail = delegatePersonalDetails?.login; - let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID ?? -1] ?? {}; const accountOwnerDetails = getPersonalDetailByEmail(login ?? ''); @@ -286,7 +283,7 @@ function ReportActionItemSingle({ ) : null} - {delegateEmail && !isReportPreviewAction && ( + {action?.delegateAccountID && !isReportPreviewAction && ( {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')} )} {children} From c081907cbebfb93d12e6379afcd3c9f5a5250037 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Tue, 24 Sep 2024 20:12:17 +0530 Subject: [PATCH 097/180] remove white space --- src/pages/home/report/ReportActionItemSingle.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 76e413a4b6d1..7e512b192d4f 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -82,7 +82,6 @@ function ReportActionItemSingle({ const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; - const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); const delegatePersonalDetails = personalDetails[action?.delegateAccountID ?? '']; const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); From 6e108b14c6d5b28516bd139034c7ea095293b30e Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 25 Sep 2024 00:36:02 +0800 Subject: [PATCH 098/180] add null safety --- src/pages/home/report/ReportActionItemSingle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 483247fa28a8..c6b879746baf 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -83,7 +83,7 @@ function ReportActionItemSingle({ const {translate} = useLocalize(); const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; const actorAccountID = ReportUtils.getReportActionActorAccountID(action, iouReport); - const policy = usePolicy(report.policyID); + const policy = usePolicy(report?.policyID); const [invoiceReceiverPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.invoiceReceiver && 'policyID' in report.invoiceReceiver ? report.invoiceReceiver.policyID : -1}`); let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); From fd09dfbf1831e3695af99905bc50c467208f274b Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:58:03 -0600 Subject: [PATCH 099/180] Update Send-an-invoice.md Adding screenshots to the Send Invoice section of the help article based on this GH - https://github.com/Expensify/Expensify/issues/407250 --- .../expenses-&-payments/Send-an-invoice.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md index 85bd6b655186..d320feb9a93d 100644 --- a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md +++ b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md @@ -57,6 +57,18 @@ Only workspace admins can send invoices. Invoices can be sent directly from Expe {% include end-selector.html %} +![Go to Account Settings click Workspace]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_01.png)){:width="100%"} + +![Click More Features for the workspace and enable Invoices]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_02.png)){:width="100%"} + +![Click the green button Send Invoice]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_03.png)){:width="100%"} + +![Enter Invoice amount]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_04.png)){:width="100%"} + +![Choose a recipient]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_05.png)){:width="100%"} + +![Add Invoice details and Send Invoice]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_06.png)){:width="100%"} + # Receive invoice payment If you have not [connected a business bank account](https://help.expensify.com/articles/new-expensify/expenses-&-payments/Connect-a-Business-Bank-Account) to receive invoice payments, you will see an **Invoice balance** in your [Wallet](https://help.expensify.com/articles/new-expensify/expenses-&-payments/Set-up-your-wallet). Expensify will automatically transfer these invoice payments once a business bank account is connected. From 9548df38308885fde366c564253467ac8d928dd7 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:09:09 -0600 Subject: [PATCH 100/180] Update Pay-an-invoice.md Adding images to the Pay-an-invoice help article https://help.expensify.com/articles/new-expensify/expenses-&-payments/Pay-an-invoice --- .../new-expensify/expenses-&-payments/Pay-an-invoice.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md index 727c6b86b7a6..a1786f8350a4 100644 --- a/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md +++ b/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md @@ -32,6 +32,10 @@ To pay an invoice, You can also view all unpaid invoices by searching for the sender’s email or phone number on the left-hand side of the app. The invoices waiting for your payment will have a green dot. +![Click Pay Button on the Invoice]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoice_01.png)){:width="100%"} + +![Choose how to pay the invoice from the options]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoice_02.png)){:width="100%"} + {% include faq-begin.md %} **Can someone else pay an invoice besides the person who received it?** From 8460793e25f42116170a8a306d4326cdcb7e82e9 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:40:13 -0600 Subject: [PATCH 101/180] Update Pay-an-invoice.md Updated the image link as I entered it wrong --- .../new-expensify/expenses-&-payments/Pay-an-invoice.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md index a1786f8350a4..d7ded144415b 100644 --- a/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md +++ b/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md @@ -32,9 +32,9 @@ To pay an invoice, You can also view all unpaid invoices by searching for the sender’s email or phone number on the left-hand side of the app. The invoices waiting for your payment will have a green dot. -![Click Pay Button on the Invoice]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoice_01.png)){:width="100%"} +![Click Pay Button on the Invoice]({{site.url}}/assets/images/invoice_01.png){:width="100%"} -![Choose how to pay the invoice from the options]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoice_02.png)){:width="100%"} +![Choose how to pay the invoice from the options]({{site.url}}/assets/images/invoice_02.png){:width="100%"} {% include faq-begin.md %} From 116348cf7ef588a9d384af1d3f8380d98f6f86ef Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:41:28 -0600 Subject: [PATCH 102/180] Update Send-an-invoice.md Updated the image link because I did it wrong the first time. --- .../expenses-&-payments/Send-an-invoice.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md index d320feb9a93d..4d7f4e0dc55f 100644 --- a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md +++ b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md @@ -57,17 +57,17 @@ Only workspace admins can send invoices. Invoices can be sent directly from Expe {% include end-selector.html %} -![Go to Account Settings click Workspace]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_01.png)){:width="100%"} +![Go to Account Settings click Workspace]({{site.url}}/assets/images/invoices_01.png)){:width="100%"} -![Click More Features for the workspace and enable Invoices]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_02.png)){:width="100%"} +![Click More Features for the workspace and enable Invoices]({{site.url}}/assets/images/invoices_02.png)){:width="100%"} -![Click the green button Send Invoice]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_03.png)){:width="100%"} +![Click the green button Send Invoice]({{site.url}}/assets/images/invoices_03.png)){:width="100%"} -![Enter Invoice amount]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_04.png)){:width="100%"} +![Enter Invoice amount]({{site.url}}/assets/images/invoices_04.png)){:width="100%"} -![Choose a recipient]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_05.png)){:width="100%"} +![Choose a recipient]({{site.url}}/assets/images/invoices_05.png)){:width="100%"} -![Add Invoice details and Send Invoice]({{site.url}}/assets/images/[image-name.png](https://github.com/Expensify/App/blob/main/docs/assets/images/invoices_06.png)){:width="100%"} +![Add Invoice details and Send Invoice]({{site.url}}/assets/images/invoices_06.png)){:width="100%"} # Receive invoice payment From 288ccae9d218dde3a2c4094469f9871a6b94167a Mon Sep 17 00:00:00 2001 From: David Barrett Date: Tue, 24 Sep 2024 18:38:28 -0700 Subject: [PATCH 103/180] Slight updates from review --- .github/workflows/deployNewHelp.yml | 2 +- help/_config.yml | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index c9061e99861b..35c6c1f184dc 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -55,7 +55,7 @@ jobs: - name: Purge Cloudflare cache if: env.IS_PR_FROM_FORK != 'true' - run: $HOME/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + run: "$HOME/.local/bin/cli4" --verbose --delete hosts=["newhelp.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} diff --git a/help/_config.yml b/help/_config.yml index ef7ba7c13913..72bb4d942f9a 100644 --- a/help/_config.yml +++ b/help/_config.yml @@ -18,16 +18,14 @@ # You can create any custom variable you would like, and they will be accessible # in the templates via {{ site.myvariable }}. -title: Your awesome title -email: your-email@example.com +title: New Expensify Help +email: concierge@expensify.com description: >- # this means to ignore newlines until "baseurl:" - Write an awesome description for your new site here. You can edit this - line in _config.yml. It will appear in your document head meta (for - Google search results) and in your feed.xml site description. + Comprehensive help documentation for New Expensify. baseurl: "" # the subpath of your site, e.g. /blog -url: "" # the base hostname & protocol for your site, e.g. http://example.com -twitter_username: jekyllrb -github_username: jekyll +url: "https://newhelp.expensify.com" # the base hostname & protocol for your site, e.g. http://example.com +twitter_username: expensify +github_username: expensify # Build settings theme: minima From 903aedf404ee877a63a0fb5137d676f5e4fb8b22 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Tue, 24 Sep 2024 18:41:43 -0700 Subject: [PATCH 104/180] Undoing suggested fix to see if it fixees worse syntax error --- .github/workflows/deployNewHelp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 35c6c1f184dc..915469f97b0d 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -55,7 +55,7 @@ jobs: - name: Purge Cloudflare cache if: env.IS_PR_FROM_FORK != 'true' - run: "$HOME/.local/bin/cli4" --verbose --delete hosts=["newhelp.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + run: $HOME/.local/bin/cli4 --verbose --delete hosts=["newhelp.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} From 2b4f0d2a58c7d709dab646d68b22576d7ef575af Mon Sep 17 00:00:00 2001 From: David Barrett Date: Tue, 24 Sep 2024 19:39:48 -0700 Subject: [PATCH 105/180] Added basic superapp page --- help/_layouts/default.html | 25 ++++++++ help/_layouts/product.html | 11 ++++ help/superapp.md | 116 +++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 help/_layouts/default.html create mode 100644 help/_layouts/product.html create mode 100644 help/superapp.md diff --git a/help/_layouts/default.html b/help/_layouts/default.html new file mode 100644 index 000000000000..cf95e1f54b06 --- /dev/null +++ b/help/_layouts/default.html @@ -0,0 +1,25 @@ + + + + + + {{ page.title }} + + +
+ +
+ + +
+ {{ content }} +
+ +
+

© 2024 Your Website

+
+ + + diff --git a/help/_layouts/product.html b/help/_layouts/product.html new file mode 100644 index 000000000000..cb8b5e882f24 --- /dev/null +++ b/help/_layouts/product.html @@ -0,0 +1,11 @@ +--- +layout: default +--- + +

{{ page.title }}

+ + +
+ {{ content }} +
+ diff --git a/help/superapp.md b/help/superapp.md new file mode 100644 index 000000000000..c09ca61bcf46 --- /dev/null +++ b/help/superapp.md @@ -0,0 +1,116 @@ +--- +layout: product +title: Expensify Superapp +--- +# Expensify Superapp + +## Introduction +The Expensify Superapp packs the full power of 6 world class business, finance, and collaboration products, into a single app that works identically on desktop and mobile, efficiently with your colleagues, and seamlessly with your customers, vendors, family, and friends. + +### When should I use Expensify? +Expensify can do a lot. You should check us out whenever you need to: + +Track and manage expenses +: Whether you are reimbursing employee receipts, deducting personal expenses, or just splitting the bill, Expensify Expense is for you. + +Issue corporate cards +: Skip the reimbursement and capture receipts electronically in realtime by issuing the Expensify Card to yourself and your employees. + +Book and manage travel +: If you are booking your own business trip, arranging a trip for a colleague, or managing the travel of your whole company, Expensify Travel has got you covered. + +Chat with friends and coworkers +: Whether it's collaborating with your team, supporting you client, negotiating with your vendor, or just saying Hi to a friend, Expensify Chat connects you with anyone with an email address or SMS number + +Collect invoice payments online +: Expensify Invoice allows you to collect online payments from consumers and businesses alike – anyone with an email address or SMS number. + +Approve and pay bills online +: Scan, process, and approve bills online using Expensify Billpay, then we'll pay them electronically or via check, whatever they prefer. + +If you send, receive, or spend money – or even just talk to literally anyone, about literally anything – Expensify is the tool for you. + +### Who uses Expensify? +Expensify offers something for everyone. Some people who commonly use us include: + +Individuals +: Millions of individuals use Expensify to track personal expenses to maximize their tax deductions, stay within personal budgets, or just see where their money is going. + +Friends +: Expensify is a great way to split bills with friends, whether it's monthly rent and household expenses, a big ticket bachelorette party, or just grabbing drinks with friends. + +Employees +: Road warriors and desk jockeys alike count on Expensify to reimburse expense reports they create in international airports, swanky hotels, imposing conference centers, quaint coffeeshops, and boring office supply stores around the world. + +Managers +: Bosses manage corporate spend with Expensify to empower their best (and keep tabs on their… not so best), staying ahead of schedule and under budget. + +Accountants +: Internal accountants, fractional CFOs, CAS practices – you name it, they use Expensify to Invoice customers, process vendor bills, capture eReceipts, manage corporate spend: the whole shebang. If you're an accountant, we're already best friends. + +Travel managers +: Anyone looking to manage employee travel has come to the right place. + +If you are a person online who does basically anything, you can probably do it with Expensify. + +### Why should I use Expensify? +Though we do a lot, you've got a lot of options for everything we do. But you should use us because we are: +Simple enough for individuals - We've worked extremely hard to make a product that strips out all the complex jargon and enterprise baggage, and gives you a simple tool that doesn't overwhelm you with functionality and language you don't understand. + +Powerful enough for enterprises +: We've worked extremely hard to make a product that "scales up" to reveal increasingly sophisticated features, but only to those who need it, and only when they need it. Expensify is used by public companies, multinational companies, companies with tens of thousands of employees, non-profits, investment firms, accounting firms, manufacturers, and basically every industry in every currency and in every country around the world. If you are a company, we can support your needs, no matter how big or small. + +6 products for the price of 1 +: Do you pay for an expense management system? A corporate card? A travel management platform? An enterprise chat tool? An invoicing tool? A billpay tool? Now you don't need to. Expensify's superapp design allows us to offer ALL these features on a single platform, at probably less than what you pay for any of them individually. + +Supports everyone everywhere +: Expensify works on iPhones and Androids, desktops and browsers. We support every currency, and can reimburse to almost any country. You don't need to be an IT wizard – if you can type in their email address or SMS number, you can do basically everything with them. + +You get paid to use it +: Do you spend money? Spend it on the Expensify Card and we pay you up to 2% cashback. It's your money after all. + +Revenue share for accountants +: Do you manage the books for a bunch of clients? Become an Expensify Approved Accountant and take home 0.5% revenue share. Or share it with your clients as a discount, up to you! + +You are in the driver's seat; we're here to earn your business. But we're going to work harder for you than the other guys, and you won't be disappointed. + +## Concepts +The Expensify Superapp has a lot of moving pieces, so let's break them down one by one. + +### What makes Expensify a superapp? +A "superapp" is a single app that combines multiple products into one seamlessly interconnected experience. Expensify isn't a "suite" of separate products linked through a single account – Expensify is a single app with a single core design that can perform multiple product functions. The secret to making such a seamless experience is that we build all product functions atop the same common core: + +App +: The basis of the superapp experience is the actual app itself, which runs on your mobile phone or desktop computer. (What is the Expensify app?) + +Chats +: Even if you don't plan on using Expensify Chat for enterprise-grade workspace collaboration, chat is infused through the entire product. (What is a chat?) + +Expense +: Even if you aren't actively managing your expenses, you've still got them. Every product that deals with money is ultimately dealing with expenses of some kind. (What is an expense?) + +Workspace +: Though Expensify works great for our millions of individual members, every product really shines when used between groups of members sharing a "workspace". (What is a workspace?) + +Domain +: To support more advanced security features, many products provide extra functionality to members who are on the same email "domain". (What is a domain?) + +These are the foundational concepts you'll see again and again that underpin the superapp as a whole. + +### What is the Expensify app? +Just like your eyes are a window to your soul, the Expensify App is the doorway through which you experience the entire global world of interconnected chat-centric collaborative data that comprises the Expensify network. The main tools of this app consist of: + +Inbox +: The main screen of the app is the Inbox, which highlights exactly what you should do next, consolidated across all products. (What does the Inbox do?) + +Search +: The next major screen is Search, which as you'd expect, let's you search everything across all products, from one convenient and powerful place. (What does Search do?) + +Settings +: Settings wraps up all your personal, workspace, and domain configuration options, all in one helpful space. (What are Expensify's settings?) + +Create +: Finally, the big green plus button is the Create button, which lets you create pretty much anything, across all the products. (What does the Create button do?) + +It's a deceptively simple app, with a few very familiar looking screens and buttons that unlock an incredible range of sophisticated multi-product power. + From 808d5a92b315659452e590ab8ff974a3a813253d Mon Sep 17 00:00:00 2001 From: David Barrett Date: Tue, 24 Sep 2024 21:56:13 -0700 Subject: [PATCH 106/180] Updated for custom plugin for deep linking --- help/Gemfile | 6 +++ help/Gemfile.lock | 20 ++++++++- help/_plugins/51_HeaderIDPostRender.rb | 59 ++++++++++++++++++++++++++ help/superapp.md | 1 - 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 help/_plugins/51_HeaderIDPostRender.rb diff --git a/help/Gemfile b/help/Gemfile index 2c345b82ae4d..3bdd94c99fb9 100644 --- a/help/Gemfile +++ b/help/Gemfile @@ -8,8 +8,10 @@ source "https://rubygems.org" # This will help ensure the proper Jekyll version is running. # Happy Jekylling! gem "jekyll", "~> 4.3.4" + # This is the default theme for new Jekyll sites. You may change this to anything you like. gem "minima", "~> 2.5" + # If you want to use GitHub Pages, remove the "gem "jekyll"" above and # uncomment the line below. To upgrade, run `bundle update github-pages`. # gem "github-pages", group: :jekyll_plugins @@ -31,3 +33,7 @@ gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin] # Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem # do not have a Java counterpart. gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby] + +# For HTML editing +gem "nokogiri" + diff --git a/help/Gemfile.lock b/help/Gemfile.lock index 77ac51524535..7434e1c4e935 100644 --- a/help/Gemfile.lock +++ b/help/Gemfile.lock @@ -76,13 +76,30 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.4.0) + mini_portile2 (2.8.7) minima (2.5.2) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) + nokogiri (1.16.7) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.16.7-aarch64-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86-linux) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-linux) + racc (~> 1.4) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (6.0.1) + racc (1.8.1) rake (13.2.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) @@ -138,7 +155,7 @@ GEM terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) unicode-display_width (2.6.0) - webrick (1.8.1) + webrick (1.8.2) PLATFORMS aarch64-linux @@ -174,6 +191,7 @@ DEPENDENCIES jekyll (~> 4.3.4) jekyll-feed (~> 0.12) minima (~> 2.5) + nokogiri tzinfo (>= 1, < 3) tzinfo-data wdm (~> 0.1) diff --git a/help/_plugins/51_HeaderIDPostRender.rb b/help/_plugins/51_HeaderIDPostRender.rb new file mode 100644 index 000000000000..4af97cc788f6 --- /dev/null +++ b/help/_plugins/51_HeaderIDPostRender.rb @@ -0,0 +1,59 @@ +require 'nokogiri' +require 'cgi' # Use CGI for URL encoding + +module Jekyll + class HeaderIDPostRender + # Hook into Jekyll's post_render stage to ensure we work with the final HTML + Jekyll::Hooks.register :pages, :post_render, priority: 51 do |page| + process_page(page) + end + + Jekyll::Hooks.register :documents, :post_render, priority: 51 do |post| + process_page(post) + end + + def self.process_page(page) + return unless page.output_ext == ".html" # Only apply to HTML pages + return if page.output.nil? # Skip if no output has been generated + + puts " Processing page: #{page.path}" + + # Parse the page's content for header elements + doc = Nokogiri::HTML(page.output) + h1_id = "" + h2_id = "" + h3_id = "" + + # Process all

,

, and

elements + (2..4).each do |level| + doc.css("h#{level}").each do |header| + header_text = header.text.strip.downcase + header_id = CGI.escape(header_text.gsub(/\s+/, '-').gsub(/[^\w\-]/, '')) + + puts " Found h#{level}: '#{header_text}' -> ID: '#{header_id}'" + + # Create hierarchical IDs by appending to the parent header IDs + if level == 2 + h2_id = header_id + header['id'] = h2_id + elsif level == 3 + h3_id = "#{h2_id}:#{header_id}" + header['id'] = h3_id + elsif level == 4 + h4_id = "#{h3_id}:#{header_id}" + header['id'] = h4_id + end + + puts " Assigned ID: #{header['id']}" + end + end + + # Log the final output being written + puts " Writing updated HTML for page: #{page.path}" + + # Write the updated HTML back to the page + page.output = doc.to_html + end + end +end + diff --git a/help/superapp.md b/help/superapp.md index c09ca61bcf46..d09860a1ce7e 100644 --- a/help/superapp.md +++ b/help/superapp.md @@ -2,7 +2,6 @@ layout: product title: Expensify Superapp --- -# Expensify Superapp ## Introduction The Expensify Superapp packs the full power of 6 world class business, finance, and collaboration products, into a single app that works identically on desktop and mobile, efficiently with your colleagues, and seamlessly with your customers, vendors, family, and friends. From d5e34283d0b7262a22e95cf533c0c659442ffe7a Mon Sep 17 00:00:00 2001 From: Gandalf Date: Wed, 25 Sep 2024 13:53:07 +0530 Subject: [PATCH 107/180] Update src/libs/ReportUtils.ts Co-authored-by: Daniel Gale-Rosen <5487802+dangrous@users.noreply.github.com> --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ed167cf123f4..f179cd35827f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4179,6 +4179,7 @@ function buildOptimisticAddCommentReportAction( const isAttachmentWithText = !!text && file !== undefined; const accountID = actorAccountID ?? currentUserAccountID ?? -1; const delegateAccountDetails = PersonalDetailsUtils.getPersonalDetailByEmail(delegateEmail); + // Remove HTML from text when applying optimistic offline comment return { commentText, From 203403cbedf15b19a848e0742491888ef99a7f20 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 25 Sep 2024 15:32:42 +0700 Subject: [PATCH 108/180] fix: deleted item remains actionable --- src/pages/Search/SavedSearchItemThreeDotMenu.tsx | 11 ++++++++--- src/pages/Search/SearchTypeMenu.tsx | 8 +++++++- src/pages/Search/SearchTypeMenuNarrow.tsx | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx index fdb06828901e..bd7a94bc1840 100644 --- a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx +++ b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx @@ -2,18 +2,23 @@ import React, {useRef, useState} from 'react'; import {View} from 'react-native'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; +import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; type SavedSearchItemThreeDotMenuProps = { menuItems: PopoverMenuItem[]; + isDisabledItem: boolean; }; -function SavedSearchItemThreeDotMenu({menuItems}: SavedSearchItemThreeDotMenuProps) { +function SavedSearchItemThreeDotMenu({menuItems, isDisabledItem}: SavedSearchItemThreeDotMenuProps) { const threeDotsMenuContainerRef = useRef(null); const [threeDotsMenuPosition, setThreeDotsMenuPosition] = useState({horizontal: 0, vertical: 0}); - + const styles = useThemeStyles(); return ( - + { diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index 704ebbb5316e..947f81ac4e8d 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -116,9 +116,15 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { SearchActions.clearAllFilters(); Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: item?.query ?? ''})); }, - rightComponent: , + rightComponent: ( + + ), styles: [styles.alignItemsCenter], pendingAction: item.pendingAction, + disabled: item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, }; if (!isNarrow) { diff --git a/src/pages/Search/SearchTypeMenuNarrow.tsx b/src/pages/Search/SearchTypeMenuNarrow.tsx index 5c466b95d461..06bc016669df 100644 --- a/src/pages/Search/SearchTypeMenuNarrow.tsx +++ b/src/pages/Search/SearchTypeMenuNarrow.tsx @@ -118,10 +118,12 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, queryJSON, title, horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }} + disabled={item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE} /> ), isSelected: currentSavedSearch?.hash === item.hash, pendingAction: item.pendingAction, + disabled: item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, })); const allMenuItems = []; allMenuItems.push(...popoverMenuItems); From f04c1e9533a695dbf7ed1776ffd7ed54beae30ee Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 25 Sep 2024 15:34:39 +0700 Subject: [PATCH 109/180] enable form when offline --- src/pages/Search/SavedSearchRenamePage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Search/SavedSearchRenamePage.tsx b/src/pages/Search/SavedSearchRenamePage.tsx index 2b227e581ac4..bc9a41597fa2 100644 --- a/src/pages/Search/SavedSearchRenamePage.tsx +++ b/src/pages/Search/SavedSearchRenamePage.tsx @@ -56,6 +56,7 @@ function SavedSearchRenamePage({route}: {route: {params: {q: string; name: strin submitButtonText={translate('common.save')} onSubmit={onSaveSearch} style={[styles.mh5, styles.flex1]} + enabledWhenOffline > Date: Wed, 25 Sep 2024 11:09:23 +0200 Subject: [PATCH 110/180] use compatible version with new Fabric arch --- package-lock.json | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87c7dd3d2600..b2638d613999 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,7 +112,7 @@ "react-native-svg": "15.6.0", "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", - "react-native-view-shot": "3.8.0", + "react-native-view-shot": "4.0.0-alpha.3", "react-native-vision-camera": "4.0.0-beta.13", "react-native-web": "^0.19.12", "react-native-web-sound": "^0.1.3", @@ -35666,8 +35666,9 @@ } }, "node_modules/react-native-view-shot": { - "version": "3.8.0", - "license": "MIT", + "version": "4.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-4.0.0-alpha.3.tgz", + "integrity": "sha512-o0KVgC6XZqWmLUKVc4q6Ev1QW1kA4g/TF45wj8CgYS13wJuWYJ+nPGCHT9C2jvX/L65mtTollKXp2L8hbDnelg==", "dependencies": { "html2canvas": "^1.4.1" }, diff --git a/package.json b/package.json index 056d29a36c19..a80a12758745 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,7 @@ "react-native-svg": "15.6.0", "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", - "react-native-view-shot": "3.8.0", + "react-native-view-shot": "4.0.0-alpha.3", "react-native-vision-camera": "4.0.0-beta.13", "react-native-web": "^0.19.12", "react-native-web-sound": "^0.1.3", From 1e689679442cb286bad5e6dec5df1ed7187924f2 Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Wed, 25 Sep 2024 22:41:21 +1200 Subject: [PATCH 111/180] Fix tooltip size becoming small after suspending and unsuspending --- src/components/Tooltip/BaseGenericTooltip/index.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/Tooltip/BaseGenericTooltip/index.tsx b/src/components/Tooltip/BaseGenericTooltip/index.tsx index 41f3e97c8087..4254080f2e96 100644 --- a/src/components/Tooltip/BaseGenericTooltip/index.tsx +++ b/src/components/Tooltip/BaseGenericTooltip/index.tsx @@ -50,8 +50,17 @@ function BaseGenericTooltip({ useLayoutEffect(() => { // Calculate the tooltip width and height before the browser repaints the screen to prevent flicker // because of the late update of the width and the height from onLayout. + const rootWrapperStyle = rootWrapper?.current?.style; + const isScaled = rootWrapperStyle?.transform === 'scale(0)'; + if (isScaled) { + // Temporarily reset the scale caused by animation to get the untransformed size. + rootWrapperStyle.transform = 'scale(1)'; + } setContentMeasuredWidth(contentRef.current?.getBoundingClientRect().width); setWrapperMeasuredHeight(rootWrapper.current?.getBoundingClientRect().height); + if (isScaled) { + rootWrapperStyle.transform = 'scale(0)'; + } }, []); const {animationStyle, rootWrapperStyle, textStyle, pointerWrapperStyle, pointerStyle} = useMemo( From a452a8effdb298b1bf4185223163557715923975 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Wed, 25 Sep 2024 05:41:35 -0700 Subject: [PATCH 112/180] Removed Github pages dependency to get plugins --- .github/workflows/deployNewHelp.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 915469f97b0d..dfe3f63571dc 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -27,17 +27,26 @@ jobs: env: IS_PR_FROM_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest + steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1.0.13 # Using the latest stable version + - name: Set up Ruby + uses: ruby/setup-ruby@v1 with: - source: ./help/ - destination: ./help/_site + ruby-version: '3.3.4' # Set to your Ruby version + + - name: Install dependencies + run: | + gem install bundler + bundle install --path vendor/bundle + + - name: Build Jekyll site + run: | + bundle exec jekyll build --source ./help --destination ./help/_site - name: Deploy to Cloudflare Pages uses: cloudflare/pages-action@v1 # Using the latest stable version From 304fdb55ac392bfb717280c399b66e010f4e92c9 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Wed, 25 Sep 2024 05:49:43 -0700 Subject: [PATCH 113/180] Trying to fix build issues in github acitons --- help/Gemfile | 36 ++++++++++++++---------------------- help/Gemfile.lock | 4 +++- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/help/Gemfile b/help/Gemfile index 3bdd94c99fb9..5afaaed2bc64 100644 --- a/help/Gemfile +++ b/help/Gemfile @@ -1,39 +1,31 @@ source "https://rubygems.org" -# Hello! This is where you manage which Jekyll version is used to run. -# When you want to use a different version, change it below, save the -# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: -# -# bundle exec jekyll serve -# -# This will help ensure the proper Jekyll version is running. -# Happy Jekylling! + +# Manage Jekyll version gem "jekyll", "~> 4.3.4" -# This is the default theme for new Jekyll sites. You may change this to anything you like. +# Default theme for new Jekyll sites gem "minima", "~> 2.5" -# If you want to use GitHub Pages, remove the "gem "jekyll"" above and -# uncomment the line below. To upgrade, run `bundle update github-pages`. -# gem "github-pages", group: :jekyll_plugins -# If you have any plugins, put them here! +# Plugins for Jekyll group :jekyll_plugins do gem "jekyll-feed", "~> 0.12" end -# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem -# and associated library. +# For development on macOS and Linux (add webrick for Jekyll 4.3+) +gem "webrick", "~> 1.8" + +# Nokogiri for HTML editing +gem "nokogiri", "~> 1.12" + +# Optional: Add tzinfo for timezone support (mainly for Windows/JRuby environments) platforms :mingw, :x64_mingw, :mswin, :jruby do gem "tzinfo", ">= 1", "< 3" gem "tzinfo-data" end -# Performance-booster for watching directories on Windows -gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin] - -# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem -# do not have a Java counterpart. +# Lock http_parser.rb gem to v0.6.x for JRuby gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby] -# For HTML editing -gem "nokogiri" +# Performance-booster for watching directories on Windows +gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin] diff --git a/help/Gemfile.lock b/help/Gemfile.lock index 7434e1c4e935..064546f52759 100644 --- a/help/Gemfile.lock +++ b/help/Gemfile.lock @@ -190,11 +190,13 @@ DEPENDENCIES http_parser.rb (~> 0.6.0) jekyll (~> 4.3.4) jekyll-feed (~> 0.12) + jekyll-seo-tag minima (~> 2.5) - nokogiri + nokogiri (~> 1.12) tzinfo (>= 1, < 3) tzinfo-data wdm (~> 0.1) + webrick (~> 1.8) BUNDLED WITH 2.5.19 From c2080347f0de54ece199c332c50fb37183772a2a Mon Sep 17 00:00:00 2001 From: David Barrett Date: Wed, 25 Sep 2024 05:57:53 -0700 Subject: [PATCH 114/180] More attempted fixes --- .github/workflows/deployNewHelp.yml | 14 +++++--------- help/Gemfile | 20 +++----------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index dfe3f63571dc..24cb8e79fdcf 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -1,23 +1,18 @@ on: - # Run on any push to main that has changes to the help directory push: branches: - main paths: - 'help/**' - # Run on any pull request (except PRs against staging or production) that has changes to the docs directory pull_request: types: [opened, synchronize] branches-ignore: [staging, production] paths: - 'help/**' - # Run on any manual trigger workflow_dispatch: -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "newhelp" cancel-in-progress: false @@ -37,19 +32,20 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3.4' # Set to your Ruby version + ruby-version: '3.3.4' - name: Install dependencies run: | gem install bundler - bundle install --path vendor/bundle + bundle config set --local path 'vendor/bundle' + bundle install - name: Build Jekyll site run: | bundle exec jekyll build --source ./help --destination ./help/_site - name: Deploy to Cloudflare Pages - uses: cloudflare/pages-action@v1 # Using the latest stable version + uses: cloudflare/pages-action@v1 id: deploy if: env.IS_PR_FROM_FORK != 'true' with: @@ -69,7 +65,7 @@ jobs: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} - name: Leave a comment on the PR - uses: actions-cool/maintain-one-comment@v3.2.0 # Using the latest stable version + uses: actions-cool/maintain-one-comment@v3.2.0 if: ${{ github.event_name == 'pull_request' && env.IS_PR_FROM_FORK != 'true' }} with: token: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/help/Gemfile b/help/Gemfile index 5afaaed2bc64..9ff999c94703 100644 --- a/help/Gemfile +++ b/help/Gemfile @@ -1,31 +1,17 @@ source "https://rubygems.org" -# Manage Jekyll version gem "jekyll", "~> 4.3.4" - -# Default theme for new Jekyll sites gem "minima", "~> 2.5" +gem "nokogiri" -# Plugins for Jekyll group :jekyll_plugins do gem "jekyll-feed", "~> 0.12" end -# For development on macOS and Linux (add webrick for Jekyll 4.3+) -gem "webrick", "~> 1.8" - -# Nokogiri for HTML editing -gem "nokogiri", "~> 1.12" - -# Optional: Add tzinfo for timezone support (mainly for Windows/JRuby environments) platforms :mingw, :x64_mingw, :mswin, :jruby do gem "tzinfo", ">= 1", "< 3" gem "tzinfo-data" end -# Lock http_parser.rb gem to v0.6.x for JRuby -gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby] - -# Performance-booster for watching directories on Windows -gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin] - +gem "wdm", "~> 0.1", platforms: [:mingw, :x64_mingw, :mswin] +gem "http_parser.rb", "~> 0.6.0", platforms: [:jruby] From 1f50571e3272dfe9247efb36601cdd8dd85a8808 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 25 Sep 2024 14:59:42 +0200 Subject: [PATCH 115/180] useOnyx migration on FormProvider --- src/components/Form/FormProvider.tsx | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index db52c45751b7..9474a4a6de99 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -4,13 +4,13 @@ import type {ForwardedRef, MutableRefObject, ReactNode, RefAttributes} from 'rea import React, {createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputSubmitEditingEventData, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import * as ValidationUtils from '@libs/ValidationUtils'; import Visibility from '@libs/Visibility'; import * as FormActions from '@userActions/FormActions'; import CONST from '@src/CONST'; -import type {OnyxFormKey} from '@src/ONYXKEYS'; +import type {OnyxFormDraftKey, OnyxFormKey} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Form} from '@src/types/form'; import type {Network} from '@src/types/onyx'; @@ -89,17 +89,17 @@ function FormProvider( shouldValidateOnBlur = true, shouldValidateOnChange = true, children, - formState, - network, enabledWhenOffline = false, - draftValues, onSubmit, shouldTrimValues = true, allowHTML = false, ...rest - }: FormProviderProps, + }: Omit, forwardedRef: ForwardedRef, ) { + const [network] = useOnyx(ONYXKEYS.NETWORK); + const [formState] = useOnyx(`${formID}`); + const [draftValues] = useOnyx(`${formID}Draft`); const {preferredLocale, translate} = useLocalize(); const inputRefs = useRef({}); const touchedInputs = useRef>({}); @@ -404,19 +404,6 @@ function FormProvider( FormProvider.displayName = 'Form'; -export default withOnyx({ - network: { - key: ONYXKEYS.NETWORK, - }, - // withOnyx typings are not able to handle such generic cases like this one, since it's a generic component we need to cast the keys to any - formState: { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any - key: ({formID}) => formID as any, - }, - draftValues: { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any - key: (props) => `${props.formID}Draft` as any, - }, -})(forwardRef(FormProvider)) as (props: Omit & RefAttributes, keyof FormProviderOnyxProps>) => ReactNode; +export default forwardRef(FormProvider) as (props: Omit & RefAttributes, keyof FormProviderOnyxProps>) => ReactNode; export type {FormProviderProps}; From 216ded6456ff80ac022e2ccc5f516605ddb1e2b2 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Wed, 25 Sep 2024 06:16:39 -0700 Subject: [PATCH 116/180] Explicitly installing Jekyll --- .github/workflows/deployNewHelp.yml | 11 +++++++++-- help/Gemfile | 2 ++ help/Gemfile.lock | 4 +--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 24cb8e79fdcf..784d5934e395 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -1,18 +1,23 @@ on: + # Run on any push to main that has changes to the help directory push: branches: - main paths: - 'help/**' + # Run on any pull request (except PRs against staging or production) that has changes to the docs directory pull_request: types: [opened, synchronize] branches-ignore: [staging, production] paths: - 'help/**' + # Run on any manual trigger workflow_dispatch: +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "newhelp" cancel-in-progress: false @@ -32,13 +37,14 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3.4' + ruby-version: '3.3.4' - name: Install dependencies run: | - gem install bundler bundle config set --local path 'vendor/bundle' bundle install + gem install jekyll # Explicitly install Jekyll + jekyll -v - name: Build Jekyll site run: | @@ -71,3 +77,4 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} + diff --git a/help/Gemfile b/help/Gemfile index 9ff999c94703..4f2e425b8aba 100644 --- a/help/Gemfile +++ b/help/Gemfile @@ -8,6 +8,7 @@ group :jekyll_plugins do gem "jekyll-feed", "~> 0.12" end +# If using tzinfo-data for timezone support, ensure it's bundled for relevant platforms platforms :mingw, :x64_mingw, :mswin, :jruby do gem "tzinfo", ">= 1", "< 3" gem "tzinfo-data" @@ -15,3 +16,4 @@ end gem "wdm", "~> 0.1", platforms: [:mingw, :x64_mingw, :mswin] gem "http_parser.rb", "~> 0.6.0", platforms: [:jruby] + diff --git a/help/Gemfile.lock b/help/Gemfile.lock index 064546f52759..7434e1c4e935 100644 --- a/help/Gemfile.lock +++ b/help/Gemfile.lock @@ -190,13 +190,11 @@ DEPENDENCIES http_parser.rb (~> 0.6.0) jekyll (~> 4.3.4) jekyll-feed (~> 0.12) - jekyll-seo-tag minima (~> 2.5) - nokogiri (~> 1.12) + nokogiri tzinfo (>= 1, < 3) tzinfo-data wdm (~> 0.1) - webrick (~> 1.8) BUNDLED WITH 2.5.19 From aeadd2aa3b2d72b16eeaf29121e738fb4484b6f0 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Wed, 25 Sep 2024 06:24:33 -0700 Subject: [PATCH 117/180] Explicitly installing ffi --- .github/workflows/deployNewHelp.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 784d5934e395..bba841eaafb6 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -39,6 +39,10 @@ jobs: with: ruby-version: '3.3.4' + # Explicitly install ffi gem to avoid platform issues + - name: Install ffi gem + run: gem install ffi --platform ruby + - name: Install dependencies run: | bundle config set --local path 'vendor/bundle' @@ -46,6 +50,13 @@ jobs: gem install jekyll # Explicitly install Jekyll jekyll -v + # Clear Bundler cache if needed to resolve caching issues + - name: Clear Bundler cache + run: | + bundle clean --force + bundle install --path vendor/bundle + + # Use bundle exec to ensure Jekyll uses the correct gem versions - name: Build Jekyll site run: | bundle exec jekyll build --source ./help --destination ./help/_site @@ -77,4 +88,3 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} - From 0cfd8403ab6329230e3948118db0fcc6183abb4f Mon Sep 17 00:00:00 2001 From: David Barrett Date: Wed, 25 Sep 2024 06:30:18 -0700 Subject: [PATCH 118/180] More fixes --- .github/workflows/deployNewHelp.yml | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index bba841eaafb6..d4abb54ba653 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -1,23 +1,16 @@ on: - # Run on any push to main that has changes to the help directory push: branches: - main paths: - 'help/**' - - # Run on any pull request (except PRs against staging or production) that has changes to the docs directory pull_request: types: [opened, synchronize] branches-ignore: [staging, production] paths: - 'help/**' - - # Run on any manual trigger workflow_dispatch: -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "newhelp" cancel-in-progress: false @@ -37,26 +30,15 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3.4' - - # Explicitly install ffi gem to avoid platform issues - - name: Install ffi gem - run: gem install ffi --platform ruby + ruby-version: '3.3.4' - name: Install dependencies run: | bundle config set --local path 'vendor/bundle' bundle install - gem install jekyll # Explicitly install Jekyll - jekyll -v - - # Clear Bundler cache if needed to resolve caching issues - - name: Clear Bundler cache - run: | - bundle clean --force - bundle install --path vendor/bundle + bundle exec gem install jekyll # Use bundle exec to avoid conflicts + bundle exec jekyll -v - # Use bundle exec to ensure Jekyll uses the correct gem versions - name: Build Jekyll site run: | bundle exec jekyll build --source ./help --destination ./help/_site From 0108a68715c49f2840246deb4fb230f274836a49 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 25 Sep 2024 15:58:54 +0200 Subject: [PATCH 119/180] fix props --- src/components/Form/FormProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index 9474a4a6de99..7173cd653458 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -94,7 +94,7 @@ function FormProvider( shouldTrimValues = true, allowHTML = false, ...rest - }: Omit, + }: FormProviderProps, forwardedRef: ForwardedRef, ) { const [network] = useOnyx(ONYXKEYS.NETWORK); From a14bd8fc22610c4ab71031f7b8b79bd61d022b15 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 25 Sep 2024 19:00:54 +0200 Subject: [PATCH 120/180] Refactor willBlurTextInputOnTapOutside to use getIsNarrowLayout --- src/libs/willBlurTextInputOnTapOutside/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/willBlurTextInputOnTapOutside/index.ts b/src/libs/willBlurTextInputOnTapOutside/index.ts index 987d8a1dfeea..31a40932189e 100644 --- a/src/libs/willBlurTextInputOnTapOutside/index.ts +++ b/src/libs/willBlurTextInputOnTapOutside/index.ts @@ -1,5 +1,6 @@ +import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import type WillBlurTextInputOnTapOutside from './types'; -const willBlurTextInputOnTapOutside: WillBlurTextInputOnTapOutside = () => true; +const willBlurTextInputOnTapOutside: WillBlurTextInputOnTapOutside = () => !getIsNarrowLayout(); export default willBlurTextInputOnTapOutside; From 103cacdd1f19db1def4b4c143897b30bbe75f0bf Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 25 Sep 2024 23:45:57 +0300 Subject: [PATCH 121/180] avoid double closeModal execution --- src/libs/actions/Modal.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/Modal.ts b/src/libs/actions/Modal.ts index 01ac832336ab..00853e9546d5 100644 --- a/src/libs/actions/Modal.ts +++ b/src/libs/actions/Modal.ts @@ -32,9 +32,11 @@ function closeTop() { } if (onModalClose) { closeModals[closeModals.length - 1](isNavigate); + closeModals.pop(); return; } closeModals[closeModals.length - 1](); + closeModals.pop(); } /** From 5bfaa7ec59d249396016b3ebee7351b08ac4adbc Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 25 Sep 2024 23:46:36 +0300 Subject: [PATCH 122/180] replace goBack with dismissModal --- src/pages/TransactionReceiptPage.tsx | 32 +++++++--------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/src/pages/TransactionReceiptPage.tsx b/src/pages/TransactionReceiptPage.tsx index 60f5ad4dbd86..495458c6b80f 100644 --- a/src/pages/TransactionReceiptPage.tsx +++ b/src/pages/TransactionReceiptPage.tsx @@ -1,7 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; @@ -13,19 +12,14 @@ import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; import * as ReportActions from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {Report, ReportMetadata, Transaction} from '@src/types/onyx'; -type TransactionReceiptOnyxProps = { - report: OnyxEntry; - transaction: OnyxEntry; - reportMetadata: OnyxEntry; -}; +type TransactionReceiptProps = StackScreenProps; -type TransactionReceiptProps = TransactionReceiptOnyxProps & StackScreenProps; - -function TransactionReceipt({transaction, report, reportMetadata = {isLoadingInitialReportActions: true}, route}: TransactionReceiptProps) { +function TransactionReceipt({route}: TransactionReceiptProps) { + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID ?? '-1'}`); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID ?? '-1'}`); + const [reportMetadata = {isLoadingInitialReportActions: true}] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${route.params.reportID ?? '-1'}`); const receiptURIs = ReceiptUtils.getThumbnailAndImageURIs(transaction); const imageSource = tryResolveUrlFromApiRoot(receiptURIs.image ?? ''); @@ -65,7 +59,7 @@ function TransactionReceipt({transaction, report, reportMetadata = {isLoadingIni originalFileName={receiptURIs?.filename} defaultOpen onModalClose={() => { - Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(report?.reportID ?? '-1')); + Navigation.dismissModal(report?.reportID ?? '-1'); }} isLoading={!transaction && reportMetadata?.isLoadingInitialReportActions} shouldShowNotFoundPage={shouldShowNotFoundPage} @@ -75,14 +69,4 @@ function TransactionReceipt({transaction, report, reportMetadata = {isLoadingIni TransactionReceipt.displayName = 'TransactionReceipt'; -export default withOnyx({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID ?? '-1'}`, - }, - transaction: { - key: ({route}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID ?? '-1'}`, - }, - reportMetadata: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${route.params.reportID ?? '-1'}`, - }, -})(TransactionReceipt); +export default TransactionReceipt; From 6e85e5f21623cd72e42a31305574d6f0bf3b43e8 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 25 Sep 2024 23:48:23 +0300 Subject: [PATCH 123/180] revert the wrong fix --- src/pages/home/report/ReportAttachments.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportAttachments.tsx b/src/pages/home/report/ReportAttachments.tsx index 369d5cef6ee4..3168f8b84617 100644 --- a/src/pages/home/report/ReportAttachments.tsx +++ b/src/pages/home/report/ReportAttachments.tsx @@ -19,14 +19,6 @@ function ReportAttachments({route}: ReportAttachmentsProps) { const accountID = route.params.accountID; const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); - const hasDismissedModalRef = useRef(false); - - useEffect( - () => () => { - hasDismissedModalRef.current = false; - }, - [], - ); // In native the imported images sources are of type number. Ref: https://reactnative.dev/docs/image#imagesource const source = Number(route.params.source) || route.params.source; @@ -48,10 +40,7 @@ function ReportAttachments({route}: ReportAttachmentsProps) { report={report} source={source} onModalClose={() => { - if (!hasDismissedModalRef.current) { - Navigation.dismissModal(); - hasDismissedModalRef.current = true; - } + Navigation.dismissModal(); // This enables Composer refocus when the attachments modal is closed by the browser navigation ComposerFocusManager.setReadyToFocus(); }} From 84776659ccfb67e6d534b54a59826c02a02ff791 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 25 Sep 2024 23:49:00 +0300 Subject: [PATCH 124/180] minor fix --- src/pages/home/report/ReportAttachments.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportAttachments.tsx b/src/pages/home/report/ReportAttachments.tsx index 3168f8b84617..1e16cfdddf4f 100644 --- a/src/pages/home/report/ReportAttachments.tsx +++ b/src/pages/home/report/ReportAttachments.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useCallback, useEffect, useRef} from 'react'; +import React, {useCallback} from 'react'; import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import type {Attachment} from '@components/Attachments/types'; From 6c427798cd04c2f5010bb3e5ca9a7c6dea4e1b7c Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 25 Sep 2024 16:04:58 -0700 Subject: [PATCH 125/180] Don't apply failureData if we receive a 460 response --- src/libs/actions/OnyxUpdates.ts | 4 ++++ src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 672f325be58a..f60fa15891ab 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -42,6 +42,10 @@ function applyHTTPSOnyxUpdates(request: Request, response: Response) { return updateHandler(request.successData); } if (response.jsonCode !== 200 && request.failureData) { + if (response.jsonCode === 460) { + Log.info('[OnyxUpdateManager] Received 460 status code, not applying failure data'); + return Promise.resolve(); + } return updateHandler(request.failureData); } return Promise.resolve(); diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index cc80705534b2..bbbf62d48c7e 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -109,14 +109,10 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { }, [policy, route.params.policyID, availableMembers, usedApproverEmails]); const optionItems: ToggleSettingOptionRowProps[] = useMemo(() => { - const {accountNumber, addressName, bankName, bankAccountID} = policy?.achAccount ?? {}; + const {addressName, bankName, bankAccountID} = policy?.achAccount ?? {}; const shouldShowBankAccount = !!bankAccountID && policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES; const bankIcon = getBankIcon({bankName: bankName as BankName, isCard: false, styles}); - let bankDisplayName = bankName ?? addressName; - if (accountNumber && bankDisplayName !== accountNumber) { - bankDisplayName += ` ${accountNumber.slice(-5)}`; - } const hasReimburserError = !!policy?.errorFields?.reimburser; const hasApprovalError = !!policy?.errorFields?.approvalMode; const hasDelayedSubmissionError = !!policy?.errorFields?.autoReporting ?? !!policy?.errorFields?.autoReportingFrequency; From 0fa776ae798c3637ab9a133f84d4b08c88e2847e Mon Sep 17 00:00:00 2001 From: rory Date: Wed, 25 Sep 2024 16:07:19 -0700 Subject: [PATCH 126/180] Add a comment explaining 460 errors --- src/libs/actions/OnyxUpdates.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index f60fa15891ab..1ba50d08e449 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -42,6 +42,10 @@ function applyHTTPSOnyxUpdates(request: Request, response: Response) { return updateHandler(request.successData); } if (response.jsonCode !== 200 && request.failureData) { + // 460 jsonCode in Expensify world means "admin required". + // Typically, this would only happen if a user attempts an API command that requires policy admin access when they aren't an admin. + // In this case, we don't want to apply failureData because it will likely result in a RedBrickRoad error on a policy field which is not accessible. + // Meaning that there's a red dot you can't dismiss. if (response.jsonCode === 460) { Log.info('[OnyxUpdateManager] Received 460 status code, not applying failure data'); return Promise.resolve(); From edc6980a6beec44e8e85cfa57754d7e37f902460 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Sep 2024 05:45:02 +0530 Subject: [PATCH 127/180] fix: Web - Sage - Two console errors appear when downloading the Expensify package for Sage Intacct. Signed-off-by: krishna2323 --- .../accounting/intacct/SageIntacctPrerequisitesPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx b/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx index 4e5863d48317..1c60065a08c8 100644 --- a/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx +++ b/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx @@ -40,7 +40,7 @@ function SageIntacctPrerequisitesPage({route}: SageIntacctPrerequisitesPageProps iconRight: Expensicons.NewWindow, shouldShowRightIcon: true, onPress: () => { - fileDownload(CONST.EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT, CONST.EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT_FILE_NAME); + fileDownload(CONST.EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT, CONST.EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT_FILE_NAME, '', true); }, onSecondaryInteraction: (event: GestureResponderEvent | MouseEvent) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, CONST.EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT, popoverAnchor.current), From 51684e8c69d211ec76d8eb785fbcb77f92a95a65 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 09:15:04 +0530 Subject: [PATCH 128/180] update all popup menu icons to be consistent --- src/components/MenuItem.tsx | 2 +- src/components/MenuItemList.tsx | 14 +------------- src/components/PopoverMenu.tsx | 5 ----- src/components/ThreeDotsMenu/index.tsx | 2 -- src/components/ThreeDotsMenu/types.ts | 3 --- src/pages/Search/SavedSearchItemThreeDotMenu.tsx | 1 - src/pages/Search/SearchTypeMenu.tsx | 2 -- src/pages/Search/SearchTypeMenuNarrow.tsx | 1 - src/pages/home/sidebar/AllSettingsScreen.tsx | 1 - src/pages/settings/InitialSettingsPage.tsx | 1 - src/pages/workspace/WorkspaceInitialPage.tsx | 1 - 11 files changed, 2 insertions(+), 31 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 2524658d6ffc..ec0fdeccea88 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -416,7 +416,7 @@ function MenuItem( titleWithTooltips, displayInDefaultIconColor = false, contentFit = 'cover', - isPaneMenu = false, + isPaneMenu = true, shouldPutLeftPaddingWhenNoIcon = false, onFocus, onBlur, diff --git a/src/components/MenuItemList.tsx b/src/components/MenuItemList.tsx index d33a17f90a5e..b2d79b6243ac 100644 --- a/src/components/MenuItemList.tsx +++ b/src/components/MenuItemList.tsx @@ -49,20 +49,9 @@ type MenuItemListProps = { /** Icon Height */ iconHeight?: number; - - /** Is this in the Pane */ - isPaneMenu?: boolean; }; -function MenuItemList({ - menuItems = [], - shouldUseSingleExecution = false, - wrapperStyle = {}, - icon = undefined, - iconWidth = undefined, - iconHeight = undefined, - isPaneMenu = false, -}: MenuItemListProps) { +function MenuItemList({menuItems = [], shouldUseSingleExecution = false, wrapperStyle = {}, icon = undefined, iconWidth = undefined, iconHeight = undefined}: MenuItemListProps) { const popoverAnchor = useRef(null); const {isExecuting, singleExecution} = useSingleExecution(); @@ -99,7 +88,6 @@ function MenuItemList({ icon={icon} iconWidth={iconWidth} iconHeight={iconHeight} - isPaneMenu={isPaneMenu} // eslint-disable-next-line react/jsx-props-no-spreading {...menuItemProps} disabled={!!menuItemProps.disabled || isExecuting} diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 81bb70a769f0..3b074bf772e6 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -103,9 +103,6 @@ type PopoverMenuProps = Partial & { /** Whether to show the selected option checkmark */ shouldShowSelectedItemCheck?: boolean; - - /** Is this in the Pane */ - isPaneMenu?: boolean; }; function PopoverMenu({ @@ -131,7 +128,6 @@ function PopoverMenu({ shouldEnableNewFocusManagement, restoreFocusType, shouldShowSelectedItemCheck = false, - isPaneMenu = false, }: PopoverMenuProps) { const styles = useThemeStyles(); const theme = useTheme(); @@ -308,7 +304,6 @@ function PopoverMenu({ interactive={item.interactive} isSelected={item.isSelected} badgeText={item.badgeText} - isPaneMenu={isPaneMenu} /> ))} diff --git a/src/components/ThreeDotsMenu/index.tsx b/src/components/ThreeDotsMenu/index.tsx index 6553833b63de..e44d57ab18e2 100644 --- a/src/components/ThreeDotsMenu/index.tsx +++ b/src/components/ThreeDotsMenu/index.tsx @@ -29,7 +29,6 @@ function ThreeDotsMenu({ shouldOverlay = false, shouldSetModalVisibility = true, disabled = false, - isPaneMenu = false, }: ThreeDotsMenuProps) { const [modal] = useOnyx(ONYXKEYS.MODAL); @@ -102,7 +101,6 @@ function ThreeDotsMenu({ shouldSetModalVisibility={shouldSetModalVisibility} anchorRef={buttonRef} shouldEnableNewFocusManagement - isPaneMenu={isPaneMenu} /> ); diff --git a/src/components/ThreeDotsMenu/types.ts b/src/components/ThreeDotsMenu/types.ts index 250a72b62135..86a10d08d449 100644 --- a/src/components/ThreeDotsMenu/types.ts +++ b/src/components/ThreeDotsMenu/types.ts @@ -38,9 +38,6 @@ type ThreeDotsMenuProps = { /** Should we announce the Modal visibility changes? */ shouldSetModalVisibility?: boolean; - - /** Is this in the Pane */ - isPaneMenu?: boolean; }; export default ThreeDotsMenuProps; diff --git a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx index 5824842a8bd9..fdb06828901e 100644 --- a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx +++ b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx @@ -29,7 +29,6 @@ function SavedSearchItemThreeDotMenu({menuItems}: SavedSearchItemThreeDotMenuPro horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }} - isPaneMenu /> ); diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchTypeMenu.tsx index 7c8af2388f52..c46954df869d 100644 --- a/src/pages/Search/SearchTypeMenu.tsx +++ b/src/pages/Search/SearchTypeMenu.tsx @@ -191,7 +191,6 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { iconWidth={variables.iconSizeNormal} iconHeight={variables.iconSizeNormal} shouldUseSingleExecution - isPaneMenu /> ), @@ -236,7 +235,6 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) { wrapperStyle={styles.sectionMenuItem} focused={index === activeItemIndex} onPress={onPress} - isPaneMenu /> ); })} diff --git a/src/pages/Search/SearchTypeMenuNarrow.tsx b/src/pages/Search/SearchTypeMenuNarrow.tsx index 8fb5c5b3b0c5..0158a15bfc41 100644 --- a/src/pages/Search/SearchTypeMenuNarrow.tsx +++ b/src/pages/Search/SearchTypeMenuNarrow.tsx @@ -118,7 +118,6 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, queryJSON, title, horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }} - isPaneMenu /> ), isSelected: currentSavedSearch?.hash === item.hash, diff --git a/src/pages/home/sidebar/AllSettingsScreen.tsx b/src/pages/home/sidebar/AllSettingsScreen.tsx index c94c7012e411..87fff1b48329 100644 --- a/src/pages/home/sidebar/AllSettingsScreen.tsx +++ b/src/pages/home/sidebar/AllSettingsScreen.tsx @@ -86,7 +86,6 @@ function AllSettingsScreen({policies}: AllSettingsScreenProps) { shouldShowRightIcon: item.shouldShowRightIcon, shouldBlockSelection: !!item.link, wrapperStyle: styles.sectionMenuItem, - isPaneMenu: true, focused: item.focused, brickRoadIndicator: item.brickRoadIndicator, })); diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 851f8ca5441b..f49b3a181d11 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -342,7 +342,6 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms !!item.routeName && !!(activeCentralPaneRoute.name.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')) } - isPaneMenu iconRight={item.iconRight} shouldShowRightIcon={item.shouldShowRightIcon} /> diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index fd7a45e31acb..0d656da69d73 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -402,7 +402,6 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyCategories wrapperStyle={styles.sectionMenuItem} highlighted={enabledItem?.routeName === item.routeName} focused={!!(item.routeName && activeRoute?.startsWith(item.routeName))} - isPaneMenu /> ))} From c5792f6d472df700e3b5482aaa8e7b24c31667d6 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 09:39:26 +0530 Subject: [PATCH 129/180] fix color on hover for "Add copilot" button --- src/pages/settings/Security/SecuritySettingsPage.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/settings/Security/SecuritySettingsPage.tsx b/src/pages/settings/Security/SecuritySettingsPage.tsx index 6403b0ac64e2..dc1b218418e1 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.tsx +++ b/src/pages/settings/Security/SecuritySettingsPage.tsx @@ -17,7 +17,6 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import {clearAddDelegateErrors} from '@libs/actions/Delegate'; @@ -36,7 +35,6 @@ function SecuritySettingsPage() { const {translate} = useLocalize(); const waitForNavigate = useWaitForNavigation(); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const theme = useTheme(); const {canUseNewDotCopilot} = usePermissions(); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const isActingAsDelegate = !!account?.delegatedAccess?.delegate ?? false; @@ -188,7 +186,6 @@ function SecuritySettingsPage() { Navigation.navigate(ROUTES.SETTINGS_ADD_DELEGATE)} shouldShowRightIcon wrapperStyle={[styles.sectionMenuItemTopDescription, styles.mb6]} From f1d48836ec69f1e910075fbf33d87b9d9816a9ae Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 09:40:29 +0530 Subject: [PATCH 130/180] fix color on hover for "request early cancellation" button --- .../CardSection/RequestEarlyCancellationMenuItem/index.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/settings/Subscription/CardSection/RequestEarlyCancellationMenuItem/index.tsx b/src/pages/settings/Subscription/CardSection/RequestEarlyCancellationMenuItem/index.tsx index 9fbb48e51605..2d1af54b5b20 100644 --- a/src/pages/settings/Subscription/CardSection/RequestEarlyCancellationMenuItem/index.tsx +++ b/src/pages/settings/Subscription/CardSection/RequestEarlyCancellationMenuItem/index.tsx @@ -2,21 +2,18 @@ import React from 'react'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import useLocalize from '@hooks/useLocalize'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import ROUTES from '@src/ROUTES'; function RequestEarlyCancellationMenuItem() { const {translate} = useLocalize(); - const theme = useTheme(); const styles = useThemeStyles(); return ( Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_REQUEST_EARLY_CANCELLATION)} From 978018866759bf1b08e69451129f8301cb34d1a5 Mon Sep 17 00:00:00 2001 From: Jason Li <30815269+jliexpensify@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:13:57 +1000 Subject: [PATCH 131/180] Update Configure-Quickbooks-Online.md Copied info from Connect doc to Configure. --- .../Configure-Quickbooks-Online.md | 73 ++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md b/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md index 787602337bd2..73e3340d41a2 100644 --- a/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md +++ b/docs/articles/new-expensify/connections/quickbooks-online/Configure-Quickbooks-Online.md @@ -1,9 +1,76 @@ --- title: Configure Quickbooks Online -description: Coming Soon +description: Configure your QuickBooks Online connection with Expensify --- -# FAQ +Once you've set up your QuickBooks Online connection, you'll be able to configure your import and export settings. + +# Step 1: Configure import settings + +The following steps help you determine how data will be imported from QuickBooks Online to Expensify. + +
    +
  1. Under the Accounting settings for your workspace, click Import under the QuickBooks Online connection.
  2. +
  3. Review each of the following import settings:
  4. +
      +
    • Chart of accounts: The chart of accounts are automatically imported from QuickBooks Online as categories. This cannot be amended.
    • +
    • Classes: Choose whether to import classes, which will be shown in Expensify as tags for expense-level coding.
    • +
    • Customers/projects: Choose whether to import customers/projects, which will be shown in Expensify as tags for expense-level coding.
    • +
    • Locations: Choose whether to import locations, which will be shown in Expensify as tags for expense-level coding.
    • +{% include info.html %} +As Locations are only configurable as tags, you cannot export expense reports as vendor bills or checks to QuickBooks Online. To unlock these export options, either disable locations import or upgrade to the Control Plan to export locations encoded as a report field. +{% include end-info.html %} +
    • Taxes: Choose whether to import tax rates and defaults.
    • +
    +
+ +# Step 2: Configure export settings + +The following steps help you determine how data will be exported from Expensify to QuickBooks Online. + +
    +
  1. Under the Accounting settings for your workspace, click Export under the QuickBooks Online connection.
  2. +
  3. Review each of the following export settings:
  4. +
      +
    • Preferred Exporter: Choose whether to assign a Workspace Admin as the Preferred Exporter. Once selected, the Preferred Exporter automatically receives reports for export in their account to help automate the exporting process.
    • + +{% include info.html %} +* Other Workspace Admins will still be able to export to QuickBooks Online. +* If you set different export accounts for individual company cards under your domain settings, then your Preferred Exporter must be a Domain Admin. +{% include end-info.html %} + +
    • Date: Choose whether to use the date of last expense, export date, or submitted date.
    • +
    • Export Out-of-Pocket Expenses as: Select whether out-of-pocket expenses will be exported as a check, journal entry, or vendor bill.
    • + +{% include info.html %} +These settings may vary based on whether tax is enabled for your workspace. +* If tax is not enabled on the workspace, you’ll also select the Accounts Payable/AP. +* If tax is enabled on the workspace, journal entry will not be available as an option. If you select the journal entries option first and later enable tax on the workspace, you will see a red dot and an error message under the “Export Out-of-Pocket Expenses as” options. To resolve this error, you must change your export option to vendor bill or check to successfully code and export expense reports. +{% include end-info.html %} + +
    • Invoices: Select the QuickBooks Online invoice account that invoices will be exported to.
    • +
    • Export as: Select whether company cards export to QuickBooks Online as a credit card (the default), debit card, or vendor bill. Then select the account they will export to.
    • +
    • If you select vendor bill, you’ll also select the accounts payable account that vendor bills will be created from, as well as whether to set a default vendor for credit card transactions upon export. If this option is enabled, you will select the vendor that all credit card transactions will be applied to.
    • +
    +
+ +# Step 3: Configure advanced settings + +The following steps help you determine the advanced settings for your connection, like auto-sync and employee invitation settings. + +
    +
  1. Under the Accounting settings for your workspace, click Advanced under the QuickBooks Online connection.
  2. +
  3. Select an option for each of the following settings:
  4. +
      +
    • Auto-sync: Choose whether to enable QuickBooks Online to automatically communicate changes with Expensify to ensure that the data shared between the two systems is up-to-date. New report approvals/reimbursements will be synced during the next auto-sync period.
    • +
    • Invite Employees: Choose whether to enable Expensify to import employee records from QuickBooks Online and invite them to this workspace.
    • +
    • Automatically Create Entities: Choose whether to enable Expensify to automatically create vendors and customers in QuickBooks Online if a matching vendor or customer does not exist.
    • +
    • Sync Reimbursed Reports: Choose whether to enable report syncing for reimbursed expenses. If enabled, all reports that are marked as Paid in QuickBooks Online will also show in Expensify as Paid. If enabled, you must also select the QuickBooks Online account that reimbursements are coming out of, and Expensify will automatically create the payment in QuickBooks Online.
    • +
    • Invoice Collection Account: Select the invoice collection account that you want invoices to appear under once the invoice is marked as paid.
    • +
    +
+ +{% include faq-begin.md %} ## How do I know if a report is successfully exported to QuickBooks Online? @@ -22,3 +89,5 @@ When an admin manually exports a report, Expensify will notify them if the repor - If a report has been exported and marked as paid in QuickBooks Online, it will be automatically marked as reimbursed in Expensify during the next sync. Reports that have yet to be exported to QuickBooks Online won’t be automatically exported. + +{% include faq-end.md %} From 240640a3390c1063c4ad7e7b70ae38f9146a3b09 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 09:45:11 +0530 Subject: [PATCH 132/180] fix color on hover for workspace > workflow > select a non default workflow > delete --- .../workspace/workflows/approvals/ApprovalWorkflowEditor.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/workspace/workflows/approvals/ApprovalWorkflowEditor.tsx b/src/pages/workspace/workflows/approvals/ApprovalWorkflowEditor.tsx index 6325ce1da21d..2b7abca0f3b3 100644 --- a/src/pages/workspace/workflows/approvals/ApprovalWorkflowEditor.tsx +++ b/src/pages/workspace/workflows/approvals/ApprovalWorkflowEditor.tsx @@ -10,7 +10,6 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; @@ -36,7 +35,6 @@ type ApprovalWorkflowEditorProps = { function ApprovalWorkflowEditor({approvalWorkflow, removeApprovalWorkflow, policy, policyID}: ApprovalWorkflowEditorProps, ref: ForwardedRef) { const styles = useThemeStyles(); - const theme = useTheme(); const {translate, toLocaleOrdinal} = useLocalize(); const approverCount = approvalWorkflow.approvers.length; @@ -168,7 +166,6 @@ function ApprovalWorkflowEditor({approvalWorkflow, removeApprovalWorkflow, polic From 8ae86d73b74c9f3d0d881eb9e68c7437b5227c2f Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 09:46:19 +0530 Subject: [PATCH 133/180] fix color on hover for workspace > expensify card > select any card > deactivate card --- .../expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx index ad2b71e8e187..03b489d93402 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx @@ -18,7 +18,6 @@ import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; import * as CurrencyUtils from '@libs/CurrencyUtils'; @@ -46,7 +45,6 @@ function WorkspaceExpensifyCardDetailsPage({route}: WorkspaceExpensifyCardDetail // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to use the correct modal type const {isSmallScreenWidth} = useResponsiveLayout(); const styles = useThemeStyles(); - const theme = useTheme(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`); @@ -153,7 +151,6 @@ function WorkspaceExpensifyCardDetailsPage({route}: WorkspaceExpensifyCardDetail (isOffline ? setIsOfflineModalVisible(true) : setIsDeactivateModalVisible(true))} From 1572322c5db1dde6e599aef8909e96133c3fcfa2 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 09:47:41 +0530 Subject: [PATCH 134/180] fix color on hover for workspace > workflow > add approval workflow --- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index cc80705534b2..cf1fdf30c7a4 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -181,7 +181,6 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { icon={Expensicons.Plus} iconHeight={20} iconWidth={20} - iconFill={theme.success} style={[styles.sectionMenuItemTopDescription, styles.mt6, styles.mbn3]} onPress={addApprovalAction} /> @@ -282,7 +281,6 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { preferredLocale, onPressAutoReportingFrequency, approvalWorkflows, - theme.success, theme.spinner, addApprovalAction, isOffline, From 2841d26220ae5799c4b4c1d638006dd7790674b3 Mon Sep 17 00:00:00 2001 From: daledah Date: Thu, 26 Sep 2024 14:44:45 +0700 Subject: [PATCH 135/180] fix: fixed 4 decimals for distance rates --- src/components/AmountForm.tsx | 6 +++++- src/libs/CurrencyUtils.ts | 2 +- src/libs/PolicyDistanceRatesUtils.ts | 2 +- .../workspace/distanceRates/CreateDistanceRatePage.tsx | 2 +- .../workspace/distanceRates/PolicyDistanceRateEditPage.tsx | 2 +- .../PolicyDistanceRateTaxReclaimableEditPage.tsx | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/AmountForm.tsx b/src/components/AmountForm.tsx index 564077387d5b..4848577bdea0 100644 --- a/src/components/AmountForm.tsx +++ b/src/components/AmountForm.tsx @@ -48,6 +48,9 @@ type AmountFormProps = { /** Whether the form should use a standard TextInput as a base */ displayAsTextInput?: boolean; + + /** Number of decimals to display */ + fixedDecimals?: number; } & Pick & Pick; @@ -75,6 +78,7 @@ function AmountForm( displayAsTextInput = false, isCurrencyPressable = true, label, + fixedDecimals, ...rest }: AmountFormProps, forwardedRef: ForwardedRef, @@ -84,7 +88,7 @@ function AmountForm( const textInput = useRef(null); - const decimals = CurrencyUtils.getCurrencyDecimals(currency) + extraDecimals; + const decimals = fixedDecimals ?? CurrencyUtils.getCurrencyDecimals(currency) + extraDecimals; const currentAmount = useMemo(() => (typeof amount === 'string' ? amount : ''), [amount]); const [shouldUpdateSelection, setShouldUpdateSelection] = useState(true); diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index c3b80797d750..9e54efa7a03d 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -164,7 +164,7 @@ function convertAmountToDisplayString(amount = 0, currency: string = CONST.CURRE return NumberFormatUtils.format(BaseLocaleListener.getPreferredLocale(), convertedAmount, { style: 'currency', currency, - minimumFractionDigits: getCurrencyDecimals(currency) + 1, + minimumFractionDigits: 4, }); } diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index 0c5493f2f97b..a9c681c9ee2b 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -18,7 +18,7 @@ function validateRateValue(values: FormOnyxValues, currency: stri const decimalSeparator = toLocaleDigit('.'); // Allow one more decimal place for accuracy - const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CurrencyUtils.getCurrencyDecimals(currency) + 1}})?$`, 'i'); + const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,4})?$`, 'i'); if (!rateValueRegex.test(parsedRate) || parsedRate === '') { errors.rate = Localize.translateLocal('common.error.invalidRateError'); } else if (NumberUtils.parseFloatAnyLocale(parsedRate) <= 0) { diff --git a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx index b2dca63ecd34..d68c7687ae48 100644 --- a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx +++ b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx @@ -90,7 +90,7 @@ function CreateDistanceRatePage({policy, route}: CreateDistanceRatePageProps) { Date: Thu, 26 Sep 2024 15:07:41 +0700 Subject: [PATCH 136/180] fix: lint --- src/libs/PolicyDistanceRatesUtils.ts | 1 - .../distanceRates/CreateDistanceRatePage.tsx | 19 ++++----------- .../PolicyDistanceRateEditPage.tsx | 23 ++++++------------- ...licyDistanceRateTaxReclaimableEditPage.tsx | 5 +--- 4 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index a9c681c9ee2b..415fcc627b4e 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -2,7 +2,6 @@ import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import CONST from '@src/CONST'; import type ONYXKEYS from '@src/ONYXKEYS'; import type {Rate} from '@src/types/onyx/Policy'; -import * as CurrencyUtils from './CurrencyUtils'; import getPermittedDecimalSeparator from './getPermittedDecimalSeparator'; import * as Localize from './Localize'; import * as MoneyRequestUtils from './MoneyRequestUtils'; diff --git a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx index d68c7687ae48..353bf42afe29 100644 --- a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx +++ b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx @@ -1,8 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; import AmountForm from '@components/AmountForm'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import FormProvider from '@components/Form/FormProvider'; @@ -12,6 +10,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; +import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import {getOptimisticRateName, validateRateValue} from '@libs/PolicyDistanceRatesUtils'; import Navigation from '@navigation/Navigation'; @@ -24,18 +23,14 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/PolicyCreateDistanceRateForm'; import type {Rate} from '@src/types/onyx/Policy'; -import type Policy from '@src/types/onyx/Policy'; -type CreateDistanceRatePageOnyxProps = { - policy: OnyxEntry; -}; +type CreateDistanceRatePageProps = StackScreenProps; -type CreateDistanceRatePageProps = CreateDistanceRatePageOnyxProps & StackScreenProps; - -function CreateDistanceRatePage({policy, route}: CreateDistanceRatePageProps) { +function CreateDistanceRatePage({route}: CreateDistanceRatePageProps) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const policyID = route.params.policyID; + const policy = usePolicy(policyID); const currency = policy?.outputCurrency ?? CONST.CURRENCY.USD; const customUnits = policy?.customUnits ?? {}; const customUnitID = customUnits[Object.keys(customUnits)[0]]?.customUnitID ?? ''; @@ -104,8 +99,4 @@ function CreateDistanceRatePage({policy, route}: CreateDistanceRatePageProps) { CreateDistanceRatePage.displayName = 'CreateDistanceRatePage'; -export default withOnyx({ - policy: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID}`, - }, -})(CreateDistanceRatePage); +export default CreateDistanceRatePage; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx index d782e6507f3f..059c11298360 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx @@ -1,8 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback} from 'react'; import {Keyboard} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; import AmountForm from '@components/AmountForm'; import FormProvider from '@components/Form/FormProvider'; import InputWrapperWithRef from '@components/Form/InputWrapper'; @@ -11,6 +9,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; +import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import {validateRateValue} from '@libs/PolicyDistanceRatesUtils'; @@ -22,27 +21,23 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/PolicyDistanceRateEditForm'; -import type * as OnyxTypes from '@src/types/onyx'; -type PolicyDistanceRateEditPageOnyxProps = { - /** Policy details */ - policy: OnyxEntry; -}; +type PolicyDistanceRateEditPageProps = StackScreenProps; -type PolicyDistanceRateEditPageProps = PolicyDistanceRateEditPageOnyxProps & StackScreenProps; - -function PolicyDistanceRateEditPage({policy, route}: PolicyDistanceRateEditPageProps) { +function PolicyDistanceRateEditPage({route}: PolicyDistanceRateEditPageProps) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); const policyID = route.params.policyID; const rateID = route.params.rateID; + + const policy = usePolicy(policyID); const customUnits = policy?.customUnits ?? {}; const customUnit = customUnits[Object.keys(customUnits)[0]]; const rate = customUnit?.rates[rateID]; const currency = rate?.currency ?? CONST.CURRENCY.USD; - const currentRateValue = (parseFloat((rate?.rate ?? 0).toString()) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET).toFixed(3); + const currentRateValue = (parseFloat((rate?.rate ?? 0).toString()) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET).toFixed(4); const submitRate = (values: FormOnyxValues) => { if (currentRateValue === values.rate) { @@ -108,8 +103,4 @@ function PolicyDistanceRateEditPage({policy, route}: PolicyDistanceRateEditPageP PolicyDistanceRateEditPage.displayName = 'PolicyDistanceRateEditPage'; -export default withOnyx({ - policy: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID}`, - }, -})(PolicyDistanceRateEditPage); +export default PolicyDistanceRateEditPage; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateTaxReclaimableEditPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateTaxReclaimableEditPage.tsx index 156954816c7f..0fd6baa89ef3 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateTaxReclaimableEditPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateTaxReclaimableEditPage.tsx @@ -9,7 +9,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import {validateTaxClaimableValue} from '@libs/PolicyDistanceRatesUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -35,9 +34,7 @@ function PolicyDistanceRateTaxReclaimableEditPage({route, policy}: PolicyDistanc const customUnit = customUnits[Object.keys(customUnits)[0]]; const rate = customUnit.rates[rateID]; const currency = rate.currency ?? CONST.CURRENCY.USD; - const extraDecimals = 1; - const decimals = CurrencyUtils.getCurrencyDecimals(currency) + extraDecimals; - const currentTaxReclaimableOnValue = rate.attributes?.taxClaimablePercentage && rate.rate ? ((rate.attributes.taxClaimablePercentage * rate.rate) / 100).toFixed(decimals) : ''; + const currentTaxReclaimableOnValue = rate.attributes?.taxClaimablePercentage && rate.rate ? ((rate.attributes.taxClaimablePercentage * rate.rate) / 100).toFixed(4) : ''; const submitTaxReclaimableOn = (values: FormOnyxValues) => { if (values.taxClaimableValue === currentTaxReclaimableOnValue) { From 4ed4b90da00a2c34375ab972b2f45083e41700c6 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Thu, 26 Sep 2024 11:52:43 +0200 Subject: [PATCH 137/180] fix types --- src/components/Form/FormProvider.tsx | 56 +++++++++++----------------- src/stories/Form.stories.tsx | 20 ++++++++-- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index 7173cd653458..80f52c8053da 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -3,7 +3,6 @@ import lodashIsEqual from 'lodash/isEqual'; import type {ForwardedRef, MutableRefObject, ReactNode, RefAttributes} from 'react'; import React, {createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputSubmitEditingEventData, ViewStyle} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import * as ValidationUtils from '@libs/ValidationUtils'; @@ -13,7 +12,6 @@ import CONST from '@src/CONST'; import type {OnyxFormDraftKey, OnyxFormKey} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Form} from '@src/types/form'; -import type {Network} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {RegisterInput} from './FormContext'; import FormContext from './FormContext'; @@ -41,46 +39,34 @@ function getInitialValueByType(valueType?: ValueTypeKey): InitialDefaultValue { } } -type FormProviderOnyxProps = { - /** Contains the form state that must be accessed outside the component */ - formState: OnyxEntry
; +type FormProviderProps = FormProps & { + /** Children to render. */ + children: ((props: {inputValues: FormOnyxValues}) => ReactNode) | ReactNode; - /** Contains draft values for each input in the form */ - draftValues: OnyxEntry; + /** Callback to validate the form */ + validate?: (values: FormOnyxValues) => FormInputErrors; - /** Information about the network */ - network: OnyxEntry; -}; - -type FormProviderProps = FormProviderOnyxProps & - FormProps & { - /** Children to render. */ - children: ((props: {inputValues: FormOnyxValues}) => ReactNode) | ReactNode; - - /** Callback to validate the form */ - validate?: (values: FormOnyxValues) => FormInputErrors; + /** Should validate function be called when input loose focus */ + shouldValidateOnBlur?: boolean; - /** Should validate function be called when input loose focus */ - shouldValidateOnBlur?: boolean; + /** Should validate function be called when the value of the input is changed */ + shouldValidateOnChange?: boolean; - /** Should validate function be called when the value of the input is changed */ - shouldValidateOnChange?: boolean; + /** Whether to remove invisible characters from strings before validation and submission */ + shouldTrimValues?: boolean; - /** Whether to remove invisible characters from strings before validation and submission */ - shouldTrimValues?: boolean; + /** Styles that will be applied to the submit button only */ + submitButtonStyles?: StyleProp; - /** Styles that will be applied to the submit button only */ - submitButtonStyles?: StyleProp; + /** Whether to apply flex to the submit button */ + submitFlexEnabled?: boolean; - /** Whether to apply flex to the submit button */ - submitFlexEnabled?: boolean; + /** Whether button is disabled */ + isSubmitDisabled?: boolean; - /** Whether button is disabled */ - isSubmitDisabled?: boolean; - - /** Whether HTML is allowed in form inputs */ - allowHTML?: boolean; - }; + /** Whether HTML is allowed in form inputs */ + allowHTML?: boolean; +}; function FormProvider( { @@ -404,6 +390,6 @@ function FormProvider( FormProvider.displayName = 'Form'; -export default forwardRef(FormProvider) as (props: Omit & RefAttributes, keyof FormProviderOnyxProps>) => ReactNode; +export default forwardRef(FormProvider) as (props: FormProviderProps & RefAttributes) => ReactNode; export type {FormProviderProps}; diff --git a/src/stories/Form.stories.tsx b/src/stories/Form.stories.tsx index ab29612b0556..c15fcb982239 100644 --- a/src/stories/Form.stories.tsx +++ b/src/stories/Form.stories.tsx @@ -2,6 +2,7 @@ import type {Meta, StoryFn} from '@storybook/react'; import React, {useState} from 'react'; import type {ComponentType} from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import AddressSearch from '@components/AddressSearch'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; import DatePicker from '@components/DatePicker'; @@ -18,8 +19,10 @@ import * as FormActions from '@userActions/FormActions'; import CONST from '@src/CONST'; import type {OnyxFormValuesMapping} from '@src/ONYXKEYS'; import {defaultStyles} from '@src/styles'; +import type {Form} from '@src/types/form'; +import type {Network} from '@src/types/onyx'; -type FormStory = StoryFn; +type FormStory = StoryFn; type StorybookFormValues = { routingNumber?: string; @@ -32,6 +35,17 @@ type StorybookFormValues = { checkbox?: boolean; }; +type FormProviderOnyxProps = { + /** Contains the form state that must be accessed outside the component */ + formState: OnyxEntry; + + /** Contains draft values for each input in the form */ + draftValues: OnyxEntry; + + /** Information about the network */ + network: OnyxEntry; +}; + type StorybookFormErrors = Partial>; const STORYBOOK_FORM_ID = 'TestForm' as keyof OnyxFormValuesMapping; @@ -50,7 +64,7 @@ const story: Meta = { }, }; -function Template(props: FormProviderProps) { +function Template(props: FormProviderProps & FormProviderOnyxProps) { // Form consumes data from Onyx, so we initialize Onyx with the necessary data here NetworkConnection.setOfflineStatus(false); FormActions.setIsLoading(props.formID, !!props.formState?.isLoading); @@ -162,7 +176,7 @@ function Template(props: FormProviderProps) { /** * Story to exhibit the native event handlers for TextInput in the Form Component */ -function WithNativeEventHandler(props: FormProviderProps) { +function WithNativeEventHandler(props: FormProviderProps & FormProviderOnyxProps) { const [log, setLog] = useState(''); // Form consumes data from Onyx, so we initialize Onyx with the necessary data here From 455b67cd3069b5c339c7369d926d195defc357c8 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 16:54:33 +0530 Subject: [PATCH 138/180] fix color on hover for table icon (workspace settings > three dot menu > import spreadsheet) --- assets/images/table.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/images/table.svg b/assets/images/table.svg index a9cfe68f339e..36d4ced774f1 100644 --- a/assets/images/table.svg +++ b/assets/images/table.svg @@ -1,3 +1,3 @@ - + From d167bdf1e85695fac675c763cc4de732f8080cb5 Mon Sep 17 00:00:00 2001 From: Jay Damani Date: Thu, 26 Sep 2024 19:06:57 +0530 Subject: [PATCH 139/180] remove withOnyx usage --- src/pages/home/sidebar/AllSettingsScreen.tsx | 18 ++---- src/pages/settings/InitialSettingsPage.tsx | 58 ++++---------------- 2 files changed, 15 insertions(+), 61 deletions(-) diff --git a/src/pages/home/sidebar/AllSettingsScreen.tsx b/src/pages/home/sidebar/AllSettingsScreen.tsx index 87fff1b48329..0025e6b429c5 100644 --- a/src/pages/home/sidebar/AllSettingsScreen.tsx +++ b/src/pages/home/sidebar/AllSettingsScreen.tsx @@ -1,6 +1,5 @@ import React, {useMemo} from 'react'; -import type {OnyxCollection} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Breadcrumbs from '@components/Breadcrumbs'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemList from '@components/MenuItemList'; @@ -17,15 +16,10 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy} from '@src/types/onyx'; -type AllSettingsScreenOnyxProps = { - policies: OnyxCollection; -}; +function AllSettingsScreen() { + const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); -type AllSettingsScreenProps = AllSettingsScreenOnyxProps; - -function AllSettingsScreen({policies}: AllSettingsScreenProps) { const styles = useThemeStyles(); const waitForNavigate = useWaitForNavigation(); const {translate} = useLocalize(); @@ -121,8 +115,4 @@ function AllSettingsScreen({policies}: AllSettingsScreenProps) { AllSettingsScreen.displayName = 'AllSettingsScreen'; -export default withOnyx({ - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, -})(AllSettingsScreen); +export default AllSettingsScreen; diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index f49b3a181d11..8dce84f8b470 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -3,8 +3,7 @@ import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, use // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native'; import {NativeModules, View} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AccountSwitcher from '@components/AccountSwitcher'; import AccountSwitcherSkeletonView from '@components/AccountSwitcherSkeletonView'; @@ -45,32 +44,11 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; -import type * as OnyxTypes from '@src/types/onyx'; import type {Icon as TIcon} from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; -type InitialSettingsPageOnyxProps = { - /** The user's wallet account */ - userWallet: OnyxEntry; - - /** List of bank accounts */ - bankAccountList: OnyxEntry; - - /** List of user's cards */ - fundList: OnyxEntry; - - /** Information about the user accepting the terms for payments */ - walletTerms: OnyxEntry; - - /** Login list for the user that is signed in */ - loginList: OnyxEntry; - - /** The policies which the user has access to */ - policies: OnyxCollection; -}; - -type InitialSettingsPageProps = InitialSettingsPageOnyxProps & WithCurrentUserPersonalDetailsProps; +type InitialSettingsPageProps = WithCurrentUserPersonalDetailsProps; type MenuData = { translationKey: TranslationPaths; @@ -94,7 +72,14 @@ type MenuData = { type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; -function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails, policies}: InitialSettingsPageProps) { +function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPageProps) { + const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); + const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); + const [fundList] = useOnyx(ONYXKEYS.FUND_LIST); + const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); + const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); + const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const network = useNetwork(); const theme = useTheme(); const styles = useThemeStyles(); @@ -449,25 +434,4 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms InitialSettingsPage.displayName = 'InitialSettingsPage'; -export default withCurrentUserPersonalDetails( - withOnyx({ - userWallet: { - key: ONYXKEYS.USER_WALLET, - }, - bankAccountList: { - key: ONYXKEYS.BANK_ACCOUNT_LIST, - }, - fundList: { - key: ONYXKEYS.FUND_LIST, - }, - walletTerms: { - key: ONYXKEYS.WALLET_TERMS, - }, - loginList: { - key: ONYXKEYS.LOGIN_LIST, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - })(InitialSettingsPage), -); +export default withCurrentUserPersonalDetails(InitialSettingsPage); From 4c72ebbe8c4fec39d3e5a8b48dd0c2dd8dd78fa4 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 26 Sep 2024 15:25:58 -0400 Subject: [PATCH 140/180] enable group.com.expensify.new app group for NSE --- ios/NewExpensify.xcodeproj/project.pbxproj | 15 ++++++++++++--- .../NotificationServiceExtension.entitlements | 10 ++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 ios/NotificationServiceExtension/NotificationServiceExtension.entitlements diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index 768062717d4b..ed704f5a2449 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -43,7 +43,7 @@ D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */; }; DD79042B2792E76D004484B4 /* RCTBootSplash.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.mm */; }; DDCB2E57F334C143AC462B43 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */; }; - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */ = {isa = PBXBuildFile; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; }; F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; }; @@ -131,6 +131,7 @@ 7F3784A52C7512CF00063508 /* NewExpensifyReleaseDevelopment.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NewExpensifyReleaseDevelopment.entitlements; path = NewExpensify/NewExpensifyReleaseDevelopment.entitlements; sourceTree = ""; }; 7F3784A62C7512D900063508 /* NewExpensifyReleaseAdHoc.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NewExpensifyReleaseAdHoc.entitlements; path = NewExpensify/NewExpensifyReleaseAdHoc.entitlements; sourceTree = ""; }; 7F3784A72C75131000063508 /* NewExpensifyReleaseProduction.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NewExpensifyReleaseProduction.entitlements; path = NewExpensify/NewExpensifyReleaseProduction.entitlements; sourceTree = ""; }; + 7F9C91352CA5EC4900FC4DC1 /* NotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationServiceExtension.entitlements; sourceTree = ""; }; 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpError.swift; sourceTree = ""; }; 7FD73C9B2B23CE9500420AF3 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; @@ -175,8 +176,8 @@ buildActionMask = 2147483647; files = ( 383643682B6D4AE2005BB9AE /* DeviceCheck.framework in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -266,6 +267,7 @@ 7FD73C9C2B23CE9500420AF3 /* NotificationServiceExtension */ = { isa = PBXGroup; children = ( + 7F9C91352CA5EC4900FC4DC1 /* NotificationServiceExtension.entitlements */, 7FD73C9D2B23CE9500420AF3 /* NotificationService.swift */, 7FD73C9F2B23CE9500420AF3 /* Info.plist */, 7F9DD8D92B2A445B005E3AFA /* ExpError.swift */, @@ -1183,6 +1185,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -1347,6 +1350,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -1433,6 +1437,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; @@ -1518,6 +1523,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -1604,6 +1610,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -1683,6 +1690,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; @@ -1761,6 +1769,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; diff --git a/ios/NotificationServiceExtension/NotificationServiceExtension.entitlements b/ios/NotificationServiceExtension/NotificationServiceExtension.entitlements new file mode 100644 index 000000000000..f52d3207d6e3 --- /dev/null +++ b/ios/NotificationServiceExtension/NotificationServiceExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.expensify.new + + + From 48081e58255c69e0636aeb7f5babcd8f89cd0a6f Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 26 Sep 2024 15:38:19 -0400 Subject: [PATCH 141/180] fix DebugProduction NSE provisioning profile --- ios/NewExpensify.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index ed704f5a2449..1a29a275b956 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -1525,7 +1525,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1566,7 +1566,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.chat.expensify.chat.NotificationServiceExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) Development: Notification Service"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "(NewApp) AppStore: Notification Service"; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; From 6f58877aacf7702f1a7b5b7e8bb1948fc3672966 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Thu, 26 Sep 2024 10:13:10 -1000 Subject: [PATCH 142/180] Fix expected missing field in ExpenseReportRulesSection --- .../workspace/rules/ExpenseReportRulesSection.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/pages/workspace/rules/ExpenseReportRulesSection.tsx b/src/pages/workspace/rules/ExpenseReportRulesSection.tsx index 9f2f3d7a80e3..88b5b71cecfd 100644 --- a/src/pages/workspace/rules/ExpenseReportRulesSection.tsx +++ b/src/pages/workspace/rules/ExpenseReportRulesSection.tsx @@ -48,6 +48,7 @@ function ExpenseReportRulesSection({policyID}: ExpenseReportRulesSectionProps) { ); }; + const reportTitlePendingFields = policy?.fieldList?.[CONST.POLICY.FIELDS.FIELD_LIST_TITLE]?.pendingFields ?? {}; const optionItems = [ { title: translate('workspace.rules.expenseReportRules.customReportNamesTitle'), @@ -62,11 +63,7 @@ function ExpenseReportRulesSection({policyID}: ExpenseReportRulesSectionProps) { onToggle: (isEnabled: boolean) => PolicyActions.enablePolicyDefaultReportTitle(policyID, isEnabled), subMenuItems: [ , Date: Thu, 26 Sep 2024 14:01:31 -0700 Subject: [PATCH 143/180] Fix exporter payload --- src/libs/actions/connections/QuickbooksOnline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/connections/QuickbooksOnline.ts b/src/libs/actions/connections/QuickbooksOnline.ts index c62c97aa88ca..bb85c8f5223f 100644 --- a/src/libs/actions/connections/QuickbooksOnline.ts +++ b/src/libs/actions/connections/QuickbooksOnline.ts @@ -384,7 +384,7 @@ function updateQuickbooksOnlinePreferredExporter Date: Thu, 26 Sep 2024 15:35:17 -0700 Subject: [PATCH 144/180] Fix types --- src/languages/en.ts | 3 ++- src/languages/es.ts | 3 ++- src/languages/params.ts | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index dabb592b2bf5..6a90b1f8302a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -54,6 +54,7 @@ import type { DefaultVendorDescriptionParams, DelegateRoleParams, DelegateSubmitParams, + DelegatorParams, DeleteActionParams, DeleteConfirmationParams, DeleteExpenseTranslationParams, @@ -4818,7 +4819,7 @@ const translations = { } }, genericError: 'Oops, something went wrong. Please try again.', - onBehalfOfMessage: (delegator: string) => `on behalf of ${delegator}`, + onBehalfOfMessage: ({delegator}: DelegatorParams) => `on behalf of ${delegator}`, accessLevel: 'Access level', confirmCopilot: 'Confirm your copilot below.', accessLevelDescription: 'Choose an access level below. Both Full and Limited access allow copilots to view all conversations and expenses.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 3dc528fe8257..8d8b33ca57dd 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -53,6 +53,7 @@ import type { DefaultVendorDescriptionParams, DelegateRoleParams, DelegateSubmitParams, + DelegatorParams, DeleteActionParams, DeleteConfirmationParams, DeleteExpenseTranslationParams, @@ -5338,7 +5339,7 @@ const translations = { } }, genericError: '¡Ups! Ha ocurrido un error. Por favor, inténtalo de nuevo.', - onBehalfOfMessage: (delegator: string) => `en nombre de ${delegator}`, + onBehalfOfMessage: ({delegator}: DelegatorParams) => `en nombre de ${delegator}`, accessLevel: 'Nivel de acceso', confirmCopilot: 'Confirma tu copiloto a continuación.', accessLevelDescription: 'Elige un nivel de acceso a continuación. Tanto el acceso Completo como el Limitado permiten a los copilotos ver todas las conversaciones y gastos.', diff --git a/src/languages/params.ts b/src/languages/params.ts index 20e7ac34185c..8ed122283064 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -469,6 +469,8 @@ type ReconciliationWorksParams = {lastFourPAN: string}; type DelegateRoleParams = {role: DelegateRole}; +type DelegatorParams = {delegator: string}; + type RoleNamesParams = {role: string}; type AssignCardParams = { @@ -565,6 +567,7 @@ export type { AssignedYouCardParams, SpreadCategoriesParams, DelegateRoleParams, + DelegatorParams, ReconciliationWorksParams, LastSyncAccountingParams, SyncStageNameConnectionsParams, From a5ff556d655d835f0cf82405928ed943b9a0bd55 Mon Sep 17 00:00:00 2001 From: rory Date: Thu, 26 Sep 2024 16:03:56 -0700 Subject: [PATCH 145/180] Fix onBehalfOfMessage usage --- src/pages/home/report/ReportActionItemSingle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 01a0847b4f77..56eaf814ff10 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -375,7 +375,7 @@ function ReportActionItemSingle({ ) : null} {action?.delegateAccountID && !isReportPreviewAction && ( - {translate('delegate.onBehalfOfMessage', accountOwnerDetails?.displayName ?? '')} + {translate('delegate.onBehalfOfMessage', {delegator: accountOwnerDetails?.displayName ?? ''})} )} {children} From 50b8441a45cfb79d88273437e402f32d45d201cb Mon Sep 17 00:00:00 2001 From: OSBotify Date: Fri, 27 Sep 2024 00:43:23 +0000 Subject: [PATCH 146/180] Update version to 9.0.40-4 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a1571e20bb38..9153966e1d5d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009004003 - versionName "9.0.40-3" + versionCode 1009004004 + versionName "9.0.40-4" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 5da99e0989d5..af696a13c998 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.40.3 + 9.0.40.4 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 721533fa460d..0795209286ed 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.40.3 + 9.0.40.4 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 3bfbc686b64a..a545bd82c164 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.40 CFBundleVersion - 9.0.40.3 + 9.0.40.4 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 00c8322e6f0b..d43c8fee25c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.40-3", + "version": "9.0.40-4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.40-3", + "version": "9.0.40-4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a36259ae815c..e8322960c61c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.40-3", + "version": "9.0.40-4", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 8090f14eb2c76aef7baf33216266066d6c483148 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:52:30 -0600 Subject: [PATCH 147/180] Update Send-an-invoice.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ){:width=”100%”} was showing for each image in the preview - not what we want. So I made a change --- .../expenses-&-payments/Send-an-invoice.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md index 4d7f4e0dc55f..57b81a031a01 100644 --- a/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md +++ b/docs/articles/new-expensify/expenses-&-payments/Send-an-invoice.md @@ -57,17 +57,17 @@ Only workspace admins can send invoices. Invoices can be sent directly from Expe {% include end-selector.html %} -![Go to Account Settings click Workspace]({{site.url}}/assets/images/invoices_01.png)){:width="100%"} +![Go to Account Settings click Workspace]({{site.url}}/assets/images/invoices_01.png){:width="100%"} -![Click More Features for the workspace and enable Invoices]({{site.url}}/assets/images/invoices_02.png)){:width="100%"} +![Click More Features for the workspace and enable Invoices]({{site.url}}/assets/images/invoices_02.png){:width="100%"} -![Click the green button Send Invoice]({{site.url}}/assets/images/invoices_03.png)){:width="100%"} +![Click the green button Send Invoice]({{site.url}}/assets/images/invoices_03.png){:width="100%"} -![Enter Invoice amount]({{site.url}}/assets/images/invoices_04.png)){:width="100%"} +![Enter Invoice amount]({{site.url}}/assets/images/invoices_04.png){:width="100%"} -![Choose a recipient]({{site.url}}/assets/images/invoices_05.png)){:width="100%"} +![Choose a recipient]({{site.url}}/assets/images/invoices_05.png){:width="100%"} -![Add Invoice details and Send Invoice]({{site.url}}/assets/images/invoices_06.png)){:width="100%"} +![Add Invoice details and Send Invoice]({{site.url}}/assets/images/invoices_06.png){:width="100%"} # Receive invoice payment From edd29e7623527e7af1dbbede657ce7deb76f6ef5 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:55:33 -0600 Subject: [PATCH 148/180] Update Pay-an-invoice.md There was another image that was better for this so I replaced the others with it. --- .../new-expensify/expenses-&-payments/Pay-an-invoice.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md b/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md index d7ded144415b..615fac731c41 100644 --- a/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md +++ b/docs/articles/new-expensify/expenses-&-payments/Pay-an-invoice.md @@ -32,9 +32,7 @@ To pay an invoice, You can also view all unpaid invoices by searching for the sender’s email or phone number on the left-hand side of the app. The invoices waiting for your payment will have a green dot. -![Click Pay Button on the Invoice]({{site.url}}/assets/images/invoice_01.png){:width="100%"} - -![Choose how to pay the invoice from the options]({{site.url}}/assets/images/invoice_02.png){:width="100%"} +![Click Pay Button on the Invoice]({{site.url}}/assets/images/ExpensifyHelp-Invoice-1.png){:width="100%"} {% include faq-begin.md %} From ee36a05eb8b6c3fe660584ff3ef4af0f530f6a6c Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 21:07:12 -0700 Subject: [PATCH 149/180] Reverted a bunch, trying again with better directory control --- .github/workflows/deployNewHelp.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index d4abb54ba653..06062d47aa9a 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -69,4 +69,3 @@ jobs: with: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} - From d65098ea51e75152536dacdda8444816258a168f Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 21:13:56 -0700 Subject: [PATCH 150/180] Reverted a bunch, trying again with better directory control --- .github/workflows/deployNewHelp.yml | 34 ++++++++++++----------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 06062d47aa9a..066845bbf49e 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -1,16 +1,23 @@ on: + # Run on any push to main that has changes to the help directory push: branches: - main paths: - 'help/**' + + # Run on any pull request (except PRs against staging or production) that has changes to the docs directory pull_request: types: [opened, synchronize] branches-ignore: [staging, production] paths: - 'help/**' + + # Run on any manual trigger workflow_dispatch: +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "newhelp" cancel-in-progress: false @@ -20,31 +27,18 @@ jobs: env: IS_PR_FROM_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest - steps: - name: Checkout uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1.0.13 # Using the latest stable version with: - ruby-version: '3.3.4' - - - name: Install dependencies - run: | - bundle config set --local path 'vendor/bundle' - bundle install - bundle exec gem install jekyll # Use bundle exec to avoid conflicts - bundle exec jekyll -v - - - name: Build Jekyll site - run: | - bundle exec jekyll build --source ./help --destination ./help/_site + source: ./help/ + destination: ./help/_site - name: Deploy to Cloudflare Pages - uses: cloudflare/pages-action@v1 + uses: cloudflare/pages-action@v1 # Using the latest stable version id: deploy if: env.IS_PR_FROM_FORK != 'true' with: @@ -59,12 +53,12 @@ jobs: - name: Purge Cloudflare cache if: env.IS_PR_FROM_FORK != 'true' - run: $HOME/.local/bin/cli4 --verbose --delete hosts=["newhelp.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + run: $HOME/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} - name: Leave a comment on the PR - uses: actions-cool/maintain-one-comment@v3.2.0 + uses: actions-cool/maintain-one-comment@v3.2.0 # Using the latest stable version if: ${{ github.event_name == 'pull_request' && env.IS_PR_FROM_FORK != 'true' }} with: token: ${{ secrets.OS_BOTIFY_TOKEN }} From ce36bfb845f4e50af743021bc77d286238dd420f Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 21:25:58 -0700 Subject: [PATCH 151/180] Removing cruft --- help/_config.yml | 50 +------------------ .../2024-09-23-welcome-to-jekyll.markdown | 29 ----------- help/about.markdown | 18 ------- help/index.markdown | 7 ++- 4 files changed, 5 insertions(+), 99 deletions(-) delete mode 100644 help/_posts/2024-09-23-welcome-to-jekyll.markdown delete mode 100644 help/about.markdown diff --git a/help/_config.yml b/help/_config.yml index 72bb4d942f9a..9135a372964e 100644 --- a/help/_config.yml +++ b/help/_config.yml @@ -1,53 +1,7 @@ -# Welcome to Jekyll! -# -# This config file is meant for settings that affect your whole blog, values -# which you are expected to set up once and rarely edit after that. If you find -# yourself editing this file very often, consider using Jekyll's data files -# feature for the data you need to update frequently. -# -# For technical reasons, this file is *NOT* reloaded automatically when you use -# 'bundle exec jekyll serve'. If you change this file, please restart the server process. -# -# If you need help with YAML syntax, here are some quick references for you: -# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml -# https://learnxinyminutes.com/docs/yaml/ -# -# Site settings -# These are used to personalize your new site. If you look in the HTML files, -# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. -# You can create any custom variable you would like, and they will be accessible -# in the templates via {{ site.myvariable }}. - title: New Expensify Help email: concierge@expensify.com -description: >- # this means to ignore newlines until "baseurl:" - Comprehensive help documentation for New Expensify. -baseurl: "" # the subpath of your site, e.g. /blog -url: "https://newhelp.expensify.com" # the base hostname & protocol for your site, e.g. http://example.com +description: Comprehensive help documentation for New Expensify. +url: https://newhelp.expensify.com twitter_username: expensify github_username: expensify -# Build settings -theme: minima -plugins: - - jekyll-feed - -# Exclude from processing. -# The following items will not be processed, by default. -# Any item listed under the `exclude:` key here will be automatically added to -# the internal "default list". -# -# Excluded items can be processed by explicitly listing the directories or -# their entries' file path in the `include:` list. -# -# exclude: -# - .sass-cache/ -# - .jekyll-cache/ -# - gemfiles/ -# - Gemfile -# - Gemfile.lock -# - node_modules/ -# - vendor/bundle/ -# - vendor/cache/ -# - vendor/gems/ -# - vendor/ruby/ diff --git a/help/_posts/2024-09-23-welcome-to-jekyll.markdown b/help/_posts/2024-09-23-welcome-to-jekyll.markdown deleted file mode 100644 index c53c7643163e..000000000000 --- a/help/_posts/2024-09-23-welcome-to-jekyll.markdown +++ /dev/null @@ -1,29 +0,0 @@ ---- -layout: post -title: "Welcome to Jekyll!" -date: 2024-09-23 12:16:06 -0700 -categories: jekyll update ---- -You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated. - -Jekyll requires blog post files to be named according to the following format: - -`YEAR-MONTH-DAY-title.MARKUP` - -Where `YEAR` is a four-digit number, `MONTH` and `DAY` are both two-digit numbers, and `MARKUP` is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works. - -Jekyll also offers powerful support for code snippets: - -{% highlight ruby %} -def print_hi(name) - puts "Hi, #{name}" -end -print_hi('Tom') -#=> prints 'Hi, Tom' to STDOUT. -{% endhighlight %} - -Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk]. - -[jekyll-docs]: https://jekyllrb.com/docs/home -[jekyll-gh]: https://github.com/jekyll/jekyll -[jekyll-talk]: https://talk.jekyllrb.com/ diff --git a/help/about.markdown b/help/about.markdown deleted file mode 100644 index 8b4e0b28c83e..000000000000 --- a/help/about.markdown +++ /dev/null @@ -1,18 +0,0 @@ ---- -layout: page -title: About -permalink: /about/ ---- - -This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](https://jekyllrb.com/) - -You can find the source code for Minima at GitHub: -[jekyll][jekyll-organization] / -[minima](https://github.com/jekyll/minima) - -You can find the source code for Jekyll at GitHub: -[jekyll][jekyll-organization] / -[jekyll](https://github.com/jekyll/jekyll) - - -[jekyll-organization]: https://github.com/jekyll diff --git a/help/index.markdown b/help/index.markdown index 06715078416f..e5d075402ecb 100644 --- a/help/index.markdown +++ b/help/index.markdown @@ -1,6 +1,5 @@ --- -# Feel free to add content and custom Front Matter to this file. -# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults - -layout: home +title: New Expensify Help --- +Pages: +* [Expensify Superapp](/superapp.html) From 639335077bbf7f8ef7f517145457d99e63d335c0 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 21:34:14 -0700 Subject: [PATCH 152/180] Still working on working directory --- .github/workflows/deployNewHelp.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 066845bbf49e..bcdea4190f6e 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -36,6 +36,7 @@ jobs: with: source: ./help/ destination: ./help/_site + working-directory: ./help - name: Deploy to Cloudflare Pages uses: cloudflare/pages-action@v1 # Using the latest stable version @@ -46,16 +47,19 @@ jobs: accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: newhelp directory: ./help/_site + working-directory: ./help - name: Setup Cloudflare CLI if: env.IS_PR_FROM_FORK != 'true' run: pip3 install cloudflare==2.19.0 + working-directory: ./help - name: Purge Cloudflare cache if: env.IS_PR_FROM_FORK != 'true' run: $HOME/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} + working-directory: ./help - name: Leave a comment on the PR uses: actions-cool/maintain-one-comment@v3.2.0 # Using the latest stable version @@ -63,3 +67,4 @@ jobs: with: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} + working-directory: ./help From 29656b5383a08efd672296cc25206e0eb87b7089 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 21:44:04 -0700 Subject: [PATCH 153/180] Still working on working directory --- .github/workflows/deployNewHelp.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index bcdea4190f6e..65d122a48a7a 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -36,7 +36,6 @@ jobs: with: source: ./help/ destination: ./help/_site - working-directory: ./help - name: Deploy to Cloudflare Pages uses: cloudflare/pages-action@v1 # Using the latest stable version @@ -67,4 +66,3 @@ jobs: with: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} - working-directory: ./help From f71e2072fbaae5322b72d46a1f1c8ace2fdc0981 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 21:48:45 -0700 Subject: [PATCH 154/180] Still working on working directory --- .github/workflows/deployNewHelp.yml | 45 ++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 65d122a48a7a..f6d80f497f9a 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -6,7 +6,7 @@ on: paths: - 'help/**' - # Run on any pull request (except PRs against staging or production) that has changes to the docs directory + # Run on any pull request (except PRs against staging or production) that has changes to the help directory pull_request: types: [opened, synchronize] branches-ignore: [staging, production] @@ -16,8 +16,7 @@ on: # Run on any manual trigger workflow_dispatch: -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +# Allow only one concurrent deployment concurrency: group: "newhelp" cancel-in-progress: false @@ -27,42 +26,60 @@ jobs: env: IS_PR_FROM_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest + steps: - - name: Checkout + - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1.0.13 # Using the latest stable version + # Set up Ruby and run commands inside the /help directory + - name: Set up Ruby + uses: ruby/setup-ruby@v1 with: - source: ./help/ - destination: ./help/_site + ruby-version: '3.3.4' # Match your local Ruby version + bundler-cache: true + working-directory: ./help # Ensures Ruby setup is applied in /help + + - name: Install dependencies + run: | + bundle config set --local path 'vendor/bundle' + bundle install # Install all gems, including custom plugins + working-directory: ./help # Make sure dependencies are installed inside /help + + - name: Build Jekyll site + run: | + bundle exec jekyll build --source ./ --destination ./_site # Build within /help + working-directory: ./help # Ensure Jekyll is building the site in /help - name: Deploy to Cloudflare Pages - uses: cloudflare/pages-action@v1 # Using the latest stable version + uses: cloudflare/pages-action@v1 id: deploy if: env.IS_PR_FROM_FORK != 'true' with: apiToken: ${{ secrets.CLOUDFLARE_PAGES_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: newhelp - directory: ./help/_site - working-directory: ./help + directory: ./help/_site # Deploy the built site + working-directory: ./help # Ensure deployment is done from /help - name: Setup Cloudflare CLI if: env.IS_PR_FROM_FORK != 'true' run: pip3 install cloudflare==2.19.0 - working-directory: ./help + working-directory: ./help # Run CLI setup in /help - name: Purge Cloudflare cache if: env.IS_PR_FROM_FORK != 'true' run: $HOME/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} - working-directory: ./help + working-directory: ./help # Ensure cache purging is executed in /help - name: Leave a comment on the PR - uses: actions-cool/maintain-one-comment@v3.2.0 # Using the latest stable version + uses: actions-cool/maintain-one-comment@v3.2.0 if: ${{ github.event_name == 'pull_request' && env.IS_PR_FROM_FORK != 'true' }} with: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} + working-directory: ./help # Leave comment from /help + From d158e6d67c881acd4d5992cb002dfe278f5f409d Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 21:50:07 -0700 Subject: [PATCH 155/180] Still working on working directory --- .github/workflows/deployNewHelp.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index f6d80f497f9a..438fdb3c990e 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -39,7 +39,6 @@ jobs: with: ruby-version: '3.3.4' # Match your local Ruby version bundler-cache: true - working-directory: ./help # Ensures Ruby setup is applied in /help - name: Install dependencies run: | @@ -61,7 +60,6 @@ jobs: accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: newhelp directory: ./help/_site # Deploy the built site - working-directory: ./help # Ensure deployment is done from /help - name: Setup Cloudflare CLI if: env.IS_PR_FROM_FORK != 'true' @@ -81,5 +79,4 @@ jobs: with: token: ${{ secrets.OS_BOTIFY_TOKEN }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} - working-directory: ./help # Leave comment from /help From 1dde318e968fa62d0b8c20d4bae1c5112d550b97 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Fri, 27 Sep 2024 10:45:37 +0530 Subject: [PATCH 156/180] Merge conflicts --- src/components/ArchivedReportFooter.tsx | 37 ++++--------------- .../ReportActionItem/ReportPreview.tsx | 13 +++---- src/languages/en.ts | 2 +- src/languages/es.ts | 7 +--- src/languages/params.ts | 20 ---------- 5 files changed, 16 insertions(+), 63 deletions(-) diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 37a686056781..af77a20b4caa 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -57,35 +57,14 @@ function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}} policyName = lodashEscape(policyName); } - let text: string; - switch (archiveReason) { - case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED: - text = translate(`reportArchiveReasons.${archiveReason}`, { - displayName: `${displayName}`, - }); - break; - case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED: - text = translate(`reportArchiveReasons.${archiveReason}`, { - displayName: `${displayName}`, - oldDisplayName: `${oldDisplayName}`, - }); - break; - case CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY: - text = translate(`reportArchiveReasons.${archiveReason}`, { - displayName: `${displayName}`, - policyName: `${policyName}`, - shouldUseYou: actorPersonalDetails?.accountID === getCurrentUserAccountID(), - }); - break; - case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: - text = translate(`reportArchiveReasons.${archiveReason}`, { - policyName: `${policyName}`, - }); - break; - default: - text = translate(`reportArchiveReasons.${archiveReason}`); - break; - } + const text = shouldRenderHTML + ? translate(`reportArchiveReasons.${archiveReason}`, { + displayName: `${displayName}`, + oldDisplayName: `${oldDisplayName}`, + policyName: `${policyName}`, + shouldUseYou: actorPersonalDetails?.accountID === getCurrentUserAccountID(), + }) + : translate(`reportArchiveReasons.${archiveReason}`); return ( `${count} UDDs added`, }), - mappingTitle: ({mappingName}: IntacctMappingTitleParams): string => { + mappingTitle: ({mappingName}: IntacctMappingTitleParams) => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: return 'departments'; diff --git a/src/languages/es.ts b/src/languages/es.ts index 3f891ea81378..66c11d2e1728 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -6,7 +6,6 @@ import type { AddEmployeeParams, AddressLineParams, AdminCanceledRequestParams, - AgeParams, AlreadySignedInParams, ApprovalWorkflowErrorParams, ApprovedAmountParams, @@ -55,8 +54,6 @@ import type { DeleteActionParams, DeleteConfirmationParams, DidSplitAmountMessageParams, - DimensionsCountParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnterMagicCodeParams, @@ -2928,7 +2925,7 @@ const translations = { one: `1 UDD añadido`, other: (count: number) => `${count} UDDs añadido`, }), - mappingTitle: ({mappingName}: IntacctMappingTitleParams): string => { + mappingTitle: ({mappingName}: IntacctMappingTitleParams) => { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: return 'departamentos'; @@ -4070,7 +4067,7 @@ const translations = { } return { one: `te eliminó del flujo de trabajo de aprobaciones y del chat del espacio de trabajo de ${joinedNames}. Los informes enviados anteriormente seguirán estando disponibles para su aprobación en tu bandeja de entrada.`, - other: `te eliminó de los flujos de trabajo de aprobaciones y de los chats del espacio de trabajo de ${joinedNames}. Los informes enviados anteriormente seguirán estando disponibles para su aprobación en tu bandeja de entrada.` + other: `te eliminó de los flujos de trabajo de aprobaciones y de los chats del espacio de trabajo de ${joinedNames}. Los informes enviados anteriormente seguirán estando disponibles para su aprobación en tu bandeja de entrada.`, }; }, }, diff --git a/src/languages/params.ts b/src/languages/params.ts index e129705cf5c0..d51bb2d20e03 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -279,12 +279,8 @@ type LogSizeAndDateParams = {size: number; date: string}; type HeldRequestParams = {comment: string}; -type DistanceRateOperationsParams = {count: number}; - type ReimbursementRateParams = {unit: Unit}; -type ConfirmHoldExpenseParams = {transactionCount: number}; - type ChangeFieldParams = {oldValue?: string; newValue: string; fieldName: string}; type ChangePolicyParams = {fromPolicy: string; toPolicy: string}; @@ -331,10 +327,6 @@ type RemoveMemberPromptParams = { memberName: string; }; -type DeleteExpenseTranslationParams = { - count: number; -}; - type IssueVirtualCardParams = { assignee: string; link: string; @@ -384,8 +376,6 @@ type DisconnectTitleParams = {integration?: ConnectionName} | undefined; type AmountWithCurrencyParams = {amountWithCurrency: string}; -type SelectedNumberParams = {selectedNumber: number}; - type LowerUpperParams = {lower: string; upper: string}; type CategoryNameParams = {categoryName: string}; @@ -454,12 +444,8 @@ type RequiredFieldParams = {fieldName: string}; type ImportFieldParams = {importField: string}; -type DimensionsCountParams = {dimensionsCount: number}; - type IntacctMappingTitleParams = {mappingName: SageIntacctMappingName}; -type AgeParams = {age: number}; - type LastSyncAccountingParams = {relativeDate: string}; type SyncStageNameConnectionsParams = {stage: PolicyConnectionSyncStage}; @@ -570,9 +556,7 @@ export type { ReconciliationWorksParams, LastSyncAccountingParams, SyncStageNameConnectionsParams, - AgeParams, RequiredFieldParams, - DimensionsCountParams, IntacctMappingTitleParams, ImportFieldParams, AssigneeParams, @@ -608,7 +592,6 @@ export type { SecondaryLoginParams, TaxAmountParams, CategoryNameParams, - SelectedNumberParams, AmountWithCurrencyParams, LowerUpperParams, LogSizeAndDateParams, @@ -622,7 +605,6 @@ export type { BeginningOfChatHistoryDomainRoomPartOneParams, CanceledRequestParams, CharacterLimitParams, - ConfirmHoldExpenseParams, ConfirmThatParams, CompanyCardFeedNameParams, DateShouldBeAfterParams, @@ -630,7 +612,6 @@ export type { DeleteActionParams, DeleteConfirmationParams, DidSplitAmountMessageParams, - DistanceRateOperationsParams, EditActionParams, ElectronicFundsParams, EnterMagicCodeParams, @@ -732,7 +713,6 @@ export type { StripePaidParams, UnapprovedParams, RemoveMembersWarningPrompt, - DeleteExpenseTranslationParams, ApprovalWorkflowErrorParams, ConnectionNameParams, LastSyncDateParams, From ea0cfb583de2bd7aa7a9f23ce7a59784d5b11917 Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Fri, 27 Sep 2024 11:48:27 +0530 Subject: [PATCH 157/180] fixed eslint error --- .../workspace/WorkspaceInviteMessagePage.tsx | 52 +++++-------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index 06f3962abbaa..c2e3e32799ec 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -2,8 +2,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import lodashDebounce from 'lodash/debounce'; import React, {useEffect, useMemo, useState} from 'react'; import {Keyboard, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {GestureResponderEvent} from 'react-native/Libraries/Types/CoreEventTypes'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; @@ -35,36 +34,16 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/WorkspaceInviteMessageForm'; -import type {InvitedEmailsToAccountIDs, PersonalDetailsList} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import AccessOrNotFoundWrapper from './AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; -type WorkspaceInviteMessagePageOnyxProps = { - /** All of the personal details for everyone */ - allPersonalDetails: OnyxEntry; - - /** An object containing the accountID for every invited user email */ - invitedEmailsToAccountIDsDraft: OnyxEntry; - - /** Updated workspace invite message */ - workspaceInviteMessageDraft: OnyxEntry; -}; - type WorkspaceInviteMessagePageProps = WithPolicyAndFullscreenLoadingProps & WithCurrentUserPersonalDetailsProps & - WorkspaceInviteMessagePageOnyxProps & StackScreenProps; -function WorkspaceInviteMessagePage({ - workspaceInviteMessageDraft, - invitedEmailsToAccountIDsDraft, - policy, - route, - allPersonalDetails, - currentUserPersonalDetails, -}: WorkspaceInviteMessagePageProps) { +function WorkspaceInviteMessagePage({policy, route, currentUserPersonalDetails}: WorkspaceInviteMessagePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -72,6 +51,10 @@ function WorkspaceInviteMessagePage({ const {inputCallbackRef, inputRef} = useAutoFocusInput(); + const [invitedEmailsToAccountIDsDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID.toString()}`); + const [workspaceInviteMessageDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT}${route.params.policyID.toString()}`); + const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const welcomeNoteSubject = useMemo( () => `# ${currentUserPersonalDetails?.displayName ?? ''} invited you to ${policy?.name ?? 'a workspace'}`, [policy?.name, currentUserPersonalDetails?.displayName], @@ -100,6 +83,13 @@ function WorkspaceInviteMessagePage({ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); + useEffect(() => { + if (isEmptyObject(invitedEmailsToAccountIDsDraft)) { + return; + } + setWelcomeNote(getDefaultWelcomeNote()); + }, [invitedEmailsToAccountIDsDraft, workspaceInviteMessageDraft, route.params.policyID, policy]); + const debouncedSaveDraft = lodashDebounce((newDraft: string | null) => { Policy.setWorkspaceInviteMessageDraft(route.params.policyID, newDraft); }); @@ -221,18 +211,4 @@ function WorkspaceInviteMessagePage({ WorkspaceInviteMessagePage.displayName = 'WorkspaceInviteMessagePage'; -export default withPolicyAndFullscreenLoading( - withCurrentUserPersonalDetails( - withOnyx({ - allPersonalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - invitedEmailsToAccountIDsDraft: { - key: ({route}) => `${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID.toString()}`, - }, - workspaceInviteMessageDraft: { - key: ({route}) => `${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT}${route.params.policyID.toString()}`, - }, - })(WorkspaceInviteMessagePage), - ), -); +export default withPolicyAndFullscreenLoading(withCurrentUserPersonalDetails(WorkspaceInviteMessagePage)); From 843e09949e6cd68685a736ee2e411a146fd8e399 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Thu, 26 Sep 2024 23:26:33 -0700 Subject: [PATCH 158/180] Fixing validation error --- .github/workflows/deployNewHelp.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 438fdb3c990e..5262e2468429 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -68,7 +68,8 @@ jobs: - name: Purge Cloudflare cache if: env.IS_PR_FROM_FORK != 'true' - run: $HOME/.local/bin/cli4 --verbose --delete hosts=["help.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache + run: "$HOME/.local/bin/cli4 --verbose --delete 'hosts=[\"newhelp.expensify.com\"]' /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache" + env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} working-directory: ./help # Ensure cache purging is executed in /help From a59988b89169539002ea930bb3b69d3e1cfa2fa1 Mon Sep 17 00:00:00 2001 From: Roji Philip Date: Fri, 27 Sep 2024 12:08:03 +0530 Subject: [PATCH 159/180] lint fix --- .../workspace/WorkspaceInviteMessagePage.tsx | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index c2e3e32799ec..3899656424ac 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -1,6 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import lodashDebounce from 'lodash/debounce'; -import React, {useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {Keyboard, View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import type {GestureResponderEvent} from 'react-native/Libraries/Types/CoreEventTypes'; @@ -60,16 +60,19 @@ function WorkspaceInviteMessagePage({policy, route, currentUserPersonalDetails}: [policy?.name, currentUserPersonalDetails?.displayName], ); - const getDefaultWelcomeNote = () => - // workspaceInviteMessageDraft can be an empty string - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - workspaceInviteMessageDraft || - // policy?.description can be an empty string - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - Parser.htmlToMarkdown(policy?.description ?? '') || - translate('workspace.common.welcomeNote', { - workspaceName: policy?.name ?? '', - }); + const getDefaultWelcomeNote = useCallback(() => { + return ( + // workspaceInviteMessageDraft can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + workspaceInviteMessageDraft || + // policy?.description can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + Parser.htmlToMarkdown(policy?.description ?? '') || + translate('workspace.common.welcomeNote', { + workspaceName: policy?.name ?? '', + }) + ); + }, [workspaceInviteMessageDraft, policy, translate]); useEffect(() => { if (!isEmptyObject(invitedEmailsToAccountIDsDraft)) { @@ -88,7 +91,7 @@ function WorkspaceInviteMessagePage({policy, route, currentUserPersonalDetails}: return; } setWelcomeNote(getDefaultWelcomeNote()); - }, [invitedEmailsToAccountIDsDraft, workspaceInviteMessageDraft, route.params.policyID, policy]); + }, [getDefaultWelcomeNote, invitedEmailsToAccountIDsDraft]); const debouncedSaveDraft = lodashDebounce((newDraft: string | null) => { Policy.setWorkspaceInviteMessageDraft(route.params.policyID, newDraft); From 405a695ac4d0a2879f8925c298bcaac4217f5634 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Fri, 27 Sep 2024 10:25:48 +0200 Subject: [PATCH 160/180] Simplify generating backTo param in Search --- src/components/Search/index.tsx | 4 ++-- src/components/SelectionList/Search/ReportListItem.tsx | 2 +- src/components/SelectionList/types.ts | 4 ---- src/libs/SearchUtils.ts | 7 +++---- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index d9c8f751ac70..b415d91b7ab4 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -227,7 +227,7 @@ function Search({queryJSON}: SearchProps) { } const ListItem = SearchUtils.getListItem(type, status); - const data = SearchUtils.getSections(type, status, searchResults.data, searchResults.search, queryJSON.inputQuery); + const data = SearchUtils.getSections(type, status, searchResults.data, searchResults.search); const sortedData = SearchUtils.getSortedSections(type, status, data, sortBy, sortOrder); const sortedSelectedData = sortedData.map((item) => mapToItemWithSelectionInfo(item, selectedTransactions, canSelectMultiple)); @@ -294,7 +294,7 @@ function Search({queryJSON}: SearchProps) { SearchActions.createTransactionThread(hash, item.transactionID, reportID, item.moneyRequestReportActionID); } - const backTo = ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: queryJSON.inputQuery}); + const backTo = Navigation.getActiveRoute(); if (SearchUtils.isReportActionListItemType(item)) { const reportActionID = item.reportActionID; diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index 63705bda90f1..2c23c3ede4c5 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -84,7 +84,7 @@ function ReportListItem({ }; const openReportInRHP = (transactionItem: TransactionListItemType) => { - const backTo = ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: reportItem.currentSearchQuery}); + const backTo = Navigation.getActiveRoute(); Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: transactionItem.transactionThreadReportID, backTo})); }; diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 3fa901073e0e..b0d657b202c6 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -1,6 +1,5 @@ import type {MutableRefObject, ReactElement, ReactNode} from 'react'; import type {GestureResponderEvent, InputModeOptions, LayoutChangeEvent, SectionListData, StyleProp, TextInput, TextStyle, ViewStyle} from 'react-native'; -import type {SearchQueryString} from '@components/Search/types'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; // eslint-disable-next-line no-restricted-imports import type CursorStyles from '@styles/utils/cursor/types'; @@ -236,9 +235,6 @@ type ReportListItemType = ListItem & /** List of transactions that belong to this report */ transactions: TransactionListItemType[]; - - /** The current search query that was used to display these report items */ - currentSearchQuery: SearchQueryString; }; type ListItemProps = CommonListItemProps & { diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index fa50fbc0ce47..703c4df5a29c 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -250,7 +250,7 @@ function getIOUReportName(data: OnyxTypes.SearchResults['data'], reportItem: Sea return reportItem.reportName; } -function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], currentSearchQuery: SearchQueryString): ReportListItemType[] { +function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']): ReportListItemType[] { const shouldShowMerchant = getShouldShowMerchant(data); const doesDataContainAPastYearTransaction = shouldShowYear(data); @@ -269,7 +269,6 @@ function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: Onyx from: data.personalDetailsList?.[reportItem.accountID ?? -1], to: reportItem.managerID ? data.personalDetailsList?.[reportItem.managerID] : emptyPersonalDetails, transactions, - currentSearchQuery, reportName: isIOUReport ? getIOUReportName(data, reportItem) : reportItem.reportName, }; } else if (isTransactionEntry(key)) { @@ -318,14 +317,14 @@ function getListItem(type: SearchDataTypes, status: SearchStatus): ListItemType< return ReportListItem; } -function getSections(type: SearchDataTypes, status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], currentSearchQuery: SearchQueryString) { +function getSections(type: SearchDataTypes, status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']) { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return getReportActionsSections(data); } if (status === CONST.SEARCH.STATUS.EXPENSE.ALL) { return getTransactionsSections(data, metadata); } - return getReportSections(data, metadata, currentSearchQuery); + return getReportSections(data, metadata); } function getSortedSections(type: SearchDataTypes, status: SearchStatus, data: ListItemDataType, sortBy?: SearchColumnType, sortOrder?: SortOrder) { From 43c46764db5879dd331095cdf9885c55c2c3e97e Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Fri, 27 Sep 2024 10:32:01 +0200 Subject: [PATCH 161/180] Fix bug after merge conflict --- src/components/PromotedActionsBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PromotedActionsBar.tsx b/src/components/PromotedActionsBar.tsx index 85437a9ae3fd..e6ce3080ee0a 100644 --- a/src/components/PromotedActionsBar.tsx +++ b/src/components/PromotedActionsBar.tsx @@ -100,7 +100,7 @@ const PromotedActions = { return; } - ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute({reportID: targetedReportID})); + ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute({reportID: targetedReportID}), currentSearchHash); }, }), } satisfies PromotedActionsType; From eb4170625c4118ad438f9a1b8b0bff0138441335 Mon Sep 17 00:00:00 2001 From: daledah Date: Fri, 27 Sep 2024 16:23:19 +0700 Subject: [PATCH 162/180] fix replace value with const --- src/libs/CurrencyUtils.ts | 2 +- src/libs/PolicyDistanceRatesUtils.ts | 2 +- src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx | 2 +- .../workspace/distanceRates/PolicyDistanceRateEditPage.tsx | 5 ++--- .../PolicyDistanceRateTaxReclaimableEditPage.tsx | 5 +++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index 9e54efa7a03d..7d11bd0d61ae 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -164,7 +164,7 @@ function convertAmountToDisplayString(amount = 0, currency: string = CONST.CURRE return NumberFormatUtils.format(BaseLocaleListener.getPreferredLocale(), convertedAmount, { style: 'currency', currency, - minimumFractionDigits: 4, + minimumFractionDigits: CONST.MAX_TAX_RATE_DECIMAL_PLACES, }); } diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index 415fcc627b4e..8e4d68f78b4c 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -17,7 +17,7 @@ function validateRateValue(values: FormOnyxValues, currency: stri const decimalSeparator = toLocaleDigit('.'); // Allow one more decimal place for accuracy - const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,4})?$`, 'i'); + const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CONST.MAX_TAX_RATE_DECIMAL_PLACES}})?$`, 'i'); if (!rateValueRegex.test(parsedRate) || parsedRate === '') { errors.rate = Localize.translateLocal('common.error.invalidRateError'); } else if (NumberUtils.parseFloatAnyLocale(parsedRate) <= 0) { diff --git a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx index 353bf42afe29..7566f458fcff 100644 --- a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx +++ b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx @@ -85,7 +85,7 @@ function CreateDistanceRatePage({route}: CreateDistanceRatePageProps) { ) => { if (currentRateValue === values.rate) { @@ -89,7 +88,7 @@ function PolicyDistanceRateEditPage({route}: PolicyDistanceRateEditPageProps) { ) => { if (values.taxClaimableValue === currentTaxReclaimableOnValue) { @@ -85,7 +86,7 @@ function PolicyDistanceRateTaxReclaimableEditPage({route, policy}: PolicyDistanc Date: Fri, 27 Sep 2024 13:57:33 +0200 Subject: [PATCH 163/180] Fix User not redirected to IOU report when logging in after deeplink --- src/libs/actions/Report.ts | 39 +++++++++++++++++-------------- src/libs/actions/Welcome/index.ts | 4 +++- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e53cac804b90..0fe2bfbf8d47 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2689,28 +2689,31 @@ function openReportFromDeepLink(url: string) { return; } - // We need skip deeplinking if the user hasn't completed the guided setup flow. - Welcome.isOnboardingFlowCompleted({ - onNotCompleted: () => OnboardingFlow.startOnboardingFlow(), - onCompleted: () => { - const state = navigationRef.getRootState(); - const currentFocusedRoute = findFocusedRoute(state); + const handleDeeplinkNavigation = () => { + const state = navigationRef.getRootState(); + const currentFocusedRoute = findFocusedRoute(state); - if (isOnboardingFlowName(currentFocusedRoute?.name)) { - Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); - return; - } + if (isOnboardingFlowName(currentFocusedRoute?.name)) { + Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton')); + return; + } - if (shouldSkipDeepLinkNavigation(route)) { - return; - } + if (shouldSkipDeepLinkNavigation(route)) { + return; + } - if (isAuthenticated) { - return; - } + if (isAuthenticated) { + return; + } - Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); - }, + Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + }; + + // We need skip deeplinking if the user hasn't completed the guided setup flow. + Welcome.isOnboardingFlowCompleted({ + onNotCompleted: OnboardingFlow.startOnboardingFlow, + onCompleted: handleDeeplinkNavigation, + onCanceled: handleDeeplinkNavigation, }); }); }, diff --git a/src/libs/actions/Welcome/index.ts b/src/libs/actions/Welcome/index.ts index 75529a879104..fc921b16f4cf 100644 --- a/src/libs/actions/Welcome/index.ts +++ b/src/libs/actions/Welcome/index.ts @@ -23,6 +23,7 @@ let onboarding: OnboardingData; type HasCompletedOnboardingFlowProps = { onCompleted?: () => void; onNotCompleted?: () => void; + onCanceled?: () => void; }; type HasOpenedForTheFirstTimeFromHybridAppProps = { @@ -50,9 +51,10 @@ function onServerDataReady(): Promise { } let isOnboardingInProgress = false; -function isOnboardingFlowCompleted({onCompleted, onNotCompleted}: HasCompletedOnboardingFlowProps) { +function isOnboardingFlowCompleted({onCompleted, onNotCompleted, onCanceled}: HasCompletedOnboardingFlowProps) { isOnboardingFlowStatusKnownPromise.then(() => { if (Array.isArray(onboarding) || onboarding?.hasCompletedGuidedSetupFlow === undefined) { + onCanceled?.(); return; } From ebdeca565a4043acc328c4942625dbf5cbd771f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Fri, 27 Sep 2024 15:07:37 +0200 Subject: [PATCH 164/180] add comments to indicate platform specific logic should be avoided at all costs --- src/pages/ShareCodePage.tsx | 6 ++++++ src/pages/workspace/WorkspaceProfileSharePage.tsx | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index 0f8edf2c8d63..d2ef35d2daa8 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -84,6 +84,9 @@ function ShareCodePage({report, policy}: ShareCodePageProps) { }, [report, currentUserPersonalDetails, isReport]); const title = isReport ? ReportUtils.getReportName(report) : currentUserPersonalDetails.displayName ?? ''; + // We should remove this logic once https://github.com/Expensify/App/issues/19834 is done + // We shouldn't introduce platform specific code in our codebase + // This is a temporary solution while Web is not supported for the QR code download feature const platform = getPlatform(); const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID; const urlWithTrailingSlash = Url.addTrailingForwardSlash(environmentURL); @@ -139,6 +142,9 @@ function ShareCodePage({report, policy}: ShareCodePageProps) { onPress={() => Clipboard.setString(url)} shouldLimitWidth={false} /> + {/* Remove this platform specific condition once https://github.com/Expensify/App/issues/19834 is done. + We shouldn't introduce platform specific code in our codebase. + This is a temporary solution while Web is not supported for the QR code download feature */} {isNative && ( + {/* Remove this once https://github.com/Expensify/App/issues/19834 is done. + We shouldn't introduce platform specific code in our codebase. + This is a temporary solution while Web is not supported for the QR code download feature */} {shouldAllowDownloadQRCode && ( Date: Fri, 27 Sep 2024 19:14:32 +0530 Subject: [PATCH 165/180] Fix backticks usage --- src/languages/en.ts | 26 +++++++++++++------------- src/languages/es.ts | 26 +++++++++++++------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 86ef7ed41fe7..6d579a2af2df 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -841,8 +841,8 @@ const translations = { receiptScanInProgress: 'Receipt scan in progress', receiptScanInProgressDescription: 'Receipt scan in progress. Check back later or enter the details now.', receiptIssuesFound: () => ({ - one: `Issue found`, - other: `Issues found`, + one: 'Issue found', + other: 'Issues found', }), fieldPending: 'Pending...', defaultRate: 'Default rate', @@ -874,12 +874,12 @@ const translations = { }; }, deleteExpense: () => ({ - one: `Delete expense`, - other: `Delete expenses`, + one: 'Delete expense', + other: 'Delete expenses', }), deleteConfirmation: () => ({ - one: `Are you sure that you want to delete this expense?`, - other: `Are you sure that you want to delete these expenses?`, + one: 'Are you sure that you want to delete this expense?', + other: 'Are you sure that you want to delete these expenses?', }), settledExpensify: 'Paid', settledElsewhere: 'Paid elsewhere', @@ -982,14 +982,14 @@ const translations = { confirmApprove: 'Confirm approval amount', confirmApprovalAmount: 'Approve only compliant expenses, or approve the entire report.', confirmApprovalAllHoldAmount: () => ({ - one: `This expense is on hold. Do you want to approve anyway?`, - other: `These expenses are on hold. Do you want to approve anyway?`, + one: 'This expense is on hold. Do you want to approve anyway?', + other: 'These expenses are on hold. Do you want to approve anyway?', }), confirmPay: 'Confirm payment amount', confirmPayAmount: "Pay what's not on hold, or pay the entire report.", confirmPayAllHoldAmount: () => ({ - one: `This expense is on hold. Do you want to pay anyway?`, - other: `These expenses are on hold. Do you want to pay anyway?`, + one: 'This expense is on hold. Do you want to pay anyway?', + other: 'These expenses are on hold. Do you want to pay anyway?', }), payOnly: 'Pay only', approveOnly: 'Approve only', @@ -2283,7 +2283,7 @@ const translations = { issueAndManageCards: 'Issue and manage cards', reconcileCards: 'Reconcile cards', selected: () => ({ - one: `1 selected`, + one: '1 selected', other: (count: number) => `${count} selected`, }), settlementFrequency: 'Settlement frequency', @@ -2887,7 +2887,7 @@ const translations = { detailedInstructionsLink: 'View detailed instructions', detailedInstructionsRestOfSentence: ' on adding user-defined dimensions.', userDimensionsAdded: () => ({ - one: `1 UDD added`, + one: '1 UDD added', other: (count: number) => `${count} UDDs added`, }), mappingTitle: ({mappingName}: IntacctMappingTitleParams) => { @@ -3880,7 +3880,7 @@ const translations = { maxExpenseAge: 'Max expense age', maxExpenseAgeDescription: 'Flag spend older than a specific number of days.', maxExpenseAgeDays: () => ({ - one: `1 day`, + one: '1 day', other: (count: number) => `${count} days`, }), billableDefault: 'Billable default', diff --git a/src/languages/es.ts b/src/languages/es.ts index 66c11d2e1728..cb19b091b058 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -831,8 +831,8 @@ const translations = { markAsCash: 'Marcar como efectivo', routePending: 'Ruta pendiente...', receiptIssuesFound: () => ({ - one: `Problema encontrado`, - other: `Problemas encontrados`, + one: 'Problema encontrado', + other: 'Problemas encontrados', }), fieldPending: 'Pendiente...', receiptScanning: 'Escaneando recibo...', @@ -867,12 +867,12 @@ const translations = { }; }, deleteExpense: () => ({ - one: `Eliminar gasto`, - other: `Eliminar gastos`, + one: 'Eliminar gasto', + other: 'Eliminar gastos', }), deleteConfirmation: () => ({ - one: `¿Estás seguro de que quieres eliminar esta solicitud?`, - other: `¿Estás seguro de que quieres eliminar estas solicitudes?`, + one: '¿Estás seguro de que quieres eliminar esta solicitud?', + other: '¿Estás seguro de que quieres eliminar estas solicitudes?', }), settledExpensify: 'Pagado', settledElsewhere: 'Pagado de otra forma', @@ -974,14 +974,14 @@ const translations = { confirmApprove: 'Confirmar importe a aprobar', confirmApprovalAmount: 'Aprueba sólo los gastos conformes, o aprueba todo el informe.', confirmApprovalAllHoldAmount: () => ({ - one: `Este gasto está bloqueado. ¿Quieres aprobarlo de todos modos?`, - other: `Estos gastos están bloqueados. ¿Quieres aprobarlos de todos modos?`, + one: 'Este gasto está bloqueado. ¿Quieres aprobarlo de todos modos?', + other: 'Estos gastos están bloqueados. ¿Quieres aprobarlos de todos modos?', }), confirmPay: 'Confirmar importe de pago', confirmPayAmount: 'Paga lo que no está bloqueado, o paga el informe completo.', confirmPayAllHoldAmount: () => ({ - one: `Este gasto está bloqueado. ¿Quieres pagarlo de todos modos?`, - other: `Estos gastos están bloqueados. ¿Quieres pagarlos de todos modos?`, + one: 'Este gasto está bloqueado. ¿Quieres pagarlo de todos modos?', + other: 'Estos gastos están bloqueados. ¿Quieres pagarlos de todos modos?', }), payOnly: 'Solo pagar', approveOnly: 'Solo aprobar', @@ -2303,7 +2303,7 @@ const translations = { issueAndManageCards: 'Emitir y gestionar tarjetas', reconcileCards: 'Reconciliar tarjetas', selected: () => ({ - one: `1 seleccionado`, + one: '1 seleccionado', other: (count: number) => `${count} seleccionados`, }), settlementFrequency: 'Frecuencia de liquidación', @@ -2922,7 +2922,7 @@ const translations = { detailedInstructionsLink: 'Ver instrucciones detalladas', detailedInstructionsRestOfSentence: ' para añadir dimensiones definidas por el usuario.', userDimensionsAdded: () => ({ - one: `1 UDD añadido`, + one: '1 UDD añadido', other: (count: number) => `${count} UDDs añadido`, }), mappingTitle: ({mappingName}: IntacctMappingTitleParams) => { @@ -3923,7 +3923,7 @@ const translations = { maxExpenseAge: 'Antigüedad máxima de los gastos', maxExpenseAgeDescription: 'Marca los gastos de más de un número determinado de días.', maxExpenseAgeDays: () => ({ - one: `1 día`, + one: '1 día', other: (count: number) => `${count} días`, }), billableDefault: 'Valor predeterminado facturable', From ff81529f0c26c8204fb6231f6c86d1cfcd4cc4ae Mon Sep 17 00:00:00 2001 From: OSBotify Date: Fri, 27 Sep 2024 16:31:13 +0000 Subject: [PATCH 166/180] Update version to 9.0.40-5 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9153966e1d5d..d6e39817ecc8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009004004 - versionName "9.0.40-4" + versionCode 1009004005 + versionName "9.0.40-5" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index af696a13c998..ee277d55f828 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.40.4 + 9.0.40.5 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 0795209286ed..6ad146a70804 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.40.4 + 9.0.40.5 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index a545bd82c164..cfd49cb6a6a6 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.40 CFBundleVersion - 9.0.40.4 + 9.0.40.5 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index fa405b2ba182..9965f17d3b60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.40-4", + "version": "9.0.40-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.40-4", + "version": "9.0.40-5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 283b5a2d6a8c..c12b35334c76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.40-4", + "version": "9.0.40-5", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 78edc3f23c5b611bb7cb9525042e19599fa7cbfd Mon Sep 17 00:00:00 2001 From: OSBotify Date: Fri, 27 Sep 2024 16:44:25 +0000 Subject: [PATCH 167/180] Update version to 9.0.40-6 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index d6e39817ecc8..37584a89a7ad 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009004005 - versionName "9.0.40-5" + versionCode 1009004006 + versionName "9.0.40-6" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index ee277d55f828..b7df12311ccf 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.40.5 + 9.0.40.6 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 6ad146a70804..5bff4734e9dc 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.40.5 + 9.0.40.6 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index cfd49cb6a6a6..4f948fbeb4cc 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.40 CFBundleVersion - 9.0.40.5 + 9.0.40.6 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 9965f17d3b60..bb00bb068675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.40-5", + "version": "9.0.40-6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.40-5", + "version": "9.0.40-6", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c12b35334c76..016135b94ec2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.40-5", + "version": "9.0.40-6", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 3be23a2e0c6b32a6b768bed7ac13c41aafa48c4e Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 27 Sep 2024 10:00:28 -0700 Subject: [PATCH 168/180] Add continue-on-error for non-critical steps --- .github/workflows/deploy.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 53afe03720f7..df3143368d2e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -507,11 +507,13 @@ jobs: GITHUB_TOKEN: ${{ github.token }} - name: Rename web and desktop sourcemaps artifacts before assets upload in order to have unique ReleaseAsset.name + continue-on-error: true run: | mv ./desktop-staging-sourcemaps-artifact/merged-source-map.js.map ./desktop-staging-sourcemaps-artifact/desktop-staging-merged-source-map.js.map mv ./web-staging-sourcemaps-artifact/merged-source-map.js.map ./web-staging-sourcemaps-artifact/web-staging-merged-source-map.js.map - name: Upload artifacts to GitHub Release + continue-on-error: true run: | gh release upload ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --clobber \ ./android-sourcemaps-artifact/index.android.bundle.map#android-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ @@ -552,11 +554,6 @@ jobs: - name: Download all workflow run artifacts uses: actions/download-artifact@v4 - - name: Rename web and desktop sourcemaps artifacts before assets upload in order to have unique ReleaseAsset.name - run: | - mv ./desktop-sourcemaps-artifact/merged-source-map.js.map ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map - mv ./web-sourcemaps-artifact/merged-source-map.js.map ./web-sourcemaps-artifact/web-merged-source-map.js.map - - name: 🚀 Edit the release to be no longer a prerelease 🚀 run: | LATEST_RELEASE="$(gh release list --repo ${{ github.repository }} --exclude-pre-releases --json tagName,isLatest --jq '.[] | select(.isLatest) | .tagName')" @@ -565,7 +562,14 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} + - name: Rename web and desktop sourcemaps artifacts before assets upload in order to have unique ReleaseAsset.name + continue-on-error: true + run: | + mv ./desktop-sourcemaps-artifact/merged-source-map.js.map ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map + mv ./web-sourcemaps-artifact/merged-source-map.js.map ./web-sourcemaps-artifact/web-merged-source-map.js.map + - name: Upload artifacts to GitHub Release + continue-on-error: true run: | gh release upload ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --clobber \ ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map#desktop-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ From 244d751dc2ada8be9321ebe0f25b585acb068d84 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Fri, 27 Sep 2024 17:29:14 +0000 Subject: [PATCH 169/180] Update version to 9.0.41-0 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 4 ++-- ios/NewExpensifyTests/Info.plist | 4 ++-- ios/NotificationServiceExtension/Info.plist | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 37584a89a7ad..49f4b7931c38 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009004006 - versionName "9.0.40-6" + versionCode 1009004100 + versionName "9.0.41-0" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index b7df12311ccf..0ce3182823a1 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 9.0.40 + 9.0.41 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.40.6 + 9.0.41.0 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 5bff4734e9dc..d12e3a20a7b2 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 9.0.40 + 9.0.41 CFBundleSignature ???? CFBundleVersion - 9.0.40.6 + 9.0.41.0 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 4f948fbeb4cc..0b6ea2c597a2 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 9.0.40 + 9.0.41 CFBundleVersion - 9.0.40.6 + 9.0.41.0 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index bb00bb068675..f165c86c4356 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.40-6", + "version": "9.0.41-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.40-6", + "version": "9.0.41-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 016135b94ec2..ccbc2e977874 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.40-6", + "version": "9.0.41-0", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 77c50f3ecc002f6370c15e701d7e69f6f5e1c7a5 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Fri, 27 Sep 2024 17:56:18 +0000 Subject: [PATCH 170/180] Update version to 9.0.41-1 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 49f4b7931c38..f23f2d2242e5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009004100 - versionName "9.0.41-0" + versionCode 1009004101 + versionName "9.0.41-1" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 0ce3182823a1..7eda516ecebf 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.41.0 + 9.0.41.1 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index d12e3a20a7b2..16c45a1b5379 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.41.0 + 9.0.41.1 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 0b6ea2c597a2..2e420277ffff 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.41 CFBundleVersion - 9.0.41.0 + 9.0.41.1 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index f165c86c4356..4fd8c29f2da4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.41-0", + "version": "9.0.41-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.41-0", + "version": "9.0.41-1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index ccbc2e977874..99a566b387dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.41-0", + "version": "9.0.41-1", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 689b355f8b58ccde4cce1eed34abc67f151b6a89 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Fri, 27 Sep 2024 11:58:38 -0700 Subject: [PATCH 171/180] With amazing updates from @rory --- .github/workflows/deployNewHelp.yml | 24 ++++++------------- .github/workflows/preDeploy.yml | 2 +- .../workflows/reassurePerformanceTests.yml | 2 +- .github/workflows/sendReassurePerfData.yml | 2 +- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 5262e2468429..917ce1b3c0ef 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -1,3 +1,5 @@ +name: Deploy New Help Site + on: # Run on any push to main that has changes to the help directory push: @@ -30,25 +32,16 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - with: - fetch-depth: 0 - # Set up Ruby and run commands inside the /help directory + # Set up Ruby and run bundle install inside the /help directory - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '3.3.4' # Match your local Ruby version bundler-cache: true - - - name: Install dependencies - run: | - bundle config set --local path 'vendor/bundle' - bundle install # Install all gems, including custom plugins - working-directory: ./help # Make sure dependencies are installed inside /help + working-directory: ./help - name: Build Jekyll site - run: | - bundle exec jekyll build --source ./ --destination ./_site # Build within /help + run: bundle exec jekyll build --source ./ --destination ./_site working-directory: ./help # Ensure Jekyll is building the site in /help - name: Deploy to Cloudflare Pages @@ -64,20 +57,17 @@ jobs: - name: Setup Cloudflare CLI if: env.IS_PR_FROM_FORK != 'true' run: pip3 install cloudflare==2.19.0 - working-directory: ./help # Run CLI setup in /help - name: Purge Cloudflare cache if: env.IS_PR_FROM_FORK != 'true' - run: "$HOME/.local/bin/cli4 --verbose --delete 'hosts=[\"newhelp.expensify.com\"]' /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache" - + run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["newhelp.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache env: CF_API_KEY: ${{ secrets.CLOUDFLARE_TOKEN }} - working-directory: ./help # Ensure cache purging is executed in /help - name: Leave a comment on the PR uses: actions-cool/maintain-one-comment@v3.2.0 if: ${{ github.event_name == 'pull_request' && env.IS_PR_FROM_FORK != 'true' }} with: - token: ${{ secrets.OS_BOTIFY_TOKEN }} + token: ${{ github.token }} body: ${{ format('A preview of your New Help changes have been deployed to {0} :zap:️', steps.deploy.outputs.alias) }} diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index 796468170275..bfe860e60224 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -4,7 +4,7 @@ name: Process new code merged to main on: push: branches: [main] - paths-ignore: [docs/**, contributingGuides/**, jest/**, tests/**] + paths-ignore: [docs/**, help/**, contributingGuides/**, jest/**, tests/**] jobs: typecheck: diff --git a/.github/workflows/reassurePerformanceTests.yml b/.github/workflows/reassurePerformanceTests.yml index d4a25a63952b..fb7a34d6fa01 100644 --- a/.github/workflows/reassurePerformanceTests.yml +++ b/.github/workflows/reassurePerformanceTests.yml @@ -4,7 +4,7 @@ on: pull_request: types: [opened, synchronize] branches-ignore: [staging, production] - paths-ignore: [docs/**, .github/**, contributingGuides/**, tests/**, '**.md', '**.sh'] + paths-ignore: [docs/**, help/**, .github/**, contributingGuides/**, tests/**, '**.md', '**.sh'] jobs: perf-tests: diff --git a/.github/workflows/sendReassurePerfData.yml b/.github/workflows/sendReassurePerfData.yml index 42d946cece95..884182bfc896 100644 --- a/.github/workflows/sendReassurePerfData.yml +++ b/.github/workflows/sendReassurePerfData.yml @@ -3,7 +3,7 @@ name: Send Reassure Performance Tests to Graphite on: push: branches: [main] - paths-ignore: [docs/**, contributingGuides/**, jest/**] + paths-ignore: [docs/**, help/**, contributingGuides/**, jest/**] jobs: perf-tests: From a2598c498adb4497eb85eb1ecd320d8a92fc879c Mon Sep 17 00:00:00 2001 From: David Barrett Date: Fri, 27 Sep 2024 12:03:30 -0700 Subject: [PATCH 172/180] Whoops, missed ruby-version file --- .github/workflows/.ruby-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/workflows/.ruby-version diff --git a/.github/workflows/.ruby-version b/.github/workflows/.ruby-version new file mode 100644 index 000000000000..a0891f563f38 --- /dev/null +++ b/.github/workflows/.ruby-version @@ -0,0 +1 @@ +3.3.4 From cbaa36f85ab676be77f0a6157cf812c6916b8503 Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 27 Sep 2024 12:04:30 -0700 Subject: [PATCH 173/180] Use loop to verify web deploy --- .github/scripts/verifyDeploy.sh | 34 +++++++++++++++++++++++++++++++++ .github/workflows/deploy.yml | 16 ++-------------- 2 files changed, 36 insertions(+), 14 deletions(-) create mode 100755 .github/scripts/verifyDeploy.sh diff --git a/.github/scripts/verifyDeploy.sh b/.github/scripts/verifyDeploy.sh new file mode 100755 index 000000000000..51986979e188 --- /dev/null +++ b/.github/scripts/verifyDeploy.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +ENV="$1" +EXPECTED_VERSION="$2" + +BASE_URL="" +if [[ "$ENV" == 'staging' ]]; then + BASE_URL='https://staging.new.expensify.com' +else + BASE_URL='https://new.expensify.com' +fi + +sleep 5 +ATTEMPT=0 +MAX_ATTEMPTS=10 +while [[ $ATTEMPT -lt $MAX_ATTEMPTS ]]; do + ((ATTEMPT++)) + + echo "Attempt $ATTEMPT: Checking deployed version..." + DOWNLOADED_VERSION="$(wget -q -O /dev/stdout "$BASE_URL"/version.json | jq -r '.version')" + + if [[ "$EXPECTED_VERSION" == "$DOWNLOADED_VERSION" ]]; then + echo "Success: Deployed version matches local version: $DOWNLOADED_VERSION" + exit 0 + fi + + if [[ $ATTEMPT -lt $MAX_ATTEMPTS ]]; then + echo "Version mismatch. Retrying in 5 seconds..." + sleep 5 + fi +done + +echo "Error: Deployed version did not match local version after $MAX_ATTEMPTS attempts. Something went wrong..." +exit 1 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index df3143368d2e..99cd0c1dabc5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -386,23 +386,11 @@ jobs: - name: Verify staging deploy if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - run: | - sleep 5 - DOWNLOADED_VERSION="$(wget -q -O /dev/stdout https://staging.new.expensify.com/version.json | jq -r '.version')" - if [[ '${{ needs.prep.outputs.APP_VERSION }}' != "$DOWNLOADED_VERSION" ]]; then - echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ needs.prep.outputs.APP_VERSION }}. Something went wrong..." - exit 1 - fi + run: ./.github/scripts/verifyDeploy.sh staging ${{ needs.prep.outputs.APP_VERSION }} - name: Verify production deploy if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - run: | - sleep 5 - DOWNLOADED_VERSION="$(wget -q -O /dev/stdout https://new.expensify.com/version.json | jq -r '.version')" - if [[ '${{ needs.prep.outputs.APP_VERSION }}' != "$DOWNLOADED_VERSION" ]]; then - echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ needs.prep.outputs.APP_VERSION }}. Something went wrong..." - exit 1 - fi + run: ./.github/scripts/verifyDeploy.sh production ${{ needs.prep.outputs.APP_VERSION }} - name: Upload web sourcemaps artifact uses: actions/upload-artifact@v4 From 93385fcbadc331335830c47c0cf6032d9c7d0775 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Fri, 27 Sep 2024 12:09:01 -0700 Subject: [PATCH 174/180] Removing working directory from Ruby install so it finds the .ruby-version --- .github/workflows/deployNewHelp.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 917ce1b3c0ef..b0a535338e43 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -38,7 +38,6 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true - working-directory: ./help - name: Build Jekyll site run: bundle exec jekyll build --source ./ --destination ./_site From ec8e29e939299c7bf53e49011d9bc9366b3821ab Mon Sep 17 00:00:00 2001 From: David Barrett Date: Fri, 27 Sep 2024 12:12:42 -0700 Subject: [PATCH 175/180] Moving .ruby-version to /help to keep install all in there --- .github/workflows/deployNewHelp.yml | 1 + {.github/workflows => help}/.ruby-version | 0 2 files changed, 1 insertion(+) rename {.github/workflows => help}/.ruby-version (100%) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index b0a535338e43..917ce1b3c0ef 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -38,6 +38,7 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true + working-directory: ./help - name: Build Jekyll site run: bundle exec jekyll build --source ./ --destination ./_site diff --git a/.github/workflows/.ruby-version b/help/.ruby-version similarity index 100% rename from .github/workflows/.ruby-version rename to help/.ruby-version From 99beb83deeab79234338e8dcb7b5284aac44537e Mon Sep 17 00:00:00 2001 From: David Barrett Date: Fri, 27 Sep 2024 12:16:08 -0700 Subject: [PATCH 176/180] Removing main commit hook per @rory's suggestion --- .github/workflows/deployNewHelp.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 917ce1b3c0ef..4f90c39d1a50 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -2,11 +2,12 @@ name: Deploy New Help Site on: # Run on any push to main that has changes to the help directory - push: - branches: - - main - paths: - - 'help/**' +# TEST: Verify Cloudflare picks this up even if not run when merged to main +# push: +# branches: +# - main +# paths: +# - 'help/**' # Run on any pull request (except PRs against staging or production) that has changes to the help directory pull_request: @@ -38,7 +39,6 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true - working-directory: ./help - name: Build Jekyll site run: bundle exec jekyll build --source ./ --destination ./_site From 4c45ce32d8e0e7104ebd9384ca379ac68acfe2e3 Mon Sep 17 00:00:00 2001 From: David Barrett Date: Fri, 27 Sep 2024 12:28:42 -0700 Subject: [PATCH 177/180] Fixed working directory --- .github/workflows/deployNewHelp.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index 4f90c39d1a50..8c4d0fb0ae3b 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -39,6 +39,7 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true + working-directory: ./help - name: Build Jekyll site run: bundle exec jekyll build --source ./ --destination ./_site From a459ee8c20e8f12a89f9945c7b0d9fe40346b4e6 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Fri, 27 Sep 2024 19:28:54 +0000 Subject: [PATCH 178/180] Update version to 9.0.41-2 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f23f2d2242e5..2491cc21a400 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009004101 - versionName "9.0.41-1" + versionCode 1009004102 + versionName "9.0.41-2" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 7eda516ecebf..2de5297dd7fb 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.41.1 + 9.0.41.2 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 16c45a1b5379..31fc4454214c 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.41.1 + 9.0.41.2 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 2e420277ffff..0abd6fae99d5 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.41 CFBundleVersion - 9.0.41.1 + 9.0.41.2 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 4fd8c29f2da4..0ef9b9b19012 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.41-1", + "version": "9.0.41-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.41-1", + "version": "9.0.41-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 99a566b387dd..aed1cf9a2c3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.41-1", + "version": "9.0.41-2", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 2ccaacc3cbc8ba64115dfe13781df72d713c79b5 Mon Sep 17 00:00:00 2001 From: Rory Abraham <47436092+roryabraham@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:17:59 -0700 Subject: [PATCH 179/180] Update .github/scripts/verifyDeploy.sh Co-authored-by: Andrew Gable --- .github/scripts/verifyDeploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/verifyDeploy.sh b/.github/scripts/verifyDeploy.sh index 51986979e188..0a8fd3c97bcf 100755 --- a/.github/scripts/verifyDeploy.sh +++ b/.github/scripts/verifyDeploy.sh @@ -25,7 +25,7 @@ while [[ $ATTEMPT -lt $MAX_ATTEMPTS ]]; do fi if [[ $ATTEMPT -lt $MAX_ATTEMPTS ]]; then - echo "Version mismatch. Retrying in 5 seconds..." + echo "Version mismatch, found $DOWNLOADED_VERSION. Retrying in 5 seconds..." sleep 5 fi done From e76359cf4ddc95152873b17007f626f1c35b095d Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 28 Sep 2024 00:50:34 -0700 Subject: [PATCH 180/180] Use .md extension for consistency, fix .prettierignore --- .prettierignore | 1 + help/{index.markdown => index.md} | 0 2 files changed, 1 insertion(+) rename help/{index.markdown => index.md} (100%) diff --git a/.prettierignore b/.prettierignore index a9f7e1464529..98d06e8c5f71 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,6 +15,7 @@ package-lock.json *.css *.scss *.md +*.markdown # We need to modify the import here specifically, hence we disable prettier to get rid of the sorted imports src/libs/E2E/reactNativeLaunchingTest.ts # Temporary while we keep react-compiler in our repo diff --git a/help/index.markdown b/help/index.md similarity index 100% rename from help/index.markdown rename to help/index.md