Add camera functionality for task completion photos
- Add camera picker interface to common ImagePicker.kt - Implement camera capture for Android using ActivityResultContracts.TakePicture - Implement camera capture for iOS using UIImagePickerController - Add camera picker stubs for wasmJs, jsMain, and jvmMain platforms - Update CompleteTaskDialog to show both "Take Photo" and "Choose from Library" buttons - Add camera permissions to Android manifest (CAMERA permission, camera intent queries) - Configure Android FileProvider for camera photo sharing - Add camera and photo library usage descriptions to iOS Info.plist - Update iOS CompleteTaskView with native camera picker support - Fix cancel button in CompleteTaskView using @Environment(\.dismiss) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,9 +4,9 @@ import ComposeApp
|
||||
|
||||
struct CompleteTaskView: View {
|
||||
let task: TaskDetail
|
||||
@Binding var isPresented: Bool
|
||||
let onComplete: () -> Void
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@StateObject private var taskViewModel = TaskViewModel()
|
||||
@State private var completedByName: String = ""
|
||||
@State private var actualCost: String = ""
|
||||
@@ -17,6 +17,7 @@ struct CompleteTaskView: View {
|
||||
@State private var isSubmitting: Bool = false
|
||||
@State private var showError: Bool = false
|
||||
@State private var errorMessage: String = ""
|
||||
@State private var showCamera: Bool = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
@@ -127,17 +128,28 @@ struct CompleteTaskView: View {
|
||||
// Images Section
|
||||
Section {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
PhotosPicker(
|
||||
selection: $selectedItems,
|
||||
maxSelectionCount: 5,
|
||||
matching: .images,
|
||||
photoLibrary: .shared()
|
||||
) {
|
||||
Label("Add Photos", systemImage: "photo.on.rectangle.angled")
|
||||
.frame(maxWidth: .infinity)
|
||||
.foregroundStyle(.blue)
|
||||
HStack(spacing: 12) {
|
||||
Button(action: {
|
||||
showCamera = true
|
||||
}) {
|
||||
Label("Take Photo", systemImage: "camera")
|
||||
.frame(maxWidth: .infinity)
|
||||
.foregroundStyle(.blue)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
||||
PhotosPicker(
|
||||
selection: $selectedItems,
|
||||
maxSelectionCount: 5,
|
||||
matching: .images,
|
||||
photoLibrary: .shared()
|
||||
) {
|
||||
Label("Library", systemImage: "photo.on.rectangle.angled")
|
||||
.frame(maxWidth: .infinity)
|
||||
.foregroundStyle(.blue)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.onChange(of: selectedItems) { newItems in
|
||||
Task {
|
||||
selectedImages = []
|
||||
@@ -200,7 +212,7 @@ struct CompleteTaskView: View {
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
isPresented = false
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,6 +221,13 @@ struct CompleteTaskView: View {
|
||||
} message: {
|
||||
Text(errorMessage)
|
||||
}
|
||||
.sheet(isPresented: $showCamera) {
|
||||
CameraPickerView { image in
|
||||
if selectedImages.count < 5 {
|
||||
selectedImages.append(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +284,7 @@ struct CompleteTaskView: View {
|
||||
DispatchQueue.main.async {
|
||||
if result is ApiResultSuccess<TaskCompletion> {
|
||||
self.isSubmitting = false
|
||||
self.isPresented = false
|
||||
self.dismiss()
|
||||
self.onComplete()
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
@@ -322,3 +341,41 @@ struct ImageThumbnailView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Camera Picker View Component
|
||||
struct CameraPickerView: UIViewControllerRepresentable {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
let onImageCaptured: (UIImage) -> Void
|
||||
|
||||
func makeUIViewController(context: Context) -> UIImagePickerController {
|
||||
let picker = UIImagePickerController()
|
||||
picker.sourceType = .camera
|
||||
picker.delegate = context.coordinator
|
||||
return picker
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
||||
let parent: CameraPickerView
|
||||
|
||||
init(_ parent: CameraPickerView) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
||||
if let image = info[.originalImage] as? UIImage {
|
||||
parent.onImageCaptured(image)
|
||||
}
|
||||
parent.dismiss()
|
||||
}
|
||||
|
||||
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
|
||||
parent.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user