forked from shadowfacts/Tusker
183 lines
6.0 KiB
Swift
183 lines
6.0 KiB
Swift
//
|
|
// NotificationGroup.swift
|
|
// Pachyderm
|
|
//
|
|
// Created by Shadowfacts on 9/5/19.
|
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import WebURL
|
|
|
|
public struct NotificationGroup: Identifiable, Hashable, Sendable {
|
|
public private(set) var notifications: [Notification]
|
|
public let id: String
|
|
public let kind: Kind
|
|
|
|
public init?(notifications: [Notification], kind: Kind) {
|
|
guard !notifications.isEmpty else { return nil }
|
|
self.notifications = notifications
|
|
self.id = notifications.first!.id
|
|
self.kind = kind
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
private static func groupKind(for notification: Notification) -> Kind {
|
|
switch notification.kind {
|
|
case .mention:
|
|
return .mention
|
|
case .reblog:
|
|
return .reblog
|
|
case .favourite:
|
|
return .favourite
|
|
case .follow:
|
|
return .follow
|
|
case .followRequest:
|
|
return .followRequest
|
|
case .poll:
|
|
return .poll
|
|
case .update:
|
|
return .update
|
|
case .status:
|
|
return .status
|
|
case .emojiReaction:
|
|
if let emoji = notification.emoji {
|
|
return .emojiReaction(emoji, notification.emojiURL)
|
|
} else {
|
|
return .unknown
|
|
}
|
|
case .unknown:
|
|
return .unknown
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
public static func createGroups(notifications: [Notification], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] {
|
|
var groups = [NotificationGroup]()
|
|
for notification in notifications {
|
|
let groupKind = groupKind(for: notification)
|
|
|
|
if allowedTypes.contains(notification.kind) {
|
|
if let lastGroup = groups.last, canMerge(notification: notification, kind: groupKind, into: lastGroup) {
|
|
groups[groups.count - 1].append(notification)
|
|
continue
|
|
} else if groups.count >= 2 {
|
|
let secondToLastGroup = groups[groups.count - 2]
|
|
if allowedTypes.contains(notification.kind), canMerge(notification: notification, kind: groupKind, into: secondToLastGroup) {
|
|
groups[groups.count - 2].append(notification)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
groups.append(NotificationGroup(notifications: [notification], kind: groupKind)!)
|
|
}
|
|
return groups
|
|
}
|
|
|
|
private static func canMerge(notification: Notification, kind: Kind, into group: NotificationGroup) -> Bool {
|
|
return 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.notificationKind) {
|
|
|
|
second.removeFirst()
|
|
|
|
guard let lastGroup = merged.last,
|
|
allowedTypes.contains(lastGroup.kind.notificationKind) else {
|
|
merged.append(firstGroupFromSecond)
|
|
break
|
|
}
|
|
|
|
if canMerge(notification: firstGroupFromSecond.notifications.first!, kind: firstGroupFromSecond.kind, 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.notificationKind), canMerge(notification: firstGroupFromSecond.notifications.first!, kind: firstGroupFromSecond.kind, into: secondToLastGroup) {
|
|
merged[merged.count - 2].append(group: firstGroupFromSecond)
|
|
} else {
|
|
merged.append(firstGroupFromSecond)
|
|
}
|
|
} else {
|
|
merged.append(firstGroupFromSecond)
|
|
}
|
|
}
|
|
merged.append(contentsOf: second)
|
|
return merged
|
|
}
|
|
|
|
public enum Kind: Sendable, Equatable {
|
|
case mention
|
|
case reblog
|
|
case favourite
|
|
case follow
|
|
case followRequest
|
|
case poll
|
|
case update
|
|
case status
|
|
case emojiReaction(String, WebURL?)
|
|
case unknown
|
|
|
|
var notificationKind: Notification.Kind {
|
|
switch self {
|
|
case .mention:
|
|
.mention
|
|
case .reblog:
|
|
.reblog
|
|
case .favourite:
|
|
.favourite
|
|
case .follow:
|
|
.follow
|
|
case .followRequest:
|
|
.followRequest
|
|
case .poll:
|
|
.poll
|
|
case .update:
|
|
.update
|
|
case .status:
|
|
.status
|
|
case .emojiReaction(_, _):
|
|
.emojiReaction
|
|
case .unknown:
|
|
.unknown
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|