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