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
|
|
|
|
|
2021-04-21 12:52:09 +02:00
|
|
|
protocol SuggestionAccountViewModelDelegate: AnyObject {
|
2021-04-22 04:11:19 +02:00
|
|
|
var homeTimelineNeedRefresh: PassthroughSubject<Void, Never> { get }
|
2021-04-21 12:52:09 +02:00
|
|
|
}
|
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
|
|
|
|
2021-04-21 12:52:09 +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:
|
2021-04-22 04:11:19 +02:00
|
|
|
self.delegate?.homeTimelineNeedRefresh.send()
|
2021-04-21 08:46:31 +02:00
|
|
|
break
|
|
|
|
}
|
|
|
|
} receiveValue: { _ in
|
|
|
|
}
|
|
|
|
.store(in: &disposeBag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|