mirror of
https://github.com/yattee/yattee.git
synced 2024-11-10 00:08:21 +00:00
177 lines
5.2 KiB
Swift
177 lines
5.2 KiB
Swift
import Cache
|
|
import Foundation
|
|
import Logging
|
|
import Siesta
|
|
import SwiftUI
|
|
import SwiftyJSON
|
|
|
|
final class SubsribedChannelsModel: ObservableObject {
|
|
static var shared = SubsribedChannelsModel()
|
|
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: SubsribedChannelsModel.diskConfig,
|
|
memoryConfig: SubsribedChannelsModel.memoryConfig,
|
|
transformer: CacheModel.jsonTransformer
|
|
)
|
|
|
|
@Published var isLoading = false
|
|
@Published var channels = [Channel]()
|
|
var accounts: AccountsModel { .shared }
|
|
|
|
var resource: Resource? {
|
|
accounts.api.subscriptions
|
|
}
|
|
|
|
var all: [Channel] {
|
|
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
|
}
|
|
|
|
func subscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
|
accounts.api.subscribe(channelID) {
|
|
self.scheduleLoad(onSuccess: onSuccess)
|
|
}
|
|
}
|
|
|
|
func unsubscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
|
accounts.api.unsubscribe(channelID) {
|
|
self.scheduleLoad(onSuccess: onSuccess)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
loadCachedChannels(account)
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self else { return }
|
|
let request = force ? self.resource?.load() : self.resource?.loadIfNeeded()
|
|
|
|
if request != nil {
|
|
self.isLoading = true
|
|
}
|
|
|
|
request?
|
|
.onCompletion { [weak self] _ in
|
|
self?.isLoading = false
|
|
}
|
|
.onSuccess { resource in
|
|
if let channels: [Channel] = resource.typedContent() {
|
|
self.channels = channels
|
|
self.storeChannels(account: account, channels: channels)
|
|
onSuccess()
|
|
}
|
|
}
|
|
.onFailure { _ in
|
|
self.channels = []
|
|
}
|
|
}
|
|
}
|
|
|
|
func loadCachedChannels(_ account: Account) {
|
|
let cache = getChannels(account: account)
|
|
if !cache.isEmpty {
|
|
DispatchQueue.main.async {
|
|
self.channels = cache
|
|
}
|
|
}
|
|
}
|
|
|
|
func storeChannels(account: Account, channels: [Channel]) {
|
|
let date = iso8601DateFormatter.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) {
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
self.load(force: true, onSuccess: onSuccess)
|
|
}
|
|
}
|
|
|
|
private var iso8601DateFormatter: ISO8601DateFormatter {
|
|
.init()
|
|
}
|
|
|
|
private func channelsCacheKey(_ account: Account) -> String {
|
|
"channels-\(account.id)"
|
|
}
|
|
|
|
private func channelsDateCacheKey(_ account: Account) -> String {
|
|
"channels-\(account.id)-date"
|
|
}
|
|
|
|
func getFeedTime(account: Account) -> Date? {
|
|
if let json = try? storage.object(forKey: channelsDateCacheKey(account)),
|
|
let string = json.dictionaryValue["date"]?.string,
|
|
let date = iso8601DateFormatter.date(from: string)
|
|
{
|
|
return date
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var feedTime: Date? {
|
|
if let account = accounts.current {
|
|
return getFeedTime(account: account)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var formattedCacheTime: String {
|
|
if let feedTime {
|
|
let isSameDay = Calendar(identifier: .iso8601).isDate(feedTime, inSameDayAs: Date())
|
|
let formatter = isSameDay ? dateFormatterForTimeOnly : dateFormatter
|
|
return formatter.string(from: feedTime)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
private var dateFormatter: DateFormatter {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .short
|
|
formatter.timeStyle = .medium
|
|
|
|
return formatter
|
|
}
|
|
|
|
private var dateFormatterForTimeOnly: DateFormatter {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .none
|
|
formatter.timeStyle = .medium
|
|
|
|
return formatter
|
|
}
|
|
}
|