Custom page link texts, optional post title

This commit is contained in:
Christoph Hagen 2025-01-07 10:34:36 +01:00
parent 1f7167b076
commit 2a50773e2f
16 changed files with 98 additions and 48 deletions

View File

@ -43,11 +43,10 @@ final class PostListPageGenerator {
private func makePostData(post: Post) -> FeedEntryData { private func makePostData(post: Post) -> FeedEntryData {
let localized: LocalizedPost = post.localized(in: language) let localized: LocalizedPost = post.localized(in: language)
#warning("Add post link text to settings or to each post")
let linkUrl = post.linkedPage.map { let linkUrl = post.linkedPage.map {
FeedEntryData.Link( FeedEntryData.Link(
url: $0.absoluteUrl(in: language), url: $0.absoluteUrl(in: language),
text: language == .english ? "View" : "Anzeigen") text: localized.pageLinkText ?? post.content.settings.localized(in: language).defaultPageLinkText)
} }
// Use the tags of the page if one is linked // Use the tags of the page if one is linked
@ -69,6 +68,7 @@ final class PostListPageGenerator {
tags: tags, tags: tags,
text: localized.text.components(separatedBy: "\n\n"), text: localized.text.components(separatedBy: "\n\n"),
images: images) images: images)
#warning("Treat post text as markdown")
} }
private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>) { private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>) {
@ -89,6 +89,7 @@ final class PostListPageGenerator {
totalPages: pageCount, totalPages: pageCount,
languageButtonUrl: languageButtonUrl, languageButtonUrl: languageButtonUrl,
linkPrefix: source.pageUrlPrefix(for: language)) linkPrefix: source.pageUrlPrefix(for: language))
// Includes leading slash
let filePath = pageUrl(in: language, pageNumber: pageIndex) + ".html" let filePath = pageUrl(in: language, pageNumber: pageIndex) + ".html"
guard save(fileContent, to: filePath) else { guard save(fileContent, to: filePath) else {
source.results.unsavedOutput(filePath, source: .feed) source.results.unsavedOutput(filePath, source: .feed)

View File

@ -13,18 +13,6 @@ extension Content {
originalUrl: tag.originalURL) originalUrl: tag.originalURL)
} }
private func convert(_ post: LocalizedPostFile, images: [String : FileResource]) -> LocalizedPost {
LocalizedPost(
content: self,
title: post.title,
text: post.content,
lastModified: post.lastModifiedDate,
images: post.images.compactMap { images[$0] },
linkPreviewImage: post.linkPreviewImage.map { images[$0] },
linkPreviewTitle: post.linkPreviewTitle,
linkPreviewDescription: post.linkPreviewDescription)
}
private func convert(_ page: LocalizedPageFile, images: [String : FileResource]) -> LocalizedPage { private func convert(_ page: LocalizedPageFile, images: [String : FileResource]) -> LocalizedPage {
LocalizedPage( LocalizedPage(
content: self, content: self,
@ -96,8 +84,8 @@ extension Content {
let posts: [String : Post] = postsData.reduce(into: [:]) { dict, data in let posts: [String : Post] = postsData.reduce(into: [:]) { dict, data in
let (postId, post) = data let (postId, post) = data
let linkedPage = post.linkedPageId.map { pages[$0] } let linkedPage = post.linkedPageId.map { pages[$0] }
let german = convert(post.german, images: images) let german = LocalizedPost(content: self, file: post.german, images: images)
let english = convert(post.english, images: images) let english = LocalizedPost(content: self, file: post.english, images: images)
dict[postId] = Post( dict[postId] = Post(
content: self, content: self,

View File

@ -83,16 +83,3 @@ private extension Post {
linkedPageId: linkedPage?.id) linkedPageId: linkedPage?.id)
} }
} }
private extension LocalizedPost {
var postFile: LocalizedPostFile {
.init(images: images.map { $0.id },
title: title.nonEmpty,
content: text,
lastModifiedDate: lastModified,
linkPreviewImage: linkPreviewImage?.id,
linkPreviewTitle: linkPreviewTitle,
linkPreviewDescription: linkPreviewDescription)
}
}

View File

@ -12,8 +12,8 @@ extension LocalizedItem {
func localized(in language: ContentLanguage) -> Localized { func localized(in language: ContentLanguage) -> Localized {
switch language { switch language {
case .german: return german case .german: german
case .english: return english case .english: english
} }
} }
} }

View File

@ -6,7 +6,7 @@ final class LocalizedPost: ObservableObject {
unowned let content: Content unowned let content: Content
@Published @Published
var title: String var title: String?
@Published @Published
var text: String var text: String
@ -17,6 +17,10 @@ final class LocalizedPost: ObservableObject {
@Published @Published
var images: [FileResource] var images: [FileResource]
/// The text to show for the link to the `linkedPage`
@Published
var pageLinkText: String?
@Published @Published
var linkPreviewImage: FileResource? var linkPreviewImage: FileResource?
@ -31,18 +35,50 @@ final class LocalizedPost: ObservableObject {
text: String, text: String,
lastModified: Date? = nil, lastModified: Date? = nil,
images: [FileResource] = [], images: [FileResource] = [],
pageLinkText: String? = nil,
linkPreviewImage: FileResource? = nil, linkPreviewImage: FileResource? = nil,
linkPreviewTitle: String? = nil, linkPreviewTitle: String? = nil,
linkPreviewDescription: String? = nil) { linkPreviewDescription: String? = nil) {
self.content = content self.content = content
self.title = title ?? "" self.title = title
self.text = text self.text = text
self.lastModified = lastModified self.lastModified = lastModified
self.images = images self.images = images
self.pageLinkText = pageLinkText
self.linkPreviewImage = linkPreviewImage self.linkPreviewImage = linkPreviewImage
self.linkPreviewTitle = linkPreviewTitle self.linkPreviewTitle = linkPreviewTitle
self.linkPreviewDescription = linkPreviewDescription self.linkPreviewDescription = linkPreviewDescription
} }
init(content: Content, file: LocalizedPostFile, images: [String : FileResource]) {
self.content = content
self.title = file.title
self.text = file.content
self.lastModified = file.lastModifiedDate
self.images = file.images.compactMap { images[$0] }
self.pageLinkText = file.pageLinkText
self.linkPreviewImage = file.linkPreviewImage.map { images[$0] }
self.linkPreviewTitle = file.linkPreviewTitle
self.linkPreviewDescription = file.linkPreviewDescription
}
var postFile: LocalizedPostFile {
.init(images: images.map { $0.id },
title: title,
content: text,
lastModifiedDate: lastModified,
pageLinkText: pageLinkText,
linkPreviewImage: linkPreviewImage?.id,
linkPreviewTitle: linkPreviewTitle,
linkPreviewDescription: linkPreviewDescription)
}
func contains(_ string: String) -> Bool {
if let title, title.contains(string) {
return true
}
return text.contains(string)
}
} }
extension LocalizedPost: LinkPreviewItem { extension LocalizedPost: LinkPreviewItem {

View File

@ -107,11 +107,17 @@ final class Post: Item {
tags.append(tag) tags.append(tag)
} }
func localized(in language: ContentLanguage) -> LocalizedPost { /**
switch language { A title for the UI, not the generation.
case .english: return english */
case .german: return german override func title(in language: ContentLanguage) -> String {
localized(in: language).title ?? id
} }
func contains(_ string: String) -> Bool {
id.contains(string) ||
german.contains(string) ||
english.contains(string)
} }
func isValid(id: String) -> Bool { func isValid(id: String) -> Bool {
@ -134,3 +140,7 @@ final class Post: Item {
extension Post: DateItem { extension Post: DateItem {
} }
extension Post: LocalizedItem {
}

View File

@ -11,10 +11,14 @@ final class LocalizedPostSettings: ObservableObject {
@Published @Published
var feedUrlPrefix: String var feedUrlPrefix: String
init(title: String, description: String, feedUrlPrefix: String) { @Published
var defaultPageLinkText: String
init(title: String, description: String, feedUrlPrefix: String, defaultPageLinkText: String) {
self.title = title self.title = title
self.description = description self.description = description
self.feedUrlPrefix = feedUrlPrefix self.feedUrlPrefix = feedUrlPrefix
self.defaultPageLinkText = defaultPageLinkText
} }
// MARK: Storage // MARK: Storage
@ -23,12 +27,14 @@ final class LocalizedPostSettings: ObservableObject {
self.title = file.feedTitle self.title = file.feedTitle
self.description = file.feedDescription self.description = file.feedDescription
self.feedUrlPrefix = file.feedUrlPrefix self.feedUrlPrefix = file.feedUrlPrefix
self.defaultPageLinkText = file.defaultPageLinkText ?? "View"
} }
var file: LocalizedPostSettingsFile { var file: LocalizedPostSettingsFile {
.init( .init(
feedTitle: title, feedTitle: title,
feedDescription: description, feedDescription: description,
feedUrlPrefix: feedUrlPrefix) feedUrlPrefix: feedUrlPrefix,
defaultPageLinkText: defaultPageLinkText)
} }
} }

View File

@ -27,13 +27,15 @@ extension LocalizedPostSettings {
.init( .init(
title: "Titel", title: "Titel",
description: "Beschreibung", description: "Beschreibung",
feedUrlPrefix: "blog") feedUrlPrefix: "blog",
defaultPageLinkText: "Anzeigen")
} }
static var english: LocalizedPostSettings { static var english: LocalizedPostSettings {
.init( .init(
title: "A Title", title: "A Title",
description: "Description", description: "Description",
feedUrlPrefix: "feed") feedUrlPrefix: "feed",
defaultPageLinkText: "View")
} }
} }

View File

@ -36,6 +36,8 @@ struct LocalizedPostFile {
let lastModifiedDate: Date? let lastModifiedDate: Date?
let pageLinkText: String?
let linkPreviewImage: String? let linkPreviewImage: String?
let linkPreviewTitle: String? let linkPreviewTitle: String?

View File

@ -9,6 +9,13 @@ struct LocalizedPostSettingsFile {
/// The path to the feed in the final website, appended with the page number /// The path to the feed in the final website, appended with the page number
let feedUrlPrefix: String let feedUrlPrefix: String
/**
The text to display when linking to a page
Each post may define a custom text.
*/
let defaultPageLinkText: String?
} }
extension LocalizedPostSettingsFile: Codable { } extension LocalizedPostSettingsFile: Codable { }
@ -18,6 +25,7 @@ extension LocalizedPostSettingsFile {
static var `default`: LocalizedPostSettingsFile { static var `default`: LocalizedPostSettingsFile {
.init(feedTitle: "A title", .init(feedTitle: "A title",
feedDescription: "A description", feedDescription: "A description",
feedUrlPrefix: "blog") feedUrlPrefix: "blog",
defaultPageLinkText: "View")
} }
} }

View File

@ -7,6 +7,11 @@ struct LocalizedPostDetailView: View {
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
OptionalStringPropertyView(
title: "Custom Page Link Text",
text: $post.pageLinkText,
footer: "The custom text to show for the link to the linked page")
OptionalStringPropertyView( OptionalStringPropertyView(
title: "Preview Title", title: "Preview Title",
text: $post.linkPreviewTitle, text: $post.linkPreviewTitle,

View File

@ -38,7 +38,7 @@ private struct LocalizedTitle: View {
} }
var body: some View { var body: some View {
TextField("", text: $post.title) OptionalTextField("", text: $post.title)
.font(.system(size: 24, weight: .bold)) .font(.system(size: 24, weight: .bold))
.foregroundStyle(Color.primary) .foregroundStyle(Color.primary)
.textFieldStyle(.plain) .textFieldStyle(.plain)

View File

@ -22,7 +22,7 @@ struct PostListView: View {
guard !searchString.isEmpty else { guard !searchString.isEmpty else {
return content.posts return content.posts
} }
return content.posts.filter { $0.localized(in: language).title.contains(searchString) } return content.posts.filter { $0.contains(searchString) }
} }
var body: some View { var body: some View {
@ -31,7 +31,7 @@ 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.localized(in: language).title).tag(post) Text(post.title(in: language)).tag(post)
} }
}.onAppear { }.onAppear {
if selectedPost == nil { if selectedPost == nil {

View File

@ -7,6 +7,11 @@ struct LocalizedPostFeedSettingsView: View {
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
StringPropertyView(
title: "Default Page Link Text",
text: $settings.defaultPageLinkText,
footer: "The text to display when linking from a post to a page. Each post can provide a custom text.")
StringPropertyView( StringPropertyView(
title: "Title", title: "Title",
text: $settings.title, text: $settings.title,

View File

@ -17,7 +17,7 @@ private struct PostSelectionView: View {
let isSelected = post.contains(tag) let isSelected = post.contains(tag)
Image(systemSymbol: isSelected ? .checkmarkCircleFill : .circle) Image(systemSymbol: isSelected ? .checkmarkCircleFill : .circle)
.foregroundStyle(Color.blue) .foregroundStyle(Color.blue)
Text(post.localized(in: language).title) Text(post.title(in: language))
} }
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {

View File

@ -39,7 +39,7 @@ struct TagContentView: View {
} }
Section("Posts") { Section("Posts") {
ForEach(selectedPosts) { post in ForEach(selectedPosts) { post in
Text(post.localized(in: language).title) Text(post.title(in: language))
} }
Button(action: { showPostSelection = true }) { Button(action: { showPostSelection = true }) {
Text("Select posts") Text("Select posts")