Add draft indicator, filter drafts, show issue count
This commit is contained in:
parent
9d95e7d210
commit
508483071a
@ -247,6 +247,7 @@
|
||||
E2FE0F6A2D2D2D55002963B7 /* LocalizedPageSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F692D2D2D4F002963B7 /* LocalizedPageSettingsFile.swift */; };
|
||||
E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */; };
|
||||
E2FE0F6E2D2D3689002963B7 /* LocalizedAudioPlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.swift */; };
|
||||
E2FE0F702D2D5235002963B7 /* DraftIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -485,6 +486,7 @@
|
||||
E2FE0F692D2D2D4F002963B7 /* LocalizedPageSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettingsFile.swift; sourceTree = "<group>"; };
|
||||
E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettingsView.swift; sourceTree = "<group>"; };
|
||||
E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedAudioPlayerSettings.swift; sourceTree = "<group>"; };
|
||||
E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftIndicator.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -699,6 +701,7 @@
|
||||
E2A21C372CB9A4F10060935B /* Generic */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */,
|
||||
E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */,
|
||||
E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */,
|
||||
E22990312D0F7678009F8D77 /* DatePropertyView.swift */,
|
||||
@ -1151,6 +1154,7 @@
|
||||
E29D313D2D047C1B0051B7F4 /* LocalizedPageContentView.swift in Sources */,
|
||||
E2FE0F242D2A8C21002963B7 /* TagDisplayView.swift in Sources */,
|
||||
E2A37D2D2CED2EF10000979F /* OptionalTextField.swift in Sources */,
|
||||
E2FE0F702D2D5235002963B7 /* DraftIndicator.swift in Sources */,
|
||||
E2A37D2B2CED2CC30000979F /* TagDetailView.swift in Sources */,
|
||||
E2A21C282CB29B2A0060935B /* FeedEntryData.swift in Sources */,
|
||||
E29D31942D0B7D280051B7F4 /* SimpleImage.swift in Sources */,
|
||||
|
@ -63,6 +63,15 @@ extension String {
|
||||
return components(separatedBy: separator).dropLast().joined(separator: separator)
|
||||
}
|
||||
|
||||
/**
|
||||
Remove everything before the last separator.
|
||||
|
||||
Also removes the separator itself. If the separator is not contained in the string, then the full string is returned.
|
||||
*/
|
||||
func dropBeforeLast<T>(_ separator: T) -> String where T: StringProtocol {
|
||||
components(separatedBy: separator).last!
|
||||
}
|
||||
|
||||
func dropBeforeFirst(_ separator: String) -> String {
|
||||
guard contains(separator) else {
|
||||
return self
|
||||
|
@ -72,7 +72,7 @@ struct HtmlCommand: CommandProcessor {
|
||||
}
|
||||
|
||||
for src in srcAttributes {
|
||||
results.warning("Found image in html: \(src)")
|
||||
findFile(path: src, source: "src of <img>")
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ struct HtmlCommand: CommandProcessor {
|
||||
if url.hasPrefix("http://") || url.hasPrefix("https://") {
|
||||
results.externalLink(to: url)
|
||||
} else {
|
||||
results.warning("Relative link in HTML: \(url)")
|
||||
findFile(path: url, source: "href of <a>")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,7 +122,7 @@ struct HtmlCommand: CommandProcessor {
|
||||
}
|
||||
|
||||
for src in srcSets {
|
||||
results.warning("Found source set in html: \(src)")
|
||||
findFile(path: src, source: "srcset of <source>")
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,17 +138,57 @@ struct HtmlCommand: CommandProcessor {
|
||||
}
|
||||
|
||||
for src in srcAttributes {
|
||||
guard content.isValidIdForFile(src) else {
|
||||
results.warning("Found source in html: \(src)")
|
||||
continue
|
||||
}
|
||||
guard let file = content.file(src) else {
|
||||
results.warning("Found source in html: \(src)")
|
||||
continue
|
||||
}
|
||||
#warning("Either find files by their full path, or replace file id with full path")
|
||||
results.require(file: file)
|
||||
findFile(path: src, source: "src of <source>")
|
||||
}
|
||||
}
|
||||
|
||||
private func findFile(path: String, source: String) {
|
||||
let type = FileType(fileExtension: path.fileExtension)
|
||||
|
||||
guard path.hasPrefix("/") else {
|
||||
findFileWith(relativePath: path, type: type, source: source)
|
||||
return
|
||||
}
|
||||
|
||||
guard !type.generatesImageVersions else {
|
||||
// Try to determine image version needed
|
||||
findImageVersion(path: path, type: type, source: source)
|
||||
return
|
||||
}
|
||||
if findFile(withAbsolutePath: path) {
|
||||
return
|
||||
}
|
||||
|
||||
let fileId = path.dropBeforeLast("/")
|
||||
if content.isValidIdForFile(fileId) {
|
||||
results.missing(file: fileId, source: "HTML: \(source)")
|
||||
} else {
|
||||
results.warning("Could not find file '\(path)' for \(source)")
|
||||
}
|
||||
}
|
||||
|
||||
private func findFile(withAbsolutePath absolutePath: String) -> Bool {
|
||||
guard let file = content.file(withOutputPath: absolutePath) else {
|
||||
return false
|
||||
}
|
||||
results.require(file: file)
|
||||
return true
|
||||
}
|
||||
|
||||
private func findImageVersion(path: String, type: FileType, source: String) {
|
||||
// First check if image original should be used
|
||||
if findFile(withAbsolutePath: path) {
|
||||
return
|
||||
}
|
||||
let fileId = path.dropAfterLast("/").dropBeforeLast("/")
|
||||
guard let file = content.file(fileId) else {
|
||||
results.missing(file: fileId, source: "HTML: \(source)")
|
||||
return
|
||||
}
|
||||
results.warning("Could not determine image version for file '\(file.id)' for \(source)")
|
||||
}
|
||||
|
||||
private func findFileWith(relativePath: String, type: FileType, source: String) {
|
||||
results.warning("Could not determine relative file '\(relativePath)' for \(source)")
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,11 @@ final class PostListPageGenerator {
|
||||
}
|
||||
|
||||
func createPages(for posts: [Post]) {
|
||||
// Sort by newest first
|
||||
let posts = posts.sorted(ascending: false) { $0.startDate }
|
||||
// Sort by newest first, filter drafts
|
||||
let posts = posts
|
||||
.filter { !$0.isDraft }
|
||||
.sorted(ascending: false) { $0.startDate }
|
||||
|
||||
let totalCount = posts.count
|
||||
guard totalCount > 0 else {
|
||||
// Create one empty page
|
||||
|
@ -246,7 +246,7 @@ final class FileResource: Item {
|
||||
|
||||
/**
|
||||
Get the url path to a file in the output folder.
|
||||
The result is an absolute path from the output folder for use in HTML.
|
||||
The result is an absolute path from the output folder for use in HTML, including a leading slash
|
||||
*/
|
||||
var absoluteUrl: String {
|
||||
if let customOutputPath {
|
||||
|
@ -180,6 +180,15 @@ enum FileType: String {
|
||||
category == .image
|
||||
}
|
||||
|
||||
var generatesImageVersions: Bool {
|
||||
switch self {
|
||||
case .jpg, .png, .avif, .webp, .tiff:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var isVideo: Bool {
|
||||
category == .video
|
||||
}
|
||||
|
15
CHDataManagement/Views/Generic/DraftIndicator.swift
Normal file
15
CHDataManagement/Views/Generic/DraftIndicator.swift
Normal file
@ -0,0 +1,15 @@
|
||||
import SwiftUI
|
||||
|
||||
struct DraftIndicator: View {
|
||||
|
||||
var body: some View {
|
||||
Text("Draft")
|
||||
.foregroundStyle(.white)
|
||||
.padding(.vertical, 2)
|
||||
.padding(.horizontal, 5)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 5, style: .circular)
|
||||
.foregroundStyle(Color.gray)
|
||||
)
|
||||
}
|
||||
}
|
@ -31,7 +31,13 @@ struct PageListView: View {
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.padding(.horizontal, 8)
|
||||
List(filteredPages, selection: $selectedPage) { page in
|
||||
Text(page.localized(in: language).title).tag(page)
|
||||
HStack {
|
||||
Text(page.title(in: language))
|
||||
Spacer()
|
||||
if page.isDraft {
|
||||
DraftIndicator()
|
||||
}
|
||||
}.tag(page)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
|
@ -31,7 +31,13 @@ struct PostListView: View {
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.padding(.horizontal, 8)
|
||||
List(filteredPosts, selection: $selectedPost) { post in
|
||||
Text(post.title(in: language)).tag(post)
|
||||
HStack {
|
||||
Text(post.title(in: language))
|
||||
Spacer()
|
||||
if post.isDraft {
|
||||
DraftIndicator()
|
||||
}
|
||||
}.tag(post)
|
||||
}
|
||||
}.onAppear {
|
||||
if selectedPost == nil {
|
||||
|
@ -61,56 +61,56 @@ struct GenerationContentView: View {
|
||||
Text("\(content.results.requiredFiles.count) files")
|
||||
}
|
||||
List {
|
||||
Section("Empty pages") {
|
||||
ForEach(content.results.emptyPages.sorted()) { id in
|
||||
Text("\(id.pageId) (\(id.language))")
|
||||
}
|
||||
}
|
||||
Section("Inaccessible files") {
|
||||
Section("Inaccessible files (\(content.results.inaccessibleFiles.count))") {
|
||||
ForEach(content.results.inaccessibleFiles.sorted()) { file in
|
||||
Text(file.id)
|
||||
}
|
||||
}
|
||||
Section("Unparsable files") {
|
||||
Section("Unparsable files (\(content.results.unparsableFiles.count))") {
|
||||
ForEach(content.results.unparsableFiles.sorted()) { file in
|
||||
Text(file.id)
|
||||
}
|
||||
}
|
||||
Section("Missing files") {
|
||||
Section("Missing files (\(content.results.missingFiles.count))") {
|
||||
ForEach(content.results.missingFiles.sorted(), id: \.self) { file in
|
||||
Text(file)
|
||||
}
|
||||
}
|
||||
Section("Missing tags") {
|
||||
Section("Missing tags (\(content.results.missingTags.count))") {
|
||||
ForEach(content.results.missingTags.sorted(), id: \.self) { tag in
|
||||
Text(tag)
|
||||
}
|
||||
}
|
||||
Section("Missing pages") {
|
||||
Section("Missing pages (\(content.results.missingPages.count))") {
|
||||
ForEach(content.results.missingPages.sorted(), id: \.self) { page in
|
||||
Text(page)
|
||||
}
|
||||
}
|
||||
Section("Invalid commands") {
|
||||
Section("Invalid commands (\(content.results.invalidCommands.count))") {
|
||||
ForEach(content.results.invalidCommands.sorted(), id: \.self) { markdown in
|
||||
Text(markdown)
|
||||
}
|
||||
}
|
||||
Section("Invalid blocks") {
|
||||
Section("Invalid blocks (\(content.results.invalidBlocks.count))") {
|
||||
ForEach(content.results.invalidBlocks.sorted(), id: \.self) { markdown in
|
||||
Text(markdown)
|
||||
}
|
||||
}
|
||||
Section("Warnings") {
|
||||
Section("Warnings (\(content.results.warnings.count))") {
|
||||
ForEach(content.results.warnings.sorted(), id: \.self) { warning in
|
||||
Text(warning)
|
||||
}
|
||||
}
|
||||
Section("Unsaved output files") {
|
||||
Section("Unsaved output files (\(content.results.unsavedOutputFiles.count))") {
|
||||
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))")
|
||||
}
|
||||
}
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user