// // NotificationGroup.swift // Pachyderm // // Created by Shadowfacts on 9/5/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import Foundation public struct NotificationGroup: Identifiable, Hashable { public private(set) var notifications: [Notification] public let id: String public let kind: Notification.Kind public let statusState: StatusState? init?(notifications: [Notification]) { guard !notifications.isEmpty else { return nil } self.notifications = notifications self.id = notifications.first!.id self.kind = notifications.first!.kind if kind == .mention { self.statusState = .unknown } else { self.statusState = nil } } public static func ==(lhs: NotificationGroup, rhs: NotificationGroup) -> Bool { guard lhs.notifications.count == rhs.notifications.count else { return false } for (a, b) in zip(lhs.notifications, rhs.notifications) where a.id != b.id { return false } return true } public func hash(into hasher: inout Hasher) { for notification in notifications { hasher.combine(notification.id) } } private mutating func append(_ notification: Notification) { notifications.append(notification) } private mutating func append(group: NotificationGroup) { notifications.append(contentsOf: group.notifications) } public static func createGroups(notifications: [Notification], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] { var groups = [NotificationGroup]() for notification in notifications { if allowedTypes.contains(notification.kind) { if let lastGroup = groups.last, canMerge(notification: notification, into: lastGroup) { groups[groups.count - 1].append(notification) continue } else if groups.count >= 2 { let secondToLastGroup = groups[groups.count - 2] if allowedTypes.contains(groups[groups.count - 1].kind), canMerge(notification: notification, into: secondToLastGroup) { groups[groups.count - 2].append(notification) continue } } } groups.append(NotificationGroup(notifications: [notification])!) } return groups } private static func canMerge(notification: Notification, into group: NotificationGroup) -> Bool { return notification.kind == group.kind && notification.status?.id == group.notifications.first!.status?.id } public static func mergeGroups(first: [NotificationGroup], second: [NotificationGroup], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] { guard !first.isEmpty else { return second } guard !second.isEmpty else { return first } var merged = first var second = second merged.reserveCapacity(second.count) while let firstGroupFromSecond = second.first, allowedTypes.contains(firstGroupFromSecond.kind) { second.removeFirst() guard let lastGroup = merged.last, allowedTypes.contains(lastGroup.kind) else { merged.append(firstGroupFromSecond) break } if canMerge(notification: firstGroupFromSecond.notifications.first!, into: lastGroup) { merged[merged.count - 1].append(group: firstGroupFromSecond) } else if merged.count >= 2 { let secondToLastGroup = merged[merged.count - 2] if allowedTypes.contains(secondToLastGroup.kind), canMerge(notification: firstGroupFromSecond.notifications.first!, into: secondToLastGroup) { merged[merged.count - 2].append(group: firstGroupFromSecond) } else { merged.append(firstGroupFromSecond) } } else { merged.append(firstGroupFromSecond) } } merged.append(contentsOf: second) return merged } }