mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
Added emoji keyword search
This commit is contained in:
parent
ebc595a62f
commit
8e1b8843b3
@ -388,7 +388,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
updatedPlayerStatusSignal = videoNode.status
|
updatedPlayerStatusSignal = videoNode.status
|
||||||
|> mapToSignal { status -> Signal<MediaPlayerStatus?, NoError> in
|
|> mapToSignal { status -> Signal<MediaPlayerStatus?, NoError> in
|
||||||
if let status = status, case .buffering = status.status {
|
if let status = status, case .buffering = status.status {
|
||||||
return .single(status) |> delay(1.0, queue: Queue.mainQueue())
|
return .single(status) |> delay(0.5, queue: Queue.mainQueue())
|
||||||
} else {
|
} else {
|
||||||
return .single(status)
|
return .single(status)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1005,7 +1005,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
} else if isSecretMedia, let secretProgressIcon = secretProgressIcon {
|
} else if isSecretMedia, let secretProgressIcon = secretProgressIcon {
|
||||||
state = .customIcon(secretProgressIcon)
|
state = .customIcon(secretProgressIcon)
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
let isInlinePlayableVideo = file.isVideo && !isSecretMedia && automaticPlayback
|
let isInlinePlayableVideo = file.isVideo && !isSecretMedia && (self.automaticPlayback ?? false)
|
||||||
if !isInlinePlayableVideo && file.isVideo {
|
if !isInlinePlayableVideo && file.isVideo {
|
||||||
state = .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
state = .play(bubbleTheme.mediaOverlayControlForegroundColor)
|
||||||
} else {
|
} else {
|
||||||
@ -1069,11 +1069,13 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let statusNode = self.statusNode {
|
if let statusNode = self.statusNode {
|
||||||
if state == .none {
|
var removeStatusNode = false
|
||||||
|
if statusNode.state != .none && state == .none {
|
||||||
self.statusNode = nil
|
self.statusNode = nil
|
||||||
|
removeStatusNode = true
|
||||||
}
|
}
|
||||||
statusNode.transitionToState(state, completion: { [weak statusNode] in
|
statusNode.transitionToState(state, completion: { [weak statusNode] in
|
||||||
if state == .none {
|
if removeStatusNode {
|
||||||
statusNode?.removeFromSupernode()
|
statusNode?.removeFromSupernode()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1217,6 +1219,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
var actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd = .loopDisablingSound
|
var actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd = .loopDisablingSound
|
||||||
if let message = self.message, message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
if let message = self.message, message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
actionAtEnd = .loop
|
actionAtEnd = .loop
|
||||||
|
} else {
|
||||||
|
actionAtEnd = .repeatIfNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
if let videoNode = self.videoNode, let context = self.context, (self.automaticPlayback ?? false) && !isAnimated {
|
if let videoNode = self.videoNode, let context = self.context, (self.automaticPlayback ?? false) && !isAnimated {
|
||||||
|
|||||||
@ -72,7 +72,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
|
|||||||
self.searchDisposable.dispose()
|
self.searchDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateText(_ text: String) {
|
func updateText(_ text: String, languageCode: String?) {
|
||||||
let signal: Signal<[FileMediaReference]?, NoError>
|
let signal: Signal<[FileMediaReference]?, NoError>
|
||||||
if !text.isEmpty {
|
if !text.isEmpty {
|
||||||
signal = self.signalForQuery(text)
|
signal = self.signalForQuery(text)
|
||||||
@ -133,7 +133,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if firstLayout {
|
if firstLayout {
|
||||||
self.updateText("")
|
self.updateText("", languageCode: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ enum MediaPlayerPlayOnceWithSoundActionAtEnd {
|
|||||||
case loop
|
case loop
|
||||||
case loopDisablingSound
|
case loopDisablingSound
|
||||||
case stop
|
case stop
|
||||||
|
case repeatIfNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaPlayerPlayOnceWithSoundSeek {
|
enum MediaPlayerPlayOnceWithSoundSeek {
|
||||||
|
|||||||
@ -275,7 +275,27 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
self.player.actionAtEnd = .loopDisablingSound(action)
|
self.player.actionAtEnd = .loopDisablingSound(action)
|
||||||
case .stop:
|
case .stop:
|
||||||
self.player.actionAtEnd = .action(action)
|
self.player.actionAtEnd = .action(action)
|
||||||
|
case .repeatIfNeeded:
|
||||||
|
let _ = (self.player.status
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
|> take(1)).start(next: { [weak self] status in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if status.timestamp > status.duration * 0.1 {
|
||||||
|
strongSelf.player.actionAtEnd = .action({ [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.player.seek(timestamp: 0.0)
|
||||||
|
strongSelf.player.actionAtEnd = .loopDisablingSound(action)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
strongSelf.player.actionAtEnd = .loopDisablingSound(action)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
self.player.playOnceWithSound(playAndRecord: playAndRecord, seekToStart: seekToStart)
|
self.player.playOnceWithSound(playAndRecord: playAndRecord, seekToStart: seekToStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +318,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
switch actionAtEnd {
|
switch actionAtEnd {
|
||||||
case .loop:
|
case .loop:
|
||||||
self.player.actionAtEnd = .loop({})
|
self.player.actionAtEnd = .loop({})
|
||||||
case .loopDisablingSound:
|
case .loopDisablingSound, .repeatIfNeeded:
|
||||||
self.player.actionAtEnd = .loopDisablingSound(action)
|
self.player.actionAtEnd = .loopDisablingSound(action)
|
||||||
case .stop:
|
case .stop:
|
||||||
self.player.actionAtEnd = .action(action)
|
self.player.actionAtEnd = .action(action)
|
||||||
|
|||||||
@ -121,7 +121,7 @@ private class PaneSearchBarTextField: UITextField {
|
|||||||
|
|
||||||
class PaneSearchBarNode: ASDisplayNode, UITextFieldDelegate {
|
class PaneSearchBarNode: ASDisplayNode, UITextFieldDelegate {
|
||||||
var cancel: (() -> Void)?
|
var cancel: (() -> Void)?
|
||||||
var textUpdated: ((String) -> Void)?
|
var textUpdated: ((String, String) -> Void)?
|
||||||
var clearPrefix: (() -> Void)?
|
var clearPrefix: (() -> Void)?
|
||||||
|
|
||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
@ -446,7 +446,7 @@ class PaneSearchBarNode: ASDisplayNode, UITextFieldDelegate {
|
|||||||
@objc func textFieldDidChange(_ textField: UITextField) {
|
@objc func textFieldDidChange(_ textField: UITextField) {
|
||||||
self.updateIsEmpty()
|
self.updateIsEmpty()
|
||||||
if let textUpdated = self.textUpdated {
|
if let textUpdated = self.textUpdated {
|
||||||
textUpdated(textField.text ?? "")
|
textUpdated(textField.text ?? "", self.textField.textInputMode?.primaryLanguage ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ protocol PaneSearchContentNode {
|
|||||||
var updateActivity: ((Bool) -> Void)? { get set }
|
var updateActivity: ((Bool) -> Void)? { get set }
|
||||||
|
|
||||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings)
|
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings)
|
||||||
func updateText(_ text: String)
|
func updateText(_ text: String, languageCode: String?)
|
||||||
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, transition: ContainedViewLayoutTransition)
|
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, transition: ContainedViewLayoutTransition)
|
||||||
|
|
||||||
func animateIn(additivePosition: CGFloat, transition: ContainedViewLayoutTransition)
|
func animateIn(additivePosition: CGFloat, transition: ContainedViewLayoutTransition)
|
||||||
@ -74,8 +74,8 @@ final class PaneSearchContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
self.searchBar.activate()
|
self.searchBar.activate()
|
||||||
|
|
||||||
self.searchBar.textUpdated = { [weak self] text in
|
self.searchBar.textUpdated = { [weak self] text, languageCode in
|
||||||
self?.contentNode.updateText(text)
|
self?.contentNode.updateText(text, languageCode: languageCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateThemeAndStrings(theme: theme, strings: strings)
|
self.updateThemeAndStrings(theme: theme, strings: strings)
|
||||||
|
|||||||
@ -207,9 +207,14 @@ public final class RadialStatusNode: ASControlNode {
|
|||||||
contentNode.frame = self.bounds
|
contentNode.frame = self.bounds
|
||||||
contentNode.prepareAnimateIn(from: nil)
|
contentNode.prepareAnimateIn(from: nil)
|
||||||
self.addSubnode(contentNode)
|
self.addSubnode(contentNode)
|
||||||
if animated, case .check = state, self.isNodeLoaded {
|
if animated, self.isNodeLoaded {
|
||||||
contentNode.layout()
|
switch state {
|
||||||
contentNode.animateIn(from: fromState, delay: 0.0)
|
case .check, .progress:
|
||||||
|
contentNode.layout()
|
||||||
|
contentNode.animateIn(from: fromState, delay: 0.0)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.transitionToBackgroundColor(backgroundColor, previousContentNode: nil, animated: animated, completion: completion)
|
self.transitionToBackgroundColor(backgroundColor, previousContentNode: nil, animated: animated, completion: completion)
|
||||||
|
|||||||
@ -151,6 +151,11 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
|
|||||||
|
|
||||||
private var enqueuedTransitions: [StickerPaneSearchGridTransition] = []
|
private var enqueuedTransitions: [StickerPaneSearchGridTransition] = []
|
||||||
|
|
||||||
|
private let languageCodePromise = ValuePromise<String>(ignoreRepeated: true)
|
||||||
|
private let emojiKeywordsPromise = Promise<EmojiKeywords?>()
|
||||||
|
|
||||||
|
private var languageCodeDisposable: Disposable?
|
||||||
|
|
||||||
private let searchDisposable = MetaDisposable()
|
private let searchDisposable = MetaDisposable()
|
||||||
|
|
||||||
private let queue = Queue()
|
private let queue = Queue()
|
||||||
@ -250,34 +255,60 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
|
|||||||
self._ready.set(self.trendingPane.ready)
|
self._ready.set(self.trendingPane.ready)
|
||||||
self.trendingPane.activate()
|
self.trendingPane.activate()
|
||||||
|
|
||||||
|
self.languageCodeDisposable = (self.languageCodePromise.get()
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] languageCode in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.emojiKeywordsPromise.set(emojiKeywords(accountManager: strongSelf.context.sharedContext.accountManager, network: strongSelf.context.account.network, inputLanguageCode: languageCode))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
self.updateThemeAndStrings(theme: theme, strings: strings)
|
self.updateThemeAndStrings(theme: theme, strings: strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
self.languageCodeDisposable?.dispose()
|
||||||
self.searchDisposable.dispose()
|
self.searchDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateText(_ text: String) {
|
func updateText(_ text: String, languageCode: String?) {
|
||||||
let signal: Signal<([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError>
|
let signal: Signal<([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError>
|
||||||
if !text.isEmpty {
|
if !text.isEmpty {
|
||||||
|
if let languageCode = languageCode, !languageCode.isEmpty {
|
||||||
|
self.languageCodePromise.set(languageCode)
|
||||||
|
}
|
||||||
|
|
||||||
let stickers: Signal<[(String?, FoundStickerItem)], NoError> = Signal { subscriber in
|
let stickers: Signal<[(String?, FoundStickerItem)], NoError> = Signal { subscriber in
|
||||||
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
|
var signals: Signal<[Signal<(String?, [FoundStickerItem]), NoError>], NoError> = .single([])
|
||||||
|
|
||||||
if text.trimmingCharacters(in: .whitespacesAndNewlines).isSingleEmoji {
|
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
signals.append(searchStickers(account: self.context.account, query: text.firstEmoji)
|
if trimmed.isSingleEmoji {
|
||||||
|
signals = .single([searchStickers(account: self.context.account, query: text.firstEmoji)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { (nil, $0) })
|
|> map { (nil, $0) }])
|
||||||
} else {
|
} else if trimmed.count > 1 {
|
||||||
for entry in TGEmojiSuggestions.suggestions(forQuery: text.lowercased()) {
|
signals = self.emojiKeywordsPromise.get()
|
||||||
if let entry = entry as? TGAlphacodeEntry {
|
|> mapToSignal { keywords in
|
||||||
signals.append(searchStickers(account: self.context.account, query: entry.emoji)
|
if let keywords = keywords {
|
||||||
|> take(1)
|
return searchEmojiKeywords(keywords: keywords, query: trimmed.lowercased(), completeMatch: true)
|
||||||
|> map { (entry.emoji, $0) })
|
|> map { result -> [Signal<(String?, [FoundStickerItem]), NoError>] in
|
||||||
|
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
|
||||||
|
for emoji in result {
|
||||||
|
signals.append(searchStickers(account: self.context.account, query: emoji)
|
||||||
|
|> take(1)
|
||||||
|
|> map { (emoji, $0) })
|
||||||
|
}
|
||||||
|
return signals
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return combineLatest(signals).start(next: { results in
|
return (signals
|
||||||
|
|> mapToSignal { signals in
|
||||||
|
return combineLatest(signals)
|
||||||
|
}).start(next: { results in
|
||||||
var result: [(String?, FoundStickerItem)] = []
|
var result: [(String?, FoundStickerItem)] = []
|
||||||
for (emoji, stickers) in results {
|
for (emoji, stickers) in results {
|
||||||
for sticker in stickers {
|
for sticker in stickers {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user