From 1d634eafc0c316e0a88d1e321a9025f1c944686a Mon Sep 17 00:00:00 2001 From: Fuwn Date: Mon, 22 Sep 2025 02:22:32 -0700 Subject: feat: Development commit --- .../Section/SettingsContentRatingsView.swift | 74 ------------ .../Settings/Section/SettingsCreditsView.swift | 9 -- .../Views/Settings/Section/SettingsDebugView.swift | 25 ---- .../Section/SettingsImportExportView.swift | 131 --------------------- .../Settings/Section/SettingsSearchView.swift | 18 --- .../SettingsSectionContentRatingsView.swift | 74 ++++++++++++ .../Section/SettingsSectionCreditsView.swift | 9 ++ .../Section/SettingsSectionDebugView.swift | 25 ++++ .../Section/SettingsSectionImportExportView.swift | 131 +++++++++++++++++++++ .../Section/SettingsSectionSearchView.swift | 18 +++ .../Section/SettingsSectionSettingsView.swift | 22 ++++ .../Section/SettingsSectionThumbnailsView.swift | 4 +- .../Settings/Section/SettingsSettingsView.swift | 22 ---- Sora/Views/Settings/SettingsView.swift | 10 +- 14 files changed, 286 insertions(+), 286 deletions(-) delete mode 100644 Sora/Views/Settings/Section/SettingsContentRatingsView.swift delete mode 100644 Sora/Views/Settings/Section/SettingsCreditsView.swift delete mode 100644 Sora/Views/Settings/Section/SettingsDebugView.swift delete mode 100644 Sora/Views/Settings/Section/SettingsImportExportView.swift delete mode 100644 Sora/Views/Settings/Section/SettingsSearchView.swift create mode 100644 Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift create mode 100644 Sora/Views/Settings/Section/SettingsSectionCreditsView.swift create mode 100644 Sora/Views/Settings/Section/SettingsSectionDebugView.swift create mode 100644 Sora/Views/Settings/Section/SettingsSectionImportExportView.swift create mode 100644 Sora/Views/Settings/Section/SettingsSectionSearchView.swift create mode 100644 Sora/Views/Settings/Section/SettingsSectionSettingsView.swift delete mode 100644 Sora/Views/Settings/Section/SettingsSettingsView.swift diff --git a/Sora/Views/Settings/Section/SettingsContentRatingsView.swift b/Sora/Views/Settings/Section/SettingsContentRatingsView.swift deleted file mode 100644 index 39524ec..0000000 --- a/Sora/Views/Settings/Section/SettingsContentRatingsView.swift +++ /dev/null @@ -1,74 +0,0 @@ -import SwiftUI - -struct SettingsContentRatingsView: View { - @EnvironmentObject var settings: SettingsManager - - var body: some View { - List { - Section(header: Text("Display Content Ratings")) { - ForEach(BooruRating.allCases, id: \.self) { rating in - Toggle( - rating.rawValue, - isOn: Binding( - get: { - settings.displayRatings.contains(rating) - }, - set: { isOn in - setRating( - ratingFor: &settings.displayRatings, - isOn: isOn, - rating: rating - ) - } - ) - ) - } - } - - Section(header: Text("Blur Content Ratings")) { - ForEach(BooruRating.allCases, id: \.self) { rating in - Toggle( - rating.rawValue, - isOn: Binding( - get: { settings.blurRatings.contains(rating) }, - set: { isOn in - setRating( - ratingFor: &settings.blurRatings, - isOn: isOn, - rating: rating - ) - } - ) - ) - } - } - } - .navigationTitle(Text("Content Filtering")) - #if !os(macOS) - .navigationBarTitleDisplayMode(.inline) - #endif - } - - func setRating( - ratingFor: inout [BooruRating], - isOn: Bool, - rating: BooruRating - ) { - var newRatings = ratingFor - - if isOn { - if !newRatings.contains(rating) { - newRatings.append(rating) - } - } else { - newRatings.removeAll { $0 == rating } - } - - ratingFor = newRatings - } -} - -#Preview { - SettingsContentRatingsView() - .environmentObject(SettingsManager()) -} diff --git a/Sora/Views/Settings/Section/SettingsCreditsView.swift b/Sora/Views/Settings/Section/SettingsCreditsView.swift deleted file mode 100644 index 741ce25..0000000 --- a/Sora/Views/Settings/Section/SettingsCreditsView.swift +++ /dev/null @@ -1,9 +0,0 @@ -import SwiftUI - -struct SettingsCreditsView: View { - var body: some View { - Text("Rabbit SVG created by Kim Sun Young") - .fontWeight(.light) - .foregroundColor(.secondary) - } -} diff --git a/Sora/Views/Settings/Section/SettingsDebugView.swift b/Sora/Views/Settings/Section/SettingsDebugView.swift deleted file mode 100644 index 81c7b68..0000000 --- a/Sora/Views/Settings/Section/SettingsDebugView.swift +++ /dev/null @@ -1,25 +0,0 @@ -import SwiftUI - -struct SettingsDebugView: View { - @EnvironmentObject private var settingsManager: SettingsManager - - var body: some View { - Button(action: { - #if DEBUG - settingsManager.addSampleBookmarks() - #endif - }) { - Text("Add Sample Bookmarks") - } - .trailingFrame() - - Button(action: { - #if DEBUG - settingsManager.addSampleSearchHistory() - #endif - }) { - Text("Add Sample Search History") - } - .trailingFrame() - } -} diff --git a/Sora/Views/Settings/Section/SettingsImportExportView.swift b/Sora/Views/Settings/Section/SettingsImportExportView.swift deleted file mode 100644 index d9f23b7..0000000 --- a/Sora/Views/Settings/Section/SettingsImportExportView.swift +++ /dev/null @@ -1,131 +0,0 @@ -import SwiftUI -import UniformTypeIdentifiers - -struct SettingsImportExportView: View { - @EnvironmentObject private var settings: SettingsManager - @State private var isFileExporterPresented = false - @State private var isFileImporterPresented = false - @State private var exportError: Error? - @State private var importError: Error? - private let dateFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateFormat = "yyyy-MM-dd_HH-mm-ss" - return formatter - }() - - var body: some View { - Group { - Button("Import Bookmarks") { - isFileImporterPresented = true - } - - Button("Export Bookmarks") { - exportBookmarksToFile() - } - } - #if os(macOS) - .trailingFrame() - .fileExporter( - isPresented: $isFileExporterPresented, - document: try? JSONFileDocument(settings.exportBookmarks()), - contentType: .json, - defaultFilename: "sora_bookmarks.json" - ) { result in - switch result { - case .success: - break - - case .failure(let error): - exportError = error - } - } - #endif - .fileImporter( - isPresented: $isFileImporterPresented, - allowedContentTypes: [.json], - allowsMultipleSelection: false - ) { result in - handleImportResult(result) - } - .alert( - "Export Failed", - isPresented: Binding( - get: { exportError != nil }, - set: { if !$0 { exportError = nil } } - ) - ) { - Button("OK", role: .cancel) { () } - } message: { - Text(exportError?.localizedDescription ?? "An unknown error occurred while exporting.") - } - .alert( - "Import Failed", - isPresented: Binding( - get: { importError != nil }, - set: { if !$0 { importError = nil } } - ) - ) { - Button("OK", role: .cancel) { () } - } message: { - Text(importError?.localizedDescription ?? "An unknown error occurred while importing.") - } - } - - private func exportBookmarksToFile() { - do { - let data = try settings.exportBookmarks() - let timestamp = dateFormatter.string(from: Date()) - - #if os(macOS) - _ = data - isFileExporterPresented = true - #elseif os(iOS) - let temporaryURL = FileManager.default.temporaryDirectory - .appendingPathComponent("sora_bookmarks_\(timestamp).json") - - try data.write(to: temporaryURL) - - let activityController = UIActivityViewController( - activityItems: [temporaryURL], - applicationActivities: nil - ) - - if let windowScene = UIApplication.shared.connectedScenes - .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene, - let rootViewController = windowScene.windows.first?.rootViewController - { - activityController.popoverPresentationController?.sourceView = rootViewController.view - - rootViewController.present(activityController, animated: true) - } - - activityController.completionWithItemsHandler = { _, _, _, _ in - try? FileManager.default.removeItem(at: temporaryURL) - } - #endif - } catch { - exportError = error - } - } - - private func handleImportResult(_ result: Result<[URL], Error>) { - do { - guard let selectedFile = try result.get().first else { return } - guard selectedFile.startAccessingSecurityScopedResource() else { - throw ImportError.accessDenied - } - - defer { selectedFile.stopAccessingSecurityScopedResource() } - - let data = try Data(contentsOf: selectedFile) - - try settings.importBookmarks(from: data) - } catch { - importError = error - } - } - - private enum ImportError: Error { - case accessDenied - } -} diff --git a/Sora/Views/Settings/Section/SettingsSearchView.swift b/Sora/Views/Settings/Section/SettingsSearchView.swift deleted file mode 100644 index 34b123b..0000000 --- a/Sora/Views/Settings/Section/SettingsSearchView.swift +++ /dev/null @@ -1,18 +0,0 @@ -import SwiftUI - -struct SettingsSearchView: View { - @EnvironmentObject var settings: SettingsManager - - var body: some View { - Picker("Suggestion Mode", selection: $settings.searchSuggestionsMode) { - ForEach(SettingsSearchSuggestionsMode.allCases, id: \.self) { type in - Text(type.rawValue.capitalized).tag(type) - } - } - - Button("Clear History") { - settings.searchHistory.removeAll() - } - .trailingFrame() - } -} diff --git a/Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift b/Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift new file mode 100644 index 0000000..85b28af --- /dev/null +++ b/Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift @@ -0,0 +1,74 @@ +import SwiftUI + +struct SettingsSectionContentRatingsView: View { + @EnvironmentObject var settings: SettingsManager + + var body: some View { + List { + Section(header: Text("Display Content Ratings")) { + ForEach(BooruRating.allCases, id: \.self) { rating in + Toggle( + rating.rawValue, + isOn: Binding( + get: { + settings.displayRatings.contains(rating) + }, + set: { isOn in + setRating( + ratingFor: &settings.displayRatings, + isOn: isOn, + rating: rating + ) + } + ) + ) + } + } + + Section(header: Text("Blur Content Ratings")) { + ForEach(BooruRating.allCases, id: \.self) { rating in + Toggle( + rating.rawValue, + isOn: Binding( + get: { settings.blurRatings.contains(rating) }, + set: { isOn in + setRating( + ratingFor: &settings.blurRatings, + isOn: isOn, + rating: rating + ) + } + ) + ) + } + } + } + .navigationTitle(Text("Content Filtering")) + #if !os(macOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } + + func setRating( + ratingFor: inout [BooruRating], + isOn: Bool, + rating: BooruRating + ) { + var newRatings = ratingFor + + if isOn { + if !newRatings.contains(rating) { + newRatings.append(rating) + } + } else { + newRatings.removeAll { $0 == rating } + } + + ratingFor = newRatings + } +} + +#Preview { + SettingsSectionContentRatingsView() + .environmentObject(SettingsManager()) +} diff --git a/Sora/Views/Settings/Section/SettingsSectionCreditsView.swift b/Sora/Views/Settings/Section/SettingsSectionCreditsView.swift new file mode 100644 index 0000000..f96f27b --- /dev/null +++ b/Sora/Views/Settings/Section/SettingsSectionCreditsView.swift @@ -0,0 +1,9 @@ +import SwiftUI + +struct SettingsSectionCreditsView: View { + var body: some View { + Text("Rabbit SVG created by Kim Sun Young") + .fontWeight(.light) + .foregroundColor(.secondary) + } +} diff --git a/Sora/Views/Settings/Section/SettingsSectionDebugView.swift b/Sora/Views/Settings/Section/SettingsSectionDebugView.swift new file mode 100644 index 0000000..83acb81 --- /dev/null +++ b/Sora/Views/Settings/Section/SettingsSectionDebugView.swift @@ -0,0 +1,25 @@ +import SwiftUI + +struct SettingsSectionDebugView: View { + @EnvironmentObject private var settingsManager: SettingsManager + + var body: some View { + Button(action: { + #if DEBUG + settingsManager.addSampleBookmarks() + #endif + }) { + Text("Add Sample Bookmarks") + } + .trailingFrame() + + Button(action: { + #if DEBUG + settingsManager.addSampleSearchHistory() + #endif + }) { + Text("Add Sample Search History") + } + .trailingFrame() + } +} diff --git a/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift b/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift new file mode 100644 index 0000000..b74fe4b --- /dev/null +++ b/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift @@ -0,0 +1,131 @@ +import SwiftUI +import UniformTypeIdentifiers + +struct SettingsSectionImportExportView: View { + @EnvironmentObject private var settings: SettingsManager + @State private var isFileExporterPresented = false + @State private var isFileImporterPresented = false + @State private var exportError: Error? + @State private var importError: Error? + private let dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd_HH-mm-ss" + return formatter + }() + + var body: some View { + Group { + Button("Import Bookmarks") { + isFileImporterPresented = true + } + + Button("Export Bookmarks") { + exportBookmarksToFile() + } + } + #if os(macOS) + .trailingFrame() + .fileExporter( + isPresented: $isFileExporterPresented, + document: try? JSONFileDocument(settings.exportBookmarks()), + contentType: .json, + defaultFilename: "sora_bookmarks.json" + ) { result in + switch result { + case .success: + break + + case .failure(let error): + exportError = error + } + } + #endif + .fileImporter( + isPresented: $isFileImporterPresented, + allowedContentTypes: [.json], + allowsMultipleSelection: false + ) { result in + handleImportResult(result) + } + .alert( + "Export Failed", + isPresented: Binding( + get: { exportError != nil }, + set: { if !$0 { exportError = nil } } + ) + ) { + Button("OK", role: .cancel) { () } + } message: { + Text(exportError?.localizedDescription ?? "An unknown error occurred while exporting.") + } + .alert( + "Import Failed", + isPresented: Binding( + get: { importError != nil }, + set: { if !$0 { importError = nil } } + ) + ) { + Button("OK", role: .cancel) { () } + } message: { + Text(importError?.localizedDescription ?? "An unknown error occurred while importing.") + } + } + + private func exportBookmarksToFile() { + do { + let data = try settings.exportBookmarks() + let timestamp = dateFormatter.string(from: Date()) + + #if os(macOS) + _ = data + isFileExporterPresented = true + #elseif os(iOS) + let temporaryURL = FileManager.default.temporaryDirectory + .appendingPathComponent("sora_bookmarks_\(timestamp).json") + + try data.write(to: temporaryURL) + + let activityController = UIActivityViewController( + activityItems: [temporaryURL], + applicationActivities: nil + ) + + if let windowScene = UIApplication.shared.connectedScenes + .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene, + let rootViewController = windowScene.windows.first?.rootViewController + { + activityController.popoverPresentationController?.sourceView = rootViewController.view + + rootViewController.present(activityController, animated: true) + } + + activityController.completionWithItemsHandler = { _, _, _, _ in + try? FileManager.default.removeItem(at: temporaryURL) + } + #endif + } catch { + exportError = error + } + } + + private func handleImportResult(_ result: Result<[URL], Error>) { + do { + guard let selectedFile = try result.get().first else { return } + guard selectedFile.startAccessingSecurityScopedResource() else { + throw ImportError.accessDenied + } + + defer { selectedFile.stopAccessingSecurityScopedResource() } + + let data = try Data(contentsOf: selectedFile) + + try settings.importBookmarks(from: data) + } catch { + importError = error + } + } + + private enum ImportError: Error { + case accessDenied + } +} diff --git a/Sora/Views/Settings/Section/SettingsSectionSearchView.swift b/Sora/Views/Settings/Section/SettingsSectionSearchView.swift new file mode 100644 index 0000000..6249c4a --- /dev/null +++ b/Sora/Views/Settings/Section/SettingsSectionSearchView.swift @@ -0,0 +1,18 @@ +import SwiftUI + +struct SettingsSectionSearchView: View { + @EnvironmentObject var settings: SettingsManager + + var body: some View { + Picker("Suggestion Mode", selection: $settings.searchSuggestionsMode) { + ForEach(SettingsSearchSuggestionsMode.allCases, id: \.self) { type in + Text(type.rawValue.capitalized).tag(type) + } + } + + Button("Clear History") { + settings.searchHistory.removeAll() + } + .trailingFrame() + } +} diff --git a/Sora/Views/Settings/Section/SettingsSectionSettingsView.swift b/Sora/Views/Settings/Section/SettingsSectionSettingsView.swift new file mode 100644 index 0000000..273cc82 --- /dev/null +++ b/Sora/Views/Settings/Section/SettingsSectionSettingsView.swift @@ -0,0 +1,22 @@ +import SwiftUI + +struct SettingsSectionSettingsView: View { + @EnvironmentObject var settings: SettingsManager + + var body: some View { + Toggle(isOn: $settings.enableSync) { + Text("Sync with iCloud") + + Text("Keep bookmarks, search history, and providers consistent across all your devices.") + .font(.caption) + } + .onChange(of: settings.enableSync) { _, _ in + settings.triggerSyncIfNeededForAll() + } + + Button("Reset to Defaults") { + settings.resetToDefaults() + } + .trailingFrame() + } +} diff --git a/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift b/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift index 27c4676..ddfbdcc 100644 --- a/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift +++ b/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift @@ -38,12 +38,12 @@ struct SettingsSectionThumbnailsView: View { isShowingContentFiltering.toggle() } .sheet(isPresented: $isShowingContentFiltering) { - SettingsContentRatingsView() + SettingsSectionContentRatingsView() .frame(minHeight: 250) } .trailingFrame() #else - NavigationLink(destination: SettingsContentRatingsView()) { + NavigationLink(destination: SettingsSectionContentRatingsView()) { Text("Content Filtering") } #endif diff --git a/Sora/Views/Settings/Section/SettingsSettingsView.swift b/Sora/Views/Settings/Section/SettingsSettingsView.swift deleted file mode 100644 index 0b10d31..0000000 --- a/Sora/Views/Settings/Section/SettingsSettingsView.swift +++ /dev/null @@ -1,22 +0,0 @@ -import SwiftUI - -struct SettingsSettingsView: View { - @EnvironmentObject var settings: SettingsManager - - var body: some View { - Toggle(isOn: $settings.enableSync) { - Text("Sync with iCloud") - - Text("Keep bookmarks, search history, and providers consistent across all your devices.") - .font(.caption) - } - .onChange(of: settings.enableSync) { _, _ in - settings.triggerSyncIfNeededForAll() - } - - Button("Reset to Defaults") { - settings.resetToDefaults() - } - .trailingFrame() - } -} diff --git a/Sora/Views/Settings/SettingsView.swift b/Sora/Views/Settings/SettingsView.swift index 6c034e3..3f1d309 100644 --- a/Sora/Views/Settings/SettingsView.swift +++ b/Sora/Views/Settings/SettingsView.swift @@ -19,7 +19,7 @@ struct SettingsView: View { } Section(header: Text("Search")) { - SettingsSearchView() + SettingsSectionSearchView() } Section(header: Text("Collections")) { @@ -29,21 +29,21 @@ struct SettingsView: View { } Section(header: Text("Settings")) { - SettingsSettingsView() + SettingsSectionSettingsView() } Section(header: Text("Import & Export")) { - SettingsImportExportView() + SettingsSectionImportExportView() } #if DEBUG Section(header: Text("Debug")) { - SettingsDebugView() + SettingsSectionDebugView() } #endif Section(header: Text("Credits")) { - SettingsCreditsView() + SettingsSectionCreditsView() } } #if os(macOS) -- cgit v1.2.3