Files
letro-ios/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift
Stefan Ceriu 0915cb81a8 Multi file uploads (#4358)
* Allow MediaPickerScreen users to select the media selection mode (single or multiple)

* Fix cancellation

* Add support for multiple media URLs on the MediaUploadPreviewScreen.

* Support processing more URLs on the `MediaUploadingPreprocessor` and sending more on the `MediaUploadPreviewScreen`

* Add feature flag for `multipleAttachmentUploadEnabled`

* Add a label showing the current preview item index in the MediaUploadPreviewScreen

* Add support for dragging and dropping or pasting multiple items at the same time.

* Support sharing more than one file through the share extension.

* Limit the number of items that can be shared in one go to 5.

* Fix unit tests

* Fix incorrect fatal error when dealing with single selection media pickers.

* Document the `multipleAttachmentUploadEnabled` usage in the context of the MediaPicker.

* Use a task group for processing selected media in the photo library picker.

* Use a task group for processing multiple selected media in the MediaUploadingPreprocessor

* Switch the maximum number of items that can be shared to 10.

* Allow multiple items to be pasted at the same time.
2025-07-30 15:44:05 +03:00

90 lines
3.3 KiB
Swift

//
// Copyright 2023, 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import SwiftUI
enum CameraPickerAction {
case selectFile(URL)
case cancel
case error(CameraPickerError)
}
enum CameraPickerError: Error {
case invalidJpegData
case invalidOriginalImage
case failedWritingToTemporaryDirectory
}
struct CameraPicker: UIViewControllerRepresentable {
private let userIndicatorController: UserIndicatorControllerProtocol
private let callback: (CameraPickerAction) -> Void
init(userIndicatorController: UserIndicatorControllerProtocol,
callback: @escaping (CameraPickerAction) -> Void) {
self.userIndicatorController = userIndicatorController
self.callback = callback
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .camera
imagePicker.allowsEditing = false
imagePicker.delegate = context.coordinator
if let mediaTypes = UIImagePickerController.availableMediaTypes(for: .camera) {
imagePicker.mediaTypes = mediaTypes
}
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
private var cameraPicker: CameraPicker
init(_ cameraPicker: CameraPicker) {
self.cameraPicker = cameraPicker
}
private static let loadingIndicatorIdentifier = "\(CameraPicker.self)-Loading"
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
picker.delegate = nil
cameraPicker.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading))
defer {
cameraPicker.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
}
if let videoURL = info[.mediaURL] as? URL {
cameraPicker.callback(.selectFile(videoURL))
} else if let image = info[.originalImage] as? UIImage {
guard let jpegData = image.jpegData(compressionQuality: 1.0) else {
cameraPicker.callback(.error(.invalidJpegData))
return
}
let fileName = "\(Date.now.formatted(.iso8601.dateSeparator(.omitted).timeSeparator(.omitted))).jpg"
do {
let url = try FileManager.default.writeDataToTemporaryDirectory(data: jpegData, fileName: fileName)
cameraPicker.callback(.selectFile(url))
} catch {
cameraPicker.callback(.error(.failedWritingToTemporaryDirectory))
}
} else {
cameraPicker.callback(.error(.invalidOriginalImage))
}
}
}
}