Rework content commands, add audio player
This commit is contained in:
@ -4,14 +4,6 @@ extension Content {
|
||||
("/" + path).replacingOccurrences(of: "//", with: "/")
|
||||
}
|
||||
|
||||
private func pathPrefix(for file: FileResource) -> String {
|
||||
switch file.type {
|
||||
case .image: return settings.paths.imagesOutputFolderPath
|
||||
case .video: return settings.paths.videosOutputFolderPath
|
||||
default: return settings.paths.filesOutputFolderPath
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Paths to items
|
||||
|
||||
func absoluteUrlPrefixForTag(_ tag: Tag, language: ContentLanguage) -> String {
|
||||
@ -22,20 +14,6 @@ extension Content {
|
||||
absoluteUrlPrefixForTag(tag, language: language) + ".html"
|
||||
}
|
||||
|
||||
func absoluteUrlToPage(_ page: Page, language: ContentLanguage) -> String {
|
||||
// TODO: Record link to trace connections between pages
|
||||
makeCleanAbsolutePath(settings.pages.pageUrlPrefix + "/" + page.localized(in: language).urlString)
|
||||
}
|
||||
|
||||
/**
|
||||
Get the url path to a file in the output folder.
|
||||
The result is an absolute path from the output folder for use in HTML.
|
||||
*/
|
||||
func absoluteUrlToFile(_ file: FileResource) -> String {
|
||||
let path = pathPrefix(for: file) + "/" + file.id
|
||||
return makeCleanAbsolutePath(path)
|
||||
}
|
||||
|
||||
// MARK: Find items by id
|
||||
|
||||
func page(_ pageId: String) -> Page? {
|
||||
@ -50,8 +28,8 @@ extension Content {
|
||||
files.first { $0.id == videoId && $0.type.isVideo }
|
||||
}
|
||||
|
||||
func file(id: String) -> FileResource? {
|
||||
files.first { $0.id == id }
|
||||
func file(_ fileId: String) -> FileResource? {
|
||||
files.first { $0.id == fileId }
|
||||
}
|
||||
|
||||
func tag(_ tagId: String) -> Tag? {
|
||||
|
@ -135,6 +135,7 @@ extension Content {
|
||||
pages[pageId] = Page(
|
||||
content: self,
|
||||
id: pageId,
|
||||
externalLink: page.externalLink,
|
||||
isDraft: page.isDraft,
|
||||
createdDate: page.createdDate,
|
||||
startDate: page.startDate,
|
||||
|
@ -47,6 +47,7 @@ private extension Page {
|
||||
|
||||
var pageFile: PageFile {
|
||||
.init(isDraft: isDraft,
|
||||
externalLink: externalLink,
|
||||
tags: tags.map { $0.id },
|
||||
createdDate: createdDate,
|
||||
startDate: startDate,
|
||||
|
28
CHDataManagement/Model/Content+Validation.swift
Normal file
28
CHDataManagement/Model/Content+Validation.swift
Normal file
@ -0,0 +1,28 @@
|
||||
import Foundation
|
||||
|
||||
extension Content {
|
||||
|
||||
private static let disallowedCharactersInIds = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-")).inverted
|
||||
|
||||
private static let disallowedCharactersInFileIds = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-.")).inverted
|
||||
|
||||
func isNewIdForTag(_ id: String) -> Bool {
|
||||
!tags.contains { $0.id == id }
|
||||
}
|
||||
|
||||
func isNewIdForPage(_ id: String) -> Bool {
|
||||
!pages.contains { $0.id == id }
|
||||
}
|
||||
|
||||
func isNewIdForPost(_ id: String) -> Bool {
|
||||
!posts.contains { $0.id == id }
|
||||
}
|
||||
|
||||
func isValidIdForTagOrTagOrPost(_ id: String) -> Bool {
|
||||
id.rangeOfCharacter(from: Content.disallowedCharactersInIds) == nil
|
||||
}
|
||||
|
||||
func isValidIdForFile(_ id: String) -> Bool {
|
||||
id.rangeOfCharacter(from: Content.disallowedCharactersInFileIds) == nil
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
final class FileResource: ObservableObject {
|
||||
|
||||
unowned let content: Content
|
||||
final class FileResource: Item {
|
||||
|
||||
let type: FileType
|
||||
|
||||
@ -24,24 +22,24 @@ final class FileResource: ObservableObject {
|
||||
var size: CGSize = .zero
|
||||
|
||||
init(content: Content, id: String, isExternallyStored: Bool, en: String, de: String) {
|
||||
self.content = content
|
||||
self.id = id
|
||||
self.type = FileType(fileExtension: id.fileExtension)
|
||||
self.englishDescription = en
|
||||
self.germanDescription = de
|
||||
self.isExternallyStored = isExternallyStored
|
||||
super.init(content: content)
|
||||
}
|
||||
|
||||
/**
|
||||
Only for bundle images
|
||||
*/
|
||||
init(resourceImage: String, type: ImageFileType) {
|
||||
self.content = .mock // TODO: Add images to mock
|
||||
self.type = .image(type)
|
||||
self.id = resourceImage
|
||||
self.englishDescription = "A test image included in the bundle"
|
||||
self.germanDescription = "Ein Testbild aus dem Bundle"
|
||||
self.isExternallyStored = true
|
||||
super.init(content: .mock) // TODO: Add images to mock
|
||||
}
|
||||
|
||||
func getDescription(for language: ContentLanguage) -> String {
|
||||
@ -62,6 +60,10 @@ final class FileResource: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func dataContent() throws -> Data {
|
||||
try content.storage.fileData(for: id)
|
||||
}
|
||||
|
||||
// MARK: Images
|
||||
|
||||
var aspectRatio: CGFloat {
|
||||
@ -94,6 +96,26 @@ final class FileResource: ObservableObject {
|
||||
private var failureImage: Image {
|
||||
Image(systemSymbol: .exclamationmarkTriangle)
|
||||
}
|
||||
|
||||
// MARK: Paths
|
||||
|
||||
/**
|
||||
Get the url path to a file in the output folder.
|
||||
The result is an absolute path from the output folder for use in HTML.
|
||||
*/
|
||||
var absoluteUrl: String {
|
||||
let path = pathPrefix + "/" + id
|
||||
return makeCleanAbsolutePath(path)
|
||||
}
|
||||
|
||||
|
||||
private var pathPrefix: String {
|
||||
switch type {
|
||||
case .image: return content.settings.paths.imagesOutputFolderPath
|
||||
case .video: return content.settings.paths.videosOutputFolderPath
|
||||
default: return content.settings.paths.filesOutputFolderPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FileResource: Identifiable {
|
||||
|
18
CHDataManagement/Model/Item.swift
Normal file
18
CHDataManagement/Model/Item.swift
Normal file
@ -0,0 +1,18 @@
|
||||
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: "/")
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
final class Page: ObservableObject {
|
||||
|
||||
unowned let content: Content
|
||||
final class Page: Item {
|
||||
|
||||
/**
|
||||
The unique id of the entry
|
||||
@ -10,6 +8,15 @@ final class Page: ObservableObject {
|
||||
@Published
|
||||
var id: String
|
||||
|
||||
/**
|
||||
The external link this page points to.
|
||||
|
||||
If this value is not `nil`, then the page has no content
|
||||
and many other features are disabled.
|
||||
*/
|
||||
@Published
|
||||
var externalLink: String?
|
||||
|
||||
@Published
|
||||
var isDraft: Bool
|
||||
|
||||
@ -44,6 +51,7 @@ final class Page: ObservableObject {
|
||||
|
||||
init(content: Content,
|
||||
id: String,
|
||||
externalLink: String?,
|
||||
isDraft: Bool,
|
||||
createdDate: Date,
|
||||
startDate: Date,
|
||||
@ -51,8 +59,8 @@ final class Page: ObservableObject {
|
||||
german: LocalizedPage,
|
||||
english: LocalizedPage,
|
||||
tags: [Tag]) {
|
||||
self.content = content
|
||||
self.id = id
|
||||
self.externalLink = externalLink
|
||||
self.isDraft = isDraft
|
||||
self.createdDate = createdDate
|
||||
self.startDate = startDate
|
||||
@ -61,6 +69,8 @@ final class Page: ObservableObject {
|
||||
self.german = german
|
||||
self.english = english
|
||||
self.tags = tags
|
||||
|
||||
super.init(content: content)
|
||||
}
|
||||
|
||||
func localized(in language: ContentLanguage) -> LocalizedPage {
|
||||
@ -78,6 +88,28 @@ final class Page: ObservableObject {
|
||||
id = newId
|
||||
return true
|
||||
}
|
||||
|
||||
var isExternalUrl: Bool {
|
||||
externalLink != nil
|
||||
}
|
||||
|
||||
// MARK: Paths
|
||||
|
||||
func absoluteUrl(for language: ContentLanguage) -> String {
|
||||
if let url = externalLink {
|
||||
return url
|
||||
}
|
||||
// TODO: Record link to trace connections between pages
|
||||
return 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).urlString
|
||||
}
|
||||
}
|
||||
|
||||
extension Page: Identifiable {
|
||||
|
22
CHDataManagement/Model/Song.swift
Normal file
22
CHDataManagement/Model/Song.swift
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
struct Song {
|
||||
|
||||
let name: String
|
||||
|
||||
let artist: String
|
||||
|
||||
let album: String
|
||||
|
||||
let track: Int
|
||||
|
||||
/// The file id of the audio file
|
||||
let file: String
|
||||
|
||||
/// The file id of the cover image
|
||||
let cover: String
|
||||
}
|
||||
|
||||
|
||||
extension Song: Codable {
|
||||
|
||||
}
|
@ -15,6 +15,13 @@ final class Tag: ObservableObject {
|
||||
@Published
|
||||
var english: LocalizedTag
|
||||
|
||||
init(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)
|
||||
}
|
||||
|
||||
init(isVisible: Bool = true, german: LocalizedTag, english: LocalizedTag) {
|
||||
self.isVisible = isVisible
|
||||
self.german = german
|
||||
|
Reference in New Issue
Block a user