diff --git a/CHDataManagement.xcodeproj/project.pbxproj b/CHDataManagement.xcodeproj/project.pbxproj index b81e7e2..f3fa458 100644 --- a/CHDataManagement.xcodeproj/project.pbxproj +++ b/CHDataManagement.xcodeproj/project.pbxproj @@ -213,6 +213,7 @@ E2FE0F1E2D281AE1002963B7 /* TagOverviewGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */; }; E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */; }; E2FE0F222D2A84A0002963B7 /* VideoCommandProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */; }; + E2FE0F242D2A8C21002963B7 /* TagDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -417,6 +418,7 @@ E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewGenerator.swift; sourceTree = ""; }; E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Remove.swift"; sourceTree = ""; }; E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCommandProcessor.swift; sourceTree = ""; }; + E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagDisplayView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -638,6 +640,7 @@ E2A21C372CB9A4F10060935B /* Generic */ = { isa = PBXGroup; children = ( + E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */, E229902F2D0F75CF009F8D77 /* BoolPropertyView.swift */, E22990312D0F7678009F8D77 /* DatePropertyView.swift */, E29D312F2D03A2BD0051B7F4 /* DescriptionField.swift */, @@ -998,6 +1001,7 @@ E229904E2D13535C009F8D77 /* SecurityBookmark.swift in Sources */, E2A21C082CB17B870060935B /* TagView.swift in Sources */, E29D313D2D047C1B0051B7F4 /* LocalizedPageContentView.swift in Sources */, + E2FE0F242D2A8C21002963B7 /* TagDisplayView.swift in Sources */, E2A37D2D2CED2EF10000979F /* OptionalTextField.swift in Sources */, E2A37D2B2CED2CC30000979F /* TagDetailView.swift in Sources */, E2A21C282CB29B2A0060935B /* FeedEntryData.swift in Sources */, diff --git a/CHDataManagement/Generator/Page Generators/FeedPageGenerator.swift b/CHDataManagement/Generator/Page Generators/FeedPageGenerator.swift index 58df26a..cca95e6 100644 --- a/CHDataManagement/Generator/Page Generators/FeedPageGenerator.swift +++ b/CHDataManagement/Generator/Page Generators/FeedPageGenerator.swift @@ -58,7 +58,7 @@ final class FeedPageGenerator { header: pageHeader, additionalFooter: footer) { content in if showTitle { - content += "

\(title)

" + content += "

\(title)

" } for post in posts { content += FeedEntry(data: post).content diff --git a/CHDataManagement/Generator/Page Generators/PageGenerator.swift b/CHDataManagement/Generator/Page Generators/PageGenerator.swift index cdf311a..d151af4 100644 --- a/CHDataManagement/Generator/Page Generators/PageGenerator.swift +++ b/CHDataManagement/Generator/Page Generators/PageGenerator.swift @@ -80,7 +80,9 @@ final class PageGenerator { additionalFooter: results.requiredFooters.sorted().joined()) { content in content += "
" if !localized.hideTitle { - content += "

\(page.dateText(in: language))

" + if !page.hideDate { + content += "

\(page.dateText(in: language))

" + } content += "

\(localized.title)

" content += TagList(tags: tags).content } diff --git a/CHDataManagement/Generator/Page Generators/TagOverviewGenerator.swift b/CHDataManagement/Generator/Page Generators/TagOverviewGenerator.swift index 7215b2f..c367099 100644 --- a/CHDataManagement/Generator/Page Generators/TagOverviewGenerator.swift +++ b/CHDataManagement/Generator/Page Generators/TagOverviewGenerator.swift @@ -121,7 +121,7 @@ final class TagOverviewGenerator { let page = GenericPage( header: pageHeader, additionalFooter: "") { content in - content += "

\(header.title)

" + content += "

\(header.title)

" for tag in tags { let description = tag.localized.description ?? "" let image = self.makePageImage(item: tag.localized) diff --git a/CHDataManagement/Generator/PageGenerationResults.swift b/CHDataManagement/Generator/PageGenerationResults.swift index 92befc0..c275059 100644 --- a/CHDataManagement/Generator/PageGenerationResults.swift +++ b/CHDataManagement/Generator/PageGenerationResults.swift @@ -99,6 +99,28 @@ final class PageGenerationResults: ObservableObject { pageIsEmpty = false } + func reset() { + inaccessibleFiles = [] + unparsableFiles = [:] + missingFiles = [:] + missingLinkedFiles = [:] + missingLinkedTags = [:] + missingLinkedPages = [:] + requiredHeaders = [] + requiredFooters = [] + requiredIcons = [] + linkedPages = [] + linkedTags = [] + externalLinks = [] + usedFiles = [] + requiredFiles = [] + imagesToGenerate = [] + invalidCommands = [] + warnings = [] + unsavedOutputFiles = [:] + pageIsEmpty = false + } + // MARK: Adding entries func inaccessibleContent(file: FileResource) { diff --git a/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift b/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift index 2dc8726..1d38116 100644 --- a/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift +++ b/CHDataManagement/Generator/Post Lists/PostListPageGenerator.swift @@ -38,37 +38,40 @@ final class PostListPageGenerator { } } - private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice) { - let posts: [FeedEntryData] = posts.map { post in - let localized: LocalizedPost = post.localized(in: language) + 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") - } - - let tags: [FeedEntryData.Tag] = post.tags.filter { $0.isVisible }.map { tag in - .init(name: tag.localized(in: language).name, - url: tag.absoluteUrl(in: language)) - } - - let images = localized.images.map { image in - image.imageSet(width: mainContentMaximumWidth, height: mainContentMaximumWidth, language: language) - } - images.forEach(source.results.require) - - return FeedEntryData( - entryId: post.id, - title: localized.title, - textAboveTitle: post.dateText(in: language), - link: linkUrl, - tags: tags, - text: localized.text.components(separatedBy: "\n"), - images: images) + #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") } + // Use the tags of the page if one is linked + let tags: [FeedEntryData.Tag] = post.usedTags().filter { $0.isVisible }.map { tag in + .init(name: tag.localized(in: language).name, + url: tag.absoluteUrl(in: language)) + } + + let images = localized.images.map { image in + image.imageSet(width: mainContentMaximumWidth, height: mainContentMaximumWidth, language: language) + } + images.forEach(source.results.require) + + return FeedEntryData( + entryId: post.id, + title: localized.title, + textAboveTitle: post.dateText(in: language), + link: linkUrl, + tags: tags, + text: localized.text.components(separatedBy: "\n\n"), + images: images) + } + + private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice) { + let posts: [FeedEntryData] = posts.map(makePostData) + let feedPageGenerator = FeedPageGenerator(content: source.content, results: source.results) let languageButtonUrl = "/" + pageUrl(in: language.next, pageNumber: pageIndex) diff --git a/CHDataManagement/Main/MainView.swift b/CHDataManagement/Main/MainView.swift index ac908bd..7371b2b 100644 --- a/CHDataManagement/Main/MainView.swift +++ b/CHDataManagement/Main/MainView.swift @@ -1,29 +1,40 @@ import SwiftUI import SFSafeSymbols -#warning("Fix podcast") -#warning("Fix endeavor basics (image compare)") -#warning("Fix cap mosaic GIF") +/** + **Content** + - Podcast: Fix audio player, preview image + - Endeavor Basics: -> image compare command + - Article Cap Mosaic: -> GIF feature + - iPhone Backgrounds: Add page, html -#warning("Add custom url string to external files (optional)") -#warning("Show all warnings on page content") -#warning("Button to delete file, show in finder, replace, mark changed (-> images)") -#warning("Transfer images of posts to other language") -#warning("Show tag selection view for pages") -#warning("Button to replace files") -#warning("Replace links to files inside pages when id changes") -#warning("Calculate file sizes") -#warning("Specify image aspect ratio to prevent page jumps") -#warning("Add version and source url properties to file resources") -#warning("Show errors during loading of content") -#warning("Generate pages for posts") -#warning("Clean up mock content") -#warning("Show posts linking to a page") -#warning("Add author to settings and page headers") -#warning("Check for files in output folder not generated by app") -#warning("Fix GIFs: Don't rescale, don't use image set") -#warning("Add view to browse images") -#warning("Show warnings for empty item properties") + **UI** + - Image search: Add view to see all images and filter + - Pages: Show linking posts + - Page Content: Show all results of `PageGenerationResults` + - Files: Show usages of file + - Images: Show list of generated versions + + **Features** + - GIF Support (No image set, don't rescale) + - Image compare command `![compare](image1;image2)` + - Files: Optional Property `customFilePath` for external files to place them in another location + - Files: Property `version` and `sourceUrl` to track asset files + - Posts: Generate separate pages for posts to link to + - Settings: Introduce `Authors` (`name`, `image`, `description`) + - Page: Property `author` + - Video: Specify versions + + **Generation** + - ImageSet: Specify image aspect ratio (width, height) to prevent page jumps + - Consistency: Check output folder for unused files + - Empty properties: Show warnings for empty link previews, etc. + + **Fixes** + - Files: Id change: Check all page contents for links to the renamed file and replace occurences + - Database: Show errors during loading + - Mock content: Clean and improve + */ @main struct MainView: App { diff --git a/CHDataManagement/Model/Content+Generation.swift b/CHDataManagement/Model/Content+Generation.swift index 69bc5cb..924c730 100644 --- a/CHDataManagement/Model/Content+Generation.swift +++ b/CHDataManagement/Model/Content+Generation.swift @@ -34,8 +34,10 @@ extension Content { func check(content: String, of page: Page, for language: ContentLanguage, onComplete: @escaping (PageGenerationResults) -> Void) { performGenerationIfIdle { let results = self.results.makeResults(for: page, in: language) + results.reset() let generator = PageContentParser(content: page.content, language: language, results: results) _ = generator.generatePage(from: content) + self.results.recalculate() DispatchQueue.main.async { onComplete(results) } @@ -227,7 +229,7 @@ extension Content { for language in ContentLanguage.allCases { let results = results.makeResults(for: tag, in: language) - let posts = posts.filter { $0.tags.contains(tag) } + let posts = posts.filter { $0.contains(tag) } guard posts.count > 0 else { continue } let source = TagPageGeneratorSource( diff --git a/CHDataManagement/Model/Content+Load.swift b/CHDataManagement/Model/Content+Load.swift index 1ce07e0..c33c125 100644 --- a/CHDataManagement/Model/Content+Load.swift +++ b/CHDataManagement/Model/Content+Load.swift @@ -180,6 +180,7 @@ extension Content { externalLink: page.externalLink, isDraft: page.isDraft, createdDate: page.createdDate, + hideDate: page.hideDate ?? false, startDate: page.startDate, endDate: page.endDate, german: convert(page.german, images: files), diff --git a/CHDataManagement/Model/Content+Save.swift b/CHDataManagement/Model/Content+Save.swift index 07438f6..7116418 100644 --- a/CHDataManagement/Model/Content+Save.swift +++ b/CHDataManagement/Model/Content+Save.swift @@ -59,6 +59,7 @@ private extension Page { .init(isDraft: isDraft, externalLink: externalLink, tags: tags.map { $0.id }, + hideDate: hideDate ? true : nil, createdDate: createdDate, startDate: startDate, endDate: hasEndDate ? endDate : nil, diff --git a/CHDataManagement/Model/LocalizedTag.swift b/CHDataManagement/Model/LocalizedTag.swift index 41d22df..e1f1423 100644 --- a/CHDataManagement/Model/LocalizedTag.swift +++ b/CHDataManagement/Model/LocalizedTag.swift @@ -49,6 +49,11 @@ final class LocalizedTag: ObservableObject { content.isValidIdForTagOrPageOrPost(urlComponent) && !content.containsTag(withUrlComponent: urlComponent) } + + /// The title to display when considering multiple items of this tag + var title: String { + linkPreviewTitle ?? name + } } extension LocalizedTag: LinkPreviewItem { diff --git a/CHDataManagement/Model/Page.swift b/CHDataManagement/Model/Page.swift index 8c58b88..e9d00f3 100644 --- a/CHDataManagement/Model/Page.swift +++ b/CHDataManagement/Model/Page.swift @@ -17,6 +17,9 @@ final class Page: Item { @Published var createdDate: Date + @Published + var hideDate: Bool + @Published var startDate: Date @@ -46,6 +49,7 @@ final class Page: Item { externalLink: String?, isDraft: Bool, createdDate: Date, + hideDate: Bool, startDate: Date, endDate: Date?, german: LocalizedPage, @@ -55,6 +59,7 @@ final class Page: Item { self.externalLink = externalLink self.isDraft = isDraft self.createdDate = createdDate + self.hideDate = hideDate self.startDate = startDate self.hasEndDate = endDate != nil self.endDate = endDate ?? startDate @@ -86,6 +91,34 @@ final class Page: Item { externalLink != nil } + // MARK: Tags + + /** + Check if a tag is associated with this page + */ + func contains(_ tag: Tag) -> Bool { + tags.contains(tag) + } + + func toggle(_ tag: Tag) { + guard let index = tags.firstIndex(of: tag) else { + tags.append(tag) + return + } + tags.remove(at: index) + } + + func remove(_ tag: Tag) { + tags.remove(tag) + } + + func associate(_ tag: Tag) { + guard !tags.contains(tag) else { + return + } + tags.append(tag) + } + // MARK: Paths override func absoluteUrl(in language: ContentLanguage) -> String { diff --git a/CHDataManagement/Model/Post.swift b/CHDataManagement/Model/Post.swift index 8f75c04..289e57e 100644 --- a/CHDataManagement/Model/Post.swift +++ b/CHDataManagement/Model/Post.swift @@ -17,6 +17,12 @@ final class Post: Item { @Published var endDate: Date + /** + The tags associated with the post + + This list is only used if ``linkedPage`` is `nil`, + otherwise the tag list of the linked page is used. + */ @Published var tags: [Tag] @@ -52,6 +58,55 @@ final class Post: Item { super.init(content: content, id: id) } + // MARK: Tags + + func usedTags() -> [Tag] { + linkedPage?.tags ?? tags + } + + /** + Check if a tag is associated with this post. + + If the post is linked to a page, then the tags of the page are checked. + */ + func contains(_ tag: Tag) -> Bool { + guard let linkedPage else { + return tags.contains(tag) + } + return linkedPage.tags.contains(tag) + } + + func toggle(_ tag: Tag) { + if let linkedPage { + linkedPage.toggle(tag) + return + } + guard let index = tags.firstIndex(of: tag) else { + tags.append(tag) + return + } + tags.remove(at: index) + } + + func remove(_ tag: Tag) { + if let linkedPage { + linkedPage.remove(tag) + return + } + tags.remove(tag) + } + + func associate(_ tag: Tag) { + if let linkedPage { + linkedPage.associate(tag) + return + } + guard !tags.contains(tag) else { + return + } + tags.append(tag) + } + func localized(in language: ContentLanguage) -> LocalizedPost { switch language { case .english: return english diff --git a/CHDataManagement/Model/Tag.swift b/CHDataManagement/Model/Tag.swift index 85e36e9..b603987 100644 --- a/CHDataManagement/Model/Tag.swift +++ b/CHDataManagement/Model/Tag.swift @@ -56,7 +56,7 @@ final class Tag: Item { } override func title(in language: ContentLanguage) -> String { - localized(in: language).name + localized(in: language).title } override var itemType: ItemType { diff --git a/CHDataManagement/Preview Content/Page+Mock.swift b/CHDataManagement/Preview Content/Page+Mock.swift index af956f4..db0a4ef 100644 --- a/CHDataManagement/Preview Content/Page+Mock.swift +++ b/CHDataManagement/Preview Content/Page+Mock.swift @@ -9,6 +9,7 @@ extension Page { externalLink: nil, isDraft: true, createdDate: Date(), + hideDate: false, startDate: Date().addingTimeInterval(-86400), endDate: nil, german: .german, diff --git a/CHDataManagement/Storage/Model/PageFile.swift b/CHDataManagement/Storage/Model/PageFile.swift index 376a506..7e1eeaf 100644 --- a/CHDataManagement/Storage/Model/PageFile.swift +++ b/CHDataManagement/Storage/Model/PageFile.swift @@ -8,6 +8,8 @@ struct PageFile { let tags: [String] + let hideDate: Bool? + let createdDate: Date let startDate: Date diff --git a/CHDataManagement/Views/Generic/TagDisplayView.swift b/CHDataManagement/Views/Generic/TagDisplayView.swift new file mode 100644 index 0000000..6e39b54 --- /dev/null +++ b/CHDataManagement/Views/Generic/TagDisplayView.swift @@ -0,0 +1,42 @@ +import SwiftUI + +struct TagDisplayView: View { + + @Environment(\.language) + private var language + + @EnvironmentObject + private var content: Content + + @Binding + var tags: [Tag] + + @State + private var showTagPicker = false + + var body: some View { + FlowHStack { + ForEach(tags, id: \.id) { tag in + TagView(text: tag.localized(in: language).name) + .foregroundStyle(.white) + } + Button(action: { showTagPicker = true }) { + Image(systemSymbol: .squareAndPencilCircleFill) + .resizable() + .aspectRatio(1, contentMode: .fit) + .frame(height: 22) + .foregroundColor(Color.gray) + .background(Circle() + .fill(Color.white) + .padding(1)) + } + .buttonStyle(.plain) + } + .sheet(isPresented: $showTagPicker) { + TagSelectionView( + presented: $showTagPicker, + selected: $tags, + tags: $content.tags) + } + } +} diff --git a/CHDataManagement/Views/Pages/AddPageView.swift b/CHDataManagement/Views/Pages/AddPageView.swift index f202ede..a291c81 100644 --- a/CHDataManagement/Views/Pages/AddPageView.swift +++ b/CHDataManagement/Views/Pages/AddPageView.swift @@ -72,6 +72,7 @@ struct AddPageView: View { externalLink: nil, isDraft: true, createdDate: .now, + hideDate: false, startDate: .now, endDate: nil, german: .init(content: content, diff --git a/CHDataManagement/Views/Pages/PageContentView.swift b/CHDataManagement/Views/Pages/PageContentView.swift index 3d57c08..069827f 100644 --- a/CHDataManagement/Views/Pages/PageContentView.swift +++ b/CHDataManagement/Views/Pages/PageContentView.swift @@ -24,9 +24,6 @@ struct PageContentView: View { @EnvironmentObject private var content: Content - @State - private var showTagPicker = false - init(page: Page) { self.page = page } @@ -44,36 +41,13 @@ struct PageContentView: View { }.padding() } else { VStack(alignment: .leading) { - FlowHStack { - ForEach(page.tags, id: \.id) { tag in - TagView(text: tag.localized(in: language).name) - .foregroundStyle(.white) - } - Button(action: { showTagPicker = true }) { - Image(systemSymbol: .squareAndPencilCircleFill) - .resizable() - .aspectRatio(1, contentMode: .fit) - .frame(height: 22) - .foregroundColor(Color.gray) - .background(Circle() - .fill(Color.white) - .padding(1)) - } - .buttonStyle(.plain) - } + TagDisplayView(tags: $page.tags) LocalizedPageContentView(pageId: page.id, page: page.localized(in: language), language: language) .id(page.id + language.rawValue) - }.padding() - .sheet(isPresented: $showTagPicker) { - TagSelectionView( - presented: $showTagPicker, - selected: $page.tags, - tags: $content.tags) } + .padding() } } - - } extension PageContentView: MainContentView { diff --git a/CHDataManagement/Views/Pages/PageDetailView.swift b/CHDataManagement/Views/Pages/PageDetailView.swift index 9ecc9aa..60f48fa 100644 --- a/CHDataManagement/Views/Pages/PageDetailView.swift +++ b/CHDataManagement/Views/Pages/PageDetailView.swift @@ -67,6 +67,18 @@ struct PageDetailView: View { value: $page.isDraft, footer: "Indicate a page as a draft to hide it from the website") .disabled(page.isExternalUrl) + .onChange(of: page.externalLink) { _, newValue in + // Ensure that external pages are not drafts + if newValue != nil && page.isDraft { + page.isDraft = false + } + } + + BoolPropertyView( + title: "Hide date", + value: $page.hideDate, + footer: "Do not show the date string on the page") + .disabled(page.isExternalUrl) DatePropertyView( title: "Start date", diff --git a/CHDataManagement/Views/Posts/PostContentView.swift b/CHDataManagement/Views/Posts/PostContentView.swift index f16882e..050e71d 100644 --- a/CHDataManagement/Views/Posts/PostContentView.swift +++ b/CHDataManagement/Views/Posts/PostContentView.swift @@ -68,14 +68,21 @@ private struct LocalizedContentEditor: View { } } +private struct LinkedPageTagView: View { + + @ObservedObject + var page: Page + + var body: some View { + TagDisplayView(tags: $page.tags) + } +} + struct LocalizedPostContentView: View { @ObservedObject var post: Post - @State - private var showTagPicker = false - @Environment(\.language) private var language @@ -95,32 +102,14 @@ struct LocalizedPostContentView: View { } PostImagesView(post: post.localized(in: language)) LocalizedTitle(post: post.localized(in: language)) - FlowHStack { - ForEach(post.tags, id: \.id) { tag in - TagView(text: tag.localized(in: language).name) - .foregroundStyle(.white) - } - Button(action: { showTagPicker = true }) { - Image(systemSymbol: .squareAndPencilCircleFill) - .resizable() - .aspectRatio(1, contentMode: .fit) - .frame(height: 22) - .foregroundColor(Color.gray) - .background(Circle() - .fill(Color.white) - .padding(1)) - } - .buttonStyle(.plain) + if let page = post.linkedPage { + LinkedPageTagView(page: page) + } else { + TagDisplayView(tags: $post.tags) } LocalizedContentEditor(post: post.localized(in: language)) } .padding() - .sheet(isPresented: $showTagPicker) { - TagSelectionView( - presented: $showTagPicker, - selected: $post.tags, - tags: $content.tags) - } } private func copyImagesFromOtherLanguage() { diff --git a/CHDataManagement/Views/Posts/PostDetailView.swift b/CHDataManagement/Views/Posts/PostDetailView.swift index cac2d00..3cbf14d 100644 --- a/CHDataManagement/Views/Posts/PostDetailView.swift +++ b/CHDataManagement/Views/Posts/PostDetailView.swift @@ -76,6 +76,16 @@ struct PostDetailView: View { title: "Linked page", selectedPage: $post.linkedPage, footer: "The page to open when clicking on the post") + .onChange(of: post.linkedPage) { oldValue, newValue in + if newValue != nil { + post.tags = [] + } else { + // Link removed, so copy tags from previous link + if let oldValue { + post.tags = oldValue.tags + } + } + } LocalizedPostDetailView(post: post.localized(in: language)) } diff --git a/CHDataManagement/Views/Settings/Content/Pages/PageIssueView.swift b/CHDataManagement/Views/Settings/Content/Pages/PageIssueView.swift index c0ffb7d..dd2f198 100644 --- a/CHDataManagement/Views/Settings/Content/Pages/PageIssueView.swift +++ b/CHDataManagement/Views/Settings/Content/Pages/PageIssueView.swift @@ -223,6 +223,7 @@ struct PageIssueView: View { externalLink: nil, isDraft: true, createdDate: .now, + hideDate: false, startDate: .now, endDate: nil, german: .init(content: content, diff --git a/CHDataManagement/Views/Tags/PageTagAssignmentView.swift b/CHDataManagement/Views/Tags/PageTagAssignmentView.swift index 2a82288..a9eaa15 100644 --- a/CHDataManagement/Views/Tags/PageTagAssignmentView.swift +++ b/CHDataManagement/Views/Tags/PageTagAssignmentView.swift @@ -14,24 +14,16 @@ private struct PageSelectionView: View { var body: some View { HStack { - let isSelected = page.tags.contains(tag) + let isSelected = page.contains(tag) Image(systemSymbol: isSelected ? .checkmarkCircleFill : .circle) .foregroundStyle(Color.blue) Text(page.localized(in: language).title) } .contentShape(Rectangle()) .onTapGesture { - toggleTagAssignment() + page.toggle(tag) } } - - private func toggleTagAssignment() { - guard let index = page.tags.firstIndex(of: tag) else { - page.tags.append(tag) - return - } - page.tags.remove(at: index) - } } struct PageTagAssignmentView: View { diff --git a/CHDataManagement/Views/Tags/PostTagAssignmentView.swift b/CHDataManagement/Views/Tags/PostTagAssignmentView.swift index 1351c2a..f1f71d1 100644 --- a/CHDataManagement/Views/Tags/PostTagAssignmentView.swift +++ b/CHDataManagement/Views/Tags/PostTagAssignmentView.swift @@ -14,24 +14,16 @@ private struct PostSelectionView: View { var body: some View { HStack { - let isSelected = post.tags.contains(tag) + let isSelected = post.contains(tag) Image(systemSymbol: isSelected ? .checkmarkCircleFill : .circle) .foregroundStyle(Color.blue) Text(post.localized(in: language).title) } .contentShape(Rectangle()) .onTapGesture { - toggleTagAssignment() + post.toggle(tag) } } - - private func toggleTagAssignment() { - guard let index = post.tags.firstIndex(of: tag) else { - post.tags.append(tag) - return - } - post.tags.remove(at: index) - } } struct PostTagAssignmentView: View { diff --git a/CHDataManagement/Views/Tags/TagContentView.swift b/CHDataManagement/Views/Tags/TagContentView.swift index 41f87ab..6fa4d13 100644 --- a/CHDataManagement/Views/Tags/TagContentView.swift +++ b/CHDataManagement/Views/Tags/TagContentView.swift @@ -22,7 +22,7 @@ struct TagContentView: View { } var selectedPosts: [Post] { - content.posts.filter { $0.tags.contains(tag) } + content.posts.filter { $0.contains(tag) } } var body: some View {