Generate tag overview, add file action
This commit is contained in:
@ -48,9 +48,8 @@ final class GenerationResults: ObservableObject {
|
||||
|
||||
private(set) var general: PageGenerationResults!
|
||||
|
||||
var resultCount: Int {
|
||||
cache.count
|
||||
}
|
||||
@Published
|
||||
var resultCount: Int = 0
|
||||
|
||||
// MARK: Life cycle
|
||||
|
||||
@ -59,12 +58,14 @@ final class GenerationResults: ObservableObject {
|
||||
let general = PageGenerationResults(itemId: id, delegate: self)
|
||||
self.general = general
|
||||
cache[id] = general
|
||||
self.resultCount = 1
|
||||
}
|
||||
|
||||
func makeResults(_ itemId: ItemId) -> PageGenerationResults {
|
||||
guard let result = cache[itemId] else {
|
||||
let result = PageGenerationResults(itemId: itemId, delegate: self)
|
||||
cache[itemId] = result
|
||||
update { self.resultCount += 1 }
|
||||
return result
|
||||
}
|
||||
return result
|
||||
@ -116,7 +117,6 @@ final class GenerationResults: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Adding entries
|
||||
|
||||
func inaccessibleContent(file: FileResource) {
|
||||
|
@ -49,6 +49,7 @@ final class ImageGenerator {
|
||||
*/
|
||||
func removeVersions(of image: String) {
|
||||
generatedImages[image] = nil
|
||||
save()
|
||||
}
|
||||
|
||||
func recalculateGeneratedImages(by images: Set<String>) {
|
||||
@ -119,6 +120,10 @@ final class ImageGenerator {
|
||||
}
|
||||
|
||||
if version.type == .avif {
|
||||
if version.image.type == .gif {
|
||||
// Skip GIFs, since they can't be converted by avifenc
|
||||
return true
|
||||
}
|
||||
// AVIF conversion is very slow, so we save bash commands
|
||||
// for the conversion instead
|
||||
let baseVersion = ImageVersion(
|
||||
|
@ -44,7 +44,7 @@ struct ImageSet {
|
||||
|
||||
var result = "<picture>"
|
||||
result += "<source type='image/avif' srcset='\(prefix1x).avif 1x, \(prefix2x).avif 2x'/>"
|
||||
result += "<source type='image/webp' srcset='\(prefix1x).webm 1x, \(prefix1x).webm 2x'/>"
|
||||
result += "<source type='image/webp' srcset='\(prefix1x).webp 1x, \(prefix1x).webp 2x'/>"
|
||||
result += "<img srcset='\(prefix2x)\(fileExtension) 2x' src='\(prefix1x)\(fileExtension)' loading='lazy' alt='\(description.htmlEscaped())'/>"
|
||||
result += "</picture>"
|
||||
return result
|
||||
|
@ -1,10 +1,78 @@
|
||||
|
||||
private struct TagData {
|
||||
|
||||
let url: String
|
||||
|
||||
let title: String
|
||||
|
||||
let localized: LocalizedTag
|
||||
|
||||
init(tag: Tag, language: ContentLanguage) {
|
||||
let localized = tag.localized(in: language)
|
||||
self.url = tag.absoluteUrl(in: language)
|
||||
self.title = localized.linkPreviewTitle ?? localized.name
|
||||
self.localized = localized
|
||||
}
|
||||
}
|
||||
|
||||
extension TagData: Comparable {
|
||||
|
||||
static func < (lhs: TagData, rhs: TagData) -> Bool {
|
||||
lhs.title < rhs.title
|
||||
}
|
||||
|
||||
static func == (lhs: TagData, rhs: TagData) -> Bool {
|
||||
lhs.title == rhs.title
|
||||
}
|
||||
}
|
||||
|
||||
private struct TagHeaderContent {
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
let title: String
|
||||
|
||||
let description: String?
|
||||
|
||||
let iconUrl: String
|
||||
|
||||
let links: [NavigationBar.Link]
|
||||
|
||||
let headers: Set<HeaderElement>
|
||||
|
||||
let baseUrl: String
|
||||
|
||||
let localizedBaseUrl: String
|
||||
|
||||
private func url(pageNumber: Int) -> String {
|
||||
baseUrl + "/\(pageNumber)"
|
||||
}
|
||||
|
||||
func fileUrl(pageNumber: Int) -> String {
|
||||
url(pageNumber: pageNumber) + ".html"
|
||||
}
|
||||
|
||||
func pageHeader(pageNumber: Int) -> PageHeader {
|
||||
.init(
|
||||
language: language,
|
||||
title: title,
|
||||
description: description,
|
||||
iconUrl: iconUrl,
|
||||
languageButton: .init(
|
||||
text: language.next.rawValue,
|
||||
url: localizedBaseUrl + "/\(pageNumber)"),
|
||||
links: links,
|
||||
headers: headers,
|
||||
icons: [])
|
||||
}
|
||||
}
|
||||
|
||||
final class TagOverviewGenerator {
|
||||
|
||||
let content: Content
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
init(content: Content, language: ContentLanguage, results: PageGenerationResults) {
|
||||
@ -13,49 +81,67 @@ final class TagOverviewGenerator {
|
||||
self.results = results
|
||||
}
|
||||
|
||||
func generatePage(tags: [Tag], overview: TagOverviewPage) {
|
||||
let iconUrl = content.settings.navigation.localized(in: language).rootUrl
|
||||
let languageUrl = overview.absoluteUrl(in: language.next)
|
||||
let languageButton = NavigationBar.Link(
|
||||
text: language.next.rawValue,
|
||||
url: languageUrl)
|
||||
|
||||
func generatePages(tags: [Tag], overview: TagOverviewPage) {
|
||||
let localized = overview.localized(in: language)
|
||||
|
||||
let pageHeader = PageHeader(
|
||||
let header = TagHeaderContent(
|
||||
language: language,
|
||||
title: localized.linkPreviewTitle ?? localized.title,
|
||||
description: localized.linkPreviewDescription,
|
||||
iconUrl: iconUrl,
|
||||
languageButton: languageButton,
|
||||
iconUrl: content.settings.navigation.localized(in: language).rootUrl,
|
||||
links: content.navigationBar(in: language),
|
||||
headers: content.defaultPageHeaders,
|
||||
icons: [])
|
||||
baseUrl: overview.absoluteUrl(in: language),
|
||||
localizedBaseUrl: overview.absoluteUrl(in: language.next))
|
||||
|
||||
// Sort tags by title
|
||||
let tagData = tags.map {
|
||||
TagData(tag: $0, language: language)
|
||||
}.sorted { $0.title }
|
||||
|
||||
let totalCount = tagData.count
|
||||
guard totalCount > 0 else {
|
||||
// Create one empty page
|
||||
generatePage(tags: [], header: header, page: 1, totalPages: 1)
|
||||
return
|
||||
}
|
||||
let tagsPerPage = content.settings.posts.postsPerPage
|
||||
|
||||
let numberOfPages = (totalCount + tagsPerPage - 1) / tagsPerPage // Round up
|
||||
for pageIndex in 1...numberOfPages {
|
||||
let startIndex = (pageIndex - 1) * tagsPerPage
|
||||
let endIndex = min(pageIndex * tagsPerPage, totalCount)
|
||||
let tagsOnPage = tagData[startIndex..<endIndex]
|
||||
generatePage(tags: tagsOnPage, header: header, page: pageIndex, totalPages: numberOfPages)
|
||||
}
|
||||
}
|
||||
|
||||
private func generatePage(tags: ArraySlice<TagData>, header: TagHeaderContent, page pageNumber: Int, totalPages: Int) {
|
||||
let pageHeader = header.pageHeader(pageNumber: pageNumber)
|
||||
|
||||
let page = GenericPage(
|
||||
header: pageHeader,
|
||||
additionalFooter: "") { content in
|
||||
content += "<h1>\(localized.title)</h1>"
|
||||
content += "<h1>\(header.title)</h1>"
|
||||
for tag in tags {
|
||||
let localized = tag.localized(in: self.language)
|
||||
let url = tag.absoluteUrl(in: self.language)
|
||||
let title = localized.name
|
||||
let description = localized.description ?? ""
|
||||
let image = self.makePageImage(item: localized)
|
||||
let description = tag.localized.description ?? ""
|
||||
let image = self.makePageImage(item: tag.localized)
|
||||
|
||||
content += RelatedPageLink(
|
||||
title: title,
|
||||
title: tag.title,
|
||||
description: description,
|
||||
url: url,
|
||||
url: tag.url,
|
||||
image: image)
|
||||
.content
|
||||
.content
|
||||
}
|
||||
if totalPages > 1 {
|
||||
content += PostFeedPageNavigation(
|
||||
linkPrefix: header.baseUrl,
|
||||
currentPage: pageNumber,
|
||||
numberOfPages: totalPages).content
|
||||
}
|
||||
// if totalPages > 1 {
|
||||
// content += PostFeedPageNavigation(currentPage: pageNumber, numberOfPages: totalPages, language: language).content
|
||||
// }
|
||||
}
|
||||
let fileContent = page.content
|
||||
let url = overview.absoluteUrl(in: language) + ".html"
|
||||
let url = header.fileUrl(pageNumber: pageNumber)
|
||||
|
||||
guard content.storage.write(fileContent, to: url) else {
|
||||
results.unsavedOutput(url, source: .tagOverview)
|
||||
|
@ -23,6 +23,8 @@ final class PostListPageGenerator {
|
||||
func createPages(for posts: [Post]) {
|
||||
let totalCount = posts.count
|
||||
guard totalCount > 0 else {
|
||||
// Create one empty page
|
||||
createPostFeedPage(1, pageCount: 1, posts: [])
|
||||
return
|
||||
}
|
||||
let postsPerPage = source.postsPerPage
|
||||
@ -80,7 +82,7 @@ final class PostListPageGenerator {
|
||||
pageNumber: pageIndex,
|
||||
totalPages: pageCount,
|
||||
languageButtonUrl: languageButtonUrl,
|
||||
linkPrefix: "/" + source.pageUrlPrefix(for: language) + "/")
|
||||
linkPrefix: source.pageUrlPrefix(for: language))
|
||||
let filePath = pageUrl(in: language, pageNumber: pageIndex) + ".html"
|
||||
guard save(fileContent, to: filePath) else {
|
||||
source.results.unsavedOutput(filePath, source: .feed)
|
||||
|
Reference in New Issue
Block a user