1.1: Offline-Cache + Outbox für Touren/Tagebuch, WeatherKit-Fix, Aufräumen
App-Review-Fix (Guideline 2.1 WeatherKit): - OneShotLocation: deterministisches async resolve() mit 10s-Timeout statt onChange-Lauschen; WetterView lädt bei fehlendem Standort einen Berlin-Fallback → kein ewiges Hängen bei "Hole Standort…", WeatherKit ist immer sichtbar. Offline-Lesen (SwiftData): - CachedRoute/CachedDiaryEntry/CachedImage + CachedAsyncImage: Touren, Tagebuch und Fotos werden cache-first geladen und sind offline verfügbar. - Cache wird bei Logout/401 geleert (RootView), kein Durchschimmern fremder User. Offline-Speichern (Outbox): - PendingRoute/PendingRoutePhoto: Tour inkl. unterwegs hinzugefügter Fotos wird offline lokal gesichert und automatisch hochgeladen (Touren-Tab + App-Start). - Touren-Liste zeigt offline gesicherte Touren mit "wird hochgeladen"-Badge. FinishWalkSheet: - Dismiss-Schutz: Speichern-Dialog lässt sich nicht mehr wegwischen — eine aufgezeichnete Tour geht nicht mehr durch Runterwischen verloren. Wetter: - Ortslabel (Reverse-Geocoding; Fallback "Berlin · Näherung"). - Saubere Offline-Meldung statt rohem networkError. Aufräumen: - Doppeltes "Gassi-Treffen" im Mehr-Tab entfernt. - Veraltete Phase-1/2-Texte neu getextet. - Tote DogsListView gelöscht (Hund-Wechsel läuft über den Heim-Picker).
This commit is contained in:
parent
9e51f3910e
commit
a2646a18ef
16 changed files with 769 additions and 199 deletions
|
|
@ -8,6 +8,7 @@ struct RouteDetailView: View {
|
|||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(AuthSession.self) private var auth
|
||||
@Environment(\.modelContext) private var ctx
|
||||
@Query private var allPhotoLocations: [PhotoLocation]
|
||||
|
||||
@State private var detail: RouteDetail?
|
||||
|
|
@ -218,27 +219,32 @@ struct RouteDetailView: View {
|
|||
}
|
||||
|
||||
private func photoThumb(_ path: String) -> some View {
|
||||
let url = URL(string: "https://banyaro.app\(path)")
|
||||
return AsyncImage(url: url) { phase in
|
||||
switch phase {
|
||||
case .success(let img):
|
||||
img.resizable().scaledToFill()
|
||||
default:
|
||||
Rectangle().fill(.gray.opacity(0.15))
|
||||
}
|
||||
CachedAsyncImage(path: path) { img in
|
||||
img.resizable().scaledToFill()
|
||||
} placeholder: {
|
||||
Rectangle().fill(.gray.opacity(0.15))
|
||||
}
|
||||
.frame(width: 160, height: 160)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
}
|
||||
|
||||
private func load() async {
|
||||
isLoading = true
|
||||
// 1) Cache-first: gespeicherten Detail-Stand zeigen (auch offline)
|
||||
if detail == nil, let cached = OfflineCache.cachedRouteDetail(id: routeId, in: ctx) {
|
||||
detail = cached
|
||||
}
|
||||
isLoading = (detail == nil)
|
||||
errorMessage = nil
|
||||
defer { isLoading = false }
|
||||
// 2) Netzwerk → Cache aktualisieren; bei Fehler bleibt der Cache stehen
|
||||
do {
|
||||
detail = try await APIClient.shared.get("/api/routes/\(routeId)")
|
||||
let fetched: RouteDetail = try await APIClient.shared.get("/api/routes/\(routeId)")
|
||||
detail = fetched
|
||||
OfflineCache.upsertRouteDetail(fetched, in: ctx)
|
||||
let paths = fetched.fotoUrls ?? []
|
||||
Task { await OfflineCache.prefetchImages(paths: paths, in: ctx) }
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
if detail == nil { errorMessage = error.localizedDescription }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,17 +313,11 @@ private struct PhotoViewerSheet: View {
|
|||
NavigationStack {
|
||||
ZStack {
|
||||
Color.black.ignoresSafeArea()
|
||||
if let url = URL(string: "https://banyaro.app\(path)") {
|
||||
AsyncImage(url: url) { phase in
|
||||
switch phase {
|
||||
case .success(let img):
|
||||
img.resizable().scaledToFit()
|
||||
case .failure:
|
||||
ContentUnavailableView("Foto nicht ladbar", systemImage: "photo.badge.exclamationmark")
|
||||
.foregroundStyle(.white)
|
||||
default:
|
||||
ProgressView().tint(.white)
|
||||
}
|
||||
if !path.isEmpty {
|
||||
CachedAsyncImage(path: path) { img in
|
||||
img.resizable().scaledToFit()
|
||||
} placeholder: {
|
||||
ProgressView().tint(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue