waffensachkunde

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

/ app src main dateizugriff.ts

4,2 KB Rohdatei
app/src/main/dateizugriff.ts — 116 Zeilen
1 /**
2 * Öffnen und Anzeigen von Dateien, die diese Anwendung selbst geschrieben hat.
3 *
4 * **Wozu.** Alle Druckausgaben und die Sicherung endeten bis 0.22.0 mit einem
5 * reinen Textsatz samt Pfad: „Der Lernbericht wurde gespeichert: …“. Wer die
6 * eben erzeugte Fragenliste drucken oder die Sicherung auf einen Stick ziehen
7 * wollte, musste den Pfad ablesen und im Explorer selbst hinnavigieren — nach
8 * „Speichern unter“ die unbequemste Fortsetzung, die ein Programm anbieten
9 * kann.
10 *
11 * **Warum eine Merkliste und keine Pfadprüfung.** Der Renderer schickt keinen
12 * Pfad, sondern eine Kennung: Wer einen Pfad mitbringen darf, darf auch einen
13 * anderen mitbringen — dieselbe Überlegung wie beim unterbrochenen
14 * Prüfungsbogen und beim Einspielweg der Sicherung. Der Hauptprozess merkt
15 * sich, was er in dieser Sitzung selbst geschrieben hat, und öffnet
16 * ausschließlich das. Eine Prüfung „liegt der Pfad unter Dokumente?“ wäre
17 * schwächer: Auch dort kann etwas liegen, das diese Anwendung nichts angeht.
18 *
19 * **Was hier nicht passiert.** `shell.openPath` überlässt dem Betriebssystem,
20 * womit eine Datei geöffnet wird. Das ist bei einem PDF gewollt und bei einer
21 * Datei unbekannter Herkunft eine Einladung — deshalb kommt in die Merkliste
22 * nur, was diese Anwendung soeben selbst erzeugt hat.
23 */
24
25 import { existsSync } from 'node:fs';
26
27 import { shell } from 'electron';
28
29 /**
30 * Was in dieser Sitzung geschrieben wurde, mit Kennung.
31 *
32 * Nur im Arbeitsspeicher: Nach einem Neustart ist die Liste leer, und ein
33 * alter Verweis führt dann nirgendwohin — richtig so, die Datei kann
34 * inzwischen verschoben oder gelöscht sein.
35 */
36 const geschrieben = new Map<string, string>();
37
38 let zaehler = 0;
39
40 /**
41 * Merkt einen geschriebenen Pfad und gibt die Kennung zurück, unter der der
42 * Renderer ihn öffnen lassen darf.
43 */
44 export function geschriebenMerken(pfad: string): string {
45 zaehler += 1;
46 const kennung = `datei-${String(zaehler)}`;
47 geschrieben.set(kennung, pfad);
48 return kennung;
49 }
50
51 /** Was zu tun ist. */
52 export type Dateiwunsch = 'oeffnen' | 'ordner';
53
54 /**
55 * Öffnet die Datei oder zeigt sie im Dateimanager.
56 *
57 * @returns `false`, wenn die Kennung unbekannt ist oder das System die Datei
58 * nicht öffnen konnte – etwa weil sie inzwischen verschoben wurde.
59 */
60 export async function dateiZeigen(kennungRoh: unknown, wunsch: Dateiwunsch): Promise<boolean> {
61 const kennung = typeof kennungRoh === 'string' ? kennungRoh : '';
62 const pfad = geschrieben.get(kennung);
63 if (pfad === undefined) {
64 return false;
65 }
66
67 if (wunsch === 'ordner') {
68 /* Zeigt den Ordner und markiert die Datei darin. Liefert nichts zurück –
69 ob es geklappt hat, ist von hier aus nicht feststellbar. */
70 shell.showItemInFolder(pfad);
71 return true;
72 }
73
74 /* `openPath` liefert eine leere Zeichenkette bei Erfolg und sonst die
75 Fehlermeldung des Systems. */
76 const fehler = await shell.openPath(pfad);
77 if (fehler !== '') {
78 console.warn(`[datei] konnte nicht geöffnet werden: ${fehler}`);
79 return false;
80 }
81 return true;
82 }
83
84 /**
85 * Öffnet einen Ordner im Dateimanager.
86 *
87 * **Warum ein Pfad und keine Kennung.** {@link dateiZeigen} nimmt bewusst nur
88 * eine Kennung entgegen: Was der Renderer schickt, darf nie ein Pfad sein.
89 * Diese Funktion ist das Gegenstück für den umgekehrten Fall – der Pfad
90 * stammt aus dem Hauptprozess selbst und wird nie aus einer Anfrage gebildet.
91 * Wer sie ruft, muss dafür einstehen; der Kanal darüber nimmt deshalb gar
92 * kein Argument an.
93 *
94 * @returns `false`, wenn es den Ordner nicht gibt oder das System ihn nicht
95 * öffnen konnte. Der Ordner der selbsttätigen Kopien entsteht erst mit der
96 * ersten Kopie – bis dahin gibt es nichts zu zeigen, und das ist kein
97 * Fehler, sondern eine Auskunft.
98 */
99 export async function ordnerZeigen(pfad: string): Promise<boolean> {
100 if (!existsSync(pfad)) {
101 return false;
102 }
103
104 const fehler = await shell.openPath(pfad);
105 if (fehler !== '') {
106 console.warn(`[datei] Ordner ließ sich nicht öffnen: ${fehler}`);
107 return false;
108 }
109 return true;
110 }
111
112 /** Gegenstück für Tests. */
113 export function geschriebeneVergessen(): void {
114 geschrieben.clear();
115 zaehler = 0;
116 }