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 title: String = ""
|
||||||
@Published var relationship: Relationshionship?
|
@Published var relationship: Relationshionship?
|
||||||
@Published var favourites: [Status] = []
|
@Published var favourites: [Status] = []
|
||||||
|
private var favouritesNextPage: LinkHandler?
|
||||||
@Published var followedTags: [Tag] = []
|
@Published var followedTags: [Tag] = []
|
||||||
@Published var featuredTags: [FeaturedTag] = []
|
@Published var featuredTags: [FeaturedTag] = []
|
||||||
@Published var fields: [Account.Field] = []
|
@Published var fields: [Account.Field] = []
|
||||||
|
@ -108,7 +109,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
tabState = .statuses(statusesState: .loading)
|
tabState = .statuses(statusesState: .loading)
|
||||||
statuses = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: nil, tag: nil))
|
statuses = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: nil, tag: nil))
|
||||||
if isCurrentUser {
|
if isCurrentUser {
|
||||||
favourites = try await client.get(endpoint: Accounts.favourites)
|
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
||||||
}
|
}
|
||||||
reloadTabState()
|
reloadTabState()
|
||||||
} catch {
|
} 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))
|
let newStatuses: [Status] = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: lastId, tag: nil))
|
||||||
statuses.append(contentsOf: newStatuses)
|
statuses.append(contentsOf: newStatuses)
|
||||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .hasNextPage))
|
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
|
break
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -157,7 +164,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
case .statuses:
|
case .statuses:
|
||||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .hasNextPage))
|
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .hasNextPage))
|
||||||
case .favourites:
|
case .favourites:
|
||||||
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .none))
|
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .hasNextPage))
|
||||||
case .followedTags:
|
case .followedTags:
|
||||||
tabState = .followedTags(tags: followedTags)
|
tabState = .followedTags(tags: followedTags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ class AccountsListViewModel: ObservableObject {
|
||||||
|
|
||||||
@Published var state = State.loading
|
@Published var state = State.loading
|
||||||
|
|
||||||
|
private var nextPageId: String?
|
||||||
|
|
||||||
init(accountId: String, mode: AccountsListMode) {
|
init(accountId: String, mode: AccountsListMode) {
|
||||||
self.accountId = accountId
|
self.accountId = accountId
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
@ -38,14 +40,16 @@ class AccountsListViewModel: ObservableObject {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
state = .loading
|
state = .loading
|
||||||
|
let link: LinkHandler?
|
||||||
switch mode {
|
switch mode {
|
||||||
case .followers:
|
case .followers:
|
||||||
accounts = try await client.get(endpoint: Accounts.followers(id: accountId,
|
(accounts, link) = try await client.getWithLink(endpoint: Accounts.followers(id: accountId,
|
||||||
sinceId: nil))
|
sinceId: nil))
|
||||||
case .following:
|
case .following:
|
||||||
accounts = try await client.get(endpoint: Accounts.following(id: accountId,
|
(accounts, link) = try await client.getWithLink(endpoint: Accounts.following(id: accountId,
|
||||||
sinceId: nil))
|
sinceId: nil))
|
||||||
}
|
}
|
||||||
|
nextPageId = link?.maxId
|
||||||
relationships = try await client.get(endpoint:
|
relationships = try await client.get(endpoint:
|
||||||
Accounts.relationships(ids: accounts.map{ $0.id }))
|
Accounts.relationships(ids: accounts.map{ $0.id }))
|
||||||
state = .display(accounts: accounts,
|
state = .display(accounts: accounts,
|
||||||
|
@ -55,23 +59,25 @@ class AccountsListViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchNextPage() async {
|
func fetchNextPage() async {
|
||||||
guard let client else { return }
|
guard let client, let nextPageId else { return }
|
||||||
do {
|
do {
|
||||||
state = .display(accounts: accounts, relationships: relationships, nextPageState: .loadingNextPage)
|
state = .display(accounts: accounts, relationships: relationships, nextPageState: .loadingNextPage)
|
||||||
let newAccounts: [Account]
|
let newAccounts: [Account]
|
||||||
|
let link: LinkHandler?
|
||||||
switch mode {
|
switch mode {
|
||||||
case .followers:
|
case .followers:
|
||||||
newAccounts = try await client.get(endpoint: Accounts.followers(id: accountId,
|
(newAccounts, link) = try await client.getWithLink(endpoint: Accounts.followers(id: accountId,
|
||||||
sinceId: accounts.last?.id))
|
sinceId: nextPageId))
|
||||||
case .following:
|
case .following:
|
||||||
newAccounts = try await client.get(endpoint: Accounts.following(id: accountId,
|
(newAccounts, link) = try await client.getWithLink(endpoint: Accounts.following(id: accountId,
|
||||||
sinceId: accounts.last?.id))
|
sinceId: nextPageId))
|
||||||
}
|
}
|
||||||
accounts.append(contentsOf: newAccounts)
|
accounts.append(contentsOf: newAccounts)
|
||||||
let newRelationships: [Relationshionship] =
|
let newRelationships: [Relationshionship] =
|
||||||
try await client.get(endpoint: Accounts.relationships(ids: newAccounts.map{ $0.id }))
|
try await client.get(endpoint: Accounts.relationships(ids: newAccounts.map{ $0.id }))
|
||||||
|
|
||||||
relationships.append(contentsOf: newRelationships)
|
relationships.append(contentsOf: newRelationships)
|
||||||
|
self.nextPageId = link?.maxId
|
||||||
state = .display(accounts: accounts,
|
state = .display(accounts: accounts,
|
||||||
relationships: relationships,
|
relationships: relationships,
|
||||||
nextPageState: .hasNextPage)
|
nextPageState: .hasNextPage)
|
||||||
|
|
|
@ -61,15 +61,29 @@ public class Client: ObservableObject, Equatable {
|
||||||
}
|
}
|
||||||
return request
|
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 {
|
public func get<Entity: Decodable>(endpoint: Endpoint) async throws -> Entity {
|
||||||
let url = makeURL(endpoint: endpoint)
|
let (data, httpResponse) = try await urlSession.data(for: makeGet(endpoint: endpoint))
|
||||||
let request = makeURLRequest(url: url, httpMethod: "GET")
|
|
||||||
let (data, httpResponse) = try await urlSession.data(for: request)
|
|
||||||
logResponseOnError(httpResponse: httpResponse, data: data)
|
logResponseOnError(httpResponse: httpResponse, data: data)
|
||||||
return try decoder.decode(Entity.self, from: 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 {
|
public func post<Entity: Decodable>(endpoint: Endpoint) async throws -> Entity {
|
||||||
let url = makeURL(endpoint: endpoint)
|
let url = makeURL(endpoint: endpoint)
|
||||||
let request = makeURLRequest(url: url, httpMethod: "POST")
|
let request = makeURLRequest(url: url, httpMethod: "POST")
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
|
|
||||||
public enum Accounts: Endpoint {
|
public enum Accounts: Endpoint {
|
||||||
case accounts(id: String)
|
case accounts(id: String)
|
||||||
case favourites
|
case favourites(sinceId: String?)
|
||||||
case followedTags
|
case followedTags
|
||||||
case featuredTags(id: String)
|
case featuredTags(id: String)
|
||||||
case verifyCredentials
|
case verifyCredentials
|
||||||
|
@ -69,6 +69,9 @@ public enum Accounts: Endpoint {
|
||||||
case let .following(_, sinceId):
|
case let .following(_, sinceId):
|
||||||
guard let sinceId else { return nil }
|
guard let sinceId else { return nil }
|
||||||
return [.init(name: "max_id", value: sinceId)]
|
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:
|
default:
|
||||||
return nil
|
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