diff --git a/BanYaroGo/Views/AddDiaryEntrySheet.swift b/BanYaroGo/Views/AddDiaryEntrySheet.swift index af1a47a..0f7483b 100644 --- a/BanYaroGo/Views/AddDiaryEntrySheet.swift +++ b/BanYaroGo/Views/AddDiaryEntrySheet.swift @@ -1,5 +1,6 @@ import SwiftUI import PhotosUI +import CoreLocation struct AddDiaryEntrySheet: View { let dogId: Int @@ -17,6 +18,10 @@ struct AddDiaryEntrySheet: View { @State private var photoSelection: [PhotosPickerItem] = [] @State private var photoData: [Data] = [] + @State private var location = OneShotLocation() + @State private var attachLocation = true + @State private var isReverseGeocoding = false + @State private var saveState: SaveState = .idle @State private var errorMessage: String? @@ -42,12 +47,55 @@ struct AddDiaryEntrySheet: View { } Section { Toggle("Meilenstein", isOn: $isMilestone) - TextField("Ort (optional)", text: $locationName) TextField("Tags (komma-getrennt)", text: $tagsInput) .textInputAutocapitalization(.never) .autocorrectionDisabled() } + Section { + Toggle(isOn: $attachLocation) { + Label("Standort verwenden", systemImage: "location.fill") + } + if attachLocation { + if let coord = location.coordinate { + HStack { + Image(systemName: "mappin.and.ellipse") + .foregroundStyle(Color.accentColor) + VStack(alignment: .leading, spacing: 2) { + if !locationName.isEmpty { + Text(locationName).font(.subheadline) + } else if isReverseGeocoding { + Text("Suche Ortsbezeichnung…") + .font(.caption) + .foregroundStyle(.secondary) + } else { + Text("Ort wird mitgespeichert") + .font(.subheadline) + } + Text(String(format: "%.5f, %.5f", coord.latitude, coord.longitude)) + .font(.caption2.monospacedDigit()) + .foregroundStyle(.tertiary) + } + } + } else if location.error != nil { + Label("Standort nicht verfügbar — wird ohne gespeichert", systemImage: "location.slash") + .font(.caption) + .foregroundStyle(.secondary) + } else { + HStack { + ProgressView() + Text("Hole Standort…").font(.caption).foregroundStyle(.secondary) + } + } + TextField("Ort überschreiben (optional)", text: $locationName) + } + } header: { + Text("Standort") + } footer: { + Text("Der Standort wird zusammen mit dem Eintrag gespeichert — banyaro.app rendert daraus deine POI-Karte.") + .font(.caption2) + } + Section { PhotosPicker( selection: $photoSelection, @@ -93,10 +141,37 @@ struct AddDiaryEntrySheet: View { .onChange(of: photoSelection) { _, items in Task { await loadPhotos(from: items) } } + .task { location.request() } + .onChange(of: location.coordinate?.latitude) { _, _ in + guard let coord = location.coordinate, locationName.isEmpty else { return } + Task { await reverseGeocode(coord) } + } .interactiveDismissDisabled(saveState != .idle) } } + private func reverseGeocode(_ coord: CLLocationCoordinate2D) async { + isReverseGeocoding = true + defer { isReverseGeocoding = false } + let geocoder = CLGeocoder() + let loc = CLLocation(latitude: coord.latitude, longitude: coord.longitude) + do { + let placemarks = try await geocoder.reverseGeocodeLocation(loc, preferredLocale: Locale(identifier: "de_DE")) + guard let p = placemarks.first else { return } + // Sample: "Café Kaiser, Berlin" — name first, then locality fallback + if let name = p.name, !name.isEmpty { + locationName = name + return + } + var parts: [String] = [] + if let street = p.thoroughfare { parts.append(street) } + if let city = p.locality { parts.append(city) } + if !parts.isEmpty { locationName = parts.joined(separator: ", ") } + } catch { + print("Reverse geocode failed: \(error)") + } + } + @ViewBuilder private var saveToolbarItem: some View { switch saveState { @@ -132,14 +207,15 @@ struct AddDiaryEntrySheet: View { .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } + let coord = attachLocation ? location.coordinate : nil let body = DiaryCreateBody( datum: formatter.string(from: date), typ: isMilestone ? "meilenstein" : "eintrag", titel: titel.trimmingCharacters(in: .whitespaces).isEmpty ? nil : titel.trimmingCharacters(in: .whitespaces), text: text.trimmingCharacters(in: .whitespaces).isEmpty ? nil : text.trimmingCharacters(in: .whitespacesAndNewlines), tags: tags.isEmpty ? nil : tags, - gpsLat: nil, - gpsLon: nil, + gpsLat: coord?.latitude, + gpsLon: coord?.longitude, locationName: locationName.trimmingCharacters(in: .whitespaces).isEmpty ? nil : locationName, isMilestone: isMilestone )