From b8f693e21394a04b25299f20f093900233bf1e2c Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Fri, 16 Dec 2022 12:31:43 +0100 Subject: [PATCH] Caching for favorites --- Model/Playlist.swift | 4 +- Model/PlaylistsModel.swift | 4 +- Shared/Channels/ChannelPlaylistView.swift | 13 ++-- Shared/Home/FavoriteItemView.swift | 80 ++++++++++++++++++----- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/Model/Playlist.swift b/Model/Playlist.swift index 95a8fd31..30829191 100644 --- a/Model/Playlist.swift +++ b/Model/Playlist.swift @@ -40,7 +40,7 @@ struct Playlist: Identifiable, Equatable, Hashable { } var json: JSON { - return [ + [ "id": id, "title": title, "visibility": visibility.rawValue, @@ -51,7 +51,7 @@ struct Playlist: Identifiable, Equatable, Hashable { } static func from(_ json: JSON) -> Self { - return .init( + .init( id: json["id"].stringValue, title: json["title"].stringValue, visibility: .init(rawValue: json["visibility"].stringValue) ?? .public, diff --git a/Model/PlaylistsModel.swift b/Model/PlaylistsModel.swift index 129ccff0..24027e45 100644 --- a/Model/PlaylistsModel.swift +++ b/Model/PlaylistsModel.swift @@ -38,13 +38,15 @@ final class PlaylistsModel: ObservableObject { } func load(force: Bool = false, onSuccess: @escaping () -> Void = {}) { - guard accounts.app.supportsUserPlaylists, accounts.signedIn, let account = accounts.current else { + guard accounts.app.supportsUserPlaylists, let account = accounts.current else { playlists = [] return } loadCachedPlaylists(account) + guard accounts.signedIn else { return } + DispatchQueue.main.async { [weak self] in guard let self else { return } let request = force ? self.resource?.load() : self.resource?.loadIfNeeded() diff --git a/Shared/Channels/ChannelPlaylistView.swift b/Shared/Channels/ChannelPlaylistView.swift index 0e6e46f1..2c9dca2c 100644 --- a/Shared/Channels/ChannelPlaylistView.swift +++ b/Shared/Channels/ChannelPlaylistView.swift @@ -67,12 +67,15 @@ struct ChannelPlaylistView: View { } .environment(\.listingStyle, channelPlaylistListingStyle) .onAppear { - if navigationStyle == .tab { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - resource?.loadIfNeeded() + if let playlist = presentedPlaylist, + let cache = ChannelPlaylistsCacheModel.shared.retrievePlaylist(playlist.id) + { + store.replace(cache) + } + resource?.loadIfNeeded()?.onSuccess { response in + if let playlist: ChannelPlaylist = response.typedContent() { + ChannelPlaylistsCacheModel.shared.storePlaylist(playlist: playlist) } - } else { - resource?.loadIfNeeded() } } #if os(tvOS) diff --git a/Shared/Home/FavoriteItemView.swift b/Shared/Home/FavoriteItemView.swift index aa4b01f9..85f73edf 100644 --- a/Shared/Home/FavoriteItemView.swift +++ b/Shared/Home/FavoriteItemView.swift @@ -45,21 +45,73 @@ struct FavoriteItemView: View { .contentShape(Rectangle()) .onAppear { resource?.addObserver(store) - if item.section == .subscriptions { - cacheFeed(resource?.loadIfNeeded()) - } else { - resource?.loadIfNeeded() - } + loadCacheAndResource() } } } .onChange(of: accounts.current) { _ in resource?.addObserver(store) - if item.section == .subscriptions { - cacheFeed(resource?.load()) - } else { - resource?.load() + loadCacheAndResource(force: true) + } + } + + func loadCacheAndResource(force: Bool = false) { + guard var resource else { return } + + var onSuccess: (Entity) -> Void = { _ in } + var contentItems = [ContentItem]() + + switch item.section { + case .subscriptions: + let feed = FeedCacheModel.shared.retrieveFeed(account: accounts.current) + contentItems = ContentItem.array(of: feed) + + onSuccess = { response in + if let videos: [Video] = response.typedContent() { + FeedCacheModel.shared.storeFeed(account: accounts.current, videos: videos) + } } + case let .channel(_, id, name): + let channel = Channel(app: .invidious, id: id, name: name) + if let cache = ChannelsCacheModel.shared.retrieve(channel.cacheKey) { + contentItems = ContentItem.array(of: cache.videos) + } + + onSuccess = { response in + if let channel: Channel = response.typedContent() { + ChannelsCacheModel.shared.store(channel) + } + } + case let .channelPlaylist(_, id, _): + if let cache = ChannelPlaylistsCacheModel.shared.retrievePlaylist(id), + !cache.videos.isEmpty + { + contentItems = ContentItem.array(of: cache.videos) + } + + onSuccess = { response in + if let playlist: ChannelPlaylist = response.typedContent() { + ChannelPlaylistsCacheModel.shared.storePlaylist(playlist: playlist) + } + } + case let .playlist(_, id): + let playlists = PlaylistsCacheModel.shared.retrievePlaylists(account: accounts.current) + + if let playlist = playlists.first(where: { $0.id == id }) { + contentItems = ContentItem.array(of: playlist.videos) + } + default: + contentItems = [] + } + + if !contentItems.isEmpty { + store.contentItems = contentItems + } + + if force { + resource.load().onSuccess(onSuccess) + } else { + resource.loadIfNeeded()?.onSuccess(onSuccess) } } @@ -173,18 +225,10 @@ struct FavoriteItemView: View { .padding(.trailing, 10) } - private func cacheFeed(_ request: Request?) { - request?.onSuccess { response in - if let videos: [Video] = response.typedContent() { - FeedCacheModel.shared.storeFeed(account: accounts.current, videos: videos) - } - } - } - private var isVisible: Bool { switch item.section { case .subscriptions: - return accounts.app.supportsSubscriptions && accounts.signedIn + return accounts.app.supportsSubscriptions case .popular: return accounts.app.supportsPopular case let .channel(appType, _, _):