ChWebsiteApp/CHDataManagement/Views/Files/FileDetailView.swift
2025-01-05 09:21:21 +01:00

170 lines
5.1 KiB
Swift

import SwiftUI
struct FileDetailView: View {
@EnvironmentObject
private var content: Content
@ObservedObject
var file: FileResource
@State
private var showFileSelection = false
@State
private var selectedFile: FileResource?
var body: some View {
VStack(alignment: .leading) {
DetailTitle(
title: "File",
text: "A file that can be used in a post or page")
VStack(alignment: .leading) {
Button("Show in Finder", action: showFileInFinder)
Button("Mark as changed", action: markFileAsChanged)
Button("Delete resource", action: deleteFile)
if file.isExternallyStored {
Button("Import file", action: replaceFile)
} else {
Button("Replace file", action: replaceFile)
Button("Make external", action: convertToExternal)
}
}
IdPropertyView(
id: $file.id,
title: "Name",
footer: "The unique name of the file, which is also used to reference it in posts and pages.",
validation: file.isValid,
update: { file.update(id: $0) })
StringPropertyView(
title: "German Description",
text: $file.german,
footer: "The description for the file in German. Descriptions are used for images and to explain the content of a file.")
StringPropertyView(
title: "English Description",
text: $file.english,
footer: "The description for the file in English. Descriptions are used for images and to explain the content of a file.")
if let imageDimensions = file.imageDimensions {
GenericPropertyView(title: "Image dimensions") {
Text("\(Int(imageDimensions.width)) x \(Int(imageDimensions.height)) (\(file.aspectRatio))")
}
#warning("Add button to show image versions")
}
if let fileSize = file.fileSize {
GenericPropertyView(title: "File size") {
Text(formatBytes(fileSize))
}
}
Spacer()
}.padding()
.onAppear {
if file.fileSize == nil {
file.determineFileSize()
}
}
}
private func formatBytes(_ bytes: Int) -> String {
let formatter = ByteCountFormatter()
formatter.allowedUnits = [.useMB, .useKB, .useBytes] // Customize units if needed
formatter.countStyle = .file
return formatter.string(fromByteCount: Int64(bytes))
}
private func showFileInFinder() {
content.storage.openFinderWindow(withSelectedFile: file.id)
}
private func markFileAsChanged() {
DispatchQueue.main.async {
file.determineImageDimensions()
file.determineFileSize()
// Force regeneration of images and/or file copying
file.removeFileFromOutputFolder()
// Trigger content view update to reload image
file.didChange()
}
}
private func replaceFile() {
guard let url = openFilePanel() else {
print("File '\(file.id)': No file selected as replacement")
return
}
guard content.storage.importExternalFile(at: url, fileId: file.id) else {
print("File '\(file.id)': Failed to replace file")
return
}
markFileAsChanged()
if file.isExternallyStored {
DispatchQueue.main.async {
file.isExternallyStored = false
}
}
}
private func openFilePanel() -> URL? {
let panel = NSOpenPanel()
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.allowsMultipleSelection = false
panel.showsHiddenFiles = false
panel.title = "Select file to replace"
panel.prompt = ""
let response = panel.runModal()
guard response == .OK else {
print("File '\(file.id)': Failed to select file to replace")
return nil
}
return panel.url
}
private func convertToExternal() {
guard !file.isExternallyStored else {
return
}
guard content.storage.delete(file: file.id) else {
print("File '\(file.id)': Failed to delete file to make it external")
return
}
DispatchQueue.main.async {
file.fileSize = nil
file.isExternallyStored = true
}
}
private func deleteFile() {
if !file.isExternallyStored {
guard content.storage.delete(file: file.id) else {
print("File '\(file.id)': Failed to delete file in content folder")
return
}
}
content.remove(file)
}
}
extension FileDetailView: MainContentView {
init(item: FileResource) {
self.init(file: item)
}
static let itemDescription = "a file"
}
#Preview {
FileDetailView(file: .mock)
}