mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-22 16:31:00 +00:00
Handle HTTP Link header for favourites and followers/following
This commit is contained in:
parent
a90a63bf1b
commit
8de2a8192b
5 changed files with 64 additions and 16 deletions
|
@ -47,6 +47,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
@Published var title: String = ""
|
||||
@Published var relationship: Relationshionship?
|
||||
@Published var favourites: [Status] = []
|
||||
private var favouritesNextPage: LinkHandler?
|
||||
@Published var followedTags: [Tag] = []
|
||||
@Published var featuredTags: [FeaturedTag] = []
|
||||
@Published var fields: [Account.Field] = []
|
||||
|
@ -108,7 +109,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
tabState = .statuses(statusesState: .loading)
|
||||
statuses = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: nil, tag: nil))
|
||||
if isCurrentUser {
|
||||
favourites = try await client.get(endpoint: Accounts.favourites)
|
||||
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
||||
}
|
||||
reloadTabState()
|
||||
} catch {
|
||||
|
@ -126,7 +127,13 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
let newStatuses: [Status] = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: lastId, tag: nil))
|
||||
statuses.append(contentsOf: newStatuses)
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .hasNextPage))
|
||||
case .favourites, .followedTags:
|
||||
case .favourites:
|
||||
guard let nextPageId = favouritesNextPage?.maxId else { return }
|
||||
let newFavourites: [Status]
|
||||
(newFavourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nextPageId))
|
||||
favourites.append(contentsOf: newFavourites)
|
||||
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .hasNextPage))
|
||||
case .followedTags:
|
||||
break
|
||||
}
|
||||
} catch {
|
||||
|
@ -157,7 +164,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
case .statuses:
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .hasNextPage))
|
||||
case .favourites:
|
||||
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .none))
|
||||
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .hasNextPage))
|
||||
case .followedTags:
|
||||
tabState = .followedTags(tags: followedTags)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ class AccountsListViewModel: ObservableObject {
|
|||
|
||||
@Published var state = State.loading
|
||||
|
||||
private var nextPageId: String?
|
||||
|
||||
init(accountId: String, mode: AccountsListMode) {
|
||||
self.accountId = accountId
|
||||
self.mode = mode
|
||||
|
@ -38,14 +40,16 @@ class AccountsListViewModel: ObservableObject {
|
|||
guard let client else { return }
|
||||
do {
|
||||
state = .loading
|
||||
let link: LinkHandler?
|
||||
switch mode {
|
||||
case .followers:
|
||||
accounts = try await client.get(endpoint: Accounts.followers(id: accountId,
|
||||
sinceId: nil))
|
||||
(accounts, link) = try await client.getWithLink(endpoint: Accounts.followers(id: accountId,
|
||||
sinceId: nil))
|
||||
case .following:
|
||||
accounts = try await client.get(endpoint: Accounts.following(id: accountId,
|
||||
sinceId: nil))
|
||||
(accounts, link) = try await client.getWithLink(endpoint: Accounts.following(id: accountId,
|
||||
sinceId: nil))
|
||||
}
|
||||
nextPageId = link?.maxId
|
||||
relationships = try await client.get(endpoint:
|
||||
Accounts.relationships(ids: accounts.map{ $0.id }))
|
||||
state = .display(accounts: accounts,
|
||||
|
@ -55,23 +59,25 @@ class AccountsListViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
func fetchNextPage() async {
|
||||
guard let client else { return }
|
||||
guard let client, let nextPageId else { return }
|
||||
do {
|
||||
state = .display(accounts: accounts, relationships: relationships, nextPageState: .loadingNextPage)
|
||||
let newAccounts: [Account]
|
||||
let link: LinkHandler?
|
||||
switch mode {
|
||||
case .followers:
|
||||
newAccounts = try await client.get(endpoint: Accounts.followers(id: accountId,
|
||||
sinceId: accounts.last?.id))
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Accounts.followers(id: accountId,
|
||||
sinceId: nextPageId))
|
||||
case .following:
|
||||
newAccounts = try await client.get(endpoint: Accounts.following(id: accountId,
|
||||
sinceId: accounts.last?.id))
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Accounts.following(id: accountId,
|
||||
sinceId: nextPageId))
|
||||
}
|
||||
accounts.append(contentsOf: newAccounts)
|
||||
let newRelationships: [Relationshionship] =
|
||||
try await client.get(endpoint: Accounts.relationships(ids: newAccounts.map{ $0.id }))
|
||||
|
||||
relationships.append(contentsOf: newRelationships)
|
||||
self.nextPageId = link?.maxId
|
||||
state = .display(accounts: accounts,
|
||||
relationships: relationships,
|
||||
nextPageState: .hasNextPage)
|
||||
|
|
|
@ -61,15 +61,29 @@ public class Client: ObservableObject, Equatable {
|
|||
}
|
||||
return request
|
||||
}
|
||||
|
||||
private func makeGet(endpoint: Endpoint) -> URLRequest {
|
||||
let url = makeURL(endpoint: endpoint)
|
||||
return makeURLRequest(url: url, httpMethod: "GET")
|
||||
}
|
||||
|
||||
public func get<Entity: Decodable>(endpoint: Endpoint) async throws -> Entity {
|
||||
let url = makeURL(endpoint: endpoint)
|
||||
let request = makeURLRequest(url: url, httpMethod: "GET")
|
||||
let (data, httpResponse) = try await urlSession.data(for: request)
|
||||
let (data, httpResponse) = try await urlSession.data(for: makeGet(endpoint: endpoint))
|
||||
logResponseOnError(httpResponse: httpResponse, data: data)
|
||||
return try decoder.decode(Entity.self, from: data)
|
||||
}
|
||||
|
||||
public func getWithLink<Entity: Decodable>(endpoint: Endpoint) async throws -> (Entity, LinkHandler?) {
|
||||
let (data, httpResponse) = try await urlSession.data(for: makeGet(endpoint: endpoint))
|
||||
var linkHandler: LinkHandler?
|
||||
if let response = httpResponse as? HTTPURLResponse,
|
||||
let link = response.allHeaderFields["Link"] as? String{
|
||||
linkHandler = .init(rawLink: link)
|
||||
}
|
||||
logResponseOnError(httpResponse: httpResponse, data: data)
|
||||
return (try decoder.decode(Entity.self, from: data), linkHandler)
|
||||
}
|
||||
|
||||
public func post<Entity: Decodable>(endpoint: Endpoint) async throws -> Entity {
|
||||
let url = makeURL(endpoint: endpoint)
|
||||
let request = makeURLRequest(url: url, httpMethod: "POST")
|
||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
|||
|
||||
public enum Accounts: Endpoint {
|
||||
case accounts(id: String)
|
||||
case favourites
|
||||
case favourites(sinceId: String?)
|
||||
case followedTags
|
||||
case featuredTags(id: String)
|
||||
case verifyCredentials
|
||||
|
@ -69,6 +69,9 @@ public enum Accounts: Endpoint {
|
|||
case let .following(_, sinceId):
|
||||
guard let sinceId else { return nil }
|
||||
return [.init(name: "max_id", value: sinceId)]
|
||||
case let .favourites(sinceId):
|
||||
guard let sinceId else { return nil }
|
||||
return [.init(name: "max_id", value: sinceId)]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
18
Packages/Network/Sources/Network/LinkHandler.swift
Normal file
18
Packages/Network/Sources/Network/LinkHandler.swift
Normal file
|
@ -0,0 +1,18 @@
|
|||
import Foundation
|
||||
import RegexBuilder
|
||||
|
||||
public struct LinkHandler {
|
||||
public let rawLink: String
|
||||
|
||||
public var maxId: String? {
|
||||
do {
|
||||
let regex = try Regex("max_id=[0-9]+")
|
||||
if let match = rawLink.firstMatch(of: regex) {
|
||||
return match.output.first?.substring?.replacingOccurrences(of: "max_id=", with: "")
|
||||
}
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue