Generate first feed pages, images

This commit is contained in:
Christoph Hagen
2024-12-04 08:10:45 +01:00
parent dc7b7a0e90
commit b3cc4a57db
25 changed files with 928 additions and 272 deletions

View File

@ -85,47 +85,20 @@ final class Content: ObservableObject {
return
}
try? storage.update(baseFolder: URL(filePath: contentPath), moveContent: false)
try? storage.update(baseFolder: URL(filePath: contentPath))
observeContentPath()
}
private func observeContentPath() {
$contentPath.sink { newValue in
let url = URL(filePath: newValue)
try? self.storage.update(baseFolder: url, moveContent: true)
do {
try self.storage.update(baseFolder: url)
try self.loadFromDisk()
} catch {
print("Failed to switch content path: \(error)")
}
}
.store(in: &cancellables)
}
// MARK: Folder access
static func accessFolderFromBookmark(key: String, operation: (URL) -> Void) {
guard let bookmarkData = UserDefaults.standard.data(forKey: key) else {
print("No bookmark data to access folder")
return
}
var isStale = false
let folderURL: URL
do {
// Resolve the bookmark to get the folder URL
folderURL = try URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
} catch {
print("Failed to resolve bookmark: \(error)")
return
}
if isStale {
print("Bookmark is stale, consider saving a new bookmark.")
}
// Start accessing the security-scoped resource
if folderURL.startAccessingSecurityScopedResource() {
print("Accessing folder: \(folderURL.path)")
operation(folderURL)
folderURL.stopAccessingSecurityScopedResource()
} else {
print("Failed to access folder: \(folderURL.path)")
}
}
}

View File

@ -93,10 +93,3 @@ extension ImageResource {
}
}
extension ImageResource {
func feedEntryImage(for language: ContentLanguage) -> FeedEntryData.Image {
.init(mainImageUrl: "images/\(id)", altText: altText.getText(for: language))
}
}

View File

@ -0,0 +1,53 @@
import Foundation
import AppKit
enum ImageType {
case jpg
case png
case avif
case webp
case gif
var fileExtension: String {
switch self {
case .jpg: return "jpg"
case .png: return "png"
case .avif: return "avif"
case .webp: return "webp"
case .gif: return "gif"
}
}
var fileType: NSBitmapImageRep.FileType {
switch self {
case .jpg:
return .jpeg
case .png, .avif, .webp:
return .png
case .gif:
return .gif
}
}
}
extension ImageType: CaseIterable {
}
extension ImageType {
init?(fileExtension: String) {
switch fileExtension {
case "jpg", "jpeg":
self = .jpg
case "png":
self = .png
case "avif":
self = .avif
case "webp":
self = .webp
default:
return nil
}
}
}

View File

@ -97,4 +97,8 @@ final class LocalizedPage: ObservableObject {
}
)
}
var relativeUrl: String {
"/page/\(urlString)"
}
}

View File

@ -153,29 +153,4 @@ extension Post {
let endText = Post.dateString(for: endDate, in: language)
return "\(datePrefixString(in: language)) - \(endText)"
}
private func paragraphs(in language: ContentLanguage) -> [String] {
localized(in: language).content
.components(separatedBy: "\n")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { $0 != "" }
}
func linkToPageInFeed(for language: ContentLanguage) -> FeedEntryData.Link? {
nil //.init(url: <#T##String#>, text: <#T##String#>)
}
func feedEntry(for language: ContentLanguage) -> FeedEntryData {
let post = localized(in: language)
return .init(
entryId: "\(id)",
title: post.title,
textAboveTitle: dateText(in: language),
link: linkToPageInFeed(for: language),
tags: tags.map { $0.data(in: language) },
text: paragraphs(in: language),
images: post.images.map {
$0.feedEntryImage(for: language)
})
}
}

View File

@ -1,102 +0,0 @@
import Foundation
struct WebsiteGeneratorConfiguration {
let language: ContentLanguage
let postsPerPage: Int
let postFeedTitle: String
let postFeedDescription: String
let postFeedUrlPrefix: String
}
final class WebsiteGenerator {
let language: ContentLanguage
let content: Content
let postsPerPage: Int
let postFeedTitle: String
let postFeedDescription: String
let postFeedUrlPrefix: String
init(content: Content, configuration: WebsiteGeneratorConfiguration) {
self.content = content
self.language = configuration.language
self.postsPerPage = configuration.postsPerPage
self.postFeedTitle = configuration.postFeedTitle
self.postFeedDescription = configuration.postFeedDescription
self.postFeedUrlPrefix = configuration.postFeedUrlPrefix
}
func generateWebsite() {
createPostFeedPages()
}
private func createPostFeedPages() {
let totalCount = content.posts.count
guard totalCount > 0 else { return }
let navBarData = createNavigationBarData()
let numberOfPages = (totalCount + postsPerPage - 1) / postsPerPage // Round up
for pageIndex in 1...numberOfPages {
let startIndex = (pageIndex - 1) * postsPerPage
let endIndex = min(pageIndex * postsPerPage, totalCount)
let postsOnPage = content.posts[startIndex..<endIndex]
createPostFeedPage(pageIndex, pageCount: numberOfPages, posts: postsOnPage, bar: navBarData)
}
}
private func createNavigationBarData() -> NavigationBarData {
let data = content.websiteData.localized(in: language)
let navigationItems: [NavigationBarLink] = content.websiteData.navigationTags.map {
let localized = $0.localized(in: language)
return .init(text: localized.name, url: localized.urlComponent)
}
return NavigationBarData(
navigationIconPath: "/assets/icons/ch.svg",
iconDescription: data.iconDescription,
navigationItems: navigationItems)
}
private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>, bar: NavigationBarData) {
let posts = posts.map { $0.feedEntry(for: language) }
let feed = PageInFeed(
language: language,
title: postFeedTitle,
description: postFeedDescription,
navigationBarData: bar,
pageNumber: pageIndex,
totalPages: pageCount,
posts: posts)
let fileContent = feed.content
if pageIndex == 1 {
save(fileContent, to: "\(postFeedUrlPrefix).html")
} else {
save(fileContent, to: "\(postFeedUrlPrefix)-\(pageIndex).html")
}
}
private func save(_ content: String, to relativePath: String) {
Content.accessFolderFromBookmark(key: Storage.outputPathBookmarkKey) { folder in
let outputFile = folder.appendingPathComponent(relativePath, isDirectory: false)
do {
try content
.data(using: .utf8)!
.write(to: outputFile)
} catch {
print("Failed to save: \(error)")
}
}
}
}