Improve display of results
This commit is contained in:
parent
7ebc9d8404
commit
dc7ab6fb15
@ -22,57 +22,76 @@ final class PageGenerationResults: ObservableObject {
|
|||||||
private unowned let delegate: GenerationResults
|
private unowned let delegate: GenerationResults
|
||||||
|
|
||||||
/// The files that could not be accessed
|
/// The files that could not be accessed
|
||||||
|
@Published
|
||||||
private(set) var inaccessibleFiles: Set<FileResource>
|
private(set) var inaccessibleFiles: Set<FileResource>
|
||||||
|
|
||||||
/// The files that could not be parsed, with the error message produced
|
/// The files that could not be parsed, with the error message produced
|
||||||
|
@Published
|
||||||
private(set) var unparsableFiles: [FileResource : Set<String>]
|
private(set) var unparsableFiles: [FileResource : Set<String>]
|
||||||
|
|
||||||
/// The missing files directly used by this page, and the source of the file
|
/// The missing files directly used by this page, and the source of the file
|
||||||
|
@Published
|
||||||
private(set) var missingFiles: [String: Set<String>]
|
private(set) var missingFiles: [String: Set<String>]
|
||||||
|
|
||||||
/// The missing files linked to from other files.
|
/// The missing files linked to from other files.
|
||||||
|
@Published
|
||||||
private(set) var missingLinkedFiles: [String : Set<FileResource>]
|
private(set) var missingLinkedFiles: [String : Set<FileResource>]
|
||||||
|
|
||||||
/// The missing tags linked to by this page, and the source of the link
|
/// The missing tags linked to by this page, and the source of the link
|
||||||
|
@Published
|
||||||
private(set) var missingLinkedTags: [String : Set<String>]
|
private(set) var missingLinkedTags: [String : Set<String>]
|
||||||
|
|
||||||
/// The missing pages linked to by this page, and the source of the link
|
/// The missing pages linked to by this page, and the source of the link
|
||||||
|
@Published
|
||||||
private(set) var missingLinkedPages: [String : Set<String>]
|
private(set) var missingLinkedPages: [String : Set<String>]
|
||||||
|
|
||||||
/// The footer scripts or html to add to the end of the body
|
/// The footer scripts or html to add to the end of the body
|
||||||
|
@Published
|
||||||
private(set) var requiredFooters: Set<String>
|
private(set) var requiredFooters: Set<String>
|
||||||
|
|
||||||
/// The known header elements to include in the page
|
/// The known header elements to include in the page
|
||||||
|
@Published
|
||||||
private(set) var requiredHeaders: Set<KnownHeaderElement>
|
private(set) var requiredHeaders: Set<KnownHeaderElement>
|
||||||
|
|
||||||
/// The known icons that need to be included as hidden SVGs
|
/// The known icons that need to be included as hidden SVGs
|
||||||
|
@Published
|
||||||
private(set) var requiredIcons: Set<PageIcon>
|
private(set) var requiredIcons: Set<PageIcon>
|
||||||
|
|
||||||
/// The pages linked to by the page
|
/// The pages linked to by the page
|
||||||
|
@Published
|
||||||
private(set) var linkedPages: Set<Page>
|
private(set) var linkedPages: Set<Page>
|
||||||
|
|
||||||
/// The tags linked to by this page
|
/// The tags linked to by this page
|
||||||
|
@Published
|
||||||
private(set) var linkedTags: Set<Tag>
|
private(set) var linkedTags: Set<Tag>
|
||||||
|
|
||||||
/// The links to external content in this page
|
/// The links to external content in this page
|
||||||
|
@Published
|
||||||
private(set) var externalLinks: Set<String>
|
private(set) var externalLinks: Set<String>
|
||||||
|
|
||||||
/// The files used by this page, but not necessarily required in the output folder
|
/// The files used by this page, but not necessarily required in the output folder
|
||||||
|
@Published
|
||||||
private(set) var usedFiles: Set<FileResource>
|
private(set) var usedFiles: Set<FileResource>
|
||||||
|
|
||||||
/// The files that need to be copied
|
/// The files that need to be copied
|
||||||
|
@Published
|
||||||
private(set) var requiredFiles: Set<FileResource>
|
private(set) var requiredFiles: Set<FileResource>
|
||||||
|
|
||||||
/// The image versions required for this page
|
/// The image versions required for this page
|
||||||
|
@Published
|
||||||
private(set) var imagesToGenerate: Set<ImageVersion>
|
private(set) var imagesToGenerate: Set<ImageVersion>
|
||||||
|
|
||||||
|
@Published
|
||||||
private(set) var invalidCommands: [(command: CommandType?, markdown: String)]
|
private(set) var invalidCommands: [(command: CommandType?, markdown: String)]
|
||||||
|
|
||||||
|
@Published
|
||||||
private(set) var invalidBlocks: [(block: ContentBlock?, markdown: String)]
|
private(set) var invalidBlocks: [(block: ContentBlock?, markdown: String)]
|
||||||
|
|
||||||
|
@Published
|
||||||
private(set) var warnings: Set<String>
|
private(set) var warnings: Set<String>
|
||||||
|
|
||||||
/// The files that could not be saved to the output folder
|
/// The files that could not be saved to the output folder
|
||||||
|
@Published
|
||||||
private(set) var unsavedOutputFiles: [String: Set<ItemReference>] = [:]
|
private(set) var unsavedOutputFiles: [String: Set<ItemReference>] = [:]
|
||||||
|
|
||||||
private(set) var pageIsEmpty: Bool
|
private(set) var pageIsEmpty: Bool
|
||||||
@ -105,159 +124,167 @@ final class PageGenerationResults: ObservableObject {
|
|||||||
redirect = nil
|
redirect = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onMain(_ operation: @escaping () -> Void) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
operation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func reset() {
|
func reset() {
|
||||||
inaccessibleFiles = []
|
onMain {
|
||||||
unparsableFiles = [:]
|
self.inaccessibleFiles = []
|
||||||
missingFiles = [:]
|
self.unparsableFiles = [:]
|
||||||
missingLinkedFiles = [:]
|
self.missingFiles = [:]
|
||||||
missingLinkedTags = [:]
|
self.missingLinkedFiles = [:]
|
||||||
missingLinkedPages = [:]
|
self.missingLinkedTags = [:]
|
||||||
requiredHeaders = []
|
self.missingLinkedPages = [:]
|
||||||
requiredFooters = []
|
self.requiredHeaders = []
|
||||||
requiredIcons = []
|
self.requiredFooters = []
|
||||||
linkedPages = []
|
self.requiredIcons = []
|
||||||
linkedTags = []
|
self.linkedPages = []
|
||||||
externalLinks = []
|
self.linkedTags = []
|
||||||
usedFiles = []
|
self.externalLinks = []
|
||||||
requiredFiles = []
|
self.usedFiles = []
|
||||||
imagesToGenerate = []
|
self.requiredFiles = []
|
||||||
invalidCommands = []
|
self.imagesToGenerate = []
|
||||||
invalidBlocks = []
|
self.invalidCommands = []
|
||||||
warnings = []
|
self.invalidBlocks = []
|
||||||
unsavedOutputFiles = [:]
|
self.warnings = []
|
||||||
pageIsEmpty = false
|
self.unsavedOutputFiles = [:]
|
||||||
redirect = nil
|
self.pageIsEmpty = false
|
||||||
|
self.redirect = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Adding entries
|
// MARK: Adding entries
|
||||||
|
|
||||||
func inaccessibleContent(file: FileResource) {
|
func inaccessibleContent(file: FileResource) {
|
||||||
inaccessibleFiles.insert(file)
|
onMain { self.inaccessibleFiles.insert(file) }
|
||||||
delegate.inaccessibleContent(file: file)
|
delegate.inaccessibleContent(file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalid(command: CommandType?, _ markdown: Substring) {
|
func invalid(command: CommandType?, _ markdown: Substring) {
|
||||||
let markdown = String(markdown)
|
let markdown = String(markdown)
|
||||||
invalidCommands.append((command, markdown))
|
onMain { self.invalidCommands.append((command, markdown)) }
|
||||||
delegate.invalidCommand(markdown)
|
delegate.invalidCommand(markdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalid(block: ContentBlock?, _ markdown: Substring) {
|
func invalid(block: ContentBlock?, _ markdown: Substring) {
|
||||||
let markdown = String(markdown)
|
let markdown = String(markdown)
|
||||||
invalidBlocks.append((block, markdown))
|
onMain { self.invalidBlocks.append((block, markdown)) }
|
||||||
delegate.invalidBlock(markdown)
|
delegate.invalidBlock(markdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
func missing(page: String, source: String) {
|
func missing(page: String, source: String) {
|
||||||
missingLinkedPages[page, default: []].insert(source)
|
onMain { self.missingLinkedPages[page, default: []].insert(source) }
|
||||||
delegate.missing(page: page)
|
delegate.missing(page: page)
|
||||||
}
|
}
|
||||||
|
|
||||||
func missing(tag: String, source: String) {
|
func missing(tag: String, source: String) {
|
||||||
missingLinkedTags[tag, default: []].insert(source)
|
onMain { self.missingLinkedTags[tag, default: []].insert(source) }
|
||||||
delegate.missing(tag: tag)
|
delegate.missing(tag: tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func missing(file: String, source: String) {
|
func missing(file: String, source: String) {
|
||||||
missingFiles[file, default: []].insert(source)
|
onMain { self.missingFiles[file, default: []].insert(source) }
|
||||||
delegate.missing(file: file)
|
delegate.missing(file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(image: ImageVersion) {
|
func require(image: ImageVersion) {
|
||||||
imagesToGenerate.insert(image)
|
onMain { self.imagesToGenerate.insert(image) }
|
||||||
used(file: image.image)
|
used(file: image.image)
|
||||||
delegate.generate(image)
|
delegate.generate(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(imageSet: ImageSet) {
|
func require(imageSet: ImageSet) {
|
||||||
let jobs = imageSet.jobs
|
let jobs = imageSet.jobs
|
||||||
imagesToGenerate.formUnion(jobs)
|
onMain { self.imagesToGenerate.formUnion(jobs) }
|
||||||
used(file: imageSet.image)
|
used(file: imageSet.image)
|
||||||
delegate.generate(jobs)
|
delegate.generate(jobs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalidFormat(file: FileResource, error: String) {
|
func invalidFormat(file: FileResource, error: String) {
|
||||||
unparsableFiles[file, default: []].insert(error)
|
onMain { self.unparsableFiles[file, default: []].insert(error) }
|
||||||
delegate.unparsable(file: file)
|
delegate.unparsable(file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func missing(file: String, containedIn sourceFile: FileResource) {
|
func missing(file: String, containedIn sourceFile: FileResource) {
|
||||||
missingLinkedFiles[file, default: []].insert(sourceFile)
|
onMain { self.missingLinkedFiles[file, default: []].insert(sourceFile) }
|
||||||
delegate.missing(file: file)
|
delegate.missing(file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func used(file: FileResource) {
|
func used(file: FileResource) {
|
||||||
usedFiles.insert(file)
|
onMain { self.usedFiles.insert(file) }
|
||||||
// TODO: Notify delegate
|
// TODO: Notify delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(file: FileResource) {
|
func require(file: FileResource) {
|
||||||
requiredFiles.insert(file)
|
onMain { self.requiredFiles.insert(file) }
|
||||||
usedFiles.insert(file)
|
used(file: file)
|
||||||
delegate.require(file: file)
|
delegate.require(file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(files: [FileResource]) {
|
func require(files: [FileResource]) {
|
||||||
requiredFiles.formUnion(files)
|
onMain { self.requiredFiles.formUnion(files) }
|
||||||
usedFiles.formUnion(files)
|
onMain { self.usedFiles.formUnion(files) }
|
||||||
delegate.require(files: files)
|
delegate.require(files: files)
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(footer: String) {
|
func require(footer: String) {
|
||||||
requiredFooters.insert(footer)
|
onMain { self.requiredFooters.insert(footer) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(header: KnownHeaderElement) {
|
func require(header: KnownHeaderElement) {
|
||||||
requiredHeaders.insert(header)
|
onMain { self.requiredHeaders.insert(header) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(headers: KnownHeaderElement...) {
|
func require(headers: KnownHeaderElement...) {
|
||||||
requiredHeaders.formUnion(headers)
|
onMain { self.requiredHeaders.formUnion(headers) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(icon: PageIcon) {
|
func require(icon: PageIcon) {
|
||||||
requiredIcons.insert(icon)
|
onMain { self.requiredIcons.insert(icon) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(icons: PageIcon...) {
|
func require(icons: PageIcon...) {
|
||||||
requiredIcons.formUnion(icons)
|
onMain { self.requiredIcons.formUnion(icons) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func require(icons: [PageIcon]) {
|
func require(icons: [PageIcon]) {
|
||||||
requiredIcons.formUnion(icons)
|
onMain { self.requiredIcons.formUnion(icons) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func linked(to page: Page) {
|
func linked(to page: Page) {
|
||||||
linkedPages.insert(page)
|
onMain { self.linkedPages.insert(page) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func linked(to tag: Tag) {
|
func linked(to tag: Tag) {
|
||||||
linkedTags.insert(tag)
|
onMain { self.linkedTags.insert(tag) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func externalLink(to url: String) {
|
func externalLink(to url: String) {
|
||||||
externalLinks.insert(url)
|
onMain { self.externalLinks.insert(url) }
|
||||||
delegate.externalLink(url)
|
delegate.externalLink(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func warning(_ warning: String) {
|
func warning(_ warning: String) {
|
||||||
warnings.insert(warning)
|
onMain { self.warnings.insert(warning) }
|
||||||
delegate.warning(warning)
|
delegate.warning(warning)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsavedOutput(_ path: String, source: ItemReference) {
|
func unsavedOutput(_ path: String, source: ItemReference) {
|
||||||
unsavedOutputFiles[path, default: []].insert(source)
|
onMain { self.unsavedOutputFiles[path, default: []].insert(source) }
|
||||||
delegate.unsaved(path)
|
delegate.unsaved(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func markPageAsEmpty() {
|
func markPageAsEmpty() {
|
||||||
guard case .page(let page) = itemId.itemType else { return }
|
guard case .page(let page) = itemId.itemType else { return }
|
||||||
pageIsEmpty = true
|
onMain { self.pageIsEmpty = true }
|
||||||
delegate.empty(.init(language: itemId.language, pageId: page.id))
|
delegate.empty(.init(language: itemId.language, pageId: page.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func redirect(from originalUrl: String, to newUrl: String) {
|
func redirect(from originalUrl: String, to newUrl: String) {
|
||||||
redirect = (originalUrl, newUrl)
|
onMain { self.redirect = (originalUrl, newUrl) }
|
||||||
delegate.redirect(from: originalUrl, to: newUrl)
|
delegate.redirect(from: originalUrl, to: newUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import SFSafeSymbols
|
||||||
|
|
||||||
enum FileTypeCategory: String, CaseIterable {
|
enum FileTypeCategory: String, CaseIterable {
|
||||||
case image
|
case image
|
||||||
@ -22,6 +23,19 @@ enum FileTypeCategory: String, CaseIterable {
|
|||||||
case .audio: return "Audio"
|
case .audio: return "Audio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var symbol: SFSymbol {
|
||||||
|
switch self {
|
||||||
|
case .image: .photo
|
||||||
|
case .code: .keyboard
|
||||||
|
case .model: .cubeTransparent
|
||||||
|
case .text: .docText
|
||||||
|
case .video: .video
|
||||||
|
case .resource: .docZipper
|
||||||
|
case .asset: .network
|
||||||
|
case .audio: .speakerWave2CircleFill
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FileTypeCategory: Hashable {
|
extension FileTypeCategory: Hashable {
|
||||||
|
@ -66,3 +66,23 @@ extension ItemReference: Comparable {
|
|||||||
lhs.id < rhs.id
|
lhs.id < rhs.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ItemReference: CustomStringConvertible {
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case .general:
|
||||||
|
return "General"
|
||||||
|
case .feed:
|
||||||
|
return "Feed"
|
||||||
|
case .post(let post):
|
||||||
|
return "Post \(post.id)"
|
||||||
|
case .page(let page):
|
||||||
|
return "Page \(page.id)"
|
||||||
|
case .tagPage(let tag):
|
||||||
|
return "Tag \(tag.id)"
|
||||||
|
case .tagOverview:
|
||||||
|
return "Tag Overview"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,10 +5,14 @@ struct ListPopup: View {
|
|||||||
@Environment(\.dismiss)
|
@Environment(\.dismiss)
|
||||||
var dismiss
|
var dismiss
|
||||||
|
|
||||||
|
let title: LocalizedStringKey
|
||||||
|
|
||||||
let items: [String]
|
let items: [String]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
|
Text(title)
|
||||||
|
.font(.title)
|
||||||
List {
|
List {
|
||||||
ForEach(items, id: \.self) { page in
|
ForEach(items, id: \.self) { page in
|
||||||
Text(page)
|
Text(page)
|
||||||
|
@ -5,12 +5,14 @@ struct TextWithPopup: View {
|
|||||||
|
|
||||||
let symbol: SFSymbol
|
let symbol: SFSymbol
|
||||||
|
|
||||||
|
let title: LocalizedStringKey
|
||||||
|
|
||||||
let text: LocalizedStringKey
|
let text: LocalizedStringKey
|
||||||
|
|
||||||
let items: [String]
|
let items: [String]
|
||||||
|
|
||||||
@State
|
@State
|
||||||
private var isHovering = false
|
private var showSheet = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
@ -23,14 +25,69 @@ struct TextWithPopup: View {
|
|||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if items.count > 0 {
|
if items.count > 0 {
|
||||||
isHovering.toggle()
|
showSheet.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $isHovering) {
|
.sheet(isPresented: $showSheet) {
|
||||||
ListPopup(items: items)
|
ListPopup(title: title, items: items)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
isHovering.toggle()
|
showSheet.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PopupWithList<ListContent>: View where ListContent: View {
|
||||||
|
|
||||||
|
let symbol: SFSymbol
|
||||||
|
|
||||||
|
let text: LocalizedStringKey
|
||||||
|
|
||||||
|
let canShowPopup: Bool
|
||||||
|
|
||||||
|
let content: ListContent
|
||||||
|
|
||||||
|
init(symbol: SFSymbol, text: LocalizedStringKey, canShowPopup: Bool, @ViewBuilder content: () -> ListContent) {
|
||||||
|
self.symbol = symbol
|
||||||
|
self.text = text
|
||||||
|
self.canShowPopup = canShowPopup
|
||||||
|
self.content = content()
|
||||||
|
}
|
||||||
|
|
||||||
|
@State
|
||||||
|
private var showSheet = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
Image(systemSymbol: symbol)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: 16, height: 16)
|
||||||
|
Text(text)
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onTapGesture {
|
||||||
|
if canShowPopup {
|
||||||
|
showSheet.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showSheet) {
|
||||||
|
Popup(content: content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Popup<Content>: View where Content: View {
|
||||||
|
|
||||||
|
@Environment(\.dismiss)
|
||||||
|
var dismiss
|
||||||
|
|
||||||
|
let content: Content
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
content
|
||||||
|
Button("Dismiss") { dismiss() }
|
||||||
|
}.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -69,6 +69,7 @@ struct LocalizedPageContentView: View {
|
|||||||
let linkingPosts = content.posts.filter { $0.linkedPage == page }
|
let linkingPosts = content.posts.filter { $0.linkedPage == page }
|
||||||
TextWithPopup(
|
TextWithPopup(
|
||||||
symbol: .ipadAndArrowForward,
|
symbol: .ipadAndArrowForward,
|
||||||
|
title: "Post linking to page",
|
||||||
text: "\(linkingPosts.count) linking posts",
|
text: "\(linkingPosts.count) linking posts",
|
||||||
items: linkingPosts.map { $0.title(in: language) })
|
items: linkingPosts.map { $0.title(in: language) })
|
||||||
}.foregroundStyle(.secondary)
|
}.foregroundStyle(.secondary)
|
||||||
|
@ -1,6 +1,23 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SFSafeSymbols
|
import SFSafeSymbols
|
||||||
|
|
||||||
|
private struct TextWithSymbol: Comparable, Identifiable {
|
||||||
|
|
||||||
|
let symbol: SFSymbol
|
||||||
|
|
||||||
|
let color: Color
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
|
||||||
|
static func < (lhs: TextWithSymbol, rhs: TextWithSymbol) -> Bool {
|
||||||
|
lhs.text < rhs.text
|
||||||
|
}
|
||||||
|
|
||||||
|
var id: String {
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct PageContentResultsView: View {
|
struct PageContentResultsView: View {
|
||||||
|
|
||||||
@Environment(\.language)
|
@Environment(\.language)
|
||||||
@ -9,48 +26,100 @@ struct PageContentResultsView: View {
|
|||||||
@ObservedObject
|
@ObservedObject
|
||||||
var results: PageGenerationResults
|
var results: PageGenerationResults
|
||||||
|
|
||||||
#warning("Rework to only show a single popup with all files, and indicate missing ones")
|
|
||||||
private var totalFileCount: Int {
|
private var totalFileCount: Int {
|
||||||
results.usedFiles.count + results.missingFiles.count + results.missingLinkedFiles.count
|
results.usedFiles.count + results.missingFiles.count + results.missingLinkedFiles.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var allFiles: [TextWithSymbol] {
|
||||||
|
results.usedFiles.map {
|
||||||
|
TextWithSymbol(
|
||||||
|
symbol: $0.type.category.symbol,
|
||||||
|
color: .blue,
|
||||||
|
text: $0.id)
|
||||||
|
}
|
||||||
|
+ results.missingFiles.keys.map {
|
||||||
|
TextWithSymbol(
|
||||||
|
symbol: .questionmarkCircleFill,
|
||||||
|
color: .red,
|
||||||
|
text: $0)
|
||||||
|
}
|
||||||
|
+ results.missingLinkedFiles.keys.map {
|
||||||
|
TextWithSymbol(
|
||||||
|
symbol: .questionmarkCircleFill,
|
||||||
|
color: .red,
|
||||||
|
text: $0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var totalLinkCount: Int {
|
||||||
|
results.externalLinks.count +
|
||||||
|
results.missingLinkedPages.count +
|
||||||
|
results.linkedPages.count
|
||||||
|
}
|
||||||
|
|
||||||
|
private var allLinks: [TextWithSymbol] {
|
||||||
|
results.externalLinks.map {
|
||||||
|
TextWithSymbol(
|
||||||
|
symbol: .network,
|
||||||
|
color: .blue,
|
||||||
|
text: $0)
|
||||||
|
}
|
||||||
|
+ results.missingLinkedPages.keys.map {
|
||||||
|
TextWithSymbol(
|
||||||
|
symbol: .questionmarkCircleFill,
|
||||||
|
color: .red,
|
||||||
|
text: $0)
|
||||||
|
}
|
||||||
|
+ results.linkedPages.map {
|
||||||
|
TextWithSymbol(
|
||||||
|
symbol: .docBadgePlus,
|
||||||
|
color: .blue,
|
||||||
|
text: $0.title(in: language))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
TextWithPopup(
|
PopupWithList(
|
||||||
symbol: .photoOnRectangleAngled,
|
symbol: .photoOnRectangleAngled,
|
||||||
text: "\(totalFileCount) images and files",
|
text: "\(totalFileCount) files", canShowPopup: totalFileCount > 0) {
|
||||||
items: results.usedFiles.sorted().map { $0.id })
|
Text("Files")
|
||||||
.foregroundStyle(.secondary)
|
.font(.title)
|
||||||
|
List {
|
||||||
|
ForEach(allFiles.sorted()) { file in
|
||||||
|
HStack {
|
||||||
|
Image(systemSymbol: file.symbol)
|
||||||
|
.frame(width: 18)
|
||||||
|
.foregroundStyle(file.color)
|
||||||
|
Text(file.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(minHeight: 400)
|
||||||
|
}
|
||||||
|
|
||||||
TextWithPopup(
|
PopupWithList(
|
||||||
symbol: .docBadgePlus,
|
symbol: .photoOnRectangleAngled,
|
||||||
text: "\(results.linkedPages.count + results.missingLinkedPages.count) page links",
|
text: "\(totalLinkCount) links", canShowPopup: totalLinkCount > 0) {
|
||||||
items: results.linkedPages.sorted().map { $0.localized(in: language).title })
|
Text("Links")
|
||||||
.foregroundStyle(.secondary)
|
.font(.title)
|
||||||
|
List {
|
||||||
|
ForEach(allLinks.sorted()) { file in
|
||||||
|
HStack {
|
||||||
|
Image(systemSymbol: file.symbol)
|
||||||
|
.frame(width: 18)
|
||||||
|
.foregroundStyle(file.color)
|
||||||
|
Text(file.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(minHeight: 400)
|
||||||
|
}
|
||||||
|
|
||||||
TextWithPopup(
|
|
||||||
symbol: .globe,
|
|
||||||
text: "\(results.externalLinks.count) external links",
|
|
||||||
items: results.externalLinks.sorted())
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
|
|
||||||
if !results.missingLinkedPages.isEmpty {
|
|
||||||
TextWithPopup(
|
|
||||||
symbol: .exclamationmarkTriangleFill,
|
|
||||||
text: "\(results.missingLinkedPages.count) missing pages",
|
|
||||||
items: results.missingLinkedPages.keys.sorted())
|
|
||||||
.foregroundStyle(.red)
|
|
||||||
}
|
|
||||||
if !results.missingFiles.isEmpty {
|
|
||||||
TextWithPopup(
|
|
||||||
symbol: .exclamationmarkTriangleFill,
|
|
||||||
text: "\(results.missingFiles.count) missing files",
|
|
||||||
items: results.missingFiles.keys.sorted())
|
|
||||||
.foregroundStyle(.red)
|
|
||||||
}
|
|
||||||
if !results.invalidCommands.isEmpty {
|
if !results.invalidCommands.isEmpty {
|
||||||
TextWithPopup(
|
TextWithPopup(
|
||||||
symbol: .exclamationmarkTriangleFill,
|
symbol: .exclamationmarkTriangleFill,
|
||||||
|
title: "Invalid commands",
|
||||||
text: "\(results.invalidCommands.count) invalid commands",
|
text: "\(results.invalidCommands.count) invalid commands",
|
||||||
items: results.invalidCommands.map { $0.markdown }.sorted())
|
items: results.invalidCommands.map { $0.markdown }.sorted())
|
||||||
.foregroundStyle(.red)
|
.foregroundStyle(.red)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user