import Foundation import TelegramApi import Postbox import SwiftSignalKit import SyncCore private func hashForIdsReverse(_ ids: [Int64]) -> Int32 { var acc: UInt32 = 0 for id in ids { let low = UInt32(UInt64(bitPattern: id) & (0xffffffff as UInt64)) let high = UInt32((UInt64(bitPattern: id) >> 32) & (0xffffffff as UInt64)) acc = (acc &* 20261) &+ high acc = (acc &* 20261) &+ low } return Int32(bitPattern: acc & UInt32(0x7FFFFFFF)) } func manageStickerPacks(network: Network, postbox: Postbox) -> Signal { return (postbox.transaction { transaction -> Void in addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync, noDelay: false) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync, noDelay: false) addSynchronizeSavedGifsOperation(transaction: transaction, operation: .sync) addSynchronizeSavedStickersOperation(transaction: transaction, operation: .sync) addSynchronizeRecentlyUsedMediaOperation(transaction: transaction, category: .stickers, operation: .sync) } |> then(.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart } func updatedFeaturedStickerPacks(network: Network, postbox: Postbox) -> Signal { return postbox.transaction { transaction -> Signal in let initialPacks = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks) var initialPackMap: [Int64: FeaturedStickerPackItem] = [:] for entry in initialPacks { let item = entry.contents as! FeaturedStickerPackItem initialPackMap[FeaturedStickerPackItemId(entry.id).packId] = item } let initialPackIds = initialPacks.map { return FeaturedStickerPackItemId($0.id).packId } let initialHash: Int32 = hashForIdsReverse(initialPackIds) return network.request(Api.functions.messages.getFeaturedStickers(hash: 0)) |> retryRequest |> mapToSignal { result -> Signal in return postbox.transaction { transaction -> Void in switch result { case .featuredStickersNotModified: break case let .featuredStickers(_, _, sets, unread): let unreadIds = Set(unread) var updatedPacks: [FeaturedStickerPackItem] = [] for set in sets { var (info, items) = parsePreviewStickerSet(set) if let previousPack = initialPackMap[info.id.id] { if previousPack.info.hash == info.hash { items = previousPack.topItems } } updatedPacks.append(FeaturedStickerPackItem(info: info, topItems: items, unread: unreadIds.contains(info.id.id))) } transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks, items: updatedPacks.map { OrderedItemListEntry(id: FeaturedStickerPackItemId($0.info.id.id).rawValue, contents: $0) }) } } } } |> switchToLatest } public func requestOldFeaturedStickerPacks(network: Network, postbox: Postbox, offset: Int, limit: Int) -> Signal<[FeaturedStickerPackItem], NoError> { return network.request(Api.functions.messages.getOldFeaturedStickers(offset: Int32(offset), limit: Int32(limit), hash: 0)) |> retryRequest |> map { result -> [FeaturedStickerPackItem] in switch result { case .featuredStickersNotModified: return [] case let .featuredStickers(_, _, sets, unread): let unreadIds = Set(unread) var updatedPacks: [FeaturedStickerPackItem] = [] for set in sets { let (info, items) = parsePreviewStickerSet(set) updatedPacks.append(FeaturedStickerPackItem(info: info, topItems: items, unread: unreadIds.contains(info.id.id))) } return updatedPacks } } } public func preloadedFeaturedStickerSet(network: Network, postbox: Postbox, id: ItemCollectionId) -> Signal { return postbox.transaction { transaction -> Signal in if let pack = transaction.getOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks, itemId: FeaturedStickerPackItemId(id.id).rawValue)?.contents as? FeaturedStickerPackItem { if pack.topItems.count < 5 && pack.topItems.count < pack.info.count { return requestStickerSet(postbox: postbox, network: network, reference: .id(id: pack.info.id.id, accessHash: pack.info.accessHash)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> mapToSignal { result -> Signal in if let result = result { return postbox.transaction { transaction -> Void in if let pack = transaction.getOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks, itemId: FeaturedStickerPackItemId(id.id).rawValue)?.contents as? FeaturedStickerPackItem { var items = result.items.map({ $0 as? StickerPackItem }).compactMap({ $0 }) if items.count > 5 { items.removeSubrange(5 ..< items.count) } transaction.updateOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks, itemId: FeaturedStickerPackItemId(id.id).rawValue, item: FeaturedStickerPackItem(info: pack.info, topItems: items, unread: pack.unread)) } } } else { return .complete() } } } } return .complete() } |> switchToLatest } func parsePreviewStickerSet(_ set: Api.StickerSetCovered, namespace: ItemCollectionId.Namespace = Namespaces.ItemCollection.CloudStickerPacks) -> (StickerPackCollectionInfo, [StickerPackItem]) { switch set { case let .stickerSetCovered(set, cover): let info = StickerPackCollectionInfo(apiSet: set, namespace: namespace) var items: [StickerPackItem] = [] if let file = telegramMediaFileFromApiDocument(cover), let id = file.id { items.append(StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: id.id), file: file, indexKeys: [])) } return (info, items) case let .stickerSetMultiCovered(set, covers): let info = StickerPackCollectionInfo(apiSet: set, namespace: namespace) var items: [StickerPackItem] = [] for cover in covers { if let file = telegramMediaFileFromApiDocument(cover), let id = file.id { items.append(StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: id.id), file: file, indexKeys: [])) } } return (info, items) } }