Tie JSON cache to domain based unique identifier

This commit is contained in:
Marcus Kida 2023-12-27 10:35:00 +01:00
parent 275fa53f80
commit 82cc0f2f3f
No known key found for this signature in database
GPG Key ID: 19FF64E08013CA40
6 changed files with 40 additions and 36 deletions

View File

@ -626,10 +626,10 @@ extension SceneCoordinator: SettingsCoordinatorDelegate {
try await self.appContext.authenticationService.signOutMastodonUser( try await self.appContext.authenticationService.signOutMastodonUser(
authenticationBox: authContext.mastodonAuthenticationBox authenticationBox: authContext.mastodonAuthenticationBox
) )
let userId = authContext.mastodonAuthenticationBox.userID let userIdentifier = authContext.mastodonAuthenticationBox
FileManager.default.invalidateHomeTimelineCache(for: userId) FileManager.default.invalidateHomeTimelineCache(for: userIdentifier)
FileManager.default.invalidateNotificationsAll(for: userId) FileManager.default.invalidateNotificationsAll(for: userIdentifier)
FileManager.default.invalidateNotificationsMentions(for: userId) FileManager.default.invalidateNotificationsMentions(for: userIdentifier)
self.setup() self.setup()
} }

View File

@ -8,15 +8,15 @@ extension FileManager {
private static let cacheItemsLimit: Int = 100 // max number of items to cache private static let cacheItemsLimit: Int = 100 // max number of items to cache
// Retrieve // Retrieve
func cachedHomeTimeline(for userId: String) throws -> [MastodonStatus] { func cachedHomeTimeline(for userId: UserIdentifier) throws -> [MastodonStatus] {
try cached(timeline: .homeTimeline(userId)).map(MastodonStatus.fromEntity) try cached(timeline: .homeTimeline(userId)).map(MastodonStatus.fromEntity)
} }
func cachedNotificationsAll(for userId: String) throws -> [Mastodon.Entity.Notification] { func cachedNotificationsAll(for userId: UserIdentifier) throws -> [Mastodon.Entity.Notification] {
try cached(timeline: .notificationsAll(userId)) try cached(timeline: .notificationsAll(userId))
} }
func cachedNotificationsMentions(for userId: String) throws -> [Mastodon.Entity.Notification] { func cachedNotificationsMentions(for userId: UserIdentifier) throws -> [Mastodon.Entity.Notification] {
try cached(timeline: .notificationsMentions(userId)) try cached(timeline: .notificationsMentions(userId))
} }
@ -38,16 +38,16 @@ extension FileManager {
} }
// Create // Create
func cacheHomeTimeline(items: [MastodonStatus], for userId: String) { func cacheHomeTimeline(items: [MastodonStatus], for userIdentifier: UserIdentifier) {
cache(items.map { $0.entity }, timeline: .homeTimeline(userId)) cache(items.map { $0.entity }, timeline: .homeTimeline(userIdentifier))
} }
func cacheNotificationsAll(items: [Mastodon.Entity.Notification], for userId: String) { func cacheNotificationsAll(items: [Mastodon.Entity.Notification], for userIdentifier: UserIdentifier) {
cache(items, timeline: .notificationsAll(userId)) cache(items, timeline: .notificationsAll(userIdentifier))
} }
func cacheNotificationsMentions(items: [Mastodon.Entity.Notification], for userId: String) { func cacheNotificationsMentions(items: [Mastodon.Entity.Notification], for userIdentifier: UserIdentifier) {
cache(items, timeline: .notificationsMentions(userId)) cache(items, timeline: .notificationsMentions(userIdentifier))
} }
private func cache<T: Encodable>(_ items: [T], timeline: Persistence) { private func cache<T: Encodable>(_ items: [T], timeline: Persistence) {
@ -71,15 +71,15 @@ extension FileManager {
} }
// Delete // Delete
func invalidateHomeTimelineCache(for userId: String) { func invalidateHomeTimelineCache(for userId: UserIdentifier) {
invalidate(timeline: .homeTimeline(userId)) invalidate(timeline: .homeTimeline(userId))
} }
func invalidateNotificationsAll(for userId: String) { func invalidateNotificationsAll(for userId: UserIdentifier) {
invalidate(timeline: .notificationsAll(userId)) invalidate(timeline: .notificationsAll(userId))
} }
func invalidateNotificationsMentions(for userId: String) { func invalidateNotificationsMentions(for userId: UserIdentifier) {
invalidate(timeline: .notificationsMentions(userId)) invalidate(timeline: .notificationsMentions(userId))
} }

View File

@ -386,10 +386,10 @@ extension HomeTimelineViewController {
@objc func signOutAction(_ sender: UIAction) { @objc func signOutAction(_ sender: UIAction) {
Task { @MainActor in Task { @MainActor in
try await context.authenticationService.signOutMastodonUser(authenticationBox: viewModel.authContext.mastodonAuthenticationBox) try await context.authenticationService.signOutMastodonUser(authenticationBox: viewModel.authContext.mastodonAuthenticationBox)
let userId = viewModel.authContext.mastodonAuthenticationBox.userID let userIdentifier = viewModel.authContext.mastodonAuthenticationBox
FileManager.default.invalidateHomeTimelineCache(for: userId) FileManager.default.invalidateHomeTimelineCache(for: userIdentifier)
FileManager.default.invalidateNotificationsAll(for: userId) FileManager.default.invalidateNotificationsAll(for: userIdentifier)
FileManager.default.invalidateNotificationsMentions(for: userId) FileManager.default.invalidateNotificationsMentions(for: userIdentifier)
self.coordinator.setup() self.coordinator.setup()
} }
} }

View File

@ -84,7 +84,7 @@ final class HomeTimelineViewModel: NSObject {
self.fetchedResultsController = FeedFetchedResultsController(context: context, authContext: authContext) self.fetchedResultsController = FeedFetchedResultsController(context: context, authContext: authContext)
self.homeTimelineNavigationBarTitleViewModel = HomeTimelineNavigationBarTitleViewModel(context: context) self.homeTimelineNavigationBarTitleViewModel = HomeTimelineNavigationBarTitleViewModel(context: context)
super.init() super.init()
self.fetchedResultsController.records = (try? FileManager.default.cachedHomeTimeline(for: authContext.mastodonAuthenticationBox.userID).map { self.fetchedResultsController.records = (try? FileManager.default.cachedHomeTimeline(for: authContext.mastodonAuthenticationBox).map {
MastodonFeed.fromStatus($0, kind: .home) MastodonFeed.fromStatus($0, kind: .home)
}) ?? [] }) ?? []
@ -111,7 +111,7 @@ final class HomeTimelineViewModel: NSObject {
guard let status = feed.status else { return nil } guard let status = feed.status else { return nil }
return status return status
} }
FileManager.default.cacheHomeTimeline(items: items, for: authContext.mastodonAuthenticationBox.userID) FileManager.default.cacheHomeTimeline(items: items, for: authContext.mastodonAuthenticationBox)
}) })
.store(in: &disposeBag) .store(in: &disposeBag)

View File

@ -56,11 +56,11 @@ final class NotificationTimelineViewModel {
switch scope { switch scope {
case .everything: case .everything:
self.feedFetchedResultsController.records = (try? FileManager.default.cachedNotificationsAll(for: authContext.mastodonAuthenticationBox.userID))?.map({ notification in self.feedFetchedResultsController.records = (try? FileManager.default.cachedNotificationsAll(for: authContext.mastodonAuthenticationBox))?.map({ notification in
MastodonFeed.fromNotification(notification, kind: .notificationAll) MastodonFeed.fromNotification(notification, kind: .notificationAll)
}) ?? [] }) ?? []
case .mentions: case .mentions:
self.feedFetchedResultsController.records = (try? FileManager.default.cachedNotificationsMentions(for: authContext.mastodonAuthenticationBox.userID))?.map({ notification in self.feedFetchedResultsController.records = (try? FileManager.default.cachedNotificationsMentions(for: authContext.mastodonAuthenticationBox))?.map({ notification in
MastodonFeed.fromNotification(notification, kind: .notificationMentions) MastodonFeed.fromNotification(notification, kind: .notificationMentions)
}) ?? [] }) ?? []
} }
@ -75,9 +75,9 @@ final class NotificationTimelineViewModel {
} }
switch self.scope { switch self.scope {
case .everything: case .everything:
FileManager.default.cacheNotificationsAll(items: items, for: authContext.mastodonAuthenticationBox.userID) FileManager.default.cacheNotificationsAll(items: items, for: authContext.mastodonAuthenticationBox)
case .mentions: case .mentions:
FileManager.default.cacheNotificationsMentions(items: items, for: authContext.mastodonAuthenticationBox.userID) FileManager.default.cacheNotificationsMentions(items: items, for: authContext.mastodonAuthenticationBox)
} }
}) })
.store(in: &disposeBag) .store(in: &disposeBag)

View File

@ -10,20 +10,24 @@ import Foundation
public enum Persistence { public enum Persistence {
case searchHistory case searchHistory
case homeTimeline(String) case homeTimeline(UserIdentifier)
case notificationsMentions(String) case notificationsMentions(UserIdentifier)
case notificationsAll(String) case notificationsAll(UserIdentifier)
private func uniqueUserDomainIdentifier(for userIdentifier: UserIdentifier) -> String {
"\(userIdentifier.userID)@\(userIdentifier.domain)"
}
private var filename: String { private var filename: String {
switch self { switch self {
case .searchHistory: case .searchHistory:
return "search_history" return "search_history" // todo: @zeitschlag should this be user-scoped as well?
case let .homeTimeline(userId): case let .homeTimeline(userIdentifier):
return "home_timeline_\(userId)" return "home_timeline_\(uniqueUserDomainIdentifier(for: userIdentifier))"
case let .notificationsMentions(userId): case let .notificationsMentions(userIdentifier):
return "notifications_mentions_\(userId)" return "notifications_mentions_\(uniqueUserDomainIdentifier(for: userIdentifier))"
case let .notificationsAll(userId): case let .notificationsAll(userIdentifier):
return "notifications_all_\(userId)" return "notifications_all_\(uniqueUserDomainIdentifier(for: userIdentifier))"
} }
} }