import { app, BrowserWindow, Menu, Tray, dialog, nativeImage, shell, } from "electron"; import fs from "fs"; import path from "path"; import { configPath, loadConfig, saveConfig, type TrayConfig } from "./config"; import { pingAgent } from "./viaplay"; import { refreshAllSilent, refreshDueSilent } from "./jobs"; let tray: Tray | null = null; let settingsWin: BrowserWindow | null = null; let cfg: TrayConfig = loadConfig(); let pollTimer: ReturnType | null = null; let busy = false; let lastPollAt: string | null = null; let lastDueCount = 0; const POLL_MS = 10 * 60 * 1000; // elke 10 min: vraag server wie bijna verloopt const WITHIN_MINUTES = 90; // vernieuw als token < 90 min geldig is function resolveAsset(...parts: string[]): string { const candidates = [ path.join(__dirname, "..", "assets", ...parts), path.join(app.getAppPath(), "assets", ...parts), path.join(process.resourcesPath, "assets", ...parts), ]; for (const p of candidates) { if (fs.existsSync(p)) return p; } return candidates[0]; } function loadTrayImage(): Electron.NativeImage { for (const name of ["tray-icon-32.png", "tray-icon-16.png", "icon.png"] as const) { const p = resolveAsset(name); if (!fs.existsSync(p)) continue; let img = nativeImage.createFromPath(p); if (img.isEmpty()) continue; const { width } = img.getSize(); // Windows systeemvak: 16–32 px; groter schalen ziet er wazig/onzichtbaar uit if (width > 32) img = img.resize({ width: 16, height: 16 }); return img; } return nativeImage.createEmpty(); } function statusTooltip(): string { const parts = ["Media Cluster Token Tray (auto)"]; if (busy) parts.push("Bezig met stille refresh…"); parts.push(`Poll: elke 10 min · drempel ${WITHIN_MINUTES} min`); if (lastPollAt) { parts.push(`Laatste check: ${new Date(lastPollAt).toLocaleString("nl-NL")}`); } if (lastDueCount > 0) parts.push(`Laatst due: ${lastDueCount} account(s)`); if (cfg.lastPushSummary) parts.push(cfg.lastPushSummary); if (cfg.lastError) parts.push(`Fout: ${cfg.lastError}`); return parts.join("\n"); } function rebuildMenu() { if (!tray) return; const menu = Menu.buildFromTemplate([ { label: busy ? "Bezig…" : "Nu checken (server → due)", enabled: !busy, click: () => void runAuto(true), }, { label: "Alles vernieuwen (stil)", enabled: !busy, click: () => void runManualAll(true), }, { label: "Alles vernieuwen (zichtbaar)", enabled: !busy, click: () => void runManualAll(false), }, { type: "separator" }, { label: "Verbinding testen", click: () => void testConnection() }, { label: "Instellingen…", click: () => openSettings() }, { label: "Configmap openen", click: () => void shell.showItemInFolder(configPath()), }, { type: "separator" }, { label: "Afsluiten", click: () => app.quit() }, ]); tray.setContextMenu(menu); tray.setToolTip(statusTooltip()); } function openSettings() { if (settingsWin && !settingsWin.isDestroyed()) { settingsWin.focus(); return; } settingsWin = new BrowserWindow({ width: 520, height: 360, title: "Token Tray — instellingen", autoHideMenuBar: true, webPreferences: { contextIsolation: true, nodeIntegration: false }, }); const html = ` Instellingen

Token Tray (auto)

Server houdt JWT-expiry bij. Deze PC pollt elke 10 min en logt stil in via Chrome als tokens bijna verlopen. Accounts + wachtwoorden staan in Admin → Scripts.

`; settingsWin.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`); settingsWin.on("page-title-updated", (e, title) => { if (!title.startsWith("SAVE:")) return; e.preventDefault(); try { const payload = JSON.parse(decodeURIComponent(title.slice(5))) as { apiUrl: string; trayKey: string; }; cfg.apiUrl = payload.apiUrl.trim().replace(/\/$/, "") || cfg.apiUrl; cfg.trayKey = payload.trayKey.trim(); saveConfig(cfg); rebuildMenu(); startPolling(); void dialog.showMessageBox({ type: "info", message: "Opgeslagen", detail: "Auto-poll draait (elke 10 min, alleen bijna-verlopen tokens).", }); settingsWin?.close(); } catch (err) { void dialog.showErrorBox("Opslaan mislukt", String(err)); } }); settingsWin.on("closed", () => { settingsWin = null; }); } function escapeHtml(s: string): string { return s .replace(/&/g, "&") .replace(/ void runAuto(true)); } function startPolling() { if (pollTimer) clearInterval(pollTimer); // Direct één check, daarna elke 10 min void runAuto(false); pollTimer = setInterval(() => void runAuto(false), POLL_MS); } app.whenReady().then(() => { try { app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true, }); } catch { /* ignore — niet op alle platforms beschikbaar */ } cfg = loadConfig(); createTray(); if (!cfg.trayKey) openSettings(); else startPolling(); }); app.on("window-all-closed", () => { /* tray blijft */ }); app.on("before-quit", () => { if (pollTimer) clearInterval(pollTimer); });