chore: make favorite and hashtag scene use next page token from response header

This commit is contained in:
CMK 2021-04-08 16:27:26 +08:00
parent 0c8134463f
commit ba48adb470
3 changed files with 64 additions and 6 deletions

View File

@ -35,6 +35,8 @@ extension HashtagTimelineViewModel.LoadOldestState {
}
class Loading: HashtagTimelineViewModel.LoadOldestState {
var maxID: String?
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == Fail.self || stateClass == Idle.self || stateClass == NoMore.self
}
@ -54,7 +56,7 @@ extension HashtagTimelineViewModel.LoadOldestState {
}
// TODO: only set large count when using Wi-Fi
let maxID = last.id
let maxID = self.maxID ?? last.id
viewModel.context.apiService.hashtagTimeline(
domain: activeMastodonAuthenticationBox.domain,
maxID: maxID,
@ -71,10 +73,19 @@ extension HashtagTimelineViewModel.LoadOldestState {
// handle isFetchingLatestTimeline in fetch controller delegate
break
}
} receiveValue: { response in
} receiveValue: { [weak self] response in
guard let self = self else { return }
let statuses = response.value
// enter no more state when no new statuses
if statuses.isEmpty || (statuses.count == 1 && statuses[0].id == maxID) {
let hasNextPage: Bool = {
guard let link = response.link else { return true } // assert has more when link invalid
return link.maxID != nil
}()
self.maxID = response.link?.maxID
if !hasNextPage || statuses.isEmpty || (statuses.count == 1 && statuses[0].id == maxID) {
stateMachine.enter(NoMore.self)
} else {
stateMachine.enter(Idle.self)

View File

@ -92,6 +92,9 @@ extension FavoriteViewModel.State {
}
class Loading: FavoriteViewModel.State {
var maxID: String?
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
switch stateClass {
case is Fail.Type:
@ -113,8 +116,11 @@ extension FavoriteViewModel.State {
stateMachine.enter(Fail.self)
return
}
let maxID = viewModel.statusFetchedResultsController.statusIDs.value.last
if previousState is Reloading {
maxID = nil
}
// prefer use `maxID` token in response header
// let maxID = viewModel.statusFetchedResultsController.statusIDs.value.last
viewModel.context.apiService.favoritedStatuses(
maxID: maxID,
@ -139,8 +145,15 @@ extension FavoriteViewModel.State {
statusIDs.append(status.id)
hasNewStatusesAppend = true
}
self.maxID = response.link?.maxID
let hasNextPage: Bool = {
guard let link = response.link else { return true } // assert has more when link invalid
return link.maxID != nil
}()
if hasNewStatusesAppend {
if hasNewStatusesAppend && hasNextPage {
stateMachine.enter(Idle.self)
} else {
stateMachine.enter(NoMore.self)

View File

@ -18,6 +18,7 @@ extension Mastodon.Response {
// application fields
public let rateLimit: RateLimit?
public let link: Link?
public let responseTime: Int?
public var networkDate: Date {
@ -33,6 +34,11 @@ extension Mastodon.Response {
}()
self.rateLimit = RateLimit(response: response)
self.link = {
guard let string = (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "link") else { return nil }
return Link(link: string)
}()
self.responseTime = {
guard let string = (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "x-response-time") else { return nil }
return Int(string)
@ -43,6 +49,7 @@ extension Mastodon.Response {
self.value = value
self.date = old.date
self.rateLimit = old.rateLimit
self.link = old.link
self.responseTime = old.responseTime
}
@ -90,3 +97,30 @@ extension Mastodon.Response {
}
}
extension Mastodon.Response {
public struct Link {
public let maxID: Mastodon.Entity.Status.ID?
public let minID: Mastodon.Entity.Status.ID?
init(link: String) {
self.maxID = {
guard let regex = try? NSRegularExpression(pattern: "max_id=([[:digit:]]+)", options: []) else { return nil }
let results = regex.matches(in: link, options: [], range: NSRange(link.startIndex..<link.endIndex, in: link))
guard let match = results.first else { return nil }
guard let range = Range(match.range(at: 1), in: link) else { return nil }
let id = link[range]
return String(id)
}()
self.minID = {
guard let regex = try? NSRegularExpression(pattern: "min_id=([[:digit:]]+)", options: []) else { return nil }
let results = regex.matches(in: link, options: [], range: NSRange(link.startIndex..<link.endIndex, in: link))
guard let match = results.first else { return nil }
guard let range = Range(match.range(at: 1), in: link) else { return nil }
let id = link[range]
return String(id)
}()
}
}
}