diff options
| author | Fuwn <[email protected]> | 2025-06-16 09:37:27 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-06-16 09:37:27 -0700 |
| commit | d292e582910c990cae8481c966366b34dc7041b3 (patch) | |
| tree | fe11aa49917d793e363094644d6c1662886ece88 | |
| parent | feat: Development commit (diff) | |
| download | sora-testing-d292e582910c990cae8481c966366b34dc7041b3.tar.xz sora-testing-d292e582910c990cae8481c966366b34dc7041b3.zip | |
feat: Development commit
| -rw-r--r-- | Localizable.xcstrings | 6 | ||||
| -rw-r--r-- | Sora/Data/Booru/BooruManager.swift | 17 | ||||
| -rw-r--r-- | Sora/Data/Booru/Provider/BooruProviderCredentials.swift | 40 | ||||
| -rw-r--r-- | Sora/Data/Settings/SettingsManager.swift | 42 | ||||
| -rw-r--r-- | Sora/Views/MainView.swift | 7 | ||||
| -rw-r--r-- | Sora/Views/Settings/Section/SettingsProviderView.swift | 52 |
6 files changed, 160 insertions, 4 deletions
diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 590727c..c3f38df 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -25,6 +25,9 @@ "All" : { }, + "API Key" : { + + }, "Are you sure you want to remove all bookmarks? This action cannot be undone." : { }, @@ -412,6 +415,9 @@ }, "Uncategorised" : { + }, + "User ID" : { + } }, "version" : "1.0" diff --git a/Sora/Data/Booru/BooruManager.swift b/Sora/Data/Booru/BooruManager.swift index 12d96f0..ce61644 100644 --- a/Sora/Data/Booru/BooruManager.swift +++ b/Sora/Data/Booru/BooruManager.swift @@ -28,6 +28,7 @@ class BooruManager: ObservableObject { .appendingPathComponent("\(BooruProvider.safebooru.asFileNameComponent)_tags.json") private let pageCache = NSCache<NSString, BooruPageCacheEntry>() // swiftlint:disable:this legacy_objc_type private let cacheDuration: TimeInterval = 300 + private let credentials: BooruProviderCredentials? // MARK: - Computed Properties var tags: [String] { @@ -42,10 +43,11 @@ class BooruManager: ObservableObject { var canGoForwardInHistory: Bool { historyIndex < searchHistory.count - 1 } // MARK: - Initialisation - init(_ provider: BooruProvider) { + init(_ provider: BooruProvider, credentials: BooruProviderCredentials? = nil) { self.provider = provider self.flavor = BooruProviderFlavor(provider: provider) self.domain = provider.domain + self.credentials = credentials pageCache.countLimit = 50 pageCache.totalCostLimit = 50 * 1_024 * 1_024 @@ -226,9 +228,18 @@ class BooruManager: ObservableObject { return URL(string: "https://\(domain)/post.xml?page=\(page)&limit=\(limit)&tags=\(tagString)") case .gelbooru: + var urlString = + "https://\(domain)/index.php?page=dapi&s=post&q=index&pid=\(page)&limit=\(limit)&tags=\(tagString)" + + if let validCredentials = credentials, + !validCredentials.apiKey.isEmpty, + validCredentials.userID != 0 + { + urlString += "&api_key=\(validCredentials.apiKey)&user_id=\(validCredentials.userID)" + } + return URL( - string: - "https://\(domain)/index.php?page=dapi&s=post&q=index&pid=\(page)&limit=\(limit)&tags=\(tagString)" + string: urlString ) } } diff --git a/Sora/Data/Booru/Provider/BooruProviderCredentials.swift b/Sora/Data/Booru/Provider/BooruProviderCredentials.swift new file mode 100644 index 0000000..898201a --- /dev/null +++ b/Sora/Data/Booru/Provider/BooruProviderCredentials.swift @@ -0,0 +1,40 @@ +import Foundation + +struct BooruProviderCredentials: Codable, Identifiable, Equatable { + let id: UUID + let provider: BooruProvider + var apiKey: String + var userID: Int + + init(provider: BooruProvider, apiKey: String, userID: Int, id: UUID = UUID()) { + self.id = id + self.provider = provider + self.apiKey = apiKey + self.userID = userID + } + + static func from( + // swiftlint:disable:next large_tuple + _ rawCredentials: [(provider: BooruProvider, apiKey: String, userID: Int)], + existingCredentials: [Self] + ) -> [Self] { + rawCredentials.map { credentials in + if let existingKey = existingCredentials.first( + where: { $0.provider == credentials.provider } + ) { + return Self( + provider: credentials.provider, + apiKey: credentials.apiKey, + userID: credentials.userID, + id: existingKey.id + ) + } + + return Self( + provider: credentials.provider, + apiKey: credentials.apiKey, + userID: credentials.userID + ) + } + } +} diff --git a/Sora/Data/Settings/SettingsManager.swift b/Sora/Data/Settings/SettingsManager.swift index 98ea00c..c824cff 100644 --- a/Sora/Data/Settings/SettingsManager.swift +++ b/Sora/Data/Settings/SettingsManager.swift @@ -60,6 +60,9 @@ class SettingsManager: ObservableObject { // swiftlint:disable:this type_body_l @AppStorage("folders") private var foldersData = Data() + @AppStorage("providerCredentials") + private var providerCredentialsData = Data() + // MARK: - Computed Properties var bookmarks: [SettingsBookmark] { get { @@ -167,6 +170,45 @@ class SettingsManager: ObservableObject { // swiftlint:disable:this type_body_l } } + // MARK: Provider Credentials + var providerCredentials: [BooruProviderCredentials] { + get { + syncableData( + key: "providerAPIKeys", + localData: providerCredentialsData, + sort: { $0 }, + identifier: { $0.id } + ) + } + + set { + let existingCredentials: [BooruProviderCredentials] = + Self.decode([BooruProviderCredentials].self, from: providerCredentialsData) ?? [] + let rawCredentials = newValue.map { credentials in + (provider: credentials.provider, apiKey: credentials.apiKey, userID: credentials.userID) + } + let mergedCredentials = BooruProviderCredentials.from( + rawCredentials, existingCredentials: existingCredentials + ) + + syncableData( + key: "providerAPIKeys", + localData: $providerCredentialsData, + newValue: mergedCredentials, + sort: { $0 }, + identifier: { $0.id } + ) + } + } + + var providerAPIKeys: [BooruProvider: String] { + Dictionary(uniqueKeysWithValues: providerCredentials.map { ($0.provider, $0.apiKey) }) + } + + var providerUserIDs: [BooruProvider: Int] { + Dictionary(uniqueKeysWithValues: providerCredentials.map { ($0.provider, $0.userID) }) + } + // MARK: - Initialisation init() { syncObservation = NotificationCenter.default.addObserver( diff --git a/Sora/Views/MainView.swift b/Sora/Views/MainView.swift index c3a8c66..81cede8 100644 --- a/Sora/Views/MainView.swift +++ b/Sora/Views/MainView.swift @@ -21,6 +21,7 @@ struct MainView: View { manager.selectedPost = nil } .onAppear(perform: initializeManager) + .onChange(of: settings.providerCredentials) { initializeManager() } } @ViewBuilder private var tabViewContent: some View { @@ -90,7 +91,11 @@ struct MainView: View { } private func initializeManager() { - manager = BooruManager(settings.preferredBooru) + manager = BooruManager( + settings.preferredBooru, + credentials: settings.providerCredentials + .first { $0.provider == settings.preferredBooru } + ) refreshTags() diff --git a/Sora/Views/Settings/Section/SettingsProviderView.swift b/Sora/Views/Settings/Section/SettingsProviderView.swift index 606fe64..82834b9 100644 --- a/Sora/Views/Settings/Section/SettingsProviderView.swift +++ b/Sora/Views/Settings/Section/SettingsProviderView.swift @@ -20,6 +20,33 @@ struct SettingsProviderView: View { } } + SecureField( + "API Key", + text: Binding( + get: { settings.providerAPIKeys[settings.preferredBooru] ?? "" }, + set: { updateCredentials(apiKey: $0) } + ) + ) + .autocorrectionDisabled(true) + + TextField( + "User ID", + text: Binding( + get: { + String(settings.providerUserIDs[settings.preferredBooru] ?? 0) + }, + set: { newValue in + let userID = Int(newValue) ?? 0 + + updateCredentials(userID: userID) + } + ) + ) + .autocorrectionDisabled(true) + #if os(iOS) + .keyboardType(.numberPad) + #endif + Button("Add Custom Provider") { showingCustomBooruSheet = true } @@ -145,6 +172,31 @@ struct SettingsProviderView: View { return true } + private func updateCredentials(apiKey: String? = nil, userID: Int? = nil) { + var allCredentials = settings.providerCredentials + + if let index = allCredentials.firstIndex(where: { $0.provider == settings.preferredBooru }) { + let credentials = allCredentials[index] + + allCredentials[index] = BooruProviderCredentials( + provider: credentials.provider, + apiKey: apiKey ?? credentials.apiKey, + userID: userID ?? credentials.userID, + id: credentials.id + ) + } else { + allCredentials.append( + BooruProviderCredentials( + provider: settings.preferredBooru, + apiKey: apiKey ?? "", + userID: userID ?? 0 + ) + ) + } + + settings.providerCredentials = allCredentials + } + private func removeCustomProviderButtonContent(_ provider: BooruProviderCustom) -> some View { Button("Remove Custom Provider") { settings.customProviders.removeAll { $0.id == provider.id } |