a99ae432fc
Fuenf Meldungen des Eigentuemers, vier davon behoben: 1. Tankstellen-Nadel war bei 32 px ein dunkler Fleck (poi zeichnet nur einen Ring). Gefuellter Punkt in currentColor zwischen Silhouette und Symbol - die Geometrie stand im Pfad. Rot wurde ausdruecklich abgelehnt, die Umsetzung aendert keine Farbe. 2. Tanken-Symbol im Standort-Blatt wurde gefuellt UND umrandet, weil das CI-Symbol zusaetzlich stroke-Attribute mitbekam. Jetzt ueber ciSVG wie das Parkplatz-Symbol daneben; in der App dafuer imRaster an SymbolTanken (der 0,84-Faktor gilt nur in der Reiterleiste). 3. Tankstellenname: der Bestand hat zwei Bauformen - aeltere Belege tragen den Betreiber in station_name und die Anschrift daneben. Neu tankstelleTeile() liest beide Felder, entfernt Firmierungen, kuerzt Strassen und trennt die Hausnummer auch ohne Leerzeichen. Uebersicht: Marke, Strasse, Ort. Einzelbeleg: zwei Zeilen. adresseTeilen() hat keinen Aufrufer mehr und ist entfernt. 4. Zwischenablage und Kalendereintrag hatten denselben Grund: Capacitor 8 registriert nur, was in packageClassList steht, und cap sync traegt dort ausschliesslich npm-Pakete ein. Beide app-eigenen Plugins lagen im Programm und wurden nie registriert. ios-teilen-einrichten.mjs traegt sie jetzt nach, ios-signieren.sh bricht ab, wenn sie im fertigen .ipa fehlen. 5. Geteilter Beleg nur mit Datum und Uhrzeit bleibt offen - der Weg nutzt denselben belegLesen()-Pfad wie eine gewaehlte Datei, es fehlt der konkrete Beleg oder die Protokollzeile. tsc sauber, 246 Tests, vite build sauber, Panel als Modul geparst, audi_ha_test auf 2026.9.4.20 ohne Traceback. Panel und App liefern fuer die Tankstelle byteweise dasselbe.
135 lines
5.8 KiB
TypeScript
135 lines
5.8 KiB
TypeScript
/**
|
|
* Beleg-PDF aus der Zwischenablage oder einer Dateiliste.
|
|
*
|
|
* Übernommen aus dem Panel (`belegAusZwischenablage()`, `istPdf()`,
|
|
* `zwischenablageKnopfSinnvoll()` in audi-dashboard-app.js) — dort gibt es
|
|
* diesen Weg seit längerem, in der App fehlte er ganz: der Beleg-Knopf öffnete
|
|
* direkt die Dateiauswahl, und ein aus der Mail kopiertes PDF ließ sich
|
|
* überhaupt nicht einfügen (gemeldet vom Eigentümer, 2026-08-31).
|
|
*
|
|
* WARUM DER KNOPF NUR AUF TIPPGERÄTEN ERSCHEINT
|
|
* ---------------------------------------------
|
|
* `navigator.clipboard.read()` gibt aus Datenschutzgründen nur Text, HTML und
|
|
* Bilder heraus. Eine im Explorer oder Finder kopierte Datei taucht dort nie
|
|
* auf — am Rechner scheitert der Weg also zwangsläufig, und ein Knopf, der
|
|
* immer fehlschlägt, ist schlimmer als kein Knopf. Dort bleiben Ziehen und
|
|
* Dateiauswahl.
|
|
*
|
|
* AUF DEM TELEFON LÄUFT ER SEIT DEM 02.09.2026 NATIV
|
|
* --------------------------------------------------
|
|
* Dieselbe Grenze der Web-Schnittstelle traf nämlich auch iOS: ein aus Mail
|
|
* kopiertes PDF ist über `navigator.clipboard.read()` NIE zu bekommen, der
|
|
* Knopf endete dort zwangsläufig mit „In der Zwischenablage liegt kein PDF"
|
|
* (gemeldet vom Eigentümer). `UIPasteboard` kennt die Grenze nicht — deshalb
|
|
* geht die Anfrage auf dem Gerät über ein kleines eigenes Plugin
|
|
* (native/BelegZwischenablage.swift). Die Einfügen-Rückfrage des Systems
|
|
* erscheint dabei genauso wie im Web.
|
|
*/
|
|
|
|
import { Capacitor, registerPlugin } from "@capacitor/core"
|
|
|
|
/** Nicht nur auf den MIME-Typ verlassen: eine aus dem Explorer/Finder kopierte
|
|
oder gezogene Datei kommt je nach Browser und Quelle mit leerem `type` an. */
|
|
export function istPdf(datei: File | null | undefined): boolean {
|
|
if (!datei) return false
|
|
return datei.type === "application/pdf" || /\.pdf$/i.test(datei.name || "")
|
|
}
|
|
|
|
/** Nimmt das erste PDF aus einer DataTransfer-/Dateiliste. */
|
|
export function pdfAusDateiliste(dateien: FileList | File[] | null | undefined): File | null {
|
|
return [...(dateien ?? [])].find(istPdf) ?? null
|
|
}
|
|
|
|
export class ZwischenablageFehler extends Error {}
|
|
|
|
interface BelegZwischenablage {
|
|
/** Leeres Ergebnis heißt „nichts Passendes kopiert" - kein Fehler. */
|
|
pdfHolen(): Promise<{ base64?: string; name?: string }>
|
|
}
|
|
|
|
/** Muss zu `jsName` in BelegZwischenablage.swift passen. */
|
|
const Nativ = registerPlugin<BelegZwischenablage>("BelegZwischenablage")
|
|
|
|
function base64ZuDatei(base64: string, name: string): File {
|
|
const roh = atob(base64)
|
|
const bytes = new Uint8Array(roh.length)
|
|
for (let i = 0; i < roh.length; i++) bytes[i] = roh.charCodeAt(i)
|
|
return new File([bytes], name, { type: "application/pdf" })
|
|
}
|
|
|
|
/**
|
|
* Holt ein PDF aus der Zwischenablage.
|
|
*
|
|
* Wirft mit einem Text, der zur jeweiligen Ursache die richtige Abhilfe nennt —
|
|
* "bitte Datei auswählen" hilft nicht weiter, wenn schlicht kein PDF kopiert
|
|
* wurde, und "erneut kopieren" nicht, wenn der Browser die Zwischenablage gar
|
|
* nicht herausgibt.
|
|
*/
|
|
export async function pdfAusZwischenablage(): Promise<File> {
|
|
if (Capacitor.isNativePlatform()) {
|
|
/* Meldet sich der native Teil nicht bei der Bruecke, hilft nur die
|
|
Dateiauswahl - der Web-Weg kann ein PDF grundsaetzlich nicht liefern
|
|
(Begruendung im Modulkopf).
|
|
|
|
Bis zum 04.09.2026 stand hier die Vermutung, die App sei nur ueber die
|
|
Aktualisierung gewachsen. Sie war falsch: der Eigentuemer hatte frisch
|
|
installiert, und BelegZwischenablagePlugin lag nachweislich im
|
|
Programm. Capacitor 8 sucht Plugin-Klassen aber nicht im Programm,
|
|
sondern registriert ausschliesslich, was in packageClassList der
|
|
capacitor.config.json steht - und `cap sync` traegt dort nur
|
|
npm-Pakete ein, keine app-eigenen Plugins. Behoben in
|
|
scripts/ios-teilen-einrichten.mjs; hier steht deshalb nur noch die
|
|
Tatsache und nicht mehr eine geratene Ursache. */
|
|
if (!Capacitor.isPluginAvailable("BelegZwischenablage")) {
|
|
throw new ZwischenablageFehler(
|
|
'Diese App-Fassung kann die Zwischenablage nicht abfragen - bitte ' +
|
|
'"Datei auswählen".',
|
|
)
|
|
}
|
|
let ergebnis: { base64?: string; name?: string }
|
|
try {
|
|
ergebnis = await Nativ.pdfHolen()
|
|
} catch {
|
|
// Abgelehnte Einfügen-Rückfrage des Systems.
|
|
throw new ZwischenablageFehler(
|
|
'Kein Zugriff auf die Zwischenablage - bitte "Datei auswählen".',
|
|
)
|
|
}
|
|
if (!ergebnis?.base64) {
|
|
throw new ZwischenablageFehler(
|
|
'In der Zwischenablage liegt kein PDF - bitte den Beleg erneut kopieren oder "Datei auswählen".',
|
|
)
|
|
}
|
|
return base64ZuDatei(ergebnis.base64, ergebnis.name || "beleg.pdf")
|
|
}
|
|
|
|
if (!navigator.clipboard?.read) {
|
|
throw new ZwischenablageFehler(
|
|
'Dieser Browser gibt die Zwischenablage nicht heraus - bitte "Datei auswählen".',
|
|
)
|
|
}
|
|
let eintraege: ClipboardItems
|
|
try {
|
|
eintraege = await navigator.clipboard.read()
|
|
} catch {
|
|
// Verweigerte Freigabe, abgebrochene System-Abfrage, unsicherer Ursprung.
|
|
throw new ZwischenablageFehler('Kein Zugriff auf die Zwischenablage - bitte "Datei auswählen".')
|
|
}
|
|
for (const eintrag of eintraege) {
|
|
const typ = eintrag.types.find((t) => t === "application/pdf")
|
|
if (typ) {
|
|
const blob = await eintrag.getType(typ)
|
|
// Ein Name muss sein: das Backend legt den Beleg unter ihm ab.
|
|
return new File([blob], "beleg.pdf", { type: "application/pdf" })
|
|
}
|
|
}
|
|
throw new ZwischenablageFehler(
|
|
'In der Zwischenablage liegt kein PDF - bitte den Beleg erneut kopieren oder "Datei auswählen".',
|
|
)
|
|
}
|
|
|
|
/** Tippgerät? Nur dort wird der Einfügen-Knopf angeboten (Begründung oben). */
|
|
export function zwischenablageKnopfSinnvoll(): boolean {
|
|
return typeof window !== "undefined" && !!window.matchMedia?.("(pointer: coarse)").matches
|
|
}
|