Move trending statuses to Explore on iPad

See #171
This commit is contained in:
Shadowfacts 2023-01-22 13:54:21 -05:00
parent a75862b5cc
commit a47b9c0c75
3 changed files with 81 additions and 38 deletions

View File

@ -41,7 +41,7 @@ class MainSidebarViewController: UIViewController {
} }
var exploreTabItems: [Item] { var exploreTabItems: [Item] {
var items: [Item] = [.explore, .bookmarks, .trendingStatuses, .profileDirectory] var items: [Item] = [.explore, .bookmarks, .profileDirectory]
let snapshot = dataSource.snapshot() let snapshot = dataSource.snapshot()
for case let .list(list) in snapshot.itemIdentifiers(inSection: .lists) { for case let .list(list) in snapshot.itemIdentifiers(inSection: .lists) {
items.append(.list(list)) items.append(.list(list))
@ -195,9 +195,6 @@ class MainSidebarViewController: UIViewController {
discoverSnapshot.append([ discoverSnapshot.append([
.profileDirectory, .profileDirectory,
], to: .discoverHeader) ], to: .discoverHeader)
if mastodonController.instanceFeatures.trendingStatusesAndLinks {
discoverSnapshot.insert([.trendingStatuses], before: .profileDirectory)
}
dataSource.apply(discoverSnapshot, to: .discover) dataSource.apply(discoverSnapshot, to: .discover)
} }
@ -388,7 +385,7 @@ extension MainSidebarViewController {
enum Item: Hashable { enum Item: Hashable {
case tab(MainTabBarViewController.Tab) case tab(MainTabBarViewController.Tab)
case explore, bookmarks case explore, bookmarks
case discoverHeader, trendingStatuses, profileDirectory case discoverHeader, profileDirectory
case listsHeader, list(List), addList case listsHeader, list(List), addList
case savedHashtagsHeader, savedHashtag(Hashtag), addSavedHashtag case savedHashtagsHeader, savedHashtag(Hashtag), addSavedHashtag
case savedInstancesHeader, savedInstance(URL), addSavedInstance case savedInstancesHeader, savedInstance(URL), addSavedInstance
@ -403,8 +400,6 @@ extension MainSidebarViewController {
return "Bookmarks" return "Bookmarks"
case .discoverHeader: case .discoverHeader:
return "Discover" return "Discover"
case .trendingStatuses:
return "Trending Posts"
case .profileDirectory: case .profileDirectory:
return "Profile Directory" return "Profile Directory"
case .listsHeader: case .listsHeader:
@ -436,8 +431,6 @@ extension MainSidebarViewController {
return "magnifyingglass" return "magnifyingglass"
case .bookmarks: case .bookmarks:
return "bookmark" return "bookmark"
case .trendingStatuses:
return "square.text.square"
case .profileDirectory: case .profileDirectory:
return "person.2.fill" return "person.2.fill"
case .list(_): case .list(_):

View File

@ -232,7 +232,7 @@ extension MainSplitViewController: UISplitViewControllerDelegate {
tabBarViewController.select(tab: .explore) tabBarViewController.select(tab: .explore)
case .bookmarks, .trendingStatuses, .profileDirectory, .list(_), .savedHashtag(_), .savedInstance(_): case .bookmarks, .profileDirectory, .list(_), .savedHashtag(_), .savedInstance(_):
tabBarViewController.select(tab: .explore) tabBarViewController.select(tab: .explore)
// Make sure the Explore VC doesn't show it's search bar when it appears, in case the user was previously // Make sure the Explore VC doesn't show it's search bar when it appears, in case the user was previously
// in compact mode and performing a search. // in compact mode and performing a search.
@ -309,7 +309,7 @@ extension MainSplitViewController: UISplitViewControllerDelegate {
case let instanceVC as InstanceTimelineViewController: case let instanceVC as InstanceTimelineViewController:
exploreItem = .savedInstance(instanceVC.instanceURL) exploreItem = .savedInstance(instanceVC.instanceURL)
case is TrendingStatusesViewController: case is TrendingStatusesViewController:
exploreItem = .trendingStatuses exploreItem = .explore
case is TrendingHashtagsViewController: case is TrendingHashtagsViewController:
exploreItem = .explore exploreItem = .explore
case is TrendingLinksViewController: case is TrendingLinksViewController:
@ -376,8 +376,6 @@ fileprivate extension MainSidebarViewController.Item {
return SearchViewController(mastodonController: mastodonController) return SearchViewController(mastodonController: mastodonController)
case .bookmarks: case .bookmarks:
return BookmarksTableViewController(mastodonController: mastodonController) return BookmarksTableViewController(mastodonController: mastodonController)
case .trendingStatuses:
return TrendingStatusesViewController(mastodonController: mastodonController)
case .profileDirectory: case .profileDirectory:
return ProfileDirectoryViewController(mastodonController: mastodonController) return ProfileDirectoryViewController(mastodonController: mastodonController)
case let .list(list): case let .list(list):

View File

@ -11,11 +11,11 @@ import Pachyderm
import SafariServices import SafariServices
import WebURLFoundationExtras import WebURLFoundationExtras
class SearchViewController: UIViewController { class SearchViewController: UIViewController, CollectionViewController {
weak var mastodonController: MastodonController! weak var mastodonController: MastodonController!
private var collectionView: UICollectionView! var collectionView: UICollectionView!
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>! private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
var resultsController: SearchResultsViewController! var resultsController: SearchResultsViewController!
@ -23,6 +23,8 @@ class SearchViewController: UIViewController {
var searchControllerStatusOnAppearance: Bool? = nil var searchControllerStatusOnAppearance: Bool? = nil
private var loadTask: Task<Void, Never>?
init(mastodonController: MastodonController) { init(mastodonController: MastodonController) {
self.mastodonController = mastodonController self.mastodonController = mastodonController
@ -59,7 +61,13 @@ class SearchViewController: UIViewController {
section.boundarySupplementaryItems = [ section.boundarySupplementaryItems = [
NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(12)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .topLeading) NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(12)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .topLeading)
] ]
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 16, trailing: 0)
return section return section
case .trendingStatuses:
var listConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
listConfig.headerMode = .supplementary
return NSCollectionLayoutSection.list(using: listConfig, layoutEnvironment: environment)
default: default:
fatalError("unimplemented") fatalError("unimplemented")
@ -100,7 +108,10 @@ class SearchViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
Task(priority: .userInitiated) { clearSelectionOnAppear(animated: animated)
loadTask?.cancel()
loadTask = Task(priority: .userInitiated) {
if (try? await mastodonController.getOwnInstance()) != nil { if (try? await mastodonController.getOwnInstance()) != nil {
await applySnapshot() await applySnapshot()
} }
@ -133,6 +144,11 @@ class SearchViewController: UIViewController {
let trendingLinkCell = UICollectionView.CellRegistration<TrendingLinkCardCollectionViewCell, Card>(cellNib: UINib(nibName: "TrendingLinkCardCollectionViewCell", bundle: .main)) { (cell, indexPath, card) in let trendingLinkCell = UICollectionView.CellRegistration<TrendingLinkCardCollectionViewCell, Card>(cellNib: UINib(nibName: "TrendingLinkCardCollectionViewCell", bundle: .main)) { (cell, indexPath, card) in
cell.updateUI(card: card) cell.updateUI(card: card)
} }
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, CollapseState)> { [unowned self] cell, indexPath, item in
cell.delegate = self
// TODO: filter trends
cell.updateUI(statusID: item.0, state: item.1, filterResult: .allow, precomputedContent: nil)
}
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
switch item { switch item {
@ -142,8 +158,8 @@ class SearchViewController: UIViewController {
case let .link(card): case let .link(card):
return collectionView.dequeueConfiguredReusableCell(using: trendingLinkCell, for: indexPath, item: card) return collectionView.dequeueConfiguredReusableCell(using: trendingLinkCell, for: indexPath, item: card)
default: case let .status(id, state):
fatalError("todo") return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, state))
} }
} }
dataSource.supplementaryViewProvider = { (collectionView, elementKind, indexPath) in dataSource.supplementaryViewProvider = { (collectionView, elementKind, indexPath) in
@ -165,27 +181,41 @@ class SearchViewController: UIViewController {
} }
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
let hashtagsReq = Client.getTrendingHashtags(limit: 5) let hashtagsReq = Client.getTrendingHashtags(limit: 5)
async let hashtags = try? mastodonController.run(hashtagsReq).0 async let hashtags = try? mastodonController.run(hashtagsReq).0
let linksReq = Client.getTrendingLinks(limit: 10)
async let links = try? mastodonController.run(linksReq).0
if let hashtags = await hashtags { if let hashtags = await hashtags {
snapshot.appendSections([.trendingHashtags]) snapshot.appendSections([.trendingHashtags])
snapshot.appendItems(hashtags.map { .tag($0) }, toSection: .trendingHashtags) snapshot.appendItems(hashtags.map { .tag($0) }, toSection: .trendingHashtags)
} }
if let links = await links { if mastodonController.instanceFeatures.trendingStatusesAndLinks {
snapshot.appendSections([.trendingLinks]) let linksReq = Client.getTrendingLinks(limit: 10)
snapshot.appendItems(links.map { .link($0) }, toSection: .trendingLinks) async let links = try? mastodonController.run(linksReq).0
let statusesReq = Client.getTrendingStatuses(limit: 10)
async let statuses = try? mastodonController.run(statusesReq).0
if let links = await links {
snapshot.appendSections([.trendingLinks])
snapshot.appendItems(links.map { .link($0) }, toSection: .trendingLinks)
}
if let statuses = await statuses {
await mastodonController.persistentContainer.addAll(statuses: statuses)
snapshot.appendSections([.trendingStatuses])
snapshot.appendItems(statuses.map { .status($0.id, .unknown) }, toSection: .trendingStatuses)
}
}
if !Task.isCancelled {
await dataSource.apply(snapshot)
} }
await dataSource.apply(snapshot)
} }
@objc private func preferencesChanged() { @objc private func preferencesChanged() {
Task { loadTask?.cancel()
loadTask = Task {
await applySnapshot() await applySnapshot()
} }
} }
@ -196,8 +226,8 @@ extension SearchViewController {
enum Section { enum Section {
case trendingHashtags case trendingHashtags
case trendingLinks case trendingLinks
case trendingStatuses
case profileSuggestions case profileSuggestions
case trendingStatuses
var title: String { var title: String {
switch self { switch self {
@ -206,20 +236,20 @@ extension SearchViewController {
case .trendingLinks: case .trendingLinks:
return "Trending Links" return "Trending Links"
case .trendingStatuses: case .trendingStatuses:
return "Trending Statuses" return "Trending Posts"
case .profileSuggestions: case .profileSuggestions:
return "Suggested Accounts" return "Suggested Accounts"
} }
} }
} }
enum Item: Equatable, Hashable { enum Item: Equatable, Hashable {
case status(String) case status(String, CollapseState)
case tag(Hashtag) case tag(Hashtag)
case link(Card) case link(Card)
static func == (lhs: SearchViewController.Item, rhs: SearchViewController.Item) -> Bool { static func == (lhs: SearchViewController.Item, rhs: SearchViewController.Item) -> Bool {
switch (lhs, rhs) { switch (lhs, rhs) {
case let (.status(a), .status(b)): case let (.status(a, _), .status(b, _)):
return a == b return a == b
case let (.tag(a), .tag(b)): case let (.tag(a), .tag(b)):
return a == b return a == b
@ -232,7 +262,7 @@ extension SearchViewController {
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
switch self { switch self {
case let .status(id): case let .status(id, _):
hasher.combine("status") hasher.combine("status")
hasher.combine(id) hasher.combine(id)
case let .tag(tag): case let .tag(tag):
@ -260,8 +290,8 @@ extension SearchViewController: UICollectionViewDelegate {
selected(url: url) selected(url: url)
} }
default: case let .status(id, state):
fatalError("todo") selected(status: id, state: state.copy())
} }
} }
@ -348,8 +378,16 @@ extension SearchViewController: UICollectionViewDragDelegate {
} }
return [UIDragItem(itemProvider: NSItemProvider(object: url as NSURL))] return [UIDragItem(itemProvider: NSItemProvider(object: url as NSURL))]
default: case let .status(id, _):
fatalError("todo") guard let status = mastodonController.persistentContainer.status(for: id),
let url = status.url else {
return []
}
let provider = NSItemProvider(object: url as NSURL)
let activity = UserActivityManager.showConversationActivity(mainStatusID: id, accountID: mastodonController.accountInfo!.id)
activity.displaysAuxiliaryScene = true
provider.registerObject(activity, visibility: .all)
return [UIDragItem(itemProvider: provider)]
} }
} }
} }
@ -363,3 +401,17 @@ extension SearchViewController: ToastableViewController {
extension SearchViewController: MenuActionProvider { extension SearchViewController: MenuActionProvider {
} }
extension SearchViewController: StatusCollectionViewCellDelegate {
func statusCellNeedsReconfigure(_ cell: StatusCollectionViewCell, animated: Bool, completion: (() -> Void)?) {
if let indexPath = collectionView.indexPath(for: cell) {
var snapshot = dataSource.snapshot()
snapshot.reconfigureItems([dataSource.itemIdentifier(for: indexPath)!])
dataSource.apply(snapshot, animatingDifferences: animated, completion: completion)
}
}
func statusCellShowFiltered(_ cell: StatusCollectionViewCell) {
// TODO: filtering
}
}