From ee7d69819c8cdba65f5307145223d4ef6757033d Mon Sep 17 00:00:00 2001 From: Peter Date: Sat, 18 Mar 2017 17:58:07 +0300 Subject: [PATCH] no message --- TelegramCore.xcodeproj/project.pbxproj | 18 + TelegramCore/Account.swift | 27 +- TelegramCore/AccountIntermediateState.swift | 18 +- TelegramCore/AccountManager.swift | 12 +- .../AccountStateManagementUtils.swift | 156 ++++++++- TelegramCore/LoadedStickerpack.swift | 123 +++++++ ...onizeInstalledStickerPacksOperations.swift | 316 +++++++++++++++++- TelegramCore/MediaPool.swift | 90 +++++ TelegramCore/MultipartUpload.swift | 2 +- TelegramCore/Network.swift | 4 +- TelegramCore/StickerManagement.swift | 8 +- TelegramCore/StickerPack.swift | 4 +- .../StickerPackInteractiveOperations.swift | 54 +++ TelegramCore/StickerSetInstallation.swift | 4 +- ...onizeInstalledStickerPacksOperations.swift | 9 +- 15 files changed, 813 insertions(+), 32 deletions(-) create mode 100644 TelegramCore/LoadedStickerpack.swift create mode 100644 TelegramCore/MediaPool.swift create mode 100644 TelegramCore/StickerPackInteractiveOperations.swift diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 97a59f6e15..0bb017cc02 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -234,6 +234,8 @@ D0528E661E65C82400E2FEF5 /* UpdateContactName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E641E65C82400E2FEF5 /* UpdateContactName.swift */; }; D0528E6A1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */; }; D0528E6B1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */; }; + D05452071E7B5093006EEF19 /* LoadedStickerpack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05452061E7B5093006EEF19 /* LoadedStickerpack.swift */; }; + D05452081E7B5093006EEF19 /* LoadedStickerpack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05452061E7B5093006EEF19 /* LoadedStickerpack.swift */; }; D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */; }; D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */; }; D0561DEA1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */; }; @@ -389,11 +391,15 @@ D0BC38791E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC38781E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift */; }; D0BC387B1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */; }; D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */; }; + D0BE383E1E7C5995000079AF /* MediaPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE383D1E7C5995000079AF /* MediaPool.swift */; }; + D0BE383F1E7C5995000079AF /* MediaPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE383D1E7C5995000079AF /* MediaPool.swift */; }; D0BEAF5D1E54941B00BD963D /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5C1E54941B00BD963D /* Authorization.swift */; }; D0BEAF5E1E54941B00BD963D /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5C1E54941B00BD963D /* Authorization.swift */; }; D0BEAF601E54ACF900BD963D /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */; }; D0BEAF611E54ACF900BD963D /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */; }; D0CAF2EA1D75EC600011F558 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */; }; + D0D748021E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D748011E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift */; }; + D0D748031E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D748011E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift */; }; D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */; }; D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354F1DE36900000195EB /* ChatContextResult.swift */; }; D0DC35511DE36908000195EB /* RequestChatContextResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */; }; @@ -595,6 +601,7 @@ D0528E5F1E65B94E00E2FEF5 /* SingleMessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleMessageView.swift; sourceTree = ""; }; D0528E641E65C82400E2FEF5 /* UpdateContactName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateContactName.swift; sourceTree = ""; }; D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebpagePreview.swift; sourceTree = ""; }; + D05452061E7B5093006EEF19 /* LoadedStickerpack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadedStickerpack.swift; sourceTree = ""; }; D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatePeerInfo.swift; sourceTree = ""; }; D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelAdmins.swift; sourceTree = ""; }; D05A32E01E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatedAccountPrivacySettings.swift; sourceTree = ""; }; @@ -686,9 +693,11 @@ D0BC38761E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizePinnedChatsOperations.swift; sourceTree = ""; }; D0BC38781E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizePinnedChatsOperation.swift; sourceTree = ""; }; D0BC387A1E40D2880044D6FE /* TogglePeerChatPinned.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TogglePeerChatPinned.swift; sourceTree = ""; }; + D0BE383D1E7C5995000079AF /* MediaPool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPool.swift; sourceTree = ""; }; D0BEAF5C1E54941B00BD963D /* Authorization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = ""; }; D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = ""; }; D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MtProtoKitDynamic.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/MtProtoKitDynamic.framework"; sourceTree = ""; }; + D0D748011E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerPackInteractiveOperations.swift; sourceTree = ""; }; D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestChatContextResults.swift; sourceTree = ""; }; D0DC354F1DE36900000195EB /* ChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatContextResult.swift; sourceTree = ""; }; D0DF0C891D819C7E008AEB01 /* JoinChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinChannel.swift; sourceTree = ""; }; @@ -786,6 +795,8 @@ D01D6BF81E42A713006151C6 /* SearchStickers.swift */, D049EAD71E43DAD200A2CD3A /* ManagedRecentStickers.swift */, C251D7421E65E50500283EDE /* StickerSetInstallation.swift */, + D0D748011E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift */, + D05452061E7B5093006EEF19 /* LoadedStickerpack.swift */, ); name = "Sticker Management"; sourceTree = ""; @@ -1001,6 +1012,7 @@ D03B0D571D631A6900955575 /* MultipartFetch.swift */, D03C53761DAFF20F004C17B3 /* MultipartUpload.swift */, D03B0D581D631A6900955575 /* Network.swift */, + D0BE383D1E7C5995000079AF /* MediaPool.swift */, D03B0D591D631A6900955575 /* Serialization.swift */, ); name = Network; @@ -1440,6 +1452,7 @@ D08774FE1E3E3A3500A97350 /* GlobalNotificationSettings.swift in Sources */, D00C7CCF1E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */, D03B0CB91D62233400955575 /* Either.swift in Sources */, + D0D748021E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */, D03B0CBD1D62234300955575 /* Regex.swift in Sources */, D03B0D661D631A8B00955575 /* AccountSettings.swift in Sources */, D0B843B91DA7FF30005F29E1 /* NBMetadataCoreTest.m in Sources */, @@ -1514,6 +1527,7 @@ D03B0CF91D62250800955575 /* TelegramMediaMap.swift in Sources */, D0BC38791E40BAF20044D6FE /* SynchronizePinnedChatsOperation.swift in Sources */, D050F2101E48AB0600988324 /* InteractivePhoneFormatter.swift in Sources */, + D0BE383E1E7C5995000079AF /* MediaPool.swift in Sources */, D03B0D671D631A8B00955575 /* AccountViewTracker.swift in Sources */, D0B843BB1DA7FF30005F29E1 /* NBMetadataCoreTestMapper.m in Sources */, D03B0D101D62255C00955575 /* UpdatesApiUtils.swift in Sources */, @@ -1535,6 +1549,7 @@ D0FA8BB91E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */, D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */, D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */, + D05452071E7B5093006EEF19 /* LoadedStickerpack.swift in Sources */, D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */, D0B843971DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift in Sources */, D0448CA21E291B14005A61A7 /* FetchSecretFileResource.swift in Sources */, @@ -1659,6 +1674,7 @@ D050F26E1E4A5B6D00988324 /* RemovePeerChat.swift in Sources */, D0613FD81E606B3B00202CDB /* ConvertGroupToSupergroup.swift in Sources */, D01D6BFA1E42A718006151C6 /* SearchStickers.swift in Sources */, + D0D748031E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */, C2A315C01E2E776A00D89000 /* RequestStartBot.swift in Sources */, D0F7B1EA1E045C87007EB8A5 /* ChangePeerNotificationSettings.swift in Sources */, D0B418A71D7E0592004562A4 /* Fetch.swift in Sources */, @@ -1733,6 +1749,7 @@ D001F3E81E128A1C007A8C60 /* ChannelState.swift in Sources */, C2366C8A1E4F40480097CCFF /* SupportPeerId.swift in Sources */, D0B844451DAB91FD005F29E1 /* AccountViewTracker.swift in Sources */, + D0BE383F1E7C5995000079AF /* MediaPool.swift in Sources */, D050F2601E4A5AD500988324 /* AutoremoveTimeoutMessageAttribute.swift in Sources */, D049EAD91E43DAD200A2CD3A /* ManagedRecentStickers.swift in Sources */, D0B418A61D7E0592004562A4 /* CloudFileMediaResource.swift in Sources */, @@ -1754,6 +1771,7 @@ D0B8444B1DAB91FD005F29E1 /* ManagedSynchronizePeerReadStates.swift in Sources */, D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */, D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */, + D05452081E7B5093006EEF19 /* LoadedStickerpack.swift in Sources */, D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */, D0DC35521DE36908000195EB /* ChatContextResult.swift in Sources */, D0F7AB301DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */, diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index 92c334f457..2af073004b 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -99,6 +99,7 @@ public func ==(lhs: AuthorizedAccountState.State, rhs: AuthorizedAccountState.St } public class UnauthorizedAccount { + public let apiId: Int32 public let id: AccountRecordId public let appGroupPath: String public let basePath: String @@ -112,7 +113,8 @@ public class UnauthorizedAccount { public let shouldBeServiceTaskMaster = Promise() - init(id: AccountRecordId, appGroupPath: String, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, shouldKeepAutoConnection: Bool = true) { + init(apiId: Int32, id: AccountRecordId, appGroupPath: String, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, shouldKeepAutoConnection: Bool = true) { + self.apiId = apiId self.id = id self.appGroupPath = appGroupPath self.basePath = basePath @@ -143,9 +145,9 @@ public class UnauthorizedAccount { postbox.removeKeychainEntryForKey(key) }) - return initializedNetwork(datacenterId: Int(masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: self.basePath), testingEnvironment: self.testingEnvironment) + return initializedNetwork(apiId: self.apiId, datacenterId: Int(masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: self.basePath), testingEnvironment: self.testingEnvironment) |> map { network in - let updated = UnauthorizedAccount(id: self.id, appGroupPath: self.appGroupPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network) + let updated = UnauthorizedAccount(apiId: self.apiId, id: self.id, appGroupPath: self.appGroupPath, basePath: self.basePath, testingEnvironment: self.testingEnvironment, postbox: self.postbox, network: network) updated.shouldBeServiceTaskMaster.set(self.shouldBeServiceTaskMaster.get()) return updated } @@ -212,6 +214,7 @@ private var declaredEncodables: Void = { declareEncodable(OutgoingContentInfoMessageAttribute.self, f: { OutgoingContentInfoMessageAttribute(decoder: $0) }) declareEncodable(ConsumableContentMessageAttribute.self, f: { ConsumableContentMessageAttribute(decoder: $0) }) declareEncodable(TelegramMediaGame.self, f: { TelegramMediaGame(decoder: $0) }) + declareEncodable(SynchronizeInstalledStickerPacksOperation.self, f: { SynchronizeInstalledStickerPacksOperation(decoder: $0) }) return }() @@ -224,7 +227,7 @@ private func accountRecordIdPathName(_ id: AccountRecordId) -> String { return "account-\(UInt64(bitPattern: id.int64))" } -public func accountWithId(_ id: AccountRecordId, appGroupPath: String, testingEnvironment: Bool, shouldKeepAutoConnection: Bool = true) -> Signal, NoError> { +public func accountWithId(apiId: Int32, id: AccountRecordId, appGroupPath: String, testingEnvironment: Bool, shouldKeepAutoConnection: Bool = true) -> Signal, NoError> { return Signal<(String, Postbox, Coding?), NoError> { subscriber in let _ = declaredEncodables @@ -258,12 +261,12 @@ public func accountWithId(_ id: AccountRecordId, appGroupPath: String, testingEn if let accountState = accountState { switch accountState { case let unauthorizedState as UnauthorizedAccountState: - return initializedNetwork(datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) + return initializedNetwork(apiId: apiId, datacenterId: Int(unauthorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) |> map { network -> Either in - .left(value: UnauthorizedAccount(id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) + .left(value: UnauthorizedAccount(apiId: apiId, id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) } case let authorizedState as AuthorizedAccountState: - return initializedNetwork(datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) + return initializedNetwork(apiId: apiId, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) |> map { network -> Either in return .right(value: Account(id: id, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, peerId: authorizedState.peerId)) } @@ -272,9 +275,9 @@ public func accountWithId(_ id: AccountRecordId, appGroupPath: String, testingEn } } - return initializedNetwork(datacenterId: 2, keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) + return initializedNetwork(apiId: apiId, datacenterId: 2, keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment) |> map { network -> Either in - return .left(value: UnauthorizedAccount(id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) + return .left(value: UnauthorizedAccount(apiId: apiId, id: id, appGroupPath: appGroupPath, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection)) } } } @@ -457,7 +460,7 @@ public class Account { appSandbox = .boolTrue #endif - return network.request(Api.functions.account.registerDevice(tokenType: 1, token: tokenString, deviceModel: "iPhone Simulator", systemVersion: systemVersion, appVersion: appVersionString, appSandbox: appSandbox)) + return network.request(Api.functions.account.registerDevice(tokenType: 1, token: tokenString, deviceModel: "iPhone", systemVersion: systemVersion, appVersion: appVersionString, appSandbox: appSandbox)) |> retryRequest |> mapToSignal { _ -> Signal in return .complete() @@ -490,7 +493,7 @@ public class Account { appSandbox = .boolTrue #endif - return network.request(Api.functions.account.registerDevice(tokenType: 9, token: tokenString, deviceModel: "iPhone Simulator", systemVersion: systemVersion, appVersion: appVersionString, appSandbox: appSandbox)) + return network.request(Api.functions.account.registerDevice(tokenType: 9, token: tokenString, deviceModel: "iPhone", systemVersion: systemVersion, appVersion: appVersionString, appSandbox: appSandbox)) |> retryRequest |> mapToSignal { _ -> Signal in return .complete() @@ -537,6 +540,8 @@ public class Account { self.managedOperationsDisposable.add(managedAutoremoveMessageOperations(postbox: self.postbox).start()) self.managedOperationsDisposable.add(managedGlobalNotificationSettings(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedSynchronizePinnedChatsOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) + self.managedOperationsDisposable.add(managedSynchronizeInstalledStickerPacksOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager, namespace: .stickers).start()) + self.managedOperationsDisposable.add(managedSynchronizeInstalledStickerPacksOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager, namespace: .masks).start()) self.managedOperationsDisposable.add(managedRecentStickers(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedRecentGifs(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedRecentlyUsedInlineBots(postbox: self.postbox, network: self.network).start()) diff --git a/TelegramCore/AccountIntermediateState.swift b/TelegramCore/AccountIntermediateState.swift index f2f2fb8233..7ae2033a9f 100644 --- a/TelegramCore/AccountIntermediateState.swift +++ b/TelegramCore/AccountIntermediateState.swift @@ -36,6 +36,12 @@ enum AccountStateUpdatePinnerPeerIdsOperation { case sync } +enum AccountStateUpdateStickerPacksOperation { + case add(Api.messages.StickerSet) + case reorder(SynchronizeInstalledStickerPacksOperationNamespace, [Int64]) + case sync +} + enum AccountStateMutationOperation { case AddMessages([StoreMessage], AddMessagesLocation) case DeleteMessagesWithGlobalIds([Int32]) @@ -60,6 +66,8 @@ enum AccountStateMutationOperation { case AddPeerInputActivity(chatPeerId: PeerId, peerId: PeerId?, activity: PeerInputActivity?) case UpdatePinnedPeerIds(AccountStateUpdatePinnerPeerIdsOperation) case ReadGlobalMessageContents([Int32]) + case UpdateMessageImpressionCount(MessageId, Int32) + case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation) } struct AccountMutableState { @@ -232,9 +240,17 @@ struct AccountMutableState { self.addOperation(.ReadGlobalMessageContents(globalIds)) } + mutating func addUpdateMessageImpressionCount(id: MessageId, count: Int32) { + self.addOperation(.UpdateMessageImpressionCount(id, count)) + } + + mutating func addUpdateInstalledStickerPacks(_ operation: AccountStateUpdateStickerPacksOperation) { + self.addOperation(.UpdateInstalledStickerPacks(operation)) + } + mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents: + case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks: break case let .AddMessages(messages, _): for message in messages { diff --git a/TelegramCore/AccountManager.swift b/TelegramCore/AccountManager.swift index e844a1e045..810d567cb7 100644 --- a/TelegramCore/AccountManager.swift +++ b/TelegramCore/AccountManager.swift @@ -14,7 +14,7 @@ private enum AccountKind { case unauthorized } -public func currentAccount(manager: AccountManager, appGroupPath: String, testingEnvironment: Bool) -> Signal?, NoError> { +public func currentAccount(apiId: Int32, manager: AccountManager, appGroupPath: String, testingEnvironment: Bool) -> Signal?, NoError> { return manager.allocatedCurrentAccountId() |> distinctUntilChanged(isEqual: { lhs, rhs in return lhs == rhs @@ -23,7 +23,7 @@ public func currentAccount(manager: AccountManager, appGroupPath: String, testin if let id = id { let reload = ValuePromise(true, ignoreRepeated: false) return reload.get() |> mapToSignal { _ -> Signal?, NoError> in - return accountWithId(id, appGroupPath: appGroupPath, testingEnvironment: testingEnvironment) + return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: testingEnvironment) |> mapToSignal { account -> Signal?, NoError> in let postbox: Postbox let initialKind: AccountKind @@ -96,7 +96,7 @@ public func logoutFromAccount(id: AccountRecordId, accountManager: AccountManage } } -public func managedCleanupAccounts(accountManager: AccountManager, appGroupPath: String) -> Signal { +public func managedCleanupAccounts(apiId: Int32, accountManager: AccountManager, appGroupPath: String) -> Signal { return Signal { subscriber in let loggedOutAccounts = Atomic<[AccountRecordId: MetaDisposable]>(value: [:]) let disposable = accountManager.accountRecords().start(next: { view in @@ -139,7 +139,7 @@ public func managedCleanupAccounts(accountManager: AccountManager, appGroupPath: disposable.dispose() } for (id, disposable) in beginList { - disposable.set(cleanupAccount(accountManager: accountManager, id: id, appGroupPath: appGroupPath).start()) + disposable.set(cleanupAccount(apiId: apiId, accountManager: accountManager, id: id, appGroupPath: appGroupPath).start()) } }) @@ -150,8 +150,8 @@ public func managedCleanupAccounts(accountManager: AccountManager, appGroupPath: } -private func cleanupAccount(accountManager: AccountManager, id: AccountRecordId, appGroupPath: String) -> Signal { - return accountWithId(id, appGroupPath: appGroupPath, testingEnvironment: false) +private func cleanupAccount(apiId: Int32, accountManager: AccountManager, id: AccountRecordId, appGroupPath: String) -> Signal { + return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: false) |> mapToSignal { account -> Signal in switch account { case .left: diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index 2635eddb5d..2bfc24b758 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -968,6 +968,20 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState, } case let .updateReadMessagesContents(messages, _, _): updatedState.addReadGlobalMessagesContents(messages) + case let .updateChannelMessageViews(channelId, id, views): + updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: id), count: views) + case let .updateNewStickerSet(stickerset): + updatedState.addUpdateInstalledStickerPacks(.add(stickerset)) + case let .updateStickerSetsOrder(flags, order): + let namespace: SynchronizeInstalledStickerPacksOperationNamespace + if (flags & (1 << 0)) != 0 { + namespace = .masks + } else { + namespace = .stickers + } + updatedState.addUpdateInstalledStickerPacks(.reorder(namespace, order)) + case .updateStickerSets: + updatedState.addUpdateInstalledStickerPacks(.sync) default: break } @@ -1301,7 +1315,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ResetReadState, .UpdatePeerNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents: + case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ResetReadState, .UpdatePeerNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -1349,6 +1363,8 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modif var updatedSecretChatTypingActivities = Set() var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:] + var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = [] + for operation in optimizedOperations(finalState.state.operations) { switch operation { case let .AddMessages(messages, location): @@ -1478,6 +1494,144 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modif for messageId in modifier.messageIdsForGlobalIds(globalIds) { markMessageContentAsConsumedRemotely(modifier: modifier, messageId: messageId) } + case let .UpdateMessageImpressionCount(id, count): + modifier.updateMessage(id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date) + } + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ViewCountMessageAttribute { + attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(count))) + break loop + } + } + return StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media) + }) + case let .UpdateInstalledStickerPacks(operation): + stickerPackOperations.append(operation) + } + } + + if !stickerPackOperations.isEmpty { + if stickerPackOperations.contains(where: { + if case .sync = $0 { + return true + } else { + return false + } + }) { + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: .stickers) + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: .masks) + } else { + var syncStickers = false + var syncMasks = false + loop: for operation in stickerPackOperations { + switch operation { + case let .add(apiSet): + let namespace: ItemCollectionId.Namespace + var items: [ItemCollectionItem] = [] + let info: StickerPackCollectionInfo + switch apiSet { + case let .stickerSet(set, 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 + } else { + fileIndexKeys = [] + } + items.append(StickerPackItem(index: ItemCollectionItemIndex(index: Int32(items.count), id: id.id), file: file, indexKeys: fileIndexKeys)) + } + } + + switch set { + case let .stickerSet(flags, _, _, _, _, _, _): + if (flags & (1 << 3)) != 0 { + namespace = Namespaces.ItemCollection.CloudMaskPacks + } else { + namespace = Namespaces.ItemCollection.CloudStickerPacks + } + } + + info = StickerPackCollectionInfo(apiSet: set, namespace: namespace) + } + + if namespace == Namespaces.ItemCollection.CloudMaskPacks && syncMasks { + continue loop + } else if namespace == Namespaces.ItemCollection.CloudStickerPacks && syncStickers { + continue loop + } + + var updatedInfos = modifier.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! StickerPackCollectionInfo } + if let index = updatedInfos.index(where: { $0.id == info.id }) { + let currentInfo = updatedInfos[index] + updatedInfos.remove(at: index) + updatedInfos.insert(currentInfo, at: 0) + } else { + updatedInfos.insert(info, at: 0) + modifier.replaceItemCollectionItems(collectionId: info.id, items: items) + } + modifier.replaceItemCollectionInfos(namespace: info.id.namespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) }) + case let .reorder(namespace, ids): + let collectionNamespace: ItemCollectionId.Namespace + switch namespace { + case .stickers: + collectionNamespace = Namespaces.ItemCollection.CloudStickerPacks + case .masks: + collectionNamespace = Namespaces.ItemCollection.CloudMaskPacks + } + let currentInfos = modifier.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } + if Set(currentInfos.map { $0.id.id }) != Set(ids) { + switch namespace { + case .stickers: + syncStickers = true + case .masks: + syncMasks = true + } + } else { + var currentDict: [ItemCollectionId: StickerPackCollectionInfo] = [:] + for info in currentInfos { + currentDict[info.id] = info + } + var updatedInfos: [StickerPackCollectionInfo] = [] + for id in ids { + let currentInfo = currentDict[ItemCollectionId(namespace: collectionNamespace, id: id)]! + updatedInfos.append(currentInfo) + } + modifier.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) }) + } + case .sync: + syncStickers = true + syncMasks = true + break loop + } + } + if syncStickers { + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: .stickers) + } + if syncMasks { + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: .masks) + } } } diff --git a/TelegramCore/LoadedStickerpack.swift b/TelegramCore/LoadedStickerpack.swift new file mode 100644 index 0000000000..f766b7246e --- /dev/null +++ b/TelegramCore/LoadedStickerpack.swift @@ -0,0 +1,123 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +extension StickerPackReference { + init(_ stickerPackInfo: StickerPackCollectionInfo) { + self = .id(id: stickerPackInfo.id.id, accessHash: stickerPackInfo.accessHash) + } + + var apiInputStickerSet: Api.InputStickerSet { + switch self { + case let .id(id, accessHash): + return .inputStickerSetID(id: id, accessHash: accessHash) + case let .name(name): + return .inputStickerSetShortName(shortName: name) + } + } +} + +public enum LoadedStickerPack { + case fetching + case none + case result(info: StickerPackCollectionInfo, items: [ItemCollectionItem], installed: Bool) +} + +public func loadedStickerPack(account: Account, reference: StickerPackReference) -> Signal { + return account.postbox.modify { modifier -> Signal in + switch reference { + case let .id(id, _): + if let info = modifier.getItemCollectionInfo(collectionId: ItemCollectionId(namespace: Namespaces.ItemCollection.CloudStickerPacks, id: id)) as? StickerPackCollectionInfo { + return account.postbox.combinedView(keys: [PostboxViewKey.itemCollectionInfo(id: info.id)]) + |> map { view in + if let view = view.views[PostboxViewKey.itemCollectionInfo(id: info.id)] as? ItemCollectionInfoView, let info = view.info as? StickerPackCollectionInfo { + return .result(info: info, items: modifier.getItemCollectionItems(collectionId: info.id), installed: true) + } else { + return .result(info: info, items: modifier.getItemCollectionItems(collectionId: info.id), installed: false) + } + } + } else if let info = modifier.getItemCollectionInfo(collectionId: ItemCollectionId(namespace: Namespaces.ItemCollection.CloudMaskPacks, id: id)) as? StickerPackCollectionInfo { + return account.postbox.combinedView(keys: [PostboxViewKey.itemCollectionInfo(id: info.id)]) + |> map { view in + if let view = view.views[PostboxViewKey.itemCollectionInfo(id: info.id)] as? ItemCollectionInfoView, let info = view.info as? StickerPackCollectionInfo { + return .result(info: info, items: modifier.getItemCollectionItems(collectionId: info.id), installed: true) + } else { + return .result(info: info, items: modifier.getItemCollectionItems(collectionId: info.id), installed: false) + } + } + } + default: + break + } + + let signal: Signal = account.network.request(Api.functions.messages.getStickerSet(stickerset: reference.apiInputStickerSet)) + |> map { Optional($0) } + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result = result else { + return .single(.none) + } + + let info: StickerPackCollectionInfo + var items: [ItemCollectionItem] = [] + switch result { + case let .stickerSet(set, packs, documents): + let namespace: ItemCollectionId.Namespace + switch set { + case let .stickerSet(flags, _, _, _, _, _, _): + if (flags & (1 << 3)) != 0 { + namespace = Namespaces.ItemCollection.CloudMaskPacks + } else { + namespace = Namespaces.ItemCollection.CloudStickerPacks + } + } + info = StickerPackCollectionInfo(apiSet: set, namespace: 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 { + 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 + } else { + fileIndexKeys = [] + } + items.append(StickerPackItem(index: ItemCollectionItemIndex(index: Int32(items.count), id: id.id), file: file, indexKeys: fileIndexKeys)) + } + } + } + + return account.postbox.combinedView(keys: [PostboxViewKey.itemCollectionInfo(id: info.id)]) + |> map { view in + if let view = view.views[PostboxViewKey.itemCollectionInfo(id: info.id)] as? ItemCollectionInfoView, let info = view.info as? StickerPackCollectionInfo { + return .result(info: info, items: items, installed: true) + } else { + return .result(info: info, items: items, installed: false) + } + } + } + return .single(.fetching) |> then(signal) + } |> switchToLatest +} diff --git a/TelegramCore/ManagedSynchronizeInstalledStickerPacksOperations.swift b/TelegramCore/ManagedSynchronizeInstalledStickerPacksOperations.swift index aa7fe267d3..8cb2ad965c 100644 --- a/TelegramCore/ManagedSynchronizeInstalledStickerPacksOperations.swift +++ b/TelegramCore/ManagedSynchronizeInstalledStickerPacksOperations.swift @@ -69,7 +69,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera } |> switchToLatest } -func managedSynchronizePinnedChatsOperations(postbox: Postbox, network: Network, stateManager: AccountStateManager, namespace: SynchronizeInstalledStickerPacksOperationNamespace) -> Signal { +func managedSynchronizeInstalledStickerPacksOperations(postbox: Postbox, network: Network, stateManager: AccountStateManager, namespace: SynchronizeInstalledStickerPacksOperationNamespace) -> Signal { return Signal { _ in let tag: PeerOperationLogTag switch namespace { @@ -121,6 +121,316 @@ func managedSynchronizePinnedChatsOperations(postbox: Postbox, network: Network, } } -private func synchronizeInstalledStickerPacks(modifier: Modifier, postbox: Postbox, network: Network, stateManager: AccountStateManager, namespace: SynchronizeInstalledStickerPacksOperationNamespace, operation: SynchronizeInstalledStickerPacksOperation) -> Signal { - return .complete() +private func hashForStickerPackInfos(_ infos: [StickerPackCollectionInfo]) -> Int32 { + var acc: UInt32 = 0 + + for info in infos { + acc = UInt32(bitPattern: Int32(bitPattern: acc &* UInt32(20261)) &+ info.hash) + } + + return Int32(bitPattern: acc % 0x7FFFFFFF) +} + +private enum SynchronizeInstalledStickerPacksError { + case restart + case done +} + +private func fetchStickerPack(network: Network, info: StickerPackCollectionInfo) -> Signal<(ItemCollectionId, [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 + } else { + fileIndexKeys = [] + } + items.append(StickerPackItem(index: ItemCollectionItemIndex(index: Int32(items.count), id: id.id), file: file, indexKeys: fileIndexKeys)) + } + } + break + } + return (info.id, items) + } + |> `catch` { _ -> Signal<(ItemCollectionId, [ItemCollectionItem]), NoError> in + return .single((info.id, [])) + } +} + +private func resolveStickerPacks(network: Network, remoteInfos: [ItemCollectionId: StickerPackCollectionInfo], localInfos: [ItemCollectionId: StickerPackCollectionInfo]) -> Signal<[ItemCollectionId: [ItemCollectionItem]], NoError> { + var signals: [Signal<(ItemCollectionId, [ItemCollectionItem]), NoError>] = [] + for (id, remoteInfo) in remoteInfos { + let localInfo = localInfos[id] + if localInfo == nil || localInfo!.hash != remoteInfo.hash { + signals.append(fetchStickerPack(network: network, info: remoteInfo)) + } + } + return combineLatest(signals) + |> map { result -> [ItemCollectionId: [ItemCollectionItem]] in + var dict: [ItemCollectionId: [ItemCollectionItem]] = [:] + for (id, items) in result { + dict[id] = items + } + return dict + } +} + +private func installRemoteStickerPacks(network: Network, infos: [StickerPackCollectionInfo]) -> Signal, NoError> { + var signals: [Signal, NoError>] = [] + for info in infos { + let install = network.request(Api.functions.messages.installStickerSet(stickerset: .inputStickerSetID(id: info.id.id, accessHash: info.accessHash), archived: .boolFalse)) + |> map { result -> Set in + switch result { + case .stickerSetInstallResultSuccess: + return Set() + case let .stickerSetInstallResultArchive(archivedSets): + var archivedIds = Set() + for archivedSet in archivedSets { + switch archivedSet { + case let .stickerSetCovered(set, _): + archivedIds.insert(StickerPackCollectionInfo(apiSet: set, namespace: info.id.namespace).id) + case let .stickerSetMultiCovered(set, _): + archivedIds.insert(StickerPackCollectionInfo(apiSet: set, namespace: info.id.namespace).id) + } + } + return archivedIds + } + } + |> `catch` { _ -> Signal, NoError> in + return .single(Set()) + } + signals.append(install) + } + return combineLatest(signals) + |> map { idsSets -> Set in + var result = Set() + for ids in idsSets { + result.formUnion(ids) + } + return result + } +} + +private func archiveRemoteStickerPacks(network: Network, infos: [StickerPackCollectionInfo]) -> Signal { + var signals: [Signal] = [] + for info in infos { + let archive = network.request(Api.functions.messages.installStickerSet(stickerset: .inputStickerSetID(id: info.id.id, accessHash: info.accessHash), archived: .boolTrue)) + |> mapToSignal { _ -> Signal in + return .complete() + } + |> `catch` { _ -> Signal in + return .complete() + } + signals.append(archive) + } + return combineLatest(signals) |> map { _ in return Void() } +} + +private func reorderRemoteStickerPacks(network: Network, namespace: SynchronizeInstalledStickerPacksOperationNamespace, ids: [ItemCollectionId]) -> Signal { + var flags: Int32 = 0 + switch namespace { + case .stickers: + break + case .masks: + flags |= (1 << 0) + } + return network.request(Api.functions.messages.reorderStickerSets(flags: flags, order: ids.map { $0.id })) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } +} + +private func synchronizeInstalledStickerPacks(modifier: Modifier, 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 = modifier.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo } + let initialLocalHash = hashForStickerPackInfos(localCollectionInfos) + + let request: Signal + switch namespace { + case .stickers: + request = network.request(Api.functions.messages.getAllStickers(hash: initialLocalHash)) + case .masks: + request = network.request(Api.functions.messages.getMaskStickers(hash: initialLocalHash)) + } + + let sequence = request + |> retryRequest + |> mapError { _ -> SynchronizeInstalledStickerPacksError in + return .restart + } + |> mapToSignal { result -> Signal in + return postbox.modify { modifier -> Signal in + let checkLocalCollectionInfos = modifier.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.modify { modifier -> Void in + modifier.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: remoteCollectionInfos.map { ($0.id, $0) }) + for (id, items) in replaceItems { + modifier.replaceItemCollectionItems(collectionId: id, items: items) + } + for id in localCollectionIds.subtracting(remoteCollectionIds) { + modifier.replaceItemCollectionItems(collectionId: id, items: []) + } + } |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart }) |> then(.fail(.done)) + } + } + } 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 archivedIds = (archiveRemoteStickerPacks(network: network, infos: removeRemoteCollectionInfos) + |> 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(archivedIds, resolvedItems) + |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart } + |> mapToSignal { archivedIds, replaceItems -> Signal in + return (postbox.modify { modifier -> Void in + modifier.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: resultingCollectionInfos.filter({ info in + return !archivedIds.contains(info.id) + }).map({ ($0.id, $0) })) + for (id, items) in replaceItems { + if !archivedIds.contains(id) { + modifier.replaceItemCollectionItems(collectionId: id, items: items) + } + } + for id in localCollectionIds.subtracting(resultingCollectionIds).union(archivedIds) { + modifier.replaceItemCollectionItems(collectionId: id, items: []) + } + } |> mapError { _ -> SynchronizeInstalledStickerPacksError in return .restart }) |> 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() + } + } diff --git a/TelegramCore/MediaPool.swift b/TelegramCore/MediaPool.swift new file mode 100644 index 0000000000..014abb590e --- /dev/null +++ b/TelegramCore/MediaPool.swift @@ -0,0 +1,90 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac + import MtProtoKitMac +#else + import Postbox + import SwiftSignalKit + import MtProtoKitDynamic +#endif + +#if os(macOS) + private typealias SignalKitTimer = SwiftSignalKitMac.Timer +#else + private typealias SignalKitTimer = SwiftSignalKit.Timer +#endif + +struct MediaPoolDownloadPart { + let location: Api.InputFileLocation + let offset: Int + let length: Int +} + +private final class DownloadRequest { + +} + +final class MediaPool { + private let queue = Queue() + + private var downloads: [Int32: Download] = [:] + private var inactiveDownloads = Set() + private var nextDownloadId: Int32 = 0 + private var collectInactiveDownloadsTimer: SignalKitTimer? + + private var requests = Bag() + + private var interfaces = Bag() + + init() { + + } + + private func takeInactiveDownload() { + + } + + func part(_ part: MediaPoolDownloadPart) -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.queue.async { + let index = self.requests.add(DownloadRequest()) + disposable.set(ActionDisposable { + self.queue.async { + self.requests.remove(index) + } + }) + } + return disposable + } + } + + func poolInterface() -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.queue.async { + let index = self.interfaces.add(MediaPoolInterface()) + disposable.set(ActionDisposable { + self.queue.async { + self.interfaces.remove(index) + self.update() + } + }) + } + return disposable + } + } + + private func update() { + + } +} + +final class MediaPoolInterface { + func part(_ part: MediaPoolDownloadPart) -> Signal { + return Signal { _ in + return EmptyDisposable + } + } +} diff --git a/TelegramCore/MultipartUpload.swift b/TelegramCore/MultipartUpload.swift index a3e5049c84..9aeabd0041 100644 --- a/TelegramCore/MultipartUpload.swift +++ b/TelegramCore/MultipartUpload.swift @@ -168,7 +168,7 @@ private final class MultipartUploadManager { self.progress = progress self.completed = completed - if let hintFileSize = hintFileSize, hintFileSize > 1 * 1024 * 1024 { + if let hintFileSize = hintFileSize, hintFileSize > 5 * 1024 * 1024 { self.defaultPartSize = 512 * 1024 self.bigTotalParts = (hintFileSize / self.defaultPartSize) + (hintFileSize % self.defaultPartSize == 0 ? 0 : 1) } else { diff --git a/TelegramCore/Network.swift b/TelegramCore/Network.swift index ff0653a5d5..f78cce631c 100644 --- a/TelegramCore/Network.swift +++ b/TelegramCore/Network.swift @@ -94,7 +94,7 @@ private var registeredLoggingFunctions: Void = { registerLoggingFunctions() }() -func initializedNetwork(datacenterId: Int, keychain: Keychain, networkUsageInfoPath: String?, testingEnvironment: Bool) -> Signal { +func initializedNetwork(apiId: Int32, datacenterId: Int, keychain: Keychain, networkUsageInfoPath: String?, testingEnvironment: Bool) -> Signal { return Signal { subscriber in Queue.concurrentDefaultQueue().async { let _ = registeredLoggingFunctions @@ -103,7 +103,7 @@ func initializedNetwork(datacenterId: Int, keychain: Keychain, networkUsageInfoP let apiEnvironment = MTApiEnvironment() - apiEnvironment.apiId = 1 + apiEnvironment.apiId = apiId apiEnvironment.layer = NSNumber(value: Int(serialization.currentLayer())) let context = MTContext(serialization: serialization, apiEnvironment: apiEnvironment)! diff --git a/TelegramCore/StickerManagement.swift b/TelegramCore/StickerManagement.swift index b53fdb9856..b655c282df 100644 --- a/TelegramCore/StickerManagement.swift +++ b/TelegramCore/StickerManagement.swift @@ -19,6 +19,11 @@ private func hashForInfos(_ infos: [StickerPackCollectionInfo]) -> Int32 { } func manageStickerPacks(network: Network, postbox: Postbox) -> Signal { + return postbox.modify { modifier -> Void in + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: .stickers) + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: .masks) + } + let currentHash = postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudStickerPacks], aroundIndex: nil, count: 1) |> take(1) |> map { view -> Int32 in @@ -38,7 +43,7 @@ func manageStickerPacks(network: Network, postbox: Postbox) -> Signal Signal = StickerPack; for pack in packs { switch pack { case let .stickerPack(text, fileIds): diff --git a/TelegramCore/StickerPack.swift b/TelegramCore/StickerPack.swift index 2ee9c2b28d..9b60f99042 100644 --- a/TelegramCore/StickerPack.swift +++ b/TelegramCore/StickerPack.swift @@ -134,7 +134,7 @@ public final class StickerPackItem: ItemCollectionItem, Equatable { } extension StickerPackCollectionInfo { - convenience init(apiSet: Api.StickerSet) { + convenience init(apiSet: Api.StickerSet, namespace: ItemCollectionId.Namespace) { switch apiSet { case let .stickerSet(flags, id, accessHash, title, shortName, count, nHash): var setFlags: StickerPackCollectionInfoFlags = StickerPackCollectionInfoFlags() @@ -144,7 +144,7 @@ extension StickerPackCollectionInfo { if (flags & (1 << 3)) != 0 { setFlags.insert(.masks) } - self.init(id: ItemCollectionId(namespace: Namespaces.ItemCollection.CloudStickerPacks, id: id), flags: setFlags, accessHash: accessHash, title: title, shortName: shortName, hash: nHash, count: count) + self.init(id: ItemCollectionId(namespace: namespace, id: id), flags: setFlags, accessHash: accessHash, title: title, shortName: shortName, hash: nHash, count: count) } } } diff --git a/TelegramCore/StickerPackInteractiveOperations.swift b/TelegramCore/StickerPackInteractiveOperations.swift new file mode 100644 index 0000000000..9b71c0c4e6 --- /dev/null +++ b/TelegramCore/StickerPackInteractiveOperations.swift @@ -0,0 +1,54 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +public func addStickerPackInteractively(postbox: Postbox, info: StickerPackCollectionInfo, items: [ItemCollectionItem]) -> Signal { + return postbox.modify { modifier -> Void in + let namespace: SynchronizeInstalledStickerPacksOperationNamespace? + switch info.id.namespace { + case Namespaces.ItemCollection.CloudStickerPacks: + namespace = .stickers + case Namespaces.ItemCollection.CloudMaskPacks: + namespace = .masks + default: + namespace = nil + } + if let namespace = namespace { + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: namespace) + var updatedInfos = modifier.getItemCollectionsInfos(namespace: info.id.namespace).map { $0.1 as! StickerPackCollectionInfo } + if let index = updatedInfos.index(where: { $0.id == info.id }) { + let currentInfo = updatedInfos[index] + updatedInfos.remove(at: index) + updatedInfos.insert(currentInfo, at: 0) + } else { + updatedInfos.insert(info, at: 0) + modifier.replaceItemCollectionItems(collectionId: info.id, items: items) + } + modifier.replaceItemCollectionInfos(namespace: info.id.namespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) }) + } + } +} + + +public func removeStickerPackInteractively(postbox: Postbox, id: ItemCollectionId) -> Signal { + return postbox.modify { modifier -> Void in + let namespace: SynchronizeInstalledStickerPacksOperationNamespace? + switch id.namespace { + case Namespaces.ItemCollection.CloudStickerPacks: + namespace = .stickers + case Namespaces.ItemCollection.CloudMaskPacks: + namespace = .masks + default: + namespace = nil + } + if let namespace = namespace { + addSynchronizeInstalledStickerPacksOperation(modifier: modifier, namespace: namespace) + modifier.removeItemCollection(collectionId: id) + } + } +} diff --git a/TelegramCore/StickerSetInstallation.swift b/TelegramCore/StickerSetInstallation.swift index 3d74a79ffa..7c7360b9aa 100644 --- a/TelegramCore/StickerSetInstallation.swift +++ b/TelegramCore/StickerSetInstallation.swift @@ -49,7 +49,7 @@ public func requestStickerSet(account:Account, reference: StickerPackReference) switch result { case let .stickerSet(set, packs, documents): - info = StickerPackCollectionInfo(apiSet: set) + info = StickerPackCollectionInfo(apiSet: set, namespace: Namespaces.ItemCollection.CloudStickerPacks) switch set { case let .stickerSet(data): @@ -150,7 +150,7 @@ public func installStickerSetInteractively(account:Account, info: StickerPackCol apiDocuments = covers } - let info = StickerPackCollectionInfo(apiSet: apiSet) + let info = StickerPackCollectionInfo(apiSet: apiSet, namespace: Namespaces.ItemCollection.CloudStickerPacks) var items:[StickerPackItem] = [] for apiDocument in apiDocuments { diff --git a/TelegramCore/SynchronizeInstalledStickerPacksOperations.swift b/TelegramCore/SynchronizeInstalledStickerPacksOperations.swift index cc0d64d2a2..de3e9abc6c 100644 --- a/TelegramCore/SynchronizeInstalledStickerPacksOperations.swift +++ b/TelegramCore/SynchronizeInstalledStickerPacksOperations.swift @@ -31,17 +31,24 @@ final class SynchronizeInstalledStickerPacksOperation: Coding { func addSynchronizeInstalledStickerPacksOperation(modifier: Modifier, namespace: SynchronizeInstalledStickerPacksOperationNamespace) { var updateLocalIndex: Int32? let tag: PeerOperationLogTag + let itemCollectionNamespace: ItemCollectionId.Namespace switch namespace { case .stickers: tag = OperationLogTags.SynchronizeInstalledStickerPacks + itemCollectionNamespace = Namespaces.ItemCollection.CloudStickerPacks case .masks: tag = OperationLogTags.SynchronizeInstalledMasks + itemCollectionNamespace = Namespaces.ItemCollection.CloudMaskPacks } + var previousSrickerPackIds: [ItemCollectionId]? modifier.operationLogEnumerateEntries(peerId: PeerId(namespace: 0, id: 0), tag: tag, { entry in updateLocalIndex = entry.tagLocalIndex + if let operation = entry.contents as? SynchronizeInstalledStickerPacksOperation { + previousSrickerPackIds = operation.previousPacks + } return false }) - let operationContents = SynchronizePinnedChatsOperation(previousPeerIds: modifier.getPinnedPeerIds()) + let operationContents = SynchronizeInstalledStickerPacksOperation(previousPacks: previousSrickerPackIds ?? modifier.getItemCollectionsInfos(namespace: itemCollectionNamespace).map { $0.0 }) if let updateLocalIndex = updateLocalIndex { let _ = modifier.operationLogRemoveEntry(peerId: PeerId(namespace: 0, id: 0), tag: tag, tagLocalIndex: updateLocalIndex) }