mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00

git-subtree-dir: submodules/TelegramCore git-subtree-mainline: 971273e8f8f49a47f14b251d2f35e3445a61fc3f git-subtree-split: 9561227540acef69894e6546395ab223a6233600
189 lines
8.0 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|