Upload via GUI (16 Dateien)
This commit is contained in:
+145
@@ -0,0 +1,145 @@
|
||||
"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();
|
||||
});
|
||||
Reference in New Issue
Block a user