forked from shadowfacts/Tusker
parent
a75862b5cc
commit
a47b9c0c75
|
@ -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(_):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,8 +61,14 @@ 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
|
||||||
|
@ -168,24 +184,38 @@ class SearchViewController: UIViewController {
|
||||||
|
|
||||||
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 mastodonController.instanceFeatures.trendingStatusesAndLinks {
|
||||||
|
let linksReq = Client.getTrendingLinks(limit: 10)
|
||||||
|
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 {
|
if let links = await links {
|
||||||
snapshot.appendSections([.trendingLinks])
|
snapshot.appendSections([.trendingLinks])
|
||||||
snapshot.appendItems(links.map { .link($0) }, toSection: .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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue