Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 539x 539x 539x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 539x 539x 1x 1x 1x 539x | import { QueryClient } from "react-query";
import {
getBackgroundTaskStatus,
markBackgroundTasksAsRead,
} from "@services/api/transactions";
import type {
BackgroundTask,
TransactionExportResult,
} from "@customTypes/backgroundTasks";
import { getTaskErrorMessage } from "@hooks/exports/asyncExport.helpers";
import { showMessage } from "@common/Toast";
export type AsyncExportEntry = {
merchantId: number;
taskId: number;
downloadAttempted: boolean;
storageKey: string;
queryKeyPrefix: string;
};
// Single global polling interval handles all in-flight async exports regardless of type
let globalPollingInterval: NodeJS.Timeout | null = null;
export const activeExports = new Map<string, AsyncExportEntry>();
export const handleExportDownload = async (
taskData: BackgroundTask<TransactionExportResult>,
merchantId: number,
) => {
Eif (taskData.result?.downloadURL) {
const link = document.createElement("a");
link.href = taskData.result.downloadURL;
link.download = taskData.result.fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showMessage(
"Success",
`Export completed! ${taskData.result.recordCount.toLocaleString()} records downloaded.`,
true,
"Download Started",
5000,
);
try {
await markBackgroundTasksAsRead(merchantId, [taskData.id]);
} catch (error) {
console.error("Error marking task as read:", error);
}
}
};
export const startGlobalPolling = (
merchantId: number,
taskId: number,
queryClient: QueryClient,
storageKey: string,
queryKeyPrefix: string,
) => {
const key = `${merchantId}-${taskId}`;
activeExports.set(key, {
merchantId,
taskId,
downloadAttempted: false,
storageKey,
queryKeyPrefix,
});
if (globalPollingInterval) {
clearInterval(globalPollingInterval);
}
globalPollingInterval = setInterval(async () => {
const entries = Array.from(activeExports.entries());
for (const [entryKey, exportInfo] of entries) {
const {
merchantId: mid,
taskId: tid,
downloadAttempted,
storageKey: sKey,
queryKeyPrefix: qPrefix,
} = exportInfo;
try {
const taskData = (await getBackgroundTaskStatus(
mid,
tid,
)) as BackgroundTask<TransactionExportResult>;
queryClient.setQueryData([qPrefix, mid, tid], taskData);
if (taskData.status === "completed" && !downloadAttempted) {
exportInfo.downloadAttempted = true;
await handleExportDownload(taskData, mid);
activeExports.delete(entryKey);
sessionStorage.removeItem(sKey);
}
if (taskData.status === "failed") {
showMessage(
"Error",
getTaskErrorMessage(taskData.error),
true,
"Export Failed",
);
activeExports.delete(entryKey);
sessionStorage.removeItem(sKey);
}
if (taskData.status === "cancelled") {
activeExports.delete(entryKey);
sessionStorage.removeItem(sKey);
}
} catch (error) {
console.error("Error polling task:", error);
}
}
if (activeExports.size === 0 && globalPollingInterval) {
clearInterval(globalPollingInterval);
globalPollingInterval = null;
}
}, 3000);
};
const clearAllExports = () => {
activeExports.forEach(({ storageKey }) => {
sessionStorage.removeItem(storageKey);
});
activeExports.clear();
Iif (globalPollingInterval) {
clearInterval(globalPollingInterval);
globalPollingInterval = null;
}
};
/**
* Clear all in-flight async exports and stop polling.
* Called on logout and account switch.
*/
export const cleanupAllAsyncExports = clearAllExports;
|