From 7ec87d785305931642cb7c157abe5090d4663863 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 1 Apr 2024 10:58:42 -0400 Subject: [PATCH] Add no content message to list timelines Closes #215 Also fix interactive dismissal of edit screen not reloading list --- .../Lists/ListTimelineViewController.swift | 76 +++++++++++++++++-- .../Timeline/TimelineViewController.swift | 12 +++ 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/Tusker/Screens/Lists/ListTimelineViewController.swift b/Tusker/Screens/Lists/ListTimelineViewController.swift index 23ffd7d7..a0db57b7 100644 --- a/Tusker/Screens/Lists/ListTimelineViewController.swift +++ b/Tusker/Screens/Lists/ListTimelineViewController.swift @@ -16,6 +16,8 @@ class ListTimelineViewController: TimelineViewController { var presentEditOnAppear = false + private var noContentView: UIStackView! + private var listRenamedCancellable: AnyCancellable? init(for list: List, mastodonController: MastodonController) { @@ -53,6 +55,39 @@ class ListTimelineViewController: TimelineViewController { } } + private func createNoContentView() { + let title = UILabel() + title.textColor = .secondaryLabel + title.font = .preferredFont(forTextStyle: .title1).withTraits(.traitBold)! + title.adjustsFontForContentSizeCategory = true + title.text = "No Posts" + + let subtitle = UILabel() + subtitle.textColor = .secondaryLabel + subtitle.font = .preferredFont(forTextStyle: .body) + subtitle.adjustsFontForContentSizeCategory = true + subtitle.numberOfLines = 0 + subtitle.textAlignment = .center + subtitle.text = "This list doesn't currently have any posts. Edit it to add accounts if necessary." + + noContentView = UIStackView(arrangedSubviews: [ + title, + subtitle, + ]) + noContentView.axis = .vertical + noContentView.spacing = 8 + noContentView.alignment = .center + noContentView.isAccessibilityElement = true + noContentView.accessibilityLabel = subtitle.text + noContentView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(noContentView) + NSLayoutConstraint.activate([ + noContentView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + noContentView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1), + view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: noContentView.trailingAnchor, multiplier: 1), + ]) + } + private func listChanged() { title = list.title } @@ -61,10 +96,23 @@ class ListTimelineViewController: TimelineViewController { let editListAccountsController = EditListAccountsViewController(list: list, mastodonController: mastodonController) editListAccountsController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(editListDoneButtonPressed)) let navController = UINavigationController(rootViewController: editListAccountsController) + navController.sheetPresentationController?.delegate = self present(navController, animated: animated) } - // MARK: - Interaction + private func reloadIfNecessary(editViewController: EditListAccountsViewController) { + guard editViewController.changedAccounts else { + return + } + noContentView?.removeFromSuperview() + noContentView = nil + Task { + applyInitialSnapshot() + await controller.loadInitial() + } + } + + // MARK: Interaction @objc func editListButtonPressed() { presentEdit(animated: true) @@ -75,12 +123,28 @@ class ListTimelineViewController: TimelineViewController { dismiss(animated: true) - if presented?.changedAccounts == true { - Task { - applyInitialSnapshot() - await controller.loadInitial() - } + if let presented { + self.reloadIfNecessary(editViewController: presented) } } + + // MARK: TimelineLikeControllerDelegate + + override func handleReplaceAllItems(_ timelineItems: [String]) async { + if timelineItems.isEmpty { + createNoContentView() + } + await super.handleReplaceAllItems(timelineItems) + } } + +extension ListTimelineViewController: UISheetPresentationControllerDelegate { + func presentationControllerWillDismiss(_ presentationController: UIPresentationController) { + guard let nav = presentationController.presentedViewController as? UINavigationController, + let edit = nav.viewControllers.first as? EditListAccountsViewController else { + return + } + reloadIfNecessary(editViewController: edit) + } +} diff --git a/Tusker/Screens/Timeline/TimelineViewController.swift b/Tusker/Screens/Timeline/TimelineViewController.swift index 24384977..18448c85 100644 --- a/Tusker/Screens/Timeline/TimelineViewController.swift +++ b/Tusker/Screens/Timeline/TimelineViewController.swift @@ -1015,7 +1015,19 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro } } self.showToast(configuration: config, animated: true) + } + + // this is copied from the TimelineLikeCollectionViewController implementation because it needs to be overridable by ListTimelineViewController + func handleReplaceAllItems(_ timelineItems: [String]) async { + var snapshot = dataSource.snapshot() + if snapshot.sectionIdentifiers.contains(.entries) { + snapshot.deleteSections([.entries]) + } + snapshot.appendSections([.entries]) + snapshot.appendItems(timelineItems.map { .fromTimelineItem($0) }, toSection: .entries) + await apply(snapshot, animatingDifferences: false) } + } extension TimelineViewController {