"use strict"; const { app, BrowserWindow, dialog } = require("electron"); const fs = require("fs"); const path = require("path"); const db = require("./db"); const lock = require("./lock"); const backup = require("./backup"); const reminders = require("./reminders"); const webserver = require("./webserver"); const registerIpc = require("./ipc"); // Works around a blank/unpainted-window glitch seen on some Windows GPU drivers // (matches the "GPU process exited unexpectedly" warnings this app triggered there) — // newly inserted DOM content (e.g. the modal) wouldn't repaint until the window was // resized or moved. Must be called before app is ready. app.disableHardwareAcceleration(); const settingsPath = path.join(app.getPath("userData"), "settings.json"); const iconPath = path.join(__dirname, "..", "assets", "icon.ico"); function readSettings() { try { return JSON.parse(fs.readFileSync(settingsPath, "utf8")); } catch { return {}; } } function writeSettings(s) { fs.mkdirSync(path.dirname(settingsPath), { recursive: true }); fs.writeFileSync(settingsPath, JSON.stringify(s, null, 2)); } let mainWindow = null; async function chooseDataFolder(isFirstRun) { const message = isFirstRun ? "Wo soll die Fellakte ihre Daten speichern?\n\nFür zwei PCs in der Praxis: einen Ordner auf einem gemeinsamen Netzlaufwerk oder einem synchronisierten Ordner (z. B. OneDrive/Dropbox) wählen. Für einen einzelnen PC reicht ein normaler Ordner." : "Neuen Datenordner wählen"; await dialog.showMessageBox({ type: "info", title: "Fellakte – Datenordner", message, buttons: ["Weiter"], }); const result = await dialog.showOpenDialog({ title: "Datenordner für die Fellakte wählen oder anlegen", properties: ["openDirectory", "createDirectory"], }); if (result.canceled || !result.filePaths[0]) return null; return result.filePaths[0]; } async function ensureDataFolder() { const settings = readSettings(); let folder = settings.dataFolder; if (!folder || !fs.existsSync(folder)) { folder = await chooseDataFolder(true); if (!folder) { app.quit(); return null; } writeSettings({ ...settings, dataFolder: folder }); } return folder; } async function checkLockAndWarn(folder) { const { active, info } = lock.checkOther(folder); if (!active) return true; const when = new Date(info.updatedAt).toLocaleTimeString("de-DE"); const { response } = await dialog.showMessageBox({ type: "warning", title: "Fellakte wird eventuell gerade woanders benutzt", message: `Auf "${info.host}" wurde dieser Datenordner zuletzt um ${when} Uhr verwendet.`, detail: "Wenn dort gerade jemand arbeitet und du hier ebenfalls speicherst, überschreibt die zuletzt gespeicherte Version die andere. Am sichersten: kurz nachfragen, ob der andere PC gerade offen ist.", buttons: ["Trotzdem öffnen", "Abbrechen"], defaultId: 1, cancelId: 1, }); return response === 0; } async function createWindow(dataFolder) { mainWindow = new BrowserWindow({ width: 1360, height: 860, minWidth: 980, minHeight: 640, backgroundColor: "#f7f0e8", icon: iconPath, webPreferences: { preload: path.join(__dirname, "preload.js"), backgroundThrottling: false, contextIsolation: true, nodeIntegration: false, sandbox: true, }, }); mainWindow.setMenuBarVisibility(false); // Belt-and-suspenders alongside the webPreferences option above: without this, // Chromium can throttle/skip repainting a window that isn't the active foreground // one yet, which reads as "newly opened content stays blank until you resize/refocus". mainWindow.webContents.setBackgroundThrottling(false); await mainWindow.loadFile(path.join(__dirname, "..", "renderer", "index.html")); } app.whenReady().then(async () => { const dataFolder = await ensureDataFolder(); if (!dataFolder) return; const proceed = await checkLockAndWarn(dataFolder); if (!proceed) { app.quit(); return; } lock.acquire(dataFolder); await db.init(dataFolder); backup.runDailyBackupIfNeeded(dataFolder); registerIpc({ dataFolder }); await createWindow(dataFolder); reminders.start(); webserver.start(dataFolder); app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(dataFolder); }); }); app.on("window-all-closed", () => { lock.release(); reminders.stop(); webserver.stop(); if (process.platform !== "darwin") app.quit(); }); app.on("before-quit", () => { lock.release(); reminders.stop(); webserver.stop(); });