Unified detail views, model

This commit is contained in:
Christoph Hagen
2024-12-16 09:54:21 +01:00
parent 1e67a99866
commit 31d1ecb8bd
57 changed files with 853 additions and 954 deletions

View File

@ -35,4 +35,11 @@ extension Content {
func tag(_ tagId: String) -> Tag? {
tags.first { $0.id == tagId }
}
func navigationBar(in language: ContentLanguage) -> [NavigationBar.Link] {
settings.navigationItems.map {
.init(text: $0.title(in: language),
url: $0.absoluteUrl(in: language))
}
}
}

View File

@ -4,6 +4,7 @@ extension Content {
private func convert(_ tag: LocalizedTagFile, images: [String : FileResource]) -> LocalizedTag {
LocalizedTag(
content: self,
urlComponent: tag.urlComponent,
name: tag.name,
subtitle: tag.subtitle,
@ -14,8 +15,9 @@ extension Content {
private func convert(_ post: LocalizedPostFile, images: [String : FileResource]) -> LocalizedPost {
LocalizedPost(
content: self,
title: post.title,
content: post.content,
text: post.content,
lastModified: post.lastModifiedDate,
images: post.images.compactMap { images[$0] },
linkPreviewImage: post.linkPreviewImage.map { images[$0] },
@ -107,8 +109,8 @@ extension Content {
let tagOverview = tagOverviewData.map { file in
TagOverviewPage(
content: self,
german: .init(file: file.german, image: file.german.linkPreviewImage.map { files[$0] }),
english: .init(file: file.english, image: file.english.linkPreviewImage.map { files[$0] }))
german: .init(content: self, file: file.german, image: file.german.linkPreviewImage.map { files[$0] }),
english: .init(content: self, file: file.english, image: file.english.linkPreviewImage.map { files[$0] }))
}
self.tags = tags.values.sorted()

View File

@ -95,7 +95,7 @@ private extension LocalizedPost {
var postFile: LocalizedPostFile {
.init(images: images.map { $0.id },
title: title.nonEmpty,
content: content,
content: text,
lastModifiedDate: lastModified,
linkPreviewImage: linkPreviewImage?.id,
linkPreviewTitle: linkPreviewTitle,

View File

@ -18,6 +18,10 @@ extension Content {
!posts.contains { $0.id == id }
}
func isNewIdForFile(_ id: String) -> Bool {
!files.contains { $0.id == id }
}
func isValidIdForTagOrPageOrPost(_ id: String) -> Bool {
id.rangeOfCharacter(from: Content.disallowedCharactersInIds) == nil
}
@ -26,6 +30,13 @@ extension Content {
id.rangeOfCharacter(from: Content.disallowedCharactersInFileIds) == nil
}
func containsPage(withUrlComponent urlComponent: String) -> Bool {
pages.contains {
$0.german.urlString == urlComponent ||
$0.english.urlString == urlComponent
}
}
func containsTag(withUrlComponent urlComponent: String) -> Bool {
(tagOverview?.contains(urlComponent: urlComponent) ?? false) ||
tags.contains { $0.contains(urlComponent: urlComponent) }

View File

@ -111,6 +111,13 @@ final class FileResource: Item {
// MARK: File
func isValid(id: String) -> Bool {
!id.isEmpty &&
content.isValidIdForFile(id) &&
content.isNewIdForFile(id)
}
@discardableResult
func update(id newId: String) -> Bool {
guard !isExternallyStored else {
id = newId

View File

@ -34,11 +34,11 @@ final class TagOverviewPage: Item {
}
private func internalPath(for language: ContentLanguage) -> String {
content.settings.paths.tagsOutputFolderPath + "/" + localized(in: language).urlString
content.settings.paths.tagsOutputFolderPath + "/" + localized(in: language).urlComponent
}
func contains(urlComponent: String) -> Bool {
english.urlString == urlComponent || german.urlString == urlComponent
english.urlComponent == urlComponent || german.urlComponent == urlComponent
}
var file: TagOverviewFile {
@ -53,16 +53,16 @@ extension TagOverviewPage: LocalizedItem {
final class LocalizedTagOverviewPage: ObservableObject {
unowned let content: Content
@Published
var title: String
/**
The string to use when creating the url for the page.
Defaults to ``id`` if unset.
*/
@Published
var urlString: String
var urlComponent: String
@Published
var linkPreviewImage: FileResource?
@ -73,27 +73,35 @@ final class LocalizedTagOverviewPage: ObservableObject {
@Published
var linkPreviewDescription: String?
init(title: String, urlString: String, linkPreviewImage: FileResource? = nil, linkPreviewTitle: String? = nil, linkPreviewDescription: String? = nil) {
init(content: Content, title: String, urlString: String, linkPreviewImage: FileResource? = nil, linkPreviewTitle: String? = nil, linkPreviewDescription: String? = nil) {
self.content = content
self.title = title
self.urlString = urlString
self.urlComponent = urlString
self.linkPreviewImage = linkPreviewImage
self.linkPreviewTitle = linkPreviewTitle
self.linkPreviewDescription = linkPreviewDescription
}
init(file: LocalizedTagOverviewFile, image: FileResource?) {
init(content: Content, file: LocalizedTagOverviewFile, image: FileResource?) {
self.content = content
self.title = file.title
self.urlString = file.url
self.urlComponent = file.url
self.linkPreviewImage = image
self.linkPreviewTitle = file.linkPreviewTitle
self.linkPreviewDescription = file.linkPreviewDescription
}
var file: LocalizedTagOverviewFile {
.init(url: urlString,
.init(url: urlComponent,
title: title,
linkPreviewImage: linkPreviewImage?.id,
linkPreviewTitle: linkPreviewTitle,
linkPreviewDescription: linkPreviewDescription)
}
func isValid(urlComponent: String) -> Bool {
!urlComponent.isEmpty &&
content.isValidIdForTagOrPageOrPost(urlComponent) &&
!content.containsTag(withUrlComponent: urlComponent)
}
}

View File

@ -89,4 +89,9 @@ final class LocalizedPage: ObservableObject {
self.linkPreviewTitle = linkPreviewTitle
self.linkPreviewDescription = linkPreviewDescription
}
func isValid(urlComponent: String) -> Bool {
content.isValidIdForTagOrPageOrPost(urlComponent) &&
!content.containsPage(withUrlComponent: urlComponent)
}
}

View File

@ -3,11 +3,13 @@ import SwiftUI
final class LocalizedPost: ObservableObject {
unowned let content: Content
@Published
var title: String
@Published
var content: String
var text: String
@Published
var lastModified: Date?
@ -24,15 +26,17 @@ final class LocalizedPost: ObservableObject {
@Published
var linkPreviewDescription: String?
init(title: String? = nil,
content: String,
init(content: Content,
title: String? = nil,
text: String,
lastModified: Date? = nil,
images: [FileResource] = [],
linkPreviewImage: FileResource? = nil,
linkPreviewTitle: String? = nil,
linkPreviewDescription: String? = nil) {
self.title = title ?? ""
self.content = content
self.title = title ?? ""
self.text = text
self.lastModified = lastModified
self.images = images
self.linkPreviewImage = linkPreviewImage

View File

@ -2,6 +2,8 @@ import Foundation
final class LocalizedTag: ObservableObject {
unowned let content: Content
@Published
var urlComponent: String
@ -22,12 +24,14 @@ final class LocalizedTag: ObservableObject {
/// The original url in the previous site layout
let originalUrl: String?
init(urlComponent: String,
init(content: Content,
urlComponent: String,
name: String,
subtitle: String? = nil,
description: String? = nil,
thumbnail: FileResource? = nil,
originalUrl: String? = nil) {
self.content = content
self.urlComponent = urlComponent
self.name = name
self.subtitle = subtitle
@ -35,4 +39,9 @@ final class LocalizedTag: ObservableObject {
self.linkPreviewImage = thumbnail
self.originalUrl = originalUrl
}
func isValid(urlComponent: String) -> Bool {
content.isValidIdForTagOrPageOrPost(urlComponent) &&
!content.containsTag(withUrlComponent: urlComponent)
}
}

View File

@ -66,9 +66,16 @@ final class Page: Item {
super.init(content: content, id: id)
}
func isValid(id: String) -> Bool {
!id.isEmpty &&
content.isValidIdForTagOrPageOrPost(id) &&
content.isNewIdForPage(id)
}
@discardableResult
func update(id newId: String) -> Bool {
guard content.storage.move(page: id, to: newId) else {
print("Failed to move file of page \(id)")
print("Failed to move files of page \(id)")
return false
}
id = newId

View File

@ -65,6 +65,13 @@ final class Post: ObservableObject {
}
}
func isValid(id: String) -> Bool {
!id.isEmpty &&
content.isValidIdForTagOrPageOrPost(id) &&
content.isNewIdForPost(id)
}
@discardableResult
func update(id newId: String) -> Bool {
do {
try content.storage.move(post: id, to: newId)

View File

@ -13,9 +13,9 @@ final class Tag: Item {
override init(content: Content, id: String) {
self.isVisible = true
self.english = .init(urlComponent: id, name: id)
self.english = .init(content: content, urlComponent: id, name: id)
let deId = id + "-" + ContentLanguage.german.rawValue
self.german = .init(urlComponent: deId, name: deId)
self.german = .init(content: content, urlComponent: deId, name: deId)
super.init(content: content, id: id)
}