Full page content, fixes, cleaner settings
This commit is contained in:
@ -72,6 +72,8 @@ struct LocalizedPageDetailView: View {
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(maxWidth: 400, maxHeight: 300)
|
||||
.cornerRadius(8)
|
||||
Text(image.id)
|
||||
.font(.headline)
|
||||
}
|
||||
|
||||
Text("Link Preview Description")
|
||||
|
@ -93,11 +93,11 @@ struct PageContentResultsView: View {
|
||||
items: results.missingFiles.sorted())
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
if !results.warnings.isEmpty {
|
||||
if !results.unknownCommands.isEmpty {
|
||||
TextWithPopup(
|
||||
symbol: .exclamationmarkTriangleFill,
|
||||
text: "\(results.warnings.count) errors",
|
||||
items: results.warnings.sorted())
|
||||
text: "\(results.unknownCommands.count) unknown commands",
|
||||
items: results.unknownCommands.sorted())
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
if !results.invalidCommandArguments.isEmpty {
|
||||
|
@ -109,11 +109,11 @@ struct PageDetailView: View {
|
||||
}
|
||||
|
||||
private func generate() {
|
||||
guard content.settings.outputDirectoryPath != "" else {
|
||||
guard content.settings.paths.outputDirectoryPath != "" else {
|
||||
print("Invalid output path")
|
||||
return
|
||||
}
|
||||
let url = URL(fileURLWithPath: content.settings.outputDirectoryPath)
|
||||
let url = content.settings.outputDirectory
|
||||
|
||||
guard FileManager.default.fileExists(atPath: url.path) else {
|
||||
print("Missing output folder")
|
||||
|
@ -46,6 +46,8 @@ struct LocalizedPostDetailView: View {
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(maxWidth: 400, maxHeight: 300)
|
||||
.cornerRadius(8)
|
||||
Text(image.id)
|
||||
.font(.headline)
|
||||
}
|
||||
|
||||
Text("Link Preview Description")
|
||||
|
@ -0,0 +1,346 @@
|
||||
import SwiftUI
|
||||
|
||||
|
||||
|
||||
private struct PageIssue {
|
||||
|
||||
let id: Int
|
||||
|
||||
let page: Page
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
let message: PageContentAnomaly
|
||||
|
||||
init(page: Page, language: ContentLanguage, message: PageContentAnomaly) {
|
||||
self.id = .random()
|
||||
self.page = page
|
||||
self.language = language
|
||||
self.message = message
|
||||
}
|
||||
|
||||
var title: String {
|
||||
page.localized(in: language).title
|
||||
}
|
||||
}
|
||||
|
||||
extension PageIssue: Identifiable {
|
||||
|
||||
}
|
||||
|
||||
private struct FixSheet: View {
|
||||
|
||||
@Binding
|
||||
var isPresented: Bool
|
||||
|
||||
@Binding
|
||||
var message: String
|
||||
|
||||
@Binding
|
||||
var infoItems: [String]
|
||||
|
||||
let action: () -> Void
|
||||
|
||||
init(isPresented: Binding<Bool>, message: Binding<String>, infoItems: Binding<[String]>, action: @escaping () -> Void) {
|
||||
self._isPresented = isPresented
|
||||
self._message = message
|
||||
self._infoItems = infoItems
|
||||
self.action = action
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Fix issue")
|
||||
.font(.headline)
|
||||
Text(message)
|
||||
.font(.body)
|
||||
List {
|
||||
ForEach(infoItems, id: \.self) { item in
|
||||
Text(item)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Button("Fix", action: {
|
||||
isPresented = false
|
||||
action()
|
||||
})
|
||||
Button("Cancel", action: { isPresented = false })
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 200)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
private struct ErrorSheet: View {
|
||||
@Binding
|
||||
var isPresented: Bool
|
||||
|
||||
@Binding
|
||||
var message: String
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Error")
|
||||
.font(.headline)
|
||||
Text(message)
|
||||
Button("Dismiss", action: { isPresented = false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PageSettingsContentView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var content: Content
|
||||
|
||||
@State
|
||||
private var isCheckingPages: Bool = false
|
||||
|
||||
|
||||
@State
|
||||
private var issues: [PageIssue] = []
|
||||
|
||||
@State
|
||||
private var message: String = "No fix available"
|
||||
|
||||
@State
|
||||
private var infoItems: [String] = ["No items set"]
|
||||
|
||||
@State
|
||||
private var fixAction: () -> () = {
|
||||
print("No fix action defined")
|
||||
}
|
||||
|
||||
@State
|
||||
private var showFixActionSheet: Bool = false
|
||||
|
||||
@State
|
||||
private var errorMessage: String = ""
|
||||
|
||||
@State
|
||||
private var showErrorAlert: Bool = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Button("Check pages", action: checkAllPagesForErrors)
|
||||
.disabled(isCheckingPages)
|
||||
Button("Fix all", action: applyAllEasyFixes)
|
||||
if isCheckingPages {
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
.frame(height: 20)
|
||||
}
|
||||
}
|
||||
Text("\(issues.count) Issues")
|
||||
.font(.headline)
|
||||
List(issues) { issue in
|
||||
HStack {
|
||||
Button("Attempt Fix", action: { attemptFix(issue: issue) })
|
||||
VStack(alignment: .leading) {
|
||||
Text(issue.message.description)
|
||||
Text("\(issue.title) (\(issue.language.rawValue.uppercased()))")
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.sheet(isPresented: $showFixActionSheet) {
|
||||
FixSheet(isPresented: $showFixActionSheet,
|
||||
message: $message,
|
||||
infoItems: $infoItems) {
|
||||
fixAction()
|
||||
resetFixSheet()
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showErrorAlert) {
|
||||
ErrorSheet(isPresented: $showErrorAlert, message: $errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private func checkAllPagesForErrors() {
|
||||
guard !isCheckingPages else {
|
||||
return
|
||||
}
|
||||
isCheckingPages = true
|
||||
issues = []
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
for language in ContentLanguage.allCases {
|
||||
let parser = PageContentParser(
|
||||
content: content,
|
||||
language: language)
|
||||
for page in content.pages {
|
||||
analyze(page: page, parser: parser)
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.isCheckingPages = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func analyze(page: Page, parser: PageContentParser) {
|
||||
parser.reset()
|
||||
do {
|
||||
let rawPageContent = try content.storage.pageContent(for: page.id, language: parser.language)
|
||||
_ = parser.generatePage(from: rawPageContent)
|
||||
let results = parser.results.convertedWarnings.map {
|
||||
PageIssue(page: page, language: parser.language, message: $0)
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
issues = results + issues
|
||||
}
|
||||
} catch {
|
||||
let message = PageContentAnomaly.failedToLoadContent(error)
|
||||
let error = PageIssue(page: page, language: parser.language, message: message)
|
||||
DispatchQueue.main.async {
|
||||
issues.insert(error, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func applyAllEasyFixes() {
|
||||
issues.forEach { issue in
|
||||
switch issue.message {
|
||||
case .missingFile(let file):
|
||||
fix(missingFile: file, in: issue.page, language: issue.language, ask: false)
|
||||
case .unknownCommand(let string):
|
||||
fixUnknownCommand(string, in: issue.page, language: issue.language)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func attemptFix(issue: PageIssue) {
|
||||
switch issue.message {
|
||||
case .failedToLoadContent:
|
||||
show(error: "No fix available for read errors")
|
||||
case .missingFile(let string):
|
||||
fix(missingFile: string, in: issue.page, language: issue.language)
|
||||
case .missingPage(let string):
|
||||
show(error: "No fix available for missing page \(string)")
|
||||
case .unknownCommand(let string):
|
||||
fixUnknownCommand(string, in: issue.page, language: issue.language)
|
||||
case .invalidCommandArguments(let command, let arguments):
|
||||
show(error: "No fix available for invalid arguments to command \(command) (\(arguments))")
|
||||
case .missingTag(let string):
|
||||
show(error: "No fix available for missing tag \(string)")
|
||||
}
|
||||
}
|
||||
|
||||
private func fix(missingFile: String, in page: Page, language: ContentLanguage, ask: Bool = true) {
|
||||
print("Fixing missing file \(missingFile)")
|
||||
let fileId = page.id + "-" + missingFile
|
||||
if let file = content.file(id: fileId) {
|
||||
replace(missingFile, with: file.id, in: page, language: language)
|
||||
// Remove all errors of the page, and generate them new
|
||||
recalculate(page: page, language: language)
|
||||
return
|
||||
}
|
||||
guard ask else {
|
||||
return
|
||||
}
|
||||
let partialMatches = content.files.filter { $0.id.contains(missingFile) }
|
||||
guard partialMatches.count == 1 else {
|
||||
show(error: "Found \(partialMatches.count) matches for file \(missingFile): \(partialMatches.map { $0.id })")
|
||||
return
|
||||
}
|
||||
let file = partialMatches[0]
|
||||
|
||||
// Ask to fix partially matching file
|
||||
let occurences = findOccurences(of: missingFile, in: page, language: language)
|
||||
message = "Found file '\(file.id)' to match \(missingFile) on page '\(page.localized(in: language).title)'. Do you want to replace it?"
|
||||
infoItems = occurences
|
||||
fixAction = {
|
||||
replace(missingFile, with: file.id, in: page, language: language)
|
||||
// Remove all errors of the page, and generate them new
|
||||
recalculate(page: page, language: language)
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
showFixActionSheet = true
|
||||
}
|
||||
}
|
||||
|
||||
private func recalculate(page: Page, language: ContentLanguage) {
|
||||
let remaining = issues.filter {
|
||||
$0.language != language || $0.page.id != page.id
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.issues = remaining
|
||||
self.isCheckingPages = true
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let parser = PageContentParser(content: content, language: language)
|
||||
self.analyze(page: page, parser: parser)
|
||||
self.isCheckingPages = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func resetFixSheet() {
|
||||
DispatchQueue.main.async {
|
||||
self.message = "No fix available"
|
||||
self.fixAction = { print("No fix action defined") }
|
||||
self.infoItems = ["No items set"]
|
||||
}
|
||||
}
|
||||
|
||||
private func show(error: String) {
|
||||
DispatchQueue.main.async {
|
||||
errorMessage = error
|
||||
showErrorAlert = true
|
||||
}
|
||||
}
|
||||
|
||||
private func findMatchingFile(with missingFile: String, in page: Page) -> FileResource? {
|
||||
let fileId = page.id + "-" + missingFile
|
||||
if let file = content.file(id: fileId) {
|
||||
return file
|
||||
}
|
||||
let partialMatches = content.files.filter { $0.id.contains(missingFile) }
|
||||
if partialMatches.count == 1 {
|
||||
return partialMatches[0]
|
||||
}
|
||||
show(error: "Found \(partialMatches.count) matches for file \(missingFile): \(partialMatches.map { $0.id })")
|
||||
return nil
|
||||
}
|
||||
|
||||
private func findOccurences(of searchString: String, in page: Page, language: ContentLanguage) -> [String] {
|
||||
let parts: [String]
|
||||
do {
|
||||
parts = try content.storage.pageContent(for: page.id, language: language)
|
||||
.components(separatedBy: searchString)
|
||||
} catch {
|
||||
show(error: "Failed to get page content to find occurences: \(error.localizedDescription)")
|
||||
return []
|
||||
}
|
||||
|
||||
var occurrences: [String] = []
|
||||
for index in parts.indices.dropLast() {
|
||||
let start = parts[index].suffix(10)
|
||||
let end = parts[index+1].prefix(10)
|
||||
let full = "...\(start)\(searchString)\(end)...".replacingOccurrences(of: "\n", with: "\\n")
|
||||
occurrences.append(full)
|
||||
}
|
||||
return occurrences
|
||||
}
|
||||
|
||||
private func replace(_ oldString: String, with newString: String, in page: Page, language: ContentLanguage) {
|
||||
do {
|
||||
let pageContent = try content.storage.pageContent(for: page.id, language: language)
|
||||
.replacingOccurrences(of: oldString, with: newString)
|
||||
try content.storage.save(pageContent: pageContent, for: page.id, language: language)
|
||||
print("Replaced \(oldString) with \(newString) in page \(page.id) (\(language))")
|
||||
} catch {
|
||||
print("Failed to replace in page \(page.id) (\(language)): \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func fixUnknownCommand(_ string: String, in page: Page, language: ContentLanguage) {
|
||||
show(error: "No fix available for command '\(string)'")
|
||||
}
|
||||
}
|
@ -8,6 +8,13 @@ struct GenerationContentView: View {
|
||||
@EnvironmentObject
|
||||
private var content: Content
|
||||
|
||||
@Binding
|
||||
private var selectedSection: SettingsSection
|
||||
|
||||
init(selected: Binding<SettingsSection>) {
|
||||
self._selectedSection = selected
|
||||
}
|
||||
|
||||
@State
|
||||
private var isGeneratingWebsite = false
|
||||
|
||||
@ -15,6 +22,16 @@ struct GenerationContentView: View {
|
||||
private var generatorText: String = ""
|
||||
|
||||
var body: some View {
|
||||
switch selectedSection {
|
||||
case .folders, .navigationBar, .postFeed:
|
||||
generationView
|
||||
case .pages:
|
||||
PageSettingsContentView()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var generationView: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Website Generation")
|
||||
@ -42,11 +59,11 @@ struct GenerationContentView: View {
|
||||
}
|
||||
|
||||
private func generateFeed() {
|
||||
guard content.settings.outputDirectoryPath != "" else {
|
||||
guard content.settings.paths.outputDirectoryPath != "" else {
|
||||
print("Invalid output path")
|
||||
return
|
||||
}
|
||||
let url = URL(fileURLWithPath: content.settings.outputDirectoryPath)
|
||||
let url = content.settings.outputDirectory
|
||||
|
||||
guard FileManager.default.fileExists(atPath: url.path) else {
|
||||
print("Missing output folder")
|
||||
@ -71,7 +88,7 @@ struct GenerationContentView: View {
|
||||
}
|
||||
|
||||
#Preview {
|
||||
GenerationContentView()
|
||||
GenerationContentView(selected: .constant(.folders))
|
||||
.environmentObject(Content.mock)
|
||||
.padding()
|
||||
}
|
||||
|
@ -10,13 +10,13 @@ struct GenerationDetailView: View {
|
||||
//case .generation:
|
||||
// GenerationSettingsView()
|
||||
case .folders:
|
||||
FolderSettingsView()
|
||||
PathSettingsView()
|
||||
case .navigationBar:
|
||||
NavigationBarSettingsView()
|
||||
case .postFeed:
|
||||
PostFeedSettingsView()
|
||||
case .pages:
|
||||
PageSettingsView()
|
||||
PageSettingsDetailView()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
|
@ -1,17 +1,5 @@
|
||||
import SwiftUI
|
||||
|
||||
private struct IconDescriptionView: View {
|
||||
|
||||
@ObservedObject
|
||||
var settings: LocalizedSettings
|
||||
|
||||
var body: some View {
|
||||
TextField("", text: $settings.navigationBarIconDescription)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(maxWidth: 300)
|
||||
}
|
||||
}
|
||||
|
||||
struct NavigationBarSettingsView: View {
|
||||
|
||||
@Environment(\.language)
|
||||
@ -33,25 +21,10 @@ struct NavigationBarSettingsView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
Text("Icon Path")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.navigationBar.iconPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
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.")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Visible Tags")
|
||||
.font(.headline)
|
||||
FlowHStack {
|
||||
ForEach(content.settings.navigationBar.tags, id: \.id) { tag in
|
||||
ForEach(content.settings.navigationTags) { tag in
|
||||
TagView(text: tag.localized(in: language).name)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
@ -74,7 +47,7 @@ struct NavigationBarSettingsView: View {
|
||||
.sheet(isPresented: $showTagPicker) {
|
||||
TagSelectionView(
|
||||
presented: $showTagPicker,
|
||||
selected: $content.settings.navigationBar.tags,
|
||||
selected: $content.settings.navigationTags,
|
||||
tags: $content.tags)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import SwiftUI
|
||||
|
||||
struct PageSettingsView: View {
|
||||
struct PageSettingsDetailView: View {
|
||||
|
||||
@Environment(\.language)
|
||||
private var language
|
||||
@ -25,7 +25,7 @@ struct PageSettingsView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Image Width")
|
||||
Text("Fullscreen Image Width")
|
||||
.font(.headline)
|
||||
IntegerField("", number: $content.settings.pages.largeImageWidth)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
@ -33,6 +33,14 @@ struct PageSettingsView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Page Link Image Width")
|
||||
.font(.headline)
|
||||
IntegerField("", number: $content.settings.pages.pageLinkImageSize)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The maximum width of images diplayed as thumbnails on page links")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Page URL Prefix")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.pages.pageUrlPrefix)
|
||||
@ -40,6 +48,14 @@ struct PageSettingsView: View {
|
||||
Text("The URL prefix used for the links to pages")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Javascript Files Path")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.pages.javascriptFilesPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path to the javascript files in the output folder")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,7 +63,7 @@ struct PageSettingsView: View {
|
||||
|
||||
|
||||
#Preview {
|
||||
PageSettingsView()
|
||||
PageSettingsDetailView()
|
||||
.environmentObject(Content.mock)
|
||||
.padding()
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import SwiftUI
|
||||
|
||||
struct FolderSettingsView: View {
|
||||
struct PathSettingsView: View {
|
||||
|
||||
@Environment(\.language)
|
||||
private var language
|
||||
@ -38,13 +38,53 @@ struct FolderSettingsView: View {
|
||||
Text("Output Folder")
|
||||
.font(.headline)
|
||||
.padding(.bottom, 1)
|
||||
Text(content.settings.outputDirectoryPath)
|
||||
Text(content.settings.paths.outputDirectoryPath)
|
||||
Button(action: selectOutputFolder) {
|
||||
Text("Select folder")
|
||||
}
|
||||
Text("The folder where the generated website is stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Pages output folder")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.paths.pagesOutputFolderPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path in the output folder where the generated pages are stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Tags output folder")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.paths.tagsOutputFolderPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path in the output folder where the generated tag pages are stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Files output folder")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.paths.filesOutputFolderPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path in the output folder where the copied files are stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Images output folder")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.paths.imagesOutputFolderPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path in the output folder where the generated images are stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Videos output folder")
|
||||
.font(.headline)
|
||||
TextField("", text: $content.settings.paths.videosOutputFolderPath)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
Text("The path in the output folder where the generated videos are stored")
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,7 +104,7 @@ struct FolderSettingsView: View {
|
||||
guard let url = savePanelUsingOpenPanel() else {
|
||||
return
|
||||
}
|
||||
content.settings.outputDirectoryPath = url.path()
|
||||
content.settings.paths.outputDirectoryPath = url.path()
|
||||
}
|
||||
|
||||
private func savePanelUsingOpenPanel() -> URL? {
|
||||
@ -91,7 +131,7 @@ struct FolderSettingsView: View {
|
||||
}
|
||||
|
||||
#Preview {
|
||||
FolderSettingsView()
|
||||
PathSettingsView()
|
||||
.environmentObject(Content.mock)
|
||||
.padding()
|
||||
}
|
@ -36,7 +36,7 @@ struct PostFeedSettingsView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
LocalizedPostFeedSettingsView(settings: content.settings.localized(in: language).posts)
|
||||
LocalizedPostFeedSettingsView(settings: content.settings.localized(in: language))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user