summaryrefslogtreecommitdiff
path: root/Sora/Views/Settings/Section
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-03-05 03:53:34 -0800
committerFuwn <[email protected]>2025-03-05 03:53:34 -0800
commitd10f51d93e35c11f883df536b13b7bb6382d463c (patch)
tree59b786a352ad3adb16a61ca1d858e5e75807db90 /Sora/Views/Settings/Section
parentfeat: Development commit (diff)
downloadsora-testing-d10f51d93e35c11f883df536b13b7bb6382d463c.tar.xz
sora-testing-d10f51d93e35c11f883df536b13b7bb6382d463c.zip
feat: Development commit
Diffstat (limited to 'Sora/Views/Settings/Section')
-rw-r--r--Sora/Views/Settings/Section/SettingsProviderView.swift148
1 files changed, 145 insertions, 3 deletions
diff --git a/Sora/Views/Settings/Section/SettingsProviderView.swift b/Sora/Views/Settings/Section/SettingsProviderView.swift
index b561bc3..eb3fb1b 100644
--- a/Sora/Views/Settings/Section/SettingsProviderView.swift
+++ b/Sora/Views/Settings/Section/SettingsProviderView.swift
@@ -2,12 +2,154 @@ import SwiftUI
struct SettingsProviderView: View {
@EnvironmentObject var settings: SettingsManager
+ @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 {
- Picker("Provider", selection: $settings.preferredBooru) {
- ForEach(BooruProvider.allCases, id: \.self) { type in
- Text(type.rawValue).tag(type)
+ Group {
+ Picker("Provider", selection: $settings.preferredBooru) {
+ ForEach(BooruProvider.allCases, id: \.self) { type in
+ Text(type.rawValue).tag(type)
+ }
+
+ ForEach(settings.customProviders, id: \.id) { provider in
+ Text(provider.domain)
+ .tag(BooruProvider.custom(provider))
+ }
+ }
+
+ Button("Add Custom Provider") {
+ showingCustomBooruSheet = true
+ }
+ .sheet(isPresented: $showingCustomBooruSheet) {
+ NavigationView {
+ Form {
+ TextField("Domain", text: $newDomain)
+ .autocorrectionDisabled(true)
+
+ Picker("Flavor", selection: $newFlavor) {
+ ForEach(BooruProviderFlavor.allCases, id: \.self) { flavor in
+ Text(flavor.rawValue).tag(flavor)
+ }
+ }
+ }
+ .toolbar {
+ ToolbarItem(placement: .cancellationAction) {
+ Button("Cancel") {
+ showingCustomBooruSheet = false
+ resetForm()
+ }
+ }
+
+ ToolbarItem(placement: .confirmationAction) {
+ Button("Add") {
+ if validateDomain() {
+ addCustomProvider()
+ showingCustomBooruSheet = false
+ }
+ }
+ .disabled(newDomain.isEmpty || domainError != nil)
+ }
+ }
+ #if os(macOS)
+ .navigationTitle("Add Custom Provider")
+ #endif
+ }
+ }
+
+ if case .custom(let provider) = settings.preferredBooru {
+ removeCustomProviderButtonContent(provider)
+ }
+
+ Button("Remove All Custom Providers") {
+ if case .custom = settings.preferredBooru {
+ settings.preferredBooru = .safebooru
+ }
+
+ if !settings.customProviders.isEmpty {
+ settings.customProviders.removeAll()
+ }
}
}
+ .alert(
+ "Invalid Domain",
+ isPresented: Binding(
+ get: { domainError != nil },
+ set: { if !$0 { domainError = nil } }
+ )
+ ) {
+ Button("OK", role: .cancel) { () }
+ } message: {
+ Text(domainError ?? "An unknown error occurred while validating the domain.")
+ }
+ }
+
+ private func addCustomProvider() {
+ let customProvider = BooruProviderCustom(
+ domain: newDomain.lowercased(),
+ flavor: newFlavor
+ )
+
+ settings.customProviders.append(customProvider)
+ resetForm()
+ }
+
+ private func resetForm() {
+ newDomain = ""
+ newFlavor = .danbooru
+ domainError = nil
+ }
+
+ @discardableResult
+ private func validateDomain() -> Bool {
+ guard !newDomain.isEmpty else {
+ domainError = nil
+
+ return false
+ }
+
+ let domain = newDomain.lowercased().trimmingCharacters(in: .whitespaces)
+ let domainRegex =
+ "^[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."
+
+ return false
+ }
+
+ guard !domain.contains("://"), !domain.contains("/"), !domain.contains("?") else {
+ domainError = "Only enter the domain name—leave out '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."
+
+ return false
+ }
+
+ 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."
+
+ return false
+ }
+
+ domainError = nil
+
+ return true
+ }
+
+ private func removeCustomProviderButtonContent(_ provider: BooruProviderCustom) -> some View {
+ Button("Remove Custom Provider") {
+ settings.customProviders.removeAll { $0.id == provider.id }
+ settings.preferredBooru = .safebooru
+ }
+ .disabled(!settings.customProviders.contains { $0.id == provider.id })
}
}