Swiftgram/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift
2024-07-30 11:29:42 +02:00

250 lines
11 KiB
Swift

import Foundation
import Postbox
import TelegramApi
extension ReactionsMessageAttribute {
func withUpdatedResults(_ reactions: Api.MessageReactions) -> ReactionsMessageAttribute {
switch reactions {
case let .messageReactions(flags, results, recentReactions, topReactors):
let _ = topReactors
let min = (flags & (1 << 0)) != 0
let canViewList = (flags & (1 << 2)) != 0
let isTags = (flags & (1 << 3)) != 0
var reactions = results.compactMap { result -> MessageReaction? in
switch result {
case let .reactionCount(_, chosenOrder, reaction, count):
if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
return MessageReaction(value: reaction, count: count, chosenOrder: chosenOrder.flatMap(Int.init))
} else {
return nil
}
}
}
let parsedRecentReactions: [ReactionsMessageAttribute.RecentPeer]
if let recentReactions = recentReactions {
parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in
switch recentReaction {
case let .messagePeerReaction(flags, peerId, date, reaction):
let isLarge = (flags & (1 << 0)) != 0
let isUnseen = (flags & (1 << 1)) != 0
let isMy = (flags & (1 << 2)) != 0
if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, isMy: isMy, peerId: peerId.peerId, timestamp: date)
} else {
return nil
}
}
}
} else {
parsedRecentReactions = []
}
if min {
var currentSelectedReactions: [MessageReaction.Reaction: Int] = [:]
for reaction in self.reactions {
if let chosenOrder = reaction.chosenOrder {
currentSelectedReactions[reaction.value] = chosenOrder
break
}
}
if !currentSelectedReactions.isEmpty {
for i in 0 ..< reactions.count {
if let chosenOrder = currentSelectedReactions[reactions[i].value] {
reactions[i].chosenOrder = chosenOrder
}
}
}
}
return ReactionsMessageAttribute(canViewList: canViewList, isTags: isTags, reactions: reactions, recentPeers: parsedRecentReactions)
}
}
}
public func mergedMessageReactionsAndPeers(accountPeerId: EnginePeer.Id, accountPeer: EnginePeer?, message: Message) -> (reactions: [MessageReaction], peers: [(MessageReaction.Reaction, EnginePeer)]) {
guard let attribute = mergedMessageReactions(attributes: message.attributes, isTags: message.areReactionsTags(accountPeerId: accountPeerId)) else {
return ([], [])
}
var recentPeers: [(MessageReaction.Reaction, EnginePeer)] = []
if message.id.peerId.namespace == Namespaces.Peer.CloudUser {
for reaction in attribute.reactions {
var selfCount: Int32 = 0
if reaction.isSelected {
selfCount += 1
if let accountPeer = accountPeer {
recentPeers.append((reaction.value, accountPeer))
}
}
if reaction.count >= selfCount + 1 {
if let peer = message.peers[message.id.peerId] {
recentPeers.append((reaction.value, EnginePeer(peer)))
}
}
}
} else {
recentPeers = attribute.recentPeers.compactMap { recentPeer -> (MessageReaction.Reaction, EnginePeer)? in
if let peer = message.peers[recentPeer.peerId] {
return (recentPeer.value, EnginePeer(peer))
} else {
return nil
}
}
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
recentPeers.removeAll()
}
}
#if DEBUG
var reactions = attribute.reactions
if "".isEmpty {
if let index = reactions.firstIndex(where: {
if case .custom(MessageReaction.starsReactionId) = $0.value {
return true
} else {
return false
}
}) {
let value = reactions[index]
reactions.remove(at: index)
reactions.insert(value, at: 0)
} else {
reactions.insert(MessageReaction(value: .custom(MessageReaction.starsReactionId), count: 1000000, chosenOrder: nil), at: 0)
}
}
#else
let reactions = attribute.reactions
#endif
return (reactions, recentPeers)
}
private func mergeReactions(reactions: [MessageReaction], recentPeers: [ReactionsMessageAttribute.RecentPeer], pending: [PendingReactionsMessageAttribute.PendingReaction], accountPeerId: PeerId) -> ([MessageReaction], [ReactionsMessageAttribute.RecentPeer]) {
var result = reactions
var recentPeers = recentPeers
var pendingIndex: Int = Int(Int32.max - 100)
for pendingReaction in pending {
if let index = result.firstIndex(where: { $0.value == pendingReaction.value }) {
var merged = result[index]
if merged.chosenOrder == nil {
merged.chosenOrder = pendingIndex
pendingIndex += 1
merged.count += 1
}
result[index] = merged
} else {
result.append(MessageReaction(value: pendingReaction.value, count: 1, chosenOrder: pendingIndex))
pendingIndex += 1
}
let pendingReactionSendAsPeerId = pendingReaction.sendAsPeerId ?? accountPeerId
if let index = recentPeers.firstIndex(where: {
$0.value == pendingReaction.value && ($0.peerId == pendingReactionSendAsPeerId || $0.isMy)
}) {
recentPeers.remove(at: index)
}
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: pendingReaction.value, isLarge: false, isUnseen: false, isMy: true, peerId: pendingReactionSendAsPeerId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)))
}
for i in (0 ..< result.count).reversed() {
if result[i].chosenOrder != nil {
if !pending.contains(where: { $0.value == result[i].value }) {
if let index = recentPeers.firstIndex(where: { $0.value == result[i].value && ($0.peerId == accountPeerId || $0.isMy) }) {
recentPeers.remove(at: index)
}
if result[i].count <= 1 {
result.remove(at: i)
} else {
result[i].count -= 1
result[i].chosenOrder = nil
}
}
}
}
if recentPeers.count > 3 {
recentPeers.removeFirst(recentPeers.count - 3)
}
return (result, recentPeers)
}
public func mergedMessageReactions(attributes: [MessageAttribute], isTags: Bool) -> ReactionsMessageAttribute? {
var current: ReactionsMessageAttribute?
var pending: PendingReactionsMessageAttribute?
for attribute in attributes {
if let attribute = attribute as? ReactionsMessageAttribute {
current = attribute
} else if let attribute = attribute as? PendingReactionsMessageAttribute {
pending = attribute
}
}
if let pending = pending, let accountPeerId = pending.accountPeerId {
var reactions = current?.reactions ?? []
var recentPeers = current?.recentPeers ?? []
let (updatedReactions, updatedRecentPeers) = mergeReactions(reactions: reactions, recentPeers: recentPeers, pending: pending.reactions, accountPeerId: accountPeerId)
reactions = updatedReactions
recentPeers = updatedRecentPeers
if !reactions.isEmpty {
return ReactionsMessageAttribute(canViewList: current?.canViewList ?? false, isTags: current?.isTags ?? isTags, reactions: reactions, recentPeers: recentPeers)
} else {
return nil
}
} else if let current = current {
return current
} else {
return nil
}
}
extension ReactionsMessageAttribute {
convenience init(apiReactions: Api.MessageReactions) {
switch apiReactions {
case let .messageReactions(flags, results, recentReactions, topReactors):
let _ = topReactors
let canViewList = (flags & (1 << 2)) != 0
let isTags = (flags & (1 << 3)) != 0
let parsedRecentReactions: [ReactionsMessageAttribute.RecentPeer]
if let recentReactions = recentReactions {
parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in
switch recentReaction {
case let .messagePeerReaction(flags, peerId, date, reaction):
let isLarge = (flags & (1 << 0)) != 0
let isUnseen = (flags & (1 << 1)) != 0
let isMy = (flags & (1 << 2)) != 0
if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, isMy: isMy, peerId: peerId.peerId, timestamp: date)
} else {
return nil
}
}
}
} else {
parsedRecentReactions = []
}
self.init(
canViewList: canViewList,
isTags: isTags,
reactions: results.compactMap { result -> MessageReaction? in
switch result {
case let .reactionCount(_, chosenOrder, reaction, count):
if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
return MessageReaction(value: reaction, count: count, chosenOrder: chosenOrder.flatMap(Int.init))
} else {
return nil
}
}
},
recentPeers: parsedRecentReactions
)
}
}
}