forked from shadowfacts/Tusker
Show jump to present toast if necessary when scene re-appears
This commit is contained in:
parent
ce534c4a05
commit
9ff1452c68
|
@ -125,6 +125,8 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
cell.update()
|
||||
}
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(sceneWillEnterForeground), name: UIScene.willEnterForegroundNotification, object: nil)
|
||||
}
|
||||
|
||||
// separate method because InstanceTimelineViewController needs to be able to customize it
|
||||
|
@ -242,6 +244,20 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
// }
|
||||
// }
|
||||
|
||||
@objc private func sceneWillEnterForeground(_ notification: Foundation.Notification) {
|
||||
guard let scene = notification.object as? UIScene,
|
||||
// view.window is nil when the VC is not on screen
|
||||
view.window?.windowScene == scene else {
|
||||
return
|
||||
}
|
||||
Task {
|
||||
if case .idle = controller.state,
|
||||
let presentItems = try? await loadInitial() {
|
||||
insertPresentItemsIfNecessary(presentItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func refresh() {
|
||||
Task {
|
||||
if case .notLoadedInitial = controller.state {
|
||||
|
@ -249,47 +265,8 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
} else {
|
||||
// I'm not sure whether this should move into TimelineLikeController/TimelineLikeCollectionViewController
|
||||
let (_, presentItems) = await (controller.loadNewer(), try? loadInitial())
|
||||
if let presentItems,
|
||||
case .status(id: let id, state: _) = dataSource.snapshot().itemIdentifiers(inSection: .statuses).first {
|
||||
// if there's no overlap between presentItems and the existing items in the data source, prompt the user to scroll to present
|
||||
if !presentItems.contains(id) {
|
||||
var snapshot = self.dataSource.snapshot()
|
||||
let currentItems = snapshot.itemIdentifiers(inSection: .statuses)
|
||||
guard let item = currentItems.first,
|
||||
case .status(id: id, state: _) = item else {
|
||||
return
|
||||
}
|
||||
|
||||
// remove any existing gap if there is one
|
||||
if let index = currentItems.lastIndex(of: .gap) {
|
||||
snapshot.deleteItems(Array(currentItems[index...]))
|
||||
}
|
||||
snapshot.insertItems([.gap], beforeItem: item)
|
||||
snapshot.insertItems(presentItems.map { .status(id: $0, state: .unknown) }, beforeItem: .gap)
|
||||
|
||||
// use a snapshot of the collection view to hide the flicker as the content offset changes and then changes back
|
||||
let snapshotView = collectionView.snapshotView(afterScreenUpdates: false)!
|
||||
snapshotView.layer.zPosition = 1000
|
||||
snapshotView.frame = view.bounds
|
||||
view.addSubview(snapshotView)
|
||||
|
||||
let bottomOffset = collectionView.contentSize.height - collectionView.contentOffset.y
|
||||
self.dataSource.apply(snapshot, animatingDifferences: false) {
|
||||
self.collectionView.contentOffset = CGPoint(x: 0, y: self.collectionView.contentSize.height - bottomOffset)
|
||||
snapshotView.removeFromSuperview()
|
||||
}
|
||||
|
||||
var config = ToastConfiguration(title: "Jump to present")
|
||||
config.edge = .top
|
||||
config.systemImageName = "arrow.up"
|
||||
config.dismissAutomaticallyAfter = 4
|
||||
config.action = { [unowned self] toast in
|
||||
toast.dismissToast(animated: true)
|
||||
|
||||
self.collectionView.scrollToTop()
|
||||
}
|
||||
self.showToast(configuration: config, animated: true)
|
||||
}
|
||||
if let presentItems {
|
||||
insertPresentItemsIfNecessary(presentItems)
|
||||
}
|
||||
}
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
|
@ -298,6 +275,68 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
}
|
||||
}
|
||||
|
||||
private func insertPresentItemsIfNecessary(_ presentItems: [String]) {
|
||||
var snapshot = dataSource.snapshot()
|
||||
let currentItems = snapshot.itemIdentifiers(inSection: .statuses)
|
||||
if case .status(id: let firstID, state: _) = currentItems.first,
|
||||
// if there's no overlap between presentItems and the existing items in the data source, insert the present items and prompt the user
|
||||
!presentItems.contains(firstID) {
|
||||
let applySnapshotBeforeScrolling: Bool
|
||||
|
||||
// remove any existing gap, if there is one
|
||||
if let index = currentItems.lastIndex(of: .gap) {
|
||||
snapshot.deleteItems(Array(currentItems[index...]))
|
||||
|
||||
let statusesSection = snapshot.indexOfSection(.statuses)!
|
||||
if collectionView.indexPathsForVisibleItems.contains(IndexPath(row: index, section: statusesSection)) {
|
||||
// the gap cell is on screen
|
||||
applySnapshotBeforeScrolling = false
|
||||
} else if let topMostVisibleCell = collectionView.indexPathsForVisibleItems.first(where: { $0.section == statusesSection }),
|
||||
index < topMostVisibleCell.row {
|
||||
// the gap cell is above the top, so applying the snapshot would remove the currently-viewed statuses
|
||||
applySnapshotBeforeScrolling = false
|
||||
} else {
|
||||
// the gap cell is below the bottom of the screen
|
||||
applySnapshotBeforeScrolling = true
|
||||
}
|
||||
} else {
|
||||
// there is no existing gap
|
||||
applySnapshotBeforeScrolling = true
|
||||
}
|
||||
snapshot.insertItems([.gap], beforeItem: currentItems.first!)
|
||||
snapshot.insertItems(presentItems.map { .status(id: $0, state: .unknown) }, beforeItem: .gap)
|
||||
|
||||
if applySnapshotBeforeScrolling {
|
||||
// use a snapshot of the collection view to hide the flicker as the content offset changes and then changes back
|
||||
let snapshotView = collectionView.snapshotView(afterScreenUpdates: false)!
|
||||
snapshotView.layer.zPosition = 1000
|
||||
snapshotView.frame = view.bounds
|
||||
view.addSubview(snapshotView)
|
||||
|
||||
let bottomOffset = collectionView.contentSize.height - collectionView.contentOffset.y
|
||||
self.dataSource.apply(snapshot, animatingDifferences: false) {
|
||||
self.collectionView.contentOffset = CGPoint(x: 0, y: self.collectionView.contentSize.height - bottomOffset)
|
||||
snapshotView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
var config = ToastConfiguration(title: "Jump to present")
|
||||
config.edge = .top
|
||||
config.systemImageName = "arrow.up"
|
||||
config.dismissAutomaticallyAfter = 4
|
||||
config.action = { [unowned self] toast in
|
||||
toast.dismissToast(animated: true)
|
||||
|
||||
if !applySnapshotBeforeScrolling {
|
||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
|
||||
self.collectionView.scrollToTop()
|
||||
}
|
||||
self.showToast(configuration: config, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension TimelineViewController {
|
||||
|
|
Loading…
Reference in New Issue