146 lines
4.5 KiB
JavaScript
146 lines
4.5 KiB
JavaScript
"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();
|
||
});
|