From 2a50773e2f07ce5586a98c0521afdeb7cf54d2ca Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Tue, 7 Jan 2025 10:34:36 +0100 Subject: [PATCH] Custom page link texts, optional post title --- .../Post Lists/PostListPageGenerator.swift | 5 ++- CHDataManagement/Model/Content+Load.swift | 16 +------- CHDataManagement/Model/Content+Save.swift | 13 ------ .../Model/Item/LocalizedItem.swift | 4 +- CHDataManagement/Model/LocalizedPost.swift | 40 ++++++++++++++++++- CHDataManagement/Model/Post.swift | 20 +++++++--- .../Settings/LocalizedPostSettings.swift | 10 ++++- .../Preview Content/WebsiteData+Mock.swift | 6 ++- CHDataManagement/Storage/Model/PostFile.swift | 2 + .../Settings/LocalizedPostSettingsFile.swift | 10 ++++- .../Views/Posts/LocalizedPostDetailView.swift | 5 +++ .../Views/Posts/PostContentView.swift | 2 +- .../Views/Posts/PostListView.swift | 4 +- .../LocalizedPostFeedSettingsView.swift | 5 +++ .../Views/Tags/PostTagAssignmentView.swift | 2 +- .../Views/Tags/TagContentView.swift | 2 +- 16 files changed, 98 insertions(+), 48 deletions(-) diff --git a/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift b/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift index 1296d88..23205dc 100644 --- a/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift +++ b/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift @@ -43,11 +43,10 @@ final class PostListPageGenerator { private func makePostData(post: Post) -> FeedEntryData { let localized: LocalizedPost = post.localized(in: language) - #warning("Add post link text to settings or to each post") let linkUrl = post.linkedPage.map { FeedEntryData.Link( 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 @@ -69,6 +68,7 @@ final class PostListPageGenerator { tags: tags, text: localized.text.components(separatedBy: "\n\n"), images: images) + #warning("Treat post text as markdown") } private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice) { @@ -89,6 +89,7 @@ final class PostListPageGenerator { totalPages: pageCount, languageButtonUrl: languageButtonUrl, linkPrefix: source.pageUrlPrefix(for: language)) + // Includes leading slash let filePath = pageUrl(in: language, pageNumber: pageIndex) + ".html" guard save(fileContent, to: filePath) else { source.results.unsavedOutput(filePath, source: .feed) diff --git a/CHDataManagement/Model/Content+Load.swift b/CHDataManagement/Model/Content+Load.swift index 1d7fd17..96fef3d 100644 --- a/CHDataManagement/Model/Content+Load.swift +++ b/CHDataManagement/Model/Content+Load.swift @@ -13,18 +13,6 @@ extension Content { 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 { LocalizedPage( content: self, @@ -96,8 +84,8 @@ extension Content { let posts: [String : Post] = postsData.reduce(into: [:]) { dict, data in let (postId, post) = data let linkedPage = post.linkedPageId.map { pages[$0] } - let german = convert(post.german, images: images) - let english = convert(post.english, images: images) + let german = LocalizedPost(content: self, file: post.german, images: images) + let english = LocalizedPost(content: self, file: post.english, images: images) dict[postId] = Post( content: self, diff --git a/CHDataManagement/Model/Content+Save.swift b/CHDataManagement/Model/Content+Save.swift index 9aa03c7..c23ad91 100644 --- a/CHDataManagement/Model/Content+Save.swift +++ b/CHDataManagement/Model/Content+Save.swift @@ -83,16 +83,3 @@ private extension Post { 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) - } -} diff --git a/CHDataManagement/Model/Item/LocalizedItem.swift b/CHDataManagement/Model/Item/LocalizedItem.swift index 3ee80e3..cf544a6 100644 --- a/CHDataManagement/Model/Item/LocalizedItem.swift +++ b/CHDataManagement/Model/Item/LocalizedItem.swift @@ -12,8 +12,8 @@ extension LocalizedItem { func localized(in language: ContentLanguage) -> Localized { switch language { - case .german: return german - case .english: return english + case .german: german + case .english: english } } } diff --git a/CHDataManagement/Model/LocalizedPost.swift b/CHDataManagement/Model/LocalizedPost.swift index 395b4f7..928a3a0 100644 --- a/CHDataManagement/Model/LocalizedPost.swift +++ b/CHDataManagement/Model/LocalizedPost.swift @@ -6,7 +6,7 @@ final class LocalizedPost: ObservableObject { unowned let content: Content @Published - var title: String + var title: String? @Published var text: String @@ -17,6 +17,10 @@ final class LocalizedPost: ObservableObject { @Published var images: [FileResource] + /// The text to show for the link to the `linkedPage` + @Published + var pageLinkText: String? + @Published var linkPreviewImage: FileResource? @@ -31,18 +35,50 @@ final class LocalizedPost: ObservableObject { text: String, lastModified: Date? = nil, images: [FileResource] = [], + pageLinkText: String? = nil, linkPreviewImage: FileResource? = nil, linkPreviewTitle: String? = nil, linkPreviewDescription: String? = nil) { self.content = content - self.title = title ?? "" + self.title = title self.text = text self.lastModified = lastModified self.images = images + self.pageLinkText = pageLinkText self.linkPreviewImage = linkPreviewImage self.linkPreviewTitle = linkPreviewTitle 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 { diff --git a/CHDataManagement/Model/Post.swift b/CHDataManagement/Model/Post.swift index 289e57e..50cdf02 100644 --- a/CHDataManagement/Model/Post.swift +++ b/CHDataManagement/Model/Post.swift @@ -107,11 +107,17 @@ final class Post: Item { tags.append(tag) } - func localized(in language: ContentLanguage) -> LocalizedPost { - switch language { - case .english: return english - case .german: return german - } + /** + A title for the UI, not the generation. + */ + 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 { @@ -134,3 +140,7 @@ final class Post: Item { extension Post: DateItem { } + +extension Post: LocalizedItem { + +} diff --git a/CHDataManagement/Model/Settings/LocalizedPostSettings.swift b/CHDataManagement/Model/Settings/LocalizedPostSettings.swift index 8c10414..4439a8f 100644 --- a/CHDataManagement/Model/Settings/LocalizedPostSettings.swift +++ b/CHDataManagement/Model/Settings/LocalizedPostSettings.swift @@ -11,10 +11,14 @@ final class LocalizedPostSettings: ObservableObject { @Published 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.description = description self.feedUrlPrefix = feedUrlPrefix + self.defaultPageLinkText = defaultPageLinkText } // MARK: Storage @@ -23,12 +27,14 @@ final class LocalizedPostSettings: ObservableObject { self.title = file.feedTitle self.description = file.feedDescription self.feedUrlPrefix = file.feedUrlPrefix + self.defaultPageLinkText = file.defaultPageLinkText ?? "View" } var file: LocalizedPostSettingsFile { .init( feedTitle: title, feedDescription: description, - feedUrlPrefix: feedUrlPrefix) + feedUrlPrefix: feedUrlPrefix, + defaultPageLinkText: defaultPageLinkText) } } diff --git a/CHDataManagement/Preview Content/WebsiteData+Mock.swift b/CHDataManagement/Preview Content/WebsiteData+Mock.swift index 290616c..cbfc8d1 100644 --- a/CHDataManagement/Preview Content/WebsiteData+Mock.swift +++ b/CHDataManagement/Preview Content/WebsiteData+Mock.swift @@ -27,13 +27,15 @@ extension LocalizedPostSettings { .init( title: "Titel", description: "Beschreibung", - feedUrlPrefix: "blog") + feedUrlPrefix: "blog", + defaultPageLinkText: "Anzeigen") } static var english: LocalizedPostSettings { .init( title: "A Title", description: "Description", - feedUrlPrefix: "feed") + feedUrlPrefix: "feed", + defaultPageLinkText: "View") } } diff --git a/CHDataManagement/Storage/Model/PostFile.swift b/CHDataManagement/Storage/Model/PostFile.swift index c2464df..3cb723a 100644 --- a/CHDataManagement/Storage/Model/PostFile.swift +++ b/CHDataManagement/Storage/Model/PostFile.swift @@ -36,6 +36,8 @@ struct LocalizedPostFile { let lastModifiedDate: Date? + let pageLinkText: String? + let linkPreviewImage: String? let linkPreviewTitle: String? diff --git a/CHDataManagement/Storage/Model/Settings/LocalizedPostSettingsFile.swift b/CHDataManagement/Storage/Model/Settings/LocalizedPostSettingsFile.swift index cf6d1b2..cba8577 100644 --- a/CHDataManagement/Storage/Model/Settings/LocalizedPostSettingsFile.swift +++ b/CHDataManagement/Storage/Model/Settings/LocalizedPostSettingsFile.swift @@ -9,6 +9,13 @@ struct LocalizedPostSettingsFile { /// The path to the feed in the final website, appended with the page number 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 { } @@ -18,6 +25,7 @@ extension LocalizedPostSettingsFile { static var `default`: LocalizedPostSettingsFile { .init(feedTitle: "A title", feedDescription: "A description", - feedUrlPrefix: "blog") + feedUrlPrefix: "blog", + defaultPageLinkText: "View") } } diff --git a/CHDataManagement/Views/Posts/LocalizedPostDetailView.swift b/CHDataManagement/Views/Posts/LocalizedPostDetailView.swift index c555281..06d0637 100644 --- a/CHDataManagement/Views/Posts/LocalizedPostDetailView.swift +++ b/CHDataManagement/Views/Posts/LocalizedPostDetailView.swift @@ -7,6 +7,11 @@ struct LocalizedPostDetailView: View { var body: some View { 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( title: "Preview Title", text: $post.linkPreviewTitle, diff --git a/CHDataManagement/Views/Posts/PostContentView.swift b/CHDataManagement/Views/Posts/PostContentView.swift index 050e71d..5f954fb 100644 --- a/CHDataManagement/Views/Posts/PostContentView.swift +++ b/CHDataManagement/Views/Posts/PostContentView.swift @@ -38,7 +38,7 @@ private struct LocalizedTitle: View { } var body: some View { - TextField("", text: $post.title) + OptionalTextField("", text: $post.title) .font(.system(size: 24, weight: .bold)) .foregroundStyle(Color.primary) .textFieldStyle(.plain) diff --git a/CHDataManagement/Views/Posts/PostListView.swift b/CHDataManagement/Views/Posts/PostListView.swift index 7b4e774..9582e80 100644 --- a/CHDataManagement/Views/Posts/PostListView.swift +++ b/CHDataManagement/Views/Posts/PostListView.swift @@ -22,7 +22,7 @@ struct PostListView: View { guard !searchString.isEmpty else { 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 { @@ -31,7 +31,7 @@ struct PostListView: View { .textFieldStyle(.roundedBorder) .padding(.horizontal, 8) List(filteredPosts, selection: $selectedPost) { post in - Text(post.localized(in: language).title).tag(post) + Text(post.title(in: language)).tag(post) } }.onAppear { if selectedPost == nil { diff --git a/CHDataManagement/Views/Settings/LocalizedPostFeedSettingsView.swift b/CHDataManagement/Views/Settings/LocalizedPostFeedSettingsView.swift index bd524c6..08d4d53 100644 --- a/CHDataManagement/Views/Settings/LocalizedPostFeedSettingsView.swift +++ b/CHDataManagement/Views/Settings/LocalizedPostFeedSettingsView.swift @@ -7,6 +7,11 @@ struct LocalizedPostFeedSettingsView: View { var body: some View { 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( title: "Title", text: $settings.title, diff --git a/CHDataManagement/Views/Tags/PostTagAssignmentView.swift b/CHDataManagement/Views/Tags/PostTagAssignmentView.swift index f1f71d1..fe37ffa 100644 --- a/CHDataManagement/Views/Tags/PostTagAssignmentView.swift +++ b/CHDataManagement/Views/Tags/PostTagAssignmentView.swift @@ -17,7 +17,7 @@ private struct PostSelectionView: View { let isSelected = post.contains(tag) Image(systemSymbol: isSelected ? .checkmarkCircleFill : .circle) .foregroundStyle(Color.blue) - Text(post.localized(in: language).title) + Text(post.title(in: language)) } .contentShape(Rectangle()) .onTapGesture { diff --git a/CHDataManagement/Views/Tags/TagContentView.swift b/CHDataManagement/Views/Tags/TagContentView.swift index 6fa4d13..a2a391b 100644 --- a/CHDataManagement/Views/Tags/TagContentView.swift +++ b/CHDataManagement/Views/Tags/TagContentView.swift @@ -39,7 +39,7 @@ struct TagContentView: View { } Section("Posts") { ForEach(selectedPosts) { post in - Text(post.localized(in: language).title) + Text(post.title(in: language)) } Button(action: { showPostSelection = true }) { Text("Select posts")