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 */; };
|
E2FE0F6A2D2D2D55002963B7 /* LocalizedPageSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F692D2D2D4F002963B7 /* LocalizedPageSettingsFile.swift */; };
|
||||||
E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */; };
|
E2FE0F6C2D2D335E002963B7 /* LocalizedPageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6B2D2D3358002963B7 /* LocalizedPageSettingsView.swift */; };
|
||||||
E2FE0F6E2D2D3689002963B7 /* LocalizedAudioPlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@ -485,6 +486,7 @@
|
|||||||
E2FE0F692D2D2D4F002963B7 /* LocalizedPageSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageSettingsFile.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -699,6 +701,7 @@
|
|||||||
E2A21C372CB9A4F10060935B /* Generic */ = {
|
E2A21C372CB9A4F10060935B /* Generic */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E2FE0F6F2D2D5231002963B7 /* DraftIndicator.swift */,
|
||||||
E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */,
|
E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */,
|
||||||
E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */,
|
E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */,
|
||||||
E22990312D0F7678009F8D77 /* DatePropertyView.swift */,
|
E22990312D0F7678009F8D77 /* DatePropertyView.swift */,
|
||||||
@ -1151,6 +1154,7 @@
|
|||||||
E29D313D2D047C1B0051B7F4 /* LocalizedPageContentView.swift in Sources */,
|
E29D313D2D047C1B0051B7F4 /* LocalizedPageContentView.swift in Sources */,
|
||||||
E2FE0F242D2A8C21002963B7 /* TagDisplayView.swift in Sources */,
|
E2FE0F242D2A8C21002963B7 /* TagDisplayView.swift in Sources */,
|
||||||
E2A37D2D2CED2EF10000979F /* OptionalTextField.swift in Sources */,
|
E2A37D2D2CED2EF10000979F /* OptionalTextField.swift in Sources */,
|
||||||
|
E2FE0F702D2D5235002963B7 /* DraftIndicator.swift in Sources */,
|
||||||
E2A37D2B2CED2CC30000979F /* TagDetailView.swift in Sources */,
|
E2A37D2B2CED2CC30000979F /* TagDetailView.swift in Sources */,
|
||||||
E2A21C282CB29B2A0060935B /* FeedEntryData.swift in Sources */,
|
E2A21C282CB29B2A0060935B /* FeedEntryData.swift in Sources */,
|
||||||
E29D31942D0B7D280051B7F4 /* SimpleImage.swift in Sources */,
|
E29D31942D0B7D280051B7F4 /* SimpleImage.swift in Sources */,
|
||||||
|
@ -63,6 +63,15 @@ extension String {
|
|||||||
return components(separatedBy: separator).dropLast().joined(separator: separator)
|
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 {
|
func dropBeforeFirst(_ separator: String) -> String {
|
||||||
guard contains(separator) else {
|
guard contains(separator) else {
|
||||||
return self
|
return self
|
||||||
|
@ -72,7 +72,7 @@ struct HtmlCommand: CommandProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for src in srcAttributes {
|
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://") {
|
if url.hasPrefix("http://") || url.hasPrefix("https://") {
|
||||||
results.externalLink(to: url)
|
results.externalLink(to: url)
|
||||||
} else {
|
} 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 {
|
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 {
|
for src in srcAttributes {
|
||||||
guard content.isValidIdForFile(src) else {
|
findFile(path: src, source: "src of <source>")
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]) {
|
func createPages(for posts: [Post]) {
|
||||||
// Sort by newest first
|
// Sort by newest first, filter drafts
|
||||||
let posts = posts.sorted(ascending: false) { $0.startDate }
|
let posts = posts
|
||||||
|
.filter { !$0.isDraft }
|
||||||
|
.sorted(ascending: false) { $0.startDate }
|
||||||
|
|
||||||
let totalCount = posts.count
|
let totalCount = posts.count
|
||||||
guard totalCount > 0 else {
|
guard totalCount > 0 else {
|
||||||
// Create one empty page
|
// Create one empty page
|
||||||
|
@ -246,7 +246,7 @@ final class FileResource: Item {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Get the url path to a file in the output folder.
|
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 {
|
var absoluteUrl: String {
|
||||||
if let customOutputPath {
|
if let customOutputPath {
|
||||||
|
@ -180,6 +180,15 @@ enum FileType: String {
|
|||||||
category == .image
|
category == .image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var generatesImageVersions: Bool {
|
||||||
|
switch self {
|
||||||
|
case .jpg, .png, .avif, .webp, .tiff:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var isVideo: Bool {
|
var isVideo: Bool {
|
||||||
category == .video
|
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)
|
.textFieldStyle(.roundedBorder)
|
||||||
.padding(.horizontal, 8)
|
.padding(.horizontal, 8)
|
||||||
List(filteredPages, selection: $selectedPage) { page in
|
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 {
|
.onAppear {
|
||||||
|
@ -31,7 +31,13 @@ struct PostListView: View {
|
|||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
.padding(.horizontal, 8)
|
.padding(.horizontal, 8)
|
||||||
List(filteredPosts, selection: $selectedPost) { post in
|
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 {
|
}.onAppear {
|
||||||
if selectedPost == nil {
|
if selectedPost == nil {
|
||||||
|
@ -61,56 +61,56 @@ struct GenerationContentView: View {
|
|||||||
Text("\(content.results.requiredFiles.count) files")
|
Text("\(content.results.requiredFiles.count) files")
|
||||||
}
|
}
|
||||||
List {
|
List {
|
||||||
Section("Empty pages") {
|
Section("Inaccessible files (\(content.results.inaccessibleFiles.count))") {
|
||||||
ForEach(content.results.emptyPages.sorted()) { id in
|
|
||||||
Text("\(id.pageId) (\(id.language))")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Section("Inaccessible files") {
|
|
||||||
ForEach(content.results.inaccessibleFiles.sorted()) { file in
|
ForEach(content.results.inaccessibleFiles.sorted()) { file in
|
||||||
Text(file.id)
|
Text(file.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Unparsable files") {
|
Section("Unparsable files (\(content.results.unparsableFiles.count))") {
|
||||||
ForEach(content.results.unparsableFiles.sorted()) { file in
|
ForEach(content.results.unparsableFiles.sorted()) { file in
|
||||||
Text(file.id)
|
Text(file.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Missing files") {
|
Section("Missing files (\(content.results.missingFiles.count))") {
|
||||||
ForEach(content.results.missingFiles.sorted(), id: \.self) { file in
|
ForEach(content.results.missingFiles.sorted(), id: \.self) { file in
|
||||||
Text(file)
|
Text(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Missing tags") {
|
Section("Missing tags (\(content.results.missingTags.count))") {
|
||||||
ForEach(content.results.missingTags.sorted(), id: \.self) { tag in
|
ForEach(content.results.missingTags.sorted(), id: \.self) { tag in
|
||||||
Text(tag)
|
Text(tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Missing pages") {
|
Section("Missing pages (\(content.results.missingPages.count))") {
|
||||||
ForEach(content.results.missingPages.sorted(), id: \.self) { page in
|
ForEach(content.results.missingPages.sorted(), id: \.self) { page in
|
||||||
Text(page)
|
Text(page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Invalid commands") {
|
Section("Invalid commands (\(content.results.invalidCommands.count))") {
|
||||||
ForEach(content.results.invalidCommands.sorted(), id: \.self) { markdown in
|
ForEach(content.results.invalidCommands.sorted(), id: \.self) { markdown in
|
||||||
Text(markdown)
|
Text(markdown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Invalid blocks") {
|
Section("Invalid blocks (\(content.results.invalidBlocks.count))") {
|
||||||
ForEach(content.results.invalidBlocks.sorted(), id: \.self) { markdown in
|
ForEach(content.results.invalidBlocks.sorted(), id: \.self) { markdown in
|
||||||
Text(markdown)
|
Text(markdown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Warnings") {
|
Section("Warnings (\(content.results.warnings.count))") {
|
||||||
ForEach(content.results.warnings.sorted(), id: \.self) { warning in
|
ForEach(content.results.warnings.sorted(), id: \.self) { warning in
|
||||||
Text(warning)
|
Text(warning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section("Unsaved output files") {
|
Section("Unsaved output files (\(content.results.unsavedOutputFiles.count))") {
|
||||||
ForEach(content.results.unsavedOutputFiles.sorted(), id: \.self) { file in
|
ForEach(content.results.unsavedOutputFiles.sorted(), id: \.self) { file in
|
||||||
Text(file)
|
Text(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Section("Empty pages (\(content.results.emptyPages.count))") {
|
||||||
|
ForEach(content.results.emptyPages.sorted()) { id in
|
||||||
|
Text("\(id.pageId) (\(id.language))")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user