Re-add public timeline descriptions
This commit is contained in:
parent
253fb8d27d
commit
a38c89a17f
|
@ -215,6 +215,7 @@
|
||||||
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */; };
|
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */; };
|
||||||
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
|
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
|
||||||
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.framework */; };
|
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.framework */; };
|
||||||
|
D6ADB6E828E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */; };
|
||||||
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
||||||
D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */; };
|
D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */; };
|
||||||
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */; };
|
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */; };
|
||||||
|
@ -560,6 +561,7 @@
|
||||||
D6A6C11425B62E9700298D0F /* CacheExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiry.swift; sourceTree = "<group>"; };
|
D6A6C11425B62E9700298D0F /* CacheExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiry.swift; sourceTree = "<group>"; };
|
||||||
D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; };
|
D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; };
|
||||||
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSceneDelegate.swift; sourceTree = "<group>"; };
|
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineDescriptionCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
||||||
D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
|
D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
|
||||||
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
|
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
|
||||||
|
@ -897,6 +899,7 @@
|
||||||
D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */,
|
D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */,
|
||||||
D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */,
|
D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */,
|
||||||
D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */,
|
D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */,
|
||||||
|
D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */,
|
||||||
);
|
);
|
||||||
path = Timeline;
|
path = Timeline;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1889,6 +1892,7 @@
|
||||||
D61A45E628DC0F2F002BE511 /* ConfirmLoadMoreCollectionViewCell.swift in Sources */,
|
D61A45E628DC0F2F002BE511 /* ConfirmLoadMoreCollectionViewCell.swift in Sources */,
|
||||||
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
|
||||||
D67895C0246870DE00D4CD9E /* LocalAccountAvatarView.swift in Sources */,
|
D67895C0246870DE00D4CD9E /* LocalAccountAvatarView.swift in Sources */,
|
||||||
|
D6ADB6E828E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift in Sources */,
|
||||||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */,
|
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */,
|
||||||
D622757824EE133700B82A16 /* ComposeAssetPicker.swift in Sources */,
|
D622757824EE133700B82A16 /* ComposeAssetPicker.swift in Sources */,
|
||||||
D681A29A249AD62D0085E54E /* LargeImageContentView.swift in Sources */,
|
D681A29A249AD62D0085E54E /* LargeImageContentView.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// PublicTimelineDescriptionCollectionViewCell.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 10/1/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class PublicTimelineDescriptionCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
weak var mastodonController: MastodonController!
|
||||||
|
|
||||||
|
var local = false {
|
||||||
|
didSet {
|
||||||
|
updateLabel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var didDismiss: (() -> Void)?
|
||||||
|
|
||||||
|
private let label = UILabel()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
contentView.backgroundColor = .tintColor
|
||||||
|
label.font = .preferredFont(forTextStyle: .body)
|
||||||
|
label.numberOfLines = 0
|
||||||
|
label.textColor = .white
|
||||||
|
label.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
contentView.addSubview(label)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
label.leadingAnchor.constraint(equalToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1),
|
||||||
|
contentView.trailingAnchor.constraint(equalToSystemSpacingAfter: label.trailingAnchor, multiplier: 1),
|
||||||
|
label.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1),
|
||||||
|
contentView.bottomAnchor.constraint(equalToSystemSpacingBelow: label.bottomAnchor, multiplier: 1),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateLabel() {
|
||||||
|
let str = NSMutableAttributedString()
|
||||||
|
let instanceStr = NSAttributedString(string: mastodonController.instanceURL.host!, attributes: [
|
||||||
|
.font: UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).withSymbolicTraits(.traitBold)!, size: 0)
|
||||||
|
])
|
||||||
|
if local {
|
||||||
|
str.append(NSAttributedString(string: "The local timeline shows public posts from only "))
|
||||||
|
str.append(instanceStr)
|
||||||
|
str.append(NSAttributedString(string: "."))
|
||||||
|
} else {
|
||||||
|
str.append(NSAttributedString(string: "The federated timeline shows public posts from all users that "))
|
||||||
|
str.append(instanceStr)
|
||||||
|
str.append(NSAttributedString(string: " knows about."))
|
||||||
|
}
|
||||||
|
label.attributedText = str
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
let confirmLoadMore = PassthroughSubject<Void, Never>()
|
let confirmLoadMore = PassthroughSubject<Void, Never>()
|
||||||
private var newer: RequestRange?
|
private var newer: RequestRange?
|
||||||
private var older: RequestRange?
|
private var older: RequestRange?
|
||||||
|
// stored separately because i don't want to query the snapshot every time the user scrolls
|
||||||
|
private var isShowingTimelineDescription = false
|
||||||
|
|
||||||
var collectionView: UICollectionView {
|
var collectionView: UICollectionView {
|
||||||
view as! UICollectionView
|
view as! UICollectionView
|
||||||
|
@ -63,6 +65,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
// collectionView.dragDelegate = self
|
// collectionView.dragDelegate = self
|
||||||
|
|
||||||
registerTimelineLikeCells()
|
registerTimelineLikeCells()
|
||||||
|
collectionView.register(PublicTimelineDescriptionCollectionViewCell.self, forCellWithReuseIdentifier: "publicTimelineDescription")
|
||||||
dataSource = createDataSource()
|
dataSource = createDataSource()
|
||||||
applyInitialSnapshot()
|
applyInitialSnapshot()
|
||||||
|
|
||||||
|
@ -94,6 +97,16 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
config.text = try! doc.text()
|
config.text = try! doc.text()
|
||||||
cell.contentConfiguration = config
|
cell.contentConfiguration = config
|
||||||
}
|
}
|
||||||
|
let timelineDescriptionCell = UICollectionView.CellRegistration<PublicTimelineDescriptionCollectionViewCell, Item> { [unowned self] cell, indexPath, item in
|
||||||
|
guard case .public(let local) = timeline else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
cell.mastodonController = self.mastodonController
|
||||||
|
cell.local = local
|
||||||
|
cell.didDismiss = { [unowned self] in
|
||||||
|
self.removeTimelineDescriptionCell()
|
||||||
|
}
|
||||||
|
}
|
||||||
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
|
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
|
||||||
switch itemIdentifier {
|
switch itemIdentifier {
|
||||||
case .status(_, _):
|
case .status(_, _):
|
||||||
|
@ -102,13 +115,22 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
return loadingIndicatorCell(for: indexPath)
|
return loadingIndicatorCell(for: indexPath)
|
||||||
case .confirmLoadMore:
|
case .confirmLoadMore:
|
||||||
return confirmLoadMoreCell(for: indexPath)
|
return confirmLoadMoreCell(for: indexPath)
|
||||||
|
case .publicTimelineDescription:
|
||||||
|
self.isShowingTimelineDescription = true
|
||||||
|
return collectionView.dequeueConfiguredReusableCell(using: timelineDescriptionCell, for: indexPath, item: itemIdentifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func applyInitialSnapshot() {
|
private func applyInitialSnapshot() {
|
||||||
// TODO: this might not be necessary
|
if case .public(let local) = timeline,
|
||||||
// TODO: yes it is, for public timeline descriptions
|
(local && !Preferences.shared.hasShownLocalTimelineDescription) ||
|
||||||
|
(!local && Preferences.shared.hasShownFederatedTimelineDescription) {
|
||||||
|
var snapshot = dataSource.snapshot()
|
||||||
|
snapshot.appendSections([.header])
|
||||||
|
snapshot.appendItems([.publicTimelineDescription], toSection: .header)
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -119,6 +141,13 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func removeTimelineDescriptionCell() {
|
||||||
|
var snapshot = dataSource.snapshot()
|
||||||
|
snapshot.deleteSections([.header])
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: true)
|
||||||
|
isShowingTimelineDescription = false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimelineViewController {
|
extension TimelineViewController {
|
||||||
|
@ -135,8 +164,7 @@ extension TimelineViewController {
|
||||||
case status(id: String, state: StatusState)
|
case status(id: String, state: StatusState)
|
||||||
case loadingIndicator
|
case loadingIndicator
|
||||||
case confirmLoadMore
|
case confirmLoadMore
|
||||||
// // TODO: remove local param from this
|
case publicTimelineDescription
|
||||||
// case publicTimelineDescription(local: Bool)
|
|
||||||
|
|
||||||
static func fromTimelineItem(_ id: String) -> Self {
|
static func fromTimelineItem(_ id: String) -> Self {
|
||||||
return .status(id: id, state: .unknown)
|
return .status(id: id, state: .unknown)
|
||||||
|
@ -150,8 +178,8 @@ extension TimelineViewController {
|
||||||
return true
|
return true
|
||||||
case (.confirmLoadMore, .confirmLoadMore):
|
case (.confirmLoadMore, .confirmLoadMore):
|
||||||
return true
|
return true
|
||||||
// case let (.publicTimelineDescription(local: a), .publicTimelineDescription(local: b)):
|
case (.publicTimelineDescription, .publicTimelineDescription):
|
||||||
// return a == b
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -166,15 +194,14 @@ extension TimelineViewController {
|
||||||
hasher.combine(1)
|
hasher.combine(1)
|
||||||
case .confirmLoadMore:
|
case .confirmLoadMore:
|
||||||
hasher.combine(2)
|
hasher.combine(2)
|
||||||
// case .publicTimelineDescription(local: let local):
|
case .publicTimelineDescription:
|
||||||
// hasher.combine(3)
|
hasher.combine(3)
|
||||||
// hasher.combine(local)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hideSeparators: Bool {
|
var hideSeparators: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .loadingIndicator:
|
case .loadingIndicator, .publicTimelineDescription:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
@ -192,8 +219,6 @@ extension TimelineViewController {
|
||||||
throw Error.noClient
|
throw Error.noClient
|
||||||
}
|
}
|
||||||
|
|
||||||
try await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC)
|
|
||||||
|
|
||||||
let request = Client.getStatuses(timeline: timeline)
|
let request = Client.getStatuses(timeline: timeline)
|
||||||
let (statuses, _) = try await mastodonController.run(request)
|
let (statuses, _) = try await mastodonController.run(request)
|
||||||
|
|
||||||
|
@ -235,8 +260,6 @@ extension TimelineViewController {
|
||||||
throw Error.noOlder
|
throw Error.noOlder
|
||||||
}
|
}
|
||||||
|
|
||||||
try await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC)
|
|
||||||
|
|
||||||
let request = Client.getStatuses(timeline: timeline, range: older)
|
let request = Client.getStatuses(timeline: timeline, range: older)
|
||||||
let (statuses, _) = try await mastodonController.run(request)
|
let (statuses, _) = try await mastodonController.run(request)
|
||||||
|
|
||||||
|
@ -273,4 +296,24 @@ extension TimelineViewController: UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
guard let item = dataSource.itemIdentifier(for: indexPath) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch item {
|
||||||
|
case .publicTimelineDescription:
|
||||||
|
removeTimelineDescriptionCell()
|
||||||
|
|
||||||
|
default:
|
||||||
|
// TODO: cell selection
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
|
if isShowingTimelineDescription {
|
||||||
|
removeTimelineDescriptionCell()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,10 @@ class TimelinesPageViewController: SegmentedPageViewController {
|
||||||
let home = TimelineViewController(for: .home, mastodonController: mastodonController)
|
let home = TimelineViewController(for: .home, mastodonController: mastodonController)
|
||||||
home.title = homeTitle
|
home.title = homeTitle
|
||||||
|
|
||||||
let federated = TimelineTableViewController(for: .public(local: false), mastodonController: mastodonController)
|
let federated = TimelineViewController(for: .public(local: false), mastodonController: mastodonController)
|
||||||
federated.title = federatedTitle
|
federated.title = federatedTitle
|
||||||
|
|
||||||
let local = TimelineTableViewController(for: .public(local: true), mastodonController: mastodonController)
|
let local = TimelineViewController(for: .public(local: true), mastodonController: mastodonController)
|
||||||
local.title = localTitle
|
local.title = localTitle
|
||||||
|
|
||||||
super.init(titles: [
|
super.init(titles: [
|
||||||
|
|
|
@ -52,7 +52,7 @@ extension TimelineLikeCollectionViewController {
|
||||||
snapshot.appendSections([.footer])
|
snapshot.appendSections([.footer])
|
||||||
}
|
}
|
||||||
snapshot.appendItems([.confirmLoadMore], toSection: .footer)
|
snapshot.appendItems([.confirmLoadMore], toSection: .footer)
|
||||||
await dataSource.apply(snapshot, animatingDifferences: false)
|
await apply(snapshot, animatingDifferences: false)
|
||||||
}
|
}
|
||||||
for await _ in confirmLoadMore.values {
|
for await _ in confirmLoadMore.values {
|
||||||
return true
|
return true
|
||||||
|
@ -94,14 +94,14 @@ extension TimelineLikeCollectionViewController {
|
||||||
} else {
|
} else {
|
||||||
snapshot.appendItems([.loadingIndicator], toSection: .footer)
|
snapshot.appendItems([.loadingIndicator], toSection: .footer)
|
||||||
}
|
}
|
||||||
await dataSource.apply(snapshot, animatingDifferences: false)
|
await apply(snapshot, animatingDifferences: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRemoveLoadingIndicator() async {
|
func handleRemoveLoadingIndicator() async {
|
||||||
let oldContentOffset = collectionView.contentOffset
|
let oldContentOffset = collectionView.contentOffset
|
||||||
var snapshot = dataSource.snapshot()
|
var snapshot = dataSource.snapshot()
|
||||||
snapshot.deleteSections([.footer])
|
snapshot.deleteSections([.footer])
|
||||||
await dataSource.apply(snapshot, animatingDifferences: false)
|
await apply(snapshot, animatingDifferences: false)
|
||||||
// prevent the collection view from scrolling as we remove the loading indicator and add the timeline items
|
// prevent the collection view from scrolling as we remove the loading indicator and add the timeline items
|
||||||
collectionView.contentOffset = oldContentOffset
|
collectionView.contentOffset = oldContentOffset
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ extension TimelineLikeCollectionViewController {
|
||||||
}
|
}
|
||||||
snapshot.appendSections([.entries])
|
snapshot.appendSections([.entries])
|
||||||
snapshot.appendItems(timelineItems.map { .fromTimelineItem($0) }, toSection: .entries)
|
snapshot.appendItems(timelineItems.map { .fromTimelineItem($0) }, toSection: .entries)
|
||||||
await dataSource.apply(snapshot, animatingDifferences: false)
|
await apply(snapshot, animatingDifferences: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadNewerError(_ error: Swift.Error) async {
|
func handleLoadNewerError(_ error: Swift.Error) async {
|
||||||
|
@ -156,7 +156,7 @@ extension TimelineLikeCollectionViewController {
|
||||||
} else {
|
} else {
|
||||||
snapshot.appendItems(items, toSection: .entries)
|
snapshot.appendItems(items, toSection: .entries)
|
||||||
}
|
}
|
||||||
await dataSource.apply(snapshot, animatingDifferences: false)
|
await apply(snapshot, animatingDifferences: false)
|
||||||
|
|
||||||
if let first,
|
if let first,
|
||||||
let indexPath = dataSource.indexPath(for: first) {
|
let indexPath = dataSource.indexPath(for: first) {
|
||||||
|
@ -183,11 +183,20 @@ extension TimelineLikeCollectionViewController {
|
||||||
snapshot.deleteItems([.confirmLoadMore])
|
snapshot.deleteItems([.confirmLoadMore])
|
||||||
}
|
}
|
||||||
snapshot.appendItems(timelineItems.map { .fromTimelineItem($0) }, toSection: .entries)
|
snapshot.appendItems(timelineItems.map { .fromTimelineItem($0) }, toSection: .entries)
|
||||||
await dataSource.apply(snapshot, animatingDifferences: false)
|
await apply(snapshot, animatingDifferences: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimelineLikeCollectionViewController {
|
extension TimelineLikeCollectionViewController {
|
||||||
|
// apply(_:animatingDifferences:) is marked as nonisolated, so just awaiting it doesn't dispatch to the main thread, unlike other async @MainActor methods
|
||||||
|
// but we always want to update the data source on the main thread for consistency, so this method does that
|
||||||
|
func apply(_ snapshot: NSDiffableDataSourceSnapshot<Section, Item>, animatingDifferences: Bool) async {
|
||||||
|
let task = Task { @MainActor in
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
|
||||||
|
}
|
||||||
|
await task.value
|
||||||
|
}
|
||||||
|
|
||||||
func registerTimelineLikeCells() {
|
func registerTimelineLikeCells() {
|
||||||
collectionView.register(LoadingCollectionViewCell.self, forCellWithReuseIdentifier: "loadingIndicator")
|
collectionView.register(LoadingCollectionViewCell.self, forCellWithReuseIdentifier: "loadingIndicator")
|
||||||
collectionView.register(ConfirmLoadMoreCollectionViewCell.self, forCellWithReuseIdentifier: "confirmLoadMore")
|
collectionView.register(ConfirmLoadMoreCollectionViewCell.self, forCellWithReuseIdentifier: "confirmLoadMore")
|
||||||
|
|
Loading…
Reference in New Issue