diff options
| author | Fuwn <[email protected]> | 2025-03-05 03:53:34 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-03-05 03:53:34 -0800 |
| commit | d10f51d93e35c11f883df536b13b7bb6382d463c (patch) | |
| tree | 59b786a352ad3adb16a61ca1d858e5e75807db90 /Sora/Views/Settings/Section | |
| parent | feat: Development commit (diff) | |
| download | sora-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.swift | 148 |
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 }) } } |