diff --git a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Block.swift b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Block.swift index d138d0401..c53428fbb 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Block.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Block.swift @@ -25,18 +25,27 @@ extension APIService { @discardableResult public func getBlocked( authenticationBox: MastodonAuthenticationBox + ) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> { + try await _getBlocked(sinceID: nil, limit: 40, authenticationBox: authenticationBox) + } + + private func _getBlocked( + sinceID: Mastodon.Entity.Status.ID?, + limit: Int, + authenticationBox: MastodonAuthenticationBox ) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> { let managedObjectContext = backgroundManagedObjectContext - let response = try await Mastodon.API.Account.blocks( session: session, domain: authenticationBox.domain, + sinceID: sinceID, + limit: limit, authorization: authenticationBox.userAuthorization ).singleOutput() let userIDs = response.value.map { $0.id } - let predicate = NSPredicate(format: "%K IN %@", #keyPath(MastodonUser.id), userIDs) - + let predicate = MastodonUser.predicate(domain: authenticationBox.domain, ids: userIDs) + let fetchRequest = MastodonUser.fetchRequest() fetchRequest.predicate = predicate fetchRequest.includesPropertyValues = false @@ -49,7 +58,12 @@ extension APIService { } } - return response + /// only try to paginate if retrieved userIDs count is larger than the set limit and if we get a prev linkId that's different than the currently used one + guard userIDs.count == limit, let prevSinceId = response.link?.linkIDs[.linkPrev]?.sinceId, sinceID != prevSinceId else { + return response + } + + return try await _getBlocked(sinceID: prevSinceId, limit: limit, authenticationBox: authenticationBox) } public func toggleBlock( diff --git a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Mute.swift b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Mute.swift index 0e00c5e0b..42178d573 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Mute.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/API/APIService+Mute.swift @@ -24,18 +24,27 @@ extension APIService { @discardableResult public func getMutes( authenticationBox: MastodonAuthenticationBox + ) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> { + try await _getMutes(sinceID: nil, limit: 40, authenticationBox: authenticationBox) + } + + private func _getMutes( + sinceID: Mastodon.Entity.Status.ID?, + limit: Int, + authenticationBox: MastodonAuthenticationBox ) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> { let managedObjectContext = backgroundManagedObjectContext - let response = try await Mastodon.API.Account.mutes( session: session, domain: authenticationBox.domain, + sinceID: sinceID, + limit: limit, authorization: authenticationBox.userAuthorization ).singleOutput() let userIDs = response.value.map { $0.id } let predicate = MastodonUser.predicate(domain: authenticationBox.domain, ids: userIDs) - + let fetchRequest = MastodonUser.fetchRequest() fetchRequest.predicate = predicate fetchRequest.includesPropertyValues = false @@ -48,7 +57,12 @@ extension APIService { } } - return response + /// only try to paginate if retrieved userIDs count is larger than the set limit and if we get a prev linkId that's different than the currently used one + guard userIDs.count == limit, let prevSinceId = response.link?.linkIDs[.linkPrev]?.sinceId, sinceID != prevSinceId else { + return response + } + + return try await _getMutes(sinceID: prevSinceId, limit: limit, authenticationBox: authenticationBox) } public func toggleMute( diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Friendship.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Friendship.swift index 2619891a7..e191c3200 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Friendship.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Friendship.swift @@ -239,11 +239,13 @@ public extension Mastodon.API.Account { static func blocks( session: URLSession, domain: String, + sinceID: Mastodon.Entity.Status.ID? = nil, + limit: Int, authorization: Mastodon.API.OAuth.Authorization ) -> AnyPublisher, Error> { let request = Mastodon.API.get( url: blocksEndpointURL(domain: domain), - query: BlocksQuery(), + query: BlocksQuery(sinceID: sinceID, limit: limit), authorization: authorization ) return session.dataTaskPublisher(for: request) @@ -255,8 +257,23 @@ public extension Mastodon.API.Account { } private struct BlocksQuery: GetQuery { + private let sinceID: Mastodon.Entity.Status.ID? + private let limit: Int? + + public init( + sinceID: Mastodon.Entity.Status.ID?, + limit: Int? + ) { + self.sinceID = sinceID + self.limit = limit + } + var queryItems: [URLQueryItem]? { - nil + var items: [URLQueryItem] = [] + sinceID.flatMap { items.append(URLQueryItem(name: "since_id", value: $0)) } + limit.flatMap { items.append(URLQueryItem(name: "limit", value: String($0))) } + guard !items.isEmpty else { return nil } + return items } } @@ -490,11 +507,13 @@ extension Mastodon.API.Account { public static func mutes( session: URLSession, domain: String, + sinceID: Mastodon.Entity.Status.ID? = nil, + limit: Int?, authorization: Mastodon.API.OAuth.Authorization ) -> AnyPublisher, Error> { let request = Mastodon.API.get( url: mutesEndpointURL(domain: domain), - query: MutesQuery(), + query: MutesQuery(sinceID: sinceID, limit: limit), authorization: authorization ) return session.dataTaskPublisher(for: request) @@ -505,8 +524,23 @@ extension Mastodon.API.Account { .eraseToAnyPublisher() struct MutesQuery: GetQuery { + private let sinceID: Mastodon.Entity.Status.ID? + private let limit: Int? + + public init( + sinceID: Mastodon.Entity.Status.ID?, + limit: Int? + ) { + self.sinceID = sinceID + self.limit = limit + } + var queryItems: [URLQueryItem]? { - nil + var items: [URLQueryItem] = [] + sinceID.flatMap { items.append(URLQueryItem(name: "since_id", value: $0)) } + limit.flatMap { items.append(URLQueryItem(name: "limit", value: String($0))) } + guard !items.isEmpty else { return nil } + return items } } } diff --git a/MastodonSDK/Sources/MastodonSDK/Response/Mastodon+Response+Content.swift b/MastodonSDK/Sources/MastodonSDK/Response/Mastodon+Response+Content.swift index 6cf95752b..aa156ac16 100644 --- a/MastodonSDK/Sources/MastodonSDK/Response/Mastodon+Response+Content.swift +++ b/MastodonSDK/Sources/MastodonSDK/Response/Mastodon+Response+Content.swift @@ -106,6 +106,7 @@ extension Mastodon.Response { public struct Link { public let maxID: Mastodon.Entity.Status.ID? public let minID: Mastodon.Entity.Status.ID? + public let linkIDs: [String: Mastodon.Entity.Status.ID] public let offset: Int? init(link: String) { @@ -135,6 +136,33 @@ extension Mastodon.Response { let offset = link[range] return Int(offset) }() + self.linkIDs = { + var linkIDs = [String: Mastodon.Entity.Status.ID]() + let links = link.components(separatedBy: ", ") + for link in links { + guard let regex = try? NSRegularExpression(pattern: "<(.*)>; *rel=\"(.*)\"") else { return [:] } + let results = regex.matches(in: link, options: [], range: NSRange(link.startIndex..