Phase 3.6: B+C+D komplett + HealthKit Sync
D.10 401-Handling: APIError.unauthorized, NotificationCenter-Bridge, AuthSession.logout() bei 401 → User landet wieder im Login D.12 PWA-Deep-Links: Settings-Section mit Forum/Hunde/Walks/Settings öffnet Safari per https://banyaro.app/#fragment B.4 Auto-Pause: 2-min-Inaktivität → isAutoPaused, automatischer Resume bei nächstem GPS-Update. Settings-Toggle, im UI eigenes Badge "Auto-Pause" (grau vs. Pause orange). C.7 Edit/Delete: RouteUpdateBody + APIClient.patch + APIClient.delete, EditRouteSheet (Name/Beschreibung/Public), Menu in Toolbar (nur eigene Touren), Alert für Delete. C.9 Statistik-Tab: neuer Tab "Statistik" zwischen Hunde und Mehr. Filtert /api/routes auf meine Touren, rechnet Woche/Monat/Allzeit (Distanz, Dauer, Touren), Längste Tour, aktuelle Streak (Tage in Folge). B.5 Walk-Review: Map-Header an die Spitze des FinishWalkSheet-Forms. B.6 Geo-Fotos: CapturedPhoto (Data + GPSPoint?), PhotoLocation @Model in SwiftData. Kamera während Walk taggt mit tracker.points.last. Nach Upload: foto_url aus Response → PhotoLocation persistiert. MiniRouteMap rendert Annotations mit Tap-Callback, PhotoViewerSheet zeigt Foto fullscreen. C.8 Share PNG+GPX: RouteShareImage (MKMapSnapshotter + Polyline overlay + SwiftUI ShareCard via ImageRenderer), GPXExporter (Tempfile mit XML), ShareSheet (UIActivityViewController-Wrapper), Menu in Route-Toolbar. D.11 Icon-Varianten: AppIcon-Dark (0.45 Brightness), AppIcon-Tinted (Grayscale + Kontrastverstärkung), Contents.json mit appearance entries. A.2 HealthKit: BanYaroGo.entitlements (com.apple.developer.healthkit), NSHealthShare/UpdateUsageDescription. WalkHealthSync.shared mit HKWorkoutBuilder (.walking) + HKWorkoutRouteBuilder, Timestamps gleichmäßig über Walk-Dauer verteilt. Settings-Toggle mit Permission-Request.
This commit is contained in:
parent
30e0fbe7ec
commit
c01e3d6be7
26 changed files with 978 additions and 28 deletions
|
|
@ -1,19 +1,37 @@
|
|||
import SwiftUI
|
||||
import MapKit
|
||||
|
||||
/// Non-interactive map showing a polyline for a GPS track. Suitable for
|
||||
/// list-row previews as well as larger detail headers.
|
||||
/// Non-interactive map showing a polyline for a GPS track. Optional photo
|
||||
/// annotations can be tapped to fire a callback.
|
||||
struct MiniRouteMap: View {
|
||||
let track: [GPSPoint]
|
||||
var lineWidth: CGFloat = 3
|
||||
var photoLocations: [PhotoLocation] = []
|
||||
var onPhotoTap: ((PhotoLocation) -> Void)? = nil
|
||||
|
||||
var body: some View {
|
||||
Map(initialPosition: .region(region)) {
|
||||
MapPolyline(coordinates: coordinates)
|
||||
.stroke(Color.accentColor, style: StrokeStyle(lineWidth: lineWidth, lineJoin: .round))
|
||||
|
||||
ForEach(photoLocations) { loc in
|
||||
Annotation("", coordinate: CLLocationCoordinate2D(latitude: loc.lat, longitude: loc.lon)) {
|
||||
Button {
|
||||
onPhotoTap?(loc)
|
||||
} label: {
|
||||
Image(systemName: "camera.circle.fill")
|
||||
.font(.title2)
|
||||
.foregroundStyle(.white, Color.accentColor)
|
||||
.background(.white, in: Circle())
|
||||
.shadow(radius: 2)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(onPhotoTap == nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
.mapStyle(.standard(elevation: .flat, pointsOfInterest: .excludingAll))
|
||||
.allowsHitTesting(false)
|
||||
.allowsHitTesting(onPhotoTap != nil)
|
||||
}
|
||||
|
||||
private var coordinates: [CLLocationCoordinate2D] {
|
||||
|
|
@ -31,7 +49,6 @@ struct MiniRouteMap: View {
|
|||
latitude: (minLat + maxLat) / 2,
|
||||
longitude: (minLon + maxLon) / 2
|
||||
)
|
||||
// Padding ~20% beyond bounding box; minimum span so very small tracks stay visible.
|
||||
let latDelta = max((maxLat - minLat) * 1.4, 0.002)
|
||||
let lonDelta = max((maxLon - minLon) * 1.4, 0.002)
|
||||
return MKCoordinateRegion(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue