From facc2caf5fe246e21a9899cd2432537f85196a97 Mon Sep 17 00:00:00 2001 From: Marcin Czachurski Date: Mon, 20 Nov 2023 15:23:42 +0100 Subject: [PATCH] Improve counter on home timeline --- .../Sources/ClientKit/Client+Timeline.swift | 6 +-- CoreData/AccountDataHandler.swift | 2 +- Localization/Localizable.xcstrings | 28 ++++++++++ .../Sources/PixelfedKit/Entities/Link.swift | 2 +- .../PixelfedKit/Entities/Linkable.swift | 28 +++++++++- .../PixelfedClient+Timelines.swift | 12 ++--- Vernissage.xcodeproj/project.pbxproj | 12 ++--- Vernissage/Services/HomeTimelineService.swift | 38 +++++++++++--- Vernissage/Views/HomeTimelineView.swift | 52 ++++++++++--------- Vernissage/Views/StatusesView.swift | 48 +++++++++-------- Vernissage/Widgets/ImagesGrid.swift | 14 +++-- .../PhotoWidget/Service/StatusFetcher.swift | 6 +-- 12 files changed, 169 insertions(+), 79 deletions(-) diff --git a/ClientKit/Sources/ClientKit/Client+Timeline.swift b/ClientKit/Sources/ClientKit/Client+Timeline.swift index e70305c..48044ac 100644 --- a/ClientKit/Sources/ClientKit/Client+Timeline.swift +++ b/ClientKit/Sources/ClientKit/Client+Timeline.swift @@ -13,7 +13,7 @@ extension Client { sinceId: String? = nil, minId: String? = nil, limit: Int = 40, - includeReblogs: Bool? = nil) async throws -> [Status] { + includeReblogs: Bool? = nil) async throws -> Linkable<[Status]> { return try await pixelfedClient.getHomeTimeline(maxId: maxId, sinceId: sinceId, minId: minId, limit: limit, includeReblogs: includeReblogs) } @@ -22,7 +22,7 @@ extension Client { maxId: String? = nil, sinceId: String? = nil, minId: String? = nil, - limit: Int = 40) async throws -> [Status] { + limit: Int = 40) async throws -> Linkable<[Status]> { return try await pixelfedClient.getPublicTimeline(local: local, remote: remote, onlyMedia: true, @@ -38,7 +38,7 @@ extension Client { maxId: String? = nil, sinceId: String? = nil, minId: String? = nil, - limit: Int = 40) async throws -> [Status] { + limit: Int = 40) async throws -> Linkable<[Status]> { return try await pixelfedClient.getTagTimeline(tag: tag, local: local, remote: remote, diff --git a/CoreData/AccountDataHandler.swift b/CoreData/AccountDataHandler.swift index b1c382f..c56d229 100644 --- a/CoreData/AccountDataHandler.swift +++ b/CoreData/AccountDataHandler.swift @@ -64,7 +64,7 @@ class AccountDataHandler { } } - func update(lastSeenStatusId: String?, lastLoadedStatusId: String?, statuses: [Status]? = nil, applicationState: ApplicationState, modelContext: ModelContext) throws { + func update(lastSeenStatusId: String?, lastLoadedStatusId: String?, statuses: Linkable<[Status]>? = nil, applicationState: ApplicationState, modelContext: ModelContext) throws { guard let accountId = applicationState.account?.id else { return } diff --git a/Localization/Localizable.xcstrings b/Localization/Localizable.xcstrings index 61f43d5..3daf974 100644 --- a/Localization/Localizable.xcstrings +++ b/Localization/Localizable.xcstrings @@ -237,6 +237,34 @@ } } }, + "+99" : { + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "+99" + } + }, + "eu" : { + "stringUnit" : { + "state" : "translated", + "value" : "+99" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+99" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+99" + } + } + } + }, "accounts.error.loadingAccountsFailed" : { "comment" : "Information message when loading account failed", "localizations" : { diff --git a/PixelfedKit/Sources/PixelfedKit/Entities/Link.swift b/PixelfedKit/Sources/PixelfedKit/Entities/Link.swift index 8a12ae2..f8ea40a 100644 --- a/PixelfedKit/Sources/PixelfedKit/Entities/Link.swift +++ b/PixelfedKit/Sources/PixelfedKit/Entities/Link.swift @@ -8,7 +8,7 @@ import Foundation import RegexBuilder /// Link returned in header for paging feature/ -public struct Link { +public struct Link: Codable { /// Raw value of header link. public let rawLink: String diff --git a/PixelfedKit/Sources/PixelfedKit/Entities/Linkable.swift b/PixelfedKit/Sources/PixelfedKit/Entities/Linkable.swift index 0942e9d..3d37d06 100644 --- a/PixelfedKit/Sources/PixelfedKit/Entities/Linkable.swift +++ b/PixelfedKit/Sources/PixelfedKit/Entities/Linkable.swift @@ -7,7 +7,7 @@ import Foundation /// Some of endpoint returns JSON data and additional information in header, like link for paging functionality. -public struct Linkable where T: Codable { +public struct Linkable : Codable where T: Codable { /// Data retunred in HTTP reponse body (mostly JSON data/entities). public let data: T @@ -20,3 +20,29 @@ public struct Linkable where T: Codable { self.link = link } } + +public extension Linkable<[Status]> { + func getMinId() -> String? { + if let link = self.link { + return link.minId + } + + if let firstItemId = self.data.first?.id { + return firstItemId + } + + return nil + } + + func getMaxId() -> String? { + if let link = self.link { + return link.maxId + } + + if let lastItemId = self.data.last?.id { + return lastItemId + } + + return nil + } +} diff --git a/PixelfedKit/Sources/PixelfedKit/PixelfedClient+Timelines.swift b/PixelfedKit/Sources/PixelfedKit/PixelfedClient+Timelines.swift index b435a72..478532d 100644 --- a/PixelfedKit/Sources/PixelfedKit/PixelfedClient+Timelines.swift +++ b/PixelfedKit/Sources/PixelfedKit/PixelfedClient+Timelines.swift @@ -13,7 +13,7 @@ public extension PixelfedClientAuthenticated { minId: EntityId? = nil, limit: Int? = nil, includeReblogs: Bool? = nil, - timeoutInterval: Double? = nil) async throws -> [Status] { + timeoutInterval: Double? = nil) async throws -> Linkable<[Status]> { let request = try Self.request( for: baseURL, @@ -22,7 +22,7 @@ public extension PixelfedClientAuthenticated { timeoutInterval: timeoutInterval ) - return try await downloadJson([Status].self, request: request) + return try await downloadJsonWithLink([Status].self, request: request) } func getPublicTimeline(local: Bool? = nil, @@ -31,7 +31,7 @@ public extension PixelfedClientAuthenticated { maxId: EntityId? = nil, sinceId: EntityId? = nil, minId: EntityId? = nil, - limit: Limit? = nil) async throws -> [Status] { + limit: Limit? = nil) async throws -> Linkable<[Status]> { let request = try Self.request( for: baseURL, @@ -39,7 +39,7 @@ public extension PixelfedClientAuthenticated { withBearerToken: token ) - return try await downloadJson([Status].self, request: request) + return try await downloadJsonWithLink([Status].self, request: request) } func getTagTimeline(tag: String, @@ -49,7 +49,7 @@ public extension PixelfedClientAuthenticated { maxId: EntityId? = nil, sinceId: EntityId? = nil, minId: EntityId? = nil, - limit: Int? = nil) async throws -> [Status] { + limit: Int? = nil) async throws -> Linkable<[Status]> { let request = try Self.request( for: baseURL, @@ -57,7 +57,7 @@ public extension PixelfedClientAuthenticated { withBearerToken: token ) - return try await downloadJson([Status].self, request: request) + return try await downloadJsonWithLink([Status].self, request: request) } func setMarkers(_ markers: [Pixelfed.Markers.Timeline: EntityId]) async throws -> Markers { diff --git a/Vernissage.xcodeproj/project.pbxproj b/Vernissage.xcodeproj/project.pbxproj index fe80c0b..b6953a4 100644 --- a/Vernissage.xcodeproj/project.pbxproj +++ b/Vernissage.xcodeproj/project.pbxproj @@ -1209,7 +1209,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.0.1; + MARKETING_VERSION = 2.0.2; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1243,7 +1243,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.0.1; + MARKETING_VERSION = 2.0.2; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1276,7 +1276,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.0.1; + MARKETING_VERSION = 2.0.2; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1308,7 +1308,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.0.1; + MARKETING_VERSION = 2.0.2; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1474,7 +1474,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.1; + MARKETING_VERSION = 2.0.2; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1517,7 +1517,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.1; + MARKETING_VERSION = 2.0.2; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/Vernissage/Services/HomeTimelineService.swift b/Vernissage/Services/HomeTimelineService.swift index b5a37df..3fa0028 100644 --- a/Vernissage/Services/HomeTimelineService.swift +++ b/Vernissage/Services/HomeTimelineService.swift @@ -43,27 +43,49 @@ public class HomeTimelineService { let client = PixelfedClient(baseURL: accountData.serverUrl).getAuthenticated(token: accessToken) var statuses: [Status] = [] - var newestStatusId = lastSeenStatusId + var latestStatusId: String? = nil + var breakProcesssing = false; // There can be more then 40 newest statuses, that's why we have to sometimes send more then one request. while true { do { - let downloadedStatuses = try await client.getHomeTimeline(minId: newestStatusId, + // Download statuses from the top or the list. + let downloadedStatuses = try await client.getHomeTimeline(maxId: latestStatusId, limit: self.maximumAmountOfDownloadedStatuses, includeReblogs: includeReblogs) - guard let firstStatus = downloadedStatuses.first else { - break + // Iterate througt the list until we go to already visible status by the user. + var temporaryList: [Status] = [] + for downloadedStatus in downloadedStatuses.data { + guard downloadedStatus.id != lastSeenStatusId else { + breakProcesssing = true + break + } + + temporaryList.append(downloadedStatus) } - + + // Remove from the list duplicated statuses. let visibleStatuses = self.getVisibleStatuses(accountId: accountData.id, - statuses: downloadedStatuses, + statuses: temporaryList, hideStatusesWithoutAlt: hideStatusesWithoutAlt, modelContext: modelContext) - + + // Add statuses to the list. statuses.append(contentsOf: visibleStatuses) - newestStatusId = firstStatus.id + // Break when we go to the already visible status. + if breakProcesssing { + break + } + + // When we discovered more then 100 statuses we can break. + if statuses.count > 100 { + break + } + + // Set status Id which should be used to download next portion of the statuses. + latestStatusId = downloadedStatuses.getMaxId() } catch { ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadingNewStatuses") break diff --git a/Vernissage/Views/HomeTimelineView.swift b/Vernissage/Views/HomeTimelineView.swift index c89eec9..3852d4e 100644 --- a/Vernissage/Views/HomeTimelineView.swift +++ b/Vernissage/Views/HomeTimelineView.swift @@ -143,8 +143,14 @@ struct HomeTimelineView: View { HStack { Image(systemName: "arrow.up") .fontWeight(.light) - Text("\(self.applicationState.amountOfNewStatuses)") - .fontWeight(.semibold) + + if self.applicationState.amountOfNewStatuses < 100 { + Text("\(self.applicationState.amountOfNewStatuses)") + .fontWeight(.semibold) + } else { + Text("+99") + .fontWeight(.semibold) + } } .padding(.vertical, 12) .padding(.horizontal, 18) @@ -183,28 +189,28 @@ struct HomeTimelineView: View { // Download statuses from API (which are older then last visible status). let statuses = try await self.loadFromCacheOrApi(timelineCache: accountData.timelineCache) - if statuses.isEmpty { + if statuses.data.isEmpty { self.allItemsLoaded = true return } // Remember last status id returned by API. - self.lastStatusId = statuses.last?.id + self.lastStatusId = statuses.getMaxId() // Get only visible statuses. let visibleStatuses = HomeTimelineService.shared.getVisibleStatuses(accountId: accountId, - statuses: statuses, + statuses: statuses.data, hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt, modelContext: modelContext) // Remeber first status returned by API in user context (when it's newer then remembered). try AccountDataHandler.shared.update(lastSeenStatusId: nil, - lastLoadedStatusId: statuses.first?.id, + lastLoadedStatusId: statuses.getMinId(), applicationState: self.applicationState, modelContext: modelContext) // Append statuses to viewed. - try ViewedStatusHandler.shared.append(contentsOf: statuses, accountId: accountId, modelContext: modelContext) + try ViewedStatusHandler.shared.append(contentsOf: statuses.data, accountId: accountId, modelContext: modelContext) // Map to view models. let statusModels = visibleStatuses.map({ StatusModel(status: $0) }) @@ -222,24 +228,24 @@ struct HomeTimelineView: View { // Download statuses from API. let statuses = try await self.loadFromApi(maxId: lastStatusId) - if statuses.isEmpty { + if statuses.data.isEmpty { self.allItemsLoaded = true return } // Now we have new last status. - if let lastStatusId = statuses.last?.id { + if let lastStatusId = statuses.getMaxId() { self.lastStatusId = lastStatusId } // Get only visible statuses. let visibleStatuses = HomeTimelineService.shared.getVisibleStatuses(accountId: accountId, - statuses: statuses, + statuses: statuses.data, hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt, modelContext: modelContext) // Append statuses to viewed. - try ViewedStatusHandler.shared.append(contentsOf: statuses, accountId: accountId, modelContext: modelContext) + try ViewedStatusHandler.shared.append(contentsOf: statuses.data, accountId: accountId, modelContext: modelContext) // Map to view models. let statusModels = visibleStatuses.map({ StatusModel(status: $0) }) @@ -260,29 +266,29 @@ struct HomeTimelineView: View { // Download statuses from API. let statuses = try await self.loadFromApi() - if statuses.isEmpty { + if statuses.data.isEmpty { self.allItemsLoaded = true return } // Remember last status id returned by API. - self.lastStatusId = statuses.last?.id + self.lastStatusId = statuses.getMaxId() // Get only visible statuses. let visibleStatuses = HomeTimelineService.shared.getVisibleStatuses(accountId: accountId, - statuses: statuses, + statuses: statuses.data, hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt, modelContext: modelContext) // Remeber first status returned by API in user context (when it's newer then remembered). try AccountDataHandler.shared.update(lastSeenStatusId: self.statusViewModels.first?.id, - lastLoadedStatusId: statuses.first?.id, + lastLoadedStatusId: statuses.getMinId(), statuses: statuses, applicationState: self.applicationState, modelContext: modelContext) // Append statuses to viewed. - try ViewedStatusHandler.shared.append(contentsOf: statuses, accountId: accountId, modelContext: modelContext) + try ViewedStatusHandler.shared.append(contentsOf: statuses.data, accountId: accountId, modelContext: modelContext) // Map to view models. let statusModels = visibleStatuses.map({ StatusModel(status: $0) }) @@ -297,24 +303,22 @@ 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 - } + private func loadFromCacheOrApi(timelineCache: String?) async throws -> Linkable<[Status]> { + if let timelineCache, let timelineCacheData = timelineCache.data(using: .utf8), + let statusesFromCache = try? JSONDecoder().decode(Linkable<[Status]>.self, from: timelineCacheData) { + return statusesFromCache } return try await self.loadFromApi() } - private func loadFromApi(maxId: String? = nil, sinceId: String? = nil, minId: String? = nil) async throws -> [Status] { + private func loadFromApi(maxId: String? = nil, sinceId: String? = nil, minId: String? = nil) async throws -> Linkable<[Status]> { return try await self.client.publicTimeline?.getHomeTimeline( maxId: maxId, sinceId: sinceId, minId: minId, limit: self.defaultLimit, - includeReblogs: self.applicationState.showReboostedStatuses) ?? [] + includeReblogs: self.applicationState.showReboostedStatuses) ?? Linkable(data: []) } private func calculateOffset() { diff --git a/Vernissage/Views/StatusesView.swift b/Vernissage/Views/StatusesView.swift index 29934d8..788392d 100644 --- a/Vernissage/Views/StatusesView.swift +++ b/Vernissage/Views/StatusesView.swift @@ -182,29 +182,29 @@ struct StatusesView: View { let statuses = try await self.loadFromApi() - if statuses.isEmpty { + if statuses.data.isEmpty { self.allItemsLoaded = true return } // Remember last status id returned by API. - self.lastStatusId = statuses.last?.id + self.lastStatusId = statuses.getMaxId() // Get only visible statuses. let visibleStatuses = HomeTimelineService.shared.getVisibleStatuses(accountId: accountId, - statuses: statuses, + statuses: statuses.data, hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt, modelContext: modelContext) if self.listType == .home { // Remeber first status returned by API in user context (when it's newer then remembered). try AccountDataHandler.shared.update(lastSeenStatusId: nil, - lastLoadedStatusId: statuses.first?.id, + lastLoadedStatusId: statuses.getMinId(), applicationState: self.applicationState, modelContext: modelContext) // Append statuses to viewed. - try ViewedStatusHandler.shared.append(contentsOf: statuses, accountId: accountId, modelContext: modelContext) + try ViewedStatusHandler.shared.append(contentsOf: statuses.data, accountId: accountId, modelContext: modelContext) } // Map to view models. @@ -221,25 +221,25 @@ struct StatusesView: View { if let lastStatusId = self.lastStatusId, let accountId = self.applicationState.account?.id { let statuses = try await self.loadFromApi(maxId: lastStatusId) - if statuses.isEmpty { + if statuses.data.isEmpty { self.allItemsLoaded = true return } // Now we have new last status. - if let lastStatusId = statuses.last?.id { + if let lastStatusId = statuses.getMaxId() { self.lastStatusId = lastStatusId } // Get only visible statuses. let visibleStatuses = HomeTimelineService.shared.getVisibleStatuses(accountId: accountId, - statuses: statuses, + statuses: statuses.data, hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt, modelContext: modelContext) if self.listType == .home { // Append statuses to viewed. - try ViewedStatusHandler.shared.append(contentsOf: statuses, accountId: accountId, modelContext: modelContext) + try ViewedStatusHandler.shared.append(contentsOf: statuses.data, accountId: accountId, modelContext: modelContext) } // Map to view models. @@ -260,29 +260,29 @@ struct StatusesView: View { let statuses = try await self.loadFromApi() - if statuses.isEmpty { + if statuses.data.isEmpty { self.allItemsLoaded = true return } // Remember last status id returned by API. - self.lastStatusId = statuses.last?.id + self.lastStatusId = statuses.getMaxId() // Get only visible statuses. let visibleStatuses = HomeTimelineService.shared.getVisibleStatuses(accountId: accountId, - statuses: statuses, + statuses: statuses.data, hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt, modelContext: modelContext) if self.listType == .home { // Remeber first status returned by API in user context (when it's newer then remembered). try AccountDataHandler.shared.update(lastSeenStatusId: self.statusViewModels.first?.id, - lastLoadedStatusId: statuses.first?.id, + lastLoadedStatusId: statuses.getMinId(), applicationState: self.applicationState, modelContext: modelContext) // Append statuses to viewed. - try ViewedStatusHandler.shared.append(contentsOf: statuses, accountId: accountId, modelContext: modelContext) + try ViewedStatusHandler.shared.append(contentsOf: statuses.data, accountId: accountId, modelContext: modelContext) } // Map to view models. @@ -296,7 +296,7 @@ struct StatusesView: View { self.statusViewModels = statusModels } - private func loadFromApi(maxId: String? = nil, sinceId: String? = nil, minId: String? = nil) async throws -> [Status] { + private func loadFromApi(maxId: String? = nil, sinceId: String? = nil, minId: String? = nil) async throws -> Linkable<[Status]> { switch self.listType { case .home: return try await self.client.publicTimeline?.getHomeTimeline( @@ -304,40 +304,44 @@ struct StatusesView: View { sinceId: sinceId, minId: minId, limit: self.defaultLimit, - includeReblogs: self.applicationState.showReboostedStatuses) ?? [] + includeReblogs: self.applicationState.showReboostedStatuses) ?? Linkable(data: []) case .local: return try await self.client.publicTimeline?.getStatuses( local: true, maxId: maxId, sinceId: sinceId, minId: minId, - limit: self.defaultLimit) ?? [] + limit: self.defaultLimit) ?? Linkable(data: []) case .federated: return try await self.client.publicTimeline?.getStatuses( remote: true, maxId: maxId, sinceId: sinceId, minId: minId, - limit: self.defaultLimit) ?? [] + limit: self.defaultLimit) ?? Linkable(data: []) case .favourites: - return try await self.client.accounts?.favourites( + let favourites = try await self.client.accounts?.favourites( maxId: maxId, sinceId: sinceId, minId: minId, limit: self.defaultLimit) ?? [] + + return Linkable(data: favourites) case .bookmarks: - return try await self.client.accounts?.bookmarks( + let bookmarks = try await self.client.accounts?.bookmarks( maxId: maxId, sinceId: sinceId, minId: minId, limit: self.defaultLimit) ?? [] + + return Linkable(data: bookmarks) case .hashtag(let tag): let hashtagsFromApi = try await self.client.search?.search(query: tag, resultsType: .hashtags) guard let hashtagsFromApi = hashtagsFromApi, hashtagsFromApi.hashtags.isEmpty == false else { ToastrService.shared.showError(title: LocalizedStringResource("global.error.hashtagNotExists"), imageSystemName: "exclamationmark.octagon") dismiss() - return [] + return Linkable(data: []) } return try await self.client.publicTimeline?.getTagStatuses( @@ -345,7 +349,7 @@ struct StatusesView: View { maxId: maxId, sinceId: sinceId, minId: minId, - limit: self.defaultLimit) ?? [] + limit: self.defaultLimit) ?? Linkable(data: []) } } diff --git a/Vernissage/Widgets/ImagesGrid.swift b/Vernissage/Widgets/ImagesGrid.swift index 74f1bcd..3bac12a 100644 --- a/Vernissage/Widgets/ImagesGrid.swift +++ b/Vernissage/Widgets/ImagesGrid.swift @@ -75,7 +75,7 @@ struct ImagesGrid: View { do { let statusesFromApi = try await self.loadStatuses() - let statusesWithImages = statusesFromApi.getStatusesWithImagesOnly() + let statusesWithImages = statusesFromApi.data.getStatusesWithImagesOnly() let photoUrls = self.getPhotoUrls(statuses: statusesWithImages) self.prefetch(photoUrls: photoUrls) @@ -119,15 +119,21 @@ struct ImagesGrid: View { } } - private func loadStatuses() async throws -> [Status] { + private func loadStatuses() async throws -> Linkable<[Status]> { switch self.gridType { case .hashtag(let name): return try await self.client.publicTimeline?.getTagStatuses( tag: name, local: true, - limit: 10) ?? [] + limit: 10) ?? Linkable(data: []) case .account(let accountId, _, _): - return try await self.client.accounts?.statuses(createdBy: accountId, onlyMedia: true, limit: 10) ?? [] + let accountStatuses = try await self.client.accounts?.statuses( + createdBy: accountId, + onlyMedia: true, + limit: 10 + ) ?? [] + + return Linkable(data: accountStatuses) } } diff --git a/VernissageWidget/PhotoWidget/Service/StatusFetcher.swift b/VernissageWidget/PhotoWidget/Service/StatusFetcher.swift index a786805..01e0483 100644 --- a/VernissageWidget/PhotoWidget/Service/StatusFetcher.swift +++ b/VernissageWidget/PhotoWidget/Service/StatusFetcher.swift @@ -33,7 +33,7 @@ public class StatusFetcher { let client = PixelfedClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken) let statuses = try await client.getHomeTimeline(limit: 20, includeReblogs: defaultSettings.showReboostedStatuses, timeoutInterval: 5.0) - let widgetEntries = await self.prepare(statuses: statuses, length: length) + let widgetEntries = await self.prepare(statuses: statuses.data, length: length) return widgetEntries } @@ -49,11 +49,11 @@ public class StatusFetcher { let accountData = AccountDataHandler.shared.getAccountData(accountId: accountId, modelContext: modelContext) guard let timelineCache = accountData?.timelineCache, let timelineCacheData = timelineCache.data(using: .utf8), - let statusesFromCache = try? JSONDecoder().decode([Status].self, from: timelineCacheData) else { + let statusesFromCache = try? JSONDecoder().decode(Linkable<[Status]>.self, from: timelineCacheData) else { return [self.placeholder()] } - let widgetEntries = await self.prepare(statuses: statusesFromCache, length: length) + let widgetEntries = await self.prepare(statuses: statusesFromCache.data, length: length) return widgetEntries }