Serialize sticker pack addition

This commit is contained in:
Ali 2020-04-15 00:52:22 +04:00
parent 7f17b1b22d
commit f0c1f3ff3e
9 changed files with 376 additions and 262 deletions

View File

@ -801,7 +801,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
} }
let restPacks = infos.filter { !processedPacks.contains($0.0) } let restPacks = infos.filter { !processedPacks.contains($0.0) }
let sortedPacks = restPacks + tempSortedPacks 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) transaction.replaceItemCollectionInfos(namespace: namespaceForMode(mode), itemCollectionInfos: sortedPacks)
} }
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {

View File

@ -9,15 +9,18 @@ public enum SynchronizeInstalledStickerPacksOperationNamespace: Int32 {
public final class SynchronizeInstalledStickerPacksOperation: PostboxCoding { public final class SynchronizeInstalledStickerPacksOperation: PostboxCoding {
public let previousPacks: [ItemCollectionId] public let previousPacks: [ItemCollectionId]
public let archivedPacks: [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.previousPacks = previousPacks
self.archivedPacks = archivedPacks self.archivedPacks = archivedPacks
self.noDelay = noDelay
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
self.previousPacks = ItemCollectionId.decodeArrayFromBuffer(decoder.decodeBytesForKey("p")!) self.previousPacks = ItemCollectionId.decodeArrayFromBuffer(decoder.decodeBytesForKey("p")!)
self.archivedPacks = decoder.decodeBytesForKey("ap").flatMap(ItemCollectionId.decodeArrayFromBuffer) ?? [] self.archivedPacks = decoder.decodeBytesForKey("ap").flatMap(ItemCollectionId.decodeArrayFromBuffer) ?? []
self.noDelay = decoder.decodeInt32ForKey("nd", orElse: 0) != 0
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
@ -27,6 +30,7 @@ public final class SynchronizeInstalledStickerPacksOperation: PostboxCoding {
buffer.reset() buffer.reset()
ItemCollectionId.encodeArrayToBuffer(self.archivedPacks, buffer: buffer) ItemCollectionId.encodeArrayToBuffer(self.archivedPacks, buffer: buffer)
encoder.encodeBytes(buffer, forKey: "ap") encoder.encodeBytes(buffer, forKey: "ap")
encoder.encodeInt32(self.noDelay ? 1 : 0, forKey: "nd")
} }
} }

View File

@ -2760,8 +2760,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
return false return false
} }
}) { }) {
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync, noDelay: false)
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync, noDelay: false)
} else { } else {
var syncStickers = false var syncStickers = false
var syncMasks = false var syncMasks = false
@ -2865,10 +2865,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
} }
} }
if syncStickers { if syncStickers {
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync, noDelay: false)
} }
if syncMasks { if syncMasks {
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync, noDelay: false)
} }
} }
} }

View File

@ -108,7 +108,7 @@ func managedSynchronizeInstalledStickerPacksOperations(postbox: Postbox, network
let _ = transaction.operationLogRemoveEntry(peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex) 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 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))) return network.request(Api.functions.messages.getStickerSet(stickerset: .inputStickerSetID(id: info.id.id, accessHash: info.accessHash)))
|> map { result -> (ItemCollectionId, [ItemCollectionItem]) in |> map { result -> (StickerPackCollectionInfo, [ItemCollectionItem]) in
var items: [ItemCollectionItem] = [] var items: [ItemCollectionItem] = []
switch result { var updatedInfo = info
case let .stickerSet(_, packs, documents): switch result {
var indexKeysByFile: [MediaId: [MemoryBuffer]] = [:] case let .stickerSet(stickerSet, packs, documents):
for pack in packs { updatedInfo = StickerPackCollectionInfo(apiSet: stickerSet, namespace: info.id.namespace)
switch pack { var indexKeysByFile: [MediaId: [MemoryBuffer]] = [:]
case let .stickerPack(text, fileIds): for pack in packs {
let key = ValueBoxKey(text).toMemoryBuffer() switch pack {
for fileId in fileIds { case let .stickerPack(text, fileIds):
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) let key = ValueBoxKey(text).toMemoryBuffer()
if indexKeysByFile[mediaId] == nil { for fileId in fileIds {
indexKeysByFile[mediaId] = [key] let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
} else { if indexKeysByFile[mediaId] == nil {
indexKeysByFile[mediaId]!.append(key) indexKeysByFile[mediaId] = [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
} else { } 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)
} for apiDocument in documents {
|> `catch` { _ -> Signal<(ItemCollectionId, [ItemCollectionItem]), NoError> in if let file = telegramMediaFileFromApiDocument(apiDocument), let id = file.id {
return .single((info.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>] = [] var signals: [Signal<(ItemCollectionId, [ItemCollectionItem]), NoError>] = []
for (id, remoteInfo) in remoteInfos { for (id, remoteInfoAndIsStale) in remoteInfos {
let (remoteInfo, isStale) = remoteInfoAndIsStale
let localInfo = localInfos[id] let localInfo = localInfos[id]
if localInfo == nil || localInfo!.hash != remoteInfo.hash { if localInfo == nil || localInfo!.hash != remoteInfo.hash || isStale {
signals.append(fetchStickerPack(network: network, info: remoteInfo)) signals.append(fetchStickerPack(network: network, info: remoteInfo)
|> map { (info, items) in
return (info.id, items)
})
} }
} }
return combineLatest(signals) return combineLatest(signals)
@ -292,6 +298,75 @@ private func synchronizeInstalledStickerPacks(transaction: Transaction, postbox:
collectionNamespace = Namespaces.ItemCollection.CloudMaskPacks 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<Void, NoError> in
return postbox.transaction { transaction -> Signal<Void, NoError> 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<Void, NoError> {
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 localCollectionInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo }
let initialLocalHash = hashForStickerPackInfos(localCollectionInfos) let initialLocalHash = hashForStickerPackInfos(localCollectionInfos)
@ -304,205 +379,234 @@ private func synchronizeInstalledStickerPacks(transaction: Transaction, postbox:
} }
let sequence = request let sequence = request
|> retryRequest |> retryRequest
|> mapError { _ -> SynchronizeInstalledStickerPacksError in
return .restart
}
|> mapToSignal { result -> Signal<Void, SynchronizeInstalledStickerPacksError> in
return postbox.transaction { transaction -> Signal<Void, SynchronizeInstalledStickerPacksError> 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<Void, SynchronizeInstalledStickerPacksError> in
return postbox.transaction { transaction -> Signal<Void, SynchronizeInstalledStickerPacksError> in
let storeSignal: Signal<Void, NoError>
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<Void, NoError> in
return .complete()
}
|> then(Signal<Void, NoError>.single(Void())))
|> mapToSignal { _ -> Signal<Set<ItemCollectionId>, NoError> in
return installRemoteStickerPacks(network: network, infos: addRemoteCollectionInfos)
|> mapToSignal { ids -> Signal<Set<ItemCollectionId>, NoError> in
return (reorderRemoteStickerPacks(network: network, namespace: namespace, ids: resultingCollectionInfos.map({ $0.0.id }).filter({ !ids.contains($0) }))
|> then(Signal<Void, NoError>.single(Void())))
|> map { _ -> Set<ItemCollectionId> 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<Void, SynchronizeInstalledStickerPacksError> in
return (postbox.transaction { transaction -> Signal<Void, SynchronizeInstalledStickerPacksError> 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 |> mapError { _ -> SynchronizeInstalledStickerPacksError in
return .restart return .restart
} }
|> mapToSignal { result -> Signal<Void, SynchronizeInstalledStickerPacksError> in |> switchToLatest
return postbox.transaction { transaction -> Signal<Void, SynchronizeInstalledStickerPacksError> 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<Void, SynchronizeInstalledStickerPacksError> in
return postbox.transaction { transaction -> Signal<Void, SynchronizeInstalledStickerPacksError> in
let storeSignal: Signal<Void, NoError>
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<Void, NoError> in
return .complete()
}
|> then(Signal<Void, NoError>.single(Void())))
|> mapToSignal { _ -> Signal<Set<ItemCollectionId>, NoError> in
return installRemoteStickerPacks(network: network, infos: addRemoteCollectionInfos)
|> mapToSignal { ids -> Signal<Set<ItemCollectionId>, NoError> in
return (reorderRemoteStickerPacks(network: network, namespace: namespace, ids: resultingCollectionInfos.map({ $0.id }).filter({ !ids.contains($0) }))
|> then(Signal<Void, NoError>.single(Void())))
|> map { _ -> Set<ItemCollectionId> 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<Void, SynchronizeInstalledStickerPacksError> in
return (postbox.transaction { transaction -> Signal<Void, SynchronizeInstalledStickerPacksError> 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<Void, SynchronizeInstalledStickerPacksError> in
switch error {
case .done:
return .fail(.done)
case .restart:
return .complete()
}
}) |> restart) |> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}
return ((sequence
|> `catch` { error -> Signal<Void, SynchronizeInstalledStickerPacksError> in
switch error {
case .done:
return .fail(.done)
case .restart:
return .complete()
}
}) |> restart) |> `catch` { _ -> Signal<Void, NoError> in
return .complete()
}
} }

View File

@ -20,8 +20,8 @@ private func hashForIdsReverse(_ ids: [Int64]) -> Int32 {
func manageStickerPacks(network: Network, postbox: Postbox) -> Signal<Void, NoError> { func manageStickerPacks(network: Network, postbox: Postbox) -> Signal<Void, NoError> {
return (postbox.transaction { transaction -> Void in return (postbox.transaction { transaction -> Void in
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .stickers, content: .sync, noDelay: false)
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync) addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: .masks, content: .sync, noDelay: false)
addSynchronizeSavedGifsOperation(transaction: transaction, operation: .sync) addSynchronizeSavedGifsOperation(transaction: transaction, operation: .sync)
addSynchronizeSavedStickersOperation(transaction: transaction, operation: .sync) addSynchronizeSavedStickersOperation(transaction: transaction, operation: .sync)
addSynchronizeRecentlyUsedMediaOperation(transaction: transaction, category: .stickers, operation: .sync) addSynchronizeRecentlyUsedMediaOperation(transaction: transaction, category: .stickers, operation: .sync)

View File

@ -16,21 +16,25 @@ public func addStickerPackInteractively(postbox: Postbox, info: StickerPackColle
namespace = nil namespace = nil
} }
if let namespace = namespace { if let namespace = namespace {
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: .add([info.id])) var mappedInfo = info
var updatedInfos = transaction.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! StickerPackCollectionInfo } if items.isEmpty {
if let index = updatedInfos.firstIndex(where: { $0.id == info.id }) { 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] let currentInfo = updatedInfos[index]
updatedInfos.remove(at: index) updatedInfos.remove(at: index)
updatedInfos.insert(currentInfo, at: 0) updatedInfos.insert(currentInfo, at: 0)
} else { } else {
if let positionInList = positionInList, positionInList <= updatedInfos.count { if let positionInList = positionInList, positionInList <= updatedInfos.count {
updatedInfos.insert(info, at: positionInList) updatedInfos.insert(mappedInfo, at: positionInList)
} else { } 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: case .archive:
content = .archive([id]) 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) 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) transaction.removeItemCollection(collectionId: id)
return index.flatMap { ($0, items) } return index.flatMap { ($0, items) }
} else { } else {

View File

@ -8,7 +8,7 @@ public enum AddSynchronizeInstalledStickerPacksOperationContent {
case archive([ItemCollectionId]) 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 let operationNamespace: SynchronizeInstalledStickerPacksOperationNamespace
switch namespace { switch namespace {
case Namespaces.ItemCollection.CloudStickerPacks: case Namespaces.ItemCollection.CloudStickerPacks:
@ -18,10 +18,10 @@ public func addSynchronizeInstalledStickerPacksOperation(transaction: Transactio
default: default:
return 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? var updateLocalIndex: Int32?
let tag: PeerOperationLogTag let tag: PeerOperationLogTag
let itemCollectionNamespace: ItemCollectionId.Namespace 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 { if let updateLocalIndex = updateLocalIndex {
let _ = transaction.operationLogRemoveEntry(peerId: PeerId(namespace: 0, id: 0), tag: tag, tagLocalIndex: updateLocalIndex) let _ = transaction.operationLogRemoveEntry(peerId: PeerId(namespace: 0, id: 0), tag: tag, tagLocalIndex: updateLocalIndex)
} }

View File

@ -201,7 +201,7 @@ private func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers
} }
var index = 0 var index = 0
for (_, info, item) in view.collectionInfos { 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)) entries.append(.stickerPack(index: index, info: info, topItem: item as? StickerPackItem, theme: theme))
index += 1 index += 1
} }

View File

@ -311,7 +311,8 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
} }
let account = strongSelf.context.account let account = strongSelf.context.account
if install { 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 |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in
switch result { switch result {
case let .result(info, items, installed): 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 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 return true
}))*/ }))*/
})) }))*/
} else { } else {
let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete) let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete)
|> deliverOnMainQueue).start(next: { _ in |> deliverOnMainQueue).start(next: { _ in
@ -1080,7 +1081,8 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode {
} }
let account = strongSelf.context.account let account = strongSelf.context.account
if install { 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 |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in
switch result { switch result {
case let .result(info, items, installed): 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 /*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 return true
}))*/ }))*/
})) }))*/
} else { } else {
let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete) let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete)
|> deliverOnMainQueue).start(next: { _ in |> deliverOnMainQueue).start(next: { _ in