Added emoji keyword search

This commit is contained in:
Ilya Laktyushin 2019-03-18 17:35:40 +03:00
parent ebc595a62f
commit 8e1b8843b3
9 changed files with 88 additions and 27 deletions

View File

@ -388,7 +388,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
updatedPlayerStatusSignal = videoNode.status
|> mapToSignal { status -> Signal<MediaPlayerStatus?, NoError> in
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 {
return .single(status)
}

View File

@ -1005,7 +1005,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
} else if isSecretMedia, let secretProgressIcon = secretProgressIcon {
state = .customIcon(secretProgressIcon)
} 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 {
state = .play(bubbleTheme.mediaOverlayControlForegroundColor)
} else {
@ -1069,11 +1069,13 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
}
if let statusNode = self.statusNode {
if state == .none {
var removeStatusNode = false
if statusNode.state != .none && state == .none {
self.statusNode = nil
removeStatusNode = true
}
statusNode.transitionToState(state, completion: { [weak statusNode] in
if state == .none {
if removeStatusNode {
statusNode?.removeFromSupernode()
}
})
@ -1217,6 +1219,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
var actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd = .loopDisablingSound
if let message = self.message, message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
actionAtEnd = .loop
} else {
actionAtEnd = .repeatIfNeeded
}
if let videoNode = self.videoNode, let context = self.context, (self.automaticPlayback ?? false) && !isAnimated {

View File

@ -72,7 +72,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
self.searchDisposable.dispose()
}
func updateText(_ text: String) {
func updateText(_ text: String, languageCode: String?) {
let signal: Signal<[FileMediaReference]?, NoError>
if !text.isEmpty {
signal = self.signalForQuery(text)
@ -133,7 +133,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
}
if firstLayout {
self.updateText("")
self.updateText("", languageCode: nil)
}
}

View File

@ -52,6 +52,7 @@ enum MediaPlayerPlayOnceWithSoundActionAtEnd {
case loop
case loopDisablingSound
case stop
case repeatIfNeeded
}
enum MediaPlayerPlayOnceWithSoundSeek {

View File

@ -275,7 +275,27 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
self.player.actionAtEnd = .loopDisablingSound(action)
case .stop:
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)
}
@ -298,7 +318,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
switch actionAtEnd {
case .loop:
self.player.actionAtEnd = .loop({})
case .loopDisablingSound:
case .loopDisablingSound, .repeatIfNeeded:
self.player.actionAtEnd = .loopDisablingSound(action)
case .stop:
self.player.actionAtEnd = .action(action)

View File

@ -121,7 +121,7 @@ private class PaneSearchBarTextField: UITextField {
class PaneSearchBarNode: ASDisplayNode, UITextFieldDelegate {
var cancel: (() -> Void)?
var textUpdated: ((String) -> Void)?
var textUpdated: ((String, String) -> Void)?
var clearPrefix: (() -> Void)?
private let backgroundNode: ASDisplayNode
@ -446,7 +446,7 @@ class PaneSearchBarNode: ASDisplayNode, UITextFieldDelegate {
@objc func textFieldDidChange(_ textField: UITextField) {
self.updateIsEmpty()
if let textUpdated = self.textUpdated {
textUpdated(textField.text ?? "")
textUpdated(textField.text ?? "", self.textField.textInputMode?.primaryLanguage ?? "")
}
}

View File

@ -13,7 +13,7 @@ protocol PaneSearchContentNode {
var updateActivity: ((Bool) -> Void)? { get set }
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 animateIn(additivePosition: CGFloat, transition: ContainedViewLayoutTransition)
@ -74,8 +74,8 @@ final class PaneSearchContainerNode: ASDisplayNode {
}
self.searchBar.activate()
self.searchBar.textUpdated = { [weak self] text in
self?.contentNode.updateText(text)
self.searchBar.textUpdated = { [weak self] text, languageCode in
self?.contentNode.updateText(text, languageCode: languageCode)
}
self.updateThemeAndStrings(theme: theme, strings: strings)

View File

@ -207,9 +207,14 @@ public final class RadialStatusNode: ASControlNode {
contentNode.frame = self.bounds
contentNode.prepareAnimateIn(from: nil)
self.addSubnode(contentNode)
if animated, case .check = state, self.isNodeLoaded {
if animated, self.isNodeLoaded {
switch state {
case .check, .progress:
contentNode.layout()
contentNode.animateIn(from: fromState, delay: 0.0)
default:
break
}
}
}
self.transitionToBackgroundColor(backgroundColor, previousContentNode: nil, animated: animated, completion: completion)

View File

@ -151,6 +151,11 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
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 queue = Queue()
@ -250,34 +255,60 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
self._ready.set(self.trendingPane.ready)
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)
}
deinit {
self.languageCodeDisposable?.dispose()
self.searchDisposable.dispose()
}
func updateText(_ text: String) {
func updateText(_ text: String, languageCode: String?) {
let signal: Signal<([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError>
if !text.isEmpty {
if let languageCode = languageCode, !languageCode.isEmpty {
self.languageCodePromise.set(languageCode)
}
let stickers: Signal<[(String?, FoundStickerItem)], NoError> = Signal { subscriber in
var signals: Signal<[Signal<(String?, [FoundStickerItem]), NoError>], NoError> = .single([])
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmed.isSingleEmoji {
signals = .single([searchStickers(account: self.context.account, query: text.firstEmoji)
|> take(1)
|> map { (nil, $0) }])
} else if trimmed.count > 1 {
signals = self.emojiKeywordsPromise.get()
|> mapToSignal { keywords in
if let keywords = keywords {
return searchEmojiKeywords(keywords: keywords, query: trimmed.lowercased(), completeMatch: true)
|> map { result -> [Signal<(String?, [FoundStickerItem]), NoError>] in
var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = []
if text.trimmingCharacters(in: .whitespacesAndNewlines).isSingleEmoji {
signals.append(searchStickers(account: self.context.account, query: text.firstEmoji)
for emoji in result {
signals.append(searchStickers(account: self.context.account, query: emoji)
|> take(1)
|> map { (nil, $0) })
|> map { (emoji, $0) })
}
return signals
}
} else {
for entry in TGEmojiSuggestions.suggestions(forQuery: text.lowercased()) {
if let entry = entry as? TGAlphacodeEntry {
signals.append(searchStickers(account: self.context.account, query: entry.emoji)
|> take(1)
|> map { (entry.emoji, $0) })
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)] = []
for (emoji, stickers) in results {
for sticker in stickers {