diff --git a/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift index 05f351555..102513640 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift @@ -29,9 +29,11 @@ enum CameraPickerError: Error { } struct CameraPicker: UIViewControllerRepresentable { + private weak var userIndicatorController: UserIndicatorControllerProtocol? private let callback: (CameraPickerAction) -> Void - init(callback: @escaping (CameraPickerAction) -> Void) { + init(userIndicatorController: UserIndicatorControllerProtocol?, callback: @escaping (CameraPickerAction) -> Void) { + self.userIndicatorController = userIndicatorController self.callback = callback } @@ -61,7 +63,16 @@ struct CameraPicker: UIViewControllerRepresentable { self.cameraPicker = cameraPicker } + private static let loadingIndicatorIdentifier = "CameraPickerLoadingIndicator" + 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 { diff --git a/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift index 9d53aa351..925093b4c 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift @@ -27,9 +27,11 @@ enum DocumentPickerError: Error { } struct DocumentPicker: UIViewControllerRepresentable { + private weak var userIndicatorController: UserIndicatorControllerProtocol? private let callback: (DocumentPickerAction) -> Void - init(callback: @escaping (DocumentPickerAction) -> Void) { + init(userIndicatorController: UserIndicatorControllerProtocol?, callback: @escaping (DocumentPickerAction) -> Void) { + self.userIndicatorController = userIndicatorController self.callback = callback } @@ -60,11 +62,21 @@ struct DocumentPicker: UIViewControllerRepresentable { documentPicker.callback(.cancel) } - func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + private static let loadingIndicatorIdentifier = "DocumentPickerLoadingIndicator" + + func documentPicker(_ picker: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let url = urls.first else { documentPicker.callback(.error(DocumentPickerError.unknown)) return } + + picker.delegate = nil + + documentPicker.userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) + defer { + documentPicker.userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + } + do { let newURL = try FileManager.default.copyFileToTemporaryDirectory(file: url) documentPicker.callback(.selectFile(newURL)) diff --git a/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift b/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift index 950f1d034..d870a8870 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift @@ -48,7 +48,7 @@ class MediaPickerScreenCoordinator: CoordinatorProtocol { case .camera: cameraPicker case .photoLibrary: - PhotoLibraryPicker { [weak self] action in + PhotoLibraryPicker(userIndicatorController: userIndicatorController) { [weak self] action in switch action { case .cancel: self?.callback?(.cancel) @@ -62,7 +62,7 @@ class MediaPickerScreenCoordinator: CoordinatorProtocol { case .documents: // The document picker automatically dismisses everything on selection // Strongly retain self in the callback to forward actions correctly - DocumentPicker { action in + DocumentPicker(userIndicatorController: userIndicatorController) { action in switch action { case .cancel: self.callback?(.cancel) @@ -77,7 +77,7 @@ class MediaPickerScreenCoordinator: CoordinatorProtocol { } private var cameraPicker: some View { - CameraPicker { [weak self] action in + CameraPicker(userIndicatorController: userIndicatorController) { [weak self] action in switch action { case .cancel: self?.callback?(.cancel) diff --git a/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift index 38c4a5a87..601c90ea7 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift @@ -29,9 +29,11 @@ enum PhotoLibraryPickerError: Error { } struct PhotoLibraryPicker: UIViewControllerRepresentable { + private weak var userIndicatorController: UserIndicatorControllerProtocol? private let callback: (PhotoLibraryPickerAction) -> Void - init(callback: @escaping (PhotoLibraryPickerAction) -> Void) { + init(userIndicatorController: UserIndicatorControllerProtocol?, callback: @escaping (PhotoLibraryPickerAction) -> Void) { + self.userIndicatorController = userIndicatorController self.callback = callback } @@ -60,6 +62,8 @@ struct PhotoLibraryPicker: UIViewControllerRepresentable { // MARK: PHPickerViewControllerDelegate + private static let loadingIndicatorIdentifier = "PhotoLibraryPickerLoadingIndicator" + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { guard let provider = results.first?.itemProvider, let contentType = provider.preferredContentType else { @@ -67,6 +71,13 @@ struct PhotoLibraryPicker: UIViewControllerRepresentable { return } + picker.delegate = nil + + photoLibraryPicker.userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) + defer { + photoLibraryPicker.userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + } + provider.loadFileRepresentation(forTypeIdentifier: contentType.identifier) { [weak self] url, error in guard let url else { Task { @MainActor in