From a0c595f5ab785bb844e3e6c6c1f7fe6bfa23ab47 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 20 Nov 2018 14:47:50 +0300 Subject: [PATCH 1/2] Fixed recently used stickers sync Fixed auth code expired error --- TelegramCore.xcodeproj/project.pbxproj | 12 ++ TelegramCore/Account.swift | 2 +- TelegramCore/AccountManager.swift | 1 + TelegramCore/Authorization.swift | 2 +- TelegramCore/ContactSyncManager.swift | 2 +- .../ManagedSecretChatOutgoingOperations.swift | 2 +- ...nchronizeRecentlyUsedMediaOperations.swift | 183 ++++++++++++++++++ TelegramCore/Namespaces.swift | 1 + TelegramCore/StickerManagement.swift | 1 + ...nchronizeRecentlyUsedMediaOperations.swift | 105 ++++++++++ 10 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 TelegramCore/ManagedSynchronizeRecentlyUsedMediaOperations.swift create mode 100644 TelegramCore/SynchronizeRecentlyUsedMediaOperations.swift diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 4538734e66..f50f297bf1 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -329,6 +329,10 @@ 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 */; }; + D0529D2421A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0529D2321A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift */; }; + D0529D2521A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0529D2321A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift */; }; + D0529D2721A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0529D2621A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift */; }; + D0529D2821A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0529D2621A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift */; }; D053B3FB1F1651FA00E2D58A /* MonotonicTime.h in Headers */ = {isa = PBXBuildFile; fileRef = D053B3F91F1651FA00E2D58A /* MonotonicTime.h */; }; D053B3FC1F1651FA00E2D58A /* MonotonicTime.m in Sources */ = {isa = PBXBuildFile; fileRef = D053B3FA1F1651FA00E2D58A /* MonotonicTime.m */; }; D053B3FE1F16534400E2D58A /* MonotonicTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = D053B3FD1F16534400E2D58A /* MonotonicTime.swift */; }; @@ -939,6 +943,8 @@ 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 = ""; }; + D0529D2321A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizeRecentlyUsedMediaOperations.swift; sourceTree = ""; }; + D0529D2621A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeRecentlyUsedMediaOperations.swift; sourceTree = ""; }; D053B3F91F1651FA00E2D58A /* MonotonicTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MonotonicTime.h; sourceTree = ""; }; D053B3FA1F1651FA00E2D58A /* MonotonicTime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MonotonicTime.m; sourceTree = ""; }; D053B3FD1F16534400E2D58A /* MonotonicTime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonotonicTime.swift; sourceTree = ""; }; @@ -1487,6 +1493,8 @@ D00422D221677F4500719B67 /* ManagedAccountPresence.swift */, D0A8998E217A37A000759EE6 /* NotificationAutolockReportManager.swift */, 09028385218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift */, + D0529D2321A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift */, + D0529D2621A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift */, ); name = State; sourceTree = ""; @@ -2392,6 +2400,7 @@ D058E0D11E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */, D0BC38751E40A7F70044D6FE /* RemovePeerChat.swift in Sources */, D09F9DC820767D2C00DB4DE1 /* Api3.swift in Sources */, + D0529D2721A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift in Sources */, D0AB0B961D662F0B002C78E7 /* ManagedChatListHoles.swift in Sources */, D05A32E41E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */, D01A21A61F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */, @@ -2426,6 +2435,7 @@ D0B85AC51F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */, D0B843C31DA7FF30005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */, C2366C861E4F403C0097CCFF /* AddressNames.swift in Sources */, + D0529D2421A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift in Sources */, D0FA08BB2046B37900DD23FC /* ContentPrivacySettings.swift in Sources */, D0F8C39D20178B9B00236FC5 /* GroupFeedPeers.swift in Sources */, D0B843C11DA7FF30005F29E1 /* NBPhoneMetaData.m in Sources */, @@ -2668,6 +2678,7 @@ D001F3E91E128A1C007A8C60 /* SecretChatState.swift in Sources */, D08F4A6A1E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift in Sources */, D0B8444B1DAB91FD005F29E1 /* ManagedSynchronizePeerReadStates.swift in Sources */, + D0529D2521A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift in Sources */, D06ECFC920B810D300C576C2 /* TermsOfService.swift in Sources */, D0F8C39E20178B9B00236FC5 /* GroupFeedPeers.swift in Sources */, D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */, @@ -2775,6 +2786,7 @@ D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */, D001F3F11E128A1C007A8C60 /* SynchronizePeerReadState.swift in Sources */, D054648F20738626002ECC1E /* SecureIdDriversLicenseValue.swift in Sources */, + D0529D2821A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift in Sources */, D050F2641E4A5AEB00988324 /* ManagedSynchronizePinnedChatsOperations.swift in Sources */, D0575AF21E9FFA5D006F2541 /* SynchronizeSavedGifsOperation.swift in Sources */, D0528E661E65C82400E2FEF5 /* UpdateContactName.swift in Sources */, diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index 61cb8dc602..0dc16ffe9a 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -1064,7 +1064,7 @@ public class Account { 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(managedSynchronizeMarkFeaturedStickerPacksAsSeenOperations(postbox: self.postbox, network: self.network).start()) - self.managedOperationsDisposable.add(managedRecentStickers(postbox: self.postbox, network: self.network).start()) + self.managedOperationsDisposable.add(managedSynchronizeRecentlyUsedMediaOperations(postbox: self.postbox, network: self.network, category: .stickers, revalidationContext: self.mediaReferenceRevalidationContext).start()) self.managedOperationsDisposable.add(managedSynchronizeSavedGifsOperations(postbox: self.postbox, network: self.network, revalidationContext: self.mediaReferenceRevalidationContext).start()) self.managedOperationsDisposable.add(managedSynchronizeSavedStickersOperations(postbox: self.postbox, network: self.network, revalidationContext: self.mediaReferenceRevalidationContext).start()) self.managedOperationsDisposable.add(managedRecentlyUsedInlineBots(postbox: self.postbox, network: self.network, accountPeerId: peerId).start()) diff --git a/TelegramCore/AccountManager.swift b/TelegramCore/AccountManager.swift index cf612dfc33..2dece4bb77 100644 --- a/TelegramCore/AccountManager.swift +++ b/TelegramCore/AccountManager.swift @@ -87,6 +87,7 @@ private var declaredEncodables: Void = { declareEncodable(SynchronizeChatInputStateOperation.self, f: { SynchronizeChatInputStateOperation(decoder: $0) }) declareEncodable(SynchronizeSavedGifsOperation.self, f: { SynchronizeSavedGifsOperation(decoder: $0) }) declareEncodable(SynchronizeSavedStickersOperation.self, f: { SynchronizeSavedStickersOperation(decoder: $0) }) + declareEncodable(SynchronizeRecentlyUsedMediaOperation.self, f: { SynchronizeRecentlyUsedMediaOperation(decoder: $0) }) declareEncodable(CacheStorageSettings.self, f: { CacheStorageSettings(decoder: $0) }) declareEncodable(LocalizationSettings.self, f: { LocalizationSettings(decoder: $0) }) declareEncodable(LocalizationListState.self, f: { LocalizationListState(decoder: $0) }) diff --git a/TelegramCore/Authorization.swift b/TelegramCore/Authorization.swift index c5f4300bd3..4dc4bca447 100644 --- a/TelegramCore/Authorization.swift +++ b/TelegramCore/Authorization.swift @@ -203,7 +203,7 @@ public func authorizeWithCode(account: UnauthorizedAccount, code: String, termsO return .fail(.limitExceeded) } else if errorDescription == "PHONE_CODE_INVALID" { return .fail(.invalidCode) - } else if errorDescription == "CODE_HASH_EXPIRED" { + } else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" { return .fail(.codeExpired) } else if errorDescription == "PHONE_NUMBER_UNOCCUPIED" { return .single(.signUp) diff --git a/TelegramCore/ContactSyncManager.swift b/TelegramCore/ContactSyncManager.swift index 486c2cd825..c6ccd94841 100644 --- a/TelegramCore/ContactSyncManager.swift +++ b/TelegramCore/ContactSyncManager.swift @@ -71,7 +71,7 @@ private final class ContactSyncManagerImpl { let timestamp = CFAbsoluteTimeGetCurrent() let shouldUpdate: Bool if let lastContactPresencesRequestTimestamp = strongSelf.lastContactPresencesRequestTimestamp { - if timestamp > lastContactPresencesRequestTimestamp + 30.0 * 60.0 { + if timestamp > lastContactPresencesRequestTimestamp + 2.0 * 60.0 { shouldUpdate = true } else { shouldUpdate = false diff --git a/TelegramCore/ManagedSecretChatOutgoingOperations.swift b/TelegramCore/ManagedSecretChatOutgoingOperations.swift index 9119f74253..e11b1be0d8 100644 --- a/TelegramCore/ManagedSecretChatOutgoingOperations.swift +++ b/TelegramCore/ManagedSecretChatOutgoingOperations.swift @@ -1195,7 +1195,7 @@ private func sendMessage(postbox: Postbox, network: Network, messageId: MessageI } for file in sentStickers { - transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20) + addRecentlyUsedSticker(transaction: transaction, fileReference: .standalone(media: file)) } } } diff --git a/TelegramCore/ManagedSynchronizeRecentlyUsedMediaOperations.swift b/TelegramCore/ManagedSynchronizeRecentlyUsedMediaOperations.swift new file mode 100644 index 0000000000..7ef8ed8245 --- /dev/null +++ b/TelegramCore/ManagedSynchronizeRecentlyUsedMediaOperations.swift @@ -0,0 +1,183 @@ +import Foundation +#if os(macOS) +import PostboxMac +import SwiftSignalKitMac +import MtProtoKitMac +#else +import Postbox +import SwiftSignalKit +import MtProtoKitDynamic +#endif + +private final class ManagedSynchronizeRecentlyUsedMediaOperationsHelper { + var operationDisposables: [Int32: Disposable] = [:] + + func update(_ entries: [PeerMergedOperationLogEntry]) -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) { + var disposeOperations: [Disposable] = [] + var beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)] = [] + + var hasRunningOperationForPeerId = Set() + var validMergedIndices = Set() + for entry in entries { + if !hasRunningOperationForPeerId.contains(entry.peerId) { + hasRunningOperationForPeerId.insert(entry.peerId) + validMergedIndices.insert(entry.mergedIndex) + + if self.operationDisposables[entry.mergedIndex] == nil { + let disposable = MetaDisposable() + beginOperations.append((entry, disposable)) + self.operationDisposables[entry.mergedIndex] = disposable + } + } + } + + var removeMergedIndices: [Int32] = [] + for (mergedIndex, disposable) in self.operationDisposables { + if !validMergedIndices.contains(mergedIndex) { + removeMergedIndices.append(mergedIndex) + disposeOperations.append(disposable) + } + } + + for mergedIndex in removeMergedIndices { + self.operationDisposables.removeValue(forKey: mergedIndex) + } + + return (disposeOperations, beginOperations) + } + + func reset() -> [Disposable] { + let disposables = Array(self.operationDisposables.values) + self.operationDisposables.removeAll() + return disposables + } +} + +private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ f: @escaping (Transaction, PeerMergedOperationLogEntry?) -> Signal) -> Signal { + return postbox.transaction { transaction -> Signal in + var result: PeerMergedOperationLogEntry? + transaction.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in + if let entry = entry, let _ = entry.mergedIndex, entry.contents is SynchronizeRecentlyUsedMediaOperation { + result = entry.mergedEntry! + return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none) + } else { + return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none) + } + }) + + return f(transaction, result) + } |> switchToLatest +} + +func managedSynchronizeRecentlyUsedMediaOperations(postbox: Postbox, network: Network, category: RecentlyUsedMediaCategory, revalidationContext: MediaReferenceRevalidationContext) -> Signal { + return Signal { _ in + let tag: PeerOperationLogTag + switch category { + case .stickers: + tag = OperationLogTags.SynchronizeRecentlyUsedStickers + } + + let helper = Atomic(value: ManagedSynchronizeRecentlyUsedMediaOperationsHelper()) + + let disposable = postbox.mergedOperationLogView(tag: tag, limit: 10).start(next: { view in + let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) in + return helper.update(view.entries) + } + + for disposable in disposeOperations { + disposable.dispose() + } + + for (entry, disposable) in beginOperations { + let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal in + if let entry = entry { + if let operation = entry.contents as? SynchronizeRecentlyUsedMediaOperation { + return synchronizeRecentlyUsedMedia(transaction: transaction, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation) + } else { + assertionFailure() + } + } + return .complete() + }) + |> then(postbox.transaction { transaction -> Void in + let _ = transaction.operationLogRemoveEntry(peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex) + }) + + disposable.set(signal.start()) + } + }) + + return ActionDisposable { + let disposables = helper.with { helper -> [Disposable] in + return helper.reset() + } + for disposable in disposables { + disposable.dispose() + } + disposable.dispose() + } + } +} + +private enum SaveRecentlyUsedMediaError { + case generic + case invalidReference +} + +private func synchronizeRecentlyUsedMedia(transaction: Transaction, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeRecentlyUsedMediaOperation) -> Signal { + switch operation.content { + case let .add(id, accessHash, fileReference): + guard let fileReference = fileReference else { + return .complete() + } + + let addSticker: (Data) -> Signal = { fileReference in + return network.request(Api.functions.messages.saveRecentSticker(flags: 0, id: .inputDocument(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), unsave: .boolFalse)) + |> mapError { error -> SaveRecentlyUsedMediaError in + if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") { + return .invalidReference + } + return .generic + } + } + + let initialSignal: Signal + if let reference = (fileReference.media.resource as? CloudDocumentMediaResource)?.fileReference { + initialSignal = addSticker(reference) + } else { + initialSignal = .fail(.invalidReference) + } + + return initialSignal + |> `catch` { error -> Signal in + switch error { + case .generic: + return .fail(.generic) + case .invalidReference: + return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource), preferBackgroundReferenceRevalidation: false), resource: fileReference.media.resource) + |> mapError { _ -> SaveRecentlyUsedMediaError in + return .generic + } + |> mapToSignal { reference -> Signal in + return addSticker(reference) + } + } + } + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } + case let .remove(id, accessHash): + return network.request(Api.functions.messages.saveRecentSticker(flags: 0, id: .inputDocument(id: id, accessHash: accessHash, fileReference: Buffer()), unsave: .boolTrue)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } + case .sync: + return managedRecentStickers(postbox: postbox, network: network) + } +} diff --git a/TelegramCore/Namespaces.swift b/TelegramCore/Namespaces.swift index 8efb7ecfdb..64c77b5623 100644 --- a/TelegramCore/Namespaces.swift +++ b/TelegramCore/Namespaces.swift @@ -125,6 +125,7 @@ public struct OperationLogTags { static let SynchronizeSavedStickers = PeerOperationLogTag(value: 14) static let SynchronizeGroupedPeers = PeerOperationLogTag(value: 15) static let SynchronizeMarkAllUnseenPersonalMessages = PeerOperationLogTag(value: 16) + static let SynchronizeRecentlyUsedStickers = PeerOperationLogTag(value: 17) } public extension PeerSummaryCounterTags { diff --git a/TelegramCore/StickerManagement.swift b/TelegramCore/StickerManagement.swift index 48e2a8b96d..964bf0dbc5 100644 --- a/TelegramCore/StickerManagement.swift +++ b/TelegramCore/StickerManagement.swift @@ -27,6 +27,7 @@ func manageStickerPacks(network: Network, postbox: Postbox) -> Signal then(.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart } diff --git a/TelegramCore/SynchronizeRecentlyUsedMediaOperations.swift b/TelegramCore/SynchronizeRecentlyUsedMediaOperations.swift new file mode 100644 index 0000000000..566833a75f --- /dev/null +++ b/TelegramCore/SynchronizeRecentlyUsedMediaOperations.swift @@ -0,0 +1,105 @@ +import Foundation +#if os(macOS) +import PostboxMac +import SwiftSignalKitMac +#else +import Postbox +import SwiftSignalKit +#endif + +private enum SynchronizeRecentlyUsedMediaOperationContentType: Int32 { + case add + case remove + case sync +} + +enum SynchronizeRecentlyUsedMediaOperationContent: PostboxCoding { + case add(id: Int64, accessHash: Int64, fileReference: FileMediaReference?) + case remove(id: Int64, accessHash: Int64) + case sync + + init(decoder: PostboxDecoder) { + switch decoder.decodeInt32ForKey("r", orElse: 0) { + case SynchronizeRecentlyUsedMediaOperationContentType.add.rawValue: + self = .add(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0), fileReference: decoder.decodeAnyObjectForKey("fr", decoder: { FileMediaReference(decoder: $0) }) as? FileMediaReference) + case SynchronizeRecentlyUsedMediaOperationContentType.remove.rawValue: + self = .remove(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0)) + case SynchronizeRecentlyUsedMediaOperationContentType.sync.rawValue: + self = .sync + default: + assertionFailure() + self = .sync + } + } + + func encode(_ encoder: PostboxEncoder) { + switch self { + case let .add(id, accessHash, fileReference): + encoder.encodeInt32(SynchronizeRecentlyUsedMediaOperationContentType.add.rawValue, forKey: "r") + encoder.encodeInt64(id, forKey: "i") + encoder.encodeInt64(accessHash, forKey: "h") + if let fileReference = fileReference { + encoder.encodeObjectWithEncoder(fileReference, encoder: fileReference.encode, forKey: "fr") + } else { + encoder.encodeNil(forKey: "fr") + } + case let .remove(id, accessHash): + encoder.encodeInt32(SynchronizeRecentlyUsedMediaOperationContentType.remove.rawValue, forKey: "r") + encoder.encodeInt64(id, forKey: "i") + encoder.encodeInt64(accessHash, forKey: "h") + case .sync: + encoder.encodeInt32(SynchronizeRecentlyUsedMediaOperationContentType.sync.rawValue, forKey: "r") + } + } +} + +final class SynchronizeRecentlyUsedMediaOperation: PostboxCoding { + let content: SynchronizeRecentlyUsedMediaOperationContent + + init(content: SynchronizeRecentlyUsedMediaOperationContent) { + self.content = content + } + + init(decoder: PostboxDecoder) { + self.content = decoder.decodeObjectForKey("c", decoder: { SynchronizeRecentlyUsedMediaOperationContent(decoder: $0) }) as! SynchronizeRecentlyUsedMediaOperationContent + } + + func encode(_ encoder: PostboxEncoder) { + encoder.encodeObject(self.content, forKey: "c") + } +} + +enum RecentlyUsedMediaCategory { + case stickers +} + +func addSynchronizeRecentlyUsedMediaOperation(transaction: Transaction, category: RecentlyUsedMediaCategory, operation: SynchronizeRecentlyUsedMediaOperationContent) { + let tag: PeerOperationLogTag + switch category { + case .stickers: + tag = OperationLogTags.SynchronizeRecentlyUsedStickers + } + let peerId = PeerId(namespace: 0, id: 0) + + var topOperation: (SynchronizeRecentlyUsedMediaOperation, Int32)? + transaction.operationLogEnumerateEntries(peerId: peerId, tag: tag, { entry in + if let operation = entry.contents as? SynchronizeRecentlyUsedMediaOperation { + topOperation = (operation, entry.tagLocalIndex) + } + return false + }) + + if let (topOperation, topLocalIndex) = topOperation, case .sync = topOperation.content { + let _ = transaction.operationLogRemoveEntry(peerId: peerId, tag: tag, tagLocalIndex: topLocalIndex) + } + + transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeRecentlyUsedMediaOperation(content: operation)) + transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeRecentlyUsedMediaOperation(content: .sync)) +} + +func addRecentlyUsedSticker(transaction: Transaction, fileReference: FileMediaReference) { + if let resource = fileReference.media.resource as? CloudDocumentMediaResource { + transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(fileReference.media.fileId).rawValue, contents: RecentMediaItem(fileReference.media)), removeTailIfCountExceeds: 20) + addSynchronizeRecentlyUsedMediaOperation(transaction: transaction, category: .stickers, operation: .add(id: resource.fileId, accessHash: resource.accessHash, fileReference: fileReference)) + } +} From e12e749c7d8a3ffb2a2b09620b08df8d5b12c22f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 20 Nov 2018 17:37:08 +0400 Subject: [PATCH 2/2] Instant View: Ignore zero webpageId on photo block links --- TelegramCore/InstantPage.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/TelegramCore/InstantPage.swift b/TelegramCore/InstantPage.swift index a266ba1c57..48f5cb5426 100644 --- a/TelegramCore/InstantPage.swift +++ b/TelegramCore/InstantPage.swift @@ -1018,8 +1018,12 @@ extension InstantPageBlock { self = .blockQuote(text: RichText(apiText: text), caption: RichText(apiText: caption)) case let .pageBlockPullquote(text, caption): self = .pullQuote(text: RichText(apiText: text), caption: RichText(apiText: caption)) - case let .pageBlockPhoto(_, photoId, caption, url, webpageId): - self = .image(id: MediaId(namespace: Namespaces.Media.CloudImage, id: photoId), caption: InstantPageCaption(apiCaption: caption), url: url, webpageId: webpageId != nil ? MediaId(namespace: Namespaces.Media.CloudWebpage, id: webpageId!) : nil) + case let .pageBlockPhoto(flags, photoId, caption, url, webpageId): + var webpageMediaId: MediaId? + if (flags & (1 << 0)) != 0, let webpageId = webpageId, webpageId != 0 { + webpageMediaId = MediaId(namespace: Namespaces.Media.CloudWebpage, id: webpageId) + } + self = .image(id: MediaId(namespace: Namespaces.Media.CloudImage, id: photoId), caption: InstantPageCaption(apiCaption: caption), url: url, webpageId: webpageMediaId) case let .pageBlockVideo(flags, videoId, caption): self = .video(id: MediaId(namespace: Namespaces.Media.CloudFile, id: videoId), caption: InstantPageCaption(apiCaption: caption), autoplay: (flags & (1 << 0)) != 0, loop: (flags & (1 << 1)) != 0) case let .pageBlockCover(cover):