aboutsummaryrefslogtreecommitdiff
path: root/Hotline/macOS/Files/FilePreviewImageView.swift
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2025-10-27 21:48:20 -0700
committerDustin Mierau <dustin@mierau.me>2025-10-27 21:48:20 -0700
commit4bb0ffba596e41a8309ba50d222248a0ba3eb42e (patch)
treefcd1c2b11c071a624d0b3b56ed16340272200c7d /Hotline/macOS/Files/FilePreviewImageView.swift
parent924012e6ee03fff5570e9362e29be9b04816b777 (diff)
Random project housekeeping/organizing source files.
Diffstat (limited to 'Hotline/macOS/Files/FilePreviewImageView.swift')
-rw-r--r--Hotline/macOS/Files/FilePreviewImageView.swift123
1 files changed, 123 insertions, 0 deletions
diff --git a/Hotline/macOS/Files/FilePreviewImageView.swift b/Hotline/macOS/Files/FilePreviewImageView.swift
new file mode 100644
index 0000000..9beeb80
--- /dev/null
+++ b/Hotline/macOS/Files/FilePreviewImageView.swift
@@ -0,0 +1,123 @@
+import SwiftUI
+import UniformTypeIdentifiers
+
+struct FilePreviewImageView: View {
+ enum FilePreviewFocus: Hashable {
+ case window
+ }
+
+ @Environment(\.controlActiveState) private var controlActiveState
+ @Environment(\.colorScheme) private var colorScheme
+ @Environment(\.dismiss) var dismiss
+
+ @Binding var info: PreviewFileInfo?
+
+ @State var preview: FilePreview? = nil
+ @FocusState private var focusField: FilePreviewFocus?
+
+ var body: some View {
+ Group {
+ if preview?.state != .loaded {
+ HStack(alignment: .center, spacing: 0) {
+ ProgressView(value: max(0.0, min(1.0, preview?.progress ?? 0.0)))
+ .focusable(false)
+ .progressViewStyle(.circular)
+ .controlSize(.extraLarge)
+ .tint(.white)
+ .frame(maxWidth: 300, alignment: .center)
+ }
+ .frame(minWidth: 350, maxWidth: 350, minHeight: 150, maxHeight: 150)
+ .padding()
+ }
+ else {
+ if let image = preview?.image {
+ FileImageView(image: image)
+ .frame(minWidth: 200, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)
+ }
+ else {
+ VStack(alignment: .center, spacing: 0) {
+ Spacer()
+
+ Image(systemName: "eye.trianglebadge.exclamationmark")
+ .resizable()
+ .scaledToFit()
+ .frame(maxWidth: .infinity)
+ .frame(height: 48)
+ .padding(.bottom)
+ Group {
+ Text("This file type is not previewable")
+ .bold()
+ Text("Try downloading and opening this file in another application.")
+ .foregroundStyle(Color.secondary)
+ }
+ .font(.system(size: 14.0))
+ .frame(maxWidth: 300)
+ .multilineTextAlignment(.center)
+
+ Spacer()
+ }
+ .frame(minWidth: 350, maxWidth: 350, minHeight: 150, maxHeight: 150)
+ .padding()
+ }
+ }
+ }
+ .focusable()
+ .focusEffectDisabled()
+ .focused($focusField, equals: .window)
+ .preferredColorScheme(.dark)
+ .navigationTitle(info?.name ?? "Preview")
+ .background(.black)
+ .toolbar {
+ ToolbarItem(placement: .navigation) {
+ FileIconView(filename: info?.name ?? "", fileType: nil)
+ .frame(width: 16, height: 16)
+ .opacity(controlActiveState == .inactive ? 0.5 : 1.0)
+ }
+
+ if let img = preview?.image {
+ if let info = info {
+ ToolbarItem(placement: .primaryAction) {
+ Button {
+ let _ = preview?.data?.saveAsFileToDownloads(filename: info.name)
+ } label: {
+ Label("Download Image...", systemImage: "arrow.down")
+ }
+ .help("Download Image")
+ }
+
+ ToolbarItem(placement: .primaryAction) {
+ ShareLink(item: img, preview: SharePreview(info.name, image: img)) {
+ Label("Share Image...", systemImage: "square.and.arrow.up")
+ }
+ .help("Share Image")
+ }
+ }
+ }
+ }
+ .task {
+ if let info = info {
+ preview = FilePreview(info: info)
+ preview?.download()
+ }
+ }
+ .onAppear {
+ if info == nil {
+ Task {
+ dismiss()
+ }
+ return
+ }
+
+ focusField = .window
+ }
+ .onDisappear {
+ preview?.cancel()
+ dismiss()
+ }
+ .onChange(of: preview?.state) {
+ if preview?.state == .failed {
+ dismiss()
+ }
+ }
+ }
+}