Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8345,6 +8345,7 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
expenseLevelExport: 'Alle Daten – Ausgabenebene',
exportInProgress: 'Export wird ausgeführt',
conciergeWillSend: 'Concierge wird dir die Datei in Kürze senden.',
conciergeWillNotifyOnExportFailure: 'Concierge wird dir eine Nachricht senden, wenn Berichte nicht exportiert werden.',
},
domain: {
notVerified: 'Nicht verifiziert',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8280,6 +8280,7 @@ const translations = {
expenseLevelExport: 'All Data - expense level',
exportInProgress: 'Export in progress',
conciergeWillSend: 'Concierge will send you the file shortly.',
conciergeWillNotifyOnExportFailure: "Concierge will send you a message if any reports don't export.",
},
domain: {
notVerified: 'Not verified',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8432,6 +8432,7 @@ ${amount} para ${merchant} - ${date}`,
expenseLevelExport: 'Todos los datos - a nivel de gasto',
exportInProgress: 'Exportación en curso',
conciergeWillSend: 'Concierge te enviará el archivo en breve.',
conciergeWillNotifyOnExportFailure: 'Concierge te enviará un mensaje si algún informe no se exporta.',
},
openAppFailureModal: {
title: 'Algo salió mal...',
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8357,6 +8357,7 @@ Voici un *reçu test* pour vous montrer comment ça fonctionne :`,
expenseLevelExport: 'Toutes les données - niveau dépense',
exportInProgress: 'Export en cours',
conciergeWillSend: 'Concierge vous enverra le fichier sous peu.',
conciergeWillNotifyOnExportFailure: 'Concierge vous enverra un message si certains rapports ne sont pas exportés.',
},
domain: {
notVerified: 'Non vérifié',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8324,6 +8324,7 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`,
expenseLevelExport: 'Tutti i dati - livello spesa',
exportInProgress: 'Esportazione in corso',
conciergeWillSend: 'Concierge ti invierà il file a breve.',
conciergeWillNotifyOnExportFailure: 'Concierge ti invierà un messaggio se alcuni report non vengono esportati.',
},
domain: {
notVerified: 'Non verificato',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8256,6 +8256,7 @@ ${reportName}
expenseLevelExport: 'すべてのデータ - 経費レベル',
exportInProgress: 'エクスポート処理中',
conciergeWillSend: 'Conciergeがまもなくファイルを送信します。',
conciergeWillNotifyOnExportFailure: 'レポートのエクスポートに失敗した場合、Concierge からメッセージが届きます。',
},
domain: {
notVerified: '未確認',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8311,6 +8311,7 @@ Hier is een *proefbon* om je te laten zien hoe het werkt:`,
expenseLevelExport: 'Alle gegevens - uitgaveniveau',
exportInProgress: 'Export bezig',
conciergeWillSend: 'Concierge stuurt je het bestand zo meteen.',
conciergeWillNotifyOnExportFailure: 'Concierge stuurt je een bericht als rapporten niet worden geëxporteerd.',
},
domain: {
notVerified: 'Niet geverifieerd',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8290,6 +8290,7 @@ Oto *paragon testowy*, żeby pokazać Ci, jak to działa:`,
expenseLevelExport: 'Wszystkie dane – poziom wydatku',
exportInProgress: 'Trwa eksport',
conciergeWillSend: 'Concierge wkrótce wyśle Ci plik.',
conciergeWillNotifyOnExportFailure: 'Concierge wyśle Ci wiadomość, jeśli niektóre raporty nie zostaną wyeksportowane.',
},
domain: {
notVerified: 'Niezweryfikowane',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8293,6 +8293,7 @@ Aqui está um *comprovante de teste* para mostrar como funciona:`,
expenseLevelExport: 'Todos os dados - nível de despesa',
exportInProgress: 'Exportação em andamento',
conciergeWillSend: 'O Concierge enviará o arquivo para você em breve.',
conciergeWillNotifyOnExportFailure: 'O Concierge enviará uma mensagem se algum relatório não for exportado.',
},
domain: {
notVerified: 'Não verificado',
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8128,6 +8128,7 @@ ${reportName}
expenseLevelExport: '所有数据 - 报销级别',
exportInProgress: '导出进行中',
conciergeWillSend: 'Concierge 将很快把文件发送给你。',
conciergeWillNotifyOnExportFailure: '如果有报告导出失败,Concierge 将向你发送消息。',
},
domain: {
notVerified: '未验证',
Expand Down
2 changes: 1 addition & 1 deletion src/libs/API/parameters/ReportExportParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';

type ReportExportParams = {
reportIDList: string;
reportIDList: string | string[];
connectionName: ValueOf<typeof CONST.POLICY.CONNECTIONS.NAME>;
type: 'MANUAL';
/**
Expand Down
56 changes: 56 additions & 0 deletions src/libs/actions/Report/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@
/** @deprecated This value is deprecated and will be removed soon after migration. Use the email from useCurrentUserPersonalDetails hook instead. */
let deprecatedCurrentUserLogin: string | undefined;

Onyx.connect({

Check warning on line 291 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function

Check warning on line 291 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
// When signed out, val is undefined
Expand All @@ -302,7 +302,7 @@
},
});

Onyx.connect({

Check warning on line 305 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function

Check warning on line 305 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.CONCIERGE_REPORT_ID,
callback: (value) => (conciergeReportIDOnyxConnect = value),
});
Expand All @@ -310,7 +310,7 @@
// map of reportID to all reportActions for that report
const allReportActions: OnyxCollection<ReportActions> = {};

Onyx.connect({

Check warning on line 313 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function

Check warning on line 313 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
callback: (actions, key) => {
if (!key || !actions) {
Expand All @@ -322,7 +322,7 @@
});

let allReports: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 325 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -331,7 +331,7 @@
});

let allPersonalDetails: OnyxEntry<PersonalDetailsList> = {};
Onyx.connect({

Check warning on line 334 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
allPersonalDetails = value ?? {};
Expand All @@ -346,7 +346,7 @@
});

let onboarding: OnyxEntry<Onboarding>;
Onyx.connect({

Check warning on line 349 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_ONBOARDING,
callback: (val) => {
if (Array.isArray(val)) {
Expand All @@ -357,7 +357,7 @@
});

let introSelected: OnyxEntry<IntroSelected> = {};
Onyx.connect({

Check warning on line 360 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_INTRO_SELECTED,
callback: (val) => (introSelected = val),
});
Expand Down Expand Up @@ -5021,6 +5021,61 @@
API.write(WRITE_COMMANDS.MARK_AS_EXPORTED, params, {optimisticData, successData, failureData});
}

function markAsManuallyExportedMultipleReports(reportIDs: string[], connectionName: ConnectionName) {
const label = CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName];
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [];
const successData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [];
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [];
const reportData: Array<{reportID: string; label: string; optimisticReportActionID: string}> = [];

// Process each report ID
for (const reportID of reportIDs) {
const action = buildOptimisticExportIntegrationAction(connectionName, true);
const optimisticReportActionID = action.reportActionID;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ PERF-13 (docs)

The function buildOptimisticExportIntegrationAction(connectionName, true) is called inside the for loop but does not use the iterator variable reportID. This results in redundant computation - the function produces the same result for each iteration based only on connectionName and the boolean true, both of which are constant throughout the loop.

The constant label is already hoisted outside the loop correctly. Similarly, the result of buildOptimisticExportIntegrationAction should be computed once and reused:

function markAsManuallyExportedMultipleReports(reportIDs: string[], connectionName: ConnectionName) {
    const label = CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName];
    const baseAction = buildOptimisticExportIntegrationAction(connectionName, true);
    
    const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [];
    const successData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [];
    const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [];
    const reportData: Array<{reportID: string; label: string; optimisticReportActionID: string}> = [];

    // Process each report ID
    for (const reportID of reportIDs) {
        const action = {...baseAction, reportActionID: baseAction.reportActionID + reportID}; // Ensure unique IDs
        const optimisticReportActionID = action.reportActionID;
        // ... rest of loop
    }
}

Note: If buildOptimisticExportIntegrationAction generates unique IDs internally (e.g., timestamps), you may need to adjust the implementation to ensure uniqueness while still avoiding redundant computation.


Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.


reportData.push({
reportID,
label,
optimisticReportActionID,
});

optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: action,
},
});

successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: {
pendingAction: null,
},
},
});

failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: {
errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
});
}

const params = {
markedManually: true,
data: JSON.stringify(reportData),
} satisfies MarkAsExportedParams;

API.write(WRITE_COMMANDS.MARK_AS_EXPORTED, params, {optimisticData, successData, failureData});
}

function exportReportToCSV({reportID, transactionIDList}: ExportReportCSVParams, onDownloadFailed: () => void, translate: LocalizedTranslate) {
let reportIDParam = reportID;
const allReportTransactions = getReportTransactions(reportID).filter((transaction) => transaction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);
Expand Down Expand Up @@ -6687,6 +6742,7 @@
leaveGroupChat,
leaveRoom,
markAsManuallyExported,
markAsManuallyExportedMultipleReports,
markCommentAsUnread,
navigateToAndOpenChildReport,
createChildReport,
Expand Down
100 changes: 100 additions & 0 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,105 @@ function exportToIntegrationOnSearch(hash: number, reportID: string | undefined,
API.write(WRITE_COMMANDS.REPORT_EXPORT, params, {optimisticData, failureData, successData});
}

function exportMultipleReportsToIntegration(hash: number, reportIDs: string[], connectionName: ConnectionName, currentSearchKey?: SearchKey) {
if (!reportIDs.length) {
return;
}

const optimisticActions: Record<string, OptimisticExportIntegrationAction> = {};
const successActions: Record<string, OptimisticExportIntegrationAction> = {};
const optimisticReportActions: Record<string, string> = {};

for (const reportID of reportIDs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ PERF-13 (docs)

The function buildOptimisticExportIntegrationAction(connectionName) is called inside the for loop but does not use the iterator variable reportID. This results in redundant computation - the function produces the same result for each iteration based only on connectionName, which is constant throughout the loop.

Hoist the function call outside the loop to eliminate O(n) redundant computations:

function exportMultipleReportsToIntegration(hash: number, reportIDs: string[], connectionName: ConnectionName, currentSearchKey?: SearchKey) {
    if (!reportIDs.length) {
        return;
    }

    const baseOptimisticAction = buildOptimisticExportIntegrationAction(connectionName);
    
    const optimisticActions: Record<string, OptimisticExportIntegrationAction> = {};
    const successActions: Record<string, OptimisticExportIntegrationAction> = {};
    const optimisticReportActions: Record<string, string> = {};

    for (const reportID of reportIDs) {
        const optimisticAction = {...baseOptimisticAction, reportActionID: baseOptimisticAction.reportActionID + reportID}; // Ensure unique IDs
        const successAction: OptimisticExportIntegrationAction = {...optimisticAction, pendingAction: null};
        const optimisticReportActionID = optimisticAction.reportActionID;

        optimisticActions[reportID] = optimisticAction;
        successActions[reportID] = successAction;
        optimisticReportActions[reportID] = optimisticReportActionID;
    }
    // ... rest of function
}

Note: If buildOptimisticExportIntegrationAction generates unique IDs internally, you may need to adjust the implementation to ensure uniqueness while avoiding redundant computation.


Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

const optimisticAction = buildOptimisticExportIntegrationAction(connectionName);
const successAction: OptimisticExportIntegrationAction = {...optimisticAction, pendingAction: null};
const optimisticReportActionID = optimisticAction.reportActionID;

optimisticActions[reportID] = optimisticAction;
successActions[reportID] = successAction;
optimisticReportActions[reportID] = optimisticReportActionID;
}

const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_METADATA | typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [
{
onyxMethod: Onyx.METHOD.MERGE_COLLECTION,
key: ONYXKEYS.COLLECTION.REPORT_METADATA,
value: Object.fromEntries(reportIDs.map((reportID) => [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, {isActionLoading: true}])),
},
];

for (const reportID of reportIDs) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActions[reportID]]: optimisticActions[reportID],
},
});
}

const successData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_METADATA | typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS | typeof ONYXKEYS.COLLECTION.SNAPSHOT>> = [
{
onyxMethod: Onyx.METHOD.MERGE_COLLECTION,
key: ONYXKEYS.COLLECTION.REPORT_METADATA,
value: Object.fromEntries(reportIDs.map((reportID) => [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, {isActionLoading: false}])),
},
];

for (const reportID of reportIDs) {
successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActions[reportID]]: successActions[reportID],
},
});
}

if (currentSearchKey === CONST.SEARCH.SEARCH_KEYS.EXPORT) {
successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
data: Object.fromEntries(reportIDs.map((reportID) => [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, null])),
},
});
}

const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_METADATA | typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS | typeof ONYXKEYS.COLLECTION.REPORT>> = [
{
onyxMethod: Onyx.METHOD.MERGE_COLLECTION,
key: ONYXKEYS.COLLECTION.REPORT_METADATA,
value: Object.fromEntries(reportIDs.map((reportID) => [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, {isActionLoading: false}])),
},
];

for (const reportID of reportIDs) {
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActions[reportID]]: null,
},
});

failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')},
});
}

const params = {
reportIDList: reportIDs,
connectionName,
type: 'MANUAL',
optimisticReportActions: JSON.stringify(optimisticReportActions),
} satisfies ReportExportParams;

API.write(WRITE_COMMANDS.REPORT_EXPORT, params, {optimisticData, failureData, successData});
}

function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[], currentSearchKey?: SearchKey) {
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_METADATA>> = [
{
Expand Down Expand Up @@ -1420,6 +1519,7 @@ export {
getLastPolicyPaymentMethod,
getLastPolicyBankAccountID,
exportToIntegrationOnSearch,
exportMultipleReportsToIntegration,
getPayOption,
isValidBulkPayOption,
handleBulkPayItemSelected,
Expand Down
Loading
Loading