Teilen-Blatt, HIG-Blaetter, geteilte Ansichtseinstellungen (2026.8.31.1)
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>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* „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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user