mastodon-ios/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel....

153 lines
6.2 KiB
Swift
Raw Normal View History

2021-04-21 08:46:31 +02:00
//
// SuggestionAccountViewModel.swift
// Mastodon
//
// Created by sxiaojian on 2021/4/21.
//
import Combine
import CoreData
import CoreDataStack
import GameplayKit
import MastodonSDK
import os.log
import UIKit
protocol SuggestionAccountViewModelDelegate: AnyObject {
func homeTimelineNeedRefresh()
}
2021-04-21 08:46:31 +02:00
final class SuggestionAccountViewModel: NSObject {
var disposeBag = Set<AnyCancellable>()
// input
let context: AppContext
2021-04-21 11:58:56 +02:00
weak var delegate: SuggestionAccountViewModelDelegate?
2021-04-21 11:58:56 +02:00
// output
2021-04-21 08:46:31 +02:00
let accounts = CurrentValueSubject<[NSManagedObjectID], Never>([])
var selectedAccounts = [NSManagedObjectID]()
2021-04-21 11:58:56 +02:00
var suggestionAccountsFallback = PassthroughSubject<Void, Never>()
2021-04-21 08:46:31 +02:00
2021-04-21 11:58:56 +02:00
var diffableDataSource: UITableViewDiffableDataSource<RecommendAccountSection, NSManagedObjectID>? {
didSet(value) {
if !accounts.value.isEmpty {
applyDataSource(accounts: accounts.value)
}
}
}
2021-04-21 08:46:31 +02:00
init(context: AppContext, accounts: [NSManagedObjectID]? = nil) {
self.context = context
2021-04-21 11:58:56 +02:00
2021-04-21 08:46:31 +02:00
super.init()
self.accounts
.receive(on: DispatchQueue.main)
.sink { [weak self] accounts in
2021-04-21 11:58:56 +02:00
self?.applyDataSource(accounts: accounts)
2021-04-21 08:46:31 +02:00
}
.store(in: &disposeBag)
2021-04-21 11:58:56 +02:00
if let accounts = accounts {
self.accounts.value = accounts
}
2021-04-21 08:46:31 +02:00
if accounts == nil || (accounts ?? []).isEmpty {
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
context.apiService.suggestionAccountV2(domain: activeMastodonAuthenticationBox.domain, query: nil, mastodonAuthenticationBox: activeMastodonAuthenticationBox)
2021-04-21 11:58:56 +02:00
.sink { [weak self] completion in
2021-04-21 08:46:31 +02:00
switch completion {
case .failure(let error):
2021-04-21 11:58:56 +02:00
if let apiError = error as? Mastodon.API.Error {
if apiError.httpResponseStatus == .notFound {
self?.suggestionAccountsFallback.send()
}
}
2021-04-21 08:46:31 +02:00
os_log("%{public}s[%{public}ld], %{public}s: fetch recommendAccountV2 failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
case .finished:
// handle isFetchingLatestTimeline in fetch controller delegate
break
}
} receiveValue: { [weak self] response in
let ids = response.value.map(\.account.id)
2021-04-21 11:58:56 +02:00
self?.receiveAccounts(ids: ids)
2021-04-21 08:46:31 +02:00
}
.store(in: &disposeBag)
2021-04-21 11:58:56 +02:00
suggestionAccountsFallback
.sink(receiveValue: { [weak self] _ in
self?.requestSuggestionAccount()
})
.store(in: &disposeBag)
2021-04-21 08:46:31 +02:00
}
}
2021-04-21 11:58:56 +02:00
func requestSuggestionAccount() {
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
context.apiService.suggestionAccount(domain: activeMastodonAuthenticationBox.domain, query: nil, mastodonAuthenticationBox: activeMastodonAuthenticationBox)
.sink { completion in
switch completion {
case .failure(let error):
os_log("%{public}s[%{public}ld], %{public}s: fetch recommendAccount failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
case .finished:
// handle isFetchingLatestTimeline in fetch controller delegate
break
}
} receiveValue: { [weak self] response in
let ids = response.value.map(\.id)
self?.receiveAccounts(ids: ids)
}
.store(in: &disposeBag)
}
func applyDataSource(accounts: [NSManagedObjectID]) {
guard let dataSource = diffableDataSource else { return }
var snapshot = NSDiffableDataSourceSnapshot<RecommendAccountSection, NSManagedObjectID>()
snapshot.appendSections([.main])
snapshot.appendItems(accounts, toSection: .main)
dataSource.apply(snapshot, animatingDifferences: false, completion: nil)
}
func receiveAccounts(ids: [String]) {
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
let users: [MastodonUser]? = {
let request = MastodonUser.sortedFetchRequest
request.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: ids)
request.returnsObjectsAsFaults = false
do {
return try context.managedObjectContext.fetch(request)
} catch {
assertionFailure(error.localizedDescription)
return nil
}
}()
if let accounts = users?.map(\.objectID) {
self.accounts.value = accounts
}
}
2021-04-21 08:46:31 +02:00
func followAction() {
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
for objectID in selectedAccounts {
let mastodonUser = context.managedObjectContext.object(with: objectID) as! MastodonUser
context.apiService.toggleFollow(
for: mastodonUser,
activeMastodonAuthenticationBox: activeMastodonAuthenticationBox,
needFeedback: false
)
.sink { completion in
switch completion {
case .failure(let error):
os_log("%{public}s[%{public}ld], %{public}s: follow failed. %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
case .finished:
self.delegate?.homeTimelineNeedRefresh()
2021-04-21 08:46:31 +02:00
break
}
} receiveValue: { _ in
}
.store(in: &disposeBag)
}
}
}