mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Update API
This commit is contained in:
parent
29fe0f0879
commit
b4f8d20d7b
@ -225,7 +225,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if let reaction = item.reaction {
|
||||
attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, isSelected: true)], recentPeers: []))
|
||||
attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, chosenOrder: 0)], recentPeers: []))
|
||||
}
|
||||
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, isCentered: true)
|
||||
|
@ -612,7 +612,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1992950669] = { return Api.Reaction.parse_reactionCustomEmoji($0) }
|
||||
dict[455247544] = { return Api.Reaction.parse_reactionEmoji($0) }
|
||||
dict[2046153753] = { return Api.Reaction.parse_reactionEmpty($0) }
|
||||
dict[609529328] = { return Api.ReactionCount.parse_reactionCount($0) }
|
||||
dict[-1546531968] = { return Api.ReactionCount.parse_reactionCount($0) }
|
||||
dict[-1551583367] = { return Api.ReceivedNotifyMessage.parse_receivedNotifyMessage($0) }
|
||||
dict[-1294306862] = { return Api.RecentMeUrl.parse_recentMeUrlChat($0) }
|
||||
dict[-347535331] = { return Api.RecentMeUrl.parse_recentMeUrlChatInvite($0) }
|
||||
@ -829,10 +829,11 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1821035490] = { return Api.Update.parse_updateSavedGifs($0) }
|
||||
dict[1960361625] = { return Api.Update.parse_updateSavedRingtones($0) }
|
||||
dict[-337352679] = { return Api.Update.parse_updateServiceNotification($0) }
|
||||
dict[1135492588] = { return Api.Update.parse_updateStickerSets($0) }
|
||||
dict[834816008] = { return Api.Update.parse_updateStickerSets($0) }
|
||||
dict[196268545] = { return Api.Update.parse_updateStickerSetsOrder($0) }
|
||||
dict[-2112423005] = { return Api.Update.parse_updateTheme($0) }
|
||||
dict[8703322] = { return Api.Update.parse_updateTranscribedAudio($0) }
|
||||
dict[674706841] = { return Api.Update.parse_updateUserEmojiStatus($0) }
|
||||
dict[-1007549728] = { return Api.Update.parse_updateUserName($0) }
|
||||
dict[88680979] = { return Api.Update.parse_updateUserPhone($0) }
|
||||
dict[-232290676] = { return Api.Update.parse_updateUserPhoto($0) }
|
||||
|
@ -68,15 +68,16 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum ReactionCount: TypeConstructorDescription {
|
||||
case reactionCount(flags: Int32, reaction: Api.Reaction, count: Int32)
|
||||
case reactionCount(flags: Int32, chosenOrder: Int32?, reaction: Api.Reaction, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .reactionCount(let flags, let reaction, let count):
|
||||
case .reactionCount(let flags, let chosenOrder, let reaction, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(609529328)
|
||||
buffer.appendInt32(-1546531968)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(chosenOrder!, buffer: buffer, boxed: false)}
|
||||
reaction.serialize(buffer, true)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
@ -85,25 +86,28 @@ public extension Api {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .reactionCount(let flags, let reaction, let count):
|
||||
return ("reactionCount", [("flags", String(describing: flags)), ("reaction", String(describing: reaction)), ("count", String(describing: count))])
|
||||
case .reactionCount(let flags, let chosenOrder, let reaction, let count):
|
||||
return ("reactionCount", [("flags", String(describing: flags)), ("chosenOrder", String(describing: chosenOrder)), ("reaction", String(describing: reaction)), ("count", String(describing: count))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_reactionCount(_ reader: BufferReader) -> ReactionCount? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Reaction?
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
var _3: Api.Reaction?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.ReactionCount.reactionCount(flags: _1!, reaction: _2!, count: _3!)
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.ReactionCount.reactionCount(flags: _1!, chosenOrder: _2, reaction: _3!, count: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -644,10 +644,11 @@ public extension Api {
|
||||
case updateSavedGifs
|
||||
case updateSavedRingtones
|
||||
case updateServiceNotification(flags: Int32, inboxDate: Int32?, type: String, message: String, media: Api.MessageMedia, entities: [Api.MessageEntity])
|
||||
case updateStickerSets
|
||||
case updateStickerSets(flags: Int32)
|
||||
case updateStickerSetsOrder(flags: Int32, order: [Int64])
|
||||
case updateTheme(theme: Api.Theme)
|
||||
case updateTranscribedAudio(flags: Int32, peer: Api.Peer, msgId: Int32, transcriptionId: Int64, text: String)
|
||||
case updateUserEmojiStatus(userId: Int64, emojiStatus: Api.EmojiStatus)
|
||||
case updateUserName(userId: Int64, firstName: String, lastName: String, username: String)
|
||||
case updateUserPhone(userId: Int64, phone: String)
|
||||
case updateUserPhoto(userId: Int64, date: Int32, photo: Api.UserProfilePhoto, previous: Api.Bool)
|
||||
@ -1461,11 +1462,11 @@ public extension Api {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .updateStickerSets:
|
||||
case .updateStickerSets(let flags):
|
||||
if boxed {
|
||||
buffer.appendInt32(1135492588)
|
||||
buffer.appendInt32(834816008)
|
||||
}
|
||||
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateStickerSetsOrder(let flags, let order):
|
||||
if boxed {
|
||||
@ -1494,6 +1495,13 @@ public extension Api {
|
||||
serializeInt64(transcriptionId, buffer: buffer, boxed: false)
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateUserEmojiStatus(let userId, let emojiStatus):
|
||||
if boxed {
|
||||
buffer.appendInt32(674706841)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
emojiStatus.serialize(buffer, true)
|
||||
break
|
||||
case .updateUserName(let userId, let firstName, let lastName, let username):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1007549728)
|
||||
@ -1736,14 +1744,16 @@ public extension Api {
|
||||
return ("updateSavedRingtones", [])
|
||||
case .updateServiceNotification(let flags, let inboxDate, let type, let message, let media, let entities):
|
||||
return ("updateServiceNotification", [("flags", String(describing: flags)), ("inboxDate", String(describing: inboxDate)), ("type", String(describing: type)), ("message", String(describing: message)), ("media", String(describing: media)), ("entities", String(describing: entities))])
|
||||
case .updateStickerSets:
|
||||
return ("updateStickerSets", [])
|
||||
case .updateStickerSets(let flags):
|
||||
return ("updateStickerSets", [("flags", String(describing: flags))])
|
||||
case .updateStickerSetsOrder(let flags, let order):
|
||||
return ("updateStickerSetsOrder", [("flags", String(describing: flags)), ("order", String(describing: order))])
|
||||
case .updateTheme(let theme):
|
||||
return ("updateTheme", [("theme", String(describing: theme))])
|
||||
case .updateTranscribedAudio(let flags, let peer, let msgId, let transcriptionId, let text):
|
||||
return ("updateTranscribedAudio", [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("transcriptionId", String(describing: transcriptionId)), ("text", String(describing: text))])
|
||||
case .updateUserEmojiStatus(let userId, let emojiStatus):
|
||||
return ("updateUserEmojiStatus", [("userId", String(describing: userId)), ("emojiStatus", String(describing: emojiStatus))])
|
||||
case .updateUserName(let userId, let firstName, let lastName, let username):
|
||||
return ("updateUserName", [("userId", String(describing: userId)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("username", String(describing: username))])
|
||||
case .updateUserPhone(let userId, let phone):
|
||||
@ -3377,7 +3387,15 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public static func parse_updateStickerSets(_ reader: BufferReader) -> Update? {
|
||||
return Api.Update.updateStickerSets
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.Update.updateStickerSets(flags: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateStickerSetsOrder(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
@ -3433,6 +3451,22 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateUserEmojiStatus(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Api.EmojiStatus?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.EmojiStatus
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.Update.updateUserEmojiStatus(userId: _1!, emojiStatus: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateUserName(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
|
@ -5726,13 +5726,17 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: Api.Reaction?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: [Api.Reaction]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1526634933)
|
||||
buffer.appendInt32(-754091820)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {reaction!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(reaction!.count))
|
||||
for item in reaction! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
return (FunctionDescription(name: "messages.sendReaction", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("reaction", String(describing: reaction))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
@ -6466,23 +6470,6 @@ public extension Api.functions.payments {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func requestRecurringPayment(userId: Api.InputUser, recurringInitCharge: String, invoiceMedia: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(342791565)
|
||||
userId.serialize(buffer, true)
|
||||
serializeString(recurringInitCharge, buffer: buffer, boxed: false)
|
||||
invoiceMedia.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "payments.requestRecurringPayment", parameters: [("userId", String(describing: userId)), ("recurringInitCharge", String(describing: recurringInitCharge)), ("invoiceMedia", String(describing: invoiceMedia))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func sendPaymentForm(flags: Int32, formId: Int64, invoice: Api.InputInvoice, requestedInfoId: String?, shippingOptionId: String?, credentials: Api.InputPaymentCredentials, tipAmount: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.PaymentResult>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -10,9 +10,9 @@ extension ReactionsMessageAttribute {
|
||||
let canViewList = (flags & (1 << 2)) != 0
|
||||
var reactions = results.compactMap { result -> MessageReaction? in
|
||||
switch result {
|
||||
case let .reactionCount(flags, reaction, count):
|
||||
case let .reactionCount(_, chosenOrder, reaction, count):
|
||||
if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
|
||||
return MessageReaction(value: reaction, count: count, isSelected: (flags & (1 << 0)) != 0)
|
||||
return MessageReaction(value: reaction, count: count, chosenOrder: chosenOrder.flatMap(Int.init))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -37,17 +37,17 @@ extension ReactionsMessageAttribute {
|
||||
}
|
||||
|
||||
if min {
|
||||
var currentSelectedReaction: MessageReaction.Reaction?
|
||||
var currentSelectedReactions: [MessageReaction.Reaction: Int] = [:]
|
||||
for reaction in self.reactions {
|
||||
if reaction.isSelected {
|
||||
currentSelectedReaction = reaction.value
|
||||
if let chosenOrder = reaction.chosenOrder {
|
||||
currentSelectedReactions[reaction.value] = chosenOrder
|
||||
break
|
||||
}
|
||||
}
|
||||
if let currentSelectedReaction = currentSelectedReaction {
|
||||
if !currentSelectedReactions.isEmpty {
|
||||
for i in 0 ..< reactions.count {
|
||||
if reactions[i].value == currentSelectedReaction {
|
||||
reactions[i].isSelected = true
|
||||
if let chosenOrder = currentSelectedReactions[reactions[i].value] {
|
||||
reactions[i].chosenOrder = chosenOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,6 +76,52 @@ public func mergedMessageReactionsAndPeers(message: Message) -> (reactions: [Mes
|
||||
return (attribute.reactions, recentPeers)
|
||||
}
|
||||
|
||||
private func mergeReactions(reactions: [MessageReaction], recentPeers: [ReactionsMessageAttribute.RecentPeer], pending: [PendingReactionsMessageAttribute.PendingReaction], accountPeerId: PeerId) -> ([MessageReaction], [ReactionsMessageAttribute.RecentPeer]) {
|
||||
var result = reactions
|
||||
var recentPeers = recentPeers
|
||||
|
||||
for pendingReaction in pending {
|
||||
if let index = result.firstIndex(where: { $0.value == pendingReaction.value }) {
|
||||
var merged = result[index]
|
||||
if merged.chosenOrder == nil {
|
||||
merged.chosenOrder = Int(Int32.max)
|
||||
merged.count += 1
|
||||
}
|
||||
result[index] = merged
|
||||
} else {
|
||||
result.append(MessageReaction(value: pendingReaction.value, count: 1, chosenOrder: Int(Int32.max)))
|
||||
}
|
||||
|
||||
if let index = recentPeers.firstIndex(where: { $0.value == pendingReaction.value && $0.peerId == accountPeerId }) {
|
||||
recentPeers.remove(at: index)
|
||||
}
|
||||
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: pendingReaction.value, isLarge: false, isUnseen: false, peerId: accountPeerId))
|
||||
}
|
||||
|
||||
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 }) {
|
||||
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]) -> ReactionsMessageAttribute? {
|
||||
var current: ReactionsMessageAttribute?
|
||||
var pending: PendingReactionsMessageAttribute?
|
||||
@ -87,45 +133,14 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM
|
||||
}
|
||||
}
|
||||
|
||||
if let pending = pending {
|
||||
if let pending = pending, let accountPeerId = pending.accountPeerId {
|
||||
var reactions = current?.reactions ?? []
|
||||
var recentPeers = current?.recentPeers ?? []
|
||||
if let value = pending.value {
|
||||
var found = false
|
||||
for i in 0 ..< reactions.count {
|
||||
if reactions[i].value == value {
|
||||
found = true
|
||||
if !reactions[i].isSelected {
|
||||
reactions[i].isSelected = true
|
||||
reactions[i].count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
reactions.append(MessageReaction(value: value, count: 1, isSelected: true))
|
||||
}
|
||||
}
|
||||
if let accountPeerId = pending.accountPeerId {
|
||||
for i in 0 ..< recentPeers.count {
|
||||
if recentPeers[i].peerId == accountPeerId {
|
||||
recentPeers.remove(at: i)
|
||||
break
|
||||
}
|
||||
}
|
||||
if let value = pending.value {
|
||||
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: value, isLarge: false, isUnseen: false, peerId: accountPeerId))
|
||||
}
|
||||
}
|
||||
for i in (0 ..< reactions.count).reversed() {
|
||||
if reactions[i].isSelected, pending.value != reactions[i].value {
|
||||
if reactions[i].count == 1 {
|
||||
reactions.remove(at: i)
|
||||
} else {
|
||||
reactions[i].isSelected = false
|
||||
reactions[i].count -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, reactions: reactions, recentPeers: recentPeers)
|
||||
} else {
|
||||
@ -165,9 +180,9 @@ extension ReactionsMessageAttribute {
|
||||
canViewList: canViewList,
|
||||
reactions: results.compactMap { result -> MessageReaction? in
|
||||
switch result {
|
||||
case let .reactionCount(flags, reaction, count):
|
||||
case let .reactionCount(_, chosenOrder, reaction, count):
|
||||
if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
|
||||
return MessageReaction(value: reaction, count: count, isSelected: (flags & (1 << 0)) != 0)
|
||||
return MessageReaction(value: reaction, count: count, chosenOrder: chosenOrder.flatMap(Int.init))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -14,10 +14,10 @@ private func reactionGeneratedEvent(_ previousReactions: ReactionsMessageAttribu
|
||||
})
|
||||
}
|
||||
let myUpdated = updatedReactions.reactions.filter { value in
|
||||
return value.isSelected
|
||||
return value.chosenOrder != nil
|
||||
}.first
|
||||
let myPrevious = prev.filter { value in
|
||||
return value.isSelected
|
||||
return value.chosenOrder != nil
|
||||
}.first
|
||||
|
||||
let previousCount = prev.reduce(0, {
|
||||
@ -28,7 +28,7 @@ private func reactionGeneratedEvent(_ previousReactions: ReactionsMessageAttribu
|
||||
})
|
||||
|
||||
let newReaction = updated.filter {
|
||||
!$0.isSelected
|
||||
$0.chosenOrder == nil
|
||||
}.first?.value
|
||||
|
||||
if !updated.isEmpty && myUpdated == myPrevious, updatedCount >= previousCount, let value = newReaction {
|
||||
|
@ -18,20 +18,29 @@ public enum UpdateMessageReaction {
|
||||
}
|
||||
}
|
||||
|
||||
public func updateMessageReactionsInteractively(account: Account, messageId: MessageId, reaction: UpdateMessageReaction?, isLarge: Bool) -> Signal<Never, NoError> {
|
||||
public func updateMessageReactionsInteractively(account: Account, messageId: MessageId, reactions: [UpdateMessageReaction], isLarge: Bool) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
let mappedReaction: MessageReaction.Reaction?
|
||||
let isPremium = (transaction.getPeer(account.peerId) as? TelegramUser)?.isPremium ?? false
|
||||
let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue
|
||||
let maxCount: Int
|
||||
if isPremium {
|
||||
let limitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: isPremium)
|
||||
maxCount = Int(limitsConfiguration.maxReactionsPerMessage)
|
||||
} else {
|
||||
maxCount = 1
|
||||
}
|
||||
|
||||
switch reaction {
|
||||
case .none:
|
||||
mappedReaction = nil
|
||||
case let .custom(fileId, file):
|
||||
mappedReaction = .custom(fileId)
|
||||
if let file = file {
|
||||
transaction.storeMediaIfNotPresent(media: file)
|
||||
var mappedReactions: [PendingReactionsMessageAttribute.PendingReaction] = []
|
||||
for reaction in reactions {
|
||||
switch reaction {
|
||||
case let .custom(fileId, file):
|
||||
mappedReactions.append(PendingReactionsMessageAttribute.PendingReaction(value: .custom(fileId)))
|
||||
if let file = file {
|
||||
transaction.storeMediaIfNotPresent(media: file)
|
||||
}
|
||||
case let .builtin(value):
|
||||
mappedReactions.append(PendingReactionsMessageAttribute.PendingReaction(value: .builtin(value)))
|
||||
}
|
||||
case let .builtin(value):
|
||||
mappedReaction = .builtin(value)
|
||||
}
|
||||
|
||||
transaction.setPendingMessageAction(type: .updateReaction, id: messageId, action: UpdateMessageReactionsAction())
|
||||
@ -47,7 +56,20 @@ public func updateMessageReactionsInteractively(account: Account, messageId: Mes
|
||||
break loop
|
||||
}
|
||||
}
|
||||
attributes.append(PendingReactionsMessageAttribute(accountPeerId: account.peerId, value: mappedReaction, isLarge: isLarge))
|
||||
|
||||
var mappedReactions = mappedReactions
|
||||
|
||||
let updatedReactions = mergedMessageReactions(attributes: attributes + [PendingReactionsMessageAttribute(accountPeerId: account.peerId, reactions: mappedReactions, isLarge: isLarge)])?.reactions ?? []
|
||||
let updatedOutgoingReactions = updatedReactions.filter(\.isSelected)
|
||||
if updatedOutgoingReactions.count > maxCount {
|
||||
let sortedOutgoingReactions = updatedOutgoingReactions.sorted(by: { $0.chosenOrder! < $1.chosenOrder! })
|
||||
mappedReactions = Array(sortedOutgoingReactions.suffix(maxCount).map { reaction -> PendingReactionsMessageAttribute.PendingReaction in
|
||||
return PendingReactionsMessageAttribute.PendingReaction(value: reaction.value)
|
||||
})
|
||||
}
|
||||
|
||||
attributes.append(PendingReactionsMessageAttribute(accountPeerId: account.peerId, reactions: mappedReactions, isLarge: isLarge))
|
||||
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
@ -59,27 +81,29 @@ private enum RequestUpdateMessageReactionError {
|
||||
}
|
||||
|
||||
private func requestUpdateMessageReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal<Never, RequestUpdateMessageReactionError> {
|
||||
return postbox.transaction { transaction -> (Peer, MessageReaction.Reaction?, Bool)? in
|
||||
return postbox.transaction { transaction -> (Peer, [MessageReaction.Reaction]?, Bool)? in
|
||||
guard let peer = transaction.getPeer(messageId.peerId) else {
|
||||
return nil
|
||||
}
|
||||
guard let message = transaction.getMessage(messageId) else {
|
||||
return nil
|
||||
}
|
||||
var value: MessageReaction.Reaction?
|
||||
var reactions: [MessageReaction.Reaction]?
|
||||
var isLarge: Bool = false
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? PendingReactionsMessageAttribute {
|
||||
value = attribute.value
|
||||
if !attribute.reactions.isEmpty {
|
||||
reactions = attribute.reactions.map(\.value)
|
||||
}
|
||||
isLarge = attribute.isLarge
|
||||
break
|
||||
}
|
||||
}
|
||||
return (peer, value, isLarge)
|
||||
return (peer, reactions, isLarge)
|
||||
}
|
||||
|> castError(RequestUpdateMessageReactionError.self)
|
||||
|> mapToSignal { peerAndValue in
|
||||
guard let (peer, value, isLarge) = peerAndValue else {
|
||||
guard let (peer, reactions, isLarge) = peerAndValue else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
guard let inputPeer = apiInputPeer(peer) else {
|
||||
@ -90,14 +114,14 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
if value != nil {
|
||||
if reactions != nil {
|
||||
flags |= 1 << 0
|
||||
if isLarge {
|
||||
flags |= 1 << 1
|
||||
}
|
||||
}
|
||||
|
||||
let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendReaction(flags: flags, peer: inputPeer, msgId: messageId.id, reaction: value?.apiReaction))
|
||||
let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendReaction(flags: flags, peer: inputPeer, msgId: messageId.id, reaction: reactions?.map(\.apiReaction)))
|
||||
|> mapError { _ -> RequestUpdateMessageReactionError in
|
||||
return .generic
|
||||
}
|
||||
@ -276,7 +300,7 @@ public extension EngineMessageReactionListContext.State {
|
||||
if let reactionsAttribute = message._asMessage().reactionsAttribute {
|
||||
for messageReaction in reactionsAttribute.reactions {
|
||||
if reaction == nil || messageReaction.value == reaction {
|
||||
if messageReaction.isSelected {
|
||||
if messageReaction.chosenOrder != nil {
|
||||
hasOutgoingReaction = true
|
||||
}
|
||||
totalCount += Int(messageReaction.count)
|
||||
|
@ -13,6 +13,7 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
public let maxUploadFileParts: Int32
|
||||
public let maxAboutLength: Int32
|
||||
public let maxAnimatedEmojisInText: Int32
|
||||
public let maxReactionsPerMessage: Int32
|
||||
|
||||
public static var defaultValue: UserLimitsConfiguration {
|
||||
return UserLimitsConfiguration(
|
||||
@ -26,7 +27,8 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
maxCaptionLength: 1024,
|
||||
maxUploadFileParts: 4000,
|
||||
maxAboutLength: 70,
|
||||
maxAnimatedEmojisInText: 10
|
||||
maxAnimatedEmojisInText: 10,
|
||||
maxReactionsPerMessage: 1
|
||||
)
|
||||
}
|
||||
|
||||
@ -41,7 +43,8 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
maxCaptionLength: Int32,
|
||||
maxUploadFileParts: Int32,
|
||||
maxAboutLength: Int32,
|
||||
maxAnimatedEmojisInText: Int32
|
||||
maxAnimatedEmojisInText: Int32,
|
||||
maxReactionsPerMessage: Int32
|
||||
) {
|
||||
self.maxPinnedChatCount = maxPinnedChatCount
|
||||
self.maxChannelsCount = maxChannelsCount
|
||||
@ -54,6 +57,7 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
self.maxUploadFileParts = maxUploadFileParts
|
||||
self.maxAboutLength = maxAboutLength
|
||||
self.maxAnimatedEmojisInText = maxAnimatedEmojisInText
|
||||
self.maxReactionsPerMessage = maxReactionsPerMessage
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,5 +93,6 @@ extension UserLimitsConfiguration {
|
||||
self.maxUploadFileParts = getValue("upload_max_fileparts", orElse: defaultValue.maxUploadFileParts)
|
||||
self.maxAboutLength = getValue("about_length_limit", orElse: defaultValue.maxAboutLength)
|
||||
self.maxAnimatedEmojisInText = getGeneralValue("message_animated_emoji_max", orElse: defaultValue.maxAnimatedEmojisInText)
|
||||
self.maxReactionsPerMessage = getGeneralValue("reactions_user_max", orElse: 1)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import Postbox
|
||||
import TelegramApi
|
||||
|
||||
public struct MessageReaction: Equatable, PostboxCoding {
|
||||
public enum Reaction: Hashable, Codable {
|
||||
public enum Reaction: Hashable, Codable, PostboxCoding {
|
||||
case builtin(String)
|
||||
case custom(Int64)
|
||||
|
||||
@ -16,6 +16,14 @@ public struct MessageReaction: Equatable, PostboxCoding {
|
||||
}
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
if let value = decoder.decodeOptionalStringForKey("v") {
|
||||
self = .builtin(value)
|
||||
} else {
|
||||
self = .custom(decoder.decodeInt64ForKey("cfid", orElse: 0))
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
@ -26,16 +34,29 @@ public struct MessageReaction: Equatable, PostboxCoding {
|
||||
try container.encode(fileId, forKey: "cfid")
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .builtin(value):
|
||||
encoder.encodeString(value, forKey: "v")
|
||||
case let .custom(fileId):
|
||||
encoder.encodeInt64(fileId, forKey: "cfid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var value: Reaction
|
||||
public var count: Int32
|
||||
public var isSelected: Bool
|
||||
public var chosenOrder: Int?
|
||||
|
||||
public init(value: Reaction, count: Int32, isSelected: Bool) {
|
||||
public var isSelected: Bool {
|
||||
return self.chosenOrder != nil
|
||||
}
|
||||
|
||||
public init(value: Reaction, count: Int32, chosenOrder: Int?) {
|
||||
self.value = value
|
||||
self.count = count
|
||||
self.isSelected = isSelected
|
||||
self.chosenOrder = chosenOrder
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -45,7 +66,13 @@ public struct MessageReaction: Equatable, PostboxCoding {
|
||||
self.value = .custom(decoder.decodeInt64ForKey("cfid", orElse: 0))
|
||||
}
|
||||
self.count = decoder.decodeInt32ForKey("c", orElse: 0)
|
||||
self.isSelected = decoder.decodeInt32ForKey("s", orElse: 0) != 0
|
||||
if let chosenOrder = decoder.decodeOptionalInt32ForKey("cord") {
|
||||
self.chosenOrder = Int(chosenOrder)
|
||||
} else if let isSelected = decoder.decodeOptionalInt32ForKey("s"), isSelected != 0 {
|
||||
self.chosenOrder = 0
|
||||
} else {
|
||||
self.chosenOrder = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -56,7 +83,11 @@ public struct MessageReaction: Equatable, PostboxCoding {
|
||||
encoder.encodeInt64(fileId, forKey: "cfid")
|
||||
}
|
||||
encoder.encodeInt32(self.count, forKey: "c")
|
||||
encoder.encodeInt32(self.isSelected ? 1 : 0, forKey: "s")
|
||||
if let chosenOrder = self.chosenOrder {
|
||||
encoder.encodeInt32(Int32(chosenOrder), forKey: "cord")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "cord")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,8 +213,24 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
||||
}
|
||||
|
||||
public final class PendingReactionsMessageAttribute: MessageAttribute {
|
||||
public struct PendingReaction: Equatable, PostboxCoding {
|
||||
public var value: MessageReaction.Reaction
|
||||
|
||||
public init(value: MessageReaction.Reaction) {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.value = decoder.decodeObjectForKey("val", decoder: { MessageReaction.Reaction(decoder: $0) }) as! MessageReaction.Reaction
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeObject(self.value, forKey: "val")
|
||||
}
|
||||
}
|
||||
|
||||
public let accountPeerId: PeerId?
|
||||
public let value: MessageReaction.Reaction?
|
||||
public let reactions: [PendingReaction]
|
||||
public let isLarge: Bool
|
||||
|
||||
public var associatedPeerIds: [PeerId] {
|
||||
@ -194,21 +241,15 @@ public final class PendingReactionsMessageAttribute: MessageAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
public init(accountPeerId: PeerId?, value: MessageReaction.Reaction?, isLarge: Bool) {
|
||||
public init(accountPeerId: PeerId?, reactions: [PendingReaction], isLarge: Bool) {
|
||||
self.accountPeerId = accountPeerId
|
||||
self.value = value
|
||||
self.reactions = reactions
|
||||
self.isLarge = isLarge
|
||||
}
|
||||
|
||||
required public init(decoder: PostboxDecoder) {
|
||||
self.accountPeerId = decoder.decodeOptionalInt64ForKey("ap").flatMap(PeerId.init)
|
||||
if let value = decoder.decodeOptionalStringForKey("v") {
|
||||
self.value = .builtin(value)
|
||||
} else if let fileId = decoder.decodeOptionalInt64ForKey("cfid") {
|
||||
self.value = .custom(fileId)
|
||||
} else {
|
||||
self.value = nil
|
||||
}
|
||||
self.reactions = decoder.decodeObjectArrayWithDecoderForKey("reac")
|
||||
self.isLarge = decoder.decodeInt32ForKey("l", orElse: 0) != 0
|
||||
}
|
||||
|
||||
@ -218,16 +259,9 @@ public final class PendingReactionsMessageAttribute: MessageAttribute {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "ap")
|
||||
}
|
||||
if let value = self.value {
|
||||
switch value {
|
||||
case let .builtin(value):
|
||||
encoder.encodeString(value, forKey: "v")
|
||||
case let .custom(fileId):
|
||||
encoder.encodeInt64(fileId, forKey: "cfid")
|
||||
}
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "v")
|
||||
}
|
||||
|
||||
encoder.encodeObjectArray(self.reactions, forKey: "reac")
|
||||
|
||||
encoder.encodeInt32(self.isLarge ? 1 : 0, forKey: "l")
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ public enum EngineConfiguration {
|
||||
public let maxUploadFileParts: Int32
|
||||
public let maxAboutLength: Int32
|
||||
public let maxAnimatedEmojisInText: Int32
|
||||
public let maxReactionsPerMessage: Int32
|
||||
|
||||
public static var defaultValue: UserLimits {
|
||||
return UserLimits(UserLimitsConfiguration.defaultValue)
|
||||
@ -78,7 +79,8 @@ public enum EngineConfiguration {
|
||||
maxCaptionLength: Int32,
|
||||
maxUploadFileParts: Int32,
|
||||
maxAboutLength: Int32,
|
||||
maxAnimatedEmojisInText: Int32
|
||||
maxAnimatedEmojisInText: Int32,
|
||||
maxReactionsPerMessage: Int32
|
||||
) {
|
||||
self.maxPinnedChatCount = maxPinnedChatCount
|
||||
self.maxChannelsCount = maxChannelsCount
|
||||
@ -91,6 +93,7 @@ public enum EngineConfiguration {
|
||||
self.maxUploadFileParts = maxUploadFileParts
|
||||
self.maxAboutLength = maxAboutLength
|
||||
self.maxAnimatedEmojisInText = maxAnimatedEmojisInText
|
||||
self.maxReactionsPerMessage = maxReactionsPerMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,7 +151,8 @@ public extension EngineConfiguration.UserLimits {
|
||||
maxCaptionLength: userLimitsConfiguration.maxCaptionLength,
|
||||
maxUploadFileParts: userLimitsConfiguration.maxUploadFileParts,
|
||||
maxAboutLength: userLimitsConfiguration.maxAboutLength,
|
||||
maxAnimatedEmojisInText: userLimitsConfiguration.maxAnimatedEmojisInText
|
||||
maxAnimatedEmojisInText: userLimitsConfiguration.maxAnimatedEmojisInText,
|
||||
maxReactionsPerMessage: userLimitsConfiguration.maxReactionsPerMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ public extension Message {
|
||||
}
|
||||
for attribute in self.attributes {
|
||||
if let attribute = attribute as? PendingReactionsMessageAttribute {
|
||||
return attribute.value != nil
|
||||
return !attribute.reactions.isEmpty
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -1308,7 +1308,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
|
||||
controller.reactionSelected = { [weak controller] reaction, isLarge in
|
||||
controller.reactionSelected = { [weak controller] chosenUpdatedReaction, isLarge in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -1317,7 +1317,97 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
var updatedReaction: UpdateMessageReaction? = reaction
|
||||
let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction
|
||||
|
||||
let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? []
|
||||
var updatedReactions: [MessageReaction.Reaction] = currentReactions.filter(\.isSelected).map(\.value)
|
||||
var removedReaction: MessageReaction.Reaction?
|
||||
var isFirst = false
|
||||
|
||||
if let index = updatedReactions.firstIndex(where: { $0 == chosenReaction }) {
|
||||
removedReaction = chosenReaction
|
||||
updatedReactions.remove(at: index)
|
||||
} else {
|
||||
updatedReactions.append(chosenReaction)
|
||||
isFirst = !currentReactions.contains(where: { $0.value == chosenReaction })
|
||||
}
|
||||
|
||||
/*guard let allowedReactions = allowedReactions else {
|
||||
itemNode.openMessageContextMenu()
|
||||
return
|
||||
}
|
||||
|
||||
switch allowedReactions {
|
||||
case let .set(set):
|
||||
if !messageAlreadyHasThisReaction && updatedReactions.contains(where: { !set.contains($0) }) {
|
||||
itemNode.openMessageContextMenu()
|
||||
return
|
||||
}
|
||||
case .all:
|
||||
break
|
||||
}*/
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item {
|
||||
if item.message.id == message.id {
|
||||
if removedReaction == nil && !updatedReactions.isEmpty {
|
||||
itemNode.awaitingAppliedReaction = (chosenReaction, { [weak itemNode] in
|
||||
guard let controller = controller else {
|
||||
return
|
||||
}
|
||||
if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction) {
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addMessageContextController(messageId: item.message.id, contextController: controller)
|
||||
|
||||
var hideTargetButton: UIView?
|
||||
if isFirst {
|
||||
hideTargetButton = targetView.superview
|
||||
}
|
||||
|
||||
controller.dismissWithReaction(value: chosenReaction, targetView: targetView, hideNode: true, animateTargetContainer: hideTargetButton, addStandaloneReactionAnimation: { standaloneReactionAnimation in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
||||
standaloneReactionAnimation.frame = strongSelf.chatDisplayNode.bounds
|
||||
strongSelf.chatDisplayNode.addSubnode(standaloneReactionAnimation)
|
||||
}, completion: { [weak itemNode, weak targetView] in
|
||||
guard let strongSelf = self, let itemNode = itemNode, let targetView = targetView else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = strongSelf
|
||||
let _ = itemNode
|
||||
let _ = targetView
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
itemNode.awaitingAppliedReaction = (nil, {
|
||||
controller?.dismiss()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mappedUpdatedReactions = updatedReactions.map { reaction -> UpdateMessageReaction in
|
||||
switch reaction {
|
||||
case let .builtin(value):
|
||||
return .builtin(value)
|
||||
case let .custom(fileId):
|
||||
var customFile: TelegramMediaFile?
|
||||
if case let .custom(customFileId, file) = chosenUpdatedReaction, fileId == customFileId {
|
||||
customFile = file
|
||||
}
|
||||
return .custom(fileId: fileId, file: customFile)
|
||||
}
|
||||
}
|
||||
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reactions: mappedUpdatedReactions, isLarge: isLarge).start()
|
||||
|
||||
/*let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? []
|
||||
var updatedReactions: [MessageReaction.Reaction] = currentReactions.filter(\.isSelected).map(\.value)
|
||||
|
||||
var isFirst = true
|
||||
for attribute in topMessage.attributes {
|
||||
if let attribute = attribute as? ReactionsMessageAttribute {
|
||||
@ -1336,49 +1426,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item {
|
||||
if item.message.id == message.id {
|
||||
if let updatedReaction = updatedReaction {
|
||||
itemNode.awaitingAppliedReaction = (updatedReaction.reaction, { [weak itemNode] in
|
||||
guard let controller = controller else {
|
||||
return
|
||||
}
|
||||
if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: updatedReaction.reaction) {
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addMessageContextController(messageId: item.message.id, contextController: controller)
|
||||
|
||||
var hideTargetButton: UIView?
|
||||
if isFirst {
|
||||
hideTargetButton = targetView.superview
|
||||
}
|
||||
|
||||
controller.dismissWithReaction(value: updatedReaction.reaction, targetView: targetView, hideNode: true, animateTargetContainer: hideTargetButton, addStandaloneReactionAnimation: { standaloneReactionAnimation in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
||||
standaloneReactionAnimation.frame = strongSelf.chatDisplayNode.bounds
|
||||
strongSelf.chatDisplayNode.addSubnode(standaloneReactionAnimation)
|
||||
}, completion: { [weak itemNode, weak targetView] in
|
||||
guard let strongSelf = self, let itemNode = itemNode, let targetView = targetView else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = strongSelf
|
||||
let _ = itemNode
|
||||
let _ = targetView
|
||||
})
|
||||
}
|
||||
})
|
||||
} else if updatedReaction == nil {
|
||||
itemNode.awaitingAppliedReaction = (nil, {
|
||||
controller?.dismiss()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction, isLarge: isLarge).start()
|
||||
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction, isLarge: isLarge).start()*/
|
||||
}
|
||||
|
||||
strongSelf.forEachController({ controller in
|
||||
@ -1461,92 +1510,71 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
var updatedReaction: UpdateMessageReaction?
|
||||
let chosenReaction: MessageReaction.Reaction?
|
||||
|
||||
switch reaction {
|
||||
case .default:
|
||||
switch item.associatedData.defaultReaction {
|
||||
case .none:
|
||||
updatedReaction = nil
|
||||
chosenReaction = nil
|
||||
case let .builtin(value):
|
||||
updatedReaction = .builtin(value)
|
||||
chosenReaction = .builtin(value)
|
||||
case let .custom(fileId):
|
||||
updatedReaction = .custom(fileId: fileId, file: nil)
|
||||
chosenReaction = .custom(fileId)
|
||||
}
|
||||
case let .reaction(value):
|
||||
switch value {
|
||||
case let .builtin(value):
|
||||
updatedReaction = .builtin(value)
|
||||
chosenReaction = .builtin(value)
|
||||
case let .custom(fileId):
|
||||
updatedReaction = .custom(fileId: fileId, file: nil)
|
||||
chosenReaction = .custom(fileId)
|
||||
}
|
||||
}
|
||||
|
||||
guard let chosenReaction = chosenReaction else {
|
||||
return
|
||||
}
|
||||
|
||||
var removedReaction: MessageReaction.Reaction?
|
||||
var messageAlreadyHasThisReaction = false
|
||||
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReactionsMessageAttribute {
|
||||
for listReaction in attribute.reactions {
|
||||
switch reaction {
|
||||
case .default:
|
||||
if listReaction.isSelected {
|
||||
updatedReaction = nil
|
||||
removedReaction = listReaction.value
|
||||
} else if listReaction.value == updatedReaction?.reaction {
|
||||
messageAlreadyHasThisReaction = true
|
||||
}
|
||||
case let .reaction(value):
|
||||
if listReaction.value == value {
|
||||
messageAlreadyHasThisReaction = true
|
||||
|
||||
if listReaction.isSelected {
|
||||
updatedReaction = nil
|
||||
removedReaction = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let attribute = attribute as? PendingReactionsMessageAttribute {
|
||||
if attribute.value != nil {
|
||||
switch reaction {
|
||||
case .default:
|
||||
updatedReaction = nil
|
||||
removedReaction = attribute.value
|
||||
case let .reaction(value):
|
||||
if attribute.value == value {
|
||||
updatedReaction = nil
|
||||
removedReaction = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let currentReactions = mergedMessageReactions(attributes: message.attributes)?.reactions ?? []
|
||||
var updatedReactions: [MessageReaction.Reaction] = currentReactions.filter(\.isSelected).map(\.value)
|
||||
|
||||
if let index = updatedReactions.firstIndex(where: { $0 == chosenReaction }) {
|
||||
removedReaction = chosenReaction
|
||||
updatedReactions.remove(at: index)
|
||||
} else {
|
||||
updatedReactions.append(chosenReaction)
|
||||
messageAlreadyHasThisReaction = currentReactions.contains(where: { $0.value == chosenReaction })
|
||||
}
|
||||
|
||||
if let updatedReaction = updatedReaction {
|
||||
guard let allowedReactions = allowedReactions else {
|
||||
guard let allowedReactions = allowedReactions else {
|
||||
itemNode.openMessageContextMenu()
|
||||
return
|
||||
}
|
||||
|
||||
switch allowedReactions {
|
||||
case let .set(set):
|
||||
if !messageAlreadyHasThisReaction && updatedReactions.contains(where: { !set.contains($0) }) {
|
||||
itemNode.openMessageContextMenu()
|
||||
return
|
||||
}
|
||||
switch allowedReactions {
|
||||
case let .set(set):
|
||||
if !messageAlreadyHasThisReaction && !set.contains(updatedReaction.reaction) {
|
||||
itemNode.openMessageContextMenu()
|
||||
return
|
||||
}
|
||||
case .all:
|
||||
break
|
||||
}
|
||||
|
||||
case .all:
|
||||
break
|
||||
}
|
||||
|
||||
if removedReaction == nil && !updatedReactions.isEmpty {
|
||||
if strongSelf.selectPollOptionFeedback == nil {
|
||||
strongSelf.selectPollOptionFeedback = HapticFeedback()
|
||||
}
|
||||
strongSelf.selectPollOptionFeedback?.tap()
|
||||
|
||||
itemNode.awaitingAppliedReaction = (updatedReaction.reaction, { [weak itemNode] in
|
||||
itemNode.awaitingAppliedReaction = (chosenReaction, { [weak itemNode] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let itemNode = itemNode, let item = itemNode.item, let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: updatedReaction.reaction) {
|
||||
if let itemNode = itemNode, let item = itemNode.item, let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: chosenReaction) {
|
||||
for reaction in availableReactions.reactions {
|
||||
guard let centerAnimation = reaction.centerAnimation else {
|
||||
continue
|
||||
@ -1555,7 +1583,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
continue
|
||||
}
|
||||
|
||||
if reaction.value == updatedReaction.reaction {
|
||||
if reaction.value == chosenReaction {
|
||||
let standaloneReactionAnimation = StandaloneReactionAnimation()
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
||||
@ -1620,7 +1648,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction, isLarge: false).start()
|
||||
let mappedUpdatedReactions = updatedReactions.map { reaction -> UpdateMessageReaction in
|
||||
switch reaction {
|
||||
case let .builtin(value):
|
||||
return .builtin(value)
|
||||
case let .custom(fileId):
|
||||
return .custom(fileId: fileId, file: nil)
|
||||
}
|
||||
}
|
||||
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reactions: mappedUpdatedReactions, isLarge: false).start()
|
||||
}
|
||||
})
|
||||
}, activateMessagePinch: { [weak self] sourceNode in
|
||||
|
Loading…
x
Reference in New Issue
Block a user