summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Sora/Data/Booru/BooruManager.swift14
-rw-r--r--Sora/Data/Booru/Provider/BooruProviderCredentials.swift36
-rw-r--r--Sora/Data/Settings/SettingsManager.swift21
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionProviderView.swift28
-rw-r--r--SoraTests/ViewDerivedDataTests.swift31
5 files changed, 118 insertions, 12 deletions
diff --git a/Sora/Data/Booru/BooruManager.swift b/Sora/Data/Booru/BooruManager.swift
index 562bb1e..2459f2d 100644
--- a/Sora/Data/Booru/BooruManager.swift
+++ b/Sora/Data/Booru/BooruManager.swift
@@ -295,10 +295,22 @@ class BooruManager: ObservableObject { // swiftlint:disable:this type_body_leng
components.scheme = "https"
components.host = domain
components.path = "/posts.json"
- components.queryItems = [
+
+ var queryItems = [
URLQueryItem(name: "page", value: String(page)),
URLQueryItem(name: "tags", value: tagString),
]
+
+ if let validCredentials = credentials {
+ let login = validCredentials.login.trimmingCharacters(in: .whitespacesAndNewlines)
+
+ if !validCredentials.apiKey.isEmpty, !login.isEmpty {
+ queryItems.append(URLQueryItem(name: "login", value: login))
+ queryItems.append(URLQueryItem(name: "api_key", value: validCredentials.apiKey))
+ }
+ }
+
+ components.queryItems = queryItems
url = components.url
case .moebooru:
diff --git a/Sora/Data/Booru/Provider/BooruProviderCredentials.swift b/Sora/Data/Booru/Provider/BooruProviderCredentials.swift
index 898201a..6733405 100644
--- a/Sora/Data/Booru/Provider/BooruProviderCredentials.swift
+++ b/Sora/Data/Booru/Provider/BooruProviderCredentials.swift
@@ -5,17 +5,45 @@ struct BooruProviderCredentials: Codable, Identifiable, Equatable {
let provider: BooruProvider
var apiKey: String
var userID: Int
+ var login: String
- init(provider: BooruProvider, apiKey: String, userID: Int, id: UUID = UUID()) {
+ init(
+ provider: BooruProvider,
+ apiKey: String,
+ userID: Int,
+ login: String = "",
+ id: UUID = UUID()
+ ) {
self.id = id
self.provider = provider
self.apiKey = apiKey
self.userID = userID
+ self.login = login
+ }
+
+ // swiftlint:disable explicit_enum_raw_value
+ private enum CodingKeys: String, CodingKey {
+ case id
+ case provider
+ case apiKey
+ case userID
+ case login
+ }
+ // swiftlint:enable explicit_enum_raw_value
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+
+ id = try container.decodeIfPresent(UUID.self, forKey: .id) ?? UUID()
+ provider = try container.decode(BooruProvider.self, forKey: .provider)
+ apiKey = try container.decodeIfPresent(String.self, forKey: .apiKey) ?? ""
+ userID = try container.decodeIfPresent(Int.self, forKey: .userID) ?? 0
+ login = try container.decodeIfPresent(String.self, forKey: .login) ?? ""
}
static func from(
// swiftlint:disable:next large_tuple
- _ rawCredentials: [(provider: BooruProvider, apiKey: String, userID: Int)],
+ _ rawCredentials: [(provider: BooruProvider, apiKey: String, userID: Int, login: String)],
existingCredentials: [Self]
) -> [Self] {
rawCredentials.map { credentials in
@@ -26,6 +54,7 @@ struct BooruProviderCredentials: Codable, Identifiable, Equatable {
provider: credentials.provider,
apiKey: credentials.apiKey,
userID: credentials.userID,
+ login: credentials.login,
id: existingKey.id
)
}
@@ -33,7 +62,8 @@ struct BooruProviderCredentials: Codable, Identifiable, Equatable {
return Self(
provider: credentials.provider,
apiKey: credentials.apiKey,
- userID: credentials.userID
+ userID: credentials.userID,
+ login: credentials.login
)
}
}
diff --git a/Sora/Data/Settings/SettingsManager.swift b/Sora/Data/Settings/SettingsManager.swift
index d7bfc44..ddeb1e4 100644
--- a/Sora/Data/Settings/SettingsManager.swift
+++ b/Sora/Data/Settings/SettingsManager.swift
@@ -289,7 +289,12 @@ class SettingsManager: ObservableObject { // swiftlint:disable:this type_body_l
let existingCredentials: [BooruProviderCredentials] =
Self.decode([BooruProviderCredentials].self, from: providerCredentialsData) ?? []
let rawCredentials = newValue.map { credentials in
- (provider: credentials.provider, apiKey: credentials.apiKey, userID: credentials.userID)
+ (
+ provider: credentials.provider,
+ apiKey: credentials.apiKey,
+ userID: credentials.userID,
+ login: credentials.login
+ )
}
let mergedCredentials = BooruProviderCredentials.from(
rawCredentials, existingCredentials: existingCredentials
@@ -335,6 +340,13 @@ class SettingsManager: ObservableObject { // swiftlint:disable:this type_body_l
)
}
+ var providerLogins: [BooruProvider: String] {
+ mergedCredentialValues(
+ extract: { $0.login },
+ isDefault: { $0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
+ )
+ }
+
// MARK: - Initialisation
init() {
syncObservation = NotificationCenter.default.addObserver(
@@ -456,7 +468,12 @@ class SettingsManager: ObservableObject { // swiftlint:disable:this type_body_l
let existingCredentials: [BooruProviderCredentials] =
Self.decode([BooruProviderCredentials].self, from: providerCredentialsData) ?? []
let rawCredentials = newValue.map { credentials in
- (provider: credentials.provider, apiKey: credentials.apiKey, userID: credentials.userID)
+ (
+ provider: credentials.provider,
+ apiKey: credentials.apiKey,
+ userID: credentials.userID,
+ login: credentials.login
+ )
}
let mergedCredentials = BooruProviderCredentials.from(
rawCredentials, existingCredentials: existingCredentials
diff --git a/Sora/Views/Settings/Section/SettingsSectionProviderView.swift b/Sora/Views/Settings/Section/SettingsSectionProviderView.swift
index 02c5b02..172ddc6 100644
--- a/Sora/Views/Settings/Section/SettingsSectionProviderView.swift
+++ b/Sora/Views/Settings/Section/SettingsSectionProviderView.swift
@@ -33,12 +33,24 @@ struct SettingsSectionProviderView: View {
.autocorrectionDisabled(true)
TextField(
- "User ID",
+ isDanbooruProvider ? "Login" : "User ID",
text: Binding(
get: {
- String(settings.providerUserIDs[settings.preferredBooru] ?? 0)
+ if isDanbooruProvider {
+ return settings.providerLogins[settings.preferredBooru] ?? ""
+ }
+
+ let userID = settings.providerUserIDs[settings.preferredBooru] ?? 0
+
+ return userID == 0 ? "" : String(userID)
},
set: { newValue in
+ if isDanbooruProvider {
+ updateCredentials(login: newValue.trimmingCharacters(in: .whitespacesAndNewlines))
+
+ return
+ }
+
let userID = Int(newValue) ?? 0
updateCredentials(userID: userID)
@@ -47,7 +59,7 @@ struct SettingsSectionProviderView: View {
)
.autocorrectionDisabled(true)
#if os(iOS)
- .keyboardType(.numberPad)
+ .keyboardType(isDanbooruProvider ? .default : .numberPad)
#endif
}
@@ -197,7 +209,11 @@ struct SettingsSectionProviderView: View {
return true
}
- private func updateCredentials(apiKey: String? = nil, userID: Int? = nil) {
+ private var isDanbooruProvider: Bool {
+ BooruProviderFlavor(provider: settings.preferredBooru) == .danbooru
+ }
+
+ private func updateCredentials(apiKey: String? = nil, userID: Int? = nil, login: String? = nil) {
var allCredentials = settings.providerCredentials
if let index = allCredentials.firstIndex(where: { $0.provider == settings.preferredBooru }) {
@@ -207,6 +223,7 @@ struct SettingsSectionProviderView: View {
provider: credentials.provider,
apiKey: apiKey ?? credentials.apiKey,
userID: userID ?? credentials.userID,
+ login: login ?? credentials.login,
id: credentials.id
)
} else {
@@ -214,7 +231,8 @@ struct SettingsSectionProviderView: View {
BooruProviderCredentials(
provider: settings.preferredBooru,
apiKey: apiKey ?? "",
- userID: userID ?? 0
+ userID: userID ?? 0,
+ login: login ?? ""
)
)
}
diff --git a/SoraTests/ViewDerivedDataTests.swift b/SoraTests/ViewDerivedDataTests.swift
index bc3a998..ed83654 100644
--- a/SoraTests/ViewDerivedDataTests.swift
+++ b/SoraTests/ViewDerivedDataTests.swift
@@ -1,6 +1,6 @@
import XCTest
-final class ViewDerivedDataTests: XCTestCase {
+final class ViewDerivedDataTests: XCTestCase { // swiftlint:disable:this type_body_length
func testGenericListViewDerivedCollectionsAreReferencedOncePerRenderPass() throws {
let source = try loadSource(at: "Sora/Views/Generic/GenericListView.swift")
let normalizedSource = strippingCommentsAndStrings(from: source)
@@ -267,6 +267,35 @@ final class ViewDerivedDataTests: XCTestCase {
)
}
+ func testBooruManagerDanbooruPostsUseQueryParamAuthentication() throws {
+ let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
+ let urlBuilderSection = try extractFunction(
+ named: "func url(forPosts page: Int, limit: Int, tags: [String]) -> URL?",
+ from: source
+ )
+ let danbooruLoginQueryCount = tokenCount(
+ matching: #"case\s+\.danbooru[\s\S]*URLQueryItem\(name:\s*"login""#,
+ in: urlBuilderSection
+ )
+ let danbooruAPIKeyQueryCount = tokenCount(
+ matching: #"case\s+\.danbooru[\s\S]*URLQueryItem\(name:\s*"api_key""#,
+ in: urlBuilderSection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ danbooruLoginQueryCount,
+ 0,
+ "Danbooru requests should authenticate with a `login` query parameter."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ danbooruAPIKeyQueryCount,
+ 0,
+ "Danbooru requests should authenticate with an `api_key` query parameter."
+ )
+ }
+
func testThumbnailGridViewAvoidsDirectColumnArrayIndexing() throws {
let source = try loadSource(at: "Sora/Views/Shared/ThumbnailGridView.swift")
let normalizedSource = strippingCommentsAndStrings(from: source)