// // HashtagTimelineViewModel.swift // Mastodon // // Created by BradGao on 2021/3/30. // import os.log import UIKit import Combine import CoreData import CoreDataStack import GameplayKit import MastodonSDK final class HashtagTimelineViewModel: NSObject { let hashtag: String var disposeBag = Set() var needLoadMiddleIndex: Int? = nil // input let context: AppContext let fetchedResultsController: StatusFetchedResultsController let isFetchingLatestTimeline = CurrentValueSubject(false) let timelinePredicate = CurrentValueSubject(nil) let hashtagEntity = CurrentValueSubject(nil) weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate? weak var tableView: UITableView? // output // top loader private(set) lazy var loadLatestStateMachine: GKStateMachine = { // exclude timeline middle fetcher state let stateMachine = GKStateMachine(states: [ LoadLatestState.Initial(viewModel: self), LoadLatestState.Loading(viewModel: self), LoadLatestState.Fail(viewModel: self), LoadLatestState.Idle(viewModel: self), ]) stateMachine.enter(LoadLatestState.Initial.self) return stateMachine }() lazy var loadLatestStateMachinePublisher = CurrentValueSubject(nil) // bottom loader private(set) lazy var loadoldestStateMachine: GKStateMachine = { // exclude timeline middle fetcher state let stateMachine = GKStateMachine(states: [ LoadOldestState.Initial(viewModel: self), LoadOldestState.Loading(viewModel: self), LoadOldestState.Fail(viewModel: self), LoadOldestState.Idle(viewModel: self), LoadOldestState.NoMore(viewModel: self), ]) stateMachine.enter(LoadOldestState.Initial.self) return stateMachine }() lazy var loadOldestStateMachinePublisher = CurrentValueSubject(nil) // middle loader let loadMiddleSateMachineList = CurrentValueSubject<[NSManagedObjectID: GKStateMachine], Never>([:]) // TimelineIndex.objectID : middle loading state machine var diffableDataSource: UITableViewDiffableDataSource? var cellFrameCache = NSCache() init(context: AppContext, hashtag: String) { self.context = context self.hashtag = hashtag let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value self.fetchedResultsController = StatusFetchedResultsController(managedObjectContext: context.managedObjectContext, domain: activeMastodonAuthenticationBox?.domain, additionalTweetPredicate: nil) super.init() fetchedResultsController.objectIDs .receive(on: DispatchQueue.main) .sink { [weak self] objectIds in self?.generateStatusItems(newObjectIDs: objectIds) } .store(in: &disposeBag) } func fetchTag() { guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return } let query = Mastodon.API.Search.Query(q: hashtag, type: .hashtags) context.apiService.search(domain: activeMastodonAuthenticationBox.domain, query: query, mastodonAuthenticationBox: activeMastodonAuthenticationBox) .sink { _ in } receiveValue: { [weak self] response in let matchedTag = response.value.hashtags.first { tag -> Bool in return tag.name == self?.hashtag } self?.hashtagEntity.send(matchedTag) } .store(in: &disposeBag) } deinit { os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function) } }