Jul 21 - Update to support photos' timestamp/location
This commit is contained in:
@@ -643,11 +643,17 @@ struct ExecuteInspectionView: View {
|
||||
private func handlePhotoSelected(localPath: String, field: [String: Any]) {
|
||||
let fid = fieldId(field)
|
||||
formValues[fid] = "local://\(localPath)"
|
||||
// Stamp capture time + the current fix now; the upload may be hours
|
||||
// later on a slow sync and must not use its own clock.
|
||||
let fix = locationManager.lastLocation ?? PhotoLocationProvider.shared.lastLocation
|
||||
let photo = PendingPhoto(
|
||||
localFilePath: localPath,
|
||||
entityType: "inspection",
|
||||
entityLocalId: inspection.localId,
|
||||
fieldId: fid
|
||||
localFilePath: localPath,
|
||||
entityType: "inspection",
|
||||
entityLocalId: inspection.localId,
|
||||
fieldId: fid,
|
||||
capturedAt: Date(),
|
||||
captureLatitude: fix?.coordinate.latitude,
|
||||
captureLongitude: fix?.coordinate.longitude
|
||||
)
|
||||
inspection.pendingPhotos.append(photo)
|
||||
context.insert(photo)
|
||||
|
||||
@@ -24,7 +24,9 @@ struct FlagIssueView: View {
|
||||
@State private var description = ""
|
||||
|
||||
// Each entry: (UIImage for display, local file path for storage)
|
||||
@State private var photos: [(image: UIImage, path: String)] = []
|
||||
// CapturedPhoto records the capture moment + GPS fix at shutter time; the
|
||||
// `image` / `path` members match the tuple this replaced.
|
||||
@State private var photos: [CapturedPhoto] = []
|
||||
|
||||
@State private var showCamera = false
|
||||
@State private var showLibrary = false
|
||||
@@ -190,6 +192,9 @@ struct FlagIssueView: View {
|
||||
}
|
||||
.navigationTitle("Flag Issue")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
// Warm up GPS so a fix exists the instant a photo is taken; the
|
||||
// coordinates are burned into the photo server-side.
|
||||
.onAppear { PhotoLocationProvider.shared.start() }
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") { dismiss() }
|
||||
@@ -242,14 +247,14 @@ struct FlagIssueView: View {
|
||||
private func appendPhoto(_ img: UIImage) {
|
||||
guard photos.count < maxPhotos else { return }
|
||||
guard let path = savePhotoToDisk(img) else { return }
|
||||
photos.append((image: img, path: path))
|
||||
photos.append(CapturedPhoto(image: img, path: path))
|
||||
}
|
||||
|
||||
private func appendPhotos(_ images: [UIImage]) {
|
||||
for img in images {
|
||||
guard photos.count < maxPhotos else { break }
|
||||
guard let path = savePhotoToDisk(img) else { continue }
|
||||
photos.append((image: img, path: path))
|
||||
photos.append(CapturedPhoto(image: img, path: path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,9 +295,12 @@ struct FlagIssueView: View {
|
||||
// Create one PendingPhoto per photo so they all upload independently
|
||||
for photo in photos {
|
||||
let pending = PendingPhoto(
|
||||
localFilePath: photo.path,
|
||||
entityType: "issue",
|
||||
entityLocalId: issue.localId
|
||||
localFilePath: photo.path,
|
||||
entityType: "issue",
|
||||
entityLocalId: issue.localId,
|
||||
capturedAt: photo.capturedAt,
|
||||
captureLatitude: photo.latitude,
|
||||
captureLongitude: photo.longitude
|
||||
)
|
||||
context.insert(pending)
|
||||
}
|
||||
|
||||
@@ -240,7 +240,8 @@ struct IssueDetailView: View {
|
||||
@State private var statusError: String?
|
||||
@State private var showStatusPicker = false
|
||||
// ── Resolution Photos ─────────────────────────────────────────────────
|
||||
@State private var resultPhotos: [(image: UIImage, path: String)] = []
|
||||
// Resolution photos are evidence too — record capture time + GPS at shutter.
|
||||
@State private var resultPhotos: [CapturedPhoto] = []
|
||||
@State private var showResultCamera = false
|
||||
@State private var showResultLibrary = false
|
||||
@State private var isUploadingResultPhotos = false
|
||||
@@ -641,6 +642,8 @@ struct IssueDetailView: View {
|
||||
}
|
||||
.navigationTitle("Issue Detail")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
// Warm up GPS so resolution photos taken here carry coordinates.
|
||||
.onAppear { PhotoLocationProvider.shared.start() }
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button {
|
||||
@@ -981,14 +984,14 @@ struct IssueDetailView: View {
|
||||
private func appendResultPhoto(_ img: UIImage) {
|
||||
guard resultPhotos.count < maxResultPhotos,
|
||||
let path = saveResultPhotoToDisk(img) else { return }
|
||||
resultPhotos.append((image: img, path: path))
|
||||
resultPhotos.append(CapturedPhoto(image: img, path: path))
|
||||
}
|
||||
|
||||
private func appendResultPhotos(_ images: [UIImage]) {
|
||||
for img in images {
|
||||
guard resultPhotos.count < maxResultPhotos,
|
||||
let path = saveResultPhotoToDisk(img) else { break }
|
||||
resultPhotos.append((image: img, path: path))
|
||||
resultPhotos.append(CapturedPhoto(image: img, path: path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1024,7 +1027,12 @@ struct IssueDetailView: View {
|
||||
do {
|
||||
var serverPaths: [String] = []
|
||||
for photo in resultPhotos {
|
||||
let path = try await APIClient.shared.uploadResultPhoto(localPath: photo.path)
|
||||
let path = try await APIClient.shared.uploadResultPhoto(
|
||||
localPath: photo.path,
|
||||
capturedAt: photo.capturedAt,
|
||||
latitude: photo.latitude,
|
||||
longitude: photo.longitude
|
||||
)
|
||||
serverPaths.append(path)
|
||||
}
|
||||
|
||||
@@ -1066,7 +1074,9 @@ struct StandaloneIssueView: View {
|
||||
|
||||
@State private var severity = "medium"
|
||||
@State private var description = ""
|
||||
@State private var photos: [(image: UIImage, path: String)] = []
|
||||
// CapturedPhoto records the capture moment + GPS fix at shutter time; the
|
||||
// `image` / `path` members match the tuple this replaced.
|
||||
@State private var photos: [CapturedPhoto] = []
|
||||
@State private var showCamera = false
|
||||
@State private var showLibrary = false
|
||||
@State private var showBanner = false
|
||||
@@ -1238,6 +1248,9 @@ struct StandaloneIssueView: View {
|
||||
}
|
||||
.navigationTitle("New Issue")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
// Warm up GPS so a fix exists the instant a photo is taken; the
|
||||
// coordinates are burned into the photo server-side.
|
||||
.onAppear { PhotoLocationProvider.shared.start() }
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") { dismiss() }
|
||||
@@ -1284,13 +1297,13 @@ struct StandaloneIssueView: View {
|
||||
|
||||
private func appendPhoto(_ img: UIImage) {
|
||||
guard photos.count < maxPhotos, let path = savePhotoToDisk(img) else { return }
|
||||
photos.append((image: img, path: path))
|
||||
photos.append(CapturedPhoto(image: img, path: path))
|
||||
}
|
||||
|
||||
private func appendPhotos(_ images: [UIImage]) {
|
||||
for img in images {
|
||||
guard photos.count < maxPhotos, let path = savePhotoToDisk(img) else { break }
|
||||
photos.append((image: img, path: path))
|
||||
photos.append(CapturedPhoto(image: img, path: path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1325,9 +1338,12 @@ struct StandaloneIssueView: View {
|
||||
|
||||
for photo in photos {
|
||||
let pending = PendingPhoto(
|
||||
localFilePath: photo.path,
|
||||
entityType: "issue",
|
||||
entityLocalId: issue.localId
|
||||
localFilePath: photo.path,
|
||||
entityType: "issue",
|
||||
entityLocalId: issue.localId,
|
||||
capturedAt: photo.capturedAt,
|
||||
captureLatitude: photo.latitude,
|
||||
captureLongitude: photo.longitude
|
||||
)
|
||||
context.insert(pending)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user