Add cache with last loaded statuses on home timeline
This commit is contained in:
parent
6c3bf3a0fc
commit
f3598a9940
|
@ -36,6 +36,9 @@ import ClientKit
|
|||
/// Last status loaded on home timeline.
|
||||
public var lastLoadedStatusId: String?
|
||||
|
||||
/// JSON string with last objects loaded into home timeline.
|
||||
public var timelineCache: String?
|
||||
|
||||
@Relationship(deleteRule: .cascade, inverse: \ViewedStatus.pixelfedAccount) public var viewedStatuses: [ViewedStatus]
|
||||
@Relationship(deleteRule: .cascade, inverse: \AccountRelationship.pixelfedAccount) public var accountRelationships: [AccountRelationship]
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import Foundation
|
||||
import SwiftData
|
||||
import PixelfedKit
|
||||
|
||||
class AccountDataHandler {
|
||||
public static let shared = AccountDataHandler()
|
||||
|
@ -62,7 +63,7 @@ class AccountDataHandler {
|
|||
}
|
||||
}
|
||||
|
||||
func update(lastSeenStatusId: String?, lastLoadedStatusId: String?, accountId: String, modelContext: ModelContext) throws {
|
||||
func update(lastSeenStatusId: String?, lastLoadedStatusId: String?, statuses: [Status]? = nil, accountId: String, modelContext: ModelContext) throws {
|
||||
guard let accountDataFromDb = self.getAccountData(accountId: accountId, modelContext: modelContext) else {
|
||||
return
|
||||
}
|
||||
|
@ -75,6 +76,10 @@ class AccountDataHandler {
|
|||
accountDataFromDb.lastLoadedStatusId = lastLoadedStatusId
|
||||
}
|
||||
|
||||
if let statuses, let statusesJsonData = try? JSONEncoder().encode(statuses) {
|
||||
accountDataFromDb.timelineCache = String(data: statusesJsonData, encoding: .utf8)
|
||||
}
|
||||
|
||||
try modelContext.save()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,13 +106,14 @@ public class HomeTimelineService {
|
|||
return visibleStatuses
|
||||
}
|
||||
|
||||
public func update(lastSeenStatusId: String?, lastLoadedStatusId: String?, applicationState: ApplicationState, modelContext: ModelContext) throws {
|
||||
public func update(lastSeenStatusId: String?, lastLoadedStatusId: String?, statuses: [Status]? = nil, applicationState: ApplicationState, modelContext: ModelContext) throws {
|
||||
guard let accountId = applicationState.account?.id else {
|
||||
return
|
||||
}
|
||||
|
||||
try AccountDataHandler.shared.update(lastSeenStatusId: lastSeenStatusId,
|
||||
lastLoadedStatusId: lastLoadedStatusId,
|
||||
statuses: statuses,
|
||||
accountId: accountId,
|
||||
modelContext: modelContext)
|
||||
|
||||
|
|
|
@ -100,7 +100,10 @@ struct AccountsPhotoView: View {
|
|||
private func loadData() async {
|
||||
do {
|
||||
self.accounts = try await self.loadAccounts()
|
||||
self.state = .loaded
|
||||
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
ErrorService.shared.handle(error, message: "trendingAccounts.error.loadingAccountsFailed", showToastr: true)
|
||||
|
|
|
@ -120,7 +120,10 @@ struct AccountsView: View {
|
|||
private func loadData(page: Int) async {
|
||||
do {
|
||||
try await self.loadAccounts(page: page)
|
||||
self.state = .loaded
|
||||
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
ErrorService.shared.handle(error, message: "accounts.error.loadingAccountsFailed", showToastr: true)
|
||||
|
|
|
@ -127,7 +127,10 @@ struct FollowRequestsView: View {
|
|||
private func loadData(page: Int) async {
|
||||
do {
|
||||
try await self.loadAccounts(page: page)
|
||||
self.state = .loaded
|
||||
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
ErrorService.shared.handle(error, message: "accounts.error.loadingAccountsFailed", showToastr: true)
|
||||
|
|
|
@ -99,7 +99,10 @@ struct HashtagsView: View {
|
|||
private func loadData() async {
|
||||
do {
|
||||
self.tags = try await self.loadTags()
|
||||
self.state = .loaded
|
||||
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
ErrorService.shared.handle(error, message: "tags.error.loadingTagsFailed", showToastr: true)
|
||||
|
|
|
@ -160,7 +160,9 @@ struct HomeTimelineView: View {
|
|||
try await self.loadFirstStatuses()
|
||||
try ViewedStatusHandler.shared.deleteOldViewedStatuses(modelContext: modelContext)
|
||||
|
||||
self.state = .loaded
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
|
||||
self.state = .error(error)
|
||||
|
@ -173,11 +175,8 @@ struct HomeTimelineView: View {
|
|||
return
|
||||
}
|
||||
|
||||
// We have to download one newer status Id.
|
||||
let newerStatusId = try await self.getNewerStatusId(from: accountData.lastLoadedStatusId)
|
||||
|
||||
// Download statuses from API (which are older then last visible status).
|
||||
let statuses = try await self.loadFromApi(maxId: newerStatusId)
|
||||
let statuses = try await self.loadFromCacheOrApi(timelineCache: accountData.timelineCache)
|
||||
|
||||
if statuses.isEmpty {
|
||||
self.allItemsLoaded = true
|
||||
|
@ -273,6 +272,7 @@ struct HomeTimelineView: View {
|
|||
// Remeber first status returned by API in user context (when it's newer then remembered).
|
||||
try HomeTimelineService.shared.update(lastSeenStatusId: self.statusViewModels.first?.id,
|
||||
lastLoadedStatusId: statuses.first?.id,
|
||||
statuses: statuses,
|
||||
applicationState: self.applicationState,
|
||||
modelContext: modelContext)
|
||||
|
||||
|
@ -292,6 +292,17 @@ struct HomeTimelineView: View {
|
|||
self.applicationState.amountOfNewStatuses = 0
|
||||
}
|
||||
|
||||
private func loadFromCacheOrApi(timelineCache: String?) async throws -> [Status] {
|
||||
if let timelineCache, let timelineCacheData = timelineCache.data(using: .utf8) {
|
||||
let statusesFromCache = try? JSONDecoder().decode([Status].self, from: timelineCacheData)
|
||||
if let statusesFromCache {
|
||||
return statusesFromCache
|
||||
}
|
||||
}
|
||||
|
||||
return try await self.loadFromApi()
|
||||
}
|
||||
|
||||
private func loadFromApi(maxId: String? = nil, sinceId: String? = nil, minId: String? = nil) async throws -> [Status] {
|
||||
return try await self.client.publicTimeline?.getHomeTimeline(
|
||||
maxId: maxId,
|
||||
|
@ -338,17 +349,4 @@ struct HomeTimelineView: View {
|
|||
private func shouldUpToDateBeVisible(statusId: String) -> Bool {
|
||||
return self.applicationState.lastSeenStatusId != statusViewModels.first?.id && self.applicationState.lastSeenStatusId == statusId
|
||||
}
|
||||
|
||||
private func getNewerStatusId(from statusId: String?) async throws -> String? {
|
||||
guard let statusId else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let statuses = try await self.client.publicTimeline?.getHomeTimeline(
|
||||
minId: statusId,
|
||||
limit: 1,
|
||||
includeReblogs: self.applicationState.showReboostedStatuses) ?? []
|
||||
|
||||
return statuses.first?.id
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,9 @@ struct InstanceView: View {
|
|||
self.instance = try await self.client.instances.instance(url: serverUrl)
|
||||
}
|
||||
|
||||
self.state = .loaded
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
ErrorService.shared.handle(error, message: "instance.error.loadingDataFailed", showToastr: true)
|
||||
|
|
|
@ -92,7 +92,9 @@ struct NotificationsView: View {
|
|||
self.allItemsLoaded = true
|
||||
}
|
||||
|
||||
self.state = .loaded
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
|
|
|
@ -124,7 +124,10 @@ struct PaginableStatusesView: View {
|
|||
private func loadData() async {
|
||||
do {
|
||||
try await self.loadStatuses()
|
||||
self.state = .loaded
|
||||
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
|
||||
self.state = .error(error)
|
||||
|
|
|
@ -201,20 +201,13 @@ struct StatusView: View {
|
|||
}
|
||||
|
||||
self.statusViewModel = statusModel
|
||||
|
||||
// If we have status in database then we can update data.
|
||||
// TODO: It seems that Pixelfed didn't support status edit, thus we don't need to update status.
|
||||
/*
|
||||
if let accountData = self.applicationState.account,
|
||||
let statusDataFromDatabase = StatusDataHandler.shared.getStatusData(accountId: accountData.id, statusId: self.statusId) {
|
||||
_ = try await HomeTimelineService.shared.update(status: statusDataFromDatabase, basedOn: status, for: accountData)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
self.state = .loaded
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch NetworkError.notSuccessResponse(let response) {
|
||||
if response.statusCode() == HTTPStatusCode.notFound, let accountId = self.applicationState.account?.id {
|
||||
if response.statusCode() == HTTPStatusCode.notFound {
|
||||
ErrorService.shared.handle(NetworkError.notSuccessResponse(response), message: "status.error.notFound", showToastr: true)
|
||||
self.dismiss()
|
||||
}
|
||||
|
|
|
@ -166,7 +166,9 @@ struct StatusesView: View {
|
|||
await self.loadTag(hashtag: hashtag)
|
||||
}
|
||||
|
||||
self.state = .loaded
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
|
||||
self.state = .error(error)
|
||||
|
|
|
@ -128,12 +128,16 @@ struct UserProfileStatusesView: View {
|
|||
// Prefetch images.
|
||||
self.prefetch(statusModels: inPlaceStatuses)
|
||||
|
||||
self.firstLoadFinished = true
|
||||
// Append downloaded statuses to the list.
|
||||
self.statusViewModels.append(contentsOf: inPlaceStatuses)
|
||||
|
||||
if statuses.count < self.defaultLimit {
|
||||
self.allItemsLoaded = true
|
||||
}
|
||||
|
||||
withAnimation {
|
||||
self.firstLoadFinished = true
|
||||
}
|
||||
}
|
||||
|
||||
private func loadMoreStatuses() async throws {
|
||||
|
|
|
@ -128,7 +128,9 @@ struct UserProfileView: View {
|
|||
|
||||
self.account = accountFromApi
|
||||
|
||||
self.state = .loaded
|
||||
withAnimation {
|
||||
self.state = .loaded
|
||||
}
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "userProfile.error.loadingAccountFailed", showToastr: !Task.isCancelled)
|
||||
self.state = .error(error)
|
||||
|
|
Loading…
Reference in New Issue