mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
92 lines
2.9 KiB
TypeScript
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,
|
|
});
|
|
}
|