mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
import { isElectron } from "@/lib/electron";
|
|
import { getSyncStatus, type SyncStatus } from "@/api/sync-api";
|
|
import { notifySyncChanged } from "@/lib/linked-server";
|
|
|
|
/**
|
|
* The desktop's sync status, polled from the embedded backend while anything
|
|
* shows it. When a pass finishes, the views that show synced data are told
|
|
* to reload.
|
|
*/
|
|
|
|
const IDLE_MS = 15_000;
|
|
const BUSY_MS = 2_000;
|
|
|
|
let snapshot: SyncStatus | null = null;
|
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
let lastSyncAt: string | null | undefined;
|
|
const listeners = new Set<() => void>();
|
|
|
|
function emit(): void {
|
|
for (const listener of listeners) listener();
|
|
}
|
|
|
|
function announce(): void {
|
|
notifySyncChanged();
|
|
window.dispatchEvent(new CustomEvent("hosts:refresh"));
|
|
window.dispatchEvent(new CustomEvent("termix:hosts-changed"));
|
|
window.dispatchEvent(new CustomEvent("termix:credentials-changed"));
|
|
window.dispatchEvent(new CustomEvent("termix:plugins-changed"));
|
|
}
|
|
|
|
async function poll(): Promise<void> {
|
|
timer = null;
|
|
try {
|
|
const next = await getSyncStatus();
|
|
const syncedAt = next.lastSyncAt ?? null;
|
|
if (lastSyncAt !== undefined && syncedAt !== lastSyncAt) announce();
|
|
if (snapshot && snapshot.linked !== next.linked) notifySyncChanged();
|
|
lastSyncAt = syncedAt;
|
|
snapshot = next;
|
|
emit();
|
|
} catch {
|
|
// The backend may still be starting; the next poll tries again.
|
|
}
|
|
schedule();
|
|
}
|
|
|
|
function schedule(delay?: number): void {
|
|
if (listeners.size === 0 || timer) return;
|
|
const busy = snapshot?.status === "syncing" || !!snapshot?.pending;
|
|
timer = setTimeout(() => void poll(), delay ?? (busy ? BUSY_MS : IDLE_MS));
|
|
}
|
|
|
|
export function subscribeSyncStatus(listener: () => void): () => void {
|
|
listeners.add(listener);
|
|
if (isElectron() && listeners.size === 1 && !timer) void poll();
|
|
return () => {
|
|
listeners.delete(listener);
|
|
if (listeners.size === 0 && timer) {
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
};
|
|
}
|
|
|
|
export function getSyncStatusSnapshot(): SyncStatus | null {
|
|
return snapshot;
|
|
}
|
|
|
|
/** Takes a status a request already returned, and checks again soon. */
|
|
export function setSyncStatus(next: SyncStatus): void {
|
|
snapshot = next;
|
|
if (lastSyncAt !== undefined && (next.lastSyncAt ?? null) !== lastSyncAt) {
|
|
announce();
|
|
}
|
|
lastSyncAt = next.lastSyncAt ?? null;
|
|
emit();
|
|
if (timer) clearTimeout(timer);
|
|
timer = null;
|
|
schedule(BUSY_MS);
|
|
}
|
|
|
|
export function refreshSyncStatus(): void {
|
|
if (timer) clearTimeout(timer);
|
|
timer = null;
|
|
void poll();
|
|
}
|