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.
This commit is contained in:
Stefan Ceriu
2025-07-30 15:44:05 +03:00
committed by GitHub
parent 01033cf53e
commit df335bd2b1
39 changed files with 466 additions and 266 deletions
@@ -15,6 +15,7 @@ struct UserDetailsEditScreenCoordinatorParameters {
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
weak var navigationStackCoordinator: NavigationStackCoordinator?
let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings
}
final class UserDetailsEditScreenCoordinator: CoordinatorProtocol {
@@ -36,11 +37,11 @@ final class UserDetailsEditScreenCoordinator: CoordinatorProtocol {
.sink { [weak self] action in
switch action {
case .displayCameraPicker:
self?.displayMediaPickerWithSource(.camera)
self?.displayMediaPickerWithMode(.init(source: .camera, selectionType: .single))
case .displayMediaPicker:
self?.displayMediaPickerWithSource(.photoLibrary)
self?.displayMediaPickerWithMode(.init(source: .photoLibrary, selectionType: .single))
case .displayFilePicker:
self?.displayMediaPickerWithSource(.documents)
self?.displayMediaPickerWithMode(.init(source: .documents, selectionType: .single))
}
}
.store(in: &cancellables)
@@ -52,15 +53,23 @@ final class UserDetailsEditScreenCoordinator: CoordinatorProtocol {
// MARK: Private
private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) {
private func displayMediaPickerWithMode(_ mode: MediaPickerScreenMode) {
let stackCoordinator = NavigationStackCoordinator()
let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: parameters.userIndicatorController, source: source, orientationManager: parameters.orientationManager) { [weak self] action in
let mediaPickerCoordinator = MediaPickerScreenCoordinator(mode: mode,
appSettings: parameters.appSettings,
userIndicatorController: parameters.userIndicatorController,
orientationManager: parameters.orientationManager) { [weak self] action in
guard let self else { return }
switch action {
case .cancel:
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
case .selectMediaAtURL(let url):
case .selectedMediaAtURLs(let urls):
guard urls.count == 1,
let url = urls.first else {
fatalError("Received an invalid number of URLs")
}
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
viewModel.didSelectMediaURL(url: url)
}