b1411625aa
Share-Erweiterung: ein PDF aus Mail landet ueber Teilen -> DataMetric360 als Tankbeleg. Alles Native liegt versioniert unter companion-app/native/, weil ios/ gitignored ist und npx cap add ios es sonst wieder frisst; scripts/ios-teilen-einrichten.mjs haengt es bei jedem Bau ins Xcode-Projekt und ist in ios-signieren.sh eingehaengt. Der pbxproj-Teil ist auf keinem Mac erprobt - er bricht vor dem Schreiben ab, und docs/SHARE_EXTENSION.md beschreibt dieselben Handgriffe fuer Xcode. "In den Kalender uebernehmen" ging in der App nie: WKWebView ignoriert das download-Attribut, der Klick lief ins Leere. Auf dem Geraet jetzt @capacitor/filesystem plus @capacitor/share. useTheme/useBoden legten je Aufrufer eigenen Zustand an - fuenf Kopien. Der Schalter aenderte nur seine eigene, data-theme in App.tsx blieb stehen, die Tag/Nacht-Umschaltung wirkte erst nach einem Neustart. Jetzt ein Speicher je Einstellung ueber useSyncExternalStore. Zwei Regressionstests, gegen den alten Stand als fehlschlagend nachgewiesen. Alle Popups nach Apple HIG: neue Bausteine Sheet und ActionSheet, blickdicht und mit abgedunkeltem Schleier, in beiden Codebasen. Popup portalierte nach document.body - ausserhalb von [data-theme] - und war deshalb im Tagmodus fast unsichtbar; Portal zielt jetzt auf .ads-root. ImagePlaceholder merkte sich "fehlgeschlagen" ohne die Adresse: nach einem leeren Galerieplatz zeigten auch die mit Foto nur noch den Platzhalter. Vier Regressionstests, ebenfalls gegen den alten Stand geprueft. Bildadressen tragen jetzt einen Cache-Brecher - HA liefert /local/ mit 31 Tagen Cache-Vorgabe aus, im Panel eingefuegte Radfotos erschienen in der App deshalb nicht. uebersichtsbild: Panel speichert den Namen, die App verglich gegen den Dateinamen - der Vergleich traf nie zu. Kanonisch ist der Name. Weiter: Navigationspfeil statt gleichschenkligem Dreieck auf der Streckenlinie (die Kerbe unterscheidet Kopf und Ende), CI-Symbol tour-s als Fahrten-Icon, Datumsfelder zeigen ihren Wert ohne erstes Antippen, feste Beispielnamen im Setup, Kopf-Gegengewicht fuer mittige Titel, Standort-Blatt mit festem Fussabstand, NSLocationWhenInUseUsageDescription, watchPosition fuer die Live-Ortung, Art-Pille wieder als Knopf, Zwischenablage fuer Tankbelege. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
/**
|
|
* „Beleg hochladen" — Vorlage: `vBelegPopup()` im Panel.
|
|
*
|
|
* Bis 2026-08-31 öffnete der Beleg-Knopf in der App direkt die Dateiauswahl.
|
|
* Auf dem iPhone landete man damit im Dateispeicher, und ein aus der Mail
|
|
* kopiertes PDF war überhaupt nicht einzufügen — im Panel gab es diesen Weg
|
|
* längst. Geteilt zwischen dem Anlegen (Tanken.tsx) und dem Nachtragen
|
|
* (TankDetail.tsx), damit beide nicht auseinanderlaufen.
|
|
*
|
|
* Ein Auswahlblatt, kein Popup: es ist eine kurze Liste von Wegen zum selben
|
|
* Ziel, und genau dafür sieht Apple das Aktionsblatt vor. „Abbrechen" steht
|
|
* abgesetzt in eigener Karte — das macht es zum Ausweg statt zur vierten
|
|
* Wahlmöglichkeit.
|
|
*
|
|
* Welche Wege angeboten werden, entscheidet das Gerät, nicht der Geschmack:
|
|
* am Telefon ist Einfügen der Hauptweg, am Rechner Ziehen bzw. Dateiauswahl.
|
|
* Begründung in daten/zwischenablage.ts.
|
|
*/
|
|
|
|
import { useRef, useState } from "react"
|
|
|
|
import { ActionSheet } from "@audi-dash/ui"
|
|
|
|
import {
|
|
ZwischenablageFehler,
|
|
pdfAusDateiliste,
|
|
pdfAusZwischenablage,
|
|
zwischenablageKnopfSinnvoll,
|
|
} from "../daten/zwischenablage"
|
|
|
|
export function BelegPopup({
|
|
offen,
|
|
beiAbbruch,
|
|
beiDatei,
|
|
}: {
|
|
offen: boolean
|
|
beiAbbruch: () => void
|
|
/** Bekommt das PDF. Das Blatt schließt sich vorher selbst. */
|
|
beiDatei: (datei: File) => void
|
|
}) {
|
|
const dateiwahl = useRef<HTMLInputElement | null>(null)
|
|
const [fehler, setzeFehler] = useState<string | null>(null)
|
|
const [darueber, setzeDarueber] = useState(false)
|
|
// Einmal beim Aufbau bestimmt: ein Gerätewechsel mitten im Dialog gibt es
|
|
// nicht, und ein bei jedem Rendern neu befragtes matchMedia wäre nur Aufwand.
|
|
const [telefon] = useState(zwischenablageKnopfSinnvoll)
|
|
|
|
const uebernehmen = (datei: File) => {
|
|
setzeFehler(null)
|
|
beiDatei(datei)
|
|
}
|
|
|
|
const einfuegen = async () => {
|
|
try {
|
|
uebernehmen(await pdfAusZwischenablage())
|
|
} catch (f) {
|
|
setzeFehler(
|
|
f instanceof ZwischenablageFehler
|
|
? f.message
|
|
: 'Die Zwischenablage ließ sich nicht lesen - bitte "Datei auswählen".',
|
|
)
|
|
}
|
|
}
|
|
|
|
const aktionen = [
|
|
...(telefon
|
|
? [{ label: "Aus Zwischenablage einfügen", onClick: () => void einfuegen() }]
|
|
: []),
|
|
{ label: "Datei auswählen", onClick: () => dateiwahl.current?.click() },
|
|
]
|
|
|
|
// Nur wenn es wirklich etwas zu zeigen gibt - sonst entstuende im Blatt
|
|
// eine leere Zeile samt Trennlinie.
|
|
const zusatz =
|
|
!telefon || fehler ? (
|
|
<>
|
|
{!telefon && (
|
|
<div
|
|
className={`dm-beleg-drop${darueber ? " dm-beleg-drop--darueber" : ""}`}
|
|
onDragOver={(e) => {
|
|
e.preventDefault()
|
|
setzeDarueber(true)
|
|
}}
|
|
onDragLeave={() => setzeDarueber(false)}
|
|
onDrop={(e) => {
|
|
e.preventDefault()
|
|
setzeDarueber(false)
|
|
const datei = pdfAusDateiliste(e.dataTransfer?.files)
|
|
if (datei) uebernehmen(datei)
|
|
else setzeFehler("Das war kein PDF - der Beleg muss als PDF vorliegen.")
|
|
}}
|
|
>
|
|
<strong>PDF hierher ziehen</strong>
|
|
<span>oder unten auswählen</span>
|
|
</div>
|
|
)}
|
|
{fehler && (
|
|
<p className="dm-fehler" role="alert" style={{ margin: 0 }}>
|
|
{fehler}
|
|
</p>
|
|
)}
|
|
</>
|
|
) : undefined
|
|
|
|
return (
|
|
<>
|
|
{/* Ausserhalb des Blattes: unsichtbar, muss aber im Baum stehen, damit
|
|
der Verweis darauf zeigt. */}
|
|
<input
|
|
ref={dateiwahl}
|
|
type="file"
|
|
accept="application/pdf,.pdf"
|
|
hidden
|
|
onChange={(e) => {
|
|
const datei = e.target.files?.[0]
|
|
e.target.value = ""
|
|
if (datei) uebernehmen(datei)
|
|
}}
|
|
/>
|
|
{/* Ohne Ueberschrift und ohne Erklaerzeile: wer den Knopf 'Beleg
|
|
hochladen' gedrueckt hat, weiss bereits, was hier passiert - der
|
|
Kopf haette es nur wiederholt (Nutzerhinweis 2026-08-31). Die
|
|
beiden Wege benennen sich selbst. */}
|
|
<ActionSheet open={offen} onClose={beiAbbruch} actions={aktionen}>
|
|
{zusatz}
|
|
</ActionSheet>
|
|
</>
|
|
)
|
|
}
|