waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app src main index.ts

4,5 KB Rohdatei
app/src/main/index.ts — 130 Zeilen
1 /**
2 * Einstiegspunkt des Main-Prozesses.
3 */
4
5 import { app, BrowserWindow, dialog } from 'electron';
6
7 import type { CspModus } from '../shared/csp';
8 import { energieUeberwachen, energieVergessen } from './energie';
9 import { hauptfensterErstellen } from './fenster';
10 import { hilfsmittelBeobachten, ipcAbmelden, ipcRegistrieren } from './ipc';
11 import { menueEinrichten } from './menue';
12 import { fehlerZeile, protokollEinrichten, protokollieren } from './protokoll';
13 import { sicherheitEinrichten } from './sicherheit';
14 import { datenbankStatus } from './datenbank';
15
16 const MODUS: CspModus = app.isPackaged ? 'production' : 'development';
17
18 app.setName('Waffensachkunde Lernsoftware');
19
20 /*
21 Die Taskleisten-Identität unter Windows.
22
23 Ohne sie trägt das laufende Fenster eine andere Kennung als die
24 Verknüpfung, die das Installationsprogramm anlegt: Windows gruppiert das
25 Programm dann nicht zuverlässig mit dem angehefteten Symbol, und „An
26 Taskleiste anheften“ kann ein zweites, doppeltes Symbol erzeugen. Die
27 Kennung muss dieselbe sein wie `appId` in `electron-builder.yml` – deshalb
28 steht sie hier wörtlich und nicht abgeleitet: Zwei Stellen, aber nur eine
29 davon liest der Bau, und ein Auseinanderlaufen fiele erst am fertigen
30 Paket auf. `tests/paketstand.test.ts` vergleicht beide.
31
32 Nur außerhalb des Store-Pakets: Dort vergibt Windows die Identität aus dem
33 AppX-Manifest, und ein eigener Aufruf überschriebe sie mit einer falschen.
34 */
35 if (process.platform === 'win32' && !process.windowsStore) {
36 app.setAppUserModelId('de.willerding.waffensachkunde');
37 }
38
39 // Nur eine Instanz: eine zweite Instanz fokussiert das bestehende Fenster.
40 if (!app.requestSingleInstanceLock()) {
41 app.quit();
42 } else {
43 app.on('second-instance', () => {
44 const [fenster] = BrowserWindow.getAllWindows();
45 if (fenster) {
46 if (fenster.isMinimized()) {
47 fenster.restore();
48 }
49 fenster.focus();
50 }
51 });
52
53 /*
54 Was sonst niemand sähe, kommt in eine Datei.
55
56 Fehler des Hauptprozesses landeten bis 0.22.0 ausschließlich in der
57 Konsole – die im gepackten Programm niemand sieht. Wer „es stürzt manchmal
58 ab“ melden wollte, hatte nichts in der Hand. Die Datei geht nirgendwohin;
59 sie liegt neben dem Lernstand und wird von Hand mitgeschickt oder gar
60 nicht. Einzelheiten und die Reinigungsregel stehen in `protokoll.ts`.
61
62 Vor `whenReady`, damit auch ein Fehler beim Hochfahren noch hineinkommt.
63 */
64 protokollEinrichten(app.getPath('userData'));
65
66 process.on('uncaughtException', (fehler: unknown) => {
67 console.error('[fehler] Unbehandelte Ausnahme:', fehler);
68 protokollieren('fehler', `Unbehandelte Ausnahme: ${fehlerZeile(fehler)}`);
69 });
70
71 process.on('unhandledRejection', (grund: unknown) => {
72 console.error('[fehler] Unbehandeltes Versprechen:', grund);
73 protokollieren('fehler', `Unbehandeltes Versprechen: ${fehlerZeile(grund)}`);
74 });
75
76 app.whenReady().then(
77 () => {
78 sicherheitEinrichten(MODUS);
79 menueEinrichten();
80 ipcRegistrieren();
81 hilfsmittelBeobachten();
82 energieUeberwachen();
83
84 // Rauchtest für better-sqlite3 einmal beim Start protokollieren.
85 const status = datenbankStatus();
86 console.info(`[datenbank] ${status.meldung}`);
87
88 hauptfensterErstellen();
89
90 app.on('activate', () => {
91 if (BrowserWindow.getAllWindows().length === 0) {
92 hauptfensterErstellen();
93 }
94 });
95 },
96 (fehler: unknown) => {
97 /*
98 Nicht wortlos beenden.
99
100 Vorher stand der Grund nur in der Konsole – die niemand sieht, der
101 das Programm über sein Symbol startet. Ein Fenster, das aufgeht und
102 sofort wieder verschwindet, ist keine Fehlermeldung. `showErrorBox`
103 geht auch ohne fertige Anwendung und wird vom Screenreader gelesen.
104 */
105 const grund = fehler instanceof Error ? fehler.message : String(fehler);
106 console.error('[start] Anwendung konnte nicht initialisiert werden:', fehler);
107 protokollieren('start', `Anwendung konnte nicht starten: ${fehlerZeile(fehler)}`);
108 dialog.showErrorBox(
109 'Waffensachkunde – Lernsoftware',
110 [
111 'Das Programm konnte nicht gestartet werden.',
112 grund,
113 'Ihr Lernstand ist davon nicht betroffen; er liegt als Datei auf Ihrer Festplatte.',
114 ].join('\n\n'),
115 );
116 app.quit();
117 },
118 );
119
120 app.on('window-all-closed', () => {
121 if (process.platform !== 'darwin') {
122 app.quit();
123 }
124 });
125
126 app.on('will-quit', () => {
127 energieVergessen();
128 ipcAbmelden();
129 });
130 }