diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index d7d088dbdd..884b6f73e6 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -1502,15 +1502,21 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } |> distinctUntilChanged + let remotePacksSignal: Signal<(sets: FoundStickerSets, isFinalResult: Bool), NoError> = .single((FoundStickerSets(), false)) |> then( + context.engine.stickers.searchEmojiSetsRemotely(query: query) |> map { + ($0, true) + } + ) + let resultSignal = signal |> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in return combineLatest( - context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), - context.engine.stickers.availableReactions(), - hasPremium + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000) |> take(1), + context.engine.stickers.availableReactions() |> take(1), + hasPremium |> take(1), + remotePacksSignal ) - |> take(1) - |> map { view, availableReactions, hasPremium -> [EmojiPagerContentComponent.ItemGroup] in + |> map { view, availableReactions, hasPremium, foundPacks -> [EmojiPagerContentComponent.ItemGroup] in var result: [(String, TelegramMediaFile?, String)] = [] var allEmoticons: [String: String] = [:] @@ -1561,7 +1567,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } - return [EmojiPagerContentComponent.ItemGroup( + var resultGroups: [EmojiPagerContentComponent.ItemGroup] = [] + resultGroups.append(EmojiPagerContentComponent.ItemGroup( supergroupId: "search", groupId: "search", title: nil, @@ -1576,7 +1583,59 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { headerItem: nil, fillWithLoadingPlaceholders: false, items: items - )] + )) + + for (collectionId, info, _, _) in foundPacks.sets.infos { + if let info = info as? StickerPackCollectionInfo { + var topItems: [StickerPackItem] = [] + for e in foundPacks.sets.entries { + if let item = e.item as? StickerPackItem { + if e.index.collectionId == collectionId { + topItems.append(item) + } + } + } + + var groupItems: [EmojiPagerContentComponent.Item] = [] + for item in topItems { + var tintMode: EmojiPagerContentComponent.Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + groupItems.append(resultItem) + } + + resultGroups.append(EmojiPagerContentComponent.ItemGroup( + supergroupId: AnyHashable(info.id), + groupId: AnyHashable(info.id), + title: info.title, + subtitle: nil, + actionButtonTitle: nil, + isFeatured: false, + isPremiumLocked: false, + isEmbedded: false, + hasClear: false, + collapsedLineCount: 3, + displayPremiumBadges: false, + headerItem: nil, + fillWithLoadingPlaceholders: false, + items: groupItems + )) + } + } + + return resultGroups } } diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index 6fad3425d1..492d41accf 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -6613,6 +6613,23 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func searchEmojiStickerSets(flags: Int32, q: String, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1833678516) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(q, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.searchEmojiStickerSets", parameters: [("flags", String(describing: flags)), ("q", String(describing: q)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FoundStickerSets? in + let reader = BufferReader(buffer) + var result: Api.messages.FoundStickerSets? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FoundStickerSets + } + return result + }) + } +} public extension Api.functions.messages { static func searchGlobal(flags: Int32, folderId: Int32?, q: String, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/SearchStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/SearchStickers.swift index b745f9583b..9470ac2a76 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/SearchStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/SearchStickers.swift @@ -558,6 +558,54 @@ func _internal_searchStickerSetsRemotely(network: Network, query: String) -> Sig } } +func _internal_searchEmojiSetsRemotely(postbox: Postbox, network: Network, query: String) -> Signal { + return network.request(Api.functions.messages.searchEmojiStickerSets(flags: 0, q: query, hash: 0)) + |> mapError {_ in} + |> mapToSignal { value in + var index: Int32 = 1000 + switch value { + case let .foundStickerSets(_, sets: sets): + var result = FoundStickerSets() + for set in sets { + let parsed = parsePreviewStickerSet(set, namespace: Namespaces.ItemCollection.CloudEmojiPacks) + let values = parsed.1.map({ ItemCollectionViewEntry(index: ItemCollectionViewEntryIndex(collectionIndex: index, collectionId: parsed.0.id, itemIndex: $0.index), item: $0) }) + result = result.withUpdatedInfosAndEntries(infos: [(parsed.0.id, parsed.0, parsed.1.first, false)], entries: values) + index += 1 + } + return .single(result) + default: + break + } + + return .complete() + } + |> `catch` { _ -> Signal in + return .single(FoundStickerSets()) + } + |> mapToSignal { result -> Signal in + return postbox.combinedView(keys: [PostboxViewKey.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudEmojiPacks])]) + |> map { combinedView -> Set in + var installed = Set() + + if let view = combinedView.views[PostboxViewKey.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudEmojiPacks])] as? ItemCollectionInfosView { + var installedIds = Set() + if let ids = view.entriesByNamespace[Namespaces.ItemCollection.CloudEmojiPacks] { + installedIds = Set(ids.map(\.id)) + } + installed = installedIds.intersection(Set(result.infos.map(\.0))) + } + + return installed + } + |> distinctUntilChanged + |> map { installed -> FoundStickerSets in + return FoundStickerSets(infos: result.infos.map { info in + return (info.0, info.1, info.2, installed.contains(info.0)) + }, entries: result.entries) + } + } +} + func _internal_searchStickerSets(postbox: Postbox, query: String) -> Signal { return postbox.transaction { transaction -> Signal in let infos = transaction.getItemCollectionsInfos(namespace: Namespaces.ItemCollection.CloudStickerPacks) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift index b7c2c3f81f..54aca5647e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -37,6 +37,10 @@ public extension TelegramEngine { public func searchStickerSetsRemotely(query: String) -> Signal { return _internal_searchStickerSetsRemotely(network: self.account.network, query: query) } + + public func searchEmojiSetsRemotely(query: String) -> Signal { + return _internal_searchEmojiSetsRemotely(postbox: self.account.postbox, network: self.account.network, query: query) + } public func searchStickerSets(query: String) -> Signal { return _internal_searchStickerSets(postbox: self.account.postbox, query: query) diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 8aafdbc515..03b89e96e6 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -882,13 +882,18 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { } } let remoteSignal: Signal<(items: [TelegramMediaFile], isFinalResult: Bool), NoError> + let remotePacksSignal: Signal<(sets: FoundStickerSets, isFinalResult: Bool), NoError> if hasPremium { remoteSignal = context.engine.stickers.searchEmoji(emojiString: Array(allEmoticons.keys)) + remotePacksSignal = .single((FoundStickerSets(), false)) |> then(context.engine.stickers.searchEmojiSetsRemotely(query: query) |> map { + ($0, true) + }) } else { remoteSignal = .single(([], true)) + remotePacksSignal = .single((FoundStickerSets(), true)) } - return remoteSignal - |> mapToSignal { foundEmoji -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in + return combineLatest(remoteSignal, remotePacksSignal) + |> mapToSignal { foundEmoji, foundPacks -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in if foundEmoji.items.isEmpty && !foundEmoji.isFinalResult { return .complete() } @@ -940,8 +945,9 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { if hasPremium { appendUnicodeEmoji() } - - return .single([EmojiPagerContentComponent.ItemGroup( + + var resultGroups: [EmojiPagerContentComponent.ItemGroup] = [] + resultGroups.append(EmojiPagerContentComponent.ItemGroup( supergroupId: "search", groupId: "search", title: nil, @@ -956,7 +962,59 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { headerItem: nil, fillWithLoadingPlaceholders: false, items: items - )]) + )) + + for (collectionId, info, _, _) in foundPacks.sets.infos { + if let info = info as? StickerPackCollectionInfo { + var topItems: [StickerPackItem] = [] + for e in foundPacks.sets.entries { + if let item = e.item as? StickerPackItem { + if e.index.collectionId == collectionId { + topItems.append(item) + } + } + } + + var groupItems: [EmojiPagerContentComponent.Item] = [] + for item in topItems { + var tintMode: EmojiPagerContentComponent.Item.TintMode = .none + if item.file.isCustomTemplateEmoji { + tintMode = .primary + } + + let animationData = EntityKeyboardAnimationData(file: item.file) + let resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: item.file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + groupItems.append(resultItem) + } + + resultGroups.append(EmojiPagerContentComponent.ItemGroup( + supergroupId: AnyHashable(info.id), + groupId: AnyHashable(info.id), + title: info.title, + subtitle: nil, + actionButtonTitle: nil, + isFeatured: false, + isPremiumLocked: false, + isEmbedded: false, + hasClear: false, + collapsedLineCount: 3, + displayPremiumBadges: false, + headerItem: nil, + fillWithLoadingPlaceholders: false, + items: groupItems + )) + } + } + + return .single(resultGroups) } } diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift index 47e0147c3c..6fc5719c84 100644 --- a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift @@ -410,7 +410,16 @@ final class PeerAllowedReactionsScreenComponent: Component { if nextCustomReactionCount > boostStatus.level { //TODO:localize let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: itemFile, loop: false, title: nil, text: "Your channel needs to reach **Level \(nextCustomReactionCount)** to add **\(nextCustomReactionCount) custom emoji as reactions.**", undoText: nil, customAction: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current) + + var animateAsReplacement = false + if let currentUndoController = self.currentUndoController { + currentUndoController.dismiss() + animateAsReplacement = true + } + + let undoController = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: itemFile, loop: false, title: nil, text: "Your channel needs to reach **Level \(nextCustomReactionCount)** to add **\(nextCustomReactionCount) custom emoji as reactions.**", undoText: nil, customAction: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: animateAsReplacement, action: { _ in return false }) + self.currentUndoController = undoController + self.environment?.controller()?.present(undoController, in: .current) } } }