Generate first feed pages, images
This commit is contained in:
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,10 +93,3 @@ extension ImageResource {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ImageResource {
|
||||
|
||||
func feedEntryImage(for language: ContentLanguage) -> FeedEntryData.Image {
|
||||
.init(mainImageUrl: "images/\(id)", altText: altText.getText(for: language))
|
||||
}
|
||||
}
|
||||
|
53
CHDataManagement/Model/ImageType.swift
Normal file
53
CHDataManagement/Model/ImageType.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -97,4 +97,8 @@ final class LocalizedPage: ObservableObject {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var relativeUrl: String {
|
||||
"/page/\(urlString)"
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user