mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-02-23 13:06:16 +00:00
Share post as image close #885
This commit is contained in:
parent
dd2ebe5506
commit
ccc504fc6f
25 changed files with 257 additions and 56 deletions
|
@ -7,6 +7,8 @@ import Lists
|
||||||
import Status
|
import Status
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Timeline
|
import Timeline
|
||||||
|
import LinkPresentation
|
||||||
|
import Models
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
extension View {
|
extension View {
|
||||||
|
@ -92,6 +94,8 @@ extension View {
|
||||||
case let .report(status):
|
case let .report(status):
|
||||||
ReportView(status: status)
|
ReportView(status: status)
|
||||||
.withEnvironments()
|
.withEnvironments()
|
||||||
|
case let .shareImage(image, status):
|
||||||
|
ActivityView(image: image, status: status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,3 +110,42 @@ extension View {
|
||||||
.environmentObject(AppAccountsManager.shared.currentClient)
|
.environmentObject(AppAccountsManager.shared.currentClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ActivityView: UIViewControllerRepresentable {
|
||||||
|
let image: UIImage
|
||||||
|
let status: Status
|
||||||
|
|
||||||
|
class LinkDelegate: NSObject, UIActivityItemSource {
|
||||||
|
let image: UIImage
|
||||||
|
let status: Status
|
||||||
|
|
||||||
|
init(image: UIImage, status: Status) {
|
||||||
|
self.image = image
|
||||||
|
self.status = status
|
||||||
|
}
|
||||||
|
|
||||||
|
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
|
||||||
|
let imageProvider = NSItemProvider(object: image)
|
||||||
|
let metadata = LPLinkMetadata()
|
||||||
|
metadata.imageProvider = imageProvider
|
||||||
|
metadata.title = status.reblog?.content.asRawText ?? status.content.asRawText
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
|
||||||
|
image
|
||||||
|
}
|
||||||
|
|
||||||
|
func activityViewController(_ activityViewController: UIActivityViewController,
|
||||||
|
itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||||
|
return UIActivityViewController(activityItems: [image, LinkDelegate(image: image, status: status)],
|
||||||
|
applicationActivities: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||||
|
}
|
||||||
|
|
|
@ -372,6 +372,9 @@
|
||||||
"status.action.reply" = "Адказаць";
|
"status.action.reply" = "Адказаць";
|
||||||
"status.action.section.your-post" = "Ваш допіс";
|
"status.action.section.your-post" = "Ваш допіс";
|
||||||
"status.action.share" = "Падзяліцца гэтым допісам";
|
"status.action.share" = "Падзяліцца гэтым допісам";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Скасаваць закладку";
|
"status.action.unbookmark" = "Скасаваць закладку";
|
||||||
"status.action.unboost" = "Адмяніць павышэнне";
|
"status.action.unboost" = "Адмяніць павышэнне";
|
||||||
"status.action.unfavorite" = "Выдаліць з улюбенага";
|
"status.action.unfavorite" = "Выдаліць з улюбенага";
|
||||||
|
|
|
@ -382,6 +382,9 @@
|
||||||
"status.action.reply" = "Respon";
|
"status.action.reply" = "Respon";
|
||||||
"status.action.section.your-post" = "La vostra publicació";
|
"status.action.section.your-post" = "La vostra publicació";
|
||||||
"status.action.share" = "Comparteix la publicació";
|
"status.action.share" = "Comparteix la publicació";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Elimina dels marcadors";
|
"status.action.unbookmark" = "Elimina dels marcadors";
|
||||||
"status.action.unboost" = "Desfés l'impulsa";
|
"status.action.unboost" = "Desfés l'impulsa";
|
||||||
"status.action.unfavorite" = "Desfés el preferit";
|
"status.action.unfavorite" = "Desfés el preferit";
|
||||||
|
|
|
@ -379,6 +379,9 @@
|
||||||
"status.action.reply" = "Antworten";
|
"status.action.reply" = "Antworten";
|
||||||
"status.action.section.your-post" = "Dein Beitrag";
|
"status.action.section.your-post" = "Dein Beitrag";
|
||||||
"status.action.share" = "Diesen Beitrag teilen";
|
"status.action.share" = "Diesen Beitrag teilen";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Lesezeichen entfernen";
|
"status.action.unbookmark" = "Lesezeichen entfernen";
|
||||||
"status.action.unboost" = "Boost entfernen";
|
"status.action.unboost" = "Boost entfernen";
|
||||||
"status.action.unfavorite" = "Favorit entfernen";
|
"status.action.unfavorite" = "Favorit entfernen";
|
||||||
|
|
|
@ -385,6 +385,9 @@
|
||||||
"status.action.reply" = "Reply";
|
"status.action.reply" = "Reply";
|
||||||
"status.action.section.your-post" = "Your post";
|
"status.action.section.your-post" = "Your post";
|
||||||
"status.action.share" = "Share this post";
|
"status.action.share" = "Share this post";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Unbookmark";
|
"status.action.unbookmark" = "Unbookmark";
|
||||||
"status.action.unboost" = "Unboost";
|
"status.action.unboost" = "Unboost";
|
||||||
"status.action.unfavorite" = "Unfavourite";
|
"status.action.unfavorite" = "Unfavourite";
|
||||||
|
|
|
@ -384,6 +384,9 @@
|
||||||
"status.action.reply" = "Reply";
|
"status.action.reply" = "Reply";
|
||||||
"status.action.section.your-post" = "Your post";
|
"status.action.section.your-post" = "Your post";
|
||||||
"status.action.share" = "Share this post";
|
"status.action.share" = "Share this post";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Unbookmark";
|
"status.action.unbookmark" = "Unbookmark";
|
||||||
"status.action.unboost" = "Unboost";
|
"status.action.unboost" = "Unboost";
|
||||||
"status.action.unfavorite" = "Unfavorite";
|
"status.action.unfavorite" = "Unfavorite";
|
||||||
|
|
|
@ -384,6 +384,9 @@
|
||||||
"status.action.reply" = "Responder";
|
"status.action.reply" = "Responder";
|
||||||
"status.action.section.your-post" = "Tus publicaciones";
|
"status.action.section.your-post" = "Tus publicaciones";
|
||||||
"status.action.share" = "Compartir esta publicación";
|
"status.action.share" = "Compartir esta publicación";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Quitar de marcadores";
|
"status.action.unbookmark" = "Quitar de marcadores";
|
||||||
"status.action.unboost" = "Deshacer Retoot";
|
"status.action.unboost" = "Deshacer Retoot";
|
||||||
"status.action.unfavorite" = "Eliminar de favoritos";
|
"status.action.unfavorite" = "Eliminar de favoritos";
|
||||||
|
|
|
@ -377,6 +377,9 @@
|
||||||
"status.action.reply" = "Erantzun";
|
"status.action.reply" = "Erantzun";
|
||||||
"status.action.section.your-post" = "Zure bidalketa";
|
"status.action.section.your-post" = "Zure bidalketa";
|
||||||
"status.action.share" = "Partekatu bidalketa";
|
"status.action.share" = "Partekatu bidalketa";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Kendu laster-marka";
|
"status.action.unbookmark" = "Kendu laster-marka";
|
||||||
"status.action.unboost" = "Kendu bultzada";
|
"status.action.unboost" = "Kendu bultzada";
|
||||||
"status.action.unfavorite" = "Kendu gogokoa";
|
"status.action.unfavorite" = "Kendu gogokoa";
|
||||||
|
|
|
@ -379,6 +379,9 @@
|
||||||
"status.action.reply" = "Répondre";
|
"status.action.reply" = "Répondre";
|
||||||
"status.action.section.your-post" = "Votre publication";
|
"status.action.section.your-post" = "Votre publication";
|
||||||
"status.action.share" = "Partager cette publication";
|
"status.action.share" = "Partager cette publication";
|
||||||
|
"status.action.share-link" = "Partager le lien";
|
||||||
|
"status.action.share-image" = "Partager comme image";
|
||||||
|
"status.action.share-title" = "Partager";
|
||||||
"status.action.unbookmark" = "Démarquer";
|
"status.action.unbookmark" = "Démarquer";
|
||||||
"status.action.unboost" = "Annuler la promotion";
|
"status.action.unboost" = "Annuler la promotion";
|
||||||
"status.action.unfavorite" = "Retirer des favoris";
|
"status.action.unfavorite" = "Retirer des favoris";
|
||||||
|
|
|
@ -384,6 +384,9 @@
|
||||||
"status.action.reply" = "Rispondi";
|
"status.action.reply" = "Rispondi";
|
||||||
"status.action.section.your-post" = "I tuoi post";
|
"status.action.section.your-post" = "I tuoi post";
|
||||||
"status.action.share" = "Condividi questo post";
|
"status.action.share" = "Condividi questo post";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Rimuovi il segnalibro";
|
"status.action.unbookmark" = "Rimuovi il segnalibro";
|
||||||
"status.action.unboost" = "Rimuovi la condivisione";
|
"status.action.unboost" = "Rimuovi la condivisione";
|
||||||
"status.action.unfavorite" = "Rimuovi l'apprezzamento";
|
"status.action.unfavorite" = "Rimuovi l'apprezzamento";
|
||||||
|
|
|
@ -383,6 +383,9 @@
|
||||||
"status.action.reply" = "リプライ";
|
"status.action.reply" = "リプライ";
|
||||||
"status.action.section.your-post" = "あなたの投稿";
|
"status.action.section.your-post" = "あなたの投稿";
|
||||||
"status.action.share" = "投稿を共有する";
|
"status.action.share" = "投稿を共有する";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "ブックマークを外す";
|
"status.action.unbookmark" = "ブックマークを外す";
|
||||||
"status.action.unboost" = "ブーストをやめる";
|
"status.action.unboost" = "ブーストをやめる";
|
||||||
"status.action.unfavorite" = "お気に入りから外す";
|
"status.action.unfavorite" = "お気に入りから外す";
|
||||||
|
|
|
@ -385,6 +385,9 @@
|
||||||
"status.action.reply" = "댓글";
|
"status.action.reply" = "댓글";
|
||||||
"status.action.section.your-post" = "내 글";
|
"status.action.section.your-post" = "내 글";
|
||||||
"status.action.share" = "공유";
|
"status.action.share" = "공유";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "보관함에서 제거";
|
"status.action.unbookmark" = "보관함에서 제거";
|
||||||
"status.action.unboost" = "부스트 취소";
|
"status.action.unboost" = "부스트 취소";
|
||||||
"status.action.unfavorite" = "좋아요 취소";
|
"status.action.unfavorite" = "좋아요 취소";
|
||||||
|
|
|
@ -383,6 +383,9 @@
|
||||||
"status.action.reply" = "Svar";
|
"status.action.reply" = "Svar";
|
||||||
"status.action.section.your-post" = "Ditt innlegg";
|
"status.action.section.your-post" = "Ditt innlegg";
|
||||||
"status.action.share" = "Del dette innlegget";
|
"status.action.share" = "Del dette innlegget";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Fjern bokmerke";
|
"status.action.unbookmark" = "Fjern bokmerke";
|
||||||
"status.action.unboost" = "Opphev forsterkningen";
|
"status.action.unboost" = "Opphev forsterkningen";
|
||||||
"status.action.unfavorite" = "Ikke favoritt";
|
"status.action.unfavorite" = "Ikke favoritt";
|
||||||
|
|
|
@ -377,6 +377,9 @@
|
||||||
"status.action.reply" = "Antwoord";
|
"status.action.reply" = "Antwoord";
|
||||||
"status.action.section.your-post" = "Jouw post";
|
"status.action.section.your-post" = "Jouw post";
|
||||||
"status.action.share" = "Deel deze post";
|
"status.action.share" = "Deel deze post";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Verwijder bladwijzer";
|
"status.action.unbookmark" = "Verwijder bladwijzer";
|
||||||
"status.action.unboost" = "Maak boost ongedaan";
|
"status.action.unboost" = "Maak boost ongedaan";
|
||||||
"status.action.unfavorite" = "Verwijder favoriet";
|
"status.action.unfavorite" = "Verwijder favoriet";
|
||||||
|
|
|
@ -379,6 +379,9 @@
|
||||||
"status.action.reply" = "Odpowiedz";
|
"status.action.reply" = "Odpowiedz";
|
||||||
"status.action.section.your-post" = "Twój post";
|
"status.action.section.your-post" = "Twój post";
|
||||||
"status.action.share" = "Udostępnij ten post";
|
"status.action.share" = "Udostępnij ten post";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Usuń zakładkę";
|
"status.action.unbookmark" = "Usuń zakładkę";
|
||||||
"status.action.unboost" = "Cofnij podbicie";
|
"status.action.unboost" = "Cofnij podbicie";
|
||||||
"status.action.unfavorite" = "Usuń z polubionych";
|
"status.action.unfavorite" = "Usuń z polubionych";
|
||||||
|
|
|
@ -383,6 +383,9 @@
|
||||||
"status.action.reply" = "Responder";
|
"status.action.reply" = "Responder";
|
||||||
"status.action.section.your-post" = "Sua postagem";
|
"status.action.section.your-post" = "Sua postagem";
|
||||||
"status.action.share" = "Compartilhe esta postagem";
|
"status.action.share" = "Compartilhe esta postagem";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Remover dos salvos";
|
"status.action.unbookmark" = "Remover dos salvos";
|
||||||
"status.action.unboost" = "Unboost";
|
"status.action.unboost" = "Unboost";
|
||||||
"status.action.unfavorite" = "Desfavoritar";
|
"status.action.unfavorite" = "Desfavoritar";
|
||||||
|
|
|
@ -379,6 +379,9 @@
|
||||||
"status.action.reply" = "Cevapla";
|
"status.action.reply" = "Cevapla";
|
||||||
"status.action.section.your-post" = "Senin gönderin";
|
"status.action.section.your-post" = "Senin gönderin";
|
||||||
"status.action.share" = "Bu gönderiyi paylaş";
|
"status.action.share" = "Bu gönderiyi paylaş";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "Yer İmini Kaldır";
|
"status.action.unbookmark" = "Yer İmini Kaldır";
|
||||||
"status.action.unboost" = "Yükseltmeyi Kaldır";
|
"status.action.unboost" = "Yükseltmeyi Kaldır";
|
||||||
"status.action.unfavorite" = "Favoriyi Kaldır";
|
"status.action.unfavorite" = "Favoriyi Kaldır";
|
||||||
|
|
|
@ -384,6 +384,9 @@
|
||||||
"status.action.reply" = "回复";
|
"status.action.reply" = "回复";
|
||||||
"status.action.section.your-post" = "你的嘟文";
|
"status.action.section.your-post" = "你的嘟文";
|
||||||
"status.action.share" = "分享嘟文";
|
"status.action.share" = "分享嘟文";
|
||||||
|
"status.action.share-link" = "Share post link";
|
||||||
|
"status.action.share-image" = "Share post as image";
|
||||||
|
"status.action.share-title" = "Share";
|
||||||
"status.action.unbookmark" = "取消书签";
|
"status.action.unbookmark" = "取消书签";
|
||||||
"status.action.unboost" = "取消转发";
|
"status.action.unboost" = "取消转发";
|
||||||
"status.action.unfavorite" = "取消收藏";
|
"status.action.unfavorite" = "取消收藏";
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import NukeUI
|
import NukeUI
|
||||||
|
import Nuke
|
||||||
import Shimmer
|
import Shimmer
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct AvatarView: View {
|
public struct AvatarView: View {
|
||||||
|
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
|
||||||
@Environment(\.redactionReasons) private var reasons
|
@Environment(\.redactionReasons) private var reasons
|
||||||
@EnvironmentObject private var theme: Theme
|
@EnvironmentObject private var theme: Theme
|
||||||
|
|
||||||
|
@ -53,6 +55,12 @@ public struct AvatarView: View {
|
||||||
RoundedRectangle(cornerRadius: size.cornerRadius)
|
RoundedRectangle(cornerRadius: size.cornerRadius)
|
||||||
.fill(.gray)
|
.fill(.gray)
|
||||||
.frame(width: size.size.width, height: size.size.height)
|
.frame(width: size.size.width, height: size.size.height)
|
||||||
|
} else {
|
||||||
|
if isInCaptureMode, let image = Nuke.ImagePipeline.shared.cache.cachedImage(for: .init(url: url))?.image {
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: size.size.width, height: size.size.height)
|
||||||
} else {
|
} else {
|
||||||
LazyImage(url: url) { state in
|
LazyImage(url: url) { state in
|
||||||
if let image = state.image {
|
if let image = state.image {
|
||||||
|
@ -63,9 +71,11 @@ public struct AvatarView: View {
|
||||||
placeholderView
|
placeholderView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.animation(nil)
|
||||||
.frame(width: size.size.width, height: size.size.height)
|
.frame(width: size.size.width, height: size.size.height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.clipShape(clipShape)
|
.clipShape(clipShape)
|
||||||
.overlay(
|
.overlay(
|
||||||
clipShape.stroke(Color.primary.opacity(0.25), lineWidth: 1)
|
clipShape.stroke(Color.primary.opacity(0.25), lineWidth: 1)
|
||||||
|
|
|
@ -13,6 +13,10 @@ private struct IsCompact: EnvironmentKey {
|
||||||
static let defaultValue: Bool = false
|
static let defaultValue: Bool = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct IsInCaptureMode: EnvironmentKey {
|
||||||
|
static let defaultValue: Bool = false
|
||||||
|
}
|
||||||
|
|
||||||
public extension EnvironmentValues {
|
public extension EnvironmentValues {
|
||||||
var isSecondaryColumn: Bool {
|
var isSecondaryColumn: Bool {
|
||||||
get { self[SecondaryColumnKey.self] }
|
get { self[SecondaryColumnKey.self] }
|
||||||
|
@ -28,4 +32,9 @@ public extension EnvironmentValues {
|
||||||
get { self[IsCompact.self] }
|
get { self[IsCompact.self] }
|
||||||
set { self[IsCompact.self] = newValue }
|
set { self[IsCompact.self] = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isInCaptureMode: Bool {
|
||||||
|
get { self[IsInCaptureMode.self] }
|
||||||
|
set { self[IsInCaptureMode.self] = newValue }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ public enum SheetDestinations: Identifiable {
|
||||||
case settings
|
case settings
|
||||||
case accountPushNotficationsSettings
|
case accountPushNotficationsSettings
|
||||||
case report(status: Status)
|
case report(status: Status)
|
||||||
|
case shareImage(image: UIImage, status: Status)
|
||||||
|
|
||||||
public var id: String {
|
public var id: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -52,6 +53,8 @@ public enum SheetDestinations: Identifiable {
|
||||||
return "statusEditHistory"
|
return "statusEditHistory"
|
||||||
case .report:
|
case .report:
|
||||||
return "report"
|
return "report"
|
||||||
|
case .shareImage:
|
||||||
|
return "shareImage"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
import Models
|
import Models
|
||||||
|
import Nuke
|
||||||
import NukeUI
|
import NukeUI
|
||||||
import Shimmer
|
import Shimmer
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct StatusRowCardView: View {
|
public struct StatusRowCardView: View {
|
||||||
@EnvironmentObject private var theme: Theme
|
|
||||||
@Environment(\.openURL) private var openURL
|
@Environment(\.openURL) private var openURL
|
||||||
|
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
|
||||||
|
|
||||||
|
@EnvironmentObject private var theme: Theme
|
||||||
let card: Card
|
let card: Card
|
||||||
|
|
||||||
public init(card: Card) {
|
public init(card: Card) {
|
||||||
|
@ -16,8 +19,10 @@ public struct StatusRowCardView: View {
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
if let title = card.title, let url = URL(string: card.url) {
|
if let title = card.title, let url = URL(string: card.url) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
if let imageURL = card.image {
|
if let imageURL = card.image, !isInCaptureMode {
|
||||||
GeometryReader { proxy in
|
GeometryReader { proxy in
|
||||||
|
let processors: [ImageProcessing] = [.resize(size: .init(width: proxy.frame(in: .local).width,
|
||||||
|
height: 200))]
|
||||||
LazyImage(url: imageURL) { state in
|
LazyImage(url: imageURL) { state in
|
||||||
if let image = state.image {
|
if let image = state.image {
|
||||||
image
|
image
|
||||||
|
@ -32,6 +37,7 @@ public struct StatusRowCardView: View {
|
||||||
.frame(height: 200)
|
.frame(height: 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.processors(processors)
|
||||||
}
|
}
|
||||||
.frame(height: 200)
|
.frame(height: 200)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import Env
|
import Env
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import DesignSystem
|
||||||
|
import Network
|
||||||
|
|
||||||
struct StatusRowContextMenu: View {
|
struct StatusRowContextMenu: View {
|
||||||
|
@Environment(\.displayScale) var displayScale
|
||||||
|
|
||||||
|
@EnvironmentObject private var sceneDelegate: SceneDelegate
|
||||||
@EnvironmentObject private var preferences: UserPreferences
|
@EnvironmentObject private var preferences: UserPreferences
|
||||||
@EnvironmentObject private var account: CurrentAccount
|
@EnvironmentObject private var account: CurrentAccount
|
||||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||||
|
@ -56,6 +61,7 @@ struct StatusRowContextMenu: View {
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
|
Menu("status.action.share-title") {
|
||||||
if let urlString = viewModel.status.reblog?.url ?? viewModel.status.url,
|
if let urlString = viewModel.status.reblog?.url ?? viewModel.status.url,
|
||||||
let url = URL(string: urlString)
|
let url = URL(string: urlString)
|
||||||
{
|
{
|
||||||
|
@ -64,6 +70,37 @@ struct StatusRowContextMenu: View {
|
||||||
message: Text(viewModel.status.reblog?.content.asRawText ?? viewModel.status.content.asRawText)) {
|
message: Text(viewModel.status.reblog?.content.asRawText ?? viewModel.status.content.asRawText)) {
|
||||||
Label("status.action.share", systemImage: "square.and.arrow.up")
|
Label("status.action.share", systemImage: "square.and.arrow.up")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShareLink(item: url) {
|
||||||
|
Label("status.action.share-link", systemImage: "link")
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
let view = HStack {
|
||||||
|
StatusRowView(viewModel: viewModel)
|
||||||
|
.padding(16)
|
||||||
|
}
|
||||||
|
.environment(\.isInCaptureMode, true)
|
||||||
|
.environmentObject(Theme.shared)
|
||||||
|
.environmentObject(preferences)
|
||||||
|
.environmentObject(account)
|
||||||
|
.environmentObject(currentInstance)
|
||||||
|
.environmentObject(SceneDelegate())
|
||||||
|
.environmentObject(QuickLook())
|
||||||
|
.environmentObject(viewModel.client)
|
||||||
|
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
|
||||||
|
.background(Theme.shared.primaryBackgroundColor)
|
||||||
|
.cornerRadius(4)
|
||||||
|
.frame(width: sceneDelegate.windowWidth)
|
||||||
|
let renderer = ImageRenderer(content: view)
|
||||||
|
renderer.scale = displayScale
|
||||||
|
if let image = renderer.uiImage {
|
||||||
|
viewModel.routerPath.presentedSheet = .shareImage(image: image, status: viewModel.status)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("status.action.share-image", systemImage: "photo")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let url = URL(string: viewModel.status.reblog?.url ?? viewModel.status.url ?? "") {
|
if let url = URL(string: viewModel.status.reblog?.url ?? viewModel.status.url ?? "") {
|
||||||
|
@ -144,3 +181,13 @@ struct StatusRowContextMenu: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ActivityView: UIViewControllerRepresentable {
|
||||||
|
let image: Image
|
||||||
|
|
||||||
|
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||||
|
return UIActivityViewController(activityItems: [image], applicationActivities: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Models
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct StatusRowHeaderView: View {
|
struct StatusRowHeaderView: View {
|
||||||
|
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
|
||||||
@EnvironmentObject private var theme: Theme
|
@EnvironmentObject private var theme: Theme
|
||||||
|
|
||||||
let status: AnyStatus
|
let status: AnyStatus
|
||||||
|
@ -17,9 +18,11 @@ struct StatusRowHeaderView: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
if !isInCaptureMode {
|
||||||
threadIcon
|
threadIcon
|
||||||
contextMenuButton
|
contextMenuButton
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.accessibilityElement()
|
.accessibilityElement()
|
||||||
.accessibilityLabel(Text("\(status.account.displayName)"))
|
.accessibilityLabel(Text("\(status.account.displayName)"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,9 @@ import SwiftUI
|
||||||
|
|
||||||
public struct StatusRowMediaPreviewView: View {
|
public struct StatusRowMediaPreviewView: View {
|
||||||
@Environment(\.openURL) private var openURL
|
@Environment(\.openURL) private var openURL
|
||||||
@Environment(\.isSecondaryColumn) private var isSecondaryColumn
|
@Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool
|
||||||
@Environment(\.extraLeadingInset) private var extraLeadingInset: CGFloat
|
@Environment(\.extraLeadingInset) private var extraLeadingInset: CGFloat
|
||||||
|
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
|
||||||
|
|
||||||
@EnvironmentObject var sceneDelegate: SceneDelegate
|
@EnvironmentObject var sceneDelegate: SceneDelegate
|
||||||
@EnvironmentObject private var preferences: UserPreferences
|
@EnvironmentObject private var preferences: UserPreferences
|
||||||
|
@ -150,8 +151,19 @@ public struct StatusRowMediaPreviewView: View {
|
||||||
let size: CGSize = size(for: attachment) ?? .init(width: imageMaxHeight, height: imageMaxHeight)
|
let size: CGSize = size(for: attachment) ?? .init(width: imageMaxHeight, height: imageMaxHeight)
|
||||||
let newSize = imageSize(from: size,
|
let newSize = imageSize(from: size,
|
||||||
newWidth: availableWidth - appLayoutWidth)
|
newWidth: availableWidth - appLayoutWidth)
|
||||||
|
let processors: [ImageProcessing] = [.resize(size: .init(width: newSize.width, height: newSize.height))]
|
||||||
switch attachment.supportedType {
|
switch attachment.supportedType {
|
||||||
case .image:
|
case .image:
|
||||||
|
if isInCaptureMode,
|
||||||
|
let image = Nuke.ImagePipeline.shared.cache.cachedImage(for: .init(url: attachment.url,
|
||||||
|
processors: processors))?.image {
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.frame(width: newSize.width, height: newSize.height)
|
||||||
|
.clipped()
|
||||||
|
.cornerRadius(4)
|
||||||
|
} else {
|
||||||
LazyImage(url: attachment.url) { state in
|
LazyImage(url: attachment.url) { state in
|
||||||
if let image = state.image {
|
if let image = state.image {
|
||||||
image
|
image
|
||||||
|
@ -168,6 +180,7 @@ public struct StatusRowMediaPreviewView: View {
|
||||||
}
|
}
|
||||||
.processors([.resize(size: .init(width: newSize.width, height: newSize.height))])
|
.processors([.resize(size: .init(width: newSize.width, height: newSize.height))])
|
||||||
.frame(width: newSize.width, height: newSize.height)
|
.frame(width: newSize.width, height: newSize.height)
|
||||||
|
}
|
||||||
|
|
||||||
case .gifv, .video, .audio:
|
case .gifv, .video, .audio:
|
||||||
if let url = attachment.url {
|
if let url = attachment.url {
|
||||||
|
@ -177,10 +190,10 @@ public struct StatusRowMediaPreviewView: View {
|
||||||
case .none:
|
case .none:
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
if sensitive {
|
if !isInCaptureMode, sensitive {
|
||||||
cornerSensitiveButton
|
cornerSensitiveButton
|
||||||
}
|
}
|
||||||
if let alt = attachment.description, !alt.isEmpty, !isNotifications, preferences.showAltTextForMedia {
|
if !isInCaptureMode, let alt = attachment.description, !alt.isEmpty, !isNotifications, preferences.showAltTextForMedia {
|
||||||
Group {
|
Group {
|
||||||
Button {
|
Button {
|
||||||
altTextDisplayed = alt
|
altTextDisplayed = alt
|
||||||
|
@ -207,7 +220,18 @@ public struct StatusRowMediaPreviewView: View {
|
||||||
switch type {
|
switch type {
|
||||||
case .image:
|
case .image:
|
||||||
let width = isNotifications ? imageMaxHeight : proxy.frame(in: .local).width
|
let width = isNotifications ? imageMaxHeight : proxy.frame(in: .local).width
|
||||||
|
let processors: [ImageProcessing] = [.resize(size: .init(width: width, height: imageMaxHeight))]
|
||||||
ZStack(alignment: .bottomTrailing) {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
|
if isInCaptureMode,
|
||||||
|
let image = Nuke.ImagePipeline.shared.cache.cachedImage(for: .init(url: attachment.previewUrl ?? attachment.url, processors: processors))?.image {
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.frame(maxWidth: width)
|
||||||
|
.frame(maxHeight: imageMaxHeight)
|
||||||
|
.clipped()
|
||||||
|
.cornerRadius(4)
|
||||||
|
} else {
|
||||||
LazyImage(url: attachment.previewUrl ?? attachment.url) { state in
|
LazyImage(url: attachment.previewUrl ?? attachment.url) { state in
|
||||||
if let image = state.image {
|
if let image = state.image {
|
||||||
image
|
image
|
||||||
|
@ -224,11 +248,16 @@ public struct StatusRowMediaPreviewView: View {
|
||||||
.frame(maxWidth: width)
|
.frame(maxWidth: width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.processors([.resize(size: .init(width: width, height: imageMaxHeight))])
|
.processors(processors)
|
||||||
if sensitive {
|
}
|
||||||
|
if sensitive, !isInCaptureMode {
|
||||||
cornerSensitiveButton
|
cornerSensitiveButton
|
||||||
}
|
}
|
||||||
if let alt = attachment.description, !alt.isEmpty, !isNotifications, preferences.showAltTextForMedia {
|
if !isInCaptureMode,
|
||||||
|
let alt = attachment.description,
|
||||||
|
!alt.isEmpty,
|
||||||
|
!isNotifications,
|
||||||
|
preferences.showAltTextForMedia {
|
||||||
Button {
|
Button {
|
||||||
altTextDisplayed = alt
|
altTextDisplayed = alt
|
||||||
isAltAlertDisplayed = true
|
isAltAlertDisplayed = true
|
||||||
|
|
Loading…
Reference in a new issue