* 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.
90 lines
3.3 KiB
Swift
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))
|
|
}
|
|
}
|
|
}
|
|
}
|