Peter 5c1613d104 Add 'submodules/TelegramCore/' from commit '9561227540acef69894e6546395ab223a6233600'
git-subtree-dir: submodules/TelegramCore
git-subtree-mainline: 971273e8f8f49a47f14b251d2f35e3445a61fc3f
git-subtree-split: 9561227540acef69894e6546395ab223a6233600
2019-06-11 18:59:08 +01:00

189 lines
8.0 KiB
Swift

import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
#else
import Postbox
import SwiftSignalKit
#endif
func emojiKeywordColletionIdForCode(_ code: String) -> ItemCollectionId {
return ItemCollectionId(namespace: Namespaces.ItemCollection.EmojiKeywords, id: Int64(HashFunctions.murMurHash32(code)))
}
public final class EmojiKeywordCollectionInfo: ItemCollectionInfo, Equatable {
public let id: ItemCollectionId
public let languageCode: String
public let inputLanguageCode: String
public let version: Int32
public let timestamp: Int32
public init(languageCode: String, inputLanguageCode: String, version: Int32, timestamp: Int32) {
self.id = emojiKeywordColletionIdForCode(inputLanguageCode)
self.languageCode = languageCode
self.inputLanguageCode = inputLanguageCode
self.version = version
self.timestamp = timestamp
}
public init(decoder: PostboxDecoder) {
self.id = ItemCollectionId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0))
self.languageCode = decoder.decodeStringForKey("lc", orElse: "")
self.inputLanguageCode = decoder.decodeStringForKey("ilc", orElse: "")
self.version = decoder.decodeInt32ForKey("v", orElse: 0)
self.timestamp = decoder.decodeInt32ForKey("t", orElse: 0)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.id.namespace, forKey: "i.n")
encoder.encodeInt64(self.id.id, forKey: "i.i")
encoder.encodeString(self.languageCode, forKey: "lc")
encoder.encodeString(self.inputLanguageCode, forKey: "ilc")
encoder.encodeInt32(self.version, forKey: "v")
encoder.encodeInt32(self.timestamp, forKey: "t")
}
public static func ==(lhs: EmojiKeywordCollectionInfo, rhs: EmojiKeywordCollectionInfo) -> Bool {
if lhs.id != rhs.id {
return false
}
if lhs.languageCode != rhs.languageCode {
return false
}
if lhs.inputLanguageCode != rhs.inputLanguageCode {
return false
}
if lhs.version != rhs.version {
return false
}
if lhs.timestamp != rhs.timestamp {
return false
}
return true
}
}
public final class EmojiKeywordItem: ItemCollectionItem, Equatable {
public let index: ItemCollectionItemIndex
public let collectionId: ItemCollectionId.Id
public let keyword: String
public let emoticons: [String]
public let indexKeys: [MemoryBuffer]
public init(index: ItemCollectionItemIndex, collectionId: ItemCollectionId.Id, keyword: String, emoticons: [String], indexKeys: [MemoryBuffer]) {
self.index = index
self.collectionId = collectionId
self.keyword = keyword
self.emoticons = emoticons
self.indexKeys = indexKeys
}
public init(decoder: PostboxDecoder) {
self.index = ItemCollectionItemIndex(index: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0))
self.collectionId = decoder.decodeInt64ForKey("c", orElse: 0)
self.keyword = decoder.decodeStringForKey("k", orElse: "")
self.emoticons = decoder.decodeStringArrayForKey("e")
self.indexKeys = decoder.decodeBytesArrayForKey("s")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.index.index, forKey: "i.n")
encoder.encodeInt64(self.index.id, forKey: "i.i")
encoder.encodeInt64(self.collectionId, forKey: "c")
encoder.encodeString(self.keyword, forKey: "k")
encoder.encodeStringArray(self.emoticons, forKey: "e")
encoder.encodeBytesArray(self.indexKeys, forKey: "s")
}
public static func ==(lhs: EmojiKeywordItem, rhs: EmojiKeywordItem) -> Bool {
return lhs.index == rhs.index && lhs.collectionId == rhs.collectionId && lhs.keyword == rhs.keyword && lhs.emoticons == rhs.emoticons && lhs.indexKeys == rhs.indexKeys
}
}
private let refreshTimeout: Int32 = 60 * 60
private enum SearchEmojiKeywordsIntermediateResult {
case updating(timestamp: Int32?)
case completed([EmojiKeywordItem])
}
public func searchEmojiKeywords(postbox: Postbox, inputLanguageCode: String, query: String, completeMatch: Bool) -> Signal<[EmojiKeywordItem], NoError> {
guard !query.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
return .single([])
}
let collectionId = emojiKeywordColletionIdForCode(inputLanguageCode)
let search: (Transaction) -> [EmojiKeywordItem] = { transaction in
let queryTokens = stringIndexTokens(query, transliteration: .none)
if let firstQueryToken = queryTokens.first {
let query: ItemCollectionSearchQuery = completeMatch ? .exact(firstQueryToken) : .matching(queryTokens)
let items = transaction.searchItemCollection(namespace: Namespaces.ItemCollection.EmojiKeywords, query: query).filter { item -> Bool in
if let item = item as? EmojiKeywordItem, item.collectionId == collectionId.id {
return true
} else {
return false
}
} as? [EmojiKeywordItem]
if let items = items {
return items.sorted(by: { lhs, rhs -> Bool in
if lhs.keyword.count == rhs.keyword.count {
return lhs.keyword < rhs.keyword
} else {
return lhs.keyword.count < rhs.keyword.count
}
})
}
}
return []
}
return postbox.transaction { transaction -> Signal<SearchEmojiKeywordsIntermediateResult, NoError> in
let currentTime = Int32(CFAbsoluteTimeGetCurrent())
let info = transaction.getItemCollectionInfo(collectionId: collectionId)
if let info = info as? EmojiKeywordCollectionInfo {
if info.timestamp + refreshTimeout < currentTime {
addSynchronizeEmojiKeywordsOperation(transaction: transaction, inputLanguageCode: inputLanguageCode, languageCode: info.languageCode, fromVersion: info.version)
return .single(.updating(timestamp: info.timestamp))
} else {
return .single(.completed(search(transaction)))
}
} else {
addSynchronizeEmojiKeywordsOperation(transaction: transaction, inputLanguageCode: inputLanguageCode, languageCode: nil, fromVersion: nil)
return .single(.updating(timestamp: nil))
}
}
|> switchToLatest
|> mapToSignal { intermediateResult -> Signal<[EmojiKeywordItem], NoError> in
switch intermediateResult {
case let .updating(timestamp):
return postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.EmojiKeywords], aroundIndex: nil, count: 10)
|> filter { view -> Bool in
for info in view.collectionInfos {
if let info = info.1 as? EmojiKeywordCollectionInfo, info.id == collectionId {
if let timestamp = timestamp {
return timestamp < info.timestamp
} else {
return true
}
}
}
return false
}
|> take(1)
|> mapToSignal { view -> Signal<[EmojiKeywordItem], NoError> in
for info in view.collectionInfos {
if let info = info.1 as? EmojiKeywordCollectionInfo, info.id == collectionId {
return postbox.transaction { transaction -> [EmojiKeywordItem] in
return search(transaction)
}
}
}
return .complete()
}
case let .completed(items):
return .single(items)
}
}
}