Subscribed channels cache

This commit is contained in:
Arkadiusz Fal 2022-12-10 21:08:03 +01:00
parent 971beddc8d
commit e9c219a76e
9 changed files with 97 additions and 23 deletions

View File

@ -0,0 +1,17 @@
import Cache
import Foundation
import Logging
import SwiftyJSON
struct BookmarksCacheModel {
static var shared = BookmarksCacheModel()
let logger = Logger(label: "stream.yattee.cache")
static let bookmarksGroup = "group.stream.yattee.app.bookmarks"
let defaults = UserDefaults(suiteName: Self.bookmarksGroup)
func clear() {
guard let defaults else { return }
defaults.dictionaryRepresentation().keys.forEach(defaults.removeObject(forKey:))
}
}

View File

@ -10,16 +10,6 @@ struct CacheModel {
static let jsonFromDataTransformer: (Data) -> JSON = { try! JSON(data: $0) } static let jsonFromDataTransformer: (Data) -> JSON = { try! JSON(data: $0) }
static let jsonTransformer = Transformer(toData: jsonToDataTransformer, fromData: jsonFromDataTransformer) static let jsonTransformer = Transformer(toData: jsonToDataTransformer, fromData: jsonFromDataTransformer)
let logger = Logger(label: "stream.yattee.cache")
static let bookmarksGroup = "group.stream.yattee.app.bookmarks"
let bookmarksDefaults = UserDefaults(suiteName: Self.bookmarksGroup)
func clearBookmarks() {
guard let bookmarksDefaults else { return }
bookmarksDefaults.dictionaryRepresentation().keys.forEach(bookmarksDefaults.removeObject(forKey:))
}
func clear() { func clear() {
FeedCacheModel.shared.clear() FeedCacheModel.shared.clear()
VideosCacheModel.shared.clear() VideosCacheModel.shared.clear()

View File

@ -103,7 +103,7 @@ extension PlayerModel {
func removeHistory() { func removeHistory() {
removeAllWatches() removeAllWatches()
CacheModel.shared.clearBookmarks() BookmarksCacheModel.shared.clear()
} }
func removeWatch(_ watch: Watch) { func removeWatch(_ watch: Watch) {

View File

@ -1,9 +1,22 @@
import Cache
import Foundation import Foundation
import Logging
import Siesta import Siesta
import SwiftUI import SwiftUI
import SwiftyJSON
final class SubscriptionsModel: ObservableObject { final class SubscriptionsModel: ObservableObject {
static var shared = SubscriptionsModel() static var shared = SubscriptionsModel()
let logger = Logger(label: "stream.yattee.cache.channels")
static let diskConfig = DiskConfig(name: "channels")
static let memoryConfig = MemoryConfig()
let storage = try! Storage<String, JSON>(
diskConfig: SubscriptionsModel.diskConfig,
memoryConfig: SubscriptionsModel.memoryConfig,
transformer: CacheModel.jsonTransformer
)
@Published var channels = [Channel]() @Published var channels = [Channel]()
var accounts: AccountsModel { .shared } var accounts: AccountsModel { .shared }
@ -33,17 +46,20 @@ final class SubscriptionsModel: ObservableObject {
} }
func load(force: Bool = false, onSuccess: @escaping () -> Void = {}) { func load(force: Bool = false, onSuccess: @escaping () -> Void = {}) {
guard accounts.app.supportsSubscriptions, accounts.signedIn else { guard accounts.app.supportsSubscriptions, accounts.signedIn, let account = accounts.current else {
channels = [] channels = []
return return
} }
loadCachedChannels(account)
let request = force ? resource?.load() : resource?.loadIfNeeded() let request = force ? resource?.load() : resource?.loadIfNeeded()
request? request?
.onSuccess { resource in .onSuccess { resource in
if let channels: [Channel] = resource.typedContent() { if let channels: [Channel] = resource.typedContent() {
self.channels = channels self.channels = channels
self.storeChannels(account: account, channels: channels)
onSuccess() onSuccess()
} }
} }
@ -52,9 +68,51 @@ final class SubscriptionsModel: ObservableObject {
} }
} }
func loadCachedChannels(_ account: Account) {
let cache = getChannels(account: account)
if !cache.isEmpty {
channels = cache
}
}
func storeChannels(account: Account, channels: [Channel]) {
let date = dateFormatter.string(from: Date())
logger.info("caching channels \(channelsDateCacheKey(account)) -- \(date)")
let dateObject: JSON = ["date": date]
let channelsObject: JSON = ["channels": channels.map(\.json).map(\.object)]
try? storage.setObject(dateObject, forKey: channelsDateCacheKey(account))
try? storage.setObject(channelsObject, forKey: channelsCacheKey(account))
}
func getChannels(account: Account) -> [Channel] {
logger.info("getting channels \(channelsDateCacheKey(account))")
if let json = try? storage.object(forKey: channelsCacheKey(account)),
let channels = json.dictionaryValue["channels"]
{
return channels.arrayValue.map { Channel.from($0) }
}
return []
}
private func scheduleLoad(onSuccess: @escaping () -> Void) { private func scheduleLoad(onSuccess: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.load(force: true, onSuccess: onSuccess) self.load(force: true, onSuccess: onSuccess)
} }
} }
private var dateFormatter: ISO8601DateFormatter {
.init()
}
private func channelsCacheKey(_ account: Account) -> String {
"channels-\(account.id)"
}
private func channelsDateCacheKey(_ account: Account) -> String {
"channels-\(account.id)-date"
}
} }

View File

@ -8,7 +8,7 @@ struct URLBookmarkModel {
var logger = Logger(label: "stream.yattee.url-bookmark") var logger = Logger(label: "stream.yattee.url-bookmark")
var allBookmarksKeys: [String] { var allBookmarksKeys: [String] {
guard let defaults = CacheModel.shared.bookmarksDefaults else { return [] } guard let defaults = BookmarksCacheModel.shared.defaults else { return [] }
return defaults.dictionaryRepresentation().keys.filter { $0.starts(with: Self.bookmarkPrefix) } return defaults.dictionaryRepresentation().keys.filter { $0.starts(with: Self.bookmarkPrefix) }
} }
@ -29,7 +29,7 @@ struct URLBookmarkModel {
return return
} }
guard let defaults = CacheModel.shared.bookmarksDefaults else { guard let defaults = BookmarksCacheModel.shared.defaults else {
logger.error("could not open bookmarks defaults") logger.error("could not open bookmarks defaults")
return return
} }
@ -49,7 +49,7 @@ struct URLBookmarkModel {
return return
} }
guard let defaults = CacheModel.shared.bookmarksDefaults else { guard let defaults = BookmarksCacheModel.shared.defaults else {
logger.error("could not open bookmarks defaults") logger.error("could not open bookmarks defaults")
return return
} }
@ -70,7 +70,7 @@ struct URLBookmarkModel {
logger.info("loading bookmark for \(bookmarkKey(urlForBookmark))") logger.info("loading bookmark for \(bookmarkKey(urlForBookmark))")
guard let defaults = CacheModel.shared.bookmarksDefaults else { guard let defaults = BookmarksCacheModel.shared.defaults else {
logger.error("could not open bookmarks defaults") logger.error("could not open bookmarks defaults")
return nil return nil
} }
@ -103,7 +103,7 @@ struct URLBookmarkModel {
func removeBookmark(_ url: URL) { func removeBookmark(_ url: URL) {
logger.info("removing bookmark for \(bookmarkKey(url))") logger.info("removing bookmark for \(bookmarkKey(url))")
guard let defaults = CacheModel.shared.bookmarksDefaults else { guard let defaults = BookmarksCacheModel.shared.defaults else {
logger.error("could not open bookmarks defaults") logger.error("could not open bookmarks defaults")
return return
} }

View File

@ -42,7 +42,8 @@ struct ContentView: View {
subscriptions.load(force: true) subscriptions.load(force: true)
playlists.load(force: true) playlists.load(force: true)
} }
.onChange(of: accounts.signedIn) { _ in .onChange(of: accounts.signedIn) { newValue in
guard newValue else { return }
subscriptions.load(force: true) subscriptions.load(force: true)
playlists.load(force: true) playlists.load(force: true)
} }

View File

@ -16,6 +16,8 @@ struct SubscriptionsView: View {
HStack { HStack {
Spacer() Spacer()
CacheStatusHeader(refreshTime: model.formattedFeedTime, isLoading: model.isLoading)
#if os(tvOS) #if os(tvOS)
Button { Button {
model.loadResources(force: true) model.loadResources(force: true)
@ -27,8 +29,7 @@ struct SubscriptionsView: View {
} }
.padding(.horizontal, 10) .padding(.horizontal, 10)
#endif #endif
}
CacheStatusHeader(refreshTime: model.formattedFeedTime, isLoading: model.isLoading)
} }
.environment(\.loadMoreContentHandler) { model.loadNextPage() } .environment(\.loadMoreContentHandler) { model.loadNextPage() }
.onAppear { .onAppear {

View File

@ -162,7 +162,6 @@ struct YatteeApp: App {
NavigationModel.shared.tabSelection = section ?? .search NavigationModel.shared.tabSelection = section ?? .search
subscriptions.load()
playlists.load() playlists.load()
#if !os(macOS) #if !os(macOS)

View File

@ -82,7 +82,6 @@
37095E82291DC85400301883 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37095E81291DC85400301883 /* ShareViewController.swift */; }; 37095E82291DC85400301883 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37095E81291DC85400301883 /* ShareViewController.swift */; };
37095E89291DC85400301883 /* Open in Yattee.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 37095E7F291DC85400301883 /* Open in Yattee.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 37095E89291DC85400301883 /* Open in Yattee.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 37095E7F291DC85400301883 /* Open in Yattee.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F5E8B5291BE9D0006C15F5 /* URLBookmarkModel.swift */; }; 37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F5E8B5291BE9D0006C15F5 /* URLBookmarkModel.swift */; };
37095E8E291DD5FE00301883 /* CacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F5E8B9291BEF69006C15F5 /* CacheModel.swift */; };
370B79C9286279810045DB77 /* NSObject+Swizzle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370B79C8286279810045DB77 /* NSObject+Swizzle.swift */; }; 370B79C9286279810045DB77 /* NSObject+Swizzle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370B79C8286279810045DB77 /* NSObject+Swizzle.swift */; };
370B79CC286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370B79CB286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift */; }; 370B79CC286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370B79CB286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift */; };
370F4FA927CC163A001B35DC /* PlayerBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C327AF0DA800F1C24B /* PlayerBackend.swift */; }; 370F4FA927CC163A001B35DC /* PlayerBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EBD8C327AF0DA800F1C24B /* PlayerBackend.swift */; };
@ -259,6 +258,10 @@
3736A219286BB72300C9E5EE /* libharfbuzz.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1FC286BB72300C9E5EE /* libharfbuzz.xcframework */; }; 3736A219286BB72300C9E5EE /* libharfbuzz.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1FC286BB72300C9E5EE /* libharfbuzz.xcframework */; };
3736A21A286BB72300C9E5EE /* libmpv.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1FD286BB72300C9E5EE /* libmpv.xcframework */; }; 3736A21A286BB72300C9E5EE /* libmpv.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1FD286BB72300C9E5EE /* libmpv.xcframework */; };
3736A21B286BB72300C9E5EE /* libmpv.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1FD286BB72300C9E5EE /* libmpv.xcframework */; }; 3736A21B286BB72300C9E5EE /* libmpv.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3736A1FD286BB72300C9E5EE /* libmpv.xcframework */; };
3738535429451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */; };
3738535529451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */; };
3738535629451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */; };
3738535729451E0C00D2D0CB /* BookmarksCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */; };
373C8FE4275B955100CB5936 /* CommentsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373C8FE3275B955100CB5936 /* CommentsPage.swift */; }; 373C8FE4275B955100CB5936 /* CommentsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373C8FE3275B955100CB5936 /* CommentsPage.swift */; };
373C8FE5275B955100CB5936 /* CommentsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373C8FE3275B955100CB5936 /* CommentsPage.swift */; }; 373C8FE5275B955100CB5936 /* CommentsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373C8FE3275B955100CB5936 /* CommentsPage.swift */; };
373C8FE6275B955100CB5936 /* CommentsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373C8FE3275B955100CB5936 /* CommentsPage.swift */; }; 373C8FE6275B955100CB5936 /* CommentsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373C8FE3275B955100CB5936 /* CommentsPage.swift */; };
@ -1128,6 +1131,7 @@
3736A1FB286BB72300C9E5EE /* libavfilter.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libavfilter.xcframework; sourceTree = "<group>"; }; 3736A1FB286BB72300C9E5EE /* libavfilter.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libavfilter.xcframework; sourceTree = "<group>"; };
3736A1FC286BB72300C9E5EE /* libharfbuzz.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libharfbuzz.xcframework; sourceTree = "<group>"; }; 3736A1FC286BB72300C9E5EE /* libharfbuzz.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libharfbuzz.xcframework; sourceTree = "<group>"; };
3736A1FD286BB72300C9E5EE /* libmpv.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libmpv.xcframework; sourceTree = "<group>"; }; 3736A1FD286BB72300C9E5EE /* libmpv.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libmpv.xcframework; sourceTree = "<group>"; };
3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksCacheModel.swift; sourceTree = "<group>"; };
373C8FE3275B955100CB5936 /* CommentsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsPage.swift; sourceTree = "<group>"; }; 373C8FE3275B955100CB5936 /* CommentsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsPage.swift; sourceTree = "<group>"; };
373CFACA26966264003CB2C6 /* SearchQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchQuery.swift; sourceTree = "<group>"; }; 373CFACA26966264003CB2C6 /* SearchQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchQuery.swift; sourceTree = "<group>"; };
373CFADA269663F1003CB2C6 /* Thumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnail.swift; sourceTree = "<group>"; }; 373CFADA269663F1003CB2C6 /* Thumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnail.swift; sourceTree = "<group>"; };
@ -1992,6 +1996,7 @@
377F9F79294403DC0043F856 /* Cache */ = { 377F9F79294403DC0043F856 /* Cache */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */,
37F5E8B9291BEF69006C15F5 /* CacheModel.swift */, 37F5E8B9291BEF69006C15F5 /* CacheModel.swift */,
377F9F7E2944175F0043F856 /* FeedCacheModel.swift */, 377F9F7E2944175F0043F856 /* FeedCacheModel.swift */,
377F9F7A294403F20043F856 /* VideosCacheModel.swift */, 377F9F7A294403F20043F856 /* VideosCacheModel.swift */,
@ -2878,8 +2883,8 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
3738535729451E0C00D2D0CB /* BookmarksCacheModel.swift in Sources */,
37FD77042932C5FC00D91A5F /* Constants.swift in Sources */, 37FD77042932C5FC00D91A5F /* Constants.swift in Sources */,
37095E8E291DD5FE00301883 /* CacheModel.swift in Sources */,
37095E82291DC85400301883 /* ShareViewController.swift in Sources */, 37095E82291DC85400301883 /* ShareViewController.swift in Sources */,
37FD77032932C5EC00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */, 37FD77032932C5EC00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */,
37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */, 37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */,
@ -3046,6 +3051,7 @@
37169AA22729D98A0011DE61 /* InstancesBridge.swift in Sources */, 37169AA22729D98A0011DE61 /* InstancesBridge.swift in Sources */,
37C3A24527235DA70087A57A /* ChannelPlaylist.swift in Sources */, 37C3A24527235DA70087A57A /* ChannelPlaylist.swift in Sources */,
37030FFF27B04DCC00ECDDAA /* PlayerControls.swift in Sources */, 37030FFF27B04DCC00ECDDAA /* PlayerControls.swift in Sources */,
3738535429451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */,
37E6D7A02944CD3800550C3D /* CacheStatusHeader.swift in Sources */, 37E6D7A02944CD3800550C3D /* CacheStatusHeader.swift in Sources */,
374C053F272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */, 374C053F272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
375F7410289DC35A00747050 /* PlayerBackendView.swift in Sources */, 375F7410289DC35A00747050 /* PlayerBackendView.swift in Sources */,
@ -3169,6 +3175,7 @@
37CEE4BE2677B670005A1EFE /* SingleAssetStream.swift in Sources */, 37CEE4BE2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
3703100327B0713600ECDDAA /* PlayerGestures.swift in Sources */, 3703100327B0713600ECDDAA /* PlayerGestures.swift in Sources */,
374C0540272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */, 374C0540272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
3738535529451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */,
379F1420289ECE7F00DE48B5 /* QualitySettings.swift in Sources */, 379F1420289ECE7F00DE48B5 /* QualitySettings.swift in Sources */,
3751BA8027E64244007B1A60 /* VideoLayer.swift in Sources */, 3751BA8027E64244007B1A60 /* VideoLayer.swift in Sources */,
375EC96B289F232600751258 /* QualityProfilesModel.swift in Sources */, 375EC96B289F232600751258 /* QualityProfilesModel.swift in Sources */,
@ -3530,6 +3537,7 @@
376A33E22720CAD6000C1D6B /* VideosApp.swift in Sources */, 376A33E22720CAD6000C1D6B /* VideosApp.swift in Sources */,
373CFADD269663F1003CB2C6 /* Thumbnail.swift in Sources */, 373CFADD269663F1003CB2C6 /* Thumbnail.swift in Sources */,
376B0562293FF45F0062AC78 /* PeerTubeAPI.swift in Sources */, 376B0562293FF45F0062AC78 /* PeerTubeAPI.swift in Sources */,
3738535629451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */,
37E64DD326D597EB00C71877 /* SubscriptionsModel.swift in Sources */, 37E64DD326D597EB00C71877 /* SubscriptionsModel.swift in Sources */,
3752069B285E8DD300CA655F /* Chapter.swift in Sources */, 3752069B285E8DD300CA655F /* Chapter.swift in Sources */,
37B044B926F7AB9000E1419D /* SettingsView.swift in Sources */, 37B044B926F7AB9000E1419D /* SettingsView.swift in Sources */,