import Foundation import Combine final class Settings: ChangeObservableItem { @Published var general: GeneralSettings @Published var paths: PathSettings /// The items to show in the navigation bar @Published var navigation: NavigationSettings @Published var posts: PostSettings @Published var pages: PageSettings @Published var audioPlayer: AudioPlayerSettings weak var content: Content? var cancellables: Set = [] init(general: GeneralSettings, paths: PathSettings, navigation: NavigationSettings, posts: PostSettings, pages: PageSettings, audioPlayer: AudioPlayerSettings) { self.general = general self.paths = paths self.navigation = navigation self.posts = posts self.pages = pages self.audioPlayer = audioPlayer } func remove(_ file: FileResource) { pages.remove(file) posts.remove(file) audioPlayer.remove(file) } func needsSaving() { content?.needsSave() } } // MARK: Storage extension Settings { convenience init(context: LoadingContext, data: Settings.Data) { self.init( general: .init(data: data.general), paths: .init(data: data.paths), navigation: .init(context: context, data: data.navigation), posts: .init(context: context, data: data.posts), pages: .init(context: context, data: data.pages), audioPlayer: .init(context: context, data: data.audioPlayer)) content = context.content } func data(tagOverview: Tag?) -> Data { .init( general: general.data, paths: paths.data, navigation: navigation.data, posts: posts.data, pages: pages.data, audioPlayer: audioPlayer.data, tagOverview: tagOverview?.data) } struct Data: Codable, Equatable { let general: GeneralSettings.Data let paths: PathSettings.Data let navigation: NavigationSettings.Data let posts: PostSettings.Data let pages: PageSettings.Data let audioPlayer: AudioPlayerSettings.Data let tagOverview: Tag.Data? } func saveToDisk(_ data: Data) -> Bool { content?.storage.save(settings: data) ?? false } } extension Settings { static let `default`: Settings = .init( general: .default, paths: .default, navigation: .default, posts: .default, pages: .default, audioPlayer: .default) } extension GeneralSettings { static let `default`: GeneralSettings = .init( url: "https://example.com", linkPreviewImageWidth: 1200, linkPreviewImageHeight: 630, remoteUserForUpload: "user", remotePortForUpload: 22, remotePathForUpload: "/home/user/web", urlForPushNotification: nil) } extension AudioPlayerSettings { static let `default`: AudioPlayerSettings = .init( playlistCoverImageSize: 280, smallCoverImageSize: 78, audioPlayerJsFile: nil, audioPlayerCssFile: nil, german: .init(playlistText: "Wiedergabeliste"), english: .init(playlistText: "Playlist")) } extension PostSettings { static var `default`: PostSettings { .init(postsPerPage: 25, contentWidth: 600, swiperCssFile: nil, swiperJsFile: nil, defaultCssFile: nil, german: .init( feedUrlPrefix: "blog", defaultPageLinkText: "Anzeigen", linkPreview: .init( title: "Beiträge", description: "Alle Beiträge")), english: .init( feedUrlPrefix: "blog", defaultPageLinkText: "View", linkPreview: .init( title: "Blog posts", description: "All blog posts")) ) } } extension PathSettings { static var `default`: PathSettings { .init( assetsOutputFolderPath: "asset", pagesOutputFolderPath: "page", imagesOutputFolderPath: "image", filesOutputFolderPath: "file", videosOutputFolderPath: "video", audioOutputFolderPath: "audio", tagsOutputFolderPath: "tag") } } extension PageSettings { static var `default`: PageSettings { .init(contentWidth: 600, largeImageWidth: 1200, pageLinkImageSize: 180, defaultCssFile: nil, codeHighlightingJsFile: nil, modelViewerJsFile: nil, imageCompareJsFile: nil, imageCompareCssFile: nil, german: .init( emptyPageTitle: "Leere Seite", emptyPageText: "Diese Seite ist leer"), english: .init( emptyPageTitle: "Empty page", emptyPageText: "This page is empty")) } }