Switch hashtag/instance/list timelines to use new collection view impl

This commit is contained in:
Shadowfacts 2022-11-01 21:06:06 -04:00
parent 658c08010d
commit b1d83f2746
7 changed files with 32 additions and 24 deletions

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
class ListTimelineViewController: TimelineTableViewController { class ListTimelineViewController: TimelineViewController {
let list: List let list: List
@ -57,8 +57,11 @@ class ListTimelineViewController: TimelineTableViewController {
@objc func editListDoneButtonPressed() { @objc func editListDoneButtonPressed() {
dismiss(animated: true) dismiss(animated: true)
// todo: show loading indicator // TODO: only reload if there were changes
reloadInitial() Task {
applyInitialSnapshot()
await controller.loadInitial()
}
} }
} }

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
class HashtagTimelineViewController: TimelineTableViewController { class HashtagTimelineViewController: TimelineViewController {
let hashtag: Hashtag let hashtag: Hashtag

View File

@ -7,13 +7,14 @@
// //
import UIKit import UIKit
import Pachyderm
protocol InstanceTimelineViewControllerDelegate: AnyObject { protocol InstanceTimelineViewControllerDelegate: AnyObject {
func didSaveInstance(url: URL) func didSaveInstance(url: URL)
func didUnsaveInstance(url: URL) func didUnsaveInstance(url: URL)
} }
class InstanceTimelineViewController: TimelineTableViewController { class InstanceTimelineViewController: TimelineViewController {
weak var delegate: InstanceTimelineViewControllerDelegate? weak var delegate: InstanceTimelineViewControllerDelegate?
@ -68,19 +69,15 @@ class InstanceTimelineViewController: TimelineTableViewController {
toggleSaveButton.title = toggleSaveButtonTitle toggleSaveButton.title = toggleSaveButtonTitle
} }
// MARK: - Table view data source override func configureStatusCell(_ cell: TimelineStatusCollectionViewCell, id: String, state: StatusState) {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath) as! TimelineStatusTableViewCell
cell.delegate = browsingEnabled ? self : nil cell.delegate = browsingEnabled ? self : nil
return cell cell.overrideMastodonController = mastodonController
cell.updateUI(statusID: id, state: state)
} }
// MARK: - Table view delegate override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard browsingEnabled else { return } guard browsingEnabled else { return }
super.tableView(tableView, didSelectRowAt: indexPath) super.collectionView(collectionView, didSelectItemAt: indexPath)
} }
// MARK: - Interaction // MARK: - Interaction

View File

@ -34,6 +34,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
self.controller = TimelineLikeController(delegate: self) self.controller = TimelineLikeController(delegate: self)
self.navigationItem.title = timeline.title
addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: "Refresh Timeline")) addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: "Refresh Timeline"))
} }
@ -84,10 +85,15 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
super.viewDidLoad() super.viewDidLoad()
} }
// separate method because InstanceTimelineViewController needs to be able to customize it
func configureStatusCell(_ cell: TimelineStatusCollectionViewCell, id: String, state: StatusState) {
cell.delegate = self
cell.updateUI(statusID: id, state: state)
}
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> { private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, StatusState)> { [unowned self] cell, indexPath, item in let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, StatusState)> { [unowned self] cell, indexPath, item in
cell.delegate = self self.configureStatusCell(cell, id: item.0, state: item.1)
cell.updateUI(statusID: item.0, state: item.1)
} }
let timelineDescriptionCell = UICollectionView.CellRegistration<PublicTimelineDescriptionCollectionViewCell, Item> { [unowned self] cell, indexPath, item in let timelineDescriptionCell = UICollectionView.CellRegistration<PublicTimelineDescriptionCollectionViewCell, Item> { [unowned self] cell, indexPath, item in
guard case .public(let local) = timeline else { guard case .public(let local) = timeline else {
@ -114,15 +120,16 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
} }
} }
private func applyInitialSnapshot() { // non-private, because ListTimelineViewController needs to be able to reload it from scratch
func applyInitialSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
if case .public(let local) = timeline, if case .public(let local) = timeline,
(local && !Preferences.shared.hasShownLocalTimelineDescription) || (local && !Preferences.shared.hasShownLocalTimelineDescription) ||
(!local && !Preferences.shared.hasShownFederatedTimelineDescription) { (!local && !Preferences.shared.hasShownFederatedTimelineDescription) {
var snapshot = dataSource.snapshot()
snapshot.appendSections([.header]) snapshot.appendSections([.header])
snapshot.appendItems([.publicTimelineDescription], toSection: .header) snapshot.appendItems([.publicTimelineDescription], toSection: .header)
dataSource.apply(snapshot, animatingDifferences: false)
} }
dataSource.apply(snapshot, animatingDifferences: false)
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {

View File

@ -51,7 +51,7 @@ actor TimelineLikeController<Item> {
} }
func loadInitial() async { func loadInitial() async {
guard state == .notLoadedInitial else { guard state == .notLoadedInitial || state == .idle else {
return return
} }
let token = LoadAttemptToken() let token = LoadAttemptToken()
@ -175,14 +175,14 @@ actor TimelineLikeController<Item> {
switch self { switch self {
case .notLoadedInitial: case .notLoadedInitial:
switch to { switch to {
case .loadingInitial(_, hasAddedLoadingIndicator: _): case .loadingInitial(_, _):
return true return true
default: default:
return false return false
} }
case .idle: case .idle:
switch to { switch to {
case .loadingNewer(_), .loadingOlder(_, _): case .loadingInitial(_, _), .loadingNewer(_), .loadingOlder(_, _):
return true return true
default: default:
return false return false

View File

@ -30,6 +30,7 @@ protocol StatusCollectionViewCell: UICollectionViewCell, AttachmentViewDelegate
var moreButton: UIButton { get } var moreButton: UIButton { get }
var delegate: StatusCollectionViewCellDelegate? { get } var delegate: StatusCollectionViewCellDelegate? { get }
var mastodonController: MastodonController! { get }
var showStatusAutomatically: Bool { get } var showStatusAutomatically: Bool { get }
var showReplyIndicator: Bool { get } var showReplyIndicator: Bool { get }
@ -75,8 +76,6 @@ extension StatusCollectionViewCell {
} }
func doUpdateUI(status: StatusMO) { func doUpdateUI(status: StatusMO) {
precondition(delegate != nil, "StatusCollectionViewCell must have delegate")
statusID = status.id statusID = status.id
accountID = status.account.id accountID = status.account.id

View File

@ -227,6 +227,8 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
private var mainContainerBottomToActionsConstraint: NSLayoutConstraint! private var mainContainerBottomToActionsConstraint: NSLayoutConstraint!
private var mainContainerBottomToSelfConstraint: NSLayoutConstraint! private var mainContainerBottomToSelfConstraint: NSLayoutConstraint!
weak var overrideMastodonController: MastodonController?
var mastodonController: MastodonController! { overrideMastodonController ?? delegate?.apiController }
weak var delegate: StatusCollectionViewCellDelegate? weak var delegate: StatusCollectionViewCellDelegate?
var showStatusAutomatically: Bool { var showStatusAutomatically: Bool {
// TODO: needed once conversation controller refactored // TODO: needed once conversation controller refactored