import NetworkImage import SwiftUI struct PostDetailsImageView: View { @EnvironmentObject var settings: Settings @EnvironmentObject var manager: BooruManager var url: URL? @Binding var loadingState: BooruPostLoadingState var finalLoadingState: BooruPostLoadingState let placeholder: () -> Placeholder let post: BooruPost? @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 { let content = NetworkImage( url: url, transaction: Transaction(animation: .default) ) { image in InteractiveImageView(image: image) .onAppear { loadingState = finalLoadingState } .background(.background) .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(macOS) Button { saveImageToPicturesFolder() } label: { Label("Save Image", systemImage: "square.and.arrow.down") } .keyboardShortcut("s", modifiers: [.command]) #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 Button { openURL(postURL(for: post?.id ?? "")) } label: { Label("Open Post in Safari", systemImage: "safari") } if let source = post?.source { Button { openURL(URL(string: source)!) } label: { Label("Open Source in Safari", systemImage: "safari") } } } } placeholder: { placeholder() .onAppear { loadingState = .loadingPreview } } #if os(macOS) return content.overlay( Button(action: saveImageToPicturesFolder) { EmptyView() } .keyboardShortcut("s", modifiers: [.command]) .frame(width: 0, height: 0) .opacity(0) ) #else return content #endif } init( url: URL?, loadingStage: Binding, finalLoadingState: BooruPostLoadingState = .loadingFile, post: BooruPost? = 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.placeholder = placeholder self.post = post } private func postURL(for id: String) -> URL { switch manager.flavor { case .moebooru: return URL(string: "https://\(manager.domain)/post/show/\(id)")! case .gelbooru: return URL(string: "https://\(manager.domain)/index.php?page=post&s=view&id=\(id)")! } } #if os(macOS) private func saveImageToPicturesFolder() { guard let url = self.url else { return } URLSession.shared.dataTask(with: url) { data, _, _ in guard let data, let post else { return } let picturesURL = FileManager.default.homeDirectoryForCurrentUser .appendingPathComponent("Pictures/Sora/\(manager.provider.rawValue)") do { try FileManager.default.createDirectory( at: picturesURL, withIntermediateDirectories: true ) try data.write( to: picturesURL .appendingPathComponent( "\(post.id)_\(settings.detailViewType.rawValue.lowercased()).\(url.pathExtension)" ) ) if settings.saveTagsToFile { try post.tags.joined(separator: "\n").write( to: picturesURL.appendingPathComponent( "\(post.id).txt" ), atomically: true, encoding: .utf8 ) } } catch { print("PostDetailsImageView.saveImageToPicturesFolder: \(error)") } } .resume() } #endif private func openURL(_ url: URL) { #if os(iOS) UIApplication.shared.open(url) #else NSWorkspace.shared.open(url) #endif } }