mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 10:11:00 +00:00
Timeline: Fetch up to 10 new pages on pull to refresh
This commit is contained in:
parent
7cf233c974
commit
2b733e6b10
10 changed files with 71 additions and 28 deletions
|
@ -51,7 +51,7 @@ struct TimelineTab: View {
|
|||
Button {
|
||||
self.timeline = timeline
|
||||
} label: {
|
||||
Text(timeline.title())
|
||||
Label(timeline.title(), systemImage: timeline.iconName() ?? "")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
|
|
|
@ -16,7 +16,7 @@ public struct Poll: Codable {
|
|||
public let expired: Bool
|
||||
public let multiple: Bool
|
||||
public let votesCount: Int
|
||||
public let votersCount: Int
|
||||
public let votersCount: Int?
|
||||
public let voted: Bool
|
||||
public let ownVotes: [Int]
|
||||
public let options: [Option]
|
||||
|
|
|
@ -64,9 +64,9 @@ public enum Accounts: Endpoint {
|
|||
case let .familiarFollowers(withAccount):
|
||||
return [.init(name: "id[]", value: withAccount)]
|
||||
case let .followers(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
||||
case let .following(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
||||
case let .favourites(sinceId):
|
||||
guard let sinceId else { return nil }
|
||||
return [.init(name: "max_id", value: sinceId)]
|
||||
|
|
|
@ -6,11 +6,13 @@ public protocol Endpoint {
|
|||
}
|
||||
|
||||
extension Endpoint {
|
||||
func makePaginationParam(sinceId: String?, maxId: String?) -> [URLQueryItem]? {
|
||||
func makePaginationParam(sinceId: String?, maxId: String?, mindId: String?) -> [URLQueryItem]? {
|
||||
if let sinceId {
|
||||
return [.init(name: "since_id", value: sinceId)]
|
||||
} else if let maxId {
|
||||
return [.init(name: "max_id", value: maxId)]
|
||||
} else if let mindId {
|
||||
return [.init(name: "min_id", value: mindId)]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public enum Notifications: Endpoint {
|
|||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
case .notifications(let sinceId, let maxId, let types):
|
||||
var params = makePaginationParam(sinceId: sinceId, maxId: maxId) ?? []
|
||||
var params = makePaginationParam(sinceId: sinceId, maxId: maxId, mindId: nil) ?? []
|
||||
if let types {
|
||||
for type in types {
|
||||
params.append(.init(name: "types[]", value: type))
|
||||
|
|
|
@ -76,9 +76,9 @@ public enum Statuses: Endpoint {
|
|||
}
|
||||
return params
|
||||
case let .rebloggedBy(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
||||
case let .favouritedBy(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Foundation
|
||||
|
||||
public enum Timelines: Endpoint {
|
||||
case pub(sinceId: String?, maxId: String?)
|
||||
case home(sinceId: String?, maxId: String?)
|
||||
case pub(sinceId: String?, maxId: String?, minId: String?, local: Bool)
|
||||
case home(sinceId: String?, maxId: String?, minId: String?)
|
||||
case hashtag(tag: String, maxId: String?)
|
||||
|
||||
public func path() -> String {
|
||||
|
@ -18,12 +18,14 @@ public enum Timelines: Endpoint {
|
|||
|
||||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
case .pub(let sinceId, let maxId):
|
||||
return makePaginationParam(sinceId: sinceId, maxId: maxId)
|
||||
case .home(let sinceId, let maxId):
|
||||
return makePaginationParam(sinceId: sinceId, maxId: maxId)
|
||||
case .pub(let sinceId, let maxId, let minId, let local):
|
||||
var params = makePaginationParam(sinceId: sinceId, maxId: maxId, mindId: minId) ?? []
|
||||
params.append(.init(name: "local", value: local ? "true" : "false"))
|
||||
return params
|
||||
case .home(let sinceId, let maxId, let mindId):
|
||||
return makePaginationParam(sinceId: sinceId, maxId: maxId, mindId: mindId)
|
||||
case let .hashtag(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import Models
|
|||
import Network
|
||||
|
||||
public enum TimelineFilter: Hashable, Equatable {
|
||||
case pub, home
|
||||
case pub, local, home
|
||||
case hashtag(tag: String, accountId: String?)
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
@ -11,13 +11,15 @@ public enum TimelineFilter: Hashable, Equatable {
|
|||
}
|
||||
|
||||
public static func availableTimeline() -> [TimelineFilter] {
|
||||
return [.pub, .home]
|
||||
return [.pub, .local, .home]
|
||||
}
|
||||
|
||||
public func title() -> String {
|
||||
switch self {
|
||||
case .pub:
|
||||
return "Public"
|
||||
return "Federated"
|
||||
case .local:
|
||||
return "Local"
|
||||
case .home:
|
||||
return "Home"
|
||||
case let .hashtag(tag, _):
|
||||
|
@ -25,10 +27,24 @@ public enum TimelineFilter: Hashable, Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
public func endpoint(sinceId: String?, maxId: String?) -> Endpoint {
|
||||
public func iconName() -> String? {
|
||||
switch self {
|
||||
case .pub: return Timelines.pub(sinceId: sinceId, maxId: maxId)
|
||||
case .home: return Timelines.home(sinceId: sinceId, maxId: maxId)
|
||||
case .pub:
|
||||
return "globe.americas"
|
||||
case .local:
|
||||
return "person.3"
|
||||
case .home:
|
||||
return "house"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func endpoint(sinceId: String?, maxId: String?, minId: String?) -> Endpoint {
|
||||
switch self {
|
||||
case .pub: return Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: false)
|
||||
case .local: return Timelines.pub(sinceId: sinceId, maxId: maxId, minId: minId, local: true)
|
||||
case .home: return Timelines.home(sinceId: sinceId, maxId: maxId, minId: minId)
|
||||
case let .hashtag(tag, accountId):
|
||||
if let accountId {
|
||||
return Accounts.statuses(id: accountId, sinceId: nil, tag: tag)
|
||||
|
|
|
@ -46,9 +46,7 @@ public struct TimelineView: View {
|
|||
viewModel.timeline = timeline
|
||||
}
|
||||
.refreshable {
|
||||
Task {
|
||||
await viewModel.fetchStatuses(userIntent: true)
|
||||
}
|
||||
await viewModel.fetchStatuses(userIntent: true)
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { id in
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
|
|
|
@ -65,14 +65,16 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
if statuses.isEmpty {
|
||||
pendingStatuses = []
|
||||
statusesState = .loading
|
||||
statuses = try await client.get(endpoint: timeline.endpoint(sinceId: nil, maxId: nil))
|
||||
statuses = try await client.get(endpoint: timeline.endpoint(sinceId: nil, maxId: nil, minId: nil))
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
} else if let first = statuses.first {
|
||||
var newStatuses: [Status] = try await client.get(endpoint: timeline.endpoint(sinceId: first.id, maxId: nil))
|
||||
var newStatuses: [Status] = await fetchNewPages(minId: first.id, maxPages: 10)
|
||||
if userIntent || !pendingStatusesEnabled {
|
||||
pendingStatuses = []
|
||||
statuses.insert(contentsOf: newStatuses, at: 0)
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
}
|
||||
} else {
|
||||
newStatuses = newStatuses.filter { status in
|
||||
!pendingStatuses.contains(where: { $0.id == status.id })
|
||||
|
@ -87,12 +89,35 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
func fetchNewPages(minId: String, maxPages: Int) async -> [Status] {
|
||||
guard let client else { return [] }
|
||||
var pagesLoaded = 0
|
||||
var allStatuses: [Status] = []
|
||||
var latestMinId = minId
|
||||
do {
|
||||
while let newStatuses: [Status] = try await client.get(endpoint: timeline.endpoint(sinceId: nil,
|
||||
maxId: nil,
|
||||
minId: latestMinId)),
|
||||
!newStatuses.isEmpty,
|
||||
pagesLoaded < maxPages {
|
||||
pagesLoaded += 1
|
||||
allStatuses.insert(contentsOf: newStatuses, at: 0)
|
||||
latestMinId = newStatuses.first?.id ?? ""
|
||||
}
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
return allStatuses
|
||||
}
|
||||
|
||||
func fetchNextPage() async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
guard let lastId = statuses.last?.id else { return }
|
||||
statusesState = .display(statuses: statuses, nextPageState: .loadingNextPage)
|
||||
let newStatuses: [Status] = try await client.get(endpoint: timeline.endpoint(sinceId: nil, maxId: lastId))
|
||||
let newStatuses: [Status] = try await client.get(endpoint: timeline.endpoint(sinceId: nil,
|
||||
maxId: lastId,
|
||||
minId: nil))
|
||||
statuses.append(contentsOf: newStatuses)
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
} catch {
|
||||
|
|
Loading…
Reference in a new issue