summaryrefslogtreecommitdiff
path: root/Sora/Views/Settings
diff options
context:
space:
mode:
Diffstat (limited to 'Sora/Views/Settings')
-rw-r--r--Sora/Views/Settings/Collections/SettingsCollectionsListView.swift3
-rw-r--r--Sora/Views/Settings/Collections/SettingsCollectionsView.swift3
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift11
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionDebugView.swift3
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionDetailsView.swift27
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionImportExportView.swift72
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionProviderView.swift72
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionSearchView.swift9
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionSettingsView.swift15
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift25
-rw-r--r--Sora/Views/Settings/SettingsView.swift24
11 files changed, 150 insertions, 114 deletions
diff --git a/Sora/Views/Settings/Collections/SettingsCollectionsListView.swift b/Sora/Views/Settings/Collections/SettingsCollectionsListView.swift
index c474b79..1cd9121 100644
--- a/Sora/Views/Settings/Collections/SettingsCollectionsListView.swift
+++ b/Sora/Views/Settings/Collections/SettingsCollectionsListView.swift
@@ -1,7 +1,8 @@
import SwiftUI
struct SettingsCollectionsListView: View {
- @EnvironmentObject private var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
var body: some View {
List {
diff --git a/Sora/Views/Settings/Collections/SettingsCollectionsView.swift b/Sora/Views/Settings/Collections/SettingsCollectionsView.swift
index a7ce5a7..f74fef9 100644
--- a/Sora/Views/Settings/Collections/SettingsCollectionsView.swift
+++ b/Sora/Views/Settings/Collections/SettingsCollectionsView.swift
@@ -1,7 +1,8 @@
import SwiftUI
struct SettingsCollectionsView: View {
- @EnvironmentObject private var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
@State private var isDeleteConfirmationPresented = false
@State private var isRenameAlertPresented = false
diff --git a/Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift b/Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift
index 85b28af..353824a 100644
--- a/Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionContentRatingsView.swift
@@ -1,11 +1,12 @@
import SwiftUI
struct SettingsSectionContentRatingsView: View {
- @EnvironmentObject var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
var body: some View {
List {
- Section(header: Text("Display Content Ratings")) {
+ Section("Show Ratings") {
ForEach(BooruRating.allCases, id: \.self) { rating in
Toggle(
rating.rawValue,
@@ -25,7 +26,7 @@ struct SettingsSectionContentRatingsView: View {
}
}
- Section(header: Text("Blur Content Ratings")) {
+ Section("Blur Ratings") {
ForEach(BooruRating.allCases, id: \.self) { rating in
Toggle(
rating.rawValue,
@@ -43,7 +44,7 @@ struct SettingsSectionContentRatingsView: View {
}
}
}
- .navigationTitle(Text("Content Filtering"))
+ .navigationTitle(Text("Sensitive Content"))
#if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
#endif
@@ -70,5 +71,5 @@ struct SettingsSectionContentRatingsView: View {
#Preview {
SettingsSectionContentRatingsView()
- .environmentObject(SettingsManager())
+ .environment(SettingsManager())
}
diff --git a/Sora/Views/Settings/Section/SettingsSectionDebugView.swift b/Sora/Views/Settings/Section/SettingsSectionDebugView.swift
index 83acb81..c97b545 100644
--- a/Sora/Views/Settings/Section/SettingsSectionDebugView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionDebugView.swift
@@ -1,7 +1,8 @@
import SwiftUI
struct SettingsSectionDebugView: View {
- @EnvironmentObject private var settingsManager: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settingsManager
var body: some View {
Button(action: {
diff --git a/Sora/Views/Settings/Section/SettingsSectionDetailsView.swift b/Sora/Views/Settings/Section/SettingsSectionDetailsView.swift
index 9634297..c386634 100644
--- a/Sora/Views/Settings/Section/SettingsSectionDetailsView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionDetailsView.swift
@@ -1,11 +1,14 @@
import SwiftUI
struct SettingsSectionDetailsView: View {
- @EnvironmentObject var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
var body: some View {
+ @Bindable var settings = settings
+
Form {
- Section(header: Text("Image Quality")) {
+ Section("Image Quality") {
Picker("Image Quality", selection: $settings.detailViewQuality) {
ForEach(BooruPostFileType.allCases, id: \.self) { type in
Text(type.rawValue.capitalized).tag(type)
@@ -13,32 +16,32 @@ struct SettingsSectionDetailsView: View {
}
}
- Section(header: Text("Display Options")) {
- Toggle("Enable \"Share Image\" Shortcut", isOn: $settings.enableShareShortcut)
+ Section("Appearance") {
+ Toggle("Show Share Action", isOn: $settings.enableShareShortcut)
- Toggle("Display Information Bar", isOn: $settings.displayDetailsInformationBar)
+ Toggle("Show Information Bar", isOn: $settings.displayDetailsInformationBar)
}
#if os(macOS)
- Section(header: Text("File Management")) {
+ Section("Saved Files") {
Toggle(isOn: $settings.saveTagsToFile) {
- Text("Save Tags to File")
+ Text("Save Tags Alongside Images")
Text("Saves post tags in a file alongside the downloaded image.")
}
}
#endif
- Section(header: Text("Performance")) {
+ Section("Loading") {
let preloadRange = 0...10
#if os(macOS)
- Picker("Preloaded Images", selection: $settings.preloadedCarouselImages) {
+ Picker("Preload Nearby Images", selection: $settings.preloadedCarouselImages) {
ForEach(preloadRange, id: \.self) { columns in Text("\(columns)") }
}
#else
Stepper(
- "Preloaded Images: \(settings.preloadedCarouselImages)",
+ "Preload Nearby Images: \(settings.preloadedCarouselImages)",
value: $settings.preloadedCarouselImages,
in: preloadRange
)
@@ -48,7 +51,7 @@ struct SettingsSectionDetailsView: View {
#if os(macOS)
.formStyle(.grouped)
#endif
- .navigationTitle("Details")
+ .navigationTitle("Viewer")
#if !os(macOS)
.navigationBarTitleDisplayMode(.large)
#endif
@@ -58,6 +61,6 @@ struct SettingsSectionDetailsView: View {
#Preview {
NavigationStack {
SettingsSectionDetailsView()
- .environmentObject(SettingsManager())
+ .environment(SettingsManager())
}
}
diff --git a/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift b/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift
index b74fe4b..846e9e5 100644
--- a/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift
@@ -2,9 +2,12 @@ import SwiftUI
import UniformTypeIdentifiers
struct SettingsSectionImportExportView: View {
- @EnvironmentObject private var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
@State private var isFileExporterPresented = false
@State private var isFileImporterPresented = false
+ @State private var bookmarksExportDocument: JSONFileDocument?
+ @State private var bookmarksExportFilename = "sora_bookmarks.json"
@State private var exportError: Error?
@State private var importError: Error?
private let dateFormatter: DateFormatter = {
@@ -20,26 +23,30 @@ struct SettingsSectionImportExportView: View {
}
Button("Export Bookmarks") {
- exportBookmarksToFile()
+ prepareBookmarksExport()
}
}
#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
+ #endif
+ .fileExporter(
+ isPresented: $isFileExporterPresented,
+ document: bookmarksExportDocument,
+ contentType: .json,
+ defaultFilename: bookmarksExportFilename
+ ) { result in
+ bookmarksExportDocument = nil
- case .failure(let error):
+ switch result {
+ case .success:
+ break
+
+ case .failure(let error):
+ if !isUserCancelled(error) {
exportError = error
}
}
- #endif
+ }
.fileImporter(
isPresented: $isFileImporterPresented,
allowedContentTypes: [.json],
@@ -71,38 +78,14 @@ struct SettingsSectionImportExportView: View {
}
}
- private func exportBookmarksToFile() {
+ private func prepareBookmarksExport() {
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
+ bookmarksExportDocument = JSONFileDocument(data)
+ bookmarksExportFilename = "sora_bookmarks_\(timestamp).json"
+ isFileExporterPresented = true
} catch {
exportError = error
}
@@ -125,6 +108,13 @@ struct SettingsSectionImportExportView: View {
}
}
+ private func isUserCancelled(_ error: Error) -> Bool {
+ let error = error as NSError
+
+ return error.domain == NSCocoaErrorDomain
+ && error.code == CocoaError.userCancelled.rawValue
+ }
+
private enum ImportError: Error {
case accessDenied
}
diff --git a/Sora/Views/Settings/Section/SettingsSectionProviderView.swift b/Sora/Views/Settings/Section/SettingsSectionProviderView.swift
index cbfae37..02a8be6 100644
--- a/Sora/Views/Settings/Section/SettingsSectionProviderView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionProviderView.swift
@@ -1,16 +1,19 @@
import SwiftUI
struct SettingsSectionProviderView: View {
- @EnvironmentObject var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
@State private var showingCustomBooruSheet = false
@State private var newDomain: String = ""
@State private var newFlavor: BooruProviderFlavor = .danbooru
@State private var domainError: String?
var body: some View {
+ @Bindable var settings = settings
+
Form {
- Section(header: Text("Provider Selection")) {
- Picker("Provider", selection: $settings.preferredBooru) {
+ Section("Source") {
+ Picker("Website", selection: $settings.preferredBooru) {
ForEach(BooruProvider.allCases, id: \.self) { type in
Text(type.rawValue).tag(type)
}
@@ -22,11 +25,13 @@ struct SettingsSectionProviderView: View {
}
}
- Section(header: Text("Moebooru Feed")) {
- Toggle("Show Held Posts", isOn: $settings.showHeldMoebooruPosts)
+ if BooruProviderFlavor(provider: settings.preferredBooru) == .moebooru {
+ Section("Hidden Posts") {
+ Toggle("Show Hidden Posts", isOn: $settings.showHeldMoebooruPosts)
+ }
}
- Section(header: Text("API Credentials")) {
+ Section {
SecureField(
"API Key",
text: Binding(
@@ -65,23 +70,40 @@ struct SettingsSectionProviderView: View {
#if os(iOS)
.keyboardType(isDanbooruProvider ? .default : .numberPad)
#endif
+ } header: {
+ Text("Account")
+ } footer: {
+ Text("Add credentials only if your selected source supports them.")
}
- Section(header: Text("Custom Providers")) {
- Button("Add Custom Provider") {
+ Section {
+ Toggle("Send User Agent", isOn: $settings.sendBooruUserAgent)
+
+ if settings.sendBooruUserAgent {
+ TextField("Custom User Agent", text: $settings.customBooruUserAgent)
+ .autocorrectionDisabled(true)
+ }
+ } header: {
+ Text("Advanced")
+ } footer: {
+ Text("Only change these options if a source requires them.")
+ }
+
+ Section {
+ Button("Add Source") {
showingCustomBooruSheet = true
}
.trailingFrame()
if case .custom(let provider) = settings.preferredBooru {
- Button("Remove Custom Provider") {
+ Button("Remove Current Source") {
settings.customProviders.removeAll { $0.id == provider.id }
settings.preferredBooru = .safebooru
}
.disabled(!settings.customProviders.contains { $0.id == provider.id })
}
- Button("Remove All Custom Providers") {
+ Button("Remove All Custom Sources") {
if case .custom = settings.preferredBooru {
settings.preferredBooru = .safebooru
}
@@ -91,23 +113,27 @@ struct SettingsSectionProviderView: View {
}
}
.trailingFrame()
+ } header: {
+ Text("Custom Sources")
+ } footer: {
+ Text("Use custom sources when you want Sora to browse a compatible site.")
}
}
#if os(macOS)
.formStyle(.grouped)
#endif
- .navigationTitle("Provider")
+ .navigationTitle("Source")
#if !os(macOS)
.navigationBarTitleDisplayMode(.large)
#endif
.sheet(isPresented: $showingCustomBooruSheet) {
NavigationStack {
Form {
- Section(header: Text("Provider Details")) {
- TextField("Domain", text: $newDomain)
+ Section("Source Details") {
+ TextField("Website", text: $newDomain)
.autocorrectionDisabled(true)
- Picker("Provider Type", selection: $newFlavor) {
+ Picker("Type", selection: $newFlavor) {
ForEach(BooruProviderFlavor.allCases, id: \.self) { flavor in
Text(flavor.rawValue).tag(flavor)
}
@@ -117,7 +143,7 @@ struct SettingsSectionProviderView: View {
#if os(macOS)
.formStyle(.grouped)
#endif
- .navigationTitle("Add Custom Provider")
+ .navigationTitle("Add Source")
#if !os(macOS)
.navigationBarTitleDisplayMode(.inline)
#endif
@@ -142,7 +168,7 @@ struct SettingsSectionProviderView: View {
}
}
.alert(
- "Invalid Domain",
+ "Invalid Website",
isPresented: Binding(
get: { domainError != nil },
set: { if !$0 { domainError = nil } }
@@ -150,7 +176,9 @@ struct SettingsSectionProviderView: View {
) {
Button("OK", role: .cancel) { () }
} message: {
- Text(domainError ?? "An unknown error occurred while validating the domain.")
+ if let domainError {
+ Text(domainError)
+ }
}
}
@@ -183,19 +211,19 @@ struct SettingsSectionProviderView: View {
"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*\\.[a-z]{2,}$"
guard NSPredicate(format: "SELF MATCHES %@", domainRegex).evaluate(with: domain) else {
- domainError = "Please enter a valid domain name, such as yande.re."
+ domainError = "Enter a valid website, such as yande.re."
return false
}
guard !domain.contains("://"), !domain.contains("/"), !domain.contains("?") else {
- domainError = "Only enter the domain name—leave out 'http://' or extra details."
+ domainError = "Enter only the website name, without 'http://' or extra details."
return false
}
guard domain.count <= 253 else { // RFC 1035
- domainError = "This domain name is too long. It must be 253 characters or fewer."
+ domainError = "This website name is too long. It must be 253 characters or fewer."
return false
}
@@ -203,7 +231,7 @@ struct SettingsSectionProviderView: View {
let labels = domain.split(separator: ".")
guard labels.allSatisfy({ $0.count <= 63 }) else {
- domainError = "Each section of the domain name must be 63 characters or fewer."
+ domainError = "Each section of the website name must be 63 characters or fewer."
return false
}
@@ -248,6 +276,6 @@ struct SettingsSectionProviderView: View {
#Preview {
NavigationStack {
SettingsSectionProviderView()
- .environmentObject(SettingsManager())
+ .environment(SettingsManager())
}
}
diff --git a/Sora/Views/Settings/Section/SettingsSectionSearchView.swift b/Sora/Views/Settings/Section/SettingsSectionSearchView.swift
index 6249c4a..e82f872 100644
--- a/Sora/Views/Settings/Section/SettingsSectionSearchView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionSearchView.swift
@@ -1,16 +1,19 @@
import SwiftUI
struct SettingsSectionSearchView: View {
- @EnvironmentObject var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
var body: some View {
- Picker("Suggestion Mode", selection: $settings.searchSuggestionsMode) {
+ @Bindable var settings = settings
+
+ Picker("Suggestions", selection: $settings.searchSuggestionsMode) {
ForEach(SettingsSearchSuggestionsMode.allCases, id: \.self) { type in
Text(type.rawValue.capitalized).tag(type)
}
}
- Button("Clear History") {
+ Button("Clear Search History") {
settings.searchHistory.removeAll()
}
.trailingFrame()
diff --git a/Sora/Views/Settings/Section/SettingsSectionSettingsView.swift b/Sora/Views/Settings/Section/SettingsSectionSettingsView.swift
index 273cc82..ad7fe83 100644
--- a/Sora/Views/Settings/Section/SettingsSectionSettingsView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionSettingsView.swift
@@ -1,20 +1,25 @@
import SwiftUI
struct SettingsSectionSettingsView: View {
- @EnvironmentObject var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
var body: some View {
+ @Bindable var settings = settings
+
Toggle(isOn: $settings.enableSync) {
Text("Sync with iCloud")
- Text("Keep bookmarks, search history, and providers consistent across all your devices.")
+ Text("Keep bookmarks, collections, search history, and sources in sync across your devices.")
.font(.caption)
}
- .onChange(of: settings.enableSync) { _, _ in
- settings.triggerSyncIfNeededForAll()
+ .onChange(of: settings.enableSync) { _, isEnabled in
+ if isEnabled {
+ settings.triggerSyncIfNeededForAll()
+ }
}
- Button("Reset to Defaults") {
+ Button("Reset Settings") {
settings.resetToDefaults()
}
.trailingFrame()
diff --git a/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift b/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift
index ddfbdcc..dc9d87e 100644
--- a/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionThumbnailsView.swift
@@ -1,12 +1,15 @@
import SwiftUI
struct SettingsSectionThumbnailsView: View {
- @EnvironmentObject var settings: SettingsManager
+ @Environment(SettingsManager.self)
+ private var settings
@State private var isShowingContentFiltering = false
var body: some View {
+ @Bindable var settings = settings
+
Form {
- Section(header: Text("Thumbnail Quality")) {
+ Section("Image Quality") {
Picker("Thumbnail Quality", selection: $settings.thumbnailQuality) {
ForEach(BooruPostFileType.allCases, id: \.self) { type in
Text(type.rawValue.capitalized).tag(type)
@@ -14,27 +17,27 @@ struct SettingsSectionThumbnailsView: View {
}
}
- Section(header: Text("Grid Layout")) {
+ Section("Layout") {
#if os(macOS)
- Picker("Thumbnail Grid Columns", selection: $settings.thumbnailGridColumns) {
+ Picker("Columns", selection: $settings.thumbnailGridColumns) {
ForEach(1...10, id: \.self) { columns in Text("\(columns)") }
}
#else
Stepper(
- "Thumbnail Grid Columns: \(settings.thumbnailGridColumns)",
+ "Columns: \(settings.thumbnailGridColumns)",
value: $settings.thumbnailGridColumns,
in: 1...10
)
#endif
- Toggle("Uniform Thumbnail Size", isOn: $settings.uniformThumbnailGrid)
+ Toggle("Square Thumbnails", isOn: $settings.uniformThumbnailGrid)
- Toggle("Lazy Thumbnail Loading", isOn: $settings.alternativeThumbnailGrid)
+ Toggle("Prioritize Scrolling Performance", isOn: $settings.alternativeThumbnailGrid)
}
- Section(header: Text("Content Filtering")) {
+ Section("Sensitive Content") {
#if os(macOS)
- Button("Content Filtering") {
+ Button("Sensitive Content") {
isShowingContentFiltering.toggle()
}
.sheet(isPresented: $isShowingContentFiltering) {
@@ -44,7 +47,7 @@ struct SettingsSectionThumbnailsView: View {
.trailingFrame()
#else
NavigationLink(destination: SettingsSectionContentRatingsView()) {
- Text("Content Filtering")
+ Text("Sensitive Content")
}
#endif
}
@@ -62,6 +65,6 @@ struct SettingsSectionThumbnailsView: View {
#Preview {
NavigationStack {
SettingsSectionThumbnailsView()
- .environmentObject(SettingsManager())
+ .environment(SettingsManager())
}
}
diff --git a/Sora/Views/Settings/SettingsView.swift b/Sora/Views/Settings/SettingsView.swift
index 814a303..ae5a620 100644
--- a/Sora/Views/Settings/SettingsView.swift
+++ b/Sora/Views/Settings/SettingsView.swift
@@ -4,45 +4,45 @@ struct SettingsView: View {
var body: some View {
NavigationStack {
Form {
- Section(header: Text("Appearance and Behavior")) {
+ Section("Browsing") {
NavigationLink(destination: SettingsSectionProviderView()) {
- Text("Provider Settings")
+ Text("Source")
}
NavigationLink(destination: SettingsSectionThumbnailsView()) {
- Text("Thumbnail Settings")
+ Text("Thumbnails")
}
NavigationLink(destination: SettingsSectionDetailsView()) {
- Text("Details Settings")
+ Text("Viewer")
}
}
- Section(header: Text("Search")) {
+ Section("Search") {
SettingsSectionSearchView()
}
- Section(header: Text("Collections")) {
+ Section("Library") {
NavigationLink(destination: SettingsCollectionsListView()) {
- Text("Manage Collections")
+ Text("Collections")
}
}
- Section(header: Text("Settings")) {
+ Section("Storage & Sync") {
SettingsSectionSettingsView()
}
- Section(header: Text("Import and Export")) {
+ Section("Bookmarks") {
SettingsSectionImportExportView()
}
#if DEBUG
- Section(header: Text("Debug")) {
+ Section("Debug") {
SettingsSectionDebugView()
}
#endif
- Section(header: Text("Credits")) {
+ Section("About") {
SettingsSectionCreditsView()
}
}
@@ -56,6 +56,6 @@ struct SettingsView: View {
#Preview {
SettingsView()
- .environmentObject(SettingsManager())
+ .environment(SettingsManager())
.environmentObject(BooruManager(.yandere))
}