Add tag overview, improve assets
This commit is contained in:
@ -51,6 +51,7 @@ extension Content {
|
||||
let postsData = try storage.loadAllPosts()
|
||||
let fileList = try storage.loadAllFiles()
|
||||
let externalFiles = try storage.loadExternalFileList()
|
||||
let tagOverviewData = try storage.loadTagOverview()
|
||||
|
||||
var files: [String : FileResource] = fileList.reduce(into: [:]) { files, fileId in
|
||||
let descriptions = imageDescriptions[fileId]
|
||||
@ -77,6 +78,7 @@ extension Content {
|
||||
let tags = tagData.reduce(into: [:]) { (tags, data) in
|
||||
tags[data.key] = Tag(
|
||||
content: self,
|
||||
id: data.value.id,
|
||||
isVisible: data.value.isVisible,
|
||||
german: convert(data.value.german, images: images),
|
||||
english: convert(data.value.english, images: images))
|
||||
@ -102,28 +104,46 @@ extension Content {
|
||||
linkedPage: linkedPage)
|
||||
}
|
||||
|
||||
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] }))
|
||||
}
|
||||
|
||||
self.tags = tags.values.sorted()
|
||||
self.pages = pages.values.sorted(ascending: false) { $0.startDate }
|
||||
self.files = files.values.sorted { $0.id }
|
||||
self.posts = posts.sorted(ascending: false) { $0.startDate }
|
||||
self.settings = makeSettings(settings, tags: tags)
|
||||
self.tagOverview = tagOverview
|
||||
self.settings = makeSettings(settings, tags: tags, pages: pages, files: files)
|
||||
}
|
||||
|
||||
private func makeSettings(_ settings: SettingsFile, tags: [String : Tag]) -> Settings {
|
||||
private func makeSettings(_ settings: SettingsFile, tags: [String : Tag], pages: [String : Page], files: [String : FileResource]) -> Settings {
|
||||
|
||||
let navigationTags = settings.navigationTags.map { tags[$0]! }
|
||||
#warning("Notify about missing links")
|
||||
let navigationItems: [Item] = settings.navigationItems.compactMap {
|
||||
switch $0.type {
|
||||
case .tag:
|
||||
return tags[$0.id]
|
||||
case .page:
|
||||
return pages[$0.id]
|
||||
case .tagOverview:
|
||||
return tagOverview
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let posts = PostSettings(
|
||||
postsPerPage: settings.posts.postsPerPage,
|
||||
contentWidth: settings.posts.contentWidth)
|
||||
let posts = PostSettings(file: settings.posts, files: files)
|
||||
|
||||
let pages = PageSettings(file: settings.pages)
|
||||
let pages = PageSettings(file: settings.pages, files: files)
|
||||
|
||||
let paths = PathSettings(file: settings.paths)
|
||||
|
||||
return Settings(
|
||||
paths: paths,
|
||||
navigationTags: navigationTags,
|
||||
navigationItems: navigationItems,
|
||||
posts: posts,
|
||||
pages: pages,
|
||||
german: .init(file: settings.german),
|
||||
|
@ -18,16 +18,17 @@ extension Content {
|
||||
try storage.save(settings: settings.file)
|
||||
|
||||
let fileDescriptions: [FileDescriptions] = files.sorted().compactMap { file in
|
||||
guard !file.englishDescription.isEmpty || !file.germanDescription.isEmpty else {
|
||||
guard !file.english.isEmpty || !file.german.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return FileDescriptions(
|
||||
fileId: file.id,
|
||||
german: file.germanDescription.nonEmpty,
|
||||
english: file.englishDescription.nonEmpty)
|
||||
german: file.german.nonEmpty,
|
||||
english: file.english.nonEmpty)
|
||||
}
|
||||
|
||||
try storage.save(fileDescriptions: fileDescriptions)
|
||||
try storage.save(tagOverview: tagOverview?.file)
|
||||
|
||||
let externalFileList = files.filter { $0.isExternallyStored }.map { $0.id }
|
||||
try storage.save(externalFileList: externalFileList)
|
||||
@ -130,7 +131,7 @@ extension Settings {
|
||||
var file: SettingsFile {
|
||||
.init(
|
||||
paths: paths.file,
|
||||
navigationTags: navigationTags.map { $0.id },
|
||||
navigationItems: navigationItems.map { .init(type: $0.itemType, id: $0.id) },
|
||||
posts: posts.file,
|
||||
pages: pages.file,
|
||||
german: german.file,
|
||||
@ -138,34 +139,14 @@ extension Settings {
|
||||
}
|
||||
}
|
||||
|
||||
private extension PathSettings {
|
||||
|
||||
var file: PathSettingsFile {
|
||||
.init(outputDirectoryPath: outputDirectoryPath,
|
||||
pagesOutputFolderPath: pagesOutputFolderPath,
|
||||
imagesOutputFolderPath: imagesOutputFolderPath,
|
||||
filesOutputFolderPath: filesOutputFolderPath,
|
||||
videosOutputFolderPath: videosOutputFolderPath,
|
||||
tagsOutputFolderPath: tagsOutputFolderPath)
|
||||
}
|
||||
}
|
||||
|
||||
private extension PostSettings {
|
||||
|
||||
var file: PostSettingsFile {
|
||||
.init(postsPerPage: postsPerPage,
|
||||
contentWidth: contentWidth)
|
||||
}
|
||||
}
|
||||
|
||||
private extension PageSettings {
|
||||
|
||||
var file: PageSettingsFile {
|
||||
.init(pageUrlPrefix: pageUrlPrefix,
|
||||
contentWidth: contentWidth,
|
||||
largeImageWidth: largeImageWidth,
|
||||
pageLinkImageSize: pageLinkImageSize,
|
||||
javascriptFilesPath: javascriptFilesPath)
|
||||
swiperCssFile: swiperCssFile?.id,
|
||||
swiperJsFile: swiperJsFile?.id,
|
||||
defaultCssFile: defaultCssFile?.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,16 @@ extension Content {
|
||||
!posts.contains { $0.id == id }
|
||||
}
|
||||
|
||||
func isValidIdForTagOrTagOrPost(_ id: String) -> Bool {
|
||||
func isValidIdForTagOrPageOrPost(_ id: String) -> Bool {
|
||||
id.rangeOfCharacter(from: Content.disallowedCharactersInIds) == nil
|
||||
}
|
||||
|
||||
func isValidIdForFile(_ id: String) -> Bool {
|
||||
id.rangeOfCharacter(from: Content.disallowedCharactersInFileIds) == nil
|
||||
}
|
||||
|
||||
func containsTag(withUrlComponent urlComponent: String) -> Bool {
|
||||
(tagOverview?.contains(urlComponent: urlComponent) ?? false) ||
|
||||
tags.contains { $0.contains(urlComponent: urlComponent) }
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,12 @@ final class Content: ObservableObject {
|
||||
@Published
|
||||
var files: [FileResource]
|
||||
|
||||
@Published
|
||||
var tagOverview: TagOverviewPage?
|
||||
|
||||
@Published
|
||||
var results: [ItemId : PageGenerationResults] = [:]
|
||||
|
||||
@AppStorage("contentPath")
|
||||
private var storedContentPath: String = ""
|
||||
|
||||
@ -38,12 +44,14 @@ final class Content: ObservableObject {
|
||||
pages: [Page],
|
||||
tags: [Tag],
|
||||
files: [FileResource],
|
||||
tagOverview: TagOverviewPage?,
|
||||
storedContentPath: String) {
|
||||
self.settings = settings
|
||||
self.posts = posts
|
||||
self.pages = pages
|
||||
self.tags = tags
|
||||
self.files = files
|
||||
self.tagOverview = tagOverview
|
||||
self.storedContentPath = storedContentPath
|
||||
self.contentPath = storedContentPath
|
||||
self.storage = Storage(baseFolder: URL(filePath: storedContentPath))
|
||||
@ -64,6 +72,7 @@ final class Content: ObservableObject {
|
||||
self.pages = []
|
||||
self.tags = []
|
||||
self.files = []
|
||||
self.tagOverview = nil
|
||||
|
||||
contentPath = storedContentPath
|
||||
do {
|
||||
|
@ -14,3 +14,21 @@ extension ContentLanguage: Codable {
|
||||
extension ContentLanguage: CaseIterable {
|
||||
|
||||
}
|
||||
|
||||
extension ContentLanguage: Hashable {
|
||||
|
||||
}
|
||||
|
||||
extension ContentLanguage: Identifiable {
|
||||
|
||||
var id: String {
|
||||
rawValue
|
||||
}
|
||||
}
|
||||
|
||||
extension ContentLanguage: Comparable {
|
||||
|
||||
static func < (lhs: ContentLanguage, rhs: ContentLanguage) -> Bool {
|
||||
lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
}
|
||||
|
@ -5,29 +5,24 @@ final class FileResource: Item {
|
||||
|
||||
let type: FileType
|
||||
|
||||
/// Globally unique id
|
||||
@Published
|
||||
var id: String
|
||||
|
||||
@Published
|
||||
var isExternallyStored: Bool
|
||||
|
||||
@Published
|
||||
var germanDescription: String
|
||||
var german: String
|
||||
|
||||
@Published
|
||||
var englishDescription: String
|
||||
var english: String
|
||||
|
||||
@Published
|
||||
var size: CGSize = .zero
|
||||
|
||||
init(content: Content, id: String, isExternallyStored: Bool, en: String, de: String) {
|
||||
self.id = id
|
||||
self.type = FileType(fileExtension: id.fileExtension)
|
||||
self.englishDescription = en
|
||||
self.germanDescription = de
|
||||
self.english = en
|
||||
self.german = de
|
||||
self.isExternallyStored = isExternallyStored
|
||||
super.init(content: content)
|
||||
super.init(content: content, id: id)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,18 +30,10 @@ final class FileResource: Item {
|
||||
*/
|
||||
init(resourceImage: String, type: ImageFileType) {
|
||||
self.type = .image(type)
|
||||
self.id = resourceImage
|
||||
self.englishDescription = "A test image included in the bundle"
|
||||
self.germanDescription = "Ein Testbild aus dem Bundle"
|
||||
self.english = "A test image included in the bundle"
|
||||
self.german = "Ein Testbild aus dem Bundle"
|
||||
self.isExternallyStored = true
|
||||
super.init(content: .mock) // TODO: Add images to mock
|
||||
}
|
||||
|
||||
func getDescription(for language: ContentLanguage) -> String {
|
||||
switch language {
|
||||
case .english: return englishDescription
|
||||
case .german: return germanDescription
|
||||
}
|
||||
super.init(content: .mock, id: resourceImage) // TODO: Add images to mock
|
||||
}
|
||||
|
||||
// MARK: Text
|
||||
@ -108,6 +95,11 @@ final class FileResource: Item {
|
||||
return makeCleanAbsolutePath(path)
|
||||
}
|
||||
|
||||
var assetUrl: String {
|
||||
let path = content.settings.paths.assetsOutputFolderPath + "/" + id
|
||||
return makeCleanAbsolutePath(path)
|
||||
}
|
||||
|
||||
|
||||
private var pathPrefix: String {
|
||||
switch type {
|
||||
@ -135,27 +127,6 @@ final class FileResource: Item {
|
||||
}
|
||||
}
|
||||
|
||||
extension FileResource: Identifiable {
|
||||
extension FileResource: LocalizedItem {
|
||||
|
||||
}
|
||||
|
||||
extension FileResource: Equatable {
|
||||
|
||||
static func == (lhs: FileResource, rhs: FileResource) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
extension FileResource: Hashable {
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
||||
extension FileResource: Comparable {
|
||||
|
||||
static func < (lhs: FileResource, rhs: FileResource) -> Bool {
|
||||
lhs.id < rhs.id
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
class Item: ObservableObject {
|
||||
|
||||
unowned let content: Content
|
||||
|
||||
init(content: Content) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
func makeCleanAbsolutePath(_ path: String) -> String {
|
||||
"/" + makeCleanRelativePath(path)
|
||||
}
|
||||
|
||||
func makeCleanRelativePath(_ path: String) -> String {
|
||||
path.components(separatedBy: "/").filter { !$0.isEmpty }.joined(separator: "/")
|
||||
}
|
||||
}
|
55
CHDataManagement/Model/Item/Item.swift
Normal file
55
CHDataManagement/Model/Item/Item.swift
Normal file
@ -0,0 +1,55 @@
|
||||
import Foundation
|
||||
|
||||
class Item: ObservableObject, Identifiable {
|
||||
|
||||
unowned let content: Content
|
||||
|
||||
@Published
|
||||
var id: String
|
||||
|
||||
init(content: Content, id: String) {
|
||||
self.content = content
|
||||
self.id = id
|
||||
}
|
||||
|
||||
func makeCleanAbsolutePath(_ path: String) -> String {
|
||||
"/" + makeCleanRelativePath(path)
|
||||
}
|
||||
|
||||
func makeCleanRelativePath(_ path: String) -> String {
|
||||
path.components(separatedBy: "/").filter { !$0.isEmpty }.joined(separator: "/")
|
||||
}
|
||||
|
||||
func title(in language: ContentLanguage) -> String {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func absoluteUrl(in language: ContentLanguage) -> String {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
var itemType: ItemType {
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
extension Item: Equatable {
|
||||
|
||||
static func == (lhs: Item, rhs: Item) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
extension Item: Hashable {
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
||||
extension Item: Comparable {
|
||||
|
||||
static func < (lhs: Item, rhs: Item) -> Bool {
|
||||
lhs.id < rhs.id
|
||||
}
|
||||
}
|
38
CHDataManagement/Model/Item/ItemId.swift
Normal file
38
CHDataManagement/Model/Item/ItemId.swift
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
struct ItemId {
|
||||
|
||||
let itemId: String
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
let itemType: ItemType
|
||||
}
|
||||
|
||||
extension ItemId: Equatable {
|
||||
|
||||
static func == (lhs: ItemId, rhs: ItemId) -> Bool {
|
||||
lhs.itemId == rhs.itemId && lhs.language == rhs.language && lhs.itemType == rhs.itemType
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemId: Hashable {
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(itemId)
|
||||
hasher.combine(language)
|
||||
hasher.combine(itemType)
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemId: Comparable {
|
||||
|
||||
static func < (lhs: ItemId, rhs: ItemId) -> Bool {
|
||||
guard lhs.itemType == rhs.itemType else {
|
||||
return lhs.itemType < rhs.itemType
|
||||
}
|
||||
guard lhs.itemId == rhs.itemId else {
|
||||
return lhs.itemId < rhs.itemId
|
||||
}
|
||||
return lhs.language < rhs.language
|
||||
}
|
||||
}
|
35
CHDataManagement/Model/Item/ItemType.swift
Normal file
35
CHDataManagement/Model/Item/ItemType.swift
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
enum ItemType: String, Codable {
|
||||
|
||||
case post
|
||||
|
||||
case tag
|
||||
|
||||
case page
|
||||
|
||||
case tagOverview
|
||||
|
||||
case file
|
||||
}
|
||||
|
||||
extension ItemType: Equatable {
|
||||
|
||||
}
|
||||
|
||||
extension ItemType: Hashable {
|
||||
|
||||
}
|
||||
|
||||
extension ItemType: Identifiable {
|
||||
|
||||
var id: String {
|
||||
rawValue
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemType: Comparable {
|
||||
|
||||
static func < (lhs: ItemType, rhs: ItemType) -> Bool {
|
||||
lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
}
|
19
CHDataManagement/Model/Item/LocalizedItem.swift
Normal file
19
CHDataManagement/Model/Item/LocalizedItem.swift
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
protocol LocalizedItem {
|
||||
|
||||
associatedtype Localized
|
||||
|
||||
var german: Localized { get }
|
||||
|
||||
var english: Localized { get }
|
||||
}
|
||||
|
||||
extension LocalizedItem {
|
||||
|
||||
func localized(in language: ContentLanguage) -> Localized {
|
||||
switch language {
|
||||
case .german: return german
|
||||
case .english: return english
|
||||
}
|
||||
}
|
||||
}
|
99
CHDataManagement/Model/Item/TagOverviewPage.swift
Normal file
99
CHDataManagement/Model/Item/TagOverviewPage.swift
Normal file
@ -0,0 +1,99 @@
|
||||
import Foundation
|
||||
|
||||
final class TagOverviewPage: Item {
|
||||
|
||||
static let id = "all-tags"
|
||||
|
||||
@Published
|
||||
var german: LocalizedTagOverviewPage
|
||||
|
||||
@Published
|
||||
var english: LocalizedTagOverviewPage
|
||||
|
||||
|
||||
init(content: Content, german: LocalizedTagOverviewPage, english: LocalizedTagOverviewPage) {
|
||||
self.german = german
|
||||
self.english = english
|
||||
super.init(content: content, id: TagOverviewPage.id)
|
||||
}
|
||||
|
||||
override var itemType: ItemType {
|
||||
.tagOverview
|
||||
}
|
||||
|
||||
override func title(in language: ContentLanguage) -> String {
|
||||
localized(in: language).title
|
||||
}
|
||||
|
||||
override func absoluteUrl(in language: ContentLanguage) -> String {
|
||||
makeCleanAbsolutePath(internalPath(for: language))
|
||||
}
|
||||
|
||||
func filePathRelativeToOutputFolder(for language: ContentLanguage) -> String {
|
||||
makeCleanRelativePath(internalPath(for: language))
|
||||
}
|
||||
|
||||
private func internalPath(for language: ContentLanguage) -> String {
|
||||
content.settings.paths.tagsOutputFolderPath + "/" + localized(in: language).urlString
|
||||
}
|
||||
|
||||
func contains(urlComponent: String) -> Bool {
|
||||
english.urlString == urlComponent || german.urlString == urlComponent
|
||||
}
|
||||
|
||||
var file: TagOverviewFile {
|
||||
.init(german: german.file,
|
||||
english: english.file)
|
||||
}
|
||||
}
|
||||
|
||||
extension TagOverviewPage: LocalizedItem {
|
||||
|
||||
}
|
||||
|
||||
final class LocalizedTagOverviewPage: ObservableObject {
|
||||
|
||||
@Published
|
||||
var title: String
|
||||
|
||||
/**
|
||||
The string to use when creating the url for the page.
|
||||
|
||||
Defaults to ``id`` if unset.
|
||||
*/
|
||||
@Published
|
||||
var urlString: String
|
||||
|
||||
@Published
|
||||
var linkPreviewImage: FileResource?
|
||||
|
||||
@Published
|
||||
var linkPreviewTitle: String?
|
||||
|
||||
@Published
|
||||
var linkPreviewDescription: String?
|
||||
|
||||
init(title: String, urlString: String, linkPreviewImage: FileResource? = nil, linkPreviewTitle: String? = nil, linkPreviewDescription: String? = nil) {
|
||||
self.title = title
|
||||
self.urlString = urlString
|
||||
self.linkPreviewImage = linkPreviewImage
|
||||
self.linkPreviewTitle = linkPreviewTitle
|
||||
self.linkPreviewDescription = linkPreviewDescription
|
||||
}
|
||||
|
||||
init(file: LocalizedTagOverviewFile, image: FileResource?) {
|
||||
self.title = file.title
|
||||
self.urlString = file.url
|
||||
self.linkPreviewImage = image
|
||||
self.linkPreviewTitle = file.linkPreviewTitle
|
||||
self.linkPreviewDescription = file.linkPreviewDescription
|
||||
}
|
||||
|
||||
var file: LocalizedTagOverviewFile {
|
||||
.init(url: urlString,
|
||||
title: title,
|
||||
linkPreviewImage: linkPreviewImage?.id,
|
||||
linkPreviewTitle: linkPreviewTitle,
|
||||
linkPreviewDescription: linkPreviewDescription)
|
||||
}
|
||||
}
|
@ -2,12 +2,6 @@ import Foundation
|
||||
|
||||
final class Page: Item {
|
||||
|
||||
/**
|
||||
The unique id of the entry
|
||||
*/
|
||||
@Published
|
||||
var id: String
|
||||
|
||||
/**
|
||||
The external link this page points to.
|
||||
|
||||
@ -59,7 +53,6 @@ final class Page: Item {
|
||||
german: LocalizedPage,
|
||||
english: LocalizedPage,
|
||||
tags: [Tag]) {
|
||||
self.id = id
|
||||
self.externalLink = externalLink
|
||||
self.isDraft = isDraft
|
||||
self.createdDate = createdDate
|
||||
@ -70,14 +63,7 @@ final class Page: Item {
|
||||
self.english = english
|
||||
self.tags = tags
|
||||
|
||||
super.init(content: content)
|
||||
}
|
||||
|
||||
func localized(in language: ContentLanguage) -> LocalizedPage {
|
||||
switch language {
|
||||
case .german: return german
|
||||
case .english: return english
|
||||
}
|
||||
super.init(content: content, id: id)
|
||||
}
|
||||
|
||||
func update(id newId: String) -> Bool {
|
||||
@ -95,7 +81,7 @@ final class Page: Item {
|
||||
|
||||
// MARK: Paths
|
||||
|
||||
func absoluteUrl(for language: ContentLanguage) -> String {
|
||||
override func absoluteUrl(in language: ContentLanguage) -> String {
|
||||
if let url = externalLink {
|
||||
return url
|
||||
}
|
||||
@ -103,40 +89,31 @@ final class Page: Item {
|
||||
return makeCleanAbsolutePath(internalPath(for: language))
|
||||
}
|
||||
|
||||
override func title(in language: ContentLanguage) -> String {
|
||||
localized(in: language).title
|
||||
}
|
||||
|
||||
func filePathRelativeToOutputFolder(for language: ContentLanguage) -> String {
|
||||
makeCleanRelativePath(internalPath(for: language))
|
||||
}
|
||||
|
||||
private func internalPath(for language: ContentLanguage) -> String {
|
||||
content.settings.pages.pageUrlPrefix + "/" + localized(in: language).urlString
|
||||
content.settings.paths.pagesOutputFolderPath + "/" + localized(in: language).urlString
|
||||
}
|
||||
}
|
||||
|
||||
extension Page: Identifiable {
|
||||
|
||||
}
|
||||
|
||||
extension Page: Equatable {
|
||||
|
||||
static func == (lhs: Page, rhs: Page) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
override var itemType: ItemType {
|
||||
.page
|
||||
}
|
||||
}
|
||||
|
||||
extension Page: Hashable {
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
||||
extension Page: Comparable {
|
||||
|
||||
static func < (lhs: Page, rhs: Page) -> Bool {
|
||||
lhs.id < rhs.id
|
||||
func contains(urlComponent: String) -> Bool {
|
||||
english.urlString == urlComponent || german.urlString == urlComponent
|
||||
}
|
||||
}
|
||||
|
||||
extension Page: DateItem {
|
||||
|
||||
}
|
||||
|
||||
extension Page: LocalizedItem {
|
||||
|
||||
}
|
||||
|
@ -2,11 +2,6 @@ import Foundation
|
||||
|
||||
final class PageSettings: ObservableObject {
|
||||
|
||||
/// The prefix of the urls for all pages
|
||||
/// The full path will be `<pagePrefix>/<page-url-component>`
|
||||
@Published
|
||||
var pageUrlPrefix: String
|
||||
|
||||
@Published
|
||||
var contentWidth: Int
|
||||
|
||||
@ -17,13 +12,39 @@ final class PageSettings: ObservableObject {
|
||||
var pageLinkImageSize: Int
|
||||
|
||||
@Published
|
||||
var javascriptFilesPath: String
|
||||
var defaultCssFile: FileResource?
|
||||
|
||||
init(file: PageSettingsFile) {
|
||||
self.pageUrlPrefix = file.pageUrlPrefix
|
||||
@Published
|
||||
var codeHighlightingJsFile: FileResource?
|
||||
|
||||
@Published
|
||||
var audioPlayerJsFile: FileResource?
|
||||
|
||||
@Published
|
||||
var audioPlayerCssFile: FileResource?
|
||||
|
||||
@Published
|
||||
var modelViewerJsFile: FileResource?
|
||||
|
||||
init(file: PageSettingsFile, files: [String : FileResource]) {
|
||||
self.contentWidth = file.contentWidth
|
||||
self.largeImageWidth = file.largeImageWidth
|
||||
self.pageLinkImageSize = file.pageLinkImageSize
|
||||
self.javascriptFilesPath = file.javascriptFilesPath
|
||||
self.defaultCssFile = file.defaultCssFile.map { files[$0] }
|
||||
self.codeHighlightingJsFile = file.codeHighlightingJsFile.map { files[$0] }
|
||||
self.audioPlayerJsFile = file.audioPlayerJsFile.map { files[$0] }
|
||||
self.audioPlayerCssFile = file.audioPlayerCssFile.map { files[$0] }
|
||||
self.modelViewerJsFile = file.modelViewerJsFile.map { files[$0] }
|
||||
}
|
||||
|
||||
var file: PageSettingsFile {
|
||||
.init(contentWidth: contentWidth,
|
||||
largeImageWidth: largeImageWidth,
|
||||
pageLinkImageSize: pageLinkImageSize,
|
||||
defaultCssFile: defaultCssFile?.id,
|
||||
codeHighlightingJsFile: codeHighlightingJsFile?.id,
|
||||
audioPlayerJsFile: audioPlayerJsFile?.id,
|
||||
audioPlayerCssFile: audioPlayerJsFile?.id,
|
||||
modelViewerJsFile: modelViewerJsFile?.id)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ final class PathSettings: ObservableObject {
|
||||
@Published
|
||||
var outputDirectoryPath: String
|
||||
|
||||
@Published
|
||||
var assetsOutputFolderPath: String
|
||||
|
||||
@Published
|
||||
var pagesOutputFolderPath: String
|
||||
|
||||
@ -21,6 +24,7 @@ final class PathSettings: ObservableObject {
|
||||
var tagsOutputFolderPath: String
|
||||
|
||||
init(file: PathSettingsFile) {
|
||||
self.assetsOutputFolderPath = file.assetsOutputFolderPath
|
||||
self.outputDirectoryPath = file.outputDirectoryPath
|
||||
self.pagesOutputFolderPath = file.pagesOutputFolderPath
|
||||
self.imagesOutputFolderPath = file.imagesOutputFolderPath
|
||||
@ -28,4 +32,14 @@ final class PathSettings: ObservableObject {
|
||||
self.videosOutputFolderPath = file.videosOutputFolderPath
|
||||
self.tagsOutputFolderPath = file.tagsOutputFolderPath
|
||||
}
|
||||
|
||||
var file: PathSettingsFile {
|
||||
.init(outputDirectoryPath: outputDirectoryPath,
|
||||
assetsOutputFolderPath: assetsOutputFolderPath,
|
||||
pagesOutputFolderPath: pagesOutputFolderPath,
|
||||
imagesOutputFolderPath: imagesOutputFolderPath,
|
||||
filesOutputFolderPath: filesOutputFolderPath,
|
||||
videosOutputFolderPath: videosOutputFolderPath,
|
||||
tagsOutputFolderPath: tagsOutputFolderPath)
|
||||
}
|
||||
}
|
||||
|
@ -10,13 +10,32 @@ final class PostSettings: ObservableObject {
|
||||
@Published
|
||||
var contentWidth: Int
|
||||
|
||||
init(postsPerPage: Int, contentWidth: Int) {
|
||||
@Published
|
||||
var swiperCssFile: FileResource?
|
||||
|
||||
@Published
|
||||
var swiperJsFile: FileResource?
|
||||
|
||||
@Published
|
||||
var defaultCssFile: FileResource?
|
||||
|
||||
init(postsPerPage: Int,
|
||||
contentWidth: Int,
|
||||
swiperCssFile: FileResource?,
|
||||
swiperJsFile: FileResource?,
|
||||
defaultCssFile: FileResource?) {
|
||||
self.postsPerPage = postsPerPage
|
||||
self.contentWidth = contentWidth
|
||||
self.swiperCssFile = swiperCssFile
|
||||
self.swiperJsFile = swiperJsFile
|
||||
self.defaultCssFile = defaultCssFile
|
||||
}
|
||||
|
||||
init(file: PostSettingsFile) {
|
||||
init(file: PostSettingsFile, files: [String : FileResource]) {
|
||||
self.postsPerPage = file.postsPerPage
|
||||
self.contentWidth = file.contentWidth
|
||||
self.swiperCssFile = file.swiperCssFile.map { files[$0] }
|
||||
self.swiperJsFile = file.swiperJsFile.map { files[$0] }
|
||||
self.defaultCssFile = file.defaultCssFile.map { files[$0] }
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ final class Settings: ObservableObject {
|
||||
@Published
|
||||
var paths: PathSettings
|
||||
|
||||
/// The tags to show in the navigation bar
|
||||
/// The items to show in the navigation bar
|
||||
@Published
|
||||
var navigationTags: [Tag]
|
||||
var navigationItems: [Item]
|
||||
|
||||
@Published
|
||||
var posts: PostSettings
|
||||
@ -21,9 +21,9 @@ final class Settings: ObservableObject {
|
||||
@Published
|
||||
var english: LocalizedPostSettings
|
||||
|
||||
init(paths: PathSettings, navigationTags: [Tag], posts: PostSettings, pages: PageSettings, german: LocalizedPostSettings, english: LocalizedPostSettings) {
|
||||
init(paths: PathSettings, navigationItems: [Item], posts: PostSettings, pages: PageSettings, german: LocalizedPostSettings, english: LocalizedPostSettings) {
|
||||
self.paths = paths
|
||||
self.navigationTags = navigationTags
|
||||
self.navigationItems = navigationItems
|
||||
self.posts = posts
|
||||
self.pages = pages
|
||||
self.german = german
|
||||
|
@ -2,10 +2,6 @@ import Foundation
|
||||
|
||||
final class Tag: Item {
|
||||
|
||||
var id: String {
|
||||
english.urlComponent
|
||||
}
|
||||
|
||||
@Published
|
||||
var isVisible: Bool
|
||||
|
||||
@ -15,19 +11,19 @@ final class Tag: Item {
|
||||
@Published
|
||||
var english: LocalizedTag
|
||||
|
||||
init(content: Content, id: String) {
|
||||
override init(content: Content, id: String) {
|
||||
self.isVisible = true
|
||||
self.english = .init(urlComponent: id, name: id)
|
||||
let deId = id + "-" + ContentLanguage.german.rawValue
|
||||
self.german = .init(urlComponent: deId, name: deId)
|
||||
super.init(content: content)
|
||||
super.init(content: content, id: id)
|
||||
}
|
||||
|
||||
init(content: Content, isVisible: Bool = true, german: LocalizedTag, english: LocalizedTag) {
|
||||
init(content: Content, id: String, isVisible: Bool = true, german: LocalizedTag, english: LocalizedTag) {
|
||||
self.isVisible = isVisible
|
||||
self.german = german
|
||||
self.english = english
|
||||
super.init(content: content)
|
||||
super.init(content: content, id: id)
|
||||
}
|
||||
|
||||
var linkName: String {
|
||||
@ -38,49 +34,33 @@ final class Tag: Item {
|
||||
"/tags/\(linkName).html"
|
||||
}
|
||||
|
||||
func localized(in language: ContentLanguage) -> LocalizedTag {
|
||||
switch language {
|
||||
case .english: return english
|
||||
case .german: return german
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Paths
|
||||
|
||||
func absoluteUrl(for language: ContentLanguage) -> String {
|
||||
makeCleanAbsolutePath(internalPath(for: language))
|
||||
}
|
||||
|
||||
func filePathRelativeToOutputFolder(for language: ContentLanguage) -> String {
|
||||
makeCleanRelativePath(internalPath(for: language))
|
||||
}
|
||||
|
||||
private func internalPath(for language: ContentLanguage) -> String {
|
||||
content.settings.pages.pageUrlPrefix + "/" + localized(in: language).urlComponent
|
||||
content.settings.paths.tagsOutputFolderPath + "/" + localized(in: language).urlComponent
|
||||
}
|
||||
|
||||
override func absoluteUrl(in language: ContentLanguage) -> String {
|
||||
makeCleanAbsolutePath(internalPath(for: language))
|
||||
}
|
||||
|
||||
override func title(in language: ContentLanguage) -> String {
|
||||
localized(in: language).name
|
||||
}
|
||||
|
||||
override var itemType: ItemType {
|
||||
.tag
|
||||
}
|
||||
|
||||
func contains(urlComponent: String) -> Bool {
|
||||
german.urlComponent == urlComponent || english.urlComponent == urlComponent
|
||||
}
|
||||
}
|
||||
|
||||
extension Tag: Identifiable {
|
||||
|
||||
}
|
||||
|
||||
extension Tag: Equatable {
|
||||
|
||||
static func == (_ lhs: Tag, _ rhs: Tag) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
extension Tag: Hashable {
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
||||
extension Tag: Comparable {
|
||||
|
||||
static func < (lhs: Tag, rhs: Tag) -> Bool {
|
||||
lhs.id < rhs.id
|
||||
}
|
||||
extension Tag: LocalizedItem {
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user