From d02b75a0c55865a6bb07da5fc211a4cb085c19ab Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 21 Aug 2017 23:28:37 +0300 Subject: [PATCH] no message --- TelegramCore.xcodeproj/project.pbxproj | 12 ++ TelegramCore/AccountIntermediateState.swift | 8 +- .../AccountStateManagementUtils.swift | 18 ++- TelegramCore/CachedStickerPack.swift | 77 ++++++++++ ...InstallInteractiveReadMessagesAction.swift | 12 +- TelegramCore/LoadedStickerPack.swift | 133 ++++++++++-------- TelegramCore/ManagedDeviceContacts.swift | 49 ++++++- ...essageContentAsConsumedInteractively.swift | 14 +- TelegramCore/Namespaces.swift | 1 + TelegramCore/PeerSpecificStickerPack.swift | 36 +++++ TelegramCore/StoreMessage_Telegram.swift | 2 +- 11 files changed, 281 insertions(+), 81 deletions(-) create mode 100644 TelegramCore/CachedStickerPack.swift create mode 100644 TelegramCore/PeerSpecificStickerPack.swift diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 595c28fea8..f4632ddae6 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -473,6 +473,10 @@ D0C0B58B1ED9DA6B000F4D2C /* ManagedLocalizationUpdatesOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C0B5891ED9DA6B000F4D2C /* ManagedLocalizationUpdatesOperations.swift */; }; D0C0B58D1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C0B58C1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift */; }; D0C0B58E1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C0B58C1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift */; }; + D0C27B3F1F4B51D000A4E170 /* CachedStickerPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B3E1F4B51D000A4E170 /* CachedStickerPack.swift */; }; + D0C27B401F4B51D000A4E170 /* CachedStickerPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B3E1F4B51D000A4E170 /* CachedStickerPack.swift */; }; + D0C27B421F4B58C000A4E170 /* PeerSpecificStickerPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B411F4B58C000A4E170 /* PeerSpecificStickerPack.swift */; }; + D0C27B431F4B58C000A4E170 /* PeerSpecificStickerPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C27B411F4B58C000A4E170 /* PeerSpecificStickerPack.swift */; }; D0C48F391E8138DF0075317D /* ArchivedStickerPacksInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C48F381E8138DF0075317D /* ArchivedStickerPacksInfo.swift */; }; D0C48F3A1E8138DF0075317D /* ArchivedStickerPacksInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C48F381E8138DF0075317D /* ArchivedStickerPacksInfo.swift */; }; D0C48F3C1E8142EF0075317D /* LoadedPeerFromMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C48F3B1E8142EF0075317D /* LoadedPeerFromMessage.swift */; }; @@ -839,6 +843,8 @@ D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = ""; }; D0C0B5891ED9DA6B000F4D2C /* ManagedLocalizationUpdatesOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedLocalizationUpdatesOperations.swift; sourceTree = ""; }; D0C0B58C1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeLocalizationUpdatesOperation.swift; sourceTree = ""; }; + D0C27B3E1F4B51D000A4E170 /* CachedStickerPack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedStickerPack.swift; sourceTree = ""; }; + D0C27B411F4B58C000A4E170 /* PeerSpecificStickerPack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerSpecificStickerPack.swift; sourceTree = ""; }; D0C48F381E8138DF0075317D /* ArchivedStickerPacksInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArchivedStickerPacksInfo.swift; sourceTree = ""; }; D0C48F3B1E8142EF0075317D /* LoadedPeerFromMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadedPeerFromMessage.swift; sourceTree = ""; }; D0C50E331E93A86600F62E39 /* CallSessionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallSessionManager.swift; sourceTree = ""; }; @@ -962,6 +968,8 @@ D0D748011E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift */, D05452061E7B5093006EEF19 /* LoadedStickerPack.swift */, D0E23DDE1E8082A400B9B6D2 /* ArchivedStickerPacks.swift */, + D0C27B3E1F4B51D000A4E170 /* CachedStickerPack.swift */, + D0C27B411F4B58C000A4E170 /* PeerSpecificStickerPack.swift */, ); name = "Sticker Management"; sourceTree = ""; @@ -1754,6 +1762,7 @@ D02ABC7B1E30058F00CAE539 /* DeleteMessagesInteractively.swift in Sources */, C2E064681ECEEF0A00387BB8 /* TelegramMediaInvoice.swift in Sources */, D0448C9F1E27F5EB005A61A7 /* Random.swift in Sources */, + D0C27B3F1F4B51D000A4E170 /* CachedStickerPack.swift in Sources */, D0B843B21DA7FF30005F29E1 /* NBAsYouTypeFormatter.m in Sources */, D07047B71F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift in Sources */, D03B0CDB1D62245F00955575 /* ApiUtils.swift in Sources */, @@ -1875,6 +1884,7 @@ D0177B7B1DF8A16C00A5083A /* SecretChatState.swift in Sources */, D0AAD1AA1E32638500D5B9DE /* ApplyMaxReadIndexInteractively.swift in Sources */, D03B0D5C1D631A6900955575 /* Download.swift in Sources */, + D0C27B421F4B58C000A4E170 /* PeerSpecificStickerPack.swift in Sources */, D01749591E1092BC0057C89A /* RequestStartBot.swift in Sources */, D01B27A21E394D8B0022A4C0 /* PrivacySettings.swift in Sources */, D0223A9B1EA5654D00211D94 /* TelegramMediaResource.swift in Sources */, @@ -2052,6 +2062,7 @@ D001F3F31E128A1C007A8C60 /* UpdateMessageService.swift in Sources */, D0C50E351E93A86600F62E39 /* CallSessionManager.swift in Sources */, D0B8442D1DAB91E0005F29E1 /* NBMetadataCoreTest.m in Sources */, + D0C27B431F4B58C000A4E170 /* PeerSpecificStickerPack.swift in Sources */, D0B844131DAB91CD005F29E1 /* StringFormat.swift in Sources */, D0C0B58E1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift in Sources */, D0E35A131DE4C69100BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */, @@ -2183,6 +2194,7 @@ D0C0B58B1ED9DA6B000F4D2C /* ManagedLocalizationUpdatesOperations.swift in Sources */, D0B844331DAB91E0005F29E1 /* NBPhoneNumber.m in Sources */, D001F3F51E128A1C007A8C60 /* PendingMessageManager.swift in Sources */, + D0C27B401F4B51D000A4E170 /* CachedStickerPack.swift in Sources */, D001F3F61E128A1C007A8C60 /* PendingMessageUploadedContent.swift in Sources */, D01C7ED71EF5E468008305F1 /* ProxySettings.swift in Sources */, C2366C871E4F403C0097CCFF /* AddressNames.swift in Sources */, diff --git a/TelegramCore/AccountIntermediateState.swift b/TelegramCore/AccountIntermediateState.swift index a00a4ff10d..fc68f40de7 100644 --- a/TelegramCore/AccountIntermediateState.swift +++ b/TelegramCore/AccountIntermediateState.swift @@ -67,7 +67,7 @@ enum AccountStateMutationOperation { case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32) case AddPeerInputActivity(chatPeerId: PeerId, peerId: PeerId?, activity: PeerInputActivity?) case UpdatePinnedPeerIds(AccountStateUpdatePinnerPeerIdsOperation) - case ReadGlobalMessageContents([Int32]) + case ReadMessageContents((PeerId?, [Int32])) case UpdateMessageImpressionCount(MessageId, Int32) case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation) case UpdateChatInputState(PeerId, SynchronizeableChatInputState?) @@ -245,8 +245,8 @@ struct AccountMutableState { self.addOperation(.UpdatePinnedPeerIds(operation)) } - mutating func addReadGlobalMessagesContents(_ globalIds: [Int32]) { - self.addOperation(.ReadGlobalMessageContents(globalIds)) + mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32])) { + self.addOperation(.ReadMessageContents(peerIdsAndMessageIds)) } mutating func addUpdateMessageImpressionCount(id: MessageId, count: Int32) { @@ -267,7 +267,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateChatInputState, .UpdateCall, .UpdateLangPack: + case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateChatInputState, .UpdateCall, .UpdateLangPack: break case let .AddMessages(messages, _): for message in messages { diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index 9c66a21187..08e435bb6a 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -970,7 +970,9 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState, updatedState.addUpdatePinnedPeerIds(.sync) } case let .updateReadMessagesContents(messages, _, _): - updatedState.addReadGlobalMessagesContents(messages) + updatedState.addReadMessagesContents((nil, messages)) + case let .updateChannelReadMessagesContents(channelId, messages): + updatedState.addReadMessagesContents((PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), 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): @@ -1421,7 +1423,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, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateChatInputState, .UpdateCall, .UpdateLangPack: + case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ResetReadState, .UpdatePeerNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateChatInputState, .UpdateCall, .UpdateLangPack: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -1598,9 +1600,15 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modif case .sync: addSynchronizePinnedChatsOperation(modifier: modifier) } - case let .ReadGlobalMessageContents(globalIds): - for messageId in modifier.messageIdsForGlobalIds(globalIds) { - markMessageContentAsConsumedRemotely(modifier: modifier, messageId: messageId) + case let .ReadMessageContents(peerId, messageIds): + if let peerId = peerId { + for id in messageIds { + markMessageContentAsConsumedRemotely(modifier: modifier, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)) + } + } else { + for messageId in modifier.messageIdsForGlobalIds(messageIds) { + markMessageContentAsConsumedRemotely(modifier: modifier, messageId: messageId) + } } case let .UpdateMessageImpressionCount(id, count): modifier.updateMessage(id, update: { currentMessage in diff --git a/TelegramCore/CachedStickerPack.swift b/TelegramCore/CachedStickerPack.swift new file mode 100644 index 0000000000..b2109470eb --- /dev/null +++ b/TelegramCore/CachedStickerPack.swift @@ -0,0 +1,77 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +private final class CachedStickerPack: Coding { + let items: [StickerPackItem] + let hash: Int32 + + init(items: [StickerPackItem], hash: Int32) { + self.items = items + self.hash = hash + } + + init(decoder: Decoder) { + self.items = decoder.decodeObjectArrayForKey("it").map { $0 as! StickerPackItem } + self.hash = decoder.decodeInt32ForKey("h", orElse: 0) + } + + func encode(_ encoder: Encoder) { + encoder.encodeObjectArray(self.items, forKey: "it") + encoder.encodeInt32(self.hash, forKey: "h") + } + + static func cacheKey(_ info: StickerPackCollectionInfo) -> ValueBoxKey { + let key = ValueBoxKey(length: 4 + 8) + key.setInt32(0, value: info.id.namespace) + key.setInt64(4, value: info.id.id) + return key + } +} + +private let collectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 100, highWaterItemCount: 200) + +public func cachedStickerPack(postbox: Postbox, network: Network, info: StickerPackCollectionInfo) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> { + return postbox.modify { modifier -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> in + if let currentInfo = modifier.getItemCollectionInfo(collectionId: info.id) as? StickerPackCollectionInfo { + let items = modifier.getItemCollectionItems(collectionId: info.id) + return .single((currentInfo, items)) + } else { + let current: Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> + var loadRemote = false + + if let cached = modifier.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(info))) as? CachedStickerPack { + current = .single((info, cached.items)) + if cached.hash != info.hash { + loadRemote = true + } + } else { + current = .complete() + loadRemote = true + } + + var signal = current + if loadRemote { + let appliedRemote = remoteStickerPack(network: network, reference: .id(id: info.id.id, accessHash: info.accessHash)) + |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> in + return postbox.modify { modifier -> (StickerPackCollectionInfo, [ItemCollectionItem])? in + if let result = result { + modifier.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(result.0)), entry: CachedStickerPack(items: result.1.map { $0 as! StickerPackItem }, hash: result.0.hash), collectionSpec: collectionSpec) + } + + return result + } + } + + signal = signal |> then(appliedRemote) + } + + return signal + } + } |> switchToLatest +} diff --git a/TelegramCore/InstallInteractiveReadMessagesAction.swift b/TelegramCore/InstallInteractiveReadMessagesAction.swift index 780ef7ec01..73bdfa9bff 100644 --- a/TelegramCore/InstallInteractiveReadMessagesAction.swift +++ b/TelegramCore/InstallInteractiveReadMessagesAction.swift @@ -13,14 +13,22 @@ public func installInteractiveReadMessagesAction(postbox: Postbox, peerId: PeerI for message in messages { if case let .Id(id) = message.id { + var hasUnconsumedMention = false + var hasUnconsumedContent = false + if message.tags.contains(.unseenPersonalMessage) { inner: for attribute in message.attributes { if let attribute = attribute as? ConsumablePersonalMentionMessageAttribute, !attribute.consumed, !attribute.pending { - consumeMessageIds.append(id) - break inner + hasUnconsumedMention = true + } else if let attribute = attribute as? ConsumableContentMessageAttribute, !attribute.consumed { + hasUnconsumedContent = true } } } + + if hasUnconsumedMention && !hasUnconsumedContent { + consumeMessageIds.append(id) + } } } diff --git a/TelegramCore/LoadedStickerPack.swift b/TelegramCore/LoadedStickerPack.swift index 598019808f..d0802ebe1f 100644 --- a/TelegramCore/LoadedStickerPack.swift +++ b/TelegramCore/LoadedStickerPack.swift @@ -28,6 +28,64 @@ public enum LoadedStickerPack { case result(info: StickerPackCollectionInfo, items: [ItemCollectionItem], installed: Bool) } +func remoteStickerPack(network: Network, reference: StickerPackReference) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> { + return network.request(Api.functions.messages.getStickerSet(stickerset: reference.apiInputStickerSet)) + |> map { Optional($0) } + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> (StickerPackCollectionInfo, [ItemCollectionItem])? in + guard let result = result else { + return nil + } + + 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) + } + } + } + } + + 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 (info, items) + } +} + public func loadedStickerPack(account: Account, reference: StickerPackReference) -> Signal { return account.postbox.modify { modifier -> Signal in switch reference { @@ -57,68 +115,21 @@ public func loadedStickerPack(account: Account, reference: StickerPackReference) 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) - } - } - } - } - - 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) - } + let signal = remoteStickerPack(network: account.network, reference: reference) |> mapToSignal { result -> Signal in + if let result = result { + return account.postbox.combinedView(keys: [PostboxViewKey.itemCollectionInfo(id: result.0.id)]) + |> map { view in + if let view = view.views[PostboxViewKey.itemCollectionInfo(id: result.0.id)] as? ItemCollectionInfoView, let info = view.info as? StickerPackCollectionInfo { + return .result(info: info, items: result.1, installed: true) + } else { + return .result(info: result.0, items: result.1, installed: false) } - } + } + } else { + return .single(.none) + } + } + return .single(.fetching) |> then(signal) } |> switchToLatest } diff --git a/TelegramCore/ManagedDeviceContacts.swift b/TelegramCore/ManagedDeviceContacts.swift index e4b418b7d1..70e1d1cfbd 100644 --- a/TelegramCore/ManagedDeviceContacts.swift +++ b/TelegramCore/ManagedDeviceContacts.swift @@ -37,12 +37,14 @@ final class ManagedDeviceContactEntryContents: Coding { let lastName: String let phoneNumber: String let peerId: PeerId? + let importDelayedUntil: Int32? - init(firstName: String, lastName: String, phoneNumber: String, peerId: PeerId?) { + init(firstName: String, lastName: String, phoneNumber: String, peerId: PeerId?, importDelayedUntil: Int32?) { self.firstName = firstName self.lastName = lastName self.phoneNumber = phoneNumber self.peerId = peerId + self.importDelayedUntil = importDelayedUntil } init(decoder: Decoder) { @@ -54,6 +56,7 @@ final class ManagedDeviceContactEntryContents: Coding { } else { self.peerId = nil } + self.importDelayedUntil = decoder.decodeOptionalInt32ForKey("dt") } func encode(_ encoder: Encoder) { @@ -65,10 +68,19 @@ final class ManagedDeviceContactEntryContents: Coding { } else { encoder.encodeNil(forKey: "p") } + if let importDelayedUntil = self.importDelayedUntil { + encoder.encodeInt32(importDelayedUntil, forKey: "dt") + } else { + encoder.encodeNil(forKey: "dt") + } } func withUpdatedPeerId(_ peerId: PeerId?) -> ManagedDeviceContactEntryContents { - return ManagedDeviceContactEntryContents(firstName: self.firstName, lastName: self.lastName, phoneNumber: self.phoneNumber, peerId: peerId) + return ManagedDeviceContactEntryContents(firstName: self.firstName, lastName: self.lastName, phoneNumber: self.phoneNumber, peerId: peerId, importDelayedUntil: self.importDelayedUntil) + } + + func withUpdatedImportDelayedUntil(_ importDelayedUntil: Int32?) -> ManagedDeviceContactEntryContents { + return ManagedDeviceContactEntryContents(firstName: self.firstName, lastName: self.lastName, phoneNumber: self.phoneNumber, peerId: self.peerId, importDelayedUntil: importDelayedUntil) } } @@ -76,7 +88,7 @@ private func unorderedListEntriesForDeviceContact(_ deviceContact: DeviceContact var entries: [UnorderedItemListEntry] = [] for phoneNumber in deviceContact.phoneNumbers { let stringToHash = "\(deviceContact.firstName):\(deviceContact.lastName):\(phoneNumber.number)" - entries.append(UnorderedItemListEntry(id: ValueBoxKey(phoneNumber.number), info: UnorderedItemListEntryInfo(hashValue: Int64(stringToHash.hashValue)), contents: ManagedDeviceContactEntryContents(firstName: deviceContact.firstName, lastName: deviceContact.lastName, phoneNumber: phoneNumber.number, peerId: nil))) + entries.append(UnorderedItemListEntry(id: ValueBoxKey(phoneNumber.number), info: UnorderedItemListEntryInfo(hashValue: Int64(stringToHash.hashValue)), contents: ManagedDeviceContactEntryContents(firstName: deviceContact.firstName, lastName: deviceContact.lastName, phoneNumber: phoneNumber.number, peerId: nil, importDelayedUntil: nil))) } return entries } @@ -106,9 +118,21 @@ func managedDeviceContacts(postbox: Postbox, network: Network, deviceContacts: S let appliedDifference = postbox.modify { modifier -> Signal in let (metaInfo, added, removed, updated) = modifier.unorderedItemListDifference(tag: Namespaces.UnorderedItemList.synchronizedDeviceContacts, updatedEntryInfos: infos) + let timestamp = Int32(CFAbsoluteTimeGetCurrent()) + var reimportKeys = Set() + modifier.unorderedItemListScan(tag: Namespaces.UnorderedItemList.synchronizedDeviceContacts, { entry in + if let contents = entry.contents as? ManagedDeviceContactEntryContents { + if let importDelayedUntil = contents.importDelayedUntil, importDelayedUntil <= timestamp { + reimportKeys.insert(entry.id) + } + } + }) + var addedOrUpdatedContacts: [ManagedDeviceContactEntryContents] = [] for id in added { + reimportKeys.remove(id) + if let entry = entries[id], let contents = entry.contents as? ManagedDeviceContactEntryContents { addedOrUpdatedContacts.append(contents) } else { @@ -117,6 +141,8 @@ func managedDeviceContacts(postbox: Postbox, network: Network, deviceContacts: S } for previousEntry in updated { + reimportKeys.remove(previousEntry.id) + if let entry = entries[previousEntry.id], let contents = entry.contents as? ManagedDeviceContactEntryContents { addedOrUpdatedContacts.append(contents) } else { @@ -126,6 +152,8 @@ func managedDeviceContacts(postbox: Postbox, network: Network, deviceContacts: S var removedPeerIds: [PeerId] = [] for entry in removed { + reimportKeys.remove(entry.id) + if let contents = entry.contents as? ManagedDeviceContactEntryContents { if let peerId = contents.peerId { removedPeerIds.append(peerId) @@ -133,6 +161,12 @@ func managedDeviceContacts(postbox: Postbox, network: Network, deviceContacts: S } } + for id in reimportKeys { + if let entry = entries[id], let contents = entry.contents as? ManagedDeviceContactEntryContents { + addedOrUpdatedContacts.append(contents) + } + } + return (applyRemovedContacts(postbox: postbox, network: network, peerIds: removedPeerIds) |> map { _ -> ([Peer], [PeerId: PeerPresence], [ValueBoxKey: ManagedDeviceContactEntryContents]) in assertionFailure() @@ -264,9 +298,14 @@ private func applyAddedOrUpdatedContacts(network: Network, contacts: [ManagedDev let reimportClientIds = Set(retryContacts) + let reimportTimestamp = Int32(CFAbsoluteTimeGetCurrent()) + Int32(1 * 60 * 60) for (clientId, contents) in clientIdToContact { - if !importedClientIds.contains(clientId) && !reimportClientIds.contains(clientId) { - importedContents[ValueBoxKey(contents.phoneNumber)] = contents + if !importedClientIds.contains(clientId) { + var updatedContents = contents + if reimportClientIds.contains(clientId) { + updatedContents = updatedContents.withUpdatedImportDelayedUntil(reimportTimestamp) + } + importedContents[ValueBoxKey(contents.phoneNumber)] = updatedContents } } diff --git a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift index c534e03788..84075ae996 100644 --- a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift +++ b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift @@ -21,7 +21,9 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI addSynchronizeConsumeMessageContentsOperation(modifier: modifier, messageIds: [message.id]) } - break + } else if let attribute = updatedAttributes[i] as? ConsumablePersonalMentionMessageAttribute, !attribute.consumed { + modifier.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: messageId, action: ConsumePersonalMessageAction()) + updatedAttributes[i] = ConsumablePersonalMentionMessageAttribute(consumed: attribute.consumed, pending: true) } } @@ -78,6 +80,7 @@ func markMessageContentAsConsumedRemotely(modifier: Modifier, messageId: Message var updateMessage = false var updatedAttributes = message.attributes var updatedMedia = message.media + var updatedTags = message.tags for i in 0 ..< updatedAttributes.count { if let attribute = updatedAttributes[i] as? ConsumableContentMessageAttribute { @@ -85,7 +88,12 @@ func markMessageContentAsConsumedRemotely(modifier: Modifier, messageId: Message updatedAttributes[i] = ConsumableContentMessageAttribute(consumed: true) updateMessage = true } - break + } else if let attribute = updatedAttributes[i] as? ConsumablePersonalMentionMessageAttribute, !attribute.consumed { + if attribute.pending { + modifier.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: messageId, action: nil) + } + updatedAttributes[i] = ConsumablePersonalMentionMessageAttribute(consumed: true, pending: false) + updatedTags.remove(.unseenPersonalMessage) } } @@ -118,7 +126,7 @@ func markMessageContentAsConsumedRemotely(modifier: Modifier, messageId: Message if let forwardInfo = currentMessage.forwardInfo { storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) } - return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: updatedTags, globalTags: currentMessage.globalTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia)) }) } } diff --git a/TelegramCore/Namespaces.swift b/TelegramCore/Namespaces.swift index f93cd963d5..fe88e291b3 100644 --- a/TelegramCore/Namespaces.swift +++ b/TelegramCore/Namespaces.swift @@ -55,6 +55,7 @@ public struct Namespaces { struct CachedItemCollection { public static let resolvedByNamePeers: Int8 = 0 public static let cachedTwoStepToken: Int8 = 1 + public static let cachedStickerPacks: Int8 = 2 } struct UnorderedItemList { diff --git a/TelegramCore/PeerSpecificStickerPack.swift b/TelegramCore/PeerSpecificStickerPack.swift new file mode 100644 index 0000000000..e18d6ce685 --- /dev/null +++ b/TelegramCore/PeerSpecificStickerPack.swift @@ -0,0 +1,36 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +private struct WrappedStickerPackCollectionInfo: Equatable { + let info: StickerPackCollectionInfo? + + static func ==(lhs: WrappedStickerPackCollectionInfo, rhs: WrappedStickerPackCollectionInfo) -> Bool { + return lhs.info == rhs.info + } +} + +public func peerSpecificStickerPack(postbox: Postbox, network: Network, peerId: PeerId) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> { + if peerId.namespace == Namespaces.Peer.CloudChannel { + return postbox.combinedView(keys: [.cachedPeerData(peerId: peerId)]) + |> map { view -> WrappedStickerPackCollectionInfo in + let dataView = view.views[.cachedPeerData(peerId: peerId)] as? CachedPeerDataView + return WrappedStickerPackCollectionInfo(info: (dataView?.cachedPeerData as? CachedChannelData)?.stickerPack) + } + |> distinctUntilChanged + |> mapToSignal { info -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem])?, NoError> in + if let info = info.info { + return cachedStickerPack(postbox: postbox, network: network, info: info) + } else { + return .single(nil) + } + } + } else { + return .single(nil) + } +} diff --git a/TelegramCore/StoreMessage_Telegram.swift b/TelegramCore/StoreMessage_Telegram.swift index 64df8441ec..099b251dda 100644 --- a/TelegramCore/StoreMessage_Telegram.swift +++ b/TelegramCore/StoreMessage_Telegram.swift @@ -428,7 +428,7 @@ extension StoreMessage { } for case let file as TelegramMediaFile in medias { - if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup { + if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel { if file.isVoice { consumableContent = (true, (flags & (1 << 5)) == 0) break