mastodon-ios/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewControll...

260 lines
9.3 KiB
Swift
Raw Normal View History

2021-04-01 04:12:57 +02:00
//
// HashtagTimelineViewController.swift
// Mastodon
//
// Created by BradGao on 2021/3/30.
//
import UIKit
import AVKit
import Combine
import GameplayKit
import CoreData
import MastodonAsset
2022-10-08 07:43:06 +02:00
import MastodonCore
import MastodonUI
import MastodonLocalization
import MastodonSDK
2021-04-01 04:12:57 +02:00
final class HashtagTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
2021-04-01 04:12:57 +02:00
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
let mediaPreviewTransitionController = MediaPreviewTransitionController()
2021-04-01 04:12:57 +02:00
var disposeBag = Set<AnyCancellable>()
var viewModel: HashtagTimelineViewModel!
private lazy var headerView: HashtagTimelineHeaderView = {
let headerView = HashtagTimelineHeaderView()
headerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
headerView.heightAnchor.constraint(equalToConstant: 118),
])
return headerView
}()
2021-04-01 04:12:57 +02:00
let composeBarButtonItem: UIBarButtonItem = {
let barButtonItem = UIBarButtonItem()
barButtonItem.image = UIImage(systemName: "square.and.pencil")!.withRenderingMode(.alwaysTemplate)
2021-04-01 04:12:57 +02:00
return barButtonItem
}()
let titleView = DoubleTitleLabelNavigationBarTitleView()
2021-04-01 04:12:57 +02:00
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
}()
2022-11-14 23:09:59 +01:00
let refreshControl = RefreshControl()
2021-04-01 04:12:57 +02:00
}
extension HashtagTimelineViewController {
override func viewDidLoad() {
super.viewDidLoad()
let _title = "#\(viewModel.hashtag)"
title = _title
titleView.update(title: _title, subtitle: nil)
2021-04-02 10:38:33 +02:00
navigationItem.titleView = titleView
2023-09-27 15:08:12 +02:00
view.backgroundColor = .secondarySystemBackground
2021-04-01 04:12:57 +02:00
navigationItem.rightBarButtonItem = composeBarButtonItem
composeBarButtonItem.target = self
composeBarButtonItem.action = #selector(HashtagTimelineViewController.composeBarButtonItemPressed(_:))
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
tableView.pinToParent()
2021-04-01 04:12:57 +02:00
tableView.delegate = self
viewModel.setupDiffableDataSource(
tableView: tableView,
statusTableViewCellDelegate: self
2021-04-01 04:12:57 +02:00
)
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
2021-04-01 04:12:57 +02:00
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
2021-04-01 04:12:57 +02:00
guard let self = self else { return }
self.viewModel.stateMachine.enter(HashtagTimelineViewModel.State.Loading.self)
2021-04-01 04:12:57 +02:00
}
.store(in: &disposeBag)
2021-04-02 04:21:51 +02:00
viewModel.hashtagEntity
.receive(on: DispatchQueue.main)
.sink { [weak self] tag in
self?.updatePromptTitle()
}
.store(in: &disposeBag)
viewModel.hashtagDetails
.receive(on: DispatchQueue.main)
.sink { [weak self] tag in
guard let tag = tag else { return }
self?.updateHeaderView(with: tag)
}
.store(in: &disposeBag)
2021-04-01 04:12:57 +02:00
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewModel.viewWillAppear()
tableView.deselectRow(with: transitionCoordinator, animated: animated)
2021-04-01 04:12:57 +02:00
}
}
2021-04-01 04:12:57 +02:00
extension HashtagTimelineViewController {
2021-04-02 04:21:51 +02:00
private func updatePromptTitle() {
2021-04-02 10:38:33 +02:00
var subtitle: String?
defer {
2021-07-23 13:10:27 +02:00
titleView.update(title: "#" + viewModel.hashtag, subtitle: subtitle)
2021-04-02 10:38:33 +02:00
}
2021-04-02 04:21:51 +02:00
guard let histories = viewModel.hashtagEntity.value?.history else {
return
}
if histories.isEmpty {
// No tag history, remove the prompt title
2021-04-02 10:38:33 +02:00
return
2021-04-02 04:21:51 +02:00
} 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)
2021-04-02 04:21:51 +02:00
}
}
}
extension HashtagTimelineViewController {
private func updateHeaderView(with tag: Mastodon.Entity.Tag) {
if tableView.tableHeaderView == nil {
tableView.tableHeaderView = headerView
}
2022-11-25 13:07:28 +01:00
headerView.update(HashtagTimelineHeaderView.Data.from(tag))
headerView.onButtonTapped = { [weak self] in
switch tag.following {
case .some(false):
self?.viewModel.followTag()
case .some(true):
self?.viewModel.unfollowTag()
default:
break
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
headerView.updateWidthConstraint(tableView.bounds.width)
}
2021-04-01 04:12:57 +02:00
}
extension HashtagTimelineViewController {
2022-11-14 23:09:59 +01:00
@objc private func refreshControlValueChanged(_ sender: RefreshControl) {
guard viewModel.stateMachine.enter(HashtagTimelineViewModel.State.Reloading.self) else {
sender.endRefreshing()
return
}
}
2021-04-01 04:12:57 +02:00
@objc private func composeBarButtonItemPressed(_ sender: UIBarButtonItem) {
let hashtag = "#" + viewModel.hashtag
UITextChecker.learnWord(hashtag)
let composeViewModel = ComposeViewModel(
context: context,
authContext: viewModel.authContext,
composeContext: .composeStatus,
destination: .topLevel,
initialContent: hashtag
)
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
2021-04-01 04:12:57 +02:00
}
}
// MARK: - AuthContextProvider
extension HashtagTimelineViewController: AuthContextProvider {
var authContext: AuthContext { viewModel.authContext }
}
2021-04-01 04:12:57 +02:00
// 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)
}
2021-04-30 13:28:06 +02:00
func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
return aspectTableView(tableView, contextMenuConfigurationForRowAt: indexPath, point: point)
}
2021-04-30 13:28:06 +02:00
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)
}
2021-04-30 13:28:06 +02:00
func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
aspectTableView(tableView, willPerformPreviewActionForMenuWith: configuration, animator: animator)
}
// sourcery:end
2021-04-01 04:12:57 +02:00
}
// MARK: - StatusTableViewCellDelegate
extension HashtagTimelineViewController: StatusTableViewCellDelegate { }
2021-04-01 04:12:57 +02:00
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)
}
}