mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-05 05:51:42 +00:00
Implement status emoji search
This commit is contained in:
parent
5b1f01ce75
commit
4be4770776
@ -235,7 +235,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
private var emojiContentDisposable: Disposable?
|
private var emojiContentDisposable: Disposable?
|
||||||
|
|
||||||
private let emojiSearchDisposable = MetaDisposable()
|
private let emojiSearchDisposable = MetaDisposable()
|
||||||
private let emojiSearchResult = Promise<(groups: [EmojiPagerContentComponent.ItemGroup], id: AnyHashable)?>(nil)
|
private let emojiSearchResult = Promise<(groups: [EmojiPagerContentComponent.ItemGroup], id: AnyHashable, emptyResultEmoji: TelegramMediaFile?)?>(nil)
|
||||||
|
private var stableEmptyResultEmoji: TelegramMediaFile?
|
||||||
|
|
||||||
private var horizontalExpandRecognizer: UIPanGestureRecognizer?
|
private var horizontalExpandRecognizer: UIPanGestureRecognizer?
|
||||||
private var horizontalExpandStartLocation: CGPoint?
|
private var horizontalExpandStartLocation: CGPoint?
|
||||||
@ -419,7 +420,22 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|
|
||||||
var emojiContent = emojiContent
|
var emojiContent = emojiContent
|
||||||
if let emojiSearchResult = emojiSearchResult {
|
if let emojiSearchResult = emojiSearchResult {
|
||||||
emojiContent = emojiContent.withUpdatedItemGroups(itemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id)
|
var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults?
|
||||||
|
if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty }) {
|
||||||
|
if strongSelf.stableEmptyResultEmoji == nil {
|
||||||
|
strongSelf.stableEmptyResultEmoji = emojiSearchResult.emptyResultEmoji
|
||||||
|
}
|
||||||
|
//TODO:localize
|
||||||
|
emptySearchResults = EmojiPagerContentComponent.EmptySearchResults(
|
||||||
|
text: "No emoji found",
|
||||||
|
iconFile: strongSelf.stableEmptyResultEmoji
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
strongSelf.stableEmptyResultEmoji = nil
|
||||||
|
}
|
||||||
|
emojiContent = emojiContent.withUpdatedItemGroups(itemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
|
||||||
|
} else {
|
||||||
|
strongSelf.stableEmptyResultEmoji = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.emojiContent = emojiContent
|
strongSelf.emojiContent = emojiContent
|
||||||
@ -1315,12 +1331,14 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
|
||||||
let resultSignal = signal
|
let resultSignal = signal
|
||||||
|> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in
|
|> mapToSignal { keywords -> Signal<(result: [EmojiPagerContentComponent.ItemGroup], emptyResultEmoji: TelegramMediaFile?), NoError> in
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
|
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
|
||||||
|
context.engine.stickers.availableReactions(),
|
||||||
hasPremium
|
hasPremium
|
||||||
)
|
)
|
||||||
|> map { view, hasPremium -> [EmojiPagerContentComponent.ItemGroup] in
|
|> take(1)
|
||||||
|
|> map { view, availableReactions, hasPremium -> (result: [EmojiPagerContentComponent.ItemGroup], emptyResultEmoji: TelegramMediaFile?) in
|
||||||
var result: [(String, TelegramMediaFile?, String)] = []
|
var result: [(String, TelegramMediaFile?, String)] = []
|
||||||
|
|
||||||
var allEmoticons: [String: String] = [:]
|
var allEmoticons: [String: String] = [:]
|
||||||
@ -1371,8 +1389,42 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [EmojiPagerContentComponent.ItemGroup(
|
var emptyResultEmoji: TelegramMediaFile?
|
||||||
supergroupId: "search", groupId: "search", title: nil, subtitle: nil, actionButtonTitle: nil, isFeatured: false, isPremiumLocked: false, isEmbedded: false, hasClear: false, collapsedLineCount: nil, displayPremiumBadges: false, headerItem: nil, items: items)]
|
if let availableReactions = availableReactions {
|
||||||
|
//let reactionFilter: [String] = ["😖", "😫", "🫠", "😨", "❓"]
|
||||||
|
let filteredReactions: [TelegramMediaFile] = availableReactions.reactions.compactMap { reaction -> TelegramMediaFile? in
|
||||||
|
switch reaction.value {
|
||||||
|
case let .builtin(value):
|
||||||
|
let _ = value
|
||||||
|
//if reactionFilter.contains(value) {
|
||||||
|
return reaction.selectAnimation
|
||||||
|
/*} else {
|
||||||
|
return nil
|
||||||
|
}*/
|
||||||
|
case .custom:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emptyResultEmoji = filteredReactions.randomElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result: [EmojiPagerContentComponent.ItemGroup(
|
||||||
|
supergroupId: "search",
|
||||||
|
groupId: "search",
|
||||||
|
title: nil,
|
||||||
|
subtitle: nil,
|
||||||
|
actionButtonTitle: nil,
|
||||||
|
isFeatured: false,
|
||||||
|
isPremiumLocked: false,
|
||||||
|
isEmbedded: false,
|
||||||
|
hasClear: false,
|
||||||
|
collapsedLineCount: nil,
|
||||||
|
displayPremiumBadges: false,
|
||||||
|
headerItem: nil,
|
||||||
|
items: items
|
||||||
|
)],
|
||||||
|
emptyResultEmoji: emptyResultEmoji
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1382,7 +1434,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.emojiSearchResult.set(.single((result, AnyHashable(query))))
|
strongSelf.emojiSearchResult.set(.single((result.result, AnyHashable(query), result.emptyResultEmoji)))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -210,6 +210,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
|||||||
self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition)
|
self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition)
|
||||||
|
|
||||||
transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: component.hideTopPanel ? -UIScreenPixel : topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel)))
|
transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: component.hideTopPanel ? -UIScreenPixel : topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel)))
|
||||||
|
transition.setAlpha(view: self.panelSeparatorView, alpha: component.hideTopPanel ? 0.0 : 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
@ -250,6 +251,10 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
private var freezeUpdates: Bool = false
|
private var freezeUpdates: Bool = false
|
||||||
private var scheduledEmojiContentAnimationHint: EmojiPagerContentComponent.ContentAnimation?
|
private var scheduledEmojiContentAnimationHint: EmojiPagerContentComponent.ContentAnimation?
|
||||||
|
|
||||||
|
private let emojiSearchDisposable = MetaDisposable()
|
||||||
|
private let emojiSearchResult = Promise<(groups: [EmojiPagerContentComponent.ItemGroup], id: AnyHashable, emptyResultEmoji: TelegramMediaFile?)?>(nil)
|
||||||
|
private var stableEmptyResultEmoji: TelegramMediaFile?
|
||||||
|
|
||||||
private var previewItem: (groupId: AnyHashable, item: EmojiPagerContentComponent.Item)?
|
private var previewItem: (groupId: AnyHashable, item: EmojiPagerContentComponent.Item)?
|
||||||
private var dismissedPreviewItem: (groupId: AnyHashable, item: EmojiPagerContentComponent.Item)?
|
private var dismissedPreviewItem: (groupId: AnyHashable, item: EmojiPagerContentComponent.Item)?
|
||||||
private var previewScreenView: ComponentView<Empty>?
|
private var previewScreenView: ComponentView<Empty>?
|
||||||
@ -265,6 +270,8 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
private var isAnimatingOut: Bool = false
|
private var isAnimatingOut: Bool = false
|
||||||
private var isDismissed: Bool = false
|
private var isDismissed: Bool = false
|
||||||
|
|
||||||
|
private var isReactionSearchActive: Bool = false
|
||||||
|
|
||||||
init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?) {
|
init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -306,12 +313,36 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
self.layer.addSublayer(self.cloudLayer0)
|
self.layer.addSublayer(self.cloudLayer0)
|
||||||
self.layer.addSublayer(self.cloudLayer1)
|
self.layer.addSublayer(self.cloudLayer1)
|
||||||
|
|
||||||
self.emojiContentDisposable = (emojiContent
|
self.emojiContentDisposable = (combineLatest(queue: .mainQueue(),
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] emojiContent in
|
emojiContent,
|
||||||
|
self.emojiSearchResult.get()
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] emojiContent, emojiSearchResult in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.controller?._ready.set(.single(true))
|
strongSelf.controller?._ready.set(.single(true))
|
||||||
|
|
||||||
|
var emojiContent = emojiContent
|
||||||
|
if let emojiSearchResult = emojiSearchResult {
|
||||||
|
var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults?
|
||||||
|
if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty }) {
|
||||||
|
if strongSelf.stableEmptyResultEmoji == nil {
|
||||||
|
strongSelf.stableEmptyResultEmoji = emojiSearchResult.emptyResultEmoji
|
||||||
|
}
|
||||||
|
//TODO:localize
|
||||||
|
emptySearchResults = EmojiPagerContentComponent.EmptySearchResults(
|
||||||
|
text: "No emoji found",
|
||||||
|
iconFile: strongSelf.stableEmptyResultEmoji
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
strongSelf.stableEmptyResultEmoji = nil
|
||||||
|
}
|
||||||
|
emojiContent = emojiContent.withUpdatedItemGroups(itemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
|
||||||
|
} else {
|
||||||
|
strongSelf.stableEmptyResultEmoji = nil
|
||||||
|
}
|
||||||
|
|
||||||
if strongSelf.emojiContent == nil || !strongSelf.freezeUpdates {
|
if strongSelf.emojiContent == nil || !strongSelf.freezeUpdates {
|
||||||
strongSelf.emojiContent = emojiContent
|
strongSelf.emojiContent = emojiContent
|
||||||
}
|
}
|
||||||
@ -366,7 +397,150 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
},
|
},
|
||||||
requestUpdate: { _ in
|
requestUpdate: { _ in
|
||||||
},
|
},
|
||||||
updateSearchQuery: { _ in
|
updateSearchQuery: { rawQuery in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = rawQuery.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
|
if query.isEmpty {
|
||||||
|
strongSelf.emojiSearchDisposable.set(nil)
|
||||||
|
strongSelf.emojiSearchResult.set(.single(nil))
|
||||||
|
} else {
|
||||||
|
let context = strongSelf.context
|
||||||
|
|
||||||
|
let languageCode = "en"
|
||||||
|
var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: false)
|
||||||
|
if !languageCode.lowercased().hasPrefix("en") {
|
||||||
|
signal = signal
|
||||||
|
|> mapToSignal { keywords in
|
||||||
|
return .single(keywords)
|
||||||
|
|> then(
|
||||||
|
context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3)
|
||||||
|
|> map { englishKeywords in
|
||||||
|
return keywords + englishKeywords
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|
|> map { peer -> Bool in
|
||||||
|
guard case let .user(user) = peer else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return user.isPremium
|
||||||
|
}
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
let resultSignal = signal
|
||||||
|
|> mapToSignal { keywords -> Signal<(result: [EmojiPagerContentComponent.ItemGroup], emptyResultEmoji: TelegramMediaFile?), NoError> in
|
||||||
|
return combineLatest(
|
||||||
|
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
|
||||||
|
context.engine.stickers.availableReactions(),
|
||||||
|
hasPremium
|
||||||
|
)
|
||||||
|
|> take(1)
|
||||||
|
|> map { view, availableReactions, hasPremium -> (result: [EmojiPagerContentComponent.ItemGroup], emptyResultEmoji: TelegramMediaFile?) in
|
||||||
|
var result: [(String, TelegramMediaFile?, String)] = []
|
||||||
|
|
||||||
|
var allEmoticons: [String: String] = [:]
|
||||||
|
for keyword in keywords {
|
||||||
|
for emoticon in keyword.emoticons {
|
||||||
|
allEmoticons[emoticon] = keyword.keyword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in view.entries {
|
||||||
|
guard let item = entry.item as? StickerPackItem else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for attribute in item.file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .CustomEmoji(_, alt, _):
|
||||||
|
if !item.file.isPremiumEmoji || hasPremium {
|
||||||
|
if !alt.isEmpty, let keyword = allEmoticons[alt] {
|
||||||
|
result.append((alt, item.file, keyword))
|
||||||
|
} else if alt == query {
|
||||||
|
result.append((alt, item.file, alt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var items: [EmojiPagerContentComponent.Item] = []
|
||||||
|
|
||||||
|
var existingIds = Set<MediaId>()
|
||||||
|
for item in result {
|
||||||
|
if let itemFile = item.1 {
|
||||||
|
if existingIds.contains(itemFile.fileId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existingIds.insert(itemFile.fileId)
|
||||||
|
let animationData = EntityKeyboardAnimationData(file: itemFile)
|
||||||
|
let item = EmojiPagerContentComponent.Item(
|
||||||
|
animationData: animationData,
|
||||||
|
content: .animation(animationData),
|
||||||
|
itemFile: itemFile, subgroupId: nil,
|
||||||
|
icon: .none,
|
||||||
|
accentTint: false
|
||||||
|
)
|
||||||
|
items.append(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyResultEmoji: TelegramMediaFile?
|
||||||
|
if let availableReactions = availableReactions {
|
||||||
|
//let reactionFilter: [String] = ["😖", "😫", "🫠", "😨", "❓"]
|
||||||
|
let filteredReactions: [TelegramMediaFile] = availableReactions.reactions.compactMap { reaction -> TelegramMediaFile? in
|
||||||
|
switch reaction.value {
|
||||||
|
case let .builtin(value):
|
||||||
|
let _ = value
|
||||||
|
//if reactionFilter.contains(value) {
|
||||||
|
return reaction.selectAnimation
|
||||||
|
/*} else {
|
||||||
|
return nil
|
||||||
|
}*/
|
||||||
|
case .custom:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emptyResultEmoji = filteredReactions.randomElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result: [EmojiPagerContentComponent.ItemGroup(
|
||||||
|
supergroupId: "search",
|
||||||
|
groupId: "search",
|
||||||
|
title: nil,
|
||||||
|
subtitle: nil,
|
||||||
|
actionButtonTitle: nil,
|
||||||
|
isFeatured: false,
|
||||||
|
isPremiumLocked: false,
|
||||||
|
isEmbedded: false,
|
||||||
|
hasClear: false,
|
||||||
|
collapsedLineCount: nil,
|
||||||
|
displayPremiumBadges: false,
|
||||||
|
headerItem: nil,
|
||||||
|
items: items
|
||||||
|
)],
|
||||||
|
emptyResultEmoji: emptyResultEmoji
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.emojiSearchDisposable.set((resultSignal
|
||||||
|
|> delay(0.15, queue: .mainQueue())
|
||||||
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.emojiSearchResult.set(.single((result.result, AnyHashable(query), result.emptyResultEmoji)))
|
||||||
|
}))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
chatPeerId: nil,
|
chatPeerId: nil,
|
||||||
peekBehavior: nil,
|
peekBehavior: nil,
|
||||||
@ -398,6 +572,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
self.emojiContentDisposable?.dispose()
|
self.emojiContentDisposable?.dispose()
|
||||||
self.availableReactionsDisposable?.dispose()
|
self.availableReactionsDisposable?.dispose()
|
||||||
self.genericReactionEffectDisposable?.dispose()
|
self.genericReactionEffectDisposable?.dispose()
|
||||||
|
self.emojiSearchDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func refreshLayout(transition: Transition) {
|
private func refreshLayout(transition: Transition) {
|
||||||
@ -664,8 +839,14 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
emojiContent: emojiContent,
|
emojiContent: emojiContent,
|
||||||
backgroundColor: listBackgroundColor,
|
backgroundColor: listBackgroundColor,
|
||||||
separatorColor: separatorColor,
|
separatorColor: separatorColor,
|
||||||
hideTopPanel: false,
|
hideTopPanel: self.isReactionSearchActive,
|
||||||
hideTopPanelUpdated: { _, _ in }
|
hideTopPanelUpdated: { [weak self] hideTopPanel, transition in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.isReactionSearchActive = hideTopPanel
|
||||||
|
strongSelf.refreshLayout(transition: transition)
|
||||||
|
}
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: componentWidth, height: min(308.0, layout.size.height))
|
containerSize: CGSize(width: componentWidth, height: min(308.0, layout.size.height))
|
||||||
@ -1045,6 +1226,10 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
return self._ready
|
return self._ready
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public var overlayWantsToBeBelowKeyboard: Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?, destinationItemView: @escaping () -> UIView?) {
|
public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?, destinationItemView: @escaping () -> UIView?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|||||||
@ -29,14 +29,12 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache",
|
"//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache",
|
||||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
||||||
|
"//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent",
|
||||||
"//submodules/SoftwareVideo:SoftwareVideo",
|
"//submodules/SoftwareVideo:SoftwareVideo",
|
||||||
"//submodules/ShimmerEffect:ShimmerEffect",
|
"//submodules/ShimmerEffect:ShimmerEffect",
|
||||||
"//submodules/PhotoResources:PhotoResources",
|
"//submodules/PhotoResources:PhotoResources",
|
||||||
"//submodules/StickerResources:StickerResources",
|
"//submodules/StickerResources:StickerResources",
|
||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
#"//submodules/ContextUI:ContextUI",
|
|
||||||
#"//submodules/PremiumUI:PremiumUI",
|
|
||||||
#"//submodules/StickerPeekUI:StickerPeekUI",
|
|
||||||
"//submodules/UndoUI:UndoUI",
|
"//submodules/UndoUI:UndoUI",
|
||||||
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
|
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
|
||||||
"//submodules/Components/SolidRoundedButtonComponent:SolidRoundedButtonComponent",
|
"//submodules/Components/SolidRoundedButtonComponent:SolidRoundedButtonComponent",
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import UndoUI
|
|||||||
import AudioToolbox
|
import AudioToolbox
|
||||||
import SolidRoundedButtonComponent
|
import SolidRoundedButtonComponent
|
||||||
import EmojiTextAttachmentView
|
import EmojiTextAttachmentView
|
||||||
|
import EmojiStatusComponent
|
||||||
|
|
||||||
private let premiumBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white)
|
private let premiumBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white)
|
||||||
private let featuredBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeAdd"), color: .white)
|
private let featuredBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeAdd"), color: .white)
|
||||||
@ -1512,6 +1513,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
private struct Params: Equatable {
|
private struct Params: Equatable {
|
||||||
var theme: PresentationTheme
|
var theme: PresentationTheme
|
||||||
var strings: PresentationStrings
|
var strings: PresentationStrings
|
||||||
|
var text: String
|
||||||
var useOpaqueTheme: Bool
|
var useOpaqueTheme: Bool
|
||||||
var isActive: Bool
|
var isActive: Bool
|
||||||
var size: CGSize
|
var size: CGSize
|
||||||
@ -1523,6 +1525,9 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
if lhs.strings !== rhs.strings {
|
if lhs.strings !== rhs.strings {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.text != rhs.text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.useOpaqueTheme != rhs.useOpaqueTheme {
|
if lhs.useOpaqueTheme != rhs.useOpaqueTheme {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1733,13 +1738,14 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.params = nil
|
self.params = nil
|
||||||
self.update(theme: params.theme, strings: params.strings, useOpaqueTheme: params.useOpaqueTheme, isActive: params.isActive, size: params.size, transition: transition)
|
self.update(theme: params.theme, strings: params.strings, text: params.text, useOpaqueTheme: params.useOpaqueTheme, isActive: params.isActive, size: params.size, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(theme: PresentationTheme, strings: PresentationStrings, useOpaqueTheme: Bool, isActive: Bool, size: CGSize, transition: Transition) {
|
public func update(theme: PresentationTheme, strings: PresentationStrings, text: String, useOpaqueTheme: Bool, isActive: Bool, size: CGSize, transition: Transition) {
|
||||||
let params = Params(
|
let params = Params(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
strings: strings,
|
strings: strings,
|
||||||
|
text: text,
|
||||||
useOpaqueTheme: useOpaqueTheme,
|
useOpaqueTheme: useOpaqueTheme,
|
||||||
isActive: isActive,
|
isActive: isActive,
|
||||||
size: size
|
size: size
|
||||||
@ -1776,11 +1782,10 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
self.backgroundLayer.cornerRadius = inputHeight * 0.5
|
self.backgroundLayer.cornerRadius = inputHeight * 0.5
|
||||||
self.tintBackgroundLayer.cornerRadius = inputHeight * 0.5
|
self.tintBackgroundLayer.cornerRadius = inputHeight * 0.5
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let textSize = self.textView.update(
|
let textSize = self.textView.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(Text(
|
component: AnyComponent(Text(
|
||||||
text: "Search Reactions",
|
text: text,
|
||||||
font: Font.regular(17.0),
|
font: Font.regular(17.0),
|
||||||
color: theme.chat.inputMediaPanel.panelContentVibrantOverlayColor
|
color: theme.chat.inputMediaPanel.panelContentVibrantOverlayColor
|
||||||
)),
|
)),
|
||||||
@ -1790,7 +1795,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
let _ = self.tintTextView.update(
|
let _ = self.tintTextView.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(Text(
|
component: AnyComponent(Text(
|
||||||
text: "Search Reactions",
|
text: text,
|
||||||
font: Font.regular(17.0),
|
font: Font.regular(17.0),
|
||||||
color: .white
|
color: .white
|
||||||
)),
|
)),
|
||||||
@ -1892,6 +1897,99 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class EmptySearchResultsView: UIView {
|
||||||
|
override public static var layerClass: AnyClass {
|
||||||
|
return PassthroughLayer.self
|
||||||
|
}
|
||||||
|
|
||||||
|
let tintContainerView: UIView
|
||||||
|
let titleLabel: ComponentView<Empty>
|
||||||
|
let titleTintLabel: ComponentView<Empty>
|
||||||
|
let icon: ComponentView<Empty>
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.tintContainerView = UIView()
|
||||||
|
|
||||||
|
self.titleLabel = ComponentView()
|
||||||
|
self.titleTintLabel = ComponentView()
|
||||||
|
self.icon = ComponentView()
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
(self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerView.layer
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(context: AccountContext, theme: PresentationTheme, useOpaqueTheme: Bool, text: String, file: TelegramMediaFile?, size: CGSize, transition: Transition) {
|
||||||
|
let titleColor: UIColor
|
||||||
|
if useOpaqueTheme {
|
||||||
|
titleColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor
|
||||||
|
} else {
|
||||||
|
titleColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconSize: CGSize
|
||||||
|
if let file = file {
|
||||||
|
iconSize = self.icon.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(EmojiStatusComponent(
|
||||||
|
context: context,
|
||||||
|
animationCache: context.animationCache,
|
||||||
|
animationRenderer: context.animationRenderer,
|
||||||
|
content: .animation(content: .file(file: file), size: CGSize(width: 32.0, height: 32.0), placeholderColor: titleColor, themeColor: nil, loopMode: .forever),
|
||||||
|
isVisibleForAnimations: true,
|
||||||
|
action: nil
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 32.0, height: 32.0)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
iconSize = CGSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
let titleSize = self.titleLabel.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(Text(text: text, font: Font.regular(15.0), color: titleColor)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: size.width, height: 100.0)
|
||||||
|
)
|
||||||
|
let _ = self.titleTintLabel.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(Text(text: text, font: Font.regular(15.0), color: .white)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: size.width, height: 100.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let spacing: CGFloat = 4.0
|
||||||
|
let contentHeight = iconSize.height + spacing + titleSize.height
|
||||||
|
let contentOriginY = floor((size.height - contentHeight) / 2.0)
|
||||||
|
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: contentOriginY), size: iconSize)
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: iconFrame.maxY + spacing), size: titleSize)
|
||||||
|
|
||||||
|
if let iconView = self.icon.view {
|
||||||
|
if iconView.superview == nil {
|
||||||
|
self.addSubview(iconView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: iconView, frame: iconFrame)
|
||||||
|
}
|
||||||
|
if let titleLabelView = self.titleLabel.view {
|
||||||
|
if titleLabelView.superview == nil {
|
||||||
|
self.addSubview(titleLabelView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: titleLabelView, frame: titleFrame)
|
||||||
|
}
|
||||||
|
if let titleTintLabelView = self.titleTintLabel.view {
|
||||||
|
if titleTintLabelView.superview == nil {
|
||||||
|
self.tintContainerView.addSubview(titleTintLabelView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: titleTintLabelView, frame: titleFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public protocol EmojiContentPeekBehavior: AnyObject {
|
public protocol EmojiContentPeekBehavior: AnyObject {
|
||||||
func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, EmojiPagerContentComponent.View.ItemLayer, TelegramMediaFile)?)
|
func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, EmojiPagerContentComponent.View.ItemLayer, TelegramMediaFile)?)
|
||||||
}
|
}
|
||||||
@ -2211,6 +2309,26 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
case detailed
|
case detailed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class EmptySearchResults: Equatable {
|
||||||
|
public let text: String
|
||||||
|
public let iconFile: TelegramMediaFile?
|
||||||
|
|
||||||
|
public init(text: String, iconFile: TelegramMediaFile?) {
|
||||||
|
self.text = text
|
||||||
|
self.iconFile = iconFile
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: EmptySearchResults, rhs: EmptySearchResults) -> Bool {
|
||||||
|
if lhs.text != rhs.text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.iconFile?.fileId != rhs.iconFile?.fileId {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public let id: AnyHashable
|
public let id: AnyHashable
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let avatarPeer: EnginePeer?
|
public let avatarPeer: EnginePeer?
|
||||||
@ -2221,7 +2339,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
public let itemLayoutType: ItemLayoutType
|
public let itemLayoutType: ItemLayoutType
|
||||||
public let itemContentUniqueId: AnyHashable?
|
public let itemContentUniqueId: AnyHashable?
|
||||||
public let warpContentsOnEdges: Bool
|
public let warpContentsOnEdges: Bool
|
||||||
public let displaySearch: Bool
|
public let displaySearchWithPlaceholder: String?
|
||||||
|
public let emptySearchResults: EmptySearchResults?
|
||||||
public let enableLongPress: Bool
|
public let enableLongPress: Bool
|
||||||
public let selectedItems: Set<MediaId>
|
public let selectedItems: Set<MediaId>
|
||||||
|
|
||||||
@ -2236,7 +2355,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemLayoutType: ItemLayoutType,
|
itemLayoutType: ItemLayoutType,
|
||||||
itemContentUniqueId: AnyHashable?,
|
itemContentUniqueId: AnyHashable?,
|
||||||
warpContentsOnEdges: Bool,
|
warpContentsOnEdges: Bool,
|
||||||
displaySearch: Bool,
|
displaySearchWithPlaceholder: String?,
|
||||||
|
emptySearchResults: EmptySearchResults?,
|
||||||
enableLongPress: Bool,
|
enableLongPress: Bool,
|
||||||
selectedItems: Set<MediaId>
|
selectedItems: Set<MediaId>
|
||||||
) {
|
) {
|
||||||
@ -2250,12 +2370,13 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
self.itemLayoutType = itemLayoutType
|
self.itemLayoutType = itemLayoutType
|
||||||
self.itemContentUniqueId = itemContentUniqueId
|
self.itemContentUniqueId = itemContentUniqueId
|
||||||
self.warpContentsOnEdges = warpContentsOnEdges
|
self.warpContentsOnEdges = warpContentsOnEdges
|
||||||
self.displaySearch = displaySearch
|
self.displaySearchWithPlaceholder = displaySearchWithPlaceholder
|
||||||
|
self.emptySearchResults = emptySearchResults
|
||||||
self.enableLongPress = enableLongPress
|
self.enableLongPress = enableLongPress
|
||||||
self.selectedItems = selectedItems
|
self.selectedItems = selectedItems
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedItemGroups(itemGroups: [ItemGroup], itemContentUniqueId: AnyHashable?) -> EmojiPagerContentComponent {
|
public func withUpdatedItemGroups(itemGroups: [ItemGroup], itemContentUniqueId: AnyHashable?, emptySearchResults: EmptySearchResults?) -> EmojiPagerContentComponent {
|
||||||
return EmojiPagerContentComponent(
|
return EmojiPagerContentComponent(
|
||||||
id: self.id,
|
id: self.id,
|
||||||
context: self.context,
|
context: self.context,
|
||||||
@ -2267,7 +2388,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemLayoutType: self.itemLayoutType,
|
itemLayoutType: self.itemLayoutType,
|
||||||
itemContentUniqueId: itemContentUniqueId,
|
itemContentUniqueId: itemContentUniqueId,
|
||||||
warpContentsOnEdges: self.warpContentsOnEdges,
|
warpContentsOnEdges: self.warpContentsOnEdges,
|
||||||
displaySearch: self.displaySearch,
|
displaySearchWithPlaceholder: self.displaySearchWithPlaceholder,
|
||||||
|
emptySearchResults: emptySearchResults,
|
||||||
enableLongPress: self.enableLongPress,
|
enableLongPress: self.enableLongPress,
|
||||||
selectedItems: self.selectedItems
|
selectedItems: self.selectedItems
|
||||||
)
|
)
|
||||||
@ -2304,7 +2426,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
if lhs.warpContentsOnEdges != rhs.warpContentsOnEdges {
|
if lhs.warpContentsOnEdges != rhs.warpContentsOnEdges {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.displaySearch != rhs.displaySearch {
|
if lhs.displaySearchWithPlaceholder != rhs.displaySearchWithPlaceholder {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.emptySearchResults != rhs.emptySearchResults {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.enableLongPress != rhs.enableLongPress {
|
if lhs.enableLongPress != rhs.enableLongPress {
|
||||||
@ -3079,6 +3204,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
private let placeholdersContainerView: UIView
|
private let placeholdersContainerView: UIView
|
||||||
private var visibleSearchHeader: EmojiSearchHeaderView?
|
private var visibleSearchHeader: EmojiSearchHeaderView?
|
||||||
|
private var visibleEmptySearchResultsView: EmptySearchResultsView?
|
||||||
private var visibleItemPlaceholderViews: [ItemLayer.Key: ItemPlaceholderView] = [:]
|
private var visibleItemPlaceholderViews: [ItemLayer.Key: ItemPlaceholderView] = [:]
|
||||||
private var visibleItemSelectionLayers: [ItemLayer.Key: ItemSelectionLayer] = [:]
|
private var visibleItemSelectionLayers: [ItemLayer.Key: ItemSelectionLayer] = [:]
|
||||||
private var visibleItemLayers: [ItemLayer.Key: ItemLayer] = [:]
|
private var visibleItemLayers: [ItemLayer.Key: ItemLayer] = [:]
|
||||||
@ -5703,7 +5829,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemGroups: itemGroups,
|
itemGroups: itemGroups,
|
||||||
expandedGroupIds: self.expandedGroupIds,
|
expandedGroupIds: self.expandedGroupIds,
|
||||||
curveNearBounds: component.warpContentsOnEdges,
|
curveNearBounds: component.warpContentsOnEdges,
|
||||||
displaySearch: component.displaySearch,
|
displaySearch: component.displaySearchWithPlaceholder != nil,
|
||||||
isSearchActivated: self.isSearchActivated,
|
isSearchActivated: self.isSearchActivated,
|
||||||
customLayout: component.inputInteractionHolder.inputInteraction?.customLayout
|
customLayout: component.inputInteractionHolder.inputInteraction?.customLayout
|
||||||
)
|
)
|
||||||
@ -5730,7 +5856,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
transition.setPosition(view: self.scrollView, position: CGPoint(x: 0.0, y: scrollOriginY))
|
transition.setPosition(view: self.scrollView, position: CGPoint(x: 0.0, y: scrollOriginY))
|
||||||
let previousSize = self.scrollView.bounds.size
|
let previousSize = self.scrollView.bounds.size
|
||||||
var resetScrolling = false
|
var resetScrolling = false
|
||||||
if self.scrollView.bounds.isEmpty && component.displaySearch {
|
if self.scrollView.bounds.isEmpty && component.displaySearchWithPlaceholder != nil {
|
||||||
resetScrolling = true
|
resetScrolling = true
|
||||||
}
|
}
|
||||||
if previousComponent?.itemContentUniqueId != component.itemContentUniqueId {
|
if previousComponent?.itemContentUniqueId != component.itemContentUniqueId {
|
||||||
@ -5829,7 +5955,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resetScrolling {
|
if resetScrolling {
|
||||||
if component.displaySearch && !self.isSearchActivated {
|
if component.displaySearchWithPlaceholder != nil && !self.isSearchActivated {
|
||||||
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 50.0), size: scrollSize)
|
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 50.0), size: scrollSize)
|
||||||
} else {
|
} else {
|
||||||
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize)
|
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize)
|
||||||
@ -5879,7 +6005,9 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if component.displaySearch {
|
let useOpaqueTheme = component.inputInteractionHolder.inputInteraction?.useOpaqueTheme ?? false
|
||||||
|
|
||||||
|
if let displaySearchWithPlaceholder = component.displaySearchWithPlaceholder {
|
||||||
let visibleSearchHeader: EmojiSearchHeaderView
|
let visibleSearchHeader: EmojiSearchHeaderView
|
||||||
if let current = self.visibleSearchHeader {
|
if let current = self.visibleSearchHeader {
|
||||||
visibleSearchHeader = current
|
visibleSearchHeader = current
|
||||||
@ -5924,12 +6052,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let useOpaqueTheme = component.inputInteractionHolder.inputInteraction?.useOpaqueTheme ?? false
|
|
||||||
|
|
||||||
let searchHeaderFrame = CGRect(origin: CGPoint(x: itemLayout.searchInsets.left, y: itemLayout.searchInsets.top), size: CGSize(width: itemLayout.width - itemLayout.searchInsets.left - itemLayout.searchInsets.right, height: itemLayout.searchHeight))
|
let searchHeaderFrame = CGRect(origin: CGPoint(x: itemLayout.searchInsets.left, y: itemLayout.searchInsets.top), size: CGSize(width: itemLayout.width - itemLayout.searchInsets.left - itemLayout.searchInsets.right, height: itemLayout.searchHeight))
|
||||||
visibleSearchHeader.update(theme: keyboardChildEnvironment.theme, strings: keyboardChildEnvironment.strings, useOpaqueTheme: useOpaqueTheme, isActive: self.isSearchActivated, size: searchHeaderFrame.size, transition: transition)
|
visibleSearchHeader.update(theme: keyboardChildEnvironment.theme, strings: keyboardChildEnvironment.strings, text: displaySearchWithPlaceholder, useOpaqueTheme: useOpaqueTheme, isActive: self.isSearchActivated, size: searchHeaderFrame.size, transition: transition)
|
||||||
transition.setFrame(view: visibleSearchHeader, frame: searchHeaderFrame, completion: { [weak self] _ in
|
transition.setFrame(view: visibleSearchHeader, frame: searchHeaderFrame, completion: { [weak self] completed in
|
||||||
guard let strongSelf = self, let visibleSearchHeader = strongSelf.visibleSearchHeader else {
|
guard let strongSelf = self, completed, let visibleSearchHeader = strongSelf.visibleSearchHeader else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5946,6 +6072,37 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let emptySearchResults = component.emptySearchResults {
|
||||||
|
let visibleEmptySearchResultsView: EmptySearchResultsView
|
||||||
|
var emptySearchResultsTransition = transition
|
||||||
|
if let current = self.visibleEmptySearchResultsView {
|
||||||
|
visibleEmptySearchResultsView = current
|
||||||
|
} else {
|
||||||
|
emptySearchResultsTransition = .immediate
|
||||||
|
visibleEmptySearchResultsView = EmptySearchResultsView(frame: CGRect())
|
||||||
|
self.visibleEmptySearchResultsView = visibleEmptySearchResultsView
|
||||||
|
self.addSubview(visibleEmptySearchResultsView)
|
||||||
|
self.mirrorContentClippingView?.addSubview(visibleEmptySearchResultsView.tintContainerView)
|
||||||
|
}
|
||||||
|
let emptySearchResultsSize = CGSize(width: availableSize.width, height: availableSize.height - itemLayout.searchInsets.top - itemLayout.searchHeight)
|
||||||
|
visibleEmptySearchResultsView.update(
|
||||||
|
context: component.context,
|
||||||
|
theme: keyboardChildEnvironment.theme,
|
||||||
|
useOpaqueTheme: useOpaqueTheme,
|
||||||
|
text: emptySearchResults.text,
|
||||||
|
file: emptySearchResults.iconFile,
|
||||||
|
size: emptySearchResultsSize,
|
||||||
|
transition: emptySearchResultsTransition
|
||||||
|
)
|
||||||
|
emptySearchResultsTransition.setFrame(view: visibleEmptySearchResultsView, frame: CGRect(origin: CGPoint(x: 0.0, y: itemLayout.searchInsets.top + itemLayout.searchHeight), size: emptySearchResultsSize))
|
||||||
|
} else {
|
||||||
|
if let visibleEmptySearchResultsView = self.visibleEmptySearchResultsView {
|
||||||
|
self.visibleEmptySearchResultsView = nil
|
||||||
|
visibleEmptySearchResultsView.removeFromSuperview()
|
||||||
|
visibleEmptySearchResultsView.tintContainerView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.updateVisibleItems(transition: itemTransition, attemptSynchronousLoads: attemptSynchronousLoads, previousItemPositions: previousItemPositions, previousAbsoluteItemPositions: previousAbsoluteItemPositions, updatedItemPositions: updatedItemPositions, hintDisappearingGroupFrame: hintDisappearingGroupFrame)
|
self.updateVisibleItems(transition: itemTransition, attemptSynchronousLoads: attemptSynchronousLoads, previousItemPositions: previousItemPositions, previousAbsoluteItemPositions: previousAbsoluteItemPositions, updatedItemPositions: updatedItemPositions, hintDisappearingGroupFrame: hintDisappearingGroupFrame)
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
@ -6623,6 +6780,13 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var displaySearchWithPlaceholder: String?
|
||||||
|
if isReactionSelection {
|
||||||
|
displaySearchWithPlaceholder = "Search Reactions"
|
||||||
|
} else if isStatusSelection {
|
||||||
|
displaySearchWithPlaceholder = "Search Statuses"
|
||||||
|
}
|
||||||
|
|
||||||
return EmojiPagerContentComponent(
|
return EmojiPagerContentComponent(
|
||||||
id: "emoji",
|
id: "emoji",
|
||||||
context: context,
|
context: context,
|
||||||
@ -6667,7 +6831,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemLayoutType: .compact,
|
itemLayoutType: .compact,
|
||||||
itemContentUniqueId: nil,
|
itemContentUniqueId: nil,
|
||||||
warpContentsOnEdges: isReactionSelection || isStatusSelection,
|
warpContentsOnEdges: isReactionSelection || isStatusSelection,
|
||||||
displaySearch: isReactionSelection,
|
displaySearchWithPlaceholder: displaySearchWithPlaceholder,
|
||||||
|
emptySearchResults: nil,
|
||||||
enableLongPress: (isReactionSelection && !isQuickReactionSelection) || isStatusSelection,
|
enableLongPress: (isReactionSelection && !isQuickReactionSelection) || isStatusSelection,
|
||||||
selectedItems: selectedItems
|
selectedItems: selectedItems
|
||||||
)
|
)
|
||||||
|
|||||||
@ -537,7 +537,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
itemLayoutType: .detailed,
|
itemLayoutType: .detailed,
|
||||||
itemContentUniqueId: nil,
|
itemContentUniqueId: nil,
|
||||||
warpContentsOnEdges: false,
|
warpContentsOnEdges: false,
|
||||||
displaySearch: false,
|
displaySearchWithPlaceholder: nil,
|
||||||
|
emptySearchResults: nil,
|
||||||
enableLongPress: false,
|
enableLongPress: false,
|
||||||
selectedItems: Set()
|
selectedItems: Set()
|
||||||
)
|
)
|
||||||
@ -1714,9 +1715,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
|
|
||||||
private func processInputData(inputData: InputData) -> InputData {
|
private func processInputData(inputData: InputData) -> InputData {
|
||||||
return InputData(
|
return InputData(
|
||||||
emoji: inputData.emoji.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.itemGroups), itemContentUniqueId: nil),
|
emoji: inputData.emoji.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.itemGroups), itemContentUniqueId: nil, emptySearchResults: nil),
|
||||||
stickers: inputData.stickers.flatMap { stickers in
|
stickers: inputData.stickers.flatMap { stickers in
|
||||||
return stickers.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.itemGroups), itemContentUniqueId: nil)
|
return stickers.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.itemGroups), itemContentUniqueId: nil, emptySearchResults: nil)
|
||||||
},
|
},
|
||||||
gifs: inputData.gifs,
|
gifs: inputData.gifs,
|
||||||
availableGifSearchEmojies: inputData.availableGifSearchEmojies
|
availableGifSearchEmojies: inputData.availableGifSearchEmojies
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user