forked from shadowfacts/Tusker
Move CollapseState out of NotificationGroup
This commit is contained in:
parent
038e4b2e4e
commit
468af3f9a6
|
@ -12,20 +12,12 @@ public struct NotificationGroup: Identifiable, Hashable, Sendable {
|
||||||
public private(set) var notifications: [Notification]
|
public private(set) var notifications: [Notification]
|
||||||
public let id: String
|
public let id: String
|
||||||
public let kind: Notification.Kind
|
public let kind: Notification.Kind
|
||||||
public let statusState: CollapseState?
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
public init?(notifications: [Notification]) {
|
public init?(notifications: [Notification]) {
|
||||||
guard !notifications.isEmpty else { return nil }
|
guard !notifications.isEmpty else { return nil }
|
||||||
self.notifications = notifications
|
self.notifications = notifications
|
||||||
self.id = notifications.first!.id
|
self.id = notifications.first!.id
|
||||||
self.kind = notifications.first!.kind
|
self.kind = notifications.first!.kind
|
||||||
switch kind {
|
|
||||||
case .mention, .status:
|
|
||||||
self.statusState = .unknown
|
|
||||||
default:
|
|
||||||
self.statusState = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: NotificationGroup, rhs: NotificationGroup) -> Bool {
|
public static func ==(lhs: NotificationGroup, rhs: NotificationGroup) -> Bool {
|
||||||
|
|
|
@ -109,10 +109,10 @@ class NotificationsCollectionViewController: UIViewController, TimelineLikeColle
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||||
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, NotificationGroup> { [unowned self] cell, indexPath, itemIdentifier in
|
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (NotificationGroup, CollapseState)> { [unowned self] cell, indexPath, itemIdentifier in
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
let statusID = itemIdentifier.notifications.first!.status!.id
|
let statusID = itemIdentifier.0.notifications.first!.status!.id
|
||||||
let statusState = itemIdentifier.statusState!
|
let statusState = itemIdentifier.1
|
||||||
cell.updateUI(statusID: statusID, state: statusState, filterResult: .allow, precomputedContent: nil)
|
cell.updateUI(statusID: statusID, state: statusState, filterResult: .allow, precomputedContent: nil)
|
||||||
}
|
}
|
||||||
let actionGroupCell = UICollectionView.CellRegistration<ActionNotificationGroupCollectionViewCell, NotificationGroup> { [unowned self] cell, indexPath, itemIdentifier in
|
let actionGroupCell = UICollectionView.CellRegistration<ActionNotificationGroupCollectionViewCell, NotificationGroup> { [unowned self] cell, indexPath, itemIdentifier in
|
||||||
|
@ -142,10 +142,10 @@ class NotificationsCollectionViewController: UIViewController, TimelineLikeColle
|
||||||
}
|
}
|
||||||
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 .group(let group):
|
case .group(let group, let collapseState):
|
||||||
switch group.kind {
|
switch group.kind {
|
||||||
case .status, .mention:
|
case .status, .mention:
|
||||||
return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: group)
|
return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (group, collapseState!))
|
||||||
case .favourite, .reblog:
|
case .favourite, .reblog:
|
||||||
return collectionView.dequeueConfiguredReusableCell(using: actionGroupCell, for: indexPath, item: group)
|
return collectionView.dequeueConfiguredReusableCell(using: actionGroupCell, for: indexPath, item: group)
|
||||||
case .follow:
|
case .follow:
|
||||||
|
@ -193,7 +193,7 @@ class NotificationsCollectionViewController: UIViewController, TimelineLikeColle
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dismissNotificationsInGroup(at indexPath: IndexPath) async {
|
private func dismissNotificationsInGroup(at indexPath: IndexPath) async {
|
||||||
guard case .group(let group) = dataSource.itemIdentifier(for: indexPath) else {
|
guard case .group(let group, let collapseState) = dataSource.itemIdentifier(for: indexPath) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let notifications = group.notifications
|
let notifications = group.notifications
|
||||||
|
@ -216,11 +216,11 @@ class NotificationsCollectionViewController: UIViewController, TimelineLikeColle
|
||||||
}
|
}
|
||||||
var snapshot = dataSource.snapshot()
|
var snapshot = dataSource.snapshot()
|
||||||
if dismissFailedIndices.isEmpty {
|
if dismissFailedIndices.isEmpty {
|
||||||
snapshot.deleteItems([.group(group)])
|
snapshot.deleteItems([.group(group, collapseState)])
|
||||||
} else if !dismissFailedIndices.isEmpty && dismissFailedIndices.count == notifications.count {
|
} else if !dismissFailedIndices.isEmpty && dismissFailedIndices.count == notifications.count {
|
||||||
let dismissFailed = dismissFailedIndices.sorted().map { notifications[$0] }
|
let dismissFailed = dismissFailedIndices.sorted().map { notifications[$0] }
|
||||||
snapshot.insertItems([.group(NotificationGroup(notifications: dismissFailed)!)], afterItem: .group(group))
|
snapshot.insertItems([.group(NotificationGroup(notifications: dismissFailed)!, collapseState)], afterItem: .group(group, collapseState))
|
||||||
snapshot.deleteItems([.group(group)])
|
snapshot.deleteItems([.group(group, collapseState)])
|
||||||
}
|
}
|
||||||
await apply(snapshot, animatingDifferences: true)
|
await apply(snapshot, animatingDifferences: true)
|
||||||
}
|
}
|
||||||
|
@ -235,16 +235,46 @@ extension NotificationsCollectionViewController {
|
||||||
static var entries: Self { .notifications }
|
static var entries: Self { .notifications }
|
||||||
}
|
}
|
||||||
enum Item: TimelineLikeCollectionViewItem {
|
enum Item: TimelineLikeCollectionViewItem {
|
||||||
case group(NotificationGroup)
|
case group(NotificationGroup, CollapseState?)
|
||||||
case loadingIndicator
|
case loadingIndicator
|
||||||
case confirmLoadMore
|
case confirmLoadMore
|
||||||
|
|
||||||
static func fromTimelineItem(_ item: NotificationGroup) -> Self {
|
static func fromTimelineItem(_ item: NotificationGroup) -> Self {
|
||||||
return .group(item)
|
switch item.kind {
|
||||||
|
case .mention, .status:
|
||||||
|
return .group(item, .unknown)
|
||||||
|
default:
|
||||||
|
return .group(item, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||||
|
switch (lhs, rhs) {
|
||||||
|
case (.group(let a, _), .group(let b, _)):
|
||||||
|
return a == b
|
||||||
|
case (.loadingIndicator, .loadingIndicator):
|
||||||
|
return true
|
||||||
|
case (.confirmLoadMore, .confirmLoadMore):
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
switch self {
|
||||||
|
case .group(let group, _):
|
||||||
|
hasher.combine(0)
|
||||||
|
hasher.combine(group)
|
||||||
|
case .loadingIndicator:
|
||||||
|
hasher.combine(1)
|
||||||
|
case .confirmLoadMore:
|
||||||
|
hasher.combine(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var group: NotificationGroup? {
|
var group: NotificationGroup? {
|
||||||
if case .group(let group) = self {
|
if case .group(let group, _) = self {
|
||||||
return group
|
return group
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -253,7 +283,7 @@ extension NotificationsCollectionViewController {
|
||||||
|
|
||||||
var isSelectable: Bool {
|
var isSelectable: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .group(_):
|
case .group(_, _):
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
@ -369,7 +399,7 @@ extension NotificationsCollectionViewController {
|
||||||
$0.id == topID
|
$0.id == topID
|
||||||
}
|
}
|
||||||
}!
|
}!
|
||||||
if let newTopIndexPath = dataSource.indexPath(for: .group(newTopGroup)) {
|
if let newTopIndexPath = dataSource.indexPath(for: .group(newTopGroup, nil)) {
|
||||||
collectionView.scrollToItem(at: newTopIndexPath, at: .top, animated: false)
|
collectionView.scrollToItem(at: newTopIndexPath, at: .top, animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,13 +459,13 @@ extension NotificationsCollectionViewController: UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
guard case .group(let group) = dataSource.itemIdentifier(for: indexPath) else {
|
guard case .group(let group, let collapseState) = dataSource.itemIdentifier(for: indexPath) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch group.kind {
|
switch group.kind {
|
||||||
case .mention, .status, .poll, .update:
|
case .mention, .status, .poll, .update:
|
||||||
let statusID = group.notifications.first!.status!.id
|
let statusID = group.notifications.first!.status!.id
|
||||||
let state = group.statusState?.copy() ?? .unknown
|
let state = collapseState?.copy() ?? .unknown
|
||||||
selected(status: statusID, state: state)
|
selected(status: statusID, state: state)
|
||||||
case .favourite, .reblog:
|
case .favourite, .reblog:
|
||||||
let type = group.kind == .favourite ? StatusActionAccountListViewController.ActionType.favorite : .reblog
|
let type = group.kind == .favourite ? StatusActionAccountListViewController.ActionType.favorite : .reblog
|
||||||
|
@ -463,7 +493,7 @@ extension NotificationsCollectionViewController: UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
guard case .group(let group) = dataSource.itemIdentifier(for: indexPath),
|
guard case .group(let group, let collapseState) = dataSource.itemIdentifier(for: indexPath),
|
||||||
let cell = collectionView.cellForItem(at: indexPath) else {
|
let cell = collectionView.cellForItem(at: indexPath) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -473,7 +503,7 @@ extension NotificationsCollectionViewController: UICollectionViewDelegate {
|
||||||
let status = mastodonController.persistentContainer.status(for: statusID) else {
|
let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let state = group.statusState?.copy() ?? .unknown
|
let state = collapseState?.copy() ?? .unknown
|
||||||
return UIContextMenuConfiguration {
|
return UIContextMenuConfiguration {
|
||||||
ConversationViewController(for: statusID, state: state, mastodonController: self.mastodonController)
|
ConversationViewController(for: statusID, state: state, mastodonController: self.mastodonController)
|
||||||
} actionProvider: { _ in
|
} actionProvider: { _ in
|
||||||
|
@ -536,7 +566,7 @@ extension NotificationsCollectionViewController: UICollectionViewDelegate {
|
||||||
|
|
||||||
extension NotificationsCollectionViewController: UICollectionViewDragDelegate {
|
extension NotificationsCollectionViewController: UICollectionViewDragDelegate {
|
||||||
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||||
guard case .group(let group) = dataSource.itemIdentifier(for: indexPath) else {
|
guard case .group(let group, _) = dataSource.itemIdentifier(for: indexPath) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
switch group.kind {
|
switch group.kind {
|
||||||
|
|
Loading…
Reference in New Issue