05/04 Update the app functionalities

This commit is contained in:
Nguyen Ngo
2026-05-04 17:36:55 -04:00
parent 09715e9c11
commit 9d6b5e5bcb
7 changed files with 980 additions and 131 deletions
@@ -12,6 +12,7 @@
import SwiftUI
import PencilKit
import PhotosUI
// MARK: - FormFieldView
// Retained for standalone/legacy usage outside the grid inspection form.
@@ -420,8 +421,14 @@ struct ImageFieldView: View {
let currentValue: String
var onPhotoSelected: ((String) -> Void)?
@State private var showPicker = false
@State private var selectedImage: UIImage?
@State private var showChoice = false
@State private var showCamera = false
@State private var showLibrary = false
private var cameraAvailable: Bool {
UIImagePickerController.isSourceTypeAvailable(.camera)
}
var body: some View {
VStack(alignment: .leading, spacing: 10) {
@@ -445,7 +452,7 @@ struct ImageFieldView: View {
}
Button {
showPicker = true
if cameraAvailable { showChoice = true } else { showLibrary = true }
} label: {
Label(
selectedImage != nil || currentValue.hasPrefix("uploads/")
@@ -459,10 +466,17 @@ struct ImageFieldView: View {
}
.buttonStyle(.plain)
}
.sheet(isPresented: $showPicker) {
ImagePickerView(image: $selectedImage) { img in
saveAndCallback(img)
}
.confirmationDialog("Add Photo", isPresented: $showChoice, titleVisibility: .visible) {
Button("Take Photo") { showCamera = true }
Button("Photo Library") { showLibrary = true }
Button("Cancel", role: .cancel) {}
}
.fullScreenCover(isPresented: $showCamera) {
CameraPickerView(image: $selectedImage, onSelected: saveAndCallback)
.ignoresSafeArea()
}
.sheet(isPresented: $showLibrary) {
LibraryPickerView(image: $selectedImage, onSelected: saveAndCallback)
}
}
@@ -480,16 +494,24 @@ struct ImageFieldView: View {
}
// MARK: - ImagePickerView
// Retained as a thin typealias so existing call sites that reference
// ImagePickerView(image:onSelected:) continue to compile without changes.
// Internally it now just shows the library picker directly callers that
// need the camera+library choice should use the inline pattern in ImageFieldView.
// NOTE: FlagIssueView and CompactImageFieldView have been updated to use
// the inline confirmationDialog pattern instead.
typealias ImagePickerView = LibraryPickerView
struct ImagePickerView: UIViewControllerRepresentable {
// Camera UIImagePickerController with .camera source
struct CameraPickerView: UIViewControllerRepresentable {
@Binding var image: UIImage?
var onSelected: (UIImage) -> Void
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
let picker = UIImagePickerController()
picker.sourceType = .camera
picker.delegate = context.coordinator
picker.sourceType = UIImagePickerController.isSourceTypeAvailable(.camera)
? .camera : .photoLibrary
return picker
}
@@ -497,8 +519,8 @@ struct ImagePickerView: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator { Coordinator(self) }
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let parent: ImagePickerView
init(_ parent: ImagePickerView) { self.parent = parent }
let parent: CameraPickerView
init(_ parent: CameraPickerView) { self.parent = parent }
func imagePickerController(
_ picker: UIImagePickerController,
@@ -517,6 +539,45 @@ struct ImagePickerView: UIViewControllerRepresentable {
}
}
// Photo Library PHPickerViewController (no permission required)
struct LibraryPickerView: UIViewControllerRepresentable {
@Binding var image: UIImage?
var onSelected: (UIImage) -> Void
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration()
config.filter = .images
config.selectionLimit = 1
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ vc: PHPickerViewController, context: Context) {}
func makeCoordinator() -> Coordinator { Coordinator(self) }
class Coordinator: NSObject, PHPickerViewControllerDelegate {
let parent: LibraryPickerView
init(_ parent: LibraryPickerView) { self.parent = parent }
func picker(_ picker: PHPickerViewController,
didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider,
provider.canLoadObject(ofClass: UIImage.self) else { return }
provider.loadObject(ofClass: UIImage.self) { object, _ in
DispatchQueue.main.async {
if let img = object as? UIImage {
self.parent.image = img
self.parent.onSelected(img)
}
}
}
}
}
}
// MARK: - TableFieldView
struct TableFieldView: View {