diff options
Diffstat (limited to 'Sora/Views/Post/Details')
| -rw-r--r-- | Sora/Views/Post/Details/PostDetailsImageView.swift | 106 | ||||
| -rw-r--r-- | Sora/Views/Post/Details/PostDetailsView.swift | 87 |
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 + } +} |