mirror of
https://github.com/yattee/yattee.git
synced 2024-11-09 15:58:20 +00:00
149 lines
4.4 KiB
Swift
149 lines
4.4 KiB
Swift
|
import Foundation
|
||
|
import Siesta
|
||
|
|
||
|
final class SubscriptionsViewModel: ObservableObject {
|
||
|
static let shared = SubscriptionsViewModel()
|
||
|
|
||
|
@Published var isLoading = false
|
||
|
@Published var videos = [Video]()
|
||
|
@Published private var page = 1
|
||
|
|
||
|
private var accounts = AccountsModel.shared
|
||
|
|
||
|
var feed: Resource? {
|
||
|
accounts.api.feed(page)
|
||
|
}
|
||
|
|
||
|
func loadResources(force: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
||
|
DispatchQueue.global(qos: .background).async { [weak self] in
|
||
|
guard let self else { return }
|
||
|
|
||
|
if !force {
|
||
|
self.loadCachedFeed()
|
||
|
}
|
||
|
|
||
|
if self.accounts.app == .invidious {
|
||
|
// Invidious for some reason won't refresh feed until homepage is loaded
|
||
|
DispatchQueue.main.async { [weak self] in
|
||
|
guard let self, let home = self.accounts.api.home else { return }
|
||
|
self.request(home, force: force)?
|
||
|
.onCompletion { _ in
|
||
|
self.loadFeed(force: force, onCompletion: onCompletion)
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
self.loadFeed(force: force, onCompletion: onCompletion)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func loadFeed(force: Bool = false, paginating: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
||
|
DispatchQueue.main.async { [weak self] in
|
||
|
guard let self,
|
||
|
!self.isLoading,
|
||
|
let account = self.accounts.current
|
||
|
else {
|
||
|
self?.isLoading = false
|
||
|
onCompletion()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if paginating {
|
||
|
self.page += 1
|
||
|
} else {
|
||
|
self.page = 1
|
||
|
}
|
||
|
|
||
|
let feedBeforeLoad = self.feed
|
||
|
var request: Request?
|
||
|
if let feedBeforeLoad {
|
||
|
request = self.request(feedBeforeLoad, force: force)
|
||
|
}
|
||
|
if request != nil {
|
||
|
self.isLoading = true
|
||
|
}
|
||
|
|
||
|
request?
|
||
|
.onCompletion { _ in
|
||
|
self.isLoading = false
|
||
|
onCompletion()
|
||
|
}
|
||
|
.onSuccess { response in
|
||
|
if let videos: [Video] = response.typedContent() {
|
||
|
if paginating {
|
||
|
self.videos.append(contentsOf: videos)
|
||
|
} else {
|
||
|
self.videos = videos
|
||
|
FeedCacheModel.shared.storeFeed(account: account, videos: self.videos)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
.onFailure { error in
|
||
|
NavigationModel.shared.presentAlert(title: "Could not refresh Subscriptions", message: error.userMessage)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func reset() {
|
||
|
videos.removeAll()
|
||
|
page = 1
|
||
|
}
|
||
|
|
||
|
func loadNextPage() {
|
||
|
guard accounts.app.paginatesSubscriptions, !isLoading else { return }
|
||
|
|
||
|
loadFeed(force: true, paginating: true)
|
||
|
}
|
||
|
|
||
|
var feedTime: Date? {
|
||
|
if let account = accounts.current {
|
||
|
return FeedCacheModel.shared.getFeedTime(account: account)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var formattedFeedTime: 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 func loadCachedFeed() {
|
||
|
let cache = FeedCacheModel.shared.retrieveFeed(account: accounts.current)
|
||
|
if !cache.isEmpty {
|
||
|
DispatchQueue.main.async { [weak self] in
|
||
|
self?.videos = cache
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
private func request(_ resource: Resource, force: Bool = false) -> Request? {
|
||
|
if force {
|
||
|
return resource.load()
|
||
|
}
|
||
|
|
||
|
return resource.loadIfNeeded()
|
||
|
}
|
||
|
}
|