parent
205bdffebd
commit
ecadb83c6d
|
@ -439,12 +439,13 @@ public class Client {
|
|||
return Request<[Hashtag]>(method: .get, path: "/api/v1/trends/tags", queryParameters: parameters)
|
||||
}
|
||||
|
||||
public static func getTrendingStatuses(limit: Int? = nil) -> Request<[Status]> {
|
||||
let parameters: [Parameter]
|
||||
if let limit = limit {
|
||||
parameters = ["limit" => limit]
|
||||
} else {
|
||||
parameters = []
|
||||
public static func getTrendingStatuses(limit: Int? = nil, offset: Int? = nil) -> Request<[Status]> {
|
||||
var parameters: [Parameter] = []
|
||||
if let limit {
|
||||
parameters.append("limit" => limit)
|
||||
}
|
||||
if let offset {
|
||||
parameters.append("offset" => offset)
|
||||
}
|
||||
return Request(method: .get, path: "/api/v1/trends/statuses", queryParameters: parameters)
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ class TrendingHashtagsViewController: UIViewController {
|
|||
} catch {
|
||||
await dataSource.apply(origSnapshot)
|
||||
|
||||
let config = ToastConfiguration(from: error, with: "Error Loading Older Tags", in: self) { [weak self] toast in
|
||||
let config = ToastConfiguration(from: error, with: "Error Loading More Tags", in: self) { [weak self] toast in
|
||||
toast.dismissToast(animated: true)
|
||||
await self?.loadOlder()
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class TrendingLinksViewController: UIViewController, CollectionViewController {
|
|||
case nil:
|
||||
fatalError()
|
||||
|
||||
case .loadingIndicator, .confirmLoadMore:
|
||||
case .loadingIndicator:
|
||||
var config = UICollectionLayoutListConfiguration(appearance: .grouped)
|
||||
config.backgroundColor = .appGroupedBackground
|
||||
config.showsSeparators = false
|
||||
|
@ -155,8 +155,8 @@ class TrendingLinksViewController: UIViewController, CollectionViewController {
|
|||
let origSnapshot = dataSource.snapshot()
|
||||
var snapshot = origSnapshot
|
||||
if Preferences.shared.disableInfiniteScrolling {
|
||||
snapshot.appendSections([.confirmLoadMore])
|
||||
snapshot.appendItems([.confirmLoadMore(false)], toSection: .confirmLoadMore)
|
||||
snapshot.appendSections([.loadingIndicator])
|
||||
snapshot.appendItems([.confirmLoadMore(false)], toSection: .loadingIndicator)
|
||||
await dataSource.apply(snapshot)
|
||||
|
||||
for await _ in confirmLoadMore.values {
|
||||
|
@ -164,11 +164,11 @@ class TrendingLinksViewController: UIViewController, CollectionViewController {
|
|||
}
|
||||
|
||||
snapshot.deleteItems([.confirmLoadMore(false)])
|
||||
snapshot.appendItems([.confirmLoadMore(true)], toSection: .confirmLoadMore)
|
||||
snapshot.appendItems([.confirmLoadMore(true)], toSection: .loadingIndicator)
|
||||
await dataSource.apply(snapshot, animatingDifferences: false)
|
||||
} else {
|
||||
snapshot.appendSections([.loadingIndicator])
|
||||
snapshot.appendItems([.loadingIndicator], toSection: .confirmLoadMore)
|
||||
snapshot.appendItems([.loadingIndicator], toSection: .loadingIndicator)
|
||||
await dataSource.apply(snapshot)
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ class TrendingLinksViewController: UIViewController, CollectionViewController {
|
|||
await dataSource.apply(snapshot)
|
||||
} catch {
|
||||
await dataSource.apply(origSnapshot)
|
||||
let config = ToastConfiguration(from: error, with: "Erorr Loading Older Links", in: self) { [weak self] toast in
|
||||
let config = ToastConfiguration(from: error, with: "Erorr Loading More Links", in: self) { [weak self] toast in
|
||||
toast.dismissToast(animated: true)
|
||||
await self?.loadOlder()
|
||||
}
|
||||
|
@ -203,7 +203,6 @@ extension TrendingLinksViewController {
|
|||
enum Section {
|
||||
case loadingIndicator
|
||||
case links
|
||||
case confirmLoadMore
|
||||
}
|
||||
enum Item: Hashable {
|
||||
case loadingIndicator
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import UIKit
|
||||
import Pachyderm
|
||||
import SafariServices
|
||||
import Combine
|
||||
|
||||
class TrendsViewController: UIViewController, CollectionViewController {
|
||||
|
||||
|
@ -18,6 +19,8 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||
|
||||
private var loadTask: Task<Void, Never>?
|
||||
private var trendingStatusesState = TrendingStatusesState.unloaded
|
||||
private let confirmLoadMoreStatuses = PassthroughSubject<Void, Never>()
|
||||
|
||||
private var isShowingTrends = false
|
||||
private var shouldShowTrends: Bool {
|
||||
|
@ -89,6 +92,15 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
var listConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
|
||||
listConfig.headerMode = .supplementary
|
||||
listConfig.backgroundColor = .appGroupedBackground
|
||||
listConfig.itemSeparatorHandler = { [unowned self] indexPath, sectionConfig in
|
||||
var config = sectionConfig
|
||||
if let item = dataSource.itemIdentifier(for: indexPath),
|
||||
item.hideListSeparators {
|
||||
config.topSeparatorVisibility = .hidden
|
||||
config.bottomSeparatorVisibility = .hidden
|
||||
}
|
||||
return config
|
||||
}
|
||||
return .list(using: listConfig, layoutEnvironment: environment)
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +124,19 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
config.text = section.title
|
||||
headerView.contentConfiguration = config
|
||||
}
|
||||
let moreCell = UICollectionView.SupplementaryRegistration<MoreTrendsFooterCollectionViewCell>(elementKind: UICollectionView.elementKindSectionFooter) { [unowned self] supplementaryView, elementKind, indexPath in
|
||||
supplementaryView.delegate = self
|
||||
switch self.dataSource.sectionIdentifier(for: indexPath.section) {
|
||||
case nil, .loadingIndicator, .trendingStatuses:
|
||||
fatalError()
|
||||
case .trendingHashtags:
|
||||
supplementaryView.updateUI(.hashtags)
|
||||
case .trendingLinks:
|
||||
supplementaryView.updateUI(.links)
|
||||
case .profileSuggestions:
|
||||
supplementaryView.updateUI(.profileSuggestions)
|
||||
}
|
||||
}
|
||||
|
||||
let loadingCell = UICollectionView.CellRegistration<LoadingCollectionViewCell, Void> { cell, indexPath, itemIdentifier in
|
||||
cell.indicator.startAnimating()
|
||||
|
@ -131,18 +156,9 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
cell.delegate = self
|
||||
cell.updateUI(accountID: item.0, source: item.1)
|
||||
}
|
||||
let moreCell = UICollectionView.SupplementaryRegistration<MoreTrendsFooterCollectionViewCell>(elementKind: UICollectionView.elementKindSectionFooter) { [unowned self] supplementaryView, elementKind, indexPath in
|
||||
supplementaryView.delegate = self
|
||||
switch self.dataSource.sectionIdentifier(for: indexPath.section) {
|
||||
case nil, .loadingIndicator, .trendingStatuses:
|
||||
fatalError()
|
||||
case .trendingHashtags:
|
||||
supplementaryView.updateUI(.hashtags)
|
||||
case .trendingLinks:
|
||||
supplementaryView.updateUI(.links)
|
||||
case .profileSuggestions:
|
||||
supplementaryView.updateUI(.profileSuggestions)
|
||||
}
|
||||
let confirmLoadMoreCell = UICollectionView.CellRegistration<ConfirmLoadMoreCollectionViewCell, Bool> { [unowned self] cell, indexPath, isLoading in
|
||||
cell.confirmLoadMore = self.confirmLoadMoreStatuses
|
||||
cell.isLoading = isLoading
|
||||
}
|
||||
|
||||
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
|
||||
|
@ -161,6 +177,9 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
|
||||
case let .account(id, source):
|
||||
return collectionView.dequeueConfiguredReusableCell(using: accountCell, for: indexPath, item: (id, source))
|
||||
|
||||
case let .confirmLoadMoreStatuses(loading):
|
||||
return collectionView.dequeueConfiguredReusableCell(using: confirmLoadMoreCell, for: indexPath, item: loading)
|
||||
}
|
||||
}
|
||||
dataSource.supplementaryViewProvider = { (collectionView, elementKind, indexPath) in
|
||||
|
@ -249,9 +268,60 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
|
||||
if !Task.isCancelled {
|
||||
await apply(snapshot: snapshot)
|
||||
if snapshot.sectionIdentifiers.contains(.trendingStatuses) {
|
||||
self.trendingStatusesState = .loaded
|
||||
} else {
|
||||
self.trendingStatusesState = .unloaded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func loadOlderStatuses() async {
|
||||
guard case .loaded = trendingStatusesState else {
|
||||
return
|
||||
}
|
||||
trendingStatusesState = .loadingOlder
|
||||
|
||||
let origSnapshot = dataSource.snapshot()
|
||||
var snapshot = origSnapshot
|
||||
if Preferences.shared.disableInfiniteScrolling {
|
||||
snapshot.appendItems([.confirmLoadMoreStatuses(false)], toSection: .trendingStatuses)
|
||||
await apply(snapshot: snapshot)
|
||||
|
||||
for await _ in confirmLoadMoreStatuses.values {
|
||||
break
|
||||
}
|
||||
|
||||
snapshot.deleteItems([.confirmLoadMoreStatuses(false)])
|
||||
snapshot.appendItems([.confirmLoadMoreStatuses(true)], toSection: .trendingStatuses)
|
||||
await apply(snapshot: snapshot, animatingDifferences: false)
|
||||
} else {
|
||||
snapshot.appendItems([.loadingIndicator], toSection: .trendingStatuses)
|
||||
await apply(snapshot: snapshot)
|
||||
}
|
||||
|
||||
do {
|
||||
let request = Client.getTrendingStatuses(offset: origSnapshot.itemIdentifiers(inSection: .trendingStatuses).count)
|
||||
let (statuses, _) = try await mastodonController.run(request)
|
||||
|
||||
await mastodonController.persistentContainer.addAll(statuses: statuses)
|
||||
|
||||
var snapshot = origSnapshot
|
||||
snapshot.appendItems(statuses.map { .status($0.id, .unknown) }, toSection: .trendingStatuses)
|
||||
await apply(snapshot: snapshot)
|
||||
} catch {
|
||||
await apply(snapshot: origSnapshot)
|
||||
let config = ToastConfiguration(from: error, with: "Error Loading More Trending Statuses", in: self) { [weak self] toast in
|
||||
toast.dismissToast(animated: true)
|
||||
await self?.loadOlderStatuses()
|
||||
}
|
||||
showToast(configuration: config, animated: true)
|
||||
}
|
||||
|
||||
trendingStatusesState = .loaded
|
||||
}
|
||||
|
||||
@objc private func preferencesChanged() {
|
||||
if isShowingTrends != shouldShowTrends {
|
||||
loadTask?.cancel()
|
||||
|
@ -261,9 +331,9 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
}
|
||||
}
|
||||
|
||||
private func apply(snapshot: NSDiffableDataSourceSnapshot<Section, Item>) async {
|
||||
private func apply(snapshot: NSDiffableDataSourceSnapshot<Section, Item>, animatingDifferences: Bool = true) async {
|
||||
await Task { @MainActor in
|
||||
self.dataSource.apply(snapshot)
|
||||
self.dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
|
||||
}.value
|
||||
}
|
||||
|
||||
|
@ -286,6 +356,14 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
|||
}
|
||||
}
|
||||
|
||||
extension TrendsViewController {
|
||||
enum TrendingStatusesState {
|
||||
case unloaded
|
||||
case loaded
|
||||
case loadingOlder
|
||||
}
|
||||
}
|
||||
|
||||
extension TrendsViewController {
|
||||
enum Section {
|
||||
case loadingIndicator
|
||||
|
@ -315,6 +393,7 @@ extension TrendsViewController {
|
|||
case tag(Hashtag)
|
||||
case link(Card)
|
||||
case account(String, Suggestion.Source)
|
||||
case confirmLoadMoreStatuses(Bool)
|
||||
|
||||
static func == (lhs: Item, rhs: Item) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
|
@ -328,6 +407,8 @@ extension TrendsViewController {
|
|||
return a.url == b.url
|
||||
case let (.account(a, _), .account(b, _)):
|
||||
return a == b
|
||||
case (.confirmLoadMoreStatuses(let a), .confirmLoadMoreStatuses(let b)):
|
||||
return a == b
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
@ -349,21 +430,42 @@ extension TrendsViewController {
|
|||
case let .account(id, _):
|
||||
hasher.combine("account")
|
||||
hasher.combine(id)
|
||||
case let .confirmLoadMoreStatuses(loading):
|
||||
hasher.combine("confirmLoadMoreStatuses")
|
||||
hasher.combine(loading)
|
||||
}
|
||||
}
|
||||
|
||||
var shouldSelect: Bool {
|
||||
switch self {
|
||||
case .loadingIndicator:
|
||||
case .loadingIndicator, .confirmLoadMoreStatuses(_):
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
var hideListSeparators: Bool {
|
||||
switch self {
|
||||
case .loadingIndicator, .confirmLoadMoreStatuses(_):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TrendsViewController: UICollectionViewDelegate {
|
||||
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
||||
if case .trendingStatuses = dataSource.sectionIdentifier(for: indexPath.section),
|
||||
indexPath.row == collectionView.numberOfItems(inSection: indexPath.section) - 1 {
|
||||
Task {
|
||||
await self.loadOlderStatuses()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
||||
return dataSource.itemIdentifier(for: indexPath)?.shouldSelect ?? false
|
||||
}
|
||||
|
@ -373,7 +475,7 @@ extension TrendsViewController: UICollectionViewDelegate {
|
|||
return
|
||||
}
|
||||
switch item {
|
||||
case .loadingIndicator:
|
||||
case .loadingIndicator, .confirmLoadMoreStatuses(_):
|
||||
return
|
||||
|
||||
case let .tag(hashtag):
|
||||
|
@ -399,7 +501,7 @@ extension TrendsViewController: UICollectionViewDelegate {
|
|||
}
|
||||
|
||||
switch item {
|
||||
case .loadingIndicator:
|
||||
case .loadingIndicator, .confirmLoadMoreStatuses(_):
|
||||
return nil
|
||||
|
||||
case let .tag(hashtag):
|
||||
|
@ -487,7 +589,7 @@ extension TrendsViewController: UICollectionViewDragDelegate {
|
|||
return []
|
||||
}
|
||||
switch item {
|
||||
case .loadingIndicator:
|
||||
case .loadingIndicator, .confirmLoadMoreStatuses(_):
|
||||
return []
|
||||
|
||||
case let .tag(hashtag):
|
||||
|
|
Loading…
Reference in New Issue