summaryrefslogtreecommitdiff
path: root/Sora/Views/Post/Details
diff options
context:
space:
mode:
Diffstat (limited to 'Sora/Views/Post/Details')
-rw-r--r--Sora/Views/Post/Details/PostDetailsImageView.swift106
-rw-r--r--Sora/Views/Post/Details/PostDetailsView.swift87
2 files changed, 193 insertions, 0 deletions
diff --git a/Sora/Views/Post/Details/PostDetailsImageView.swift b/Sora/Views/Post/Details/PostDetailsImageView.swift
new file mode 100644
index 0000000..d6307d5
--- /dev/null
+++ b/Sora/Views/Post/Details/PostDetailsImageView.swift
@@ -0,0 +1,106 @@
+import SwiftUI
+
+struct PostDetailsImageView<Placeholder: View>: View {
+ @EnvironmentObject var settings: Settings
+ var url: URL?
+ @Binding var loadingState: BooruPostLoadingState
+ var finalLoadingState: BooruPostLoadingState
+ var postURL: URL?
+ let placeholder: () -> Placeholder
+ @State private var currentScale: CGFloat = 1.0
+ @State private var finalScale: CGFloat = 1.0
+ @State private var currentOffset: CGSize = .zero
+ @State private var finalOffset: CGSize = .zero
+
+ #if os(iOS)
+ var keyWindow: UIWindow? {
+ guard
+ let window = UIApplication.shared.connectedScenes
+ .compactMap({ $0 as? UIWindowScene })
+ .flatMap(\.windows)
+ .first(where: \.isKeyWindow)
+ else {
+ return nil
+ }
+
+ return window
+ }
+ #endif
+
+ var body: some View {
+ AsyncImage(url: url) { image in
+ InteractiveImageView(image: image)
+ .onAppear {
+ loadingState = finalLoadingState
+ }
+ .contextMenu {
+ #if os(iOS)
+ Button {
+ guard let url else { return }
+
+ URLSession.shared.dataTask(with: url) { data, _, _ in
+ guard let data, let uiImage = UIImage(data: data) else { return }
+
+ UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
+ }
+ .resume()
+ } label: {
+ Label("Save Image", systemImage: "square.and.arrow.down")
+ }
+ #endif
+
+ #if os(iOS)
+ if settings.enableShareShortcut {
+ Button {
+ keyWindow?.rootViewController?.present(
+ UIActivityViewController(
+ activityItems: [url ?? URL(string: "")!], applicationActivities: nil
+ ), animated: true
+ )
+ } label: {
+ Label("Share Image", systemImage: "square.and.arrow.up")
+ }
+ }
+ #endif
+
+ if let url = postURL {
+ Button {
+ #if os(iOS)
+ UIApplication.shared.open(url)
+ #else
+ NSWorkspace.shared.open(url)
+ #endif
+ } label: {
+ Label("Open in Safari", systemImage: "safari")
+ }
+ }
+ }
+ } placeholder: {
+ placeholder()
+ .onAppear {
+ loadingState = .loadingPreview
+ }
+ }
+ }
+
+ init(
+ url: URL?,
+ loadingStage: Binding<BooruPostLoadingState>,
+ finalLoadingState: BooruPostLoadingState = .loadingFile,
+ postURL: URL? = nil,
+ @ViewBuilder placeholder: @escaping () -> Placeholder = {
+ GeometryReader { geometry in
+ ProgressView()
+ .frame(width: geometry.size.width, height: geometry.size.height)
+ .position(x: geometry.size.width / 2, y: geometry.size.height / 2)
+ .padding()
+ }
+ }
+ ) {
+ self.url = url
+ _loadingState = loadingStage
+ self.finalLoadingState = finalLoadingState
+ self.postURL = postURL
+ self.placeholder = placeholder
+ }
+}
diff --git a/Sora/Views/Post/Details/PostDetailsView.swift b/Sora/Views/Post/Details/PostDetailsView.swift
new file mode 100644
index 0000000..3c004ca
--- /dev/null
+++ b/Sora/Views/Post/Details/PostDetailsView.swift
@@ -0,0 +1,87 @@
+import SwiftUI
+
+struct PostDetailsView: View {
+ @EnvironmentObject var settings: Settings
+ let post: BooruPost
+ @State private var loadingStage: BooruPostLoadingState = .loadingPreview
+ private var imageURL: URL? {
+ switch settings.detailViewType {
+ case .preview:
+ post.previewURL
+
+ case .sample:
+ post.sampleURL
+
+ case .original:
+ post.fileURL
+ }
+ }
+
+ var body: some View {
+ VStack(spacing: 0) {
+ PostDetailsImageView(
+ url: imageURL,
+ loadingStage: $loadingStage,
+ finalLoadingState: .loaded,
+ postURL: URL(string: "https://yande.re/post/show/\(post.id)")!
+ ) {
+ PostDetailsImageView(
+ url: post.previewURL,
+ loadingStage: $loadingStage
+ )
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
+ .id(post.previewURL)
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
+ .id(imageURL)
+ .padding(0)
+ .zIndex(0)
+
+ VStack(spacing: 5) {
+ HStack {
+ Text(post.tags.joined(separator: ", "))
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ HStack {
+ Text(
+ post.createdAt.formatted()
+ )
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Group {
+ switch loadingStage {
+ case .loadingPreview:
+ Text("Loading preview …")
+
+ case .loadingFile:
+ Text("Loading \(settings.detailViewType.rawValue) …")
+
+ case .loaded:
+ EmptyView()
+ }
+ }
+ .padding(.trailing, 5)
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .foregroundStyle(.secondary)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 10 / 1.33)
+ .textSelection(.enabled)
+ .font(.footnote)
+ #if os(iOS)
+ .background(.ultraThinMaterial)
+ #else
+ .background(.opacity(0.1))
+ #endif
+ .zIndex(1)
+ }
+ .navigationTitle("Details")
+ #if os(iOS)
+ .navigationBarTitleDisplayMode(.inline)
+ .toolbarBackground(.visible, for: .navigationBar)
+ .toolbarBackground(.ultraThinMaterial, for: .navigationBar)
+ #endif
+ }
+}