diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index 5675e00417..a04e8d8483 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -801,7 +801,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } let restPacks = infos.filter { !processedPacks.contains($0.0) } let sortedPacks = restPacks + tempSortedPacks - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespaceForMode(mode), content: .sync) + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespaceForMode(mode), content: .sync, noDelay: false) transaction.replaceItemCollectionInfos(namespace: namespaceForMode(mode), itemCollectionInfos: sortedPacks) } |> deliverOnMainQueue).start(completed: { diff --git a/submodules/SyncCore/Sources/SynchronizeInstalledStickerPacksOperations.swift b/submodules/SyncCore/Sources/SynchronizeInstalledStickerPacksOperations.swift index 331274806a..c7ad51c754 100644 --- a/submodules/SyncCore/Sources/SynchronizeInstalledStickerPacksOperations.swift +++ b/submodules/SyncCore/Sources/SynchronizeInstalledStickerPacksOperations.swift @@ -9,15 +9,18 @@ public enum SynchronizeInstalledStickerPacksOperationNamespace: Int32 { public final class SynchronizeInstalledStickerPacksOperation: PostboxCoding { public let previousPacks: [ItemCollectionId] public let archivedPacks: [ItemCollectionId] + public let noDelay: Bool - public init(previousPacks: [ItemCollectionId], archivedPacks: [ItemCollectionId]) { + public init(previousPacks: [ItemCollectionId], archivedPacks: [ItemCollectionId], noDelay: Bool) { self.previousPacks = previousPacks self.archivedPacks = archivedPacks + self.noDelay = noDelay } public init(decoder: PostboxDecoder) { self.previousPacks = ItemCollectionId.decodeArrayFromBuffer(decoder.decodeBytesForKey("p")!) self.archivedPacks = decoder.decodeBytesForKey("ap").flatMap(ItemCollectionId.decodeArrayFromBuffer) ?? [] + self.noDelay = decoder.decodeInt32ForKey("nd", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { @@ -27,6 +30,7 @@ public final class SynchronizeInstalledStickerPacksOperation: PostboxCoding { buffer.reset() ItemCollectionId.encodeArrayToBuffer(self.archivedPacks, buffer: buffer) encoder.encodeBytes(buffer, forKey: "ap") + encoder.encodeInt32(self.noDelay ? 1 : 0, forKey: "nd") } } diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 3ca40ebb64..f9dff4046a 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -2760,8 +2760,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP return false } }) { - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync) - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync) + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync, noDelay: false) + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync, noDelay: false) } else { var syncStickers = false var syncMasks = false @@ -2865,10 +2865,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP } } if syncStickers { - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync) + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync, noDelay: false) } if syncMasks { - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync) + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync, noDelay: false) } } } diff --git a/submodules/TelegramCore/Sources/ManagedSynchronizeInstalledStickerPacksOperations.swift b/submodules/TelegramCore/Sources/ManagedSynchronizeInstalledStickerPacksOperations.swift index 4b3c294219..e57bdb862f 100644 --- a/submodules/TelegramCore/Sources/ManagedSynchronizeInstalledStickerPacksOperations.swift +++ b/submodules/TelegramCore/Sources/ManagedSynchronizeInstalledStickerPacksOperations.swift @@ -108,7 +108,7 @@ func managedSynchronizeInstalledStickerPacksOperations(postbox: Postbox, network let _ = transaction.operationLogRemoveEntry(peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex) }) - disposable.set((signal |> delay(2.0, queue: Queue.concurrentDefaultQueue())).start()) + disposable.set((signal |> delay(0.0, queue: Queue.concurrentDefaultQueue())).start()) } }) @@ -139,55 +139,61 @@ private enum SynchronizeInstalledStickerPacksError { case done } -private func fetchStickerPack(network: Network, info: StickerPackCollectionInfo) -> Signal<(ItemCollectionId, [ItemCollectionItem]), NoError> { +private func fetchStickerPack(network: Network, info: StickerPackCollectionInfo) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> { return network.request(Api.functions.messages.getStickerSet(stickerset: .inputStickerSetID(id: info.id.id, accessHash: info.accessHash))) - |> map { result -> (ItemCollectionId, [ItemCollectionItem]) in - var items: [ItemCollectionItem] = [] - switch result { - case let .stickerSet(_, packs, documents): - var indexKeysByFile: [MediaId: [MemoryBuffer]] = [:] - for pack in packs { - switch pack { - case let .stickerPack(text, fileIds): - let key = ValueBoxKey(text).toMemoryBuffer() - for fileId in fileIds { - let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) - if indexKeysByFile[mediaId] == nil { - indexKeysByFile[mediaId] = [key] - } else { - indexKeysByFile[mediaId]!.append(key) - } - } - break - } - } - - for apiDocument in documents { - if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id { - let fileIndexKeys: [MemoryBuffer] - if let indexKeys = indexKeysByFile[id] { - fileIndexKeys = indexKeys + |> map { result -> (StickerPackCollectionInfo, [ItemCollectionItem]) in + var items: [ItemCollectionItem] = [] + var updatedInfo = info + switch result { + case let .stickerSet(stickerSet, packs, documents): + updatedInfo = StickerPackCollectionInfo(apiSet: stickerSet, namespace: info.id.namespace) + var indexKeysByFile: [MediaId: [MemoryBuffer]] = [:] + for pack in packs { + switch pack { + case let .stickerPack(text, fileIds): + let key = ValueBoxKey(text).toMemoryBuffer() + for fileId in fileIds { + let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) + if indexKeysByFile[mediaId] == nil { + indexKeysByFile[mediaId] = [key] } else { - fileIndexKeys = [] + indexKeysByFile[mediaId]!.append(key) } - items.append(StickerPackItem(index: ItemCollectionItemIndex(index: Int32(items.count), id: id.id), file: file, indexKeys: fileIndexKeys)) } + break } - break } - return (info.id, items) - } - |> `catch` { _ -> Signal<(ItemCollectionId, [ItemCollectionItem]), NoError> in - return .single((info.id, [])) + + for apiDocument in documents { + if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id { + let fileIndexKeys: [MemoryBuffer] + if let indexKeys = indexKeysByFile[id] { + fileIndexKeys = indexKeys + } else { + fileIndexKeys = [] + } + items.append(StickerPackItem(index: ItemCollectionItemIndex(index: Int32(items.count), id: id.id), file: file, indexKeys: fileIndexKeys)) + } + } + break } + return (updatedInfo, items) + } + |> `catch` { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in + return .single((info, [])) + } } -private func resolveStickerPacks(network: Network, remoteInfos: [ItemCollectionId: StickerPackCollectionInfo], localInfos: [ItemCollectionId: StickerPackCollectionInfo]) -> Signal<[ItemCollectionId: [ItemCollectionItem]], NoError> { +private func resolveStickerPacks(network: Network, remoteInfos: [ItemCollectionId: (StickerPackCollectionInfo, Bool)], localInfos: [ItemCollectionId: StickerPackCollectionInfo]) -> Signal<[ItemCollectionId: [ItemCollectionItem]], NoError> { var signals: [Signal<(ItemCollectionId, [ItemCollectionItem]), NoError>] = [] - for (id, remoteInfo) in remoteInfos { + for (id, remoteInfoAndIsStale) in remoteInfos { + let (remoteInfo, isStale) = remoteInfoAndIsStale let localInfo = localInfos[id] - if localInfo == nil || localInfo!.hash != remoteInfo.hash { - signals.append(fetchStickerPack(network: network, info: remoteInfo)) + if localInfo == nil || localInfo!.hash != remoteInfo.hash || isStale { + signals.append(fetchStickerPack(network: network, info: remoteInfo) + |> map { (info, items) in + return (info.id, items) + }) } } return combineLatest(signals) @@ -292,6 +298,75 @@ private func synchronizeInstalledStickerPacks(transaction: Transaction, postbox: collectionNamespace = Namespaces.ItemCollection.CloudMaskPacks } + let localCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } + let localStateCollectionOrder = localCollectionInfos.map({ $0.id }) + let localPreviousStateCollectionOrder = operation.previousPacks + + if localStateCollectionOrder == localPreviousStateCollectionOrder { + return continueSynchronizeInstalledStickerPacks( + transaction: transaction, + postbox: postbox, + network: network, + stateManager: stateManager, + namespace: namespace, + operation: operation + ) + } + + let locallyAddedInfos = localCollectionInfos.filter { !localPreviousStateCollectionOrder.contains($0.id) } + + if locallyAddedInfos.isEmpty { + return continueSynchronizeInstalledStickerPacks( + transaction: transaction, + postbox: postbox, + network: network, + stateManager: stateManager, + namespace: namespace, + operation: operation + ) + } + + var fetchSignals: [Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError>] = [] + for info in locallyAddedInfos { + fetchSignals.append(fetchStickerPack(network: network, info: info) + ) + } + let fetchedPacks = combineLatest(fetchSignals) + + return fetchedPacks + |> mapToSignal { fetchedPacks -> Signal in + return postbox.transaction { transaction -> Signal in + var currentInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } + for (info, items) in fetchedPacks { + if let index = currentInfos.firstIndex(where: { $0.id == info.id }) { + currentInfos[index] = info + transaction.replaceItemCollectionItems(collectionId: info.id, items: items) + } + } + transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: currentInfos.map { ($0.id, $0) }) + + return continueSynchronizeInstalledStickerPacks( + transaction: transaction, + postbox: postbox, + network: network, + stateManager: stateManager, + namespace: namespace, + operation: operation + ) + } + |> switchToLatest + } +} + +private func continueSynchronizeInstalledStickerPacks(transaction: Transaction, postbox: Postbox, network: Network, stateManager: AccountStateManager, namespace: SynchronizeInstalledStickerPacksOperationNamespace, operation: SynchronizeInstalledStickerPacksOperation) -> Signal { + let collectionNamespace: ItemCollectionId.Namespace + switch namespace { + case .stickers: + collectionNamespace = Namespaces.ItemCollection.CloudStickerPacks + case .masks: + collectionNamespace = Namespaces.ItemCollection.CloudMaskPacks + } + let localCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } let initialLocalHash = hashForStickerPackInfos(localCollectionInfos) @@ -304,205 +379,234 @@ private func synchronizeInstalledStickerPacks(transaction: Transaction, postbox: } let sequence = request - |> retryRequest + |> retryRequest + |> mapError { _ -> SynchronizeInstalledStickerPacksError in + return .restart + } + |> mapToSignal { result -> Signal in + return postbox.transaction { transaction -> Signal in + let checkLocalCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } + if checkLocalCollectionInfos != localCollectionInfos { + return .fail(.restart) + } + + let localInitialStateCollectionOrder = operation.previousPacks + let localInitialStateCollectionIds = Set(localInitialStateCollectionOrder) + + let localCollectionOrder = checkLocalCollectionInfos.map { $0.id } + let localCollectionIds = Set(localCollectionOrder) + + var remoteCollectionInfos: [StickerPackCollectionInfo] = [] + switch result { + case let .allStickers(_, sets): + for apiSet in sets { + let info = StickerPackCollectionInfo(apiSet: apiSet, namespace: collectionNamespace) + remoteCollectionInfos.append(info) + } + case .allStickersNotModified: + remoteCollectionInfos = checkLocalCollectionInfos + } + + let remoteCollectionOrder = remoteCollectionInfos.map { $0.id } + let remoteCollectionIds = Set(remoteCollectionOrder) + + var remoteInfos: [ItemCollectionId: StickerPackCollectionInfo] = [:] + for info in remoteCollectionInfos { + remoteInfos[info.id] = info + } + var localInfos: [ItemCollectionId: StickerPackCollectionInfo] = [:] + for info in checkLocalCollectionInfos { + localInfos[info.id] = info + } + + if localInitialStateCollectionOrder == localCollectionOrder { + if checkLocalCollectionInfos == remoteCollectionInfos { + return .fail(.done) + } else { + var resolveRemoteInfos: [ItemCollectionId: (StickerPackCollectionInfo, Bool)] = [:] + for (id, info) in remoteInfos { + resolveRemoteInfos[id] = (info, false) + } + return resolveStickerPacks(network: network, remoteInfos: resolveRemoteInfos, localInfos: localInfos) + |> mapError { _ -> SynchronizeInstalledStickerPacksError in + return .restart + } + |> mapToSignal { replaceItems -> Signal in + return postbox.transaction { transaction -> Signal in + let storeSignal: Signal + if localCollectionIds.isEmpty { + var incrementalSignal = postbox.transaction { transaction -> Void in + transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: remoteCollectionInfos.map { ($0.id, $0) }) + for id in localCollectionIds.subtracting(remoteCollectionIds) { + transaction.replaceItemCollectionItems(collectionId: id, items: []) + } + } + for (id, items) in replaceItems { + let partSignal = postbox.transaction { transaction -> Void in + transaction.replaceItemCollectionItems(collectionId: id, items: items) + } + incrementalSignal = incrementalSignal + |> then( + partSignal + |> delay(0.01, queue: Queue.concurrentDefaultQueue()) + ) + } + storeSignal = incrementalSignal + } else { + storeSignal = postbox.transaction { transaction -> Void in + transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: remoteCollectionInfos.map { ($0.id, $0) }) + for (id, items) in replaceItems { + transaction.replaceItemCollectionItems(collectionId: id, items: items) + } + for id in localCollectionIds.subtracting(remoteCollectionIds) { + transaction.replaceItemCollectionItems(collectionId: id, items: []) + } + } + } + + return ( + storeSignal + |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart + } + ) + |> then(.fail(.done)) + } + |> castError(SynchronizeInstalledStickerPacksError.self) + |> switchToLatest + } + } + } else { + let locallyRemovedCollectionIds = localInitialStateCollectionIds.subtracting(localCollectionIds) + let locallyAddedCollectionIds = localCollectionIds.subtracting(localInitialStateCollectionIds) + let remotelyAddedCollections = remoteCollectionInfos.filter { info in + return !locallyRemovedCollectionIds.contains(info.id) && !localCollectionIds.contains(info.id) + } + let remotelyRemovedCollectionIds = remoteCollectionIds.subtracting(localInitialStateCollectionIds).subtracting(locallyAddedCollectionIds) + + var resultingCollectionInfos: [(StickerPackCollectionInfo, Bool)] = [] + resultingCollectionInfos.append(contentsOf: remotelyAddedCollections.map { ($0, false) }) + var remoteCollectionInfoMap: [ItemCollectionId: StickerPackCollectionInfo] = [:] + for info in remoteCollectionInfos { + remoteCollectionInfoMap[info.id] = info + } + for info in checkLocalCollectionInfos { + if !remotelyRemovedCollectionIds.contains(info.id) { + if let remoteInfo = remoteCollectionInfoMap[info.id] { + resultingCollectionInfos.append((remoteInfo, false)) + } else { + resultingCollectionInfos.append((info, true)) + } + } + } + + let resultingCollectionIds = Set(resultingCollectionInfos.map { $0.0.id }) + let removeRemoteCollectionIds = remoteCollectionIds.subtracting(resultingCollectionIds) + let addRemoteCollectionIds = resultingCollectionIds.subtracting(remoteCollectionIds) + + var removeRemoteCollectionInfos: [StickerPackCollectionInfo] = [] + for id in removeRemoteCollectionIds { + if let info = remoteInfos[id] { + removeRemoteCollectionInfos.append(info) + } else if let info = localInfos[id] { + removeRemoteCollectionInfos.append(info) + } + } + + var addRemoteCollectionInfos: [StickerPackCollectionInfo] = [] + for id in addRemoteCollectionIds { + if let info = remoteInfos[id] { + addRemoteCollectionInfos.append(info) + } else if let info = localInfos[id] { + addRemoteCollectionInfos.append(info) + } + } + + let infosToArchive = removeRemoteCollectionInfos.filter { info in + return operation.archivedPacks.contains(info.id) + } + let infosToRemove = removeRemoteCollectionInfos.filter { info in + return !operation.archivedPacks.contains(info.id) + } + + let archivedOrRemovedIds = (combineLatest(archiveRemoteStickerPacks(network: network, infos: infosToArchive), removeRemoteStickerPacks(network: network, infos: infosToRemove)) + |> mapToSignal { _ -> Signal in + return .complete() + } + |> then(Signal.single(Void()))) + |> mapToSignal { _ -> Signal, NoError> in + return installRemoteStickerPacks(network: network, infos: addRemoteCollectionInfos) + |> mapToSignal { ids -> Signal, NoError> in + return (reorderRemoteStickerPacks(network: network, namespace: namespace, ids: resultingCollectionInfos.map({ $0.0.id }).filter({ !ids.contains($0) })) + |> then(Signal.single(Void()))) + |> map { _ -> Set in + return ids + } + } + } + + var resultingInfos: [ItemCollectionId: (StickerPackCollectionInfo, Bool)] = [:] + for info in resultingCollectionInfos { + resultingInfos[info.0.id] = info + } + for (id, info) in resultingInfos { + if info.0.shortName.lowercased() == "thevirus" { + print("id1 = \(id)") + } + } + for (id, info) in localInfos { + if info.shortName.lowercased() == "thevirus" { + print("id1 = \(id)") + } + } + + let resolvedItems = resolveStickerPacks(network: network, remoteInfos: resultingInfos, localInfos: localInfos) + + return combineLatest(archivedOrRemovedIds, resolvedItems) + |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart } + |> mapToSignal { archivedOrRemovedIds, replaceItems -> Signal in + return (postbox.transaction { transaction -> Signal in + let finalCheckLocalCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } + if finalCheckLocalCollectionInfos != localCollectionInfos { + return .fail(.restart) + } + + transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: resultingCollectionInfos.filter({ info in + return !archivedOrRemovedIds.contains(info.0.id) + }).map({ ($0.0.id, $0.0) })) + for (id, items) in replaceItems { + if !archivedOrRemovedIds.contains(id) { + transaction.replaceItemCollectionItems(collectionId: id, items: items) + } + } + for id in localCollectionIds.subtracting(resultingCollectionIds).union(archivedOrRemovedIds) { + transaction.replaceItemCollectionItems(collectionId: id, items: []) + } + + return .complete() + } + |> mapError { _ -> SynchronizeInstalledStickerPacksError in + return .restart + }) + |> switchToLatest + |> then(.fail(.done)) + } + } + } |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart } - |> mapToSignal { result -> Signal in - return postbox.transaction { transaction -> Signal in - let checkLocalCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } - if checkLocalCollectionInfos != localCollectionInfos { - return .fail(.restart) - } - - let localInitialStateCollectionOrder = operation.previousPacks - let localInitialStateCollectionIds = Set(localInitialStateCollectionOrder) - - let localCollectionOrder = checkLocalCollectionInfos.map { $0.id } - let localCollectionIds = Set(localCollectionOrder) - - var remoteCollectionInfos: [StickerPackCollectionInfo] = [] - switch result { - case let .allStickers(_, sets): - for apiSet in sets { - let info = StickerPackCollectionInfo(apiSet: apiSet, namespace: collectionNamespace) - remoteCollectionInfos.append(info) - } - case .allStickersNotModified: - remoteCollectionInfos = checkLocalCollectionInfos - } - - let remoteCollectionOrder = remoteCollectionInfos.map { $0.id } - let remoteCollectionIds = Set(remoteCollectionOrder) - - var remoteInfos: [ItemCollectionId: StickerPackCollectionInfo] = [:] - for info in remoteCollectionInfos { - remoteInfos[info.id] = info - } - var localInfos: [ItemCollectionId: StickerPackCollectionInfo] = [:] - for info in checkLocalCollectionInfos { - localInfos[info.id] = info - } - - if localInitialStateCollectionOrder == localCollectionOrder { - if checkLocalCollectionInfos == remoteCollectionInfos { - return .fail(.done) - } else { - return resolveStickerPacks(network: network, remoteInfos: remoteInfos, localInfos: localInfos) - |> mapError { _ -> SynchronizeInstalledStickerPacksError in - return .restart - } - |> mapToSignal { replaceItems -> Signal in - return postbox.transaction { transaction -> Signal in - let storeSignal: Signal - if localCollectionIds.isEmpty { - var incrementalSignal = postbox.transaction { transaction -> Void in - transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: remoteCollectionInfos.map { ($0.id, $0) }) - for id in localCollectionIds.subtracting(remoteCollectionIds) { - transaction.replaceItemCollectionItems(collectionId: id, items: []) - } - } - for (id, items) in replaceItems { - let partSignal = postbox.transaction { transaction -> Void in - transaction.replaceItemCollectionItems(collectionId: id, items: items) - } - incrementalSignal = incrementalSignal - |> then( - partSignal - |> delay(0.01, queue: Queue.concurrentDefaultQueue()) - ) - } - storeSignal = incrementalSignal - } else { - storeSignal = postbox.transaction { transaction -> Void in - transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: remoteCollectionInfos.map { ($0.id, $0) }) - for (id, items) in replaceItems { - transaction.replaceItemCollectionItems(collectionId: id, items: items) - } - for id in localCollectionIds.subtracting(remoteCollectionIds) { - transaction.replaceItemCollectionItems(collectionId: id, items: []) - } - } - } - - return ( - storeSignal - |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart - } - ) - |> then(.fail(.done)) - } - |> castError(SynchronizeInstalledStickerPacksError.self) - |> switchToLatest - } - } - } else { - let locallyRemovedCollectionIds = localInitialStateCollectionIds.subtracting(localCollectionIds) - let locallyAddedCollectionIds = localCollectionIds.subtracting(localInitialStateCollectionIds) - let remotelyAddedCollections = remoteCollectionInfos.filter { info in - return !locallyRemovedCollectionIds.contains(info.id) && !localCollectionIds.contains(info.id) - } - let remotelyRemovedCollectionIds = remoteCollectionIds.subtracting(localInitialStateCollectionIds).subtracting(locallyAddedCollectionIds) - - var resultingCollectionInfos: [StickerPackCollectionInfo] = [] - resultingCollectionInfos.append(contentsOf: remotelyAddedCollections) - resultingCollectionInfos.append(contentsOf: checkLocalCollectionInfos.filter { info in - return !remotelyRemovedCollectionIds.contains(info.id) - }) - - let resultingCollectionIds = Set(resultingCollectionInfos.map { $0.id }) - let removeRemoteCollectionIds = remoteCollectionIds.subtracting(resultingCollectionIds) - let addRemoteCollectionIds = resultingCollectionIds.subtracting(remoteCollectionIds) - - var removeRemoteCollectionInfos: [StickerPackCollectionInfo] = [] - for id in removeRemoteCollectionIds { - if let info = remoteInfos[id] { - removeRemoteCollectionInfos.append(info) - } else if let info = localInfos[id] { - removeRemoteCollectionInfos.append(info) - } - } - - var addRemoteCollectionInfos: [StickerPackCollectionInfo] = [] - for id in addRemoteCollectionIds { - if let info = remoteInfos[id] { - addRemoteCollectionInfos.append(info) - } else if let info = localInfos[id] { - addRemoteCollectionInfos.append(info) - } - } - - let infosToArchive = removeRemoteCollectionInfos.filter { info in - return operation.archivedPacks.contains(info.id) - } - let infosToRemove = removeRemoteCollectionInfos.filter { info in - return !operation.archivedPacks.contains(info.id) - } - - let archivedOrRemovedIds = (combineLatest(archiveRemoteStickerPacks(network: network, infos: infosToArchive), removeRemoteStickerPacks(network: network, infos: infosToRemove)) - |> mapToSignal { _ -> Signal in - return .complete() - } - |> then(Signal.single(Void()))) - |> mapToSignal { _ -> Signal, NoError> in - return installRemoteStickerPacks(network: network, infos: addRemoteCollectionInfos) - |> mapToSignal { ids -> Signal, NoError> in - return (reorderRemoteStickerPacks(network: network, namespace: namespace, ids: resultingCollectionInfos.map({ $0.id }).filter({ !ids.contains($0) })) - |> then(Signal.single(Void()))) - |> map { _ -> Set in - return ids - } - } - } - - var resultingInfos: [ItemCollectionId: StickerPackCollectionInfo] = [:] - for info in resultingCollectionInfos { - resultingInfos[info.id] = info - } - let resolvedItems = resolveStickerPacks(network: network, remoteInfos: resultingInfos, localInfos: localInfos) - - return combineLatest(archivedOrRemovedIds, resolvedItems) - |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart } - |> mapToSignal { archivedOrRemovedIds, replaceItems -> Signal in - return (postbox.transaction { transaction -> Signal in - let finalCheckLocalCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } - if finalCheckLocalCollectionInfos != localCollectionInfos { - return .fail(.restart) - } - - transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: resultingCollectionInfos.filter({ info in - return !archivedOrRemovedIds.contains(info.id) - }).map({ ($0.id, $0) })) - for (id, items) in replaceItems { - if !archivedOrRemovedIds.contains(id) { - transaction.replaceItemCollectionItems(collectionId: id, items: items) - } - } - for id in localCollectionIds.subtracting(resultingCollectionIds).union(archivedOrRemovedIds) { - transaction.replaceItemCollectionItems(collectionId: id, items: []) - } - - return .complete() - } - |> mapError { _ -> SynchronizeInstalledStickerPacksError in - return .restart - }) - |> switchToLatest - |> then(.fail(.done)) - } - } - } |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart } |> switchToLatest - } - return ((sequence - |> `catch` { error -> Signal in - switch error { - case .done: - return .fail(.done) - case .restart: - return .complete() - } - }) |> restart) |> `catch` { _ -> Signal in - return .complete() - } + |> switchToLatest + } + return ((sequence + |> `catch` { error -> Signal in + switch error { + case .done: + return .fail(.done) + case .restart: + return .complete() + } + }) |> restart) |> `catch` { _ -> Signal in + return .complete() + } } diff --git a/submodules/TelegramCore/Sources/StickerManagement.swift b/submodules/TelegramCore/Sources/StickerManagement.swift index 574c4669f2..62044d5b1a 100644 --- a/submodules/TelegramCore/Sources/StickerManagement.swift +++ b/submodules/TelegramCore/Sources/StickerManagement.swift @@ -20,8 +20,8 @@ private func hashForIdsReverse(_ ids: [Int64]) -> Int32 { func manageStickerPacks(network: Network, postbox: Postbox) -> Signal { return (postbox.transaction { transaction -> Void in - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync) - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync) + 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) diff --git a/submodules/TelegramCore/Sources/StickerPackInteractiveOperations.swift b/submodules/TelegramCore/Sources/StickerPackInteractiveOperations.swift index 4605f32262..14b0b46ffa 100644 --- a/submodules/TelegramCore/Sources/StickerPackInteractiveOperations.swift +++ b/submodules/TelegramCore/Sources/StickerPackInteractiveOperations.swift @@ -16,21 +16,25 @@ public func addStickerPackInteractively(postbox: Postbox, info: StickerPackColle namespace = nil } if let namespace = namespace { - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: .add([info.id])) - var updatedInfos = transaction.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! StickerPackCollectionInfo } - if let index = updatedInfos.firstIndex(where: { $0.id == info.id }) { + var mappedInfo = info + if items.isEmpty { + mappedInfo = StickerPackCollectionInfo(id: info.id, flags: info.flags, accessHash: info.accessHash, title: info.title, shortName: info.shortName, thumbnail: info.thumbnail, hash: Int32(bitPattern: arc4random()), count: info.count) + } + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: .add([mappedInfo.id]), noDelay: items.isEmpty) + var updatedInfos = transaction.getItemCollectionsInfos(namespace: mappedInfo.id.namespace).map { $0.1 as! StickerPackCollectionInfo } + if let index = updatedInfos.firstIndex(where: { $0.id == mappedInfo.id }) { let currentInfo = updatedInfos[index] updatedInfos.remove(at: index) updatedInfos.insert(currentInfo, at: 0) } else { if let positionInList = positionInList, positionInList <= updatedInfos.count { - updatedInfos.insert(info, at: positionInList) + updatedInfos.insert(mappedInfo, at: positionInList) } else { - updatedInfos.insert(info, at: 0) + updatedInfos.insert(mappedInfo, at: 0) } - transaction.replaceItemCollectionItems(collectionId: info.id, items: items) + transaction.replaceItemCollectionItems(collectionId: mappedInfo.id, items: items) } - transaction.replaceItemCollectionInfos(namespace: info.id.namespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) }) + transaction.replaceItemCollectionInfos(namespace: mappedInfo.id.namespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) }) } } } @@ -59,10 +63,10 @@ public func removeStickerPackInteractively(postbox: Postbox, id: ItemCollectionI case .archive: content = .archive([id]) } - let index = transaction.getItemCollectionsInfos(namespace: id.namespace).index(where: { $0.0 == id }) + let index = transaction.getItemCollectionsInfos(namespace: id.namespace).firstIndex(where: { $0.0 == id }) let items = transaction.getItemCollectionItems(collectionId: id) - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: content) + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: content, noDelay: false) transaction.removeItemCollection(collectionId: id) return index.flatMap { ($0, items) } } else { diff --git a/submodules/TelegramCore/Sources/SynchronizeInstalledStickerPacksOperation.swift b/submodules/TelegramCore/Sources/SynchronizeInstalledStickerPacksOperation.swift index 6c95c4b044..f550172e03 100644 --- a/submodules/TelegramCore/Sources/SynchronizeInstalledStickerPacksOperation.swift +++ b/submodules/TelegramCore/Sources/SynchronizeInstalledStickerPacksOperation.swift @@ -8,7 +8,7 @@ public enum AddSynchronizeInstalledStickerPacksOperationContent { case archive([ItemCollectionId]) } -public func addSynchronizeInstalledStickerPacksOperation(transaction: Transaction, namespace: ItemCollectionId.Namespace, content: AddSynchronizeInstalledStickerPacksOperationContent) { +public func addSynchronizeInstalledStickerPacksOperation(transaction: Transaction, namespace: ItemCollectionId.Namespace, content: AddSynchronizeInstalledStickerPacksOperationContent, noDelay: Bool) { let operationNamespace: SynchronizeInstalledStickerPacksOperationNamespace switch namespace { case Namespaces.ItemCollection.CloudStickerPacks: @@ -18,10 +18,10 @@ public func addSynchronizeInstalledStickerPacksOperation(transaction: Transactio default: return } - addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: operationNamespace, content: content) + addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: operationNamespace, content: content, noDelay: noDelay) } -func addSynchronizeInstalledStickerPacksOperation(transaction: Transaction, namespace: SynchronizeInstalledStickerPacksOperationNamespace, content: AddSynchronizeInstalledStickerPacksOperationContent) { +func addSynchronizeInstalledStickerPacksOperation(transaction: Transaction, namespace: SynchronizeInstalledStickerPacksOperationNamespace, content: AddSynchronizeInstalledStickerPacksOperationContent, noDelay: Bool) { var updateLocalIndex: Int32? let tag: PeerOperationLogTag let itemCollectionNamespace: ItemCollectionId.Namespace @@ -62,7 +62,7 @@ func addSynchronizeInstalledStickerPacksOperation(transaction: Transaction, name } } } - let operationContents = SynchronizeInstalledStickerPacksOperation(previousPacks: previousPacks, archivedPacks: archivedPacks) + let operationContents = SynchronizeInstalledStickerPacksOperation(previousPacks: previousPacks, archivedPacks: archivedPacks, noDelay: noDelay) if let updateLocalIndex = updateLocalIndex { let _ = transaction.operationLogRemoveEntry(peerId: PeerId(namespace: 0, id: 0), tag: tag, tagLocalIndex: updateLocalIndex) } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index 763945a290..af3f4f36f9 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -201,7 +201,7 @@ private func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers } var index = 0 for (_, info, item) in view.collectionInfos { - if let info = info as? StickerPackCollectionInfo { + if let info = info as? StickerPackCollectionInfo, item != nil { entries.append(.stickerPack(index: index, info: info, topItem: item as? StickerPackItem, theme: theme)) index += 1 } diff --git a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift index 8074a2d7d9..73a2b2a29c 100644 --- a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift +++ b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift @@ -311,7 +311,8 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { } let account = strongSelf.context.account if install { - var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + let _ = addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: []).start() + /*var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in switch result { case let .result(info, items, installed): @@ -385,7 +386,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return true }))*/ - })) + }))*/ } else { let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete) |> deliverOnMainQueue).start(next: { _ in @@ -1080,7 +1081,8 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { } let account = strongSelf.context.account if install { - var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + let _ = addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: []).start() + /*var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in switch result { case let .result(info, items, installed): @@ -1155,7 +1157,7 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { /*strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return true }))*/ - })) + }))*/ } else { let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete) |> deliverOnMainQueue).start(next: { _ in