From 9057740162441bd9dfe08553989f7d99bb6213c1 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Fri, 10 Mar 2023 18:22:45 +0100 Subject: [PATCH] Add upload from file browsing + better compression for images --- .../Localization/be.lproj/Localizable.strings | 2 + .../Localization/ca.lproj/Localizable.strings | 2 + .../Localization/de.lproj/Localizable.strings | 2 + .../en-GB.lproj/Localizable.strings | 2 + .../Localization/en.lproj/Localizable.strings | 2 + .../Localization/es.lproj/Localizable.strings | 2 + .../Localization/eu.lproj/Localizable.strings | 2 + .../Localization/fr.lproj/Localizable.strings | 2 + .../Localization/it.lproj/Localizable.strings | 2 + .../Localization/ja.lproj/Localizable.strings | 2 + .../Localization/ko.lproj/Localizable.strings | 2 + .../Localization/nb.lproj/Localizable.strings | 2 + .../Localization/nl.lproj/Localizable.strings | 2 + .../Localization/pl.lproj/Localizable.strings | 2 + .../pt-BR.lproj/Localizable.strings | 2 + .../Localization/tr.lproj/Localizable.strings | 2 + .../Localization/uk.lproj/Localizable.strings | 2 + .../zh-Hans.lproj/Localizable.strings | 2 + .../zh-Hant.lproj/Localizable.strings | 2 + .../Sources/AppAccount/AppAccountView.swift | 4 +- .../StatusEditorAccessoryView.swift | 27 +++++++++- .../Components/StatusEditorCompressor.swift | 53 +++++++++++++++++++ .../StatusEditorUTTypeSupported.swift | 25 ++------- .../Status/Editor/StatusEditorViewModel.swift | 48 ++++++++--------- 24 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 Packages/Status/Sources/Status/Editor/Components/StatusEditorCompressor.swift diff --git a/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings index 7f910a3d..f1ecc660 100644 --- a/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings @@ -446,6 +446,8 @@ "status.editor.spoiler" = "Тэкст спойлера"; "status.editor.text.placeholder" = "Пра што вы думаеце?"; "status.editor.visibility" = "Бачнасць допісу"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Пры загрузцы паведамленняў адбылася памылка, паўтарыце спробу."; "status.error.message" = "Адбылася памылка ў кантэксце гэтай публікацыі, паспрабуйце яшчэ раз."; "status.error.title" = "Узнікла памылка"; diff --git a/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings index d69a1662..eff97fa5 100644 --- a/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings @@ -440,6 +440,8 @@ "status.editor.spoiler" = "Escriviu l'espòiler"; "status.editor.text.placeholder" = "Què us passa pel cap?"; "status.editor.visibility" = "Visibilitat de la publicació"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "S'ha produït un error en carregar les publicacions, torneu-ho a provar."; "status.error.message" = "S'ha produït un error en el context d'aquesta publicació, torneu-ho a provar."; "status.error.title" = "S'ha produït un error"; diff --git a/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings index 10b2d750..34824f6e 100644 --- a/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings @@ -437,6 +437,8 @@ "status.editor.spoiler" = "Inhaltswarnung"; "status.editor.text.placeholder" = "Woran denkst du?"; "status.editor.visibility" = "Sichtbarkeit"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Beim Laden der Beiträge ist ein Fehler aufgetreten. Bitte versuche es erneut."; "status.error.message" = "Es ist ein Fehler aufgetreten. Bitte versuche es erneut."; "status.error.title" = "Ein Fehler ist aufgetreten"; diff --git a/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings index edbd74cb..30c965a2 100644 --- a/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings @@ -441,6 +441,8 @@ "status.editor.spoiler" = "Spoiler Text"; "status.editor.text.placeholder" = "What's on your mind?"; "status.editor.visibility" = "Post visibility"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "An error occurred while loading posts, please try again."; "status.error.message" = "An error occurred in the context of this post, please try again."; "status.error.title" = "An error occurred"; diff --git a/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings index e32a4e96..43884214 100644 --- a/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings @@ -442,6 +442,8 @@ "status.editor.spoiler" = "Spoiler Text"; "status.editor.text.placeholder" = "What's on your mind?"; "status.editor.visibility" = "Post visibility"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "An error occurred while loading posts, please try again."; "status.error.message" = "An error occurred in the context of this post, please try again."; "status.error.title" = "An error occurred"; diff --git a/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings index b413b7b0..f241c503 100644 --- a/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings @@ -442,6 +442,8 @@ "status.editor.spoiler" = "Escribe tu advertencia aquí"; "status.editor.text.placeholder" = "¿En qué estás pensando?"; "status.editor.visibility" = "Visibilidad de la publicación"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Ha ocurrido un error al cargar las publicaciones, por favor, vuelve a intentarlo."; "status.error.message" = "Ha ocurrido un error al cargar el contexto de esta publicación, por favor, vuelve a intentarlo."; "status.error.title" = "Ha ocurrido un error"; diff --git a/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings index b0aded62..e8c2c151 100644 --- a/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings @@ -435,6 +435,8 @@ "status.editor.spoiler" = "Edukiari buruzko oharra"; "status.editor.text.placeholder" = "Zer duzu buruan?"; "status.editor.visibility" = "Bidalketaren irismena"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Errorea bidalketak kargatzean; saiatu berriro."; "status.error.message" = "Errorea bidalketa honen testuinguruan; saiatu berriro."; "status.error.title" = "Errorea gertatu da"; diff --git a/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings index 0a881444..f74dac77 100644 --- a/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings @@ -437,6 +437,8 @@ "status.editor.spoiler" = "Texte spoilé"; "status.editor.text.placeholder" = "Qu'est-ce qui vous passe par la tête ?"; "status.editor.visibility" = "Visibilité de la publication"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Une erreur s'est produite lors du chargement des publications, veuillez réessayer."; "status.error.message" = "Une erreur s'est produite dans le contexte de cette publication, veuillez réessayer."; "status.error.title" = "Une erreur s'est produite"; diff --git a/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings index aee74f79..1c60893a 100644 --- a/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings @@ -442,6 +442,8 @@ "status.editor.spoiler" = "Testo spoiler"; "status.editor.text.placeholder" = "A cosa stai pensando?"; "status.editor.visibility" = "Visibilità del post"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Si è verificato un errore durante il caricamento dei post, per favore riprova."; "status.error.message" = "Si è verificato un errore durante il caricamento del post, per favore riprova."; "status.error.title" = "Si è verificato un errore"; diff --git a/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings index 4626eea8..540585cf 100644 --- a/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings @@ -441,6 +441,8 @@ "status.editor.spoiler" = "ネタバレ"; "status.editor.text.placeholder" = "いま、何を考えているの?"; "status.editor.visibility" = "投稿の公開範囲"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "投稿の読み込み中にエラーが発生しました、もう一度試してください"; "status.error.message" = "この投稿のコンテキストでエラーが発生しました、もう一度試してください"; "status.error.title" = "エラーが発生しました"; diff --git a/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings index aa333e9b..0d70556d 100644 --- a/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings @@ -443,6 +443,8 @@ "status.editor.spoiler" = "열람 주의 문구"; "status.editor.text.placeholder" = "무슨 생각을 하고 계신가요?"; "status.editor.visibility" = "글 공개 범위"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "글을 불러오지 못했습니다. 다시 시도해주세요."; "status.error.message" = "글의 상세 정보를 불러오지 못했습니다. 다시 시도해주세요."; "status.error.title" = "오류"; diff --git a/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings index 38d2bb15..9923c776 100644 --- a/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings @@ -441,6 +441,8 @@ "status.editor.spoiler" = "Spoilertekst"; "status.editor.text.placeholder" = "Hva tenker du på?"; "status.editor.visibility" = "Innleggssynlighet"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Det oppsto en feil under innlasting av innlegg, prøv igjen."; "status.error.message" = "Det oppsto en feil i forbindelse med dette innlegget, prøv igjen."; "status.error.title" = "En feil oppstod"; diff --git a/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings index bacac3e8..f925b78c 100644 --- a/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings @@ -435,6 +435,8 @@ "status.editor.spoiler" = "Spoilertekst"; "status.editor.text.placeholder" = "Waar denk je aan?"; "status.editor.visibility" = "Zichtbaarheid"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Er heeft zich een fout voorgedaan tijdens het laden van je posts. Probeer het nogmaals."; "status.error.message" = "Er heeft zich een fout voorgedaan. Probeer het nogmaals."; "status.error.title" = "Er heeft zich een fout voorgedaan"; diff --git a/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings index 7ebb5900..06f5047d 100644 --- a/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings @@ -437,6 +437,8 @@ "status.editor.spoiler" = "Tekst spoilera"; "status.editor.text.placeholder" = "Co ci chodzi po głowie?"; "status.editor.visibility" = "Widoczność postu"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Wystąpił błąd podczas ładowania postów, spróbuj ponownie."; "status.error.message" = "Wystąpił błąd dotyczący tego postu, proszę spróbuj ponownie."; "status.error.title" = "Wystąpił błąd"; diff --git a/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings index 251286f2..786d8c85 100644 --- a/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings @@ -441,6 +441,8 @@ "status.editor.spoiler" = "Texto de Spoiler"; "status.editor.text.placeholder" = "O que você está pensando?"; "status.editor.visibility" = "Visibilidade da postagem"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Ocorreu um erro enquanto as postagens eram carregadas, por favor, tente novamente."; "status.error.message" = "Ocorreu um erro com esta postagem, por favor, tente novamente."; "status.error.title" = "Ocorreu um erro"; diff --git a/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings index 23498ea1..39cef8f4 100644 --- a/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings @@ -437,6 +437,8 @@ "status.editor.spoiler" = "Spoiler Yazısı"; "status.editor.text.placeholder" = "Aklında ne var?"; "status.editor.visibility" = "Görüntü görünürlüğü"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "Gönderi yüklenirken bir hata oluştu, lütfen tekrar deneyin."; "status.error.message" = "Bu gönderi bağlamında bir hata oluştu, lütfen tekrar deneyin."; "status.error.title" = "Bir hata oluştu"; diff --git a/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings index 3ec20caf..079dc986 100644 --- a/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings @@ -442,6 +442,8 @@ "status.editor.spoiler" = "Спойлер"; "status.editor.text.placeholder" = "Що у вас на думці?"; "status.editor.visibility" = "Видимість допису"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "An error occurred while loading posts, please try again."; "status.error.message" = "An error occurred in the context of this post, please try again."; "status.error.title" = "An error occurred"; diff --git a/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings index c19b7d68..325ff8bf 100644 --- a/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings @@ -440,6 +440,8 @@ "status.editor.spoiler" = "剧透警告"; "status.editor.text.placeholder" = "在想些什么呢?"; "status.editor.visibility" = "嘟文可见性"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "加载嘟文时发生错误,请重试。"; "status.error.message" = "嘟文的上下文出现了错误,请重试。"; "status.error.title" = "发生了一个错误"; diff --git a/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings index 0b17b1c3..81cfb563 100644 --- a/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings @@ -442,6 +442,8 @@ "status.editor.spoiler" = "劇透警告"; "status.editor.text.placeholder" = "您在想些什麼呢?"; "status.editor.visibility" = "嘟文能見度"; +"status.editor.photo-library" = "Photos Library"; +"status.editor.browse-file" = "Browse Files"; "status.error.loading.message" = "下載嘟文時發生錯誤,請再試一次。"; "status.error.message" = "嘟文上下文發生錯誤,請再試一次。"; "status.error.title" = "發生錯誤"; diff --git a/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift b/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift index 955978a3..f3c47f8e 100644 --- a/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift +++ b/Packages/AppAccount/Sources/AppAccount/AppAccountView.swift @@ -51,8 +51,8 @@ public struct AppAccountView: View { .offset(x: 5, y: -5) } else if viewModel.showBadge, let token = viewModel.appAccount.oauthToken, - let notificationsCount = preferences.getNotificationsCount(for: token), - notificationsCount > 0{ + preferences.getNotificationsCount(for: token) > 0 { + let notificationsCount = preferences.getNotificationsCount(for: token) ZStack { Circle() .fill(.red) diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift index 4da3e5c2..457bfeac 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift @@ -19,6 +19,8 @@ struct StatusEditorAccessoryView: View { @State private var isCustomEmojisSheetDisplay: Bool = false @State private var languageSearch: String = "" @State private var isLoadingAIRequest: Bool = false + @State private var isPhotosPickerPresented: Bool = false + @State private var isFileImporterPresented: Bool = false var body: some View { VStack(spacing: 0) { @@ -26,16 +28,37 @@ struct StatusEditorAccessoryView: View { HStack { ScrollView(.horizontal) { HStack(alignment: .center, spacing: 16) { - PhotosPicker(selection: $viewModel.selectedMedias, - matching: .any(of: [.images, .videos])) { + Menu { + Button { + isPhotosPickerPresented = true + } label: { + Label("status.editor.photo-library", systemImage: "photo") + } + Button { + isFileImporterPresented = true + } label: { + Label("status.editor.browse-file", systemImage: "folder") + } + } label: { if viewModel.isMediasLoading { ProgressView() } else { Image(systemName: "photo.on.rectangle.angled") } } + .photosPicker(isPresented: $isPhotosPickerPresented, + selection: $viewModel.selectedMedias, + matching: .any(of: [.images, .videos])) + .fileImporter(isPresented: $isFileImporterPresented, + allowedContentTypes: [.image, .video], + allowsMultipleSelection: true) { result in + if let urls = try? result.get() { + viewModel.processURLs(urls: urls) + } + } .accessibilityLabel("accessibility.editor.button.attach-photo") .disabled(viewModel.showPoll) + Button { withAnimation { diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorCompressor.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorCompressor.swift new file mode 100644 index 00000000..ba5f904a --- /dev/null +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorCompressor.swift @@ -0,0 +1,53 @@ +import Foundation +import UIKit +import AVFoundation + +actor StatusEditorCompressor { + enum CompressorError: Error { + case noData + } + + func compressImage(_ image: UIImage) async throws -> Data { + var image = image + if image.size.height > 5000 || image.size.width > 5000 { + image = image.resized(to: .init(width: image.size.width / 4, + height: image.size.height / 4)) + } + + guard var imageData = image.jpegData(compressionQuality: 0.8) else { + throw CompressorError.noData + } + + let maxSize: Int = 10 * 1024 * 1024 + + if imageData.count > maxSize { + while imageData.count > maxSize { + guard let compressedImage = UIImage(data: imageData), + let compressedData = compressedImage.jpegData(compressionQuality: 0.8) else { + throw CompressorError.noData + } + imageData = compressedData + } + } + + return imageData + } + + func compressVideo(_ url: URL) async -> URL? { + await withCheckedContinuation { continuation in + let urlAsset = AVURLAsset(url: url, options: nil) + guard let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPreset1920x1080) else { + continuation.resume(returning: nil) + return + } + let outputURL = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(url.pathExtension)") + exportSession.outputURL = outputURL + exportSession.outputFileType = .mp4 + exportSession.shouldOptimizeForNetworkUse = true + exportSession.exportAsynchronously { () in + continuation.resume(returning: outputURL) + } + } + } + +} diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorUTTypeSupported.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorUTTypeSupported.swift index 3776d627..f794b1b1 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorUTTypeSupported.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorUTTypeSupported.swift @@ -21,6 +21,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable { case gif = "public.gif" case gif2 = "com.compuserve.gif" case quickTimeMovie = "com.apple.quicktime-movie" + case adobeRawImage = "com.adobe.raw-image" case uiimage = "com.apple.uikit.image" @@ -32,7 +33,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable { // nonisolated public static var allCases: [StatusEditorUTTypeSupported] { [.url, .text, .plaintext, .image, .jpeg, .png, .tiff, .video, - .movie, .mp4, .gif, .gif2, .quickTimeMovie, .uiimage] + .movie, .mp4, .gif, .gif2, .quickTimeMovie, .uiimage, .adobeRawImage] } static func types() -> [UTType] { @@ -66,7 +67,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable { } else if isGif, let transferable = await getGifTransferable(item: item) { return transferable } - if self == .jpeg || self == .png || self == .tiff || self == .image || self == .uiimage { + if self == .jpeg || self == .png || self == .tiff || self == .image || self == .uiimage || self == .adobeRawImage { if let image = result as? UIImage { return image } else if let imageURL = result as? URL, @@ -134,25 +135,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable { } struct MovieFileTranseferable: Transferable { - private let url: URL - var compressedVideoURL: URL? { - get async { - await withCheckedContinuation { continuation in - let urlAsset = AVURLAsset(url: url, options: nil) - guard let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPreset1920x1080) else { - continuation.resume(returning: nil) - return - } - let outputURL = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(url.pathExtension)") - exportSession.outputURL = outputURL - exportSession.outputFileType = .mp4 - exportSession.shouldOptimizeForNetworkUse = true - exportSession.exportAsynchronously { () in - continuation.resume(returning: outputURL) - } - } - } - } + let url: URL static var transferRepresentation: some TransferRepresentation { FileRepresentation(contentType: .movie) { movie in diff --git a/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift b/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift index 98c94ec9..21cdd0ac 100644 --- a/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift +++ b/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift @@ -363,6 +363,13 @@ public class StatusEditorViewModel: NSObject, ObservableObject { } // MARK: - Shar sheet / Item provider + + func processURLs(urls: [URL]) { + isMediasLoading = true + let items = urls.filter { $0.startAccessingSecurityScopedResource() } + .compactMap { NSItemProvider(contentsOf: $0) } + processItemsProvider(items: items) + } private func processItemsProvider(items: [NSItemProvider]) { Task { @@ -402,7 +409,9 @@ public class StatusEditorViewModel: NSObject, ObservableObject { mediaAttachment: nil, error: nil)) } - } catch {} + } catch { + isMediasLoading = false + } } } if !initialText.isEmpty { @@ -591,31 +600,22 @@ public class StatusEditorViewModel: NSObject, ObservableObject { mediasImages[index] = newContainer do { if let index = indexOf(container: newContainer) { + let compressor = StatusEditorCompressor() if let image = originalContainer.image { - let data: Data? - // Mastodon API don't support images over 5K - if image.size.height > 5000 || image.size.width > 5000 { - data = image.resized(to: .init(width: image.size.width / 4, - height: image.size.height / 4)) - .jpegData(compressionQuality: 0.80) - } else { - data = image.jpegData(compressionQuality: 0.80) + let imageData = try await compressor.compressImage(image) + let uploadedMedia = try await uploadMedia(data: imageData, mimeType: "image/jpeg") + mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil, + movieTransferable: nil, + gifTransferable: nil, + mediaAttachment: uploadedMedia, + error: nil) + if let uploadedMedia, uploadedMedia.url == nil { + scheduleAsyncMediaRefresh(mediaAttachement: uploadedMedia) } - if let data { - let uploadedMedia = try await uploadMedia(data: data, mimeType: "image/jpeg") - mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil, - movieTransferable: nil, - gifTransferable: nil, - mediaAttachment: uploadedMedia, - error: nil) - if let uploadedMedia, uploadedMedia.url == nil { - scheduleAsyncMediaRefresh(mediaAttachement: uploadedMedia) - } - } - } else if let videoURL = await originalContainer.movieTransferable?.compressedVideoURL, - let data = try? Data(contentsOf: videoURL) - { - let uploadedMedia = try await uploadMedia(data: data, mimeType: videoURL.mimeType()) + } else if let videoURL = originalContainer.movieTransferable?.url, + let compressedVideoURL = await compressor.compressVideo(videoURL), + let data = try? Data(contentsOf: compressedVideoURL) { + let uploadedMedia = try await uploadMedia(data: data, mimeType: compressedVideoURL.mimeType()) mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil, movieTransferable: originalContainer.movieTransferable, gifTransferable: nil,