mastodon-ios/Mastodon/Scene/Thread/ThreadViewModel.swift

128 lines
4.0 KiB
Swift
Raw Normal View History

2021-04-13 13:46:42 +02:00
//
// ThreadViewModel.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-4-12.
//
import os.log
import UIKit
import Combine
import CoreData
import CoreDataStack
import GameplayKit
import MastodonSDK
2021-07-23 13:10:27 +02:00
import MastodonMeta
import MastodonAsset
2022-10-08 07:43:06 +02:00
import MastodonCore
import MastodonLocalization
2021-04-13 13:46:42 +02:00
class ThreadViewModel {
let logger = Logger(subsystem: "ThreadViewModel", category: "ViewModel")
2021-04-13 13:46:42 +02:00
var disposeBag = Set<AnyCancellable>()
var rootItemObserver: AnyCancellable?
2021-04-13 13:46:42 +02:00
// input
let context: AppContext
let authContext: AuthContext
let mastodonStatusThreadViewModel: MastodonStatusThreadViewModel
2021-04-13 13:46:42 +02:00
// output
var diffableDataSource: UITableViewDiffableDataSource<StatusSection, StatusItem>?
@Published var root: StatusItem.Thread?
@Published var threadContext: ThreadContext?
2021-04-13 13:46:42 +02:00
private(set) lazy var loadThreadStateMachine: GKStateMachine = {
let stateMachine = GKStateMachine(states: [
LoadThreadState.Initial(viewModel: self),
LoadThreadState.Loading(viewModel: self),
LoadThreadState.Fail(viewModel: self),
LoadThreadState.NoMore(viewModel: self),
])
stateMachine.enter(LoadThreadState.Initial.self)
return stateMachine
}()
@Published var navigationBarTitle: MastodonMetaContent?
2021-04-13 13:46:42 +02:00
init(
context: AppContext,
authContext: AuthContext,
optionalRoot: StatusItem.Thread?
) {
2021-04-13 13:46:42 +02:00
self.context = context
self.authContext = authContext
self.root = optionalRoot
self.mastodonStatusThreadViewModel = MastodonStatusThreadViewModel(context: context)
// end init
2021-04-13 13:46:42 +02:00
ManagedObjectObserver.observe(context: context.managedObjectContext)
.sink(receiveCompletion: { completion in
// do nohting
}, receiveValue: { [weak self] changes in
guard let self = self else { return }
2021-04-13 13:46:42 +02:00
let objectIDs: [NSManagedObjectID] = changes.changeTypes.compactMap { changeType in
guard case let .delete(object) = changeType else { return nil }
return object.objectID
2021-04-13 13:46:42 +02:00
}
self.delete(objectIDs: objectIDs)
})
2021-04-13 13:46:42 +02:00
.store(in: &disposeBag)
$root
2021-04-13 13:46:42 +02:00
.receive(on: DispatchQueue.main)
.sink { [weak self] root in
guard let self = self else { return }
guard case let .root(threadContext) = root else { return }
guard let status = threadContext.status.object(in: self.context.managedObjectContext) else { return }
2021-04-13 13:46:42 +02:00
// bind threadContext
self.threadContext = .init(
domain: status.domain,
statusID: status.id,
replyToID: status.inReplyToID
)
2021-04-13 13:46:42 +02:00
// bind titleView
self.navigationBarTitle = {
let title = L10n.Scene.Thread.title(status.author.displayNameWithFallback)
let content = MastodonContent(content: title, emojis: status.author.emojis.asDictionary)
return try? MastodonMetaContent.convert(document: content)
}()
2021-04-13 13:46:42 +02:00
}
.store(in: &disposeBag)
}
deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}
}
extension ThreadViewModel {
struct ThreadContext {
2021-04-13 13:46:42 +02:00
let domain: String
let statusID: Mastodon.Entity.Status.ID
let replyToID: Mastodon.Entity.Status.ID?
}
}
2021-04-13 13:46:42 +02:00
extension ThreadViewModel {
func delete(objectIDs: [NSManagedObjectID]) {
if let root = self.root,
case let .root(threadContext) = root,
objectIDs.contains(threadContext.status.objectID)
{
self.root = nil
2021-04-13 13:46:42 +02:00
}
self.mastodonStatusThreadViewModel.delete(objectIDs: objectIDs)
2021-04-13 13:46:42 +02:00
}
}