netdiag-app/src/lib/backButton.svelte.ts
Eduard Wisch bf01b4cd21
Some checks failed
Build APK / build-apk (push) Failing after 11m29s
Initiales Commit — NetDiag App vollständig implementiert [apk]
SvelteKit + Capacitor 6 Netzwerk-Diagnose-App:
- Tool-Plattform (IP-Scan, Port, Ping, WLAN, DHCP, SNMP, Traceroute, Stresstest, iperf)
- Offline-First SQLite-Cache + idempotenter Dolibarr-Sync
- Natives Kotlin-Plugin NetDiagScanner (ARP, Ping, Ports, WLAN, DHCP, SNMP, Traceroute)
- Backbutton-Single-Instance-Modul, Auto-Updater, Toast-System
- Auftrags-/Kunden-Übersicht nach Baustellen-App-Muster
- CI: [apk]-Tag → Forgejo Runner → Package Registry netdiag-apk

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:01:56 +02:00

85 lines
2.4 KiB
TypeScript

/**
* Hardware-Backbutton (Android).
*
* Muster aus der Wissensbasis (KB #480, #549): Den nativen Listener NICHT im
* Svelte-$effect registrieren — bei State-Toggles würde er mehrfach hängen und
* Taps verschlucken. Stattdessen Modul-Scope mit Single-Instance-Garantie.
*
* Ablauf eines Back-Taps:
* 1. Gibt es einen offenen Dialog/Sheet? -> schließen (handler liefert true)
* 2. Sind wir nicht auf der Hauptroute? -> eine Ebene zurück
* 3. Auf der Hauptroute: 1. Tap = Hinweis, 2. Tap binnen 1,8 s = App beenden
*/
import { App, type PluginListenerHandle } from '@capacitor/app';
import { Capacitor } from '@capacitor/core';
interface BackConfig {
/** Schließt einen offenen Overlay-Zustand. true = verarbeitet, nichts weiter tun. */
handleOverlay: () => boolean;
/** true wenn die aktuelle Route die Hauptroute (Auftragsliste) ist */
isHomeRoute: () => boolean;
/** Eine Navigationsebene zurück */
goBack: () => void;
/** Hinweis "nochmal drücken zum Beenden" anzeigen */
showExitHint: () => void;
}
let listener: PluginListenerHandle | null = null;
let registering = false;
let config: BackConfig | null = null;
let exitRequestedUntil = 0;
const EXIT_WINDOW_MS = 1800;
function onBackPressed(): void {
if (!config) return;
// 1. Overlay schließen
if (config.handleOverlay()) {
exitRequestedUntil = 0;
return;
}
// 2. Nicht auf der Hauptroute -> zurück
if (!config.isHomeRoute()) {
exitRequestedUntil = 0;
config.goBack();
return;
}
// 3. Hauptroute -> Doppel-Tap zum Beenden
const now = Date.now();
if (now < exitRequestedUntil) {
App.exitApp();
} else {
exitRequestedUntil = now + EXIT_WINDOW_MS;
config.showExitHint();
}
}
/**
* Backbutton-Listener registrieren bzw. Konfiguration aktualisieren.
* Mehrfachaufrufe sind sicher — der native Listener wird nur einmal angelegt.
*/
export function registerBackListener(cfg: BackConfig): void {
config = cfg; // Callbacks immer aktualisieren
if (!Capacitor.isNativePlatform()) return;
if (listener || registering) return;
registering = true;
App.addListener('backButton', onBackPressed)
.then((handle) => {
listener = handle;
})
.finally(() => {
registering = false;
});
}
/** Listener endgültig entfernen (nur beim Zerstören des Root-Layouts) */
export function removeBackListener(): void {
listener?.remove();
listener = null;
config = null;
}