Add page settings, improve settings UI

This commit is contained in:
Christoph Hagen 2024-12-05 21:07:06 +01:00
parent f2d78aef93
commit 18eb64f289
17 changed files with 189 additions and 43 deletions

View File

@ -66,6 +66,10 @@
E25DA5732D018AA100AEF16D /* FileContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5722D018AA100AEF16D /* FileContentView.swift */; };
E25DA5752D018B6100AEF16D /* FileDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5742D018B6100AEF16D /* FileDetailView.swift */; };
E25DA5772D018B9900AEF16D /* File+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5762D018B9500AEF16D /* File+Mock.swift */; };
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA58E2D02368A00AEF16D /* PageSettings.swift */; };
E25DA5912D023A8400AEF16D /* IntegerField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5902D023A7E00AEF16D /* IntegerField.swift */; };
E25DA5932D023B3C00AEF16D /* PageSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5922D023B3600AEF16D /* PageSettingsFile.swift */; };
E25DA5952D023BD100AEF16D /* PageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5942D023BCC00AEF16D /* PageSettingsView.swift */; };
E2A21C012CB16A820060935B /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A21C002CB16A820060935B /* PostView.swift */; };
E2A21C032CB16C290060935B /* Environment+Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A21C022CB16C220060935B /* Environment+Language.swift */; };
E2A21C052CB1766C0060935B /* LocalizedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A21C042CB176670060935B /* LocalizedText.swift */; };
@ -176,6 +180,10 @@
E25DA5722D018AA100AEF16D /* FileContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileContentView.swift; sourceTree = "<group>"; };
E25DA5742D018B6100AEF16D /* FileDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDetailView.swift; sourceTree = "<group>"; };
E25DA5762D018B9500AEF16D /* File+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "File+Mock.swift"; sourceTree = "<group>"; };
E25DA58E2D02368A00AEF16D /* PageSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettings.swift; sourceTree = "<group>"; };
E25DA5902D023A7E00AEF16D /* IntegerField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerField.swift; sourceTree = "<group>"; };
E25DA5922D023B3600AEF16D /* PageSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettingsFile.swift; sourceTree = "<group>"; };
E25DA5942D023BCC00AEF16D /* PageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettingsView.swift; sourceTree = "<group>"; };
E2A21C002CB16A820060935B /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
E2A21C022CB16C220060935B /* Environment+Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Environment+Language.swift"; sourceTree = "<group>"; };
E2A21C042CB176670060935B /* LocalizedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedText.swift; sourceTree = "<group>"; };
@ -275,6 +283,7 @@
E25DA5392D00423F00AEF16D /* LocalizedSettingsFile.swift */,
E25DA5332D0041CB00AEF16D /* NavigationBarSettingsFile.swift */,
E25DA5352D0041E200AEF16D /* PostSettingsFile.swift */,
E25DA5922D023B3600AEF16D /* PageSettingsFile.swift */,
E21850342CFAFA570090B18B /* SettingsFile.swift */,
);
path = Settings;
@ -283,6 +292,7 @@
E25DA53B2D0042EA00AEF16D /* Settings */ = {
isa = PBXGroup;
children = (
E25DA58E2D02368A00AEF16D /* PageSettings.swift */,
E25DA5402D00446700AEF16D /* PostSettings.swift */,
E25DA53E2D00441C00AEF16D /* NavigationBarSettings.swift */,
E25DA53C2D0043E200AEF16D /* LocalizedSettings.swift */,
@ -304,6 +314,7 @@
E2A21C342CB9A3CA0060935B /* Settings */ = {
isa = PBXGroup;
children = (
E25DA5942D023BCC00AEF16D /* PageSettingsView.swift */,
E25DA5442D00952D00AEF16D /* SettingsSection.swift */,
E25DA5422D0094A400AEF16D /* SettingsSidebar.swift */,
E25DA5302D003FC000AEF16D /* SectionedSettingsView.swift */,
@ -319,6 +330,7 @@
E2A21C372CB9A4F10060935B /* Generic */ = {
isa = PBXGroup;
children = (
E25DA5902D023A7E00AEF16D /* IntegerField.swift */,
E218501C2CEE6CB30090B18B /* VerticalCenter.swift */,
E2A37D2C2CED2EEE0000979F /* OptionalTextField.swift */,
E2A21C2F2CB490F90060935B /* HorizontalCenter.swift */,
@ -631,6 +643,7 @@
E2A21C102CB18B3A0060935B /* FlowHStack.swift in Sources */,
E25DA5382D00420E00AEF16D /* LocalizedPostSettingsFile.swift in Sources */,
E2B85F3D2C4293F80047CD0C /* PageInFeed.swift in Sources */,
E25DA5952D023BD100AEF16D /* PageSettingsView.swift in Sources */,
E21850092CEE01C30090B18B /* PagePickerView.swift in Sources */,
E2A21C2A2CB2AA4F0060935B /* Post+Mock.swift in Sources */,
E24252082C5168750029FF16 /* GenericMetadata+Localized.swift in Sources */,
@ -696,11 +709,14 @@
E2A21C0E2CB189DC0060935B /* Color+RGB.swift in Sources */,
E25DA5152CFF00C100AEF16D /* Content+Load.swift in Sources */,
E25DA5312D003FCB00AEF16D /* SectionedSettingsView.swift in Sources */,
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */,
E25DA50D2CFD9BA200AEF16D /* PostTagAssignmentView.swift in Sources */,
E25DA5932D023B3C00AEF16D /* PageSettingsFile.swift in Sources */,
E2A21C362CB9A3D70060935B /* FolderSettingsView.swift in Sources */,
E2A21C012CB16A820060935B /* PostView.swift in Sources */,
E2A21C052CB1766C0060935B /* LocalizedText.swift in Sources */,
E25DA5362D0041EB00AEF16D /* PostSettingsFile.swift in Sources */,
E25DA5912D023A8400AEF16D /* IntegerField.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -0,0 +1,17 @@
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
init(pageUrlPrefix: String, contentWidth: Int) {
self.pageUrlPrefix = pageUrlPrefix
self.contentWidth = contentWidth
}
}

View File

@ -8,9 +8,9 @@ final class PostSettings: ObservableObject {
/// The maximum width of the main content
@Published
var contentWidth: CGFloat
var contentWidth: Int
init(postsPerPage: Int, contentWidth: CGFloat) {
init(postsPerPage: Int, contentWidth: Int) {
self.postsPerPage = postsPerPage
self.contentWidth = contentWidth
}

View File

@ -11,16 +11,20 @@ final class Settings: ObservableObject {
@Published
var posts: PostSettings
@Published
var pages: PageSettings
@Published
var german: LocalizedSettings
@Published
var english: LocalizedSettings
init(outputDirectoryPath: String, navigationBar: NavigationBarSettings, posts: PostSettings, german: LocalizedSettings, english: LocalizedSettings) {
init(outputDirectoryPath: String, navigationBar: NavigationBarSettings, posts: PostSettings, pages: PageSettings, german: LocalizedSettings, english: LocalizedSettings) {
self.outputDirectoryPath = outputDirectoryPath
self.navigationBar = navigationBar
self.posts = posts
self.pages = pages
self.german = german
self.english = english
}

View File

@ -6,6 +6,7 @@ extension Settings {
outputDirectoryPath: "/some/path",
navigationBar: .init(iconPath: "/some/other/path", tags: []),
posts: .mock,
pages: .mock,
german: .german,
english: .english)
}
@ -17,6 +18,13 @@ extension PostSettings {
}
}
extension PageSettings {
static var mock: PageSettings {
.init(pageUrlPrefix: "pages", contentWidth: 600)
}
}
extension LocalizedSettings {
static var german: LocalizedSettings {

View File

@ -0,0 +1,11 @@
struct PageSettingsFile {
let pageUrlPrefix: String
let contentWidth: Int
}
extension PageSettingsFile: Codable {
}

View File

@ -6,7 +6,7 @@ struct PostSettingsFile {
let postsPerPage: Int
/// The maximum width of the main content
let contentWidth: CGFloat
let contentWidth: Int
}
extension PostSettingsFile: Codable { }

View File

@ -9,6 +9,8 @@ struct SettingsFile {
let posts: PostSettingsFile
let pages: PageSettingsFile
let german: LocalizedSettingsFile
let english: LocalizedSettingsFile

View File

@ -0,0 +1,31 @@
import SwiftUI
struct IntegerField: View {
private let titleKey: LocalizedStringKey
@Binding
private var number: Int
@State
private var text: String = ""
init(_ titleKey: LocalizedStringKey, number: Binding<Int>) {
self.titleKey = titleKey
self._number = number
}
var body: some View {
TextField(titleKey, text: $text)
.onChange(of: text) { _, newValue in
if let intValue = Int(newValue) {
number = intValue
} else {
text = "\(number)"
}
}
.onAppear {
text = "\(number)"
}
}
}

View File

@ -21,7 +21,9 @@ struct FolderSettingsView: View {
.font(.largeTitle)
.bold()
Text("Select the folders for the app to work.")
.foregroundStyle(.secondary)
.padding(.bottom, 30)
Text("Content Folder")
.font(.headline)
.padding(.bottom, 1)
@ -29,7 +31,10 @@ struct FolderSettingsView: View {
Button(action: selectContentFolder) {
Text("Select folder")
}
.padding(.bottom)
Text("The folder where the raw content of the website is stored")
.foregroundStyle(.secondary)
.padding(.bottom)
Text("Output Folder")
.font(.headline)
.padding(.bottom, 1)
@ -37,8 +42,9 @@ struct FolderSettingsView: View {
Button(action: selectOutputFolder) {
Text("Select folder")
}
.padding(.bottom)
Text("The folder where the generated website is stored")
.foregroundStyle(.secondary)
.padding(.bottom)
}
}
}

View File

@ -21,8 +21,9 @@ struct GenerationSettingsView: View {
.font(.largeTitle)
.bold()
Text("Regenerate the website and monitor the output")
.foregroundStyle(.secondary)
.padding(.bottom, 30)
HStack {
Button(action: generateFeed) {
Text("Generate")

View File

@ -13,14 +13,18 @@ struct LocalizedPostFeedSettingsView: View {
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 400)
Text("The title of all post feed pages.")
.foregroundStyle(.secondary)
.padding(.bottom)
Text("URL prefix")
.font(.headline)
TextField("", text: $settings.feedUrlPrefix)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 400)
Text("The prefix to generate the urls for all post feed pages.")
.foregroundStyle(.secondary)
.padding(.bottom)
Text("Description")
.font(.headline)
TextEditor(text: $settings.description)
@ -33,6 +37,7 @@ struct LocalizedPostFeedSettingsView: View {
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
Text("The description of all post feed pages.")
.foregroundStyle(.secondary)
.padding(.bottom)
}
}

View File

@ -30,19 +30,25 @@ struct NavigationBarSettingsView: View {
.font(.largeTitle)
.bold()
Text("Customize the navigation bar for all pages at the top of the website")
.foregroundStyle(.secondary)
.padding(.bottom, 30)
Text("Icon Path")
.font(.headline)
TextField("", text: $content.settings.navigationBar.iconPath)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 300)
Text("Specify the path to the icon file with regard to the final website folder.")
.foregroundStyle(.secondary)
.padding(.bottom, 30)
Text("Icon Description")
.font(.headline)
IconDescriptionView(settings: content.settings.localized(in: language))
Text("Provide a description of the icon for screen readers.")
.padding(.bottom, 30)
.foregroundStyle(.secondary)
.padding(.bottom)
Text("Visible Tags")
.font(.headline)
FlowHStack {
@ -66,6 +72,7 @@ struct NavigationBarSettingsView: View {
.buttonStyle(.plain)
}
Text("Select the tags to show in the navigation bar. The number should be even.")
.foregroundStyle(.secondary)
}
}
.sheet(isPresented: $showTagPicker) {

View File

@ -0,0 +1,47 @@
import SwiftUI
struct PageSettingsView: View {
@Environment(\.language)
private var language
@EnvironmentObject
private var content: Content
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("Page Settings")
.font(.largeTitle)
.bold()
Text("Change the way pages are displayed")
.padding(.bottom, 30)
Text("Content Width")
.font(.headline)
IntegerField("", number: $content.settings.pages.contentWidth)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 400)
Text("The maximum width of the content in pages (in pixels)")
.foregroundStyle(.secondary)
.padding(.bottom)
Text("Page URL Prefix")
.font(.headline)
TextField("", text: $content.settings.pages.pageUrlPrefix)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 400)
Text("The URL prefix used for the links to pages")
.foregroundStyle(.secondary)
.padding(.bottom)
}
}
}
}
#Preview {
PageSettingsView()
.environmentObject(Content.mock)
.padding()
}

View File

@ -15,7 +15,27 @@ struct PostFeedSettingsView: View {
.font(.largeTitle)
.bold()
Text("Change the way the posts are displayed")
.foregroundStyle(.secondary)
.padding(.bottom, 30)
Text("Content Width")
.font(.headline)
IntegerField("", number: $content.settings.posts.contentWidth)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 400)
Text("The maximum width of the content the post feed (in pixels)")
.foregroundStyle(.secondary)
.padding(.bottom)
Text("Posts Per Page")
.font(.headline)
IntegerField("", number: $content.settings.posts.postsPerPage)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 400)
Text("The maximum number of posts displayed on a single page")
.foregroundStyle(.secondary)
.padding(.bottom)
LocalizedPostFeedSettingsView(settings: content.settings.localized(in: language).posts)
}
}

View File

@ -31,6 +31,8 @@ struct DetailView: View {
NavigationBarSettingsView()
case .postFeed:
PostFeedSettingsView()
case .pages:
PageSettingsView()
case .none:
Text("Select a setting from the sidebar")
.foregroundStyle(.secondary)
@ -42,40 +44,6 @@ struct DetailView: View {
}
}
struct AppearanceView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Appearance Settings")
.font(.largeTitle)
.bold()
Text("Customize the look and feel of the app.")
}
}
}
struct NotificationsView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Notifications Settings")
.font(.largeTitle)
.bold()
Text("Manage your notification preferences.")
}
}
}
struct PrivacyView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Privacy Settings")
.font(.largeTitle)
.bold()
Text("Configure your privacy and security settings.")
}
}
}
#Preview {
SectionedSettingsView()
}

View File

@ -10,6 +10,8 @@ enum SettingsSection: String {
case postFeed = "Post Feed"
case pages = "Pages"
}
extension SettingsSection {
@ -20,6 +22,7 @@ extension SettingsSection {
case .folders: return .folder
case .navigationBar: return .menubarRectangle
case .postFeed: return .rectangleGrid1x2
case .pages: return .docRichtext
}
}
}