yattee/Model/Cache/SubscribedChannelsModel.swift

185 lines
5.9 KiB
Swift
Raw Normal View History

2022-12-10 20:08:03 +00:00
import Cache
2021-09-25 08:18:22 +00:00
import Foundation
2022-12-10 20:08:03 +00:00
import Logging
2021-09-25 08:18:22 +00:00
import Siesta
import SwiftUI
2022-12-10 20:08:03 +00:00
import SwiftyJSON
2021-09-25 08:18:22 +00:00
2022-12-12 09:21:46 +00:00
final class SubscribedChannelsModel: ObservableObject, CacheModel {
2022-12-11 15:15:42 +00:00
static var shared = SubscribedChannelsModel()
2022-12-10 20:08:03 +00:00
let logger = Logger(label: "stream.yattee.cache.channels")
static let diskConfig = DiskConfig(name: "channels")
static let memoryConfig = MemoryConfig()
2022-12-12 09:21:46 +00:00
let storage = try? Storage<String, JSON>(
2022-12-11 15:15:42 +00:00
diskConfig: SubscribedChannelsModel.diskConfig,
memoryConfig: SubscribedChannelsModel.memoryConfig,
fileManager: FileManager.default,
2022-12-12 09:21:46 +00:00
transformer: BaseCacheModel.jsonTransformer
2022-12-10 20:08:03 +00:00
)
@Published var isLoading = false
2021-09-25 08:18:22 +00:00
@Published var channels = [Channel]()
2022-12-16 08:35:10 +00:00
@Published var error: RequestError?
var accounts: AccountsModel { .shared }
var unwatchedFeedCount: UnwatchedFeedCountModel { .shared }
2021-10-16 22:48:58 +00:00
2021-10-20 22:21:50 +00:00
var resource: Resource? {
accounts.api.subscriptions
2021-09-25 08:18:22 +00:00
}
var all: [Channel] {
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
}
var allByUnwatchedCount: [Channel] {
if let account = accounts.current {
return all.sorted { c1, c2 in
let c1HasUnwatched = (unwatchedFeedCount.unwatchedByChannel[account]?[c1.id] ?? -1) > 0
let c2HasUnwatched = (unwatchedFeedCount.unwatchedByChannel[account]?[c2.id] ?? -1) > 0
let nameIncreasing = c1.name.lowercased() < c2.name.lowercased()
return c1HasUnwatched ? (c2HasUnwatched ? nameIncreasing : true) : (c2HasUnwatched ? false : nameIncreasing)
}
}
return all
}
2021-09-25 08:18:22 +00:00
func subscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
accounts.api.subscribe(channelID) {
self.scheduleLoad(onSuccess: onSuccess)
}
2021-09-25 08:18:22 +00:00
}
func unsubscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
accounts.api.unsubscribe(channelID) {
self.scheduleLoad(onSuccess: onSuccess)
}
2021-09-25 08:18:22 +00:00
}
func isSubscribing(_ channelID: String) -> Bool {
channels.contains { $0.id == channelID }
}
func load(force: Bool = false, onSuccess: @escaping () -> Void = {}) {
guard accounts.app.supportsSubscriptions, !isLoading, accounts.signedIn, let account = accounts.current else {
channels = []
return
}
DispatchQueue.main.async { [weak self] in
guard let self else { return }
2023-06-09 16:03:42 +00:00
let request = force ? self.resource?.load() : self.resource?.loadIfNeeded()
2023-06-09 15:45:51 +00:00
guard request != nil else { return }
2023-06-09 16:03:42 +00:00
self.loadCachedChannels(account)
2021-09-25 08:18:22 +00:00
self.isLoading = true
request?
.onCompletion { [weak self] _ in
self?.isLoading = false
}
.onSuccess { resource in
2022-12-16 08:35:10 +00:00
self.error = nil
if let channels: [Channel] = resource.typedContent() {
self.channels = channels
self.storeChannels(account: account, channels: channels)
2022-12-13 12:14:20 +00:00
FeedModel.shared.calculateUnwatchedFeed()
onSuccess()
}
}
2022-12-16 08:35:10 +00:00
.onFailure { self.error = $0 }
}
2021-09-25 08:18:22 +00:00
}
2022-12-10 20:08:03 +00:00
func loadCachedChannels(_ account: Account) {
let cache = getChannels(account: account)
if !cache.isEmpty {
DispatchQueue.main.async {
self.channels = cache
}
2022-12-10 20:08:03 +00:00
}
}
func storeChannels(account: Account, channels: [Channel]) {
DispatchQueue.global(qos: .background).async {
let date = self.iso8601DateFormatter.string(from: Date())
self.logger.info("caching channels \(self.channelsDateCacheKey(account)) -- \(date)")
2022-12-10 20:08:03 +00:00
channels.forEach { ChannelsCacheModel.shared.storeIfMissing($0) }
2022-12-13 23:07:32 +00:00
let dateObject: JSON = ["date": date]
let channelsObject: JSON = ["channels": channels.map(\.json).map(\.object)]
2022-12-10 20:08:03 +00:00
try? self.storage?.setObject(dateObject, forKey: self.channelsDateCacheKey(account))
try? self.storage?.setObject(channelsObject, forKey: self.channelsCacheKey(account))
}
2022-12-10 20:08:03 +00:00
}
func getChannels(account: Account) -> [Channel] {
logger.info("getting channels \(channelsDateCacheKey(account))")
2022-12-12 09:21:46 +00:00
if let json = try? storage?.object(forKey: channelsCacheKey(account)),
2022-12-10 20:08:03 +00:00
let channels = json.dictionaryValue["channels"]
{
return channels.arrayValue.compactMap { json in
2022-12-13 23:07:32 +00:00
let channel = Channel.from(json)
if !channel.hasExtendedDetails,
let cache = ChannelsCacheModel.shared.retrieve(channel.cacheKey)
{
return cache.channel
2022-12-13 23:07:32 +00:00
}
return channel
}
2022-12-10 20:08:03 +00:00
}
return []
}
private func scheduleLoad(onSuccess: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
2021-09-25 08:18:22 +00:00
self.load(force: true, onSuccess: onSuccess)
}
}
2022-12-10 20:08:03 +00:00
private func channelsCacheKey(_ account: Account) -> String {
"channels-\(account.id)"
}
private func channelsDateCacheKey(_ account: Account) -> String {
"channels-\(account.id)-date"
}
2022-12-12 09:21:46 +00:00
func getChannelsTime(account: Account) -> Date? {
if let json = try? storage?.object(forKey: channelsDateCacheKey(account)),
let string = json.dictionaryValue["date"]?.string,
2022-12-12 09:21:46 +00:00
let date = iso8601DateFormatter.date(from: string)
{
return date
}
return nil
}
2022-12-12 09:21:46 +00:00
var channelsTime: Date? {
if let account = accounts.current {
2022-12-12 09:21:46 +00:00
return getChannelsTime(account: account)
}
return nil
}
var formattedCacheTime: String {
2022-12-12 09:21:46 +00:00
getFormattedDate(channelsTime)
}
2022-12-20 22:51:04 +00:00
func onAccountChange() {
channels = []
load(force: true)
}
2021-09-25 08:18:22 +00:00
}