diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index 538e4ea2cf..0f26d0e7b8 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -1156,6 +1156,11 @@ final class ContextControllerActionsStackNode: ASDisplayNode { let animateAppearingContainers = transition.isAnimated && !self.dismissingItemContainers.isEmpty + struct TipLayout { + var tipNode: ASDisplayNode + var tipHeight: CGFloat + } + struct ItemLayout { var size: CGSize var apparentHeight: CGFloat @@ -1163,6 +1168,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { var alphaTransitionFraction: CGFloat var itemTransition: ContainedViewLayoutTransition var animateAppearingContainer: Bool + var tip: TipLayout? } var topItemSize = CGSize() @@ -1204,13 +1210,19 @@ final class ContextControllerActionsStackNode: ASDisplayNode { topItemSize = itemSize.size } + var tip: TipLayout? + if let (tipNode, tipHeight) = itemContainer.updateTip(presentationData: presentationData, width: itemSize.size.width, transition: itemContainerTransition) { + tip = TipLayout(tipNode: tipNode, tipHeight: tipHeight) + } + itemLayouts.append(ItemLayout( size: itemSize.size, apparentHeight: itemSize.apparentHeight, transitionFraction: transitionFraction, alphaTransitionFraction: alphaTransitionFraction, itemTransition: itemContainerTransition, - animateAppearingContainer: animateAppearingContainer + animateAppearingContainer: animateAppearingContainer, + tip: tip )) } @@ -1232,6 +1244,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { } let navigationContainerFrame = CGRect(origin: CGPoint(), size: CGSize(width: topItemWidth, height: max(14 * 2.0, topItemApparentHeight))) + let previousNavigationContainerFrame = self.navigationContainer.frame transition.updateFrame(node: self.navigationContainer, frame: navigationContainerFrame, beginWithCurrentState: true) self.navigationContainer.update(presentationData: presentationData, presentation: presentation, size: navigationContainerFrame.size, transition: transition) @@ -1258,20 +1271,28 @@ final class ContextControllerActionsStackNode: ASDisplayNode { self.itemContainers[i].updateDimNode(presentationData: presentationData, size: CGSize(width: itemLayouts[i].size.width, height: navigationContainerFrame.size.height), transitionFraction: itemLayouts[i].alphaTransitionFraction, transition: transition) - if let (tipNode, tipHeight) = self.itemContainers[i].updateTip(presentationData: presentationData, width: itemLayouts[i].size.width, transition: transition) { - var tipTransition = transition - if tipNode.supernode == nil { - tipTransition = .immediate - self.addSubnode(tipNode) + if let tip = itemLayouts[i].tip { + let tipTransition = transition + var animateTipIn = false + if tip.tipNode.supernode == nil { + self.addSubnode(tip.tipNode) + animateTipIn = transition.isAnimated + tip.tipNode.frame = CGRect(origin: CGPoint(x: previousNavigationContainerFrame.minX, y: previousNavigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight)) } let tipAlpha: CGFloat = itemLayouts[i].alphaTransitionFraction - tipTransition.updateFrame(node: tipNode, frame: CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tipHeight)), beginWithCurrentState: true) - tipTransition.updateAlpha(node: tipNode, alpha: tipAlpha, beginWithCurrentState: true) + tipTransition.updateFrame(node: tip.tipNode, frame: CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tip.tipHeight)), beginWithCurrentState: true) + + if animateTipIn { + tip.tipNode.alpha = tipAlpha + tip.tipNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } else { + tipTransition.updateAlpha(node: tip.tipNode, alpha: tipAlpha, beginWithCurrentState: true) + } if i == self.itemContainers.count - 1 { - topItemSize.height += tipSpacing + tipHeight + topItemSize.height += tipSpacing + tip.tipHeight } } } diff --git a/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift b/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift index 7de6a36d20..a4f1d2737a 100644 --- a/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift +++ b/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift @@ -323,23 +323,21 @@ public func peerAllowedReactionListController( updateState { state in var state = state - if allowedReactions == nil { - state.updatedMode = .all - if let availableReactions = availableReactions { - let updatedAllowedReactions = availableReactions.reactions.map { $0.value } - state.updatedAllowedReactions = Set(updatedAllowedReactions) - } - } else if let allowedReactions = allowedReactions, !allowedReactions.isEmpty { - if let availableReactions = availableReactions, Set(allowedReactions) == Set(availableReactions.reactions.map(\.value)) { + switch allowedReactions { + case .unknown: + break + case let .known(value): + switch value { + case .all: state.updatedMode = .all - } else { + state.updatedAllowedReactions = Set() + case let .limited(reactions): state.updatedMode = .some + state.updatedAllowedReactions = Set(reactions) + case .empty: + state.updatedMode = .empty + state.updatedAllowedReactions = Set() } - let updatedAllowedReactions = Set(allowedReactions) - state.updatedAllowedReactions = updatedAllowedReactions - } else { - state.updatedMode = .empty - state.updatedAllowedReactions = Set() } return state @@ -371,6 +369,12 @@ public func peerAllowedReactionListController( } case .some: updatedAllowedReactions.removeAll() + if let thumbsUp = availableReactions.reactions.first(where: { $0.value == .builtin("👍") }) { + updatedAllowedReactions.insert(thumbsUp.value) + } + if let thumbsDown = availableReactions.reactions.first(where: { $0.value == .builtin("👎") }) { + updatedAllowedReactions.insert(thumbsDown.value) + } case .empty: updatedAllowedReactions.removeAll() } @@ -443,12 +447,24 @@ public func peerAllowedReactionListController( let controller = ItemListController(context: context, state: signal) controller.willDisappear = { _ in let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.AllowedReactions(id: peerId)) - |> deliverOnMainQueue).start(next: { initialAllowedReactionList in - let initialAllowedReactions = initialAllowedReactionList.flatMap(Set.init) + |> deliverOnMainQueue).start(next: { initialAllowedReactions in + let state = stateValue.with({ $0 }) + guard let updatedMode = state.updatedMode, let updatedAllowedReactions = state.updatedAllowedReactions else { + return + } - let updatedAllowedReactions = stateValue.with({ $0 }).updatedAllowedReactions - if let updatedAllowedReactions = updatedAllowedReactions, initialAllowedReactions != updatedAllowedReactions { - let _ = context.engine.peers.updatePeerAllowedReactions(peerId: peerId, allowedReactions: Array(updatedAllowedReactions)).start() + let updatedValue: PeerAllowedReactions + switch updatedMode { + case .all: + updatedValue = .all + case .some: + updatedValue = .limited(Array(updatedAllowedReactions)) + case .empty: + updatedValue = .empty + } + + if initialAllowedReactions != .known(updatedValue) { + let _ = context.engine.peers.updatePeerAllowedReactions(peerId: peerId, allowedReactions: updatedValue).start() } }) } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index f5efaafca9..6807723a58 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -151,8 +151,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1605510357] = { return Api.ChatAdminRights.parse_chatAdminRights($0) } dict[-219353309] = { return Api.ChatAdminWithInvites.parse_chatAdminWithInvites($0) } dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) } - dict[-362240487] = { return Api.ChatFull.parse_channelFull($0) } - dict[-779165146] = { return Api.ChatFull.parse_chatFull($0) } + dict[-231385849] = { return Api.ChatFull.parse_channelFull($0) } + dict[-908914376] = { return Api.ChatFull.parse_chatFull($0) } dict[806110401] = { return Api.ChatInvite.parse_chatInvite($0) } dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) } dict[1634294960] = { return Api.ChatInvite.parse_chatInvitePeek($0) } @@ -165,6 +165,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2023500831] = { return Api.ChatParticipants.parse_chatParticipantsForbidden($0) } dict[476978193] = { return Api.ChatPhoto.parse_chatPhoto($0) } dict[935395612] = { return Api.ChatPhoto.parse_chatPhotoEmpty($0) } + dict[1385335754] = { return Api.ChatReactions.parse_chatReactionsAll($0) } + dict[-352570692] = { return Api.ChatReactions.parse_chatReactionsNone($0) } + dict[1713193015] = { return Api.ChatReactions.parse_chatReactionsSome($0) } dict[-1973130814] = { return Api.CodeSettings.parse_codeSettings($0) } dict[589653676] = { return Api.Config.parse_config($0) } dict[341499403] = { return Api.Contact.parse_contact($0) } @@ -1207,6 +1210,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.ChatPhoto: _1.serialize(buffer, boxed) + case let _1 as Api.ChatReactions: + _1.serialize(buffer, boxed) case let _1 as Api.CodeSettings: _1.serialize(buffer, boxed) case let _1 as Api.Config: diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 9157386322..8856435340 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -5321,6 +5321,23 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func reportReaction(peer: Api.InputPeer, id: Int32, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1631726152) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + return (FunctionDescription(name: "messages.reportReaction", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.messages { static func reportSpam(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -5898,15 +5915,11 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func setChatAvailableReactions(peer: Api.InputPeer, availableReactions: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func setChatAvailableReactions(peer: Api.InputPeer, availableReactions: Api.ChatReactions) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(335875750) + buffer.appendInt32(-21928079) peer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(availableReactions.count)) - for item in availableReactions { - serializeString(item, buffer: buffer, boxed: false) - } + availableReactions.serialize(buffer, true) return (FunctionDescription(name: "messages.setChatAvailableReactions", parameters: [("peer", String(describing: peer)), ("availableReactions", String(describing: availableReactions))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index cad9b775f2..be885f4680 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -880,14 +880,14 @@ public extension Api { } public extension Api { enum ChatFull: TypeConstructorDescription { - case channelFull(flags: Int32, flags2: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?, defaultSendAs: Api.Peer?, availableReactions: [String]?) - case chatFull(flags: Int32, id: Int64, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?, availableReactions: [String]?) + case channelFull(flags: Int32, flags2: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?, defaultSendAs: Api.Peer?, availableReactions: Api.ChatReactions?) + case chatFull(flags: Int32, id: Int64, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?, recentRequesters: [Int64]?, availableReactions: Api.ChatReactions?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { case .channelFull(let flags, let flags2, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let defaultSendAs, let availableReactions): if boxed { - buffer.appendInt32(-362240487) + buffer.appendInt32(-231385849) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -937,15 +937,11 @@ public extension Api { serializeInt64(item, buffer: buffer, boxed: false) }} if Int(flags) & Int(1 << 29) != 0 {defaultSendAs!.serialize(buffer, true)} - if Int(flags) & Int(1 << 30) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(availableReactions!.count)) - for item in availableReactions! { - serializeString(item, buffer: buffer, boxed: false) - }} + if Int(flags) & Int(1 << 30) != 0 {availableReactions!.serialize(buffer, true)} break case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending, let recentRequesters, let availableReactions): if boxed { - buffer.appendInt32(-779165146) + buffer.appendInt32(-908914376) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) @@ -971,11 +967,7 @@ public extension Api { for item in recentRequesters! { serializeInt64(item, buffer: buffer, boxed: false) }} - if Int(flags) & Int(1 << 18) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(availableReactions!.count)) - for item in availableReactions! { - serializeString(item, buffer: buffer, boxed: false) - }} + if Int(flags) & Int(1 << 18) != 0 {availableReactions!.serialize(buffer, true)} break } } @@ -1084,9 +1076,9 @@ public extension Api { if Int(_1!) & Int(1 << 29) != 0 {if let signature = reader.readInt32() { _36 = Api.parse(reader, signature: signature) as? Api.Peer } } - var _37: [String]? - if Int(_1!) & Int(1 << 30) != 0 {if let _ = reader.readInt32() { - _37 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) + var _37: Api.ChatReactions? + if Int(_1!) & Int(1 << 30) != 0 {if let signature = reader.readInt32() { + _37 = Api.parse(reader, signature: signature) as? Api.ChatReactions } } let _c1 = _1 != nil let _c2 = _2 != nil @@ -1181,9 +1173,9 @@ public extension Api { if Int(_1!) & Int(1 << 17) != 0 {if let _ = reader.readInt32() { _16 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } } - var _17: [String]? - if Int(_1!) & Int(1 << 18) != 0 {if let _ = reader.readInt32() { - _17 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self) + var _17: Api.ChatReactions? + if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() { + _17 = Api.parse(reader, signature: signature) as? Api.ChatReactions } } let _c1 = _1 != nil let _c2 = _2 != nil diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 36e39fc393..a382f35afc 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -318,6 +318,80 @@ public extension Api { } } +public extension Api { + enum ChatReactions: TypeConstructorDescription { + case chatReactionsAll(flags: Int32) + case chatReactionsNone + case chatReactionsSome(reactions: [Api.Reaction]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .chatReactionsAll(let flags): + if boxed { + buffer.appendInt32(1385335754) + } + serializeInt32(flags, buffer: buffer, boxed: false) + break + case .chatReactionsNone: + if boxed { + buffer.appendInt32(-352570692) + } + + break + case .chatReactionsSome(let reactions): + if boxed { + buffer.appendInt32(1713193015) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(reactions.count)) + for item in reactions { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .chatReactionsAll(let flags): + return ("chatReactionsAll", [("flags", String(describing: flags))]) + case .chatReactionsNone: + return ("chatReactionsNone", []) + case .chatReactionsSome(let reactions): + return ("chatReactionsSome", [("reactions", String(describing: reactions))]) + } + } + + public static func parse_chatReactionsAll(_ reader: BufferReader) -> ChatReactions? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.ChatReactions.chatReactionsAll(flags: _1!) + } + else { + return nil + } + } + public static func parse_chatReactionsNone(_ reader: BufferReader) -> ChatReactions? { + return Api.ChatReactions.chatReactionsNone + } + public static func parse_chatReactionsSome(_ reader: BufferReader) -> ChatReactions? { + var _1: [Api.Reaction]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.ChatReactions.chatReactionsSome(reactions: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum CodeSettings: TypeConstructorDescription { case codeSettings(flags: Int32, logoutTokens: [Buffer]?) diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index cd50840a64..6af7bd12b4 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -558,7 +558,7 @@ public enum UpdatePeerAllowedReactionsError { case generic } -func _internal_updatePeerAllowedReactions(account: Account, peerId: PeerId, allowedReactions: [MessageReaction.Reaction]) -> Signal { +func _internal_updatePeerAllowedReactions(account: Account, peerId: PeerId, allowedReactions: PeerAllowedReactions) -> Signal { return account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } @@ -567,14 +567,18 @@ func _internal_updatePeerAllowedReactions(account: Account, peerId: PeerId, allo guard let inputPeer = inputPeer else { return .fail(.generic) } - return account.network.request(Api.functions.messages.setChatAvailableReactions(peer: inputPeer, availableReactions: allowedReactions.compactMap { item -> String? in - switch item { - case let .builtin(value): - return value - case .custom: - return nil - } - })) + + let mappedReactions: Api.ChatReactions + switch allowedReactions { + case .all: + mappedReactions = .chatReactionsAll(flags: 0) + case let .limited(array): + mappedReactions = .chatReactionsSome(reactions: array.map(\.apiReaction)) + case .empty: + mappedReactions = .chatReactionsNone + } + + return account.network.request(Api.functions.messages.setChatAvailableReactions(peer: inputPeer, availableReactions: mappedReactions)) |> mapError { _ -> UpdatePeerAllowedReactionsError in return .generic } @@ -584,9 +588,9 @@ func _internal_updatePeerAllowedReactions(account: Account, peerId: PeerId, allo return account.postbox.transaction { transaction -> Void in transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in if let current = current as? CachedChannelData { - return current.withUpdatedAllowedReactions(allowedReactions) + return current.withUpdatedAllowedReactions(.known(allowedReactions)) } else if let current = current as? CachedGroupData { - return current.withUpdatedAllowedReactions(allowedReactions) + return current.withUpdatedAllowedReactions(.known(allowedReactions)) } else { return current } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift index 85c823bd45..35c772d39c 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedChannelData.swift @@ -238,7 +238,7 @@ public final class CachedChannelData: CachedPeerData { public let themeEmoticon: String? public let inviteRequestsPending: Int32? public let sendAsPeerId: PeerId? - public let allowedReactions: [MessageReaction.Reaction]? + public let allowedReactions: EnginePeerCachedInfoItem public let peerIds: Set public let messageIds: Set @@ -276,7 +276,7 @@ public final class CachedChannelData: CachedPeerData { self.themeEmoticon = nil self.inviteRequestsPending = nil self.sendAsPeerId = nil - self.allowedReactions = nil + self.allowedReactions = .unknown } public init( @@ -307,7 +307,7 @@ public final class CachedChannelData: CachedPeerData { themeEmoticon: String?, inviteRequestsPending: Int32?, sendAsPeerId: PeerId?, - allowedReactions: [MessageReaction.Reaction]? + allowedReactions: EnginePeerCachedInfoItem ) { self.isNotAccessible = isNotAccessible self.flags = flags @@ -471,7 +471,7 @@ public final class CachedChannelData: CachedPeerData { return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: sendAsPeerId, allowedReactions: self.allowedReactions) } - public func withUpdatedAllowedReactions(_ allowedReactions: [MessageReaction.Reaction]?) -> CachedChannelData { + public func withUpdatedAllowedReactions(_ allowedReactions: EnginePeerCachedInfoItem) -> CachedChannelData { return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, invitedOn: self.invitedOn, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, sendAsPeerId: self.sendAsPeerId, allowedReactions: allowedReactions) } @@ -562,10 +562,12 @@ public final class CachedChannelData: CachedPeerData { self.sendAsPeerId = decoder.decodeOptionalInt64ForKey("sendAsPeerId").flatMap(PeerId.init) - if let allowedReactions = decoder.decodeOptionalStringArrayForKey("allowedReactions") { - self.allowedReactions = allowedReactions.map(MessageReaction.Reaction.builtin) + if let legacyAllowedReactions = decoder.decodeOptionalStringArrayForKey("allowedReactions") { + self.allowedReactions = .known(.limited(legacyAllowedReactions.map(MessageReaction.Reaction.builtin))) + } else if let allowedReactions = decoder.decode(PeerAllowedReactions.self, forKey: "allowedReactionSet") { + self.allowedReactions = .known(allowedReactions) } else { - self.allowedReactions = nil + self.allowedReactions = .unknown } if case let .known(linkedDiscussionPeerIdValue) = self.linkedDiscussionPeerId { @@ -712,17 +714,11 @@ public final class CachedChannelData: CachedPeerData { encoder.encodeNil(forKey: "sendAsPeerId") } - if let allowedReactions = self.allowedReactions { - encoder.encodeStringArray(allowedReactions.compactMap { item -> String? in - switch item { - case let .builtin(value): - return value - case .custom: - return nil - } - }, forKey: "allowedReactions") - } else { - encoder.encodeNil(forKey: "allowedReactions") + switch self.allowedReactions { + case .unknown: + encoder.encodeNil(forKey: "allowedReactionSet") + case let .known(value): + encoder.encode(value, forKey: "allowedReactionSet") } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift index 5c1862a2e6..1095bf9a41 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedGroupData.swift @@ -39,6 +39,49 @@ public struct CachedGroupFlags: OptionSet { public static let canChangeUsername = CachedGroupFlags(rawValue: 1 << 0) } +public enum PeerAllowedReactions: Equatable, Codable { + private enum Discriminant: Int32 { + case all = 0 + case limited = 1 + case empty = 2 + } + + case all + case limited([MessageReaction.Reaction]) + case empty + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + let discriminant = try container.decode(Int32.self, forKey: "_d") + switch discriminant { + case Discriminant.all.rawValue: + self = .all + case Discriminant.limited.rawValue: + self = .limited(try container.decode([MessageReaction.Reaction].self, forKey: "r")) + case Discriminant.empty.rawValue: + self = .empty + default: + assertionFailure() + self = .all + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + switch self { + case .all: + try container.encode(Discriminant.all.rawValue, forKey: "_d") + case let .limited(reactions): + try container.encode(Discriminant.limited.rawValue, forKey: "_d") + try container.encode(reactions, forKey: "r") + case .empty: + try container.encode(Discriminant.empty.rawValue, forKey: "_d") + } + } +} + public final class CachedGroupData: CachedPeerData { public let participants: CachedGroupParticipants? public let exportedInvitation: ExportedInvitation? @@ -55,7 +98,8 @@ public final class CachedGroupData: CachedPeerData { public let callJoinPeerId: PeerId? public let themeEmoticon: String? public let inviteRequestsPending: Int32? - public let allowedReactions: [MessageReaction.Reaction]? + + public let allowedReactions: EnginePeerCachedInfoItem public let peerIds: Set public let messageIds: Set @@ -79,7 +123,7 @@ public final class CachedGroupData: CachedPeerData { self.callJoinPeerId = nil self.themeEmoticon = nil self.inviteRequestsPending = nil - self.allowedReactions = nil + self.allowedReactions = .unknown } public init( @@ -98,7 +142,7 @@ public final class CachedGroupData: CachedPeerData { callJoinPeerId: PeerId?, themeEmoticon: String?, inviteRequestsPending: Int32?, - allowedReactions: [MessageReaction.Reaction]? + allowedReactions: EnginePeerCachedInfoItem ) { self.participants = participants self.exportedInvitation = exportedInvitation @@ -180,10 +224,12 @@ public final class CachedGroupData: CachedPeerData { self.inviteRequestsPending = decoder.decodeOptionalInt32ForKey("irp") - if let allowedReactions = decoder.decodeOptionalStringArrayForKey("allowedReactions") { - self.allowedReactions = allowedReactions.map(MessageReaction.Reaction.builtin) + if let legacyAllowedReactions = decoder.decodeOptionalStringArrayForKey("allowedReactions") { + self.allowedReactions = .known(.limited(legacyAllowedReactions.map(MessageReaction.Reaction.builtin))) + } else if let allowedReactions = decoder.decode(PeerAllowedReactions.self, forKey: "allowedReactionSet") { + self.allowedReactions = .known(allowedReactions) } else { - self.allowedReactions = nil + self.allowedReactions = .unknown } var messageIds = Set() @@ -276,17 +322,11 @@ public final class CachedGroupData: CachedPeerData { encoder.encodeNil(forKey: "irp") } - if let allowedReactions = self.allowedReactions { - encoder.encodeStringArray(allowedReactions.compactMap { item -> String? in - switch item { - case let .builtin(value): - return value - case .custom: - return nil - } - }, forKey: "allowedReactions") - } else { - encoder.encodeNil(forKey: "allowedReactions") + switch self.allowedReactions { + case .unknown: + encoder.encodeNil(forKey: "allowedReactionSet") + case let .known(value): + encoder.encode(value, forKey: "allowedReactionSet") } } @@ -370,7 +410,7 @@ public final class CachedGroupData: CachedPeerData { return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: inviteRequestsPending, allowedReactions: self.allowedReactions) } - public func withUpdatedAllowedReactions(_ allowedReactions: [MessageReaction.Reaction]?) -> CachedGroupData { + public func withUpdatedAllowedReactions(_ allowedReactions: EnginePeerCachedInfoItem) -> CachedGroupData { return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId, themeEmoticon: self.themeEmoticon, inviteRequestsPending: self.inviteRequestsPending, allowedReactions: allowedReactions) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index 21ed971f09..047fefea4e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -9,6 +9,25 @@ public enum EnginePeerCachedInfoItem { case unknown } +extension EnginePeerCachedInfoItem: Equatable where T: Equatable { + public static func ==(lhs: EnginePeerCachedInfoItem, rhs: EnginePeerCachedInfoItem) -> Bool { + switch lhs { + case let .known(value): + if case .known(value) = rhs { + return true + } else { + return false + } + case .unknown: + if case .unknown = rhs { + return true + } else { + return false + } + } + } +} + public enum EngineChannelParticipant: Equatable { case creator(id: EnginePeer.Id, adminInfo: ChannelParticipantAdminInfo?, rank: String?) case member(id: EnginePeer.Id, invitedAt: Int32, adminInfo: ChannelParticipantAdminInfo?, banInfo: ChannelParticipantBannedInfo?, rank: String?) @@ -452,7 +471,7 @@ public extension TelegramEngine.EngineData.Item { } public struct AllowedReactions: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { - public typealias Result = [MessageReaction.Reaction]? + public typealias Result = EnginePeerCachedInfoItem fileprivate var id: EnginePeer.Id public var mapKey: EnginePeer.Id { @@ -476,7 +495,7 @@ public extension TelegramEngine.EngineData.Item { } else if let cachedData = view.cachedPeerData as? CachedGroupData { return cachedData.allowedReactions } else { - return nil + return .unknown } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 05d601451a..741e3446da 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -694,7 +694,7 @@ public extension TelegramEngine { return _internal_updatePeerSendAsPeer(account: self.account, peerId: peerId, sendAs: sendAs) } - public func updatePeerAllowedReactions(peerId: PeerId, allowedReactions: [MessageReaction.Reaction]) -> Signal { + public func updatePeerAllowedReactions(peerId: PeerId, allowedReactions: PeerAllowedReactions) -> Signal { return _internal_updatePeerAllowedReactions(account: account, peerId: peerId, allowedReactions: allowedReactions) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 2e123e9ec3..7cfe3db726 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -376,6 +376,20 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } } + let mappedAllowedReactions: PeerAllowedReactions + if let allowedReactions = allowedReactions { + switch allowedReactions { + case .chatReactionsAll: + mappedAllowedReactions = .all + case let .chatReactionsSome(reactions): + mappedAllowedReactions = .limited(reactions.compactMap(MessageReaction.Reaction.init(apiReaction:))) + case .chatReactionsNone: + mappedAllowedReactions = .empty + } + } else { + mappedAllowedReactions = .empty + } + return previous.withUpdatedParticipants(participants) .withUpdatedExportedInvitation(exportedInvitation) .withUpdatedBotInfos(botInfos) @@ -389,7 +403,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedCallJoinPeerId(groupCallDefaultJoinAs?.peerId) .withUpdatedThemeEmoticon(chatFullThemeEmoticon) .withUpdatedInviteRequestsPending(chatFullRequestsPending) - .withUpdatedAllowedReactions(allowedReactions.flatMap({ $0.map(MessageReaction.Reaction.builtin) }) ?? []) + .withUpdatedAllowedReactions(.known(mappedAllowedReactions)) }) case .channelFull: break @@ -602,6 +616,20 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } } + let mappedAllowedReactions: PeerAllowedReactions + if let allowedReactions = allowedReactions { + switch allowedReactions { + case .chatReactionsAll: + mappedAllowedReactions = .all + case let .chatReactionsSome(reactions): + mappedAllowedReactions = .limited(reactions.compactMap(MessageReaction.Reaction.init(apiReaction:))) + case .chatReactionsNone: + mappedAllowedReactions = .empty + } + } else { + mappedAllowedReactions = .empty + } + return previous.withUpdatedFlags(channelFlags) .withUpdatedAbout(about) .withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: participantsCount, adminCount: adminsCount, bannedCount: bannedCount, kickedCount: kickedCount)) @@ -627,7 +655,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee .withUpdatedThemeEmoticon(themeEmoticon) .withUpdatedInviteRequestsPending(requestsPending) .withUpdatedSendAsPeerId(sendAsPeerId) - .withUpdatedAllowedReactions(allowedReactions.flatMap({ $0.map(MessageReaction.Reaction.builtin) }) ?? []) + .withUpdatedAllowedReactions(.known(mappedAllowedReactions)) }) if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 6d9cda265c..36a1f16c82 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -16963,12 +16963,18 @@ func peerAllowedReactions(context: AccountContext, peerId: PeerId) -> Signal map { peer, allowedReactions -> AllowedReactions? in - if let allowedReactions = allowedReactions { - return .set(Set(allowedReactions)) - } else if case .user = peer { - return .all - } else { + switch allowedReactions { + case .unknown: return nil + case let .known(value): + switch value { + case .all: + return .all + case let .limited(reactions): + return .set(Set(reactions)) + case .empty: + return .set(Set()) + } } } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 52dfe58514..f023e5fdf4 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1277,11 +1277,15 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { let label: String - if let cachedData = data.cachedData as? CachedChannelData, let allowedReactions = cachedData.allowedReactions { - if allowedReactions.isEmpty { + if let cachedData = data.cachedData as? CachedChannelData, case let .known(allowedReactions) = cachedData.allowedReactions { + switch allowedReactions { + case .all: + //TODO:localize + label = "Enabled" + case .empty: label = presentationData.strings.PeerInfo_ReactionsDisabled - } else { - label = "\(allowedReactions.count)" + case let .limited(reactions): + label = "\(reactions.count)" } } else { label = "" @@ -1439,11 +1443,15 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { let label: String - if let cachedData = data.cachedData as? CachedChannelData, let allowedReactions = cachedData.allowedReactions { - if allowedReactions.isEmpty { + if let cachedData = data.cachedData as? CachedChannelData, case let .known(allowedReactions) = cachedData.allowedReactions { + switch allowedReactions { + case .all: + //TODO:localize + label = "Enabled" + case .empty: label = presentationData.strings.PeerInfo_ReactionsDisabled - } else { - label = "\(allowedReactions.count)" + case let .limited(reactions): + label = "\(reactions.count)" } } else { label = "" @@ -1461,11 +1469,15 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr } else { if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { let label: String - if let cachedData = data.cachedData as? CachedChannelData, let allowedReactions = cachedData.allowedReactions { - if allowedReactions.isEmpty { + if let cachedData = data.cachedData as? CachedChannelData, case let .known(allowedReactions) = cachedData.allowedReactions { + switch allowedReactions { + case .all: + //TODO:localize + label = "Enabled" + case .empty: label = presentationData.strings.PeerInfo_ReactionsDisabled - } else { - label = "\(allowedReactions.count)" + case let .limited(reactions): + label = "\(reactions.count)" } } else { label = "" @@ -1571,11 +1583,15 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr do { let label: String - if let cachedData = data.cachedData as? CachedGroupData, let allowedReactions = cachedData.allowedReactions { - if allowedReactions.isEmpty { + if let cachedData = data.cachedData as? CachedGroupData, case let .known(allowedReactions) = cachedData.allowedReactions { + switch allowedReactions { + case .all: + //TODO:localize + label = "Enabled" + case .empty: label = presentationData.strings.PeerInfo_ReactionsDisabled - } else { - label = "\(allowedReactions.count)" + case let .limited(reactions): + label = "\(reactions.count)" } } else { label = ""