Files
Termix/plugins/file-manager/src/frontend/TransferMonitor.tsx
T

92 lines
2.9 KiB
TypeScript

import type { TranslateFn } from "@termix/plugin-sdk/frontend";
import type { TFunction } from "i18next";
import { getTransferStatus, listActiveTransfers } from "./api/transfer-api";
import { createFormatTransferMetrics } from "./transferMetricsFormat.ts";
import {
beginTransferProgressMonitoring,
isTransferBeingMonitored,
showTransferCompletionToast,
} from "./transferProgressMonitor.tsx";
import {
clearStalePendingTransfer,
getPendingTransferIds,
isTransferNotified,
} from "./transferNotificationStore.ts";
import { runAdaptivePolling } from "@termix/plugin-sdk/ui";
const POLL_INTERVAL_MS = 2000;
/**
* Resumes progress toasts for in-flight host-to-host transfers across app
* reloads and tab switches. No UI of its own, so it runs as plain background
* polling from activate() rather than a mounted headless component - core
* cannot mount a plugin component outside a tab/panel/slot, and this needs
* nothing from one.
*/
export function startTransferMonitor(translate: TranslateFn): () => void {
// Every call here is a plain t("key") / t("key", {options}) - the calls
// these helpers make everywhere else, so the SDK's simpler translate
// function satisfies the same shape they're already typed against.
const t = translate as unknown as TFunction;
const formatTransferMetrics = createFormatTransferMetrics(t);
const reconcileTransfers = async () => {
let hasWork = false;
try {
const { transfers } = await listActiveTransfers();
hasWork = transfers.length > 0;
for (const transfer of transfers) {
if (isTransferBeingMonitored(transfer.transferId)) continue;
beginTransferProgressMonitoring(transfer.transferId, t, {
resumed: true,
initialStatus: transfer,
formatTransferMetrics,
});
}
} catch {
// Non-fatal: file-manager service may be unavailable briefly
}
const pendingTransferIds = getPendingTransferIds();
for (const transferId of pendingTransferIds) {
if (
isTransferBeingMonitored(transferId) ||
isTransferNotified(transferId)
) {
continue;
}
hasWork = true;
try {
const status = await getTransferStatus(transferId);
if (status.status === "running") {
if (!isTransferBeingMonitored(transferId)) {
beginTransferProgressMonitoring(transferId, t, {
resumed: true,
initialStatus: status,
formatTransferMetrics,
});
}
continue;
}
showTransferCompletionToast(
status,
t,
undefined,
formatTransferMetrics,
);
} catch {
clearStalePendingTransfer(transferId);
}
}
return hasWork;
};
return runAdaptivePolling(reconcileTransfers, {
minIntervalMs: POLL_INTERVAL_MS,
maxIntervalMs: 30_000,
stablePollsPerStep: 1,
});
}