123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- //
- // HashtagTimelineViewController.swift
- // Mastodon
- //
- // Created by BradGao on 2021/3/30.
- //
- import os.log
- import UIKit
- import AVKit
- import Combine
- import GameplayKit
- import CoreData
- import MastodonAsset
- import MastodonCore
- import MastodonUI
- import MastodonLocalization
- final class HashtagTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
-
- let logger = Logger(subsystem: "HashtagTimelineViewController", category: "ViewController")
-
- weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
- weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
-
- let mediaPreviewTransitionController = MediaPreviewTransitionController()
- var disposeBag = Set<AnyCancellable>()
- var viewModel: HashtagTimelineViewModel!
-
- let composeBarButtonItem: UIBarButtonItem = {
- let barButtonItem = UIBarButtonItem()
- barButtonItem.image = Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate)
- return barButtonItem
- }()
-
- let titleView = DoubleTitleLabelNavigationBarTitleView()
-
- let tableView: UITableView = {
- let tableView = ControlContainableTableView()
- tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self))
- tableView.register(TimelineMiddleLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineMiddleLoaderTableViewCell.self))
- tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
- tableView.rowHeight = UITableView.automaticDimension
- tableView.separatorStyle = .none
- tableView.backgroundColor = .clear
-
- return tableView
- }()
-
- let refreshControl = RefreshControl()
-
- deinit {
- os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
- }
- }
- extension HashtagTimelineViewController {
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- let _title = "#\(viewModel.hashtag)"
- title = _title
- titleView.update(title: _title, subtitle: nil)
- navigationItem.titleView = titleView
- view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
- ThemeService.shared.currentTheme
- .receive(on: RunLoop.main)
- .sink { [weak self] theme in
- guard let self = self else { return }
- self.view.backgroundColor = theme.secondarySystemBackgroundColor
- }
- .store(in: &disposeBag)
-
- navigationItem.rightBarButtonItem = composeBarButtonItem
- composeBarButtonItem.target = self
- composeBarButtonItem.action = #selector(HashtagTimelineViewController.composeBarButtonItemPressed(_:))
-
- tableView.translatesAutoresizingMaskIntoConstraints = false
- view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
- tableView.delegate = self
- viewModel.setupDiffableDataSource(
- tableView: tableView,
- statusTableViewCellDelegate: self
- )
-
- tableView.refreshControl = refreshControl
- refreshControl.addTarget(self, action: #selector(HashtagTimelineViewController.refreshControlValueChanged(_:)), for: .valueChanged)
- viewModel.didLoadLatest
- .receive(on: DispatchQueue.main)
- .sink { [weak self] _ in
- guard let self = self else { return }
- self.refreshControl.endRefreshing()
- }
- .store(in: &disposeBag)
-
- // setup batch fetch
- viewModel.listBatchFetchViewModel.setup(scrollView: tableView)
- viewModel.listBatchFetchViewModel.shouldFetch
- .receive(on: DispatchQueue.main)
- .sink { [weak self] _ in
- guard let self = self else { return }
- self.viewModel.stateMachine.enter(HashtagTimelineViewModel.State.Loading.self)
- }
- .store(in: &disposeBag)
-
- viewModel.hashtagEntity
- .receive(on: DispatchQueue.main)
- .sink { [weak self] tag in
- self?.updatePromptTitle()
- }
- .store(in: &disposeBag)
- }
-
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
-
- tableView.deselectRow(with: transitionCoordinator, animated: animated)
- }
-
- }
- extension HashtagTimelineViewController {
-
- private func updatePromptTitle() {
- var subtitle: String?
- defer {
- titleView.update(title: "#" + viewModel.hashtag, subtitle: subtitle)
- }
- guard let histories = viewModel.hashtagEntity.value?.history else {
- return
- }
- if histories.isEmpty {
- // No tag history, remove the prompt title
- return
- } else {
- let sortedHistory = histories.sorted { (h1, h2) -> Bool in
- return h1.day > h2.day
- }
- let peopleTalkingNumber = sortedHistory
- .prefix(2)
- .compactMap({ Int($0.accounts) })
- .reduce(0, +)
- subtitle = L10n.Plural.peopleTalking(peopleTalkingNumber)
- }
- }
- }
- extension HashtagTimelineViewController {
-
- @objc private func refreshControlValueChanged(_ sender: RefreshControl) {
- guard viewModel.stateMachine.enter(HashtagTimelineViewModel.State.Reloading.self) else {
- sender.endRefreshing()
- return
- }
- }
-
- @objc private func composeBarButtonItemPressed(_ sender: UIBarButtonItem) {
- os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
- let composeViewModel = ComposeViewModel(
- context: context,
- authContext: viewModel.authContext,
- kind: .hashtag(hashtag: viewModel.hashtag)
- )
- _ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
- }
- }
- // MARK: - AuthContextProvider
- extension HashtagTimelineViewController: AuthContextProvider {
- var authContext: AuthContext { viewModel.authContext }
- }
- // MARK: - UITableViewDelegate
- extension HashtagTimelineViewController: UITableViewDelegate, AutoGenerateTableViewDelegate {
- // sourcery:inline:HashtagTimelineViewController.AutoGenerateTableViewDelegate
- // Generated using Sourcery
- // DO NOT EDIT
- func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
- aspectTableView(tableView, didSelectRowAt: indexPath)
- }
- func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
- return aspectTableView(tableView, contextMenuConfigurationForRowAt: indexPath, point: point)
- }
- func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
- return aspectTableView(tableView, previewForHighlightingContextMenuWithConfiguration: configuration)
- }
- func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
- return aspectTableView(tableView, previewForDismissingContextMenuWithConfiguration: configuration)
- }
- func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
- aspectTableView(tableView, willPerformPreviewActionForMenuWith: configuration, animator: animator)
- }
- // sourcery:end
-
- }
- // MARK: - StatusTableViewCellDelegate
- extension HashtagTimelineViewController: StatusTableViewCellDelegate { }
- extension HashtagTimelineViewController {
- override var keyCommands: [UIKeyCommand]? {
- return navigationKeyCommands + statusNavigationKeyCommands
- }
- }
- // MARK: - StatusTableViewControllerNavigateable
- extension HashtagTimelineViewController: StatusTableViewControllerNavigateable {
- @objc func navigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
- navigateKeyCommandHandler(sender)
- }
-
- @objc func statusKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
- statusKeyCommandHandler(sender)
- }
- }
|