Improve display of generation results
This commit is contained in:
parent
156bbf77d1
commit
2b88584ba1
@ -13,6 +13,7 @@
|
|||||||
E20BCC9D2D5382F000B8DBEB /* SettingsSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20BCC9C2D5382ED00B8DBEB /* SettingsSheet.swift */; };
|
E20BCC9D2D5382F000B8DBEB /* SettingsSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20BCC9C2D5382ED00B8DBEB /* SettingsSheet.swift */; };
|
||||||
E20BCC9F2D53851400B8DBEB /* SelectableListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20BCC9E2D53850A00B8DBEB /* SelectableListItem.swift */; };
|
E20BCC9F2D53851400B8DBEB /* SelectableListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20BCC9E2D53850A00B8DBEB /* SelectableListItem.swift */; };
|
||||||
E20BCCA32D5398AA00B8DBEB /* LocalizedAudioSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20BCCA22D5398AA00B8DBEB /* LocalizedAudioSettingsDetailView.swift */; };
|
E20BCCA32D5398AA00B8DBEB /* LocalizedAudioSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20BCCA22D5398AA00B8DBEB /* LocalizedAudioSettingsDetailView.swift */; };
|
||||||
|
E20BCCAB2D53B86900B8DBEB /* GenerationResultsIssueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20BCCAA2D53B85300B8DBEB /* GenerationResultsIssueView.swift */; };
|
||||||
E21850092CEE01C30090B18B /* PagePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850082CEE01BF0090B18B /* PagePickerView.swift */; };
|
E21850092CEE01C30090B18B /* PagePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850082CEE01BF0090B18B /* PagePickerView.swift */; };
|
||||||
E218500B2CEE02FD0090B18B /* Content+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218500A2CEE02FA0090B18B /* Content+Mock.swift */; };
|
E218500B2CEE02FD0090B18B /* Content+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218500A2CEE02FA0090B18B /* Content+Mock.swift */; };
|
||||||
E21850172CEE55FC0090B18B /* FileType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850162CEE55FB0090B18B /* FileType.swift */; };
|
E21850172CEE55FC0090B18B /* FileType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850162CEE55FB0090B18B /* FileType.swift */; };
|
||||||
@ -272,6 +273,7 @@
|
|||||||
E20BCC9C2D5382ED00B8DBEB /* SettingsSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSheet.swift; sourceTree = "<group>"; };
|
E20BCC9C2D5382ED00B8DBEB /* SettingsSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSheet.swift; sourceTree = "<group>"; };
|
||||||
E20BCC9E2D53850A00B8DBEB /* SelectableListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableListItem.swift; sourceTree = "<group>"; };
|
E20BCC9E2D53850A00B8DBEB /* SelectableListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableListItem.swift; sourceTree = "<group>"; };
|
||||||
E20BCCA22D5398AA00B8DBEB /* LocalizedAudioSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedAudioSettingsDetailView.swift; sourceTree = "<group>"; };
|
E20BCCA22D5398AA00B8DBEB /* LocalizedAudioSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedAudioSettingsDetailView.swift; sourceTree = "<group>"; };
|
||||||
|
E20BCCAA2D53B85300B8DBEB /* GenerationResultsIssueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationResultsIssueView.swift; sourceTree = "<group>"; };
|
||||||
E21850082CEE01BF0090B18B /* PagePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagePickerView.swift; sourceTree = "<group>"; };
|
E21850082CEE01BF0090B18B /* PagePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagePickerView.swift; sourceTree = "<group>"; };
|
||||||
E218500A2CEE02FA0090B18B /* Content+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Mock.swift"; sourceTree = "<group>"; };
|
E218500A2CEE02FA0090B18B /* Content+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Mock.swift"; sourceTree = "<group>"; };
|
||||||
E21850162CEE55FB0090B18B /* FileType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileType.swift; sourceTree = "<group>"; };
|
E21850162CEE55FB0090B18B /* FileType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileType.swift; sourceTree = "<group>"; };
|
||||||
@ -540,6 +542,7 @@
|
|||||||
E20BCCA02D53985500B8DBEB /* Generation */ = {
|
E20BCCA02D53985500B8DBEB /* Generation */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E20BCCAA2D53B85300B8DBEB /* GenerationResultsIssueView.swift */,
|
||||||
E25DA5702D01015400AEF16D /* GenerationContentView.swift */,
|
E25DA5702D01015400AEF16D /* GenerationContentView.swift */,
|
||||||
);
|
);
|
||||||
path = Generation;
|
path = Generation;
|
||||||
@ -1292,6 +1295,7 @@
|
|||||||
E29D31432D0488960051B7F4 /* MainContentView.swift in Sources */,
|
E29D31432D0488960051B7F4 /* MainContentView.swift in Sources */,
|
||||||
E29D31282D0371930051B7F4 /* ContentPageVideo.swift in Sources */,
|
E29D31282D0371930051B7F4 /* ContentPageVideo.swift in Sources */,
|
||||||
E20BCC9F2D53851400B8DBEB /* SelectableListItem.swift in Sources */,
|
E20BCC9F2D53851400B8DBEB /* SelectableListItem.swift in Sources */,
|
||||||
|
E20BCCAB2D53B86900B8DBEB /* GenerationResultsIssueView.swift in Sources */,
|
||||||
E22990262D0F582B009F8D77 /* FilePropertyView.swift in Sources */,
|
E22990262D0F582B009F8D77 /* FilePropertyView.swift in Sources */,
|
||||||
E2FD1D462D46428100B48627 /* PageIconView.swift in Sources */,
|
E2FD1D462D46428100B48627 /* PageIconView.swift in Sources */,
|
||||||
E2A37D252CEBD7A10000979F /* PageListView.swift in Sources */,
|
E2A37D252CEBD7A10000979F /* PageListView.swift in Sources */,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import SFSafeSymbols
|
||||||
|
|
||||||
struct GenerationContentView: View {
|
struct GenerationContentView: View {
|
||||||
|
|
||||||
@ -34,71 +35,61 @@ struct GenerationContentView: View {
|
|||||||
if content.isGeneratingWebsite {
|
if content.isGeneratingWebsite {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.progressViewStyle(.circular)
|
.progressViewStyle(.circular)
|
||||||
.frame(height: 25)
|
.scaleEffect(0.6)
|
||||||
|
.frame(height: 16)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
Text(content.generationStatus)
|
Text(content.generationStatus)
|
||||||
.padding(.vertical, 5)
|
.padding(.vertical, 5)
|
||||||
HStack(spacing: 8) {
|
GenerationResultsIssueView(
|
||||||
Text("\(content.results.imagesToGenerate.count) images")
|
text: "\(content.results.imagesToGenerate.count) images",
|
||||||
Text("\(content.results.externalLinks.count) external links")
|
status: .nominal,
|
||||||
Text("\(content.results.resultCount) items processed")
|
items: { [] })
|
||||||
Text("\(content.results.requiredFiles.count) files")
|
GenerationResultsIssueView(
|
||||||
}
|
text: "\(content.results.resultCount) items processed",
|
||||||
List {
|
status: .nominal,
|
||||||
Section("Inaccessible files (\(content.results.inaccessibleFiles.count))") {
|
items: { [] })
|
||||||
ForEach(content.results.inaccessibleFiles.sorted()) { file in
|
GenerationStringIssuesView(
|
||||||
Text(file.id)
|
text: "external links",
|
||||||
}
|
statusWhenNonEmpty: .nominal,
|
||||||
}
|
items: $content.results.externalLinks)
|
||||||
Section("Unparsable files (\(content.results.unparsableFiles.count))") {
|
GenerationStringIssuesView(
|
||||||
ForEach(content.results.unparsableFiles.sorted()) { file in
|
text: "required files",
|
||||||
Text(file.id)
|
statusWhenNonEmpty: .nominal,
|
||||||
}
|
items: $content.results.requiredFiles) { $0.id }
|
||||||
}
|
GenerationStringIssuesView(
|
||||||
Section("Missing files (\(content.results.missingFiles.count))") {
|
text: "empty pages",
|
||||||
ForEach(content.results.missingFiles.sorted(), id: \.self) { file in
|
statusWhenNonEmpty: .warning,
|
||||||
Text(file)
|
items: $content.results.emptyPages) { "\($0.pageId) (\($0.language))" }
|
||||||
}
|
GenerationStringIssuesView(
|
||||||
}
|
text: "inaccessible files",
|
||||||
Section("Missing tags (\(content.results.missingTags.count))") {
|
items: $content.results.inaccessibleFiles) { $0.id }
|
||||||
ForEach(content.results.missingTags.sorted(), id: \.self) { tag in
|
GenerationStringIssuesView(
|
||||||
Text(tag)
|
text: "unparsable files",
|
||||||
}
|
items: $content.results.unparsableFiles) { $0.id }
|
||||||
}
|
GenerationStringIssuesView(
|
||||||
Section("Missing pages (\(content.results.missingPages.count))") {
|
text: "unsaved output files",
|
||||||
ForEach(content.results.missingPages.sorted(), id: \.self) { page in
|
items: $content.results.unsavedOutputFiles)
|
||||||
Text(page)
|
GenerationStringIssuesView(
|
||||||
}
|
text: "missing files",
|
||||||
}
|
items: $content.results.missingFiles)
|
||||||
Section("Invalid commands (\(content.results.invalidCommands.count))") {
|
GenerationStringIssuesView(
|
||||||
ForEach(content.results.invalidCommands.sorted(), id: \.self) { markdown in
|
text: "missing tags",
|
||||||
Text(markdown)
|
items: $content.results.missingTags)
|
||||||
}
|
GenerationStringIssuesView(
|
||||||
}
|
text: "missing pages",
|
||||||
Section("Invalid blocks (\(content.results.invalidBlocks.count))") {
|
items: $content.results.missingPages)
|
||||||
ForEach(content.results.invalidBlocks.sorted(), id: \.self) { markdown in
|
GenerationStringIssuesView(
|
||||||
Text(markdown)
|
text: "invalid commands",
|
||||||
}
|
items: $content.results.invalidCommands)
|
||||||
}
|
GenerationStringIssuesView(
|
||||||
Section("Warnings (\(content.results.warnings.count))") {
|
text: "invalid blocks",
|
||||||
ForEach(content.results.warnings.sorted(), id: \.self) { warning in
|
items: $content.results.invalidBlocks)
|
||||||
Text(warning)
|
GenerationStringIssuesView(
|
||||||
}
|
text: "warnings",
|
||||||
}
|
statusWhenNonEmpty: .warning,
|
||||||
Section("Unsaved output files (\(content.results.unsavedOutputFiles.count))") {
|
items: $content.results.warnings)
|
||||||
ForEach(content.results.unsavedOutputFiles.sorted(), id: \.self) { file in
|
|
||||||
Text(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Section("Empty pages (\(content.results.emptyPages.count))") {
|
|
||||||
ForEach(content.results.emptyPages.sorted()) { id in
|
|
||||||
Text("\(id.pageId) (\(id.language))")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(minHeight: 400)
|
|
||||||
HorizontalCenter {
|
HorizontalCenter {
|
||||||
Button(action: { dismiss() }) {
|
Button(action: { dismiss() }) {
|
||||||
Text("Close")
|
Text("Close")
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import SFSafeSymbols
|
||||||
|
|
||||||
|
enum IssueStatus {
|
||||||
|
case nominal
|
||||||
|
case warning
|
||||||
|
case error
|
||||||
|
|
||||||
|
var symbol: SFSymbol {
|
||||||
|
switch self {
|
||||||
|
case .nominal: .checkmarkCircleFill
|
||||||
|
case .warning, .error: .exclamationmarkTriangle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var color: Color {
|
||||||
|
switch self {
|
||||||
|
case .nominal: .green
|
||||||
|
case .warning: .yellow
|
||||||
|
case .error: .red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GenerationStringIssuesView<T>: View where T: Hashable {
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
|
||||||
|
let statusWhenNonEmpty: IssueStatus
|
||||||
|
|
||||||
|
@Binding
|
||||||
|
var items: Set<T>
|
||||||
|
|
||||||
|
let map: (T) -> String
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var showList = false
|
||||||
|
|
||||||
|
var status: IssueStatus {
|
||||||
|
items.isEmpty ? .nominal : statusWhenNonEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
init(text: String, statusWhenNonEmpty: IssueStatus = .error, items: Binding<Set<T>>, map: @escaping (T) -> String) {
|
||||||
|
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.map(map).sorted(), id: \.self) { item in
|
||||||
|
Text(item)
|
||||||
|
}
|
||||||
|
.frame(minHeight: 400)
|
||||||
|
Button("Close") { showList = false }
|
||||||
|
}.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showListIfNonEmpty() {
|
||||||
|
guard !items.isEmpty else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
showList = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GenerationStringIssuesView where T == String {
|
||||||
|
|
||||||
|
init(text: String, statusWhenNonEmpty: IssueStatus = .error, items: Binding<Set<String>>) {
|
||||||
|
self.text = text
|
||||||
|
self.statusWhenNonEmpty = statusWhenNonEmpty
|
||||||
|
self._items = items
|
||||||
|
self.map = { $0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GenerationResultsIssueView: View {
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var showList = false
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
|
||||||
|
let status: IssueStatus
|
||||||
|
|
||||||
|
let items: () -> [String]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
Button(action: showListIfNonEmpty) {
|
||||||
|
Image(systemSymbol: status.symbol)
|
||||||
|
.foregroundStyle(status.color)
|
||||||
|
}.buttonStyle(.plain)
|
||||||
|
Text(text)
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showList) {
|
||||||
|
VStack {
|
||||||
|
List(items(), id: \.self) { item in
|
||||||
|
Text(item)
|
||||||
|
}
|
||||||
|
.frame(minHeight: 400)
|
||||||
|
Button("Close") { showList = false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showListIfNonEmpty() {
|
||||||
|
// guard !items.isEmpty else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
showList = true
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user