mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 18:21:00 +00:00
Add upload from file browsing + better compression for images
This commit is contained in:
parent
c3d1c6d363
commit
9057740162
24 changed files with 146 additions and 49 deletions
|
@ -446,6 +446,8 @@
|
||||||
"status.editor.spoiler" = "Тэкст спойлера";
|
"status.editor.spoiler" = "Тэкст спойлера";
|
||||||
"status.editor.text.placeholder" = "Пра што вы думаеце?";
|
"status.editor.text.placeholder" = "Пра што вы думаеце?";
|
||||||
"status.editor.visibility" = "Бачнасць допісу";
|
"status.editor.visibility" = "Бачнасць допісу";
|
||||||
|
"status.editor.photo-library" = "Photos Library";
|
||||||
|
"status.editor.browse-file" = "Browse Files";
|
||||||
"status.error.loading.message" = "Пры загрузцы паведамленняў адбылася памылка, паўтарыце спробу.";
|
"status.error.loading.message" = "Пры загрузцы паведамленняў адбылася памылка, паўтарыце спробу.";
|
||||||
"status.error.message" = "Адбылася памылка ў кантэксце гэтай публікацыі, паспрабуйце яшчэ раз.";
|
"status.error.message" = "Адбылася памылка ў кантэксце гэтай публікацыі, паспрабуйце яшчэ раз.";
|
||||||
"status.error.title" = "Узнікла памылка";
|
"status.error.title" = "Узнікла памылка";
|
||||||
|
|
|
@ -440,6 +440,8 @@
|
||||||
"status.editor.spoiler" = "Escriviu l'espòiler";
|
"status.editor.spoiler" = "Escriviu l'espòiler";
|
||||||
"status.editor.text.placeholder" = "Què us passa pel cap?";
|
"status.editor.text.placeholder" = "Què us passa pel cap?";
|
||||||
"status.editor.visibility" = "Visibilitat de la publicació";
|
"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.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.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";
|
"status.error.title" = "S'ha produït un error";
|
||||||
|
|
|
@ -437,6 +437,8 @@
|
||||||
"status.editor.spoiler" = "Inhaltswarnung";
|
"status.editor.spoiler" = "Inhaltswarnung";
|
||||||
"status.editor.text.placeholder" = "Woran denkst du?";
|
"status.editor.text.placeholder" = "Woran denkst du?";
|
||||||
"status.editor.visibility" = "Sichtbarkeit";
|
"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.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.message" = "Es ist ein Fehler aufgetreten. Bitte versuche es erneut.";
|
||||||
"status.error.title" = "Ein Fehler ist aufgetreten";
|
"status.error.title" = "Ein Fehler ist aufgetreten";
|
||||||
|
|
|
@ -441,6 +441,8 @@
|
||||||
"status.editor.spoiler" = "Spoiler Text";
|
"status.editor.spoiler" = "Spoiler Text";
|
||||||
"status.editor.text.placeholder" = "What's on your mind?";
|
"status.editor.text.placeholder" = "What's on your mind?";
|
||||||
"status.editor.visibility" = "Post visibility";
|
"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.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.message" = "An error occurred in the context of this post, please try again.";
|
||||||
"status.error.title" = "An error occurred";
|
"status.error.title" = "An error occurred";
|
||||||
|
|
|
@ -442,6 +442,8 @@
|
||||||
"status.editor.spoiler" = "Spoiler Text";
|
"status.editor.spoiler" = "Spoiler Text";
|
||||||
"status.editor.text.placeholder" = "What's on your mind?";
|
"status.editor.text.placeholder" = "What's on your mind?";
|
||||||
"status.editor.visibility" = "Post visibility";
|
"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.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.message" = "An error occurred in the context of this post, please try again.";
|
||||||
"status.error.title" = "An error occurred";
|
"status.error.title" = "An error occurred";
|
||||||
|
|
|
@ -442,6 +442,8 @@
|
||||||
"status.editor.spoiler" = "Escribe tu advertencia aquí";
|
"status.editor.spoiler" = "Escribe tu advertencia aquí";
|
||||||
"status.editor.text.placeholder" = "¿En qué estás pensando?";
|
"status.editor.text.placeholder" = "¿En qué estás pensando?";
|
||||||
"status.editor.visibility" = "Visibilidad de la publicación";
|
"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.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.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";
|
"status.error.title" = "Ha ocurrido un error";
|
||||||
|
|
|
@ -435,6 +435,8 @@
|
||||||
"status.editor.spoiler" = "Edukiari buruzko oharra";
|
"status.editor.spoiler" = "Edukiari buruzko oharra";
|
||||||
"status.editor.text.placeholder" = "Zer duzu buruan?";
|
"status.editor.text.placeholder" = "Zer duzu buruan?";
|
||||||
"status.editor.visibility" = "Bidalketaren irismena";
|
"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.loading.message" = "Errorea bidalketak kargatzean; saiatu berriro.";
|
||||||
"status.error.message" = "Errorea bidalketa honen testuinguruan; saiatu berriro.";
|
"status.error.message" = "Errorea bidalketa honen testuinguruan; saiatu berriro.";
|
||||||
"status.error.title" = "Errorea gertatu da";
|
"status.error.title" = "Errorea gertatu da";
|
||||||
|
|
|
@ -437,6 +437,8 @@
|
||||||
"status.editor.spoiler" = "Texte spoilé";
|
"status.editor.spoiler" = "Texte spoilé";
|
||||||
"status.editor.text.placeholder" = "Qu'est-ce qui vous passe par la tête ?";
|
"status.editor.text.placeholder" = "Qu'est-ce qui vous passe par la tête ?";
|
||||||
"status.editor.visibility" = "Visibilité de la publication";
|
"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.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.message" = "Une erreur s'est produite dans le contexte de cette publication, veuillez réessayer.";
|
||||||
"status.error.title" = "Une erreur s'est produite";
|
"status.error.title" = "Une erreur s'est produite";
|
||||||
|
|
|
@ -442,6 +442,8 @@
|
||||||
"status.editor.spoiler" = "Testo spoiler";
|
"status.editor.spoiler" = "Testo spoiler";
|
||||||
"status.editor.text.placeholder" = "A cosa stai pensando?";
|
"status.editor.text.placeholder" = "A cosa stai pensando?";
|
||||||
"status.editor.visibility" = "Visibilità del post";
|
"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.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.message" = "Si è verificato un errore durante il caricamento del post, per favore riprova.";
|
||||||
"status.error.title" = "Si è verificato un errore";
|
"status.error.title" = "Si è verificato un errore";
|
||||||
|
|
|
@ -441,6 +441,8 @@
|
||||||
"status.editor.spoiler" = "ネタバレ";
|
"status.editor.spoiler" = "ネタバレ";
|
||||||
"status.editor.text.placeholder" = "いま、何を考えているの?";
|
"status.editor.text.placeholder" = "いま、何を考えているの?";
|
||||||
"status.editor.visibility" = "投稿の公開範囲";
|
"status.editor.visibility" = "投稿の公開範囲";
|
||||||
|
"status.editor.photo-library" = "Photos Library";
|
||||||
|
"status.editor.browse-file" = "Browse Files";
|
||||||
"status.error.loading.message" = "投稿の読み込み中にエラーが発生しました、もう一度試してください";
|
"status.error.loading.message" = "投稿の読み込み中にエラーが発生しました、もう一度試してください";
|
||||||
"status.error.message" = "この投稿のコンテキストでエラーが発生しました、もう一度試してください";
|
"status.error.message" = "この投稿のコンテキストでエラーが発生しました、もう一度試してください";
|
||||||
"status.error.title" = "エラーが発生しました";
|
"status.error.title" = "エラーが発生しました";
|
||||||
|
|
|
@ -443,6 +443,8 @@
|
||||||
"status.editor.spoiler" = "열람 주의 문구";
|
"status.editor.spoiler" = "열람 주의 문구";
|
||||||
"status.editor.text.placeholder" = "무슨 생각을 하고 계신가요?";
|
"status.editor.text.placeholder" = "무슨 생각을 하고 계신가요?";
|
||||||
"status.editor.visibility" = "글 공개 범위";
|
"status.editor.visibility" = "글 공개 범위";
|
||||||
|
"status.editor.photo-library" = "Photos Library";
|
||||||
|
"status.editor.browse-file" = "Browse Files";
|
||||||
"status.error.loading.message" = "글을 불러오지 못했습니다. 다시 시도해주세요.";
|
"status.error.loading.message" = "글을 불러오지 못했습니다. 다시 시도해주세요.";
|
||||||
"status.error.message" = "글의 상세 정보를 불러오지 못했습니다. 다시 시도해주세요.";
|
"status.error.message" = "글의 상세 정보를 불러오지 못했습니다. 다시 시도해주세요.";
|
||||||
"status.error.title" = "오류";
|
"status.error.title" = "오류";
|
||||||
|
|
|
@ -441,6 +441,8 @@
|
||||||
"status.editor.spoiler" = "Spoilertekst";
|
"status.editor.spoiler" = "Spoilertekst";
|
||||||
"status.editor.text.placeholder" = "Hva tenker du på?";
|
"status.editor.text.placeholder" = "Hva tenker du på?";
|
||||||
"status.editor.visibility" = "Innleggssynlighet";
|
"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.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.message" = "Det oppsto en feil i forbindelse med dette innlegget, prøv igjen.";
|
||||||
"status.error.title" = "En feil oppstod";
|
"status.error.title" = "En feil oppstod";
|
||||||
|
|
|
@ -435,6 +435,8 @@
|
||||||
"status.editor.spoiler" = "Spoilertekst";
|
"status.editor.spoiler" = "Spoilertekst";
|
||||||
"status.editor.text.placeholder" = "Waar denk je aan?";
|
"status.editor.text.placeholder" = "Waar denk je aan?";
|
||||||
"status.editor.visibility" = "Zichtbaarheid";
|
"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.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.message" = "Er heeft zich een fout voorgedaan. Probeer het nogmaals.";
|
||||||
"status.error.title" = "Er heeft zich een fout voorgedaan";
|
"status.error.title" = "Er heeft zich een fout voorgedaan";
|
||||||
|
|
|
@ -437,6 +437,8 @@
|
||||||
"status.editor.spoiler" = "Tekst spoilera";
|
"status.editor.spoiler" = "Tekst spoilera";
|
||||||
"status.editor.text.placeholder" = "Co ci chodzi po głowie?";
|
"status.editor.text.placeholder" = "Co ci chodzi po głowie?";
|
||||||
"status.editor.visibility" = "Widoczność postu";
|
"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.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.message" = "Wystąpił błąd dotyczący tego postu, proszę spróbuj ponownie.";
|
||||||
"status.error.title" = "Wystąpił błąd";
|
"status.error.title" = "Wystąpił błąd";
|
||||||
|
|
|
@ -441,6 +441,8 @@
|
||||||
"status.editor.spoiler" = "Texto de Spoiler";
|
"status.editor.spoiler" = "Texto de Spoiler";
|
||||||
"status.editor.text.placeholder" = "O que você está pensando?";
|
"status.editor.text.placeholder" = "O que você está pensando?";
|
||||||
"status.editor.visibility" = "Visibilidade da postagem";
|
"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.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.message" = "Ocorreu um erro com esta postagem, por favor, tente novamente.";
|
||||||
"status.error.title" = "Ocorreu um erro";
|
"status.error.title" = "Ocorreu um erro";
|
||||||
|
|
|
@ -437,6 +437,8 @@
|
||||||
"status.editor.spoiler" = "Spoiler Yazısı";
|
"status.editor.spoiler" = "Spoiler Yazısı";
|
||||||
"status.editor.text.placeholder" = "Aklında ne var?";
|
"status.editor.text.placeholder" = "Aklında ne var?";
|
||||||
"status.editor.visibility" = "Görüntü görünürlüğü";
|
"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.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.message" = "Bu gönderi bağlamında bir hata oluştu, lütfen tekrar deneyin.";
|
||||||
"status.error.title" = "Bir hata oluştu";
|
"status.error.title" = "Bir hata oluştu";
|
||||||
|
|
|
@ -442,6 +442,8 @@
|
||||||
"status.editor.spoiler" = "Спойлер";
|
"status.editor.spoiler" = "Спойлер";
|
||||||
"status.editor.text.placeholder" = "Що у вас на думці?";
|
"status.editor.text.placeholder" = "Що у вас на думці?";
|
||||||
"status.editor.visibility" = "Видимість допису";
|
"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.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.message" = "An error occurred in the context of this post, please try again.";
|
||||||
"status.error.title" = "An error occurred";
|
"status.error.title" = "An error occurred";
|
||||||
|
|
|
@ -440,6 +440,8 @@
|
||||||
"status.editor.spoiler" = "剧透警告";
|
"status.editor.spoiler" = "剧透警告";
|
||||||
"status.editor.text.placeholder" = "在想些什么呢?";
|
"status.editor.text.placeholder" = "在想些什么呢?";
|
||||||
"status.editor.visibility" = "嘟文可见性";
|
"status.editor.visibility" = "嘟文可见性";
|
||||||
|
"status.editor.photo-library" = "Photos Library";
|
||||||
|
"status.editor.browse-file" = "Browse Files";
|
||||||
"status.error.loading.message" = "加载嘟文时发生错误,请重试。";
|
"status.error.loading.message" = "加载嘟文时发生错误,请重试。";
|
||||||
"status.error.message" = "嘟文的上下文出现了错误,请重试。";
|
"status.error.message" = "嘟文的上下文出现了错误,请重试。";
|
||||||
"status.error.title" = "发生了一个错误";
|
"status.error.title" = "发生了一个错误";
|
||||||
|
|
|
@ -442,6 +442,8 @@
|
||||||
"status.editor.spoiler" = "劇透警告";
|
"status.editor.spoiler" = "劇透警告";
|
||||||
"status.editor.text.placeholder" = "您在想些什麼呢?";
|
"status.editor.text.placeholder" = "您在想些什麼呢?";
|
||||||
"status.editor.visibility" = "嘟文能見度";
|
"status.editor.visibility" = "嘟文能見度";
|
||||||
|
"status.editor.photo-library" = "Photos Library";
|
||||||
|
"status.editor.browse-file" = "Browse Files";
|
||||||
"status.error.loading.message" = "下載嘟文時發生錯誤,請再試一次。";
|
"status.error.loading.message" = "下載嘟文時發生錯誤,請再試一次。";
|
||||||
"status.error.message" = "嘟文上下文發生錯誤,請再試一次。";
|
"status.error.message" = "嘟文上下文發生錯誤,請再試一次。";
|
||||||
"status.error.title" = "發生錯誤";
|
"status.error.title" = "發生錯誤";
|
||||||
|
|
|
@ -51,8 +51,8 @@ public struct AppAccountView: View {
|
||||||
.offset(x: 5, y: -5)
|
.offset(x: 5, y: -5)
|
||||||
} else if viewModel.showBadge,
|
} else if viewModel.showBadge,
|
||||||
let token = viewModel.appAccount.oauthToken,
|
let token = viewModel.appAccount.oauthToken,
|
||||||
let notificationsCount = preferences.getNotificationsCount(for: token),
|
preferences.getNotificationsCount(for: token) > 0 {
|
||||||
notificationsCount > 0{
|
let notificationsCount = preferences.getNotificationsCount(for: token)
|
||||||
ZStack {
|
ZStack {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(.red)
|
.fill(.red)
|
||||||
|
|
|
@ -19,6 +19,8 @@ struct StatusEditorAccessoryView: View {
|
||||||
@State private var isCustomEmojisSheetDisplay: Bool = false
|
@State private var isCustomEmojisSheetDisplay: Bool = false
|
||||||
@State private var languageSearch: String = ""
|
@State private var languageSearch: String = ""
|
||||||
@State private var isLoadingAIRequest: Bool = false
|
@State private var isLoadingAIRequest: Bool = false
|
||||||
|
@State private var isPhotosPickerPresented: Bool = false
|
||||||
|
@State private var isFileImporterPresented: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
|
@ -26,17 +28,38 @@ struct StatusEditorAccessoryView: View {
|
||||||
HStack {
|
HStack {
|
||||||
ScrollView(.horizontal) {
|
ScrollView(.horizontal) {
|
||||||
HStack(alignment: .center, spacing: 16) {
|
HStack(alignment: .center, spacing: 16) {
|
||||||
PhotosPicker(selection: $viewModel.selectedMedias,
|
Menu {
|
||||||
matching: .any(of: [.images, .videos])) {
|
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 {
|
if viewModel.isMediasLoading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: "photo.on.rectangle.angled")
|
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")
|
.accessibilityLabel("accessibility.editor.button.attach-photo")
|
||||||
.disabled(viewModel.showPoll)
|
.disabled(viewModel.showPoll)
|
||||||
|
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
viewModel.showPoll.toggle()
|
viewModel.showPoll.toggle()
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
case gif = "public.gif"
|
case gif = "public.gif"
|
||||||
case gif2 = "com.compuserve.gif"
|
case gif2 = "com.compuserve.gif"
|
||||||
case quickTimeMovie = "com.apple.quicktime-movie"
|
case quickTimeMovie = "com.apple.quicktime-movie"
|
||||||
|
case adobeRawImage = "com.adobe.raw-image"
|
||||||
|
|
||||||
case uiimage = "com.apple.uikit.image"
|
case uiimage = "com.apple.uikit.image"
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
//
|
//
|
||||||
nonisolated public static var allCases: [StatusEditorUTTypeSupported] {
|
nonisolated public static var allCases: [StatusEditorUTTypeSupported] {
|
||||||
[.url, .text, .plaintext, .image, .jpeg, .png, .tiff, .video,
|
[.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] {
|
static func types() -> [UTType] {
|
||||||
|
@ -66,7 +67,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
} else if isGif, let transferable = await getGifTransferable(item: item) {
|
} else if isGif, let transferable = await getGifTransferable(item: item) {
|
||||||
return transferable
|
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 {
|
if let image = result as? UIImage {
|
||||||
return image
|
return image
|
||||||
} else if let imageURL = result as? URL,
|
} else if let imageURL = result as? URL,
|
||||||
|
@ -134,25 +135,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MovieFileTranseferable: Transferable {
|
struct MovieFileTranseferable: Transferable {
|
||||||
private let url: URL
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var transferRepresentation: some TransferRepresentation {
|
static var transferRepresentation: some TransferRepresentation {
|
||||||
FileRepresentation(contentType: .movie) { movie in
|
FileRepresentation(contentType: .movie) { movie in
|
||||||
|
|
|
@ -364,6 +364,13 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
|
|
||||||
// MARK: - Shar sheet / Item provider
|
// 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]) {
|
private func processItemsProvider(items: [NSItemProvider]) {
|
||||||
Task {
|
Task {
|
||||||
var initialText: String = ""
|
var initialText: String = ""
|
||||||
|
@ -402,7 +409,9 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
mediaAttachment: nil,
|
mediaAttachment: nil,
|
||||||
error: nil))
|
error: nil))
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {
|
||||||
|
isMediasLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !initialText.isEmpty {
|
if !initialText.isEmpty {
|
||||||
|
@ -591,18 +600,10 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
mediasImages[index] = newContainer
|
mediasImages[index] = newContainer
|
||||||
do {
|
do {
|
||||||
if let index = indexOf(container: newContainer) {
|
if let index = indexOf(container: newContainer) {
|
||||||
|
let compressor = StatusEditorCompressor()
|
||||||
if let image = originalContainer.image {
|
if let image = originalContainer.image {
|
||||||
let data: Data?
|
let imageData = try await compressor.compressImage(image)
|
||||||
// Mastodon API don't support images over 5K
|
let uploadedMedia = try await uploadMedia(data: imageData, mimeType: "image/jpeg")
|
||||||
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)
|
|
||||||
}
|
|
||||||
if let data {
|
|
||||||
let uploadedMedia = try await uploadMedia(data: data, mimeType: "image/jpeg")
|
|
||||||
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||||
movieTransferable: nil,
|
movieTransferable: nil,
|
||||||
gifTransferable: nil,
|
gifTransferable: nil,
|
||||||
|
@ -611,11 +612,10 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
if let uploadedMedia, uploadedMedia.url == nil {
|
if let uploadedMedia, uploadedMedia.url == nil {
|
||||||
scheduleAsyncMediaRefresh(mediaAttachement: uploadedMedia)
|
scheduleAsyncMediaRefresh(mediaAttachement: uploadedMedia)
|
||||||
}
|
}
|
||||||
}
|
} else if let videoURL = originalContainer.movieTransferable?.url,
|
||||||
} else if let videoURL = await originalContainer.movieTransferable?.compressedVideoURL,
|
let compressedVideoURL = await compressor.compressVideo(videoURL),
|
||||||
let data = try? Data(contentsOf: videoURL)
|
let data = try? Data(contentsOf: compressedVideoURL) {
|
||||||
{
|
let uploadedMedia = try await uploadMedia(data: data, mimeType: compressedVideoURL.mimeType())
|
||||||
let uploadedMedia = try await uploadMedia(data: data, mimeType: videoURL.mimeType())
|
|
||||||
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||||
movieTransferable: originalContainer.movieTransferable,
|
movieTransferable: originalContainer.movieTransferable,
|
||||||
gifTransferable: nil,
|
gifTransferable: nil,
|
||||||
|
|
Loading…
Reference in a new issue