From f968ccad2987afda73447efe95f42908a5827747 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sun, 4 May 2025 20:57:49 +0200 Subject: [PATCH] First actions for generation view --- CHDataManagement.xcodeproj/project.pbxproj | 4 ++ .../Generation/GenerationContentView.swift | 17 ++++- .../Generation/GenerationIssuesView.swift | 65 +++++++++++++++++++ .../Views/Generic/ColoredButton.swift | 45 +++++++++++++ 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 CHDataManagement/Views/Generation/GenerationIssuesView.swift create mode 100644 CHDataManagement/Views/Generic/ColoredButton.swift diff --git a/CHDataManagement.xcodeproj/project.pbxproj b/CHDataManagement.xcodeproj/project.pbxproj index 16b70f6..629e524 100644 --- a/CHDataManagement.xcodeproj/project.pbxproj +++ b/CHDataManagement.xcodeproj/project.pbxproj @@ -208,6 +208,7 @@ E2F3B39C2DC5542E00CFA712 /* LabelEditingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F3B39B2DC5542E00CFA712 /* LabelEditingView.swift */; }; E2F3B39E2DC55B1C00CFA712 /* LabelCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F3B39D2DC55B1C00CFA712 /* LabelCreationView.swift */; }; E2F3B3A22DC769C300CFA712 /* DeleteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F3B3A12DC769BF00CFA712 /* DeleteButton.swift */; }; + E2F3B3A42DC7DC2400CFA712 /* GenerationIssuesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F3B3A32DC7DC1F00CFA712 /* GenerationIssuesView.swift */; }; E2FD1D0D2D2DBBA600B48627 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D0C2D2DBBA100B48627 /* LinkPreview.swift */; }; E2FD1D192D2DC4F500B48627 /* LoadingContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D182D2DC4F500B48627 /* LoadingContext.swift */; }; E2FD1D1B2D2DC63800B48627 /* LinkPreviewDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FD1D1A2D2DC62C00B48627 /* LinkPreviewDetailView.swift */; }; @@ -492,6 +493,7 @@ E2F3B39B2DC5542E00CFA712 /* LabelEditingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelEditingView.swift; sourceTree = ""; }; E2F3B39D2DC55B1C00CFA712 /* LabelCreationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCreationView.swift; sourceTree = ""; }; E2F3B3A12DC769BF00CFA712 /* DeleteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteButton.swift; sourceTree = ""; }; + E2F3B3A32DC7DC1F00CFA712 /* GenerationIssuesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationIssuesView.swift; sourceTree = ""; }; E2FD1D0C2D2DBBA100B48627 /* LinkPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreview.swift; sourceTree = ""; }; E2FD1D182D2DC4F500B48627 /* LoadingContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingContext.swift; sourceTree = ""; }; E2FD1D1A2D2DC62C00B48627 /* LinkPreviewDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewDetailView.swift; sourceTree = ""; }; @@ -601,6 +603,7 @@ E20BCCA02D53985500B8DBEB /* Generation */ = { isa = PBXGroup; children = ( + E2F3B3A32DC7DC1F00CFA712 /* GenerationIssuesView.swift */, E20BCCAE2D53F4A500B8DBEB /* GenerationStringIssuesView.swift */, E20BCCAC2D53F48100B8DBEB /* IssueStatus.swift */, E20BCCAA2D53B85300B8DBEB /* GenerationResultsIssueView.swift */, @@ -1560,6 +1563,7 @@ E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */, E2B85F572C4BD0BB0047CD0C /* Binding+Extension.swift in Sources */, E2B85F432C4294F60047CD0C /* FeedEntry.swift in Sources */, + E2F3B3A42DC7DC2400CFA712 /* GenerationIssuesView.swift in Sources */, E25DA56D2D00EBCF00AEF16D /* NavigationBarSettingsView.swift in Sources */, E25DA5272CFF745700AEF16D /* URL+Extensions.swift in Sources */, E2FE0F642D2C2F4D002963B7 /* ButtonBlock.swift in Sources */, diff --git a/CHDataManagement/Views/Generation/GenerationContentView.swift b/CHDataManagement/Views/Generation/GenerationContentView.swift index f37743b..5bfd5d5 100644 --- a/CHDataManagement/Views/Generation/GenerationContentView.swift +++ b/CHDataManagement/Views/Generation/GenerationContentView.swift @@ -86,10 +86,16 @@ struct GenerationContentView: View { text: "draft posts", statusWhenNonEmpty: .warning, items: draftPosts) { $0.id } - GenerationStringIssuesView( + GenerationIssuesView( text: "additional output files", statusWhenNonEmpty: .warning, - items: content.results.unusedFilesInOutput) + items: $content.results.unusedFilesInOutput) { filePath in + HStack { + Text(filePath) + Spacer() + Button("Delete", action: { delete(unusedFile: filePath) }) + } + } GenerationStringIssuesView( text: "inaccessible files", items: content.results.inaccessibleFiles) { $0.id } @@ -134,6 +140,13 @@ struct GenerationContentView: View { } }.padding() } + + private func delete(unusedFile: String) { + guard content.storage.deleteInOutputFolder(unusedFile) else { + return + } + content.results.unusedFilesInOutput.remove(unusedFile) + } } #Preview { diff --git a/CHDataManagement/Views/Generation/GenerationIssuesView.swift b/CHDataManagement/Views/Generation/GenerationIssuesView.swift new file mode 100644 index 0000000..109414f --- /dev/null +++ b/CHDataManagement/Views/Generation/GenerationIssuesView.swift @@ -0,0 +1,65 @@ +import SwiftUI + +struct GenerationIssuesView: View where S: Collection, T: Hashable & Comparable, V: View, S.Element == T { + + let text: String + + let statusWhenNonEmpty: IssueStatus + + @Binding + var items: S + + let map: (T) -> V + + @State + private var showList = false + + var status: IssueStatus { + items.isEmpty ? .nominal : statusWhenNonEmpty + } + + init(text: String, statusWhenNonEmpty: IssueStatus = .error, items: Binding, map: @escaping (T) -> V) { + self.text = text + self.statusWhenNonEmpty = statusWhenNonEmpty + self._items = items + self.map = map + } + + var body: some View { + HStack { + Button(action: showListIfNonEmpty) { + Image(systemSymbol: status.symbol) + .foregroundStyle(status.color) + }.buttonStyle(.plain) + Text("\(items.count) \(text)") + } + .sheet(isPresented: $showList) { + VStack { + Text("\(items.count) \(text)") + .font(.title) + List(items.sorted(), id: \.self) { item in + map(item) + } + .frame(minHeight: 400) + Button("Close") { showList = false } + }.padding() + } + } + + private func showListIfNonEmpty() { + guard !items.isEmpty else { + return + } + showList = true + } +} + +extension GenerationIssuesView where S == Set, V == Text { + + init(text: String, statusWhenNonEmpty: IssueStatus = .error, items: Binding>) { + self.text = text + self.statusWhenNonEmpty = statusWhenNonEmpty + self._items = items + self.map = { Text($0) } + } +} diff --git a/CHDataManagement/Views/Generic/ColoredButton.swift b/CHDataManagement/Views/Generic/ColoredButton.swift new file mode 100644 index 0000000..c625564 --- /dev/null +++ b/CHDataManagement/Views/Generic/ColoredButton.swift @@ -0,0 +1,45 @@ +import SwiftUI +import SFSafeSymbols + +struct ColoredButton: View { + + let icon: SFSymbol + + let text: LocalizedStringKey + + let fillColor: Color + + let textColor: Color + + let action: () -> Void + + init(icon: SFSymbol, text: LocalizedStringKey, fillColor: Color = .blue, textColor: Color = .white, action: @escaping () -> Void) { + self.icon = icon + self.text = text + self.fillColor = fillColor + self.textColor = textColor + self.action = action + } + + init(delete: @escaping () -> Void) { + self.icon = .trash + self.text = "Delete" + self.fillColor = .red + self.textColor = .white + self.action = delete + } + + var body: some View { + Button(action: action) { + HStack { + Spacer() + Image(systemSymbol: icon) + Text(text) + .padding(.vertical, 8) + Spacer() + } + .foregroundStyle(textColor) + .background(RoundedRectangle(cornerRadius: 8).fill(fillColor)) + }.buttonStyle(.plain) + } +}