diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index cce4390154..595c28fea8 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -101,6 +101,12 @@ D018D3381E648ACF00C5E089 /* CreateChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018D3361E648ACF00C5E089 /* CreateChannel.swift */; }; D019B1CC1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019B1CB1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift */; }; D019B1CD1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019B1CB1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift */; }; + D01A21A61F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A21A51F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift */; }; + D01A21A71F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A21A51F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift */; }; + D01A21A91F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A21A81F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift */; }; + D01A21AA1F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A21A81F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift */; }; + D01A21AC1F38D10E00DDA104 /* SavedStickerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A21AB1F38D10E00DDA104 /* SavedStickerItem.swift */; }; + D01A21AD1F38D10E00DDA104 /* SavedStickerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A21AB1F38D10E00DDA104 /* SavedStickerItem.swift */; }; D01AC91D1DD5DA5E00E8160F /* RequestMessageActionCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01AC91C1DD5DA5E00E8160F /* RequestMessageActionCallback.swift */; }; D01AC9211DD5E7E500E8160F /* RequestEditMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01AC9201DD5E7E500E8160F /* RequestEditMessage.swift */; }; D01AC9231DD5E9A200E8160F /* ApplyUpdateMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01AC9221DD5E9A200E8160F /* ApplyUpdateMessage.swift */; }; @@ -175,7 +181,6 @@ D03B0D5E1D631A6900955575 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D581D631A6900955575 /* Network.swift */; }; D03B0D5F1D631A6900955575 /* Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D591D631A6900955575 /* Serialization.swift */; }; D03B0D651D631A8B00955575 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D611D631A8B00955575 /* Account.swift */; }; - D03B0D661D631A8B00955575 /* AccountSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D621D631A8B00955575 /* AccountSettings.swift */; }; D03B0D671D631A8B00955575 /* AccountViewTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D631D631A8B00955575 /* AccountViewTracker.swift */; }; D03B0D681D631A8B00955575 /* RecentPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D641D631A8B00955575 /* RecentPeers.swift */; }; D03B0D6D1D631AA300955575 /* ContactManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D6C1D631AA300955575 /* ContactManagement.swift */; }; @@ -299,8 +304,16 @@ D0613FD01E60520700202CDB /* ChannelMembers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0613FCE1E60520700202CDB /* ChannelMembers.swift */; }; D0613FD71E606B3B00202CDB /* ConvertGroupToSupergroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0613FD61E606B3B00202CDB /* ConvertGroupToSupergroup.swift */; }; D0613FD81E606B3B00202CDB /* ConvertGroupToSupergroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0613FD61E606B3B00202CDB /* ConvertGroupToSupergroup.swift */; }; + D0642EF91F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0642EF81F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift */; }; + D0642EFA1F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0642EF81F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift */; }; D067066C1D512ADB00DED3E3 /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706671D512ADB00DED3E3 /* Postbox.framework */; }; D067066D1D512ADB00DED3E3 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D06706681D512ADB00DED3E3 /* SwiftSignalKit.framework */; }; + D07047B41F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B31F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift */; }; + D07047B51F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B31F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift */; }; + D07047B71F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B61F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift */; }; + D07047B81F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B61F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift */; }; + D07047BA1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B91F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift */; }; + D07047BB1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07047B91F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift */; }; D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE5C1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift */; }; D073CE601DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D073CE5F1DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift */; }; D073CE6A1DCBCF17007511FD /* ViewCountMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CE71D6224AD00955575 /* ViewCountMessageAttribute.swift */; }; @@ -378,8 +391,6 @@ D0B418BA1D7E05BB004562A4 /* NetworkLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = D03B0E421D631E6600955575 /* NetworkLogging.m */; }; D0B418BB1D7E05BE004562A4 /* NetworkLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = D03B0E411D631E6600955575 /* NetworkLogging.h */; }; D0B418BC1D7E05D0004562A4 /* TelegramCoreIncludes.h in Headers */ = {isa = PBXBuildFile; fileRef = D03B0E5B1D63240700955575 /* TelegramCoreIncludes.h */; }; - D0B477731EBF54A20033A0AB /* RecentCalls.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B477721EBF54A20033A0AB /* RecentCalls.swift */; }; - D0B477741EBF54A20033A0AB /* RecentCalls.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B477721EBF54A20033A0AB /* RecentCalls.swift */; }; D0B843811DA6EDAE005F29E1 /* CachedUserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B843801DA6EDAE005F29E1 /* CachedUserData.swift */; }; D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B843821DA6EDB8005F29E1 /* CachedGroupData.swift */; }; D0B843851DA6EDC4005F29E1 /* CachedChannelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B843841DA6EDC4005F29E1 /* CachedChannelData.swift */; }; @@ -436,7 +447,6 @@ D0B844351DAB91E0005F29E1 /* NBPhoneNumberDesc.m in Sources */ = {isa = PBXBuildFile; fileRef = D0B843AF1DA7FF30005F29E1 /* NBPhoneNumberDesc.m */; }; D0B844361DAB91E0005F29E1 /* NBPhoneNumberUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = D0B843B11DA7FF30005F29E1 /* NBPhoneNumberUtil.m */; }; D0B844431DAB91FD005F29E1 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D611D631A8B00955575 /* Account.swift */; }; - D0B844441DAB91FD005F29E1 /* AccountSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D621D631A8B00955575 /* AccountSettings.swift */; }; D0B844451DAB91FD005F29E1 /* AccountViewTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D631D631A8B00955575 /* AccountViewTracker.swift */; }; D0B844461DAB91FD005F29E1 /* RecentPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D641D631A8B00955575 /* RecentPeers.swift */; }; D0B844471DAB91FD005F29E1 /* ManagedServiceViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AB0B911D65E9FA002C78E7 /* ManagedServiceViews.swift */; }; @@ -472,6 +482,8 @@ 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 */; }; + D0DB7F031F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB7F021F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift */; }; + D0DB7F041F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DB7F021F43030C00591D48 /* InstallInteractiveReadMessagesAction.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 */; }; @@ -607,6 +619,9 @@ D0177B7A1DF8A16C00A5083A /* SecretChatState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatState.swift; sourceTree = ""; }; D018D3361E648ACF00C5E089 /* CreateChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateChannel.swift; sourceTree = ""; }; D019B1CB1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretChatRekeySession.swift; sourceTree = ""; }; + D01A21A51F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeSavedStickersOperation.swift; sourceTree = ""; }; + D01A21A81F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeSavedStickersOperations.swift; sourceTree = ""; }; + D01A21AB1F38D10E00DDA104 /* SavedStickerItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SavedStickerItem.swift; sourceTree = ""; }; D01AC91C1DD5DA5E00E8160F /* RequestMessageActionCallback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestMessageActionCallback.swift; sourceTree = ""; }; D01AC9201DD5E7E500E8160F /* RequestEditMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestEditMessage.swift; sourceTree = ""; }; D01AC9221DD5E9A200E8160F /* ApplyUpdateMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplyUpdateMessage.swift; sourceTree = ""; }; @@ -667,7 +682,6 @@ D03B0D581D631A6900955575 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; D03B0D591D631A6900955575 /* Serialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Serialization.swift; sourceTree = ""; }; D03B0D611D631A8B00955575 /* Account.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; - D03B0D621D631A8B00955575 /* AccountSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountSettings.swift; sourceTree = ""; }; D03B0D631D631A8B00955575 /* AccountViewTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountViewTracker.swift; sourceTree = ""; }; D03B0D641D631A8B00955575 /* RecentPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentPeers.swift; sourceTree = ""; }; D03B0D6C1D631AA300955575 /* ContactManagement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactManagement.swift; sourceTree = ""; }; @@ -726,11 +740,15 @@ D0613FC91E60440600202CDB /* InvitationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvitationLinks.swift; sourceTree = ""; }; D0613FCE1E60520700202CDB /* ChannelMembers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelMembers.swift; sourceTree = ""; }; D0613FD61E606B3B00202CDB /* ConvertGroupToSupergroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvertGroupToSupergroup.swift; sourceTree = ""; }; + D0642EF81F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EarliestUnseenPersonalMentionMessage.swift; sourceTree = ""; }; D06706641D512ADB00DED3E3 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D06706651D512ADB00DED3E3 /* Display.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Display.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D06706671D512ADB00DED3E3 /* Postbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Postbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D06706681D512ADB00DED3E3 /* SwiftSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SwiftSignalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D067066E1D512AEB00DED3E3 /* MtProtoKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MtProtoKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D07047B31F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsumablePersonalMentionMessageAttribute.swift; sourceTree = ""; }; + D07047B61F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedConsumePersonalMessagesActions.swift; sourceTree = ""; }; + D07047B91F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsumePersonalMessageAction.swift; sourceTree = ""; }; D073CE5C1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForwardSourceInfoAttribute.swift; sourceTree = ""; }; D073CE5F1DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageInfoAttribute.swift; sourceTree = ""; }; D0754D291EEE10FC00884F6E /* BotPaymentForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BotPaymentForm.swift; sourceTree = ""; }; @@ -774,7 +792,6 @@ D0B418701D7E0409004562A4 /* PostboxMac.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = PostboxMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0B418711D7E0409004562A4 /* SwiftSignalKitMac.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SwiftSignalKitMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0B4187E1D7E054E004562A4 /* MtProtoKitMac.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MtProtoKitMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D0B477721EBF54A20033A0AB /* RecentCalls.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentCalls.swift; sourceTree = ""; }; D0B843801DA6EDAE005F29E1 /* CachedUserData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedUserData.swift; sourceTree = ""; }; D0B843821DA6EDB8005F29E1 /* CachedGroupData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedGroupData.swift; sourceTree = ""; }; D0B843841DA6EDC4005F29E1 /* CachedChannelData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedChannelData.swift; sourceTree = ""; }; @@ -827,6 +844,7 @@ D0C50E331E93A86600F62E39 /* CallSessionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallSessionManager.swift; sourceTree = ""; }; D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MtProtoKitDynamic.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0D748011E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerPackInteractiveOperations.swift; sourceTree = ""; }; + D0DB7F021F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstallInteractiveReadMessagesAction.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 = ""; }; @@ -926,6 +944,7 @@ children = ( D021E0DE1DB539FC00C6B04F /* StickerPack.swift */, D049EAD41E43D98500A2CD3A /* RecentMediaItem.swift */, + D01A21AB1F38D10E00DDA104 /* SavedStickerItem.swift */, D049EAE71E44B67100A2CD3A /* RecentPeerItem.swift */, D0E23DD41E8042F500B9B6D2 /* FeaturedStickerPack.swift */, D0C48F381E8138DF0075317D /* ArchivedStickerPacksInfo.swift */, @@ -1082,6 +1101,7 @@ D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */, D0458C871E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift */, D00D343E1E6ED6E50057B307 /* ConsumableContentMessageAttribute.swift */, + D07047B31F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift */, D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */, C28725411EF967E700613564 /* NotificationInfoMessageAttribute.swift */, ); @@ -1142,10 +1162,14 @@ D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */, D0575AF01E9FFA5D006F2541 /* SynchronizeSavedGifsOperation.swift */, D0575AF31E9FFDDD006F2541 /* ManagedSynchronizeSavedGifsOperations.swift */, + D01A21A51F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift */, + D01A21A81F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift */, D058E0D01E8AD65C00A442DE /* StandaloneSendMessage.swift */, D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */, D0C0B58C1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift */, D0C0B5891ED9DA6B000F4D2C /* ManagedLocalizationUpdatesOperations.swift */, + D07047B91F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift */, + D07047B61F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift */, ); name = State; sourceTree = ""; @@ -1194,7 +1218,6 @@ children = ( D03B0D611D631A8B00955575 /* Account.swift */, D049EAF41E44DF3300A2CD3A /* AccountState.swift */, - D03B0D621D631A8B00955575 /* AccountSettings.swift */, D03B0D631D631A8B00955575 /* AccountViewTracker.swift */, D03B0D641D631A8B00955575 /* RecentPeers.swift */, D0AB0B911D65E9FA002C78E7 /* ManagedServiceViews.swift */, @@ -1238,6 +1261,8 @@ D0528E5F1E65B94E00E2FEF5 /* SingleMessageView.swift */, D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */, D0FA35071EA632E400E56FFA /* CollectCacheUsageStats.swift */, + D0642EF81F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift */, + D0DB7F021F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift */, ); name = Messages; sourceTree = ""; @@ -1395,7 +1420,6 @@ isa = PBXGroup; children = ( D0C50E331E93A86600F62E39 /* CallSessionManager.swift */, - D0B477721EBF54A20033A0AB /* RecentCalls.swift */, C2F4ED1C1EC60064005F2696 /* RateCall.swift */, ); name = Calls; @@ -1673,7 +1697,6 @@ D03B0CB91D62233400955575 /* Either.swift in Sources */, D0D748021E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */, D03B0CBD1D62234300955575 /* Regex.swift in Sources */, - D03B0D661D631A8B00955575 /* AccountSettings.swift in Sources */, D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */, D0B843B91DA7FF30005F29E1 /* NBMetadataCoreTest.m in Sources */, D09A2FE61D7CD4940018FB72 /* TelegramChannel.swift in Sources */, @@ -1681,6 +1704,8 @@ D053B4181F18DE4F00E2D58A /* AuthorSignatureMessageAttribute.swift in Sources */, D0F3A89F1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */, D01AC9231DD5E9A200E8160F /* ApplyUpdateMessage.swift in Sources */, + D01A21AC1F38D10E00DDA104 /* SavedStickerItem.swift in Sources */, + D0642EF91F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift in Sources */, D03B0CF71D62250800955575 /* TelegramMediaImage.swift in Sources */, D0E23DDF1E8082A400B9B6D2 /* ArchivedStickerPacks.swift in Sources */, D0BC386E1E3FDAB70044D6FE /* CreateGroup.swift in Sources */, @@ -1710,7 +1735,9 @@ D0F3CC7D1DDE289E008148FA /* ResolvePeerByName.swift in Sources */, D0B843B71DA7FF30005F29E1 /* NBMetadataCoreMapper.m in Sources */, D0AB0B921D65E9FA002C78E7 /* ManagedServiceViews.swift in Sources */, + D0DB7F031F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift in Sources */, D0AAD1B81E326FE200D5B9DE /* ManagedAutoremoveMessageOperations.swift in Sources */, + D01A21A91F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift in Sources */, D099EA1C1DE72867001AF5A8 /* PeerCommands.swift in Sources */, D03B0CCE1D62239600955575 /* PhoneNumbers.swift in Sources */, D00DBBDA1E64E67E00DB5485 /* UpdateSecretChat.swift in Sources */, @@ -1728,6 +1755,7 @@ C2E064681ECEEF0A00387BB8 /* TelegramMediaInvoice.swift in Sources */, D0448C9F1E27F5EB005A61A7 /* Random.swift in Sources */, D0B843B21DA7FF30005F29E1 /* NBAsYouTypeFormatter.m in Sources */, + D07047B71F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift in Sources */, D03B0CDB1D62245F00955575 /* ApiUtils.swift in Sources */, D01C7F0A1EFC3607008305F1 /* ManagedDeviceContacts.swift in Sources */, D0B843C91DA7FF30005F29E1 /* NBPhoneNumberDesc.m in Sources */, @@ -1750,7 +1778,6 @@ D03B0D0A1D62255C00955575 /* Holes.swift in Sources */, D0B843CB1DA7FF30005F29E1 /* NBPhoneNumberUtil.m in Sources */, D03B0D5E1D631A6900955575 /* Network.swift in Sources */, - D0B477731EBF54A20033A0AB /* RecentCalls.swift in Sources */, D0B8438E1DA7D296005F29E1 /* CachedGroupParticipants.swift in Sources */, D0B843BD1DA7FF30005F29E1 /* NBMetadataHelper.m in Sources */, D03B0CF51D62250800955575 /* TelegramMediaContact.swift in Sources */, @@ -1793,6 +1820,7 @@ D01D6BF91E42A713006151C6 /* SearchStickers.swift in Sources */, D08F4A691E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift in Sources */, D0575AF11E9FFA5D006F2541 /* SynchronizeSavedGifsOperation.swift in Sources */, + D07047B41F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift in Sources */, D0FA8BB91E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */, D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */, D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */, @@ -1860,6 +1888,7 @@ D0BC38751E40A7F70044D6FE /* RemovePeerChat.swift in Sources */, D0AB0B961D662F0B002C78E7 /* ManagedChatListHoles.swift in Sources */, D05A32E41E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */, + D01A21A61F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */, D03E5E0C1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */, D0F3A8A51E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */, D03B0CD71D62245300955575 /* TelegramGroup.swift in Sources */, @@ -1904,6 +1933,7 @@ D03B0D0D1D62255C00955575 /* SynchronizePeerReadState.swift in Sources */, D03B0D081D62255C00955575 /* ChannelState.swift in Sources */, C251D7431E65E50500283EDE /* StickerSetInstallation.swift in Sources */, + D07047BA1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */, D0C0B58D1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1942,9 +1972,11 @@ D00D34401E6ED6E50057B307 /* ConsumableContentMessageAttribute.swift in Sources */, D050F26E1E4A5B6D00988324 /* RemovePeerChat.swift in Sources */, D0613FD81E606B3B00202CDB /* ConvertGroupToSupergroup.swift in Sources */, + D0DB7F041F43030C00591D48 /* InstallInteractiveReadMessagesAction.swift in Sources */, D01D6BFA1E42A718006151C6 /* SearchStickers.swift in Sources */, D0D748031E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */, C2A315C01E2E776A00D89000 /* RequestStartBot.swift in Sources */, + D0642EFA1F3E05D700792790 /* EarliestUnseenPersonalMentionMessage.swift in Sources */, D0F7B1EA1E045C87007EB8A5 /* ChangePeerNotificationSettings.swift in Sources */, D0B418A71D7E0592004562A4 /* Fetch.swift in Sources */, D02ABC7C1E30058F00CAE539 /* DeleteMessagesInteractively.swift in Sources */, @@ -1954,7 +1986,6 @@ D0E23DE01E8082A400B9B6D2 /* ArchivedStickerPacks.swift in Sources */, D050F2521E4A59C200988324 /* JoinLink.swift in Sources */, D0F7B1E91E045C87007EB8A5 /* PeerCommands.swift in Sources */, - D0B477741EBF54A20033A0AB /* RecentCalls.swift in Sources */, D00D97C81E32901700E5C2B6 /* PeerInputActivity.swift in Sources */, D0754D2B1EEE10FC00884F6E /* BotPaymentForm.swift in Sources */, D0B844311DAB91E0005F29E1 /* NBPhoneMetaData.m in Sources */, @@ -1967,6 +1998,7 @@ D00D97CB1E32917C00E5C2B6 /* PeerInputActivityManager.swift in Sources */, D0B844491DAB91FD005F29E1 /* ManagedChatListHoles.swift in Sources */, D03C53711DAD5CA9004C17B3 /* CachedGroupParticipants.swift in Sources */, + D07047B81F3DF2CD00F6A8D4 /* ManagedConsumePersonalMessagesActions.swift in Sources */, C2366C841E4F3EAA0097CCFF /* GroupReturnAndLeft.swift in Sources */, C2E0646E1ECF171E00387BB8 /* TelegramMediaWebDocument.swift in Sources */, D03C53671DAD5CA9004C17B3 /* ApiUtils.swift in Sources */, @@ -2001,10 +2033,10 @@ D0FA8BA81E1FA6DF001E855B /* TelegramSecretChat.swift in Sources */, C23BC3881E9BE3CB00D79F92 /* ImportContact.swift in Sources */, D001F3EB1E128A1C007A8C60 /* EnqueueMessage.swift in Sources */, + D01A21A71F38CDC700DDA104 /* SynchronizeSavedStickersOperation.swift in Sources */, D00C7CEC1E37A8540080C3D5 /* SetSecretChatMessageAutoremoveTimeoutInteractively.swift in Sources */, D0B844481DAB91FD005F29E1 /* ManagedMessageHistoryHoles.swift in Sources */, D0F3CC7B1DDE2859008148FA /* RequestEditMessage.swift in Sources */, - D0B844441DAB91FD005F29E1 /* AccountSettings.swift in Sources */, D049EAEC1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift in Sources */, D0FA8B991E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */, D001F3F01E128A1C007A8C60 /* AccountStateManagementUtils.swift in Sources */, @@ -2032,6 +2064,7 @@ D0E23DDB1E806F7700B9B6D2 /* ManagedSynchronizeMarkFeaturedStickerPacksAsSeenOperations.swift in Sources */, D0B844341DAB91E0005F29E1 /* NBPhoneNumberDefines.m in Sources */, D0B8442A1DAB91E0005F29E1 /* NBAsYouTypeFormatter.m in Sources */, + D07047B51F3DF1FE00F6A8D4 /* ConsumablePersonalMentionMessageAttribute.swift in Sources */, D0448C8F1E22993C005A61A7 /* ProcessSecretChatIncomingDecryptedOperations.swift in Sources */, D073CE6E1DCBCF17007511FD /* ForwardSourceInfoAttribute.swift in Sources */, D05A32E21E6F0982002760B4 /* UpdatedAccountPrivacySettings.swift in Sources */, @@ -2053,6 +2086,7 @@ D02ABC821E310E5D00CAE539 /* ManagedCloudChatRemoveMessagesOperations.swift in Sources */, C2FD33E51E687BF1008D13D4 /* PeerPhotoUpdater.swift in Sources */, D0B8442E1DAB91E0005F29E1 /* NBMetadataCoreTestMapper.m in Sources */, + D01A21AD1F38D10E00DDA104 /* SavedStickerItem.swift in Sources */, D03C53731DAD5CA9004C17B3 /* CachedGroupData.swift in Sources */, D019B1CD1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift in Sources */, C2F4ED1E1EC60064005F2696 /* RateCall.swift in Sources */, @@ -2100,6 +2134,7 @@ D0B4188E1D7E0578004562A4 /* StoreMessage_Telegram.swift in Sources */, D0B844461DAB91FD005F29E1 /* RecentPeers.swift in Sources */, D03C53681DAD5CA9004C17B3 /* PeerUtils.swift in Sources */, + D07047BB1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */, D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */, D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */, D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */, @@ -2119,6 +2154,7 @@ D073CE6A1DCBCF17007511FD /* ViewCountMessageAttribute.swift in Sources */, C27982511E72C97800262BFD /* MacosLegacy.swift in Sources */, D0B418AB1D7E0597004562A4 /* MultipartFetch.swift in Sources */, + D01A21AA1F38CDDC00DDA104 /* ManagedSynchronizeSavedStickersOperations.swift in Sources */, D03C53741DAD5CA9004C17B3 /* CachedChannelData.swift in Sources */, D0B418861D7E056D004562A4 /* Namespaces.swift in Sources */, D05A32E51E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */, diff --git a/TelegramCore.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist b/TelegramCore.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist index bbe7c497fe..e4def498f4 100644 --- a/TelegramCore.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/TelegramCore.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ TelegramCoreMac.xcscheme orderHint - 6 + 5 SuppressBuildableAutocreation diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index 1f7e7912ae..e4fc8f1ce0 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -227,6 +227,7 @@ private var declaredEncodables: Void = { declareEncodable(ArchivedStickerPacksInfo.self, f: { ArchivedStickerPacksInfo(decoder: $0) }) declareEncodable(SynchronizeChatInputStateOperation.self, f: { SynchronizeChatInputStateOperation(decoder: $0) }) declareEncodable(SynchronizeSavedGifsOperation.self, f: { SynchronizeSavedGifsOperation(decoder: $0) }) + declareEncodable(SynchronizeSavedStickersOperation.self, f: { SynchronizeSavedStickersOperation(decoder: $0) }) declareEncodable(CacheStorageSettings.self, f: { CacheStorageSettings(decoder: $0) }) declareEncodable(LocalizationSettings.self, f: { LocalizationSettings(decoder: $0) }) declareEncodable(ProxySettings.self, f: { ProxySettings(decoder: $0) }) @@ -240,6 +241,9 @@ private var declaredEncodables: Void = { declareEncodable(TemporaryTwoStepPasswordToken.self, f: { TemporaryTwoStepPasswordToken(decoder: $0) }) declareEncodable(AuthorSignatureMessageAttribute.self, f: { AuthorSignatureMessageAttribute(decoder: $0) }) declareEncodable(TelegramMediaExpiredContent.self, f: { TelegramMediaExpiredContent(decoder: $0) }) + declareEncodable(SavedStickerItem.self, f: { SavedStickerItem(decoder: $0) }) + declareEncodable(ConsumablePersonalMentionMessageAttribute.self, f: { ConsumablePersonalMentionMessageAttribute(decoder: $0) }) + declareEncodable(ConsumePersonalMessageAction.self, f: { ConsumePersonalMessageAction(decoder: $0) }) return }() @@ -268,7 +272,7 @@ public func accountWithId(networkArguments: NetworkInitializationArguments, id: initializeMessageNamespacesWithHoles.append((peerNamespace, Namespaces.Message.Cloud)) } - let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: 1))], initializeMessageNamespacesWithHoles: initializeMessageNamespacesWithHoles, existingMessageTags: MessageTags.all, existingGlobalMessageTags: GlobalMessageTags.all, peerNamespacesRequiringMessageTextIndex: [Namespaces.Peer.SecretChat]) + let seedConfiguration = SeedConfiguration(initializeChatListWithHoles: [ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: 1))], initializeMessageNamespacesWithHoles: initializeMessageNamespacesWithHoles, existingMessageTags: MessageTags.all, messageTagsWithSummary: MessageTags.unseenPersonalMessage, existingGlobalMessageTags: GlobalMessageTags.all, peerNamespacesRequiringMessageTextIndex: [Namespaces.Peer.SecretChat]) let postbox = openPostbox(basePath: path + "/postbox", globalMessageIdsNamespace: Namespaces.Message.Cloud, seedConfiguration: seedConfiguration) @@ -456,8 +460,6 @@ public class Account { public var applicationContext: Any? - public let settings: AccountSettings = defaultAccountSettings() - public let notificationToken = Promise() public let voipToken = Promise() private let notificationTokenDisposable = MetaDisposable() @@ -636,9 +638,11 @@ public class Account { 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(managedSynchronizeSavedGifsOperations(postbox: self.postbox, network: self.network).start()) + self.managedOperationsDisposable.add(managedSynchronizeSavedStickersOperations(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedRecentlyUsedInlineBots(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedLocalTypingActivities(activities: self.localInputActivityManager.allActivities(), postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) + self.managedOperationsDisposable.add(managedConsumePersonalMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedSynchronizeChatInputStateOperations(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedConfigurationUpdates(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedLocalizationUpdatesOperations(postbox: self.postbox, network: self.network).start()) diff --git a/TelegramCore/AccountSettings.swift b/TelegramCore/AccountSettings.swift deleted file mode 100644 index 3a6a2cae06..0000000000 --- a/TelegramCore/AccountSettings.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation -#if os(macOS) - import PostboxMac -#else - import Postbox -#endif - -public struct AutomaticDownloadSettings { - public let downloadPhotos: Bool - public let downloadVoiceMessages: Bool - public let downloadGifs: Bool -} - -public struct AccountSettings { - public let oneToOneChatsAutomaticDownloadSettings: AutomaticDownloadSettings - public let groupChatsAutomaticDownloadSettings: AutomaticDownloadSettings -} - -func defaultAccountSettings() -> AccountSettings { - return AccountSettings(oneToOneChatsAutomaticDownloadSettings: AutomaticDownloadSettings(downloadPhotos: true, downloadVoiceMessages: true, downloadGifs: true), groupChatsAutomaticDownloadSettings: AutomaticDownloadSettings(downloadPhotos: true, downloadVoiceMessages: true, downloadGifs: true)) -} - -public extension AccountSettings { - public func automaticDownloadSettingsForPeerId(_ peerId: PeerId) -> AutomaticDownloadSettings { - if peerId.namespace == Namespaces.Peer.CloudUser { - return self.oneToOneChatsAutomaticDownloadSettings - } else { - return self.groupChatsAutomaticDownloadSettings - } - } -} diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index b4e61da274..9c66a21187 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -1287,7 +1287,7 @@ private func pollChannel(_ account: Account, peer: Peer, state: AccountMutableSt channelState = ChannelState(pts: pts, invalidatedPts: nil) } updatedState.updateChannelState(peer.id, state: channelState) - case let .channelDifferenceTooLong(_, pts, timeout, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, messages, chats, users): + case let .channelDifferenceTooLong(_, pts, timeout, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, messages, chats, users): apiTimeout = timeout let channelState = ChannelState(pts: pts, invalidatedPts: pts) diff --git a/TelegramCore/AccountViewTracker.swift b/TelegramCore/AccountViewTracker.swift index 274ff13e8d..3879055c4a 100644 --- a/TelegramCore/AccountViewTracker.swift +++ b/TelegramCore/AccountViewTracker.swift @@ -200,6 +200,8 @@ public final class AccountViewTracker { private var nextUpdatedViewCountDisposableId: Int32 = 0 private var updatedViewCountDisposables = DisposableDict() + private var updatedSeenPersonalMessageIds = Set() + private var cachedDataContexts: [PeerId: PeerCachedDataContext] = [:] private var cachedChannelParticipantsContexts: [PeerId: CachedChannelParticipantsContext] = [:] @@ -407,6 +409,49 @@ public final class AccountViewTracker { } } + public func updateMarkMentionsSeenForMessageIds(messageIds: Set) { + self.queue.async { + var addedMessageIds: [MessageId] = [] + for messageId in messageIds { + if !self.updatedSeenPersonalMessageIds.contains(messageId) { + self.updatedSeenPersonalMessageIds.insert(messageId) + addedMessageIds.append(messageId) + } + } + if !addedMessageIds.isEmpty { + if let account = self.account { + let _ = (account.postbox.modify { modifier -> Void in + for id in addedMessageIds { + if let message = modifier.getMessage(id) { + var consume = false + inner: for attribute in message.attributes { + if let attribute = attribute as? ConsumablePersonalMentionMessageAttribute, !attribute.consumed, !attribute.pending { + consume = true + break inner + } + } + if consume { + modifier.updateMessage(id, update: { currentMessage in + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute { + attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: attribute.consumed, pending: true) + break loop + } + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + + modifier.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: ConsumePersonalMessageAction()) + } + } + } + }).start() + } + } + } + } + private func updateCachedPeerData(peerId: PeerId, viewId: Int32, referenceData: CachedPeerData?) { self.queue.async { let context: PeerCachedDataContext @@ -794,4 +839,42 @@ public final class AccountViewTracker { return .never() } } + + public func unseenPersonalMessagesCount(peerId: PeerId) -> Signal { + if let account = self.account { + let pendingKey: PostboxViewKey = .pendingMessageActionsSummary(type: .consumeUnseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud) + let summaryKey: PostboxViewKey = .historyTagSummaryView(tag: .unseenPersonalMessage, peerId: peerId, namespace: Namespaces.Message.Cloud) + return account.postbox.combinedView(keys: [pendingKey, summaryKey]) + |> map { views -> Int32 in + var count: Int32 = 0 + if let view = views.views[pendingKey] as? PendingMessageActionsSummaryView { + count -= view.count + } + if let view = views.views[summaryKey] as? MessageHistoryTagSummaryView { + if let unseenCount = view.count { + count += unseenCount + } + } + return max(0, count) + } |> distinctUntilChanged + } else { + return .never() + } + } + + public func tailChatListView(count: Int) -> Signal<(ChatListView, ViewUpdateType), NoError> { + if let account = self.account { + return account.postbox.tailChatListView(count: count, summaryComponents: ChatListEntrySummaryComponents(tagSummary: ChatListEntryMessageTagSummaryComponent(tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(type: PendingMessageActionType.consumeUnseenPersonalMessage, namespace: Namespaces.Message.Cloud))) + } else { + return .never() + } + } + + public func aroundChatListView(index: ChatListIndex, count: Int) -> Signal<(ChatListView, ViewUpdateType), NoError> { + if let account = self.account { + return account.postbox.aroundChatListView(index: index, count: count, summaryComponents: ChatListEntrySummaryComponents(tagSummary: ChatListEntryMessageTagSummaryComponent(tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent(type: PendingMessageActionType.consumeUnseenPersonalMessage, namespace: Namespaces.Message.Cloud))) + } else { + return .never() + } + } } diff --git a/TelegramCore/Api.swift b/TelegramCore/Api.swift index 6c5277cd02..bafa6b5102 100644 --- a/TelegramCore/Api.swift +++ b/TelegramCore/Api.swift @@ -21,7 +21,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-206066487] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) } dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } dict[771925524] = { return Api.ChatFull.parse_chatFull($0) } - dict[-1781833897] = { return Api.ChatFull.parse_channelFull($0) } + dict[401891279] = { return Api.ChatFull.parse_channelFull($0) } dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) } dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) } dict[-489233354] = { return Api.ChatParticipant.parse_chatParticipantAdmin($0) } @@ -119,7 +119,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-496024847] = { return Api.UserStatus.parse_userStatusRecently($0) } dict[129960444] = { return Api.UserStatus.parse_userStatusLastWeek($0) } dict[2011940674] = { return Api.UserStatus.parse_userStatusLastMonth($0) } - dict[1728035348] = { return Api.Dialog.parse_dialog($0) } + dict[-455150117] = { return Api.Dialog.parse_dialog($0) } dict[381645902] = { return Api.SendMessageAction.parse_sendMessageTypingAction($0) } dict[-44119819] = { return Api.SendMessageAction.parse_sendMessageCancelAction($0) } dict[-1584933265] = { return Api.SendMessageAction.parse_sendMessageRecordVideoAction($0) } @@ -198,6 +198,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1425052898] = { return Api.Update.parse_updatePhoneCall($0) } dict[281165899] = { return Api.Update.parse_updateLangPackTooLong($0) } dict[1442983757] = { return Api.Update.parse_updateLangPack($0) } + dict[-451831443] = { return Api.Update.parse_updateFavedStickers($0) } + dict[-1987495099] = { return Api.Update.parse_updateChannelReadMessagesContents($0) } + dict[1887741886] = { return Api.Update.parse_updateContactsReset($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) } dict[-1557620115] = { return Api.ChannelParticipant.parse_channelParticipantSelf($0) } @@ -258,7 +261,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[371037736] = { return Api.TopPeerCategory.parse_topPeerCategoryChannels($0) } dict[511092620] = { return Api.TopPeerCategory.parse_topPeerCategoryPhoneCalls($0) } dict[-1219778094] = { return Api.contacts.Contacts.parse_contactsNotModified($0) } - dict[1871416498] = { return Api.contacts.Contacts.parse_contacts($0) } + dict[-353862078] = { return Api.contacts.Contacts.parse_contacts($0) } dict[-1798033689] = { return Api.ChannelMessagesFilter.parse_channelMessagesFilterEmpty($0) } dict[-847783593] = { return Api.ChannelMessagesFilter.parse_channelMessagesFilter($0) } dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) } @@ -315,6 +318,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-484690728] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantInvite($0) } dict[-422036098] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantToggleBan($0) } dict[-714643696] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantToggleAdmin($0) } + dict[-1312568665] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeStickerSet($0) } dict[-543777747] = { return Api.auth.ExportedAuthorization.parse_exportedAuthorization($0) } dict[-1269012015] = { return Api.messages.AffectedHistory.parse_affectedHistory($0) } dict[-2037289493] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) } @@ -361,7 +365,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[182649427] = { return Api.MessageRange.parse_messageRange($0) } dict[946083368] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultSuccess($0) } dict[904138920] = { return Api.messages.StickerSetInstallResult.parse_stickerSetInstallResultArchive($0) } - dict[2146355336] = { return Api.Config.parse_config($0) } + dict[-1913424220] = { return Api.Config.parse_config($0) } dict[-75283823] = { return Api.TopPeerCategoryPeers.parse_topPeerCategoryPeers($0) } dict[-1107729093] = { return Api.Game.parse_game($0) } dict[-1032140601] = { return Api.BotCommand.parse_botCommand($0) } @@ -484,6 +488,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2134272152] = { return Api.MessagesFilter.parse_inputMessagesFilterPhoneCalls($0) } dict[2054952868] = { return Api.MessagesFilter.parse_inputMessagesFilterRoundVoice($0) } dict[-1253451181] = { return Api.MessagesFilter.parse_inputMessagesFilterRoundVideo($0) } + dict[-1040652646] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentions($0) } + dict[1187706024] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentionsUnread($0) } dict[364538944] = { return Api.messages.Dialogs.parse_dialogs($0) } dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) } dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) } @@ -495,6 +501,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1130767150] = { return Api.BotInlineMessage.parse_botInlineMessageMediaVenue($0) } dict[904770772] = { return Api.BotInlineMessage.parse_botInlineMessageMediaContact($0) } dict[949182130] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) } + dict[-1634752813] = { return Api.messages.FavedStickers.parse_favedStickersNotModified($0) } + dict[-209768682] = { return Api.messages.FavedStickers.parse_favedStickers($0) } dict[1776236393] = { return Api.ExportedChatInvite.parse_chatInviteEmpty($0) } dict[-64092740] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) } dict[2079516406] = { return Api.Authorization.parse_authorization($0) } @@ -557,7 +565,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-715532088] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) } dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) } - dict[1091431943] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) } + dict[1788705589] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) } dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) } dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) } dict[1996904104] = { return Api.InputAppEvent.parse_inputAppEvent($0) } @@ -976,6 +984,8 @@ public struct Api { return _1.serialize(buffer, boxed) case let _1 as Api.InputPeerNotifySettings: return _1.serialize(buffer, boxed) + case let _1 as Api.messages.FavedStickers: + return _1.serialize(buffer, boxed) case let _1 as Api.ExportedChatInvite: return _1.serialize(buffer, boxed) case let _1 as Api.Authorization: @@ -2387,6 +2397,75 @@ public struct Api { } } + public enum FavedStickers: CustomStringConvertible { + case favedStickersNotModified + case favedStickers(hash: Int32, packs: [Api.StickerPack], stickers: [Api.Document]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { + switch self { + case .favedStickersNotModified: + if boxed { + buffer.appendInt32(-1634752813) + } + + break + case .favedStickers(let hash, let packs, let stickers): + if boxed { + buffer.appendInt32(-209768682) + } + serializeInt32(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(packs.count)) + for item in packs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { + item.serialize(buffer, true) + } + break + } + return true + } + + fileprivate static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? { + return Api.messages.FavedStickers.favedStickersNotModified + } + fileprivate static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + } + var _3: [Api.Document]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!) + } + else { + return nil + } + } + + public var description: String { + get { + switch self { + case .favedStickersNotModified: + return "(messages.favedStickersNotModified)" + case .favedStickers(let hash, let packs, let stickers): + return "(messages.favedStickers hash: \(hash), packs: \(packs), stickers: \(stickers))" + } + } + } + } + public enum AllStickers: CustomStringConvertible { case allStickersNotModified case allStickers(hash: Int32, sets: [Api.StickerSet]) @@ -2555,7 +2634,7 @@ public struct Api { public enum ChatFull: CustomStringConvertible { case chatFull(id: Int32, participants: Api.ChatParticipants, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]) - case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?) + case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { switch self { @@ -2574,9 +2653,9 @@ public struct Api { item.serialize(buffer, true) } break - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId): + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset): if boxed { - buffer.appendInt32(-1781833897) + buffer.appendInt32(401891279) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -2599,6 +2678,7 @@ public struct Api { if Int(flags) & Int(1 << 4) != 0 {serializeInt32(migratedFromChatId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {serializeInt32(migratedFromMaxId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 5) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {stickerset!.serialize(buffer, true)} break } return true @@ -2683,6 +2763,10 @@ public struct Api { if Int(_1!) & Int(1 << 4) != 0 {_16 = reader.readInt32() } var _17: Int32? if Int(_1!) & Int(1 << 5) != 0 {_17 = reader.readInt32() } + var _18: Api.StickerSet? + if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.StickerSet + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -2700,8 +2784,9 @@ public struct Api { let _c15 = (Int(_1!) & Int(1 << 4) == 0) || _15 != nil let _c16 = (Int(_1!) & Int(1 << 4) == 0) || _16 != nil let _c17 = (Int(_1!) & Int(1 << 5) == 0) || _17 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, readInboxMaxId: _8!, readOutboxMaxId: _9!, unreadCount: _10!, chatPhoto: _11!, notifySettings: _12!, exportedInvite: _13!, botInfo: _14!, migratedFromChatId: _15, migratedFromMaxId: _16, pinnedMsgId: _17) + let _c18 = (Int(_1!) & Int(1 << 8) == 0) || _18 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 { + return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, readInboxMaxId: _8!, readOutboxMaxId: _9!, unreadCount: _10!, chatPhoto: _11!, notifySettings: _12!, exportedInvite: _13!, botInfo: _14!, migratedFromChatId: _15, migratedFromMaxId: _16, pinnedMsgId: _17, stickerset: _18) } else { return nil @@ -2713,8 +2798,8 @@ public struct Api { switch self { case .chatFull(let id, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo): return "(chatFull id: \(id), participants: \(participants), chatPhoto: \(chatPhoto), notifySettings: \(notifySettings), exportedInvite: \(exportedInvite), botInfo: \(botInfo))" - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId): - return "(channelFull flags: \(flags), id: \(id), about: \(about), participantsCount: \(participantsCount), adminsCount: \(adminsCount), kickedCount: \(kickedCount), bannedCount: \(bannedCount), readInboxMaxId: \(readInboxMaxId), readOutboxMaxId: \(readOutboxMaxId), unreadCount: \(unreadCount), chatPhoto: \(chatPhoto), notifySettings: \(notifySettings), exportedInvite: \(exportedInvite), botInfo: \(botInfo), migratedFromChatId: \(migratedFromChatId), migratedFromMaxId: \(migratedFromMaxId), pinnedMsgId: \(pinnedMsgId))" + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset): + return "(channelFull flags: \(flags), id: \(id), about: \(about), participantsCount: \(participantsCount), adminsCount: \(adminsCount), kickedCount: \(kickedCount), bannedCount: \(bannedCount), readInboxMaxId: \(readInboxMaxId), readOutboxMaxId: \(readOutboxMaxId), unreadCount: \(unreadCount), chatPhoto: \(chatPhoto), notifySettings: \(notifySettings), exportedInvite: \(exportedInvite), botInfo: \(botInfo), migratedFromChatId: \(migratedFromChatId), migratedFromMaxId: \(migratedFromMaxId), pinnedMsgId: \(pinnedMsgId), stickerset: \(stickerset))" } } } @@ -4955,13 +5040,13 @@ public struct Api { } public enum Dialog: CustomStringConvertible { - case dialog(flags: Int32, peer: Api.Peer, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, notifySettings: Api.PeerNotifySettings, pts: Int32?, draft: Api.DraftMessage?) + case dialog(flags: Int32, peer: Api.Peer, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, notifySettings: Api.PeerNotifySettings, pts: Int32?, draft: Api.DraftMessage?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { switch self { - case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let notifySettings, let pts, let draft): + case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let notifySettings, let pts, let draft): if boxed { - buffer.appendInt32(1728035348) + buffer.appendInt32(-455150117) } serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) @@ -4969,6 +5054,7 @@ public struct Api { serializeInt32(readInboxMaxId, buffer: buffer, boxed: false) serializeInt32(readOutboxMaxId, buffer: buffer, boxed: false) serializeInt32(unreadCount, buffer: buffer, boxed: false) + serializeInt32(unreadMentionsCount, buffer: buffer, boxed: false) notifySettings.serialize(buffer, true) if Int(flags) & Int(1 << 0) != 0 {serializeInt32(pts!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {draft!.serialize(buffer, true)} @@ -4992,15 +5078,17 @@ public struct Api { _5 = reader.readInt32() var _6: Int32? _6 = reader.readInt32() - var _7: Api.PeerNotifySettings? + var _7: Int32? + _7 = reader.readInt32() + var _8: Api.PeerNotifySettings? if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings + _8 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings } - var _8: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_8 = reader.readInt32() } - var _9: Api.DraftMessage? + var _9: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_9 = reader.readInt32() } + var _10: Api.DraftMessage? if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.DraftMessage + _10 = Api.parse(reader, signature: signature) as? Api.DraftMessage } } let _c1 = _1 != nil let _c2 = _2 != nil @@ -5009,10 +5097,11 @@ public struct Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Dialog.dialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, notifySettings: _7!, pts: _8, draft: _9) + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { + return Api.Dialog.dialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, unreadMentionsCount: _7!, notifySettings: _8!, pts: _9, draft: _10) } else { return nil @@ -5022,8 +5111,8 @@ public struct Api { public var description: String { get { switch self { - case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let notifySettings, let pts, let draft): - return "(dialog flags: \(flags), peer: \(peer), topMessage: \(topMessage), readInboxMaxId: \(readInboxMaxId), readOutboxMaxId: \(readOutboxMaxId), unreadCount: \(unreadCount), notifySettings: \(notifySettings), pts: \(pts), draft: \(draft))" + case .dialog(let flags, let peer, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let notifySettings, let pts, let draft): + return "(dialog flags: \(flags), peer: \(peer), topMessage: \(topMessage), readInboxMaxId: \(readInboxMaxId), readOutboxMaxId: \(readOutboxMaxId), unreadCount: \(unreadCount), unreadMentionsCount: \(unreadMentionsCount), notifySettings: \(notifySettings), pts: \(pts), draft: \(draft))" } } } @@ -5350,6 +5439,9 @@ public struct Api { case updatePhoneCall(phoneCall: Api.PhoneCall) case updateLangPackTooLong case updateLangPack(difference: Api.LangPackDifference) + case updateFavedStickers + case updateChannelReadMessagesContents(channelId: Int32, messages: [Int32]) + case updateContactsReset public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { switch self { @@ -5866,6 +5958,29 @@ public struct Api { buffer.appendInt32(1442983757) } difference.serialize(buffer, true) + break + case .updateFavedStickers: + if boxed { + buffer.appendInt32(-451831443) + } + + break + case .updateChannelReadMessagesContents(let channelId, let messages): + if boxed { + buffer.appendInt32(-1987495099) + } + serializeInt32(channelId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .updateContactsReset: + if boxed { + buffer.appendInt32(1887741886) + } + break } return true @@ -6923,6 +7038,28 @@ public struct Api { return nil } } + fileprivate static func parse_updateFavedStickers(_ reader: BufferReader) -> Update? { + return Api.Update.updateFavedStickers + } + fileprivate static func parse_updateChannelReadMessagesContents(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Int32]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateChannelReadMessagesContents(channelId: _1!, messages: _2!) + } + else { + return nil + } + } + fileprivate static func parse_updateContactsReset(_ reader: BufferReader) -> Update? { + return Api.Update.updateContactsReset + } public var description: String { get { @@ -7051,6 +7188,12 @@ public struct Api { return "(updateLangPackTooLong)" case .updateLangPack(let difference): return "(updateLangPack difference: \(difference))" + case .updateFavedStickers: + return "(updateFavedStickers)" + case .updateChannelReadMessagesContents(let channelId, let messages): + return "(updateChannelReadMessagesContents channelId: \(channelId), messages: \(messages))" + case .updateContactsReset: + return "(updateContactsReset)" } } } @@ -9649,6 +9792,7 @@ public struct Api { case channelAdminLogEventActionParticipantInvite(participant: Api.ChannelParticipant) case channelAdminLogEventActionParticipantToggleBan(prevParticipant: Api.ChannelParticipant, newParticipant: Api.ChannelParticipant) case channelAdminLogEventActionParticipantToggleAdmin(prevParticipant: Api.ChannelParticipant, newParticipant: Api.ChannelParticipant) + case channelAdminLogEventActionChangeStickerSet(prevStickerset: Api.InputStickerSet, newStickerset: Api.InputStickerSet) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { switch self { @@ -9743,6 +9887,13 @@ public struct Api { prevParticipant.serialize(buffer, true) newParticipant.serialize(buffer, true) break + case .channelAdminLogEventActionChangeStickerSet(let prevStickerset, let newStickerset): + if boxed { + buffer.appendInt32(-1312568665) + } + prevStickerset.serialize(buffer, true) + newStickerset.serialize(buffer, true) + break } return true } @@ -9932,6 +10083,24 @@ public struct Api { return nil } } + fileprivate static func parse_channelAdminLogEventActionChangeStickerSet(_ reader: BufferReader) -> ChannelAdminLogEventAction? { + var _1: Api.InputStickerSet? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputStickerSet + } + var _2: Api.InputStickerSet? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.InputStickerSet + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeStickerSet(prevStickerset: _1!, newStickerset: _2!) + } + else { + return nil + } + } public var description: String { get { @@ -9964,6 +10133,8 @@ public struct Api { return "(channelAdminLogEventActionParticipantToggleBan prevParticipant: \(prevParticipant), newParticipant: \(newParticipant))" case .channelAdminLogEventActionParticipantToggleAdmin(let prevParticipant, let newParticipant): return "(channelAdminLogEventActionParticipantToggleAdmin prevParticipant: \(prevParticipant), newParticipant: \(newParticipant))" + case .channelAdminLogEventActionChangeStickerSet(let prevStickerset, let newStickerset): + return "(channelAdminLogEventActionChangeStickerSet prevStickerset: \(prevStickerset), newStickerset: \(newStickerset))" } } } @@ -11010,13 +11181,13 @@ public struct Api { } public enum Config: CustomStringConvertible { - case config(flags: Int32, date: Int32, expires: Int32, testMode: Api.Bool, thisDc: Int32, dcOptions: [Api.DcOption], chatSizeMax: Int32, megagroupSizeMax: Int32, forwardedCountMax: Int32, onlineUpdatePeriodMs: Int32, offlineBlurTimeoutMs: Int32, offlineIdleTimeoutMs: Int32, onlineCloudTimeoutMs: Int32, notifyCloudDelayMs: Int32, notifyDefaultDelayMs: Int32, chatBigSize: Int32, pushChatPeriodMs: Int32, pushChatLimit: Int32, savedGifsLimit: Int32, editTimeLimit: Int32, ratingEDecay: Int32, stickersRecentLimit: Int32, tmpSessions: Int32?, pinnedDialogsCountMax: Int32, callReceiveTimeoutMs: Int32, callRingTimeoutMs: Int32, callConnectTimeoutMs: Int32, callPacketTimeoutMs: Int32, meUrlPrefix: String, suggestedLangCode: String?, langPackVersion: Int32?, disabledFeatures: [Api.DisabledFeature]) + case config(flags: Int32, date: Int32, expires: Int32, testMode: Api.Bool, thisDc: Int32, dcOptions: [Api.DcOption], chatSizeMax: Int32, megagroupSizeMax: Int32, forwardedCountMax: Int32, onlineUpdatePeriodMs: Int32, offlineBlurTimeoutMs: Int32, offlineIdleTimeoutMs: Int32, onlineCloudTimeoutMs: Int32, notifyCloudDelayMs: Int32, notifyDefaultDelayMs: Int32, chatBigSize: Int32, pushChatPeriodMs: Int32, pushChatLimit: Int32, savedGifsLimit: Int32, editTimeLimit: Int32, ratingEDecay: Int32, stickersRecentLimit: Int32, stickersFavedLimit: Int32, tmpSessions: Int32?, pinnedDialogsCountMax: Int32, callReceiveTimeoutMs: Int32, callRingTimeoutMs: Int32, callConnectTimeoutMs: Int32, callPacketTimeoutMs: Int32, meUrlPrefix: String, suggestedLangCode: String?, langPackVersion: Int32?, disabledFeatures: [Api.DisabledFeature]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { switch self { - case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let chatBigSize, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let ratingEDecay, let stickersRecentLimit, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let suggestedLangCode, let langPackVersion, let disabledFeatures): + case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let chatBigSize, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let ratingEDecay, let stickersRecentLimit, let stickersFavedLimit, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let suggestedLangCode, let langPackVersion, let disabledFeatures): if boxed { - buffer.appendInt32(2146355336) + buffer.appendInt32(-1913424220) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(date, buffer: buffer, boxed: false) @@ -11044,6 +11215,7 @@ public struct Api { serializeInt32(editTimeLimit, buffer: buffer, boxed: false) serializeInt32(ratingEDecay, buffer: buffer, boxed: false) serializeInt32(stickersRecentLimit, buffer: buffer, boxed: false) + serializeInt32(stickersFavedLimit, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt32(tmpSessions!, buffer: buffer, boxed: false)} serializeInt32(pinnedDialogsCountMax, buffer: buffer, boxed: false) serializeInt32(callReceiveTimeoutMs, buffer: buffer, boxed: false) @@ -11113,9 +11285,9 @@ public struct Api { var _22: Int32? _22 = reader.readInt32() var _23: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_23 = reader.readInt32() } + _23 = reader.readInt32() var _24: Int32? - _24 = reader.readInt32() + if Int(_1!) & Int(1 << 0) != 0 {_24 = reader.readInt32() } var _25: Int32? _25 = reader.readInt32() var _26: Int32? @@ -11124,15 +11296,17 @@ public struct Api { _27 = reader.readInt32() var _28: Int32? _28 = reader.readInt32() - var _29: String? - _29 = parseString(reader) + var _29: Int32? + _29 = reader.readInt32() var _30: String? - if Int(_1!) & Int(1 << 2) != 0 {_30 = parseString(reader) } - var _31: Int32? - if Int(_1!) & Int(1 << 2) != 0 {_31 = reader.readInt32() } - var _32: [Api.DisabledFeature]? + _30 = parseString(reader) + var _31: String? + if Int(_1!) & Int(1 << 2) != 0 {_31 = parseString(reader) } + var _32: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_32 = reader.readInt32() } + var _33: [Api.DisabledFeature]? if let _ = reader.readInt32() { - _32 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DisabledFeature.self) + _33 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DisabledFeature.self) } let _c1 = _1 != nil let _c2 = _2 != nil @@ -11156,18 +11330,19 @@ public struct Api { let _c20 = _20 != nil let _c21 = _21 != nil let _c22 = _22 != nil - let _c23 = (Int(_1!) & Int(1 << 0) == 0) || _23 != nil - let _c24 = _24 != nil + let _c23 = _23 != nil + let _c24 = (Int(_1!) & Int(1 << 0) == 0) || _24 != nil let _c25 = _25 != nil let _c26 = _26 != nil let _c27 = _27 != nil let _c28 = _28 != nil let _c29 = _29 != nil - let _c30 = (Int(_1!) & Int(1 << 2) == 0) || _30 != nil + let _c30 = _30 != nil let _c31 = (Int(_1!) & Int(1 << 2) == 0) || _31 != nil - let _c32 = _32 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 { - return Api.Config.config(flags: _1!, date: _2!, expires: _3!, testMode: _4!, thisDc: _5!, dcOptions: _6!, chatSizeMax: _7!, megagroupSizeMax: _8!, forwardedCountMax: _9!, onlineUpdatePeriodMs: _10!, offlineBlurTimeoutMs: _11!, offlineIdleTimeoutMs: _12!, onlineCloudTimeoutMs: _13!, notifyCloudDelayMs: _14!, notifyDefaultDelayMs: _15!, chatBigSize: _16!, pushChatPeriodMs: _17!, pushChatLimit: _18!, savedGifsLimit: _19!, editTimeLimit: _20!, ratingEDecay: _21!, stickersRecentLimit: _22!, tmpSessions: _23, pinnedDialogsCountMax: _24!, callReceiveTimeoutMs: _25!, callRingTimeoutMs: _26!, callConnectTimeoutMs: _27!, callPacketTimeoutMs: _28!, meUrlPrefix: _29!, suggestedLangCode: _30, langPackVersion: _31, disabledFeatures: _32!) + let _c32 = (Int(_1!) & Int(1 << 2) == 0) || _32 != nil + let _c33 = _33 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 { + return Api.Config.config(flags: _1!, date: _2!, expires: _3!, testMode: _4!, thisDc: _5!, dcOptions: _6!, chatSizeMax: _7!, megagroupSizeMax: _8!, forwardedCountMax: _9!, onlineUpdatePeriodMs: _10!, offlineBlurTimeoutMs: _11!, offlineIdleTimeoutMs: _12!, onlineCloudTimeoutMs: _13!, notifyCloudDelayMs: _14!, notifyDefaultDelayMs: _15!, chatBigSize: _16!, pushChatPeriodMs: _17!, pushChatLimit: _18!, savedGifsLimit: _19!, editTimeLimit: _20!, ratingEDecay: _21!, stickersRecentLimit: _22!, stickersFavedLimit: _23!, tmpSessions: _24, pinnedDialogsCountMax: _25!, callReceiveTimeoutMs: _26!, callRingTimeoutMs: _27!, callConnectTimeoutMs: _28!, callPacketTimeoutMs: _29!, meUrlPrefix: _30!, suggestedLangCode: _31, langPackVersion: _32, disabledFeatures: _33!) } else { return nil @@ -11177,8 +11352,8 @@ public struct Api { public var description: String { get { switch self { - case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let chatBigSize, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let ratingEDecay, let stickersRecentLimit, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let suggestedLangCode, let langPackVersion, let disabledFeatures): - return "(config flags: \(flags), date: \(date), expires: \(expires), testMode: \(testMode), thisDc: \(thisDc), dcOptions: \(dcOptions), chatSizeMax: \(chatSizeMax), megagroupSizeMax: \(megagroupSizeMax), forwardedCountMax: \(forwardedCountMax), onlineUpdatePeriodMs: \(onlineUpdatePeriodMs), offlineBlurTimeoutMs: \(offlineBlurTimeoutMs), offlineIdleTimeoutMs: \(offlineIdleTimeoutMs), onlineCloudTimeoutMs: \(onlineCloudTimeoutMs), notifyCloudDelayMs: \(notifyCloudDelayMs), notifyDefaultDelayMs: \(notifyDefaultDelayMs), chatBigSize: \(chatBigSize), pushChatPeriodMs: \(pushChatPeriodMs), pushChatLimit: \(pushChatLimit), savedGifsLimit: \(savedGifsLimit), editTimeLimit: \(editTimeLimit), ratingEDecay: \(ratingEDecay), stickersRecentLimit: \(stickersRecentLimit), tmpSessions: \(tmpSessions), pinnedDialogsCountMax: \(pinnedDialogsCountMax), callReceiveTimeoutMs: \(callReceiveTimeoutMs), callRingTimeoutMs: \(callRingTimeoutMs), callConnectTimeoutMs: \(callConnectTimeoutMs), callPacketTimeoutMs: \(callPacketTimeoutMs), meUrlPrefix: \(meUrlPrefix), suggestedLangCode: \(suggestedLangCode), langPackVersion: \(langPackVersion), disabledFeatures: \(disabledFeatures))" + case .config(let flags, let date, let expires, let testMode, let thisDc, let dcOptions, let chatSizeMax, let megagroupSizeMax, let forwardedCountMax, let onlineUpdatePeriodMs, let offlineBlurTimeoutMs, let offlineIdleTimeoutMs, let onlineCloudTimeoutMs, let notifyCloudDelayMs, let notifyDefaultDelayMs, let chatBigSize, let pushChatPeriodMs, let pushChatLimit, let savedGifsLimit, let editTimeLimit, let ratingEDecay, let stickersRecentLimit, let stickersFavedLimit, let tmpSessions, let pinnedDialogsCountMax, let callReceiveTimeoutMs, let callRingTimeoutMs, let callConnectTimeoutMs, let callPacketTimeoutMs, let meUrlPrefix, let suggestedLangCode, let langPackVersion, let disabledFeatures): + return "(config flags: \(flags), date: \(date), expires: \(expires), testMode: \(testMode), thisDc: \(thisDc), dcOptions: \(dcOptions), chatSizeMax: \(chatSizeMax), megagroupSizeMax: \(megagroupSizeMax), forwardedCountMax: \(forwardedCountMax), onlineUpdatePeriodMs: \(onlineUpdatePeriodMs), offlineBlurTimeoutMs: \(offlineBlurTimeoutMs), offlineIdleTimeoutMs: \(offlineIdleTimeoutMs), onlineCloudTimeoutMs: \(onlineCloudTimeoutMs), notifyCloudDelayMs: \(notifyCloudDelayMs), notifyDefaultDelayMs: \(notifyDefaultDelayMs), chatBigSize: \(chatBigSize), pushChatPeriodMs: \(pushChatPeriodMs), pushChatLimit: \(pushChatLimit), savedGifsLimit: \(savedGifsLimit), editTimeLimit: \(editTimeLimit), ratingEDecay: \(ratingEDecay), stickersRecentLimit: \(stickersRecentLimit), stickersFavedLimit: \(stickersFavedLimit), tmpSessions: \(tmpSessions), pinnedDialogsCountMax: \(pinnedDialogsCountMax), callReceiveTimeoutMs: \(callReceiveTimeoutMs), callRingTimeoutMs: \(callRingTimeoutMs), callConnectTimeoutMs: \(callConnectTimeoutMs), callPacketTimeoutMs: \(callPacketTimeoutMs), meUrlPrefix: \(meUrlPrefix), suggestedLangCode: \(suggestedLangCode), langPackVersion: \(langPackVersion), disabledFeatures: \(disabledFeatures))" } } } @@ -14321,6 +14496,8 @@ public struct Api { case inputMessagesFilterPhoneCalls(flags: Int32) case inputMessagesFilterRoundVoice case inputMessagesFilterRoundVideo + case inputMessagesFilterMyMentions + case inputMessagesFilterMyMentionsUnread public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { switch self { @@ -14407,6 +14584,18 @@ public struct Api { buffer.appendInt32(-1253451181) } + break + case .inputMessagesFilterMyMentions: + if boxed { + buffer.appendInt32(-1040652646) + } + + break + case .inputMessagesFilterMyMentionsUnread: + if boxed { + buffer.appendInt32(1187706024) + } + break } return true @@ -14462,6 +14651,12 @@ public struct Api { fileprivate static func parse_inputMessagesFilterRoundVideo(_ reader: BufferReader) -> MessagesFilter? { return Api.MessagesFilter.inputMessagesFilterRoundVideo } + fileprivate static func parse_inputMessagesFilterMyMentions(_ reader: BufferReader) -> MessagesFilter? { + return Api.MessagesFilter.inputMessagesFilterMyMentions + } + fileprivate static func parse_inputMessagesFilterMyMentionsUnread(_ reader: BufferReader) -> MessagesFilter? { + return Api.MessagesFilter.inputMessagesFilterMyMentionsUnread + } public var description: String { get { @@ -14494,6 +14689,10 @@ public struct Api { return "(inputMessagesFilterRoundVoice)" case .inputMessagesFilterRoundVideo: return "(inputMessagesFilterRoundVideo)" + case .inputMessagesFilterMyMentions: + return "(inputMessagesFilterMyMentions)" + case .inputMessagesFilterMyMentionsUnread: + return "(inputMessagesFilterMyMentionsUnread)" } } } @@ -18183,7 +18382,7 @@ public struct Api { public enum Contacts: CustomStringConvertible { case contactsNotModified - case contacts(contacts: [Api.Contact], users: [Api.User]) + case contacts(contacts: [Api.Contact], savedCount: Int32, users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { switch self { @@ -18193,15 +18392,16 @@ public struct Api { } break - case .contacts(let contacts, let users): + case .contacts(let contacts, let savedCount, let users): if boxed { - buffer.appendInt32(1871416498) + buffer.appendInt32(-353862078) } buffer.appendInt32(481674261) buffer.appendInt32(Int32(contacts.count)) for item in contacts { item.serialize(buffer, true) } + serializeInt32(savedCount, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { @@ -18220,14 +18420,17 @@ public struct Api { if let _ = reader.readInt32() { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Contact.self) } - var _2: [Api.User]? + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.User]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.contacts.Contacts.contacts(contacts: _1!, users: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.contacts.Contacts.contacts(contacts: _1!, savedCount: _2!, users: _3!) } else { return nil @@ -18239,8 +18442,8 @@ public struct Api { switch self { case .contactsNotModified: return "(contacts.contactsNotModified)" - case .contacts(let contacts, let users): - return "(contacts.contacts contacts: \(contacts), users: \(users))" + case .contacts(let contacts, let savedCount, let users): + return "(contacts.contacts contacts: \(contacts), savedCount: \(savedCount), users: \(users))" } } } @@ -19011,7 +19214,7 @@ public struct Api { public enum ChannelDifference: CustomStringConvertible { case channelDifferenceEmpty(flags: Int32, pts: Int32, timeout: Int32?) - case channelDifferenceTooLong(flags: Int32, pts: Int32, timeout: Int32?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case channelDifferenceTooLong(flags: Int32, pts: Int32, timeout: Int32?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool { @@ -19024,9 +19227,9 @@ public struct Api { serializeInt32(pts, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} break - case .channelDifferenceTooLong(let flags, let pts, let timeout, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let messages, let chats, let users): + case .channelDifferenceTooLong(let flags, let pts, let timeout, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let messages, let chats, let users): if boxed { - buffer.appendInt32(1091431943) + buffer.appendInt32(1788705589) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(pts, buffer: buffer, boxed: false) @@ -19035,6 +19238,7 @@ public struct Api { serializeInt32(readInboxMaxId, buffer: buffer, boxed: false) serializeInt32(readOutboxMaxId, buffer: buffer, boxed: false) serializeInt32(unreadCount, buffer: buffer, boxed: false) + serializeInt32(unreadMentionsCount, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(messages.count)) for item in messages { @@ -19115,17 +19319,19 @@ public struct Api { _6 = reader.readInt32() var _7: Int32? _7 = reader.readInt32() - var _8: [Api.Message]? + var _8: Int32? + _8 = reader.readInt32() + var _9: [Api.Message]? if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) } - var _9: [Api.Chat]? + var _10: [Api.Chat]? if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _10: [Api.User]? + var _11: [Api.User]? if let _ = reader.readInt32() { - _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil @@ -19137,8 +19343,9 @@ public struct Api { let _c8 = _8 != nil let _c9 = _9 != nil let _c10 = _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, pts: _2!, timeout: _3, topMessage: _4!, readInboxMaxId: _5!, readOutboxMaxId: _6!, unreadCount: _7!, messages: _8!, chats: _9!, users: _10!) + let _c11 = _11 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { + return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, pts: _2!, timeout: _3, topMessage: _4!, readInboxMaxId: _5!, readOutboxMaxId: _6!, unreadCount: _7!, unreadMentionsCount: _8!, messages: _9!, chats: _10!, users: _11!) } else { return nil @@ -19187,8 +19394,8 @@ public struct Api { switch self { case .channelDifferenceEmpty(let flags, let pts, let timeout): return "(updates.channelDifferenceEmpty flags: \(flags), pts: \(pts), timeout: \(timeout))" - case .channelDifferenceTooLong(let flags, let pts, let timeout, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let messages, let chats, let users): - return "(updates.channelDifferenceTooLong flags: \(flags), pts: \(pts), timeout: \(timeout), topMessage: \(topMessage), readInboxMaxId: \(readInboxMaxId), readOutboxMaxId: \(readOutboxMaxId), unreadCount: \(unreadCount), messages: \(messages), chats: \(chats), users: \(users))" + case .channelDifferenceTooLong(let flags, let pts, let timeout, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let messages, let chats, let users): + return "(updates.channelDifferenceTooLong flags: \(flags), pts: \(pts), timeout: \(timeout), topMessage: \(topMessage), readInboxMaxId: \(readInboxMaxId), readOutboxMaxId: \(readOutboxMaxId), unreadCount: \(unreadCount), unreadMentionsCount: \(unreadMentionsCount), messages: \(messages), chats: \(chats), users: \(users))" case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): return "(updates.channelDifference flags: \(flags), pts: \(pts), timeout: \(timeout), newMessages: \(newMessages), otherUpdates: \(otherUpdates), chats: \(chats), users: \(users))" } @@ -21427,29 +21634,6 @@ public struct Api { }) } - public static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputUser?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offset: Int32, maxId: Int32, limit: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.messages.Messages?) { - let buffer = Buffer() - buffer.appendInt32(-225926539) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeString(q, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {fromId!.serialize(buffer, true)} - filter.serialize(buffer, true) - serializeInt32(minDate, buffer: buffer, boxed: false) - serializeInt32(maxDate, buffer: buffer, boxed: false) - serializeInt32(offset, buffer: buffer, boxed: false) - serializeInt32(maxId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription({return "(messages.search flags: \(flags), peer: \(peer), q: \(q), fromId: \(fromId), filter: \(filter), minDate: \(minDate), maxDate: \(maxDate), offset: \(offset), maxId: \(maxId), limit: \(limit))"}), buffer, { (buffer: Buffer) -> Api.messages.Messages? in - let reader = BufferReader(buffer) - var result: Api.messages.Messages? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages - } - return result - }) - } - public static func sendScreenshotNotification(peer: Api.InputPeer, replyToMsgId: Int32, randomId: Int64) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Updates?) { let buffer = Buffer() buffer.appendInt32(-914493408) @@ -21465,6 +21649,79 @@ public struct Api { return result }) } + + public static func getFavedStickers(hash: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.messages.FavedStickers?) { + let buffer = Buffer() + buffer.appendInt32(567151374) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription({return "(messages.getFavedStickers hash: \(hash))"}), buffer, { (buffer: Buffer) -> Api.messages.FavedStickers? in + let reader = BufferReader(buffer) + var result: Api.messages.FavedStickers? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.FavedStickers + } + return result + }) + } + + public static func faveSticker(id: Api.InputDocument, unfave: Api.Bool) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Bool?) { + let buffer = Buffer() + buffer.appendInt32(-1174420133) + id.serialize(buffer, true) + unfave.serialize(buffer, true) + return (FunctionDescription({return "(messages.faveSticker id: \(id), unfave: \(unfave))"}), buffer, { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + + public static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputUser?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.messages.Messages?) { + let buffer = Buffer() + buffer.appendInt32(60726944) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeString(q, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {fromId!.serialize(buffer, true)} + filter.serialize(buffer, true) + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(maxDate, buffer: buffer, boxed: false) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + return (FunctionDescription({return "(messages.search flags: \(flags), peer: \(peer), q: \(q), fromId: \(fromId), filter: \(filter), minDate: \(minDate), maxDate: \(maxDate), offsetId: \(offsetId), addOffset: \(addOffset), limit: \(limit), maxId: \(maxId), minId: \(minId))"}), buffer, { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } + + public static func getUnreadMentions(peer: Api.InputPeer, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.messages.Messages?) { + let buffer = Buffer() + buffer.appendInt32(1180140658) + peer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + return (FunctionDescription({return "(messages.getUnreadMentions peer: \(peer), offsetId: \(offsetId), addOffset: \(addOffset), limit: \(limit), maxId: \(maxId), minId: \(minId))"}), buffer, { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } } public struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Bool?) { @@ -21932,6 +22189,40 @@ public struct Api { return result }) } + + public static func setStickers(channel: Api.InputChannel, stickerset: Api.InputStickerSet) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Bool?) { + let buffer = Buffer() + buffer.appendInt32(-359881479) + channel.serialize(buffer, true) + stickerset.serialize(buffer, true) + return (FunctionDescription({return "(channels.setStickers channel: \(channel), stickerset: \(stickerset))"}), buffer, { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + + public static func readMessageContents(channel: Api.InputChannel, id: [Int32]) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Bool?) { + let buffer = Buffer() + buffer.appendInt32(-357180360) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription({return "(channels.readMessageContents channel: \(channel), id: \(id))"}), buffer, { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } } public struct payments { public static func getPaymentForm(msgId: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.payments.PaymentForm?) { @@ -22370,39 +22661,6 @@ public struct Api { }) } - public static func getContacts(hash: String) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.contacts.Contacts?) { - let buffer = Buffer() - buffer.appendInt32(583445000) - serializeString(hash, buffer: buffer, boxed: false) - return (FunctionDescription({return "(contacts.getContacts hash: \(hash))"}), buffer, { (buffer: Buffer) -> Api.contacts.Contacts? in - let reader = BufferReader(buffer) - var result: Api.contacts.Contacts? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.Contacts - } - return result - }) - } - - public static func importContacts(contacts: [Api.InputContact], replace: Api.Bool) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.contacts.ImportedContacts?) { - let buffer = Buffer() - buffer.appendInt32(-634342611) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(contacts.count)) - for item in contacts { - item.serialize(buffer, true) - } - replace.serialize(buffer, true) - return (FunctionDescription({return "(contacts.importContacts contacts: \(contacts), replace: \(replace))"}), buffer, { (buffer: Buffer) -> Api.contacts.ImportedContacts? in - let reader = BufferReader(buffer) - var result: Api.contacts.ImportedContacts? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.contacts.ImportedContacts - } - return result - }) - } - public static func deleteContact(id: Api.InputUser) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.contacts.Link?) { let buffer = Buffer() buffer.appendInt32(-1902823612) @@ -22570,6 +22828,52 @@ public struct Api { return result }) } + + public static func importContacts(contacts: [Api.InputContact]) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.contacts.ImportedContacts?) { + let buffer = Buffer() + buffer.appendInt32(746589157) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(contacts.count)) + for item in contacts { + item.serialize(buffer, true) + } + return (FunctionDescription({return "(contacts.importContacts contacts: \(contacts))"}), buffer, { (buffer: Buffer) -> Api.contacts.ImportedContacts? in + let reader = BufferReader(buffer) + var result: Api.contacts.ImportedContacts? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.ImportedContacts + } + return result + }) + } + + public static func resetSaved() -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Bool?) { + let buffer = Buffer() + buffer.appendInt32(-2020263951) + + return (FunctionDescription({return "(contacts.resetSaved )"}), buffer, { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + + public static func getContacts(hash: Int32) -> (CustomStringConvertible, Buffer, (Buffer) -> Api.contacts.Contacts?) { + let buffer = Buffer() + buffer.appendInt32(-1071414113) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription({return "(contacts.getContacts hash: \(hash))"}), buffer, { (buffer: Buffer) -> Api.contacts.Contacts? in + let reader = BufferReader(buffer) + var result: Api.contacts.Contacts? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.contacts.Contacts + } + return result + }) + } } public struct help { public static func getConfig() -> (CustomStringConvertible, Buffer, (Buffer) -> Api.Config?) { diff --git a/TelegramCore/CachedChannelData.swift b/TelegramCore/CachedChannelData.swift index be3afa3d23..8c731a9f0b 100644 --- a/TelegramCore/CachedChannelData.swift +++ b/TelegramCore/CachedChannelData.swift @@ -109,6 +109,7 @@ public final class CachedChannelData: CachedPeerData { public let topParticipants: CachedChannelParticipants? public let reportStatus: PeerReportStatus public let pinnedMessageId: MessageId? + public let stickerPack: StickerPackCollectionInfo? public let peerIds: Set @@ -122,9 +123,10 @@ public final class CachedChannelData: CachedPeerData { self.reportStatus = .unknown self.pinnedMessageId = nil self.peerIds = Set() + self.stickerPack = nil } - init(flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], topParticipants: CachedChannelParticipants?, reportStatus: PeerReportStatus, pinnedMessageId: MessageId?) { + init(flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], topParticipants: CachedChannelParticipants?, reportStatus: PeerReportStatus, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?) { self.flags = flags self.about = about self.participantsSummary = participantsSummary @@ -133,6 +135,7 @@ public final class CachedChannelData: CachedPeerData { self.topParticipants = topParticipants self.reportStatus = reportStatus self.pinnedMessageId = pinnedMessageId + self.stickerPack = stickerPack var peerIds = Set() if let topParticipants = topParticipants { @@ -147,35 +150,39 @@ public final class CachedChannelData: CachedPeerData { } func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData { - return CachedChannelData(flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId) + return CachedChannelData(flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack) } func withUpdatedAbout(_ about: String?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId) + return CachedChannelData(flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack) } func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId) + return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack) } func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId) + return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack) } func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId) + return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack) } func withUpdatedTopParticipants(_ topParticipants: CachedChannelParticipants?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId) + return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack) } func withUpdatedReportStatus(_ reportStatus: PeerReportStatus) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: reportStatus, pinnedMessageId: self.pinnedMessageId) + return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack) } func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: pinnedMessageId) + return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack) + } + + func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData { + return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack) } public init(decoder: Decoder) { @@ -193,6 +200,12 @@ public final class CachedChannelData: CachedPeerData { self.pinnedMessageId = nil } + if let stickerPack = decoder.decodeObjectForKey("sp", decoder: { StickerPackCollectionInfo(decoder: $0) }) as? StickerPackCollectionInfo { + self.stickerPack = stickerPack + } else { + self.stickerPack = nil + } + if let topParticipants = self.topParticipants { for participant in topParticipants.participants { peerIds.insert(participant.peerId) @@ -234,6 +247,11 @@ public final class CachedChannelData: CachedPeerData { encoder.encodeNil(forKey: "pm.n") encoder.encodeNil(forKey: "pm.i") } + if let stickerPack = self.stickerPack { + encoder.encodeObject(stickerPack, forKey: "sp") + } else { + encoder.encodeNil(forKey: "sp") + } } public func isEqual(to: CachedPeerData) -> Bool { @@ -273,6 +291,10 @@ public final class CachedChannelData: CachedPeerData { return false } + if other.stickerPack != self.stickerPack { + return false + } + return true } } diff --git a/TelegramCore/ChannelAdminEventLogs.swift b/TelegramCore/ChannelAdminEventLogs.swift index 515b547202..4bb2912543 100644 --- a/TelegramCore/ChannelAdminEventLogs.swift +++ b/TelegramCore/ChannelAdminEventLogs.swift @@ -38,6 +38,7 @@ public enum AdminLogEventAction { case participantInvite(RenderedChannelParticipant) case participantToggleBan(prev: RenderedChannelParticipant, new: RenderedChannelParticipant) case participantToggleAdmin(prev: RenderedChannelParticipant, new: RenderedChannelParticipant) + case changeStickerPack(prev: StickerPackReference?, new: StickerPackReference?) } public enum ChannelAdminLogEventError { @@ -108,79 +109,81 @@ public func channelAdminLogEvents(_ account:Account, peerId:PeerId, maxId:AdminL switch result { case let .adminLogResults(apiEvents, apiChats, apiUsers): - let peers = (apiChats.flatMap {parseTelegramGroupOrChannel(chat: $0)} + apiUsers.flatMap {TelegramUser(user: $0)} + Array(arrayLiteral: peer)).reduce([:], { current, peer -> [PeerId : Peer] in + let peers = (apiChats.flatMap {parseTelegramGroupOrChannel(chat: $0)} + apiUsers.flatMap { TelegramUser(user: $0) } + Array(arrayLiteral: peer)).reduce([:], { current, peer -> [PeerId : Peer] in var current = current current[peer.id] = peer return current }) - var events:[AdminLogEvent] = [] + var events: [AdminLogEvent] = [] for event in apiEvents { switch event { - case let .channelAdminLogEvent(id, date, userId, apiAction): - var action: AdminLogEventAction? - switch apiAction { - case let .channelAdminLogEventActionChangeTitle(prev, new): - action = .changeTitle(prev: prev, new: new) - case let .channelAdminLogEventActionChangeAbout(prev, new): - action = .changeAbout(prev: prev, new: new) - case let .channelAdminLogEventActionChangeUsername(prev, new): - action = .changeUsername(prev: prev, new: new) - case let .channelAdminLogEventActionChangePhoto(prev, new): - action = .changePhoto(prev: imageRepresentationsForApiChatPhoto(prev), new: imageRepresentationsForApiChatPhoto(new)) - case let .channelAdminLogEventActionToggleInvites(new): - action = .toggleInvites(boolFromApiValue(new)) - case let .channelAdminLogEventActionToggleSignatures(new): - action = .toggleSignatures(boolFromApiValue(new)) - case let .channelAdminLogEventActionUpdatePinned(new): - switch new { - case .messageEmpty: - action = .updatePinned(nil) - default: - if let message = StoreMessage(apiMessage: new), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .updatePinned(rendered) - } + case let .channelAdminLogEvent(id, date, userId, apiAction): + var action: AdminLogEventAction? + switch apiAction { + case let .channelAdminLogEventActionChangeTitle(prev, new): + action = .changeTitle(prev: prev, new: new) + case let .channelAdminLogEventActionChangeAbout(prev, new): + action = .changeAbout(prev: prev, new: new) + case let .channelAdminLogEventActionChangeUsername(prev, new): + action = .changeUsername(prev: prev, new: new) + case let .channelAdminLogEventActionChangePhoto(prev, new): + action = .changePhoto(prev: imageRepresentationsForApiChatPhoto(prev), new: imageRepresentationsForApiChatPhoto(new)) + case let .channelAdminLogEventActionToggleInvites(new): + action = .toggleInvites(boolFromApiValue(new)) + case let .channelAdminLogEventActionToggleSignatures(new): + action = .toggleSignatures(boolFromApiValue(new)) + case let .channelAdminLogEventActionUpdatePinned(new): + switch new { + case .messageEmpty: + action = .updatePinned(nil) + default: + if let message = StoreMessage(apiMessage: new), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .updatePinned(rendered) + } + } + + case let .channelAdminLogEventActionEditMessage(prev, new): + if let prev = StoreMessage(apiMessage: prev), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new), let newRendered = locallyRenderedMessage(message: new, peers: peers) { + action = .editMessage(prev: prevRendered, new: newRendered) + } + case let .channelAdminLogEventActionDeleteMessage(message): + if let message = StoreMessage(apiMessage: message), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .deleteMessage(rendered) + } + case .channelAdminLogEventActionParticipantJoin: + action = .participantJoin + case .channelAdminLogEventActionParticipantLeave: + action = .participantLeave + case let .channelAdminLogEventActionParticipantInvite(participant): + let participant = ChannelParticipant(apiParticipant: participant) + + if let peer = peers[participant.peerId] { + action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer)) + } + case let .channelAdminLogEventActionParticipantToggleBan(prev, new): + let prevParticipant = ChannelParticipant(apiParticipant: prev) + let newParticipant = ChannelParticipant(apiParticipant: new) + + if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { + action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) + } + case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new): + let prevParticipant = ChannelParticipant(apiParticipant: prev) + let newParticipant = ChannelParticipant(apiParticipant: new) + + if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { + action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) + } + case let .channelAdminLogEventActionChangeStickerSet(prevStickerset, newStickerset): + action = .changeStickerPack(prev: StickerPackReference(apiInputSet: prevStickerset), new: StickerPackReference(apiInputSet: newStickerset)) } - - case let .channelAdminLogEventActionEditMessage(prev, new): - if let prev = StoreMessage(apiMessage: prev), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new), let newRendered = locallyRenderedMessage(message: new, peers: peers) { - action = .editMessage(prev: prevRendered, new: newRendered) - } - case let .channelAdminLogEventActionDeleteMessage(message): - if let message = StoreMessage(apiMessage: message), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .deleteMessage(rendered) - } - case .channelAdminLogEventActionParticipantJoin: - action = .participantJoin - case .channelAdminLogEventActionParticipantLeave: - action = .participantLeave - case let .channelAdminLogEventActionParticipantInvite(participant): - let participant = ChannelParticipant(apiParticipant: participant) - - if let peer = peers[participant.peerId] { - action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer)) - } - case let .channelAdminLogEventActionParticipantToggleBan(prev, new): - let prevParticipant = ChannelParticipant(apiParticipant: prev) - let newParticipant = ChannelParticipant(apiParticipant: new) - - if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { - action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) - } - case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new): - let prevParticipant = ChannelParticipant(apiParticipant: prev) - let newParticipant = ChannelParticipant(apiParticipant: new) - - if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { - action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + if let action = action { + events.append(AdminLogEvent(id: id, peerId: peerId, date: date, action: action)) } } - let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - if let action = action { - events.append(AdminLogEvent(id: id, peerId: peerId, date: date, action: action)) - } - } } return AdminLogEventsResult(peerId: peerId, peers: peers, events: events) } diff --git a/TelegramCore/CloudFileMediaResource.swift b/TelegramCore/CloudFileMediaResource.swift index 61fd10fef3..17a3deaa8b 100644 --- a/TelegramCore/CloudFileMediaResource.swift +++ b/TelegramCore/CloudFileMediaResource.swift @@ -45,9 +45,9 @@ public struct CloudFileMediaResourceId: MediaResourceId { public class CloudFileMediaResource: TelegramCloudMediaResource, TelegramMultipartFetchableResource { public let datacenterId: Int - let volumeId: Int64 - let localId: Int32 - let secret: Int64 + public let volumeId: Int64 + public let localId: Int32 + public let secret: Int64 public let size: Int? public var id: MediaResourceId { @@ -130,7 +130,7 @@ public struct CloudDocumentMediaResourceId: MediaResourceId { public class CloudDocumentMediaResource: TelegramCloudMediaResource, TelegramMultipartFetchableResource { public let datacenterId: Int let fileId: Int64 - let accessHash: Int64 + public let accessHash: Int64 public let size: Int? public var id: MediaResourceId { diff --git a/TelegramCore/ConsumablePersonalMentionMessageAttribute.swift b/TelegramCore/ConsumablePersonalMentionMessageAttribute.swift new file mode 100644 index 0000000000..fdf1402194 --- /dev/null +++ b/TelegramCore/ConsumablePersonalMentionMessageAttribute.swift @@ -0,0 +1,26 @@ +import Foundation +#if os(macOS) + import PostboxMac +#else + import Postbox +#endif + +public class ConsumablePersonalMentionMessageAttribute: MessageAttribute { + public let consumed: Bool + public let pending: Bool + + public init(consumed: Bool, pending: Bool) { + self.consumed = consumed + self.pending = pending + } + + required public init(decoder: Decoder) { + self.consumed = decoder.decodeInt32ForKey("c", orElse: 0) != 0 + self.pending = decoder.decodeInt32ForKey("p", orElse: 0) != 0 + } + + public func encode(_ encoder: Encoder) { + encoder.encodeInt32(self.consumed ? 1 : 0, forKey: "c") + encoder.encodeInt32(self.pending ? 1 : 0, forKey: "p") + } +} diff --git a/TelegramCore/ConsumePersonalMessageAction.swift b/TelegramCore/ConsumePersonalMessageAction.swift new file mode 100644 index 0000000000..5cf5feb3ef --- /dev/null +++ b/TelegramCore/ConsumePersonalMessageAction.swift @@ -0,0 +1,27 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +final class ConsumePersonalMessageAction: PendingMessageActionData { + init() { + } + + init(decoder: Decoder) { + } + + func encode(_ encoder: Encoder) { + } + + func isEqual(to: PendingMessageActionData) -> Bool { + if let _ = to as? ConsumePersonalMessageAction { + return true + } else { + return false + } + } +} diff --git a/TelegramCore/ContactManagement.swift b/TelegramCore/ContactManagement.swift index fda49bad19..b4339a1a07 100644 --- a/TelegramCore/ContactManagement.swift +++ b/TelegramCore/ContactManagement.swift @@ -19,14 +19,14 @@ private func md5(_ data : Data) -> Data { return res } -private func updatedRemoteContactPeers(network: Network, hash: String) -> Signal<([Peer], [PeerId: PeerPresence])?, NoError> { +private func updatedRemoteContactPeers(network: Network, hash: Int32) -> Signal<([Peer], [PeerId: PeerPresence], Int32)?, NoError> { return network.request(Api.functions.contacts.getContacts(hash: hash)) |> retryRequest - |> map { result -> ([Peer], [PeerId: PeerPresence])? in + |> map { result -> ([Peer], [PeerId: PeerPresence], Int32)? in switch result { case .contactsNotModified: return nil - case let .contacts(_, users): + case let .contacts(_, savedCount, users): var peers: [Peer] = [] var peerPresences: [PeerId: PeerPresence] = [:] for user in users { @@ -36,52 +36,45 @@ private func updatedRemoteContactPeers(network: Network, hash: String) -> Signal peerPresences[telegramUser.id] = presence } } - return (peers, peerPresences) + return (peers, peerPresences, savedCount) } } } +private func hashForCountAndIds(count: Int32, ids: [Int32]) -> Int32 { + var acc: UInt32 = 0 + + acc = (acc &* 20261) &+ UInt32(bitPattern: count) + + for id in ids { + let low = UInt32(bitPattern: id) + acc = (acc &* 20261) &+ low + } + return Int32(bitPattern: acc % UInt32(0x7FFFFFFF)) +} + func manageContacts(network: Network, postbox: Postbox) -> Signal { let initialContactPeerIdsHash = postbox.contactPeerIdsView() |> take(1) - |> map { peerIds -> String in - var stringToHash = "" - var first = true - let sortedUserIds = Set(peerIds.peerIds.filter({ $0.namespace == Namespaces.Peer.CloudUser }).map({ $0.id })).sorted() - for userId in sortedUserIds { - if first { - first = false - } else { - stringToHash.append(",") - } - stringToHash.append("\(userId)") - } + |> map { view -> Int32 in + let sortedUserIds = Set(view.peerIds.filter({ $0.namespace == Namespaces.Peer.CloudUser }).map({ $0.id })).sorted() - let hashData = md5(stringToHash.data(using: .utf8)!) - let hashString = hashData.withUnsafeBytes { (bytes: UnsafePointer) -> String in - let hexString = NSMutableString() - for i in 0 ..< hashData.count { - let byteValue = UInt(bytes.advanced(by: i).pointee) - hexString.appendFormat("%02x", byteValue) - } - return hexString as String - } - - return hashString + return hashForCountAndIds(count: view.remoteTotalCount, ids: sortedUserIds) } let updatedPeers = initialContactPeerIdsHash - |> mapToSignal { hash -> Signal<([Peer], [PeerId: PeerPresence])?, NoError> in + |> mapToSignal { hash -> Signal<([Peer], [PeerId: PeerPresence], Int32)?, NoError> in return updatedRemoteContactPeers(network: network, hash: hash) } let appliedUpdatedPeers = updatedPeers |> mapToSignal { peersAndPresences -> Signal in - if let (peers, peerPresences) = peersAndPresences { + if let (peers, peerPresences, totalCount) = peersAndPresences { return postbox.modify { modifier in updatePeers(modifier: modifier, peers: peers, update: { return $1 }) modifier.updatePeerPresences(peerPresences) modifier.replaceContactPeerIds(Set(peers.map { $0.id })) + modifier.replaceRemoteContactCount(totalCount) } } else { return .complete() @@ -94,7 +87,7 @@ func manageContacts(network: Network, postbox: Postbox) -> Signal public func addContactPeerInteractively(account: Account, peerId: PeerId) -> Signal { return account.postbox.modify { modifier -> Signal in if let peer = modifier.getPeer(peerId) as? TelegramUser, let phone = peer.phone, !phone.isEmpty { - return account.network.request(Api.functions.contacts.importContacts(contacts: [Api.InputContact.inputPhoneContact(clientId: 1, phone: phone, firstName: peer.firstName ?? "", lastName: peer.lastName ?? "")], replace: .boolFalse)) + return account.network.request(Api.functions.contacts.importContacts(contacts: [Api.InputContact.inputPhoneContact(clientId: 1, phone: phone, firstName: peer.firstName ?? "", lastName: peer.lastName ?? "")])) |> map { Optional($0) } |> `catch` { _ -> Signal in return .single(nil) diff --git a/TelegramCore/EarliestUnseenPersonalMentionMessage.swift b/TelegramCore/EarliestUnseenPersonalMentionMessage.swift new file mode 100644 index 0000000000..f7394725d4 --- /dev/null +++ b/TelegramCore/EarliestUnseenPersonalMentionMessage.swift @@ -0,0 +1,37 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac + import MtProtoKitMac +#else + import Postbox + import SwiftSignalKit + import MtProtoKitDynamic +#endif + +public func earliestUnseenPersonalMentionMessage(postbox: Postbox, peerId: PeerId) -> Signal { + return postbox.modify { modifier -> Signal in + var resultMessageId: MessageId? + modifier.scanMessages(peerId: peerId, tagMask: .unseenPersonalMessage, { entry in + switch entry { + case let .message(message): + for attribute in message.attributes { + if let attribute = attribute as? ConsumablePersonalMentionMessageAttribute, !attribute.pending { + resultMessageId = message.id + return false + } + } + break + case let .hole(hole): + break + } + return true + }) + + if let resultMessageId = resultMessageId { + return .single(resultMessageId) + } + + return .single(nil) + } |> switchToLatest +} diff --git a/TelegramCore/EnqueueMessage.swift b/TelegramCore/EnqueueMessage.swift index 7e66e23a25..8a1e63e7a6 100644 --- a/TelegramCore/EnqueueMessage.swift +++ b/TelegramCore/EnqueueMessage.swift @@ -36,6 +36,8 @@ private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAtt return true case _ as OutgoingChatContextResultMessageAttribute: return true + case _ as AutoremoveTimeoutMessageAttribute: + return true default: return false } diff --git a/TelegramCore/HistoryViewChannelStateValidation.swift b/TelegramCore/HistoryViewChannelStateValidation.swift index 9e9664541f..e334113d49 100644 --- a/TelegramCore/HistoryViewChannelStateValidation.swift +++ b/TelegramCore/HistoryViewChannelStateValidation.swift @@ -216,7 +216,7 @@ private func validateBatch(postbox: Postbox, network: Network, messageIds: [Mess } case let .channelDifferenceEmpty(_, pts, _): finalPts = pts - case let .channelDifferenceTooLong(_, pts, _, _, _, _, _, _, _, _): + case let .channelDifferenceTooLong(_, pts, _, _, _, _, _, _, _, _, _): finalPts = pts } diff --git a/TelegramCore/Holes.swift b/TelegramCore/Holes.swift index 80f8c54599..35f633d0f2 100644 --- a/TelegramCore/Holes.swift +++ b/TelegramCore/Holes.swift @@ -10,15 +10,15 @@ import Foundation #endif private func messageFilterForTagMask(_ tagMask: MessageTags) -> Api.MessagesFilter? { - if tagMask == .PhotoOrVideo { + if tagMask == .photoOrVideo { return Api.MessagesFilter.inputMessagesFilterPhotoVideo - } else if tagMask == .File { + } else if tagMask == .file { return Api.MessagesFilter.inputMessagesFilterDocument - } else if tagMask == .Music { + } else if tagMask == .music { return Api.MessagesFilter.inputMessagesFilterMusic - } else if tagMask == .WebPage { + } else if tagMask == .webPage { return Api.MessagesFilter.inputMessagesFilterUrl - } else if tagMask == .VoiceOrInstantVideo { + } else if tagMask == .voiceOrInstantVideo { return Api.MessagesFilter.inputMessagesFilterRoundVoice } else { return nil @@ -35,16 +35,58 @@ func fetchMessageHistoryHole(network: Network, postbox: Postbox, hole: MessageHi let request: Signal if let tagMask = tagMask, let filter = messageFilterForTagMask(tagMask) { - switch direction { - case .UpperToLower: - break - case .LowerToUpper: - assertionFailure(".LowerToUpper not supported") - case .AroundIndex: - assertionFailure(".AroundIndex not supported") + if tagMask == MessageTags.unseenPersonalMessage { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = limit + let maxId: Int32 + let minId: Int32 + + switch direction { + case .UpperToLower: + offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + addOffset = 0 + maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + minId = 1 + case .LowerToUpper: + offsetId = hole.min <= 1 ? 1 : (hole.min - 1) + addOffset = Int32(-limit) + maxId = Int32.max + minId = hole.min - 1 + case let .AroundIndex(index): + offsetId = index.id.id + addOffset = Int32(-limit / 2) + maxId = Int32.max + minId = 1 + } + request = network.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) + } else { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = limit + let maxId: Int32 + let minId: Int32 + + switch direction { + case .UpperToLower: + offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + addOffset = 0 + maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + minId = 1 + case .LowerToUpper: + offsetId = hole.min <= 1 ? 1 : (hole.min - 1) + addOffset = Int32(-limit) + maxId = Int32.max + minId = hole.min - 1 + case let .AroundIndex(index): + offsetId = index.id.id + addOffset = Int32(-limit / 2) + maxId = Int32.max + minId = 1 + } + + request = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) } - //request = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offset: 0, maxId: hole.maxIndex.id.id + 1, limit: Int32(limit))) - request = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offset: 0, maxId: Int32.max, limit: Int32(limit))) } else { let offsetId: Int32 let addOffset: Int32 @@ -70,7 +112,6 @@ func fetchMessageHistoryHole(network: Network, postbox: Postbox, hole: MessageHi minId = 1 } - //request = network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1), minId: hole.min - 1)) request = network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) } @@ -183,6 +224,7 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - var replacementHole: ChatListHole? var storeMessages: [StoreMessage] = [] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] + var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] var chatStates: [PeerId: PeerChatState] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:] @@ -197,15 +239,17 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - let apiReadOutboxMaxId: Int32 let apiTopMessage: Int32 let apiUnreadCount: Int32 + let apiUnreadMentionsCount: Int32 var apiChannelPts: Int32? let apiNotificationSettings: Api.PeerNotifySettings switch dialog { - case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, peerNotificationSettings, pts, _): + case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _): apiPeer = peer apiTopMessage = topMessage apiReadInboxMaxId = readInboxMaxId apiReadOutboxMaxId = readOutboxMaxId apiUnreadCount = unreadCount + apiUnreadMentionsCount = unreadMentionsCount apiNotificationSettings = peerNotificationSettings apiChannelPts = pts } @@ -225,6 +269,10 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - } readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount) + if apiUnreadMentionsCount != 0 && apiTopMessage != 0 { + mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)) + } + if let apiChannelPts = apiChannelPts { chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: nil) } @@ -253,16 +301,18 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - let apiReadInboxMaxId: Int32 let apiReadOutboxMaxId: Int32 let apiUnreadCount: Int32 + let apiUnreadMentionsCount: Int32 let apiNotificationSettings: Api.PeerNotifySettings let isPinned: Bool switch dialog { - case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, peerNotificationSettings, _, _): + case let .dialog(flags, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, _, _): isPinned = (flags & (1 << 2)) != 0 apiPeer = peer apiTopMessage = topMessage apiReadInboxMaxId = readInboxMaxId apiReadOutboxMaxId = readOutboxMaxId apiUnreadCount = unreadCount + apiUnreadMentionsCount = unreadMentionsCount apiNotificationSettings = peerNotificationSettings } @@ -281,6 +331,10 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - } readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount) + if apiUnreadMentionsCount != 0 && apiTopMessage != 0 { + mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)) + } + notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) let topMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: apiTopMessage) @@ -317,15 +371,17 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - let apiReadOutboxMaxId: Int32 let apiTopMessage: Int32 let apiUnreadCount: Int32 + let apiUnreadMentionsCount: Int32 var apiChannelPts: Int32? let apiNotificationSettings: Api.PeerNotifySettings switch dialog { - case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, peerNotificationSettings, pts, _): + case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _): apiPeer = peer apiTopMessage = topMessage apiReadInboxMaxId = readInboxMaxId apiReadOutboxMaxId = readOutboxMaxId apiUnreadCount = unreadCount + apiUnreadMentionsCount = unreadMentionsCount apiNotificationSettings = peerNotificationSettings apiChannelPts = pts } @@ -347,6 +403,10 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - } readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount) + if apiUnreadMentionsCount != 0 && apiTopMessage != 0 { + mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)) + } + if let apiChannelPts = apiChannelPts { chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: nil) } @@ -413,6 +473,10 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) - if let replacePinnedPeerIds = replacePinnedPeerIds { modifier.setPinnedPeerIds(replacePinnedPeerIds) } + + for (peerId, summary) in mentionTagSummaries { + modifier.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: summary.count, maxId: summary.range.maxId) + } } } } @@ -431,7 +495,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, holeIndex: MessageInd } return offset |> mapToSignal { (timestamp, id, peer) -> Signal in - let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: peer, q: "", fromId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offset: 0, maxId: holeIndex.id.id, limit: limit)) + let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: peer, q: "", fromId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0)) |> retryRequest |> mapToSignal { result -> Signal in let messages: [Api.Message] diff --git a/TelegramCore/ImportContact.swift b/TelegramCore/ImportContact.swift index 7032238e0f..33e3407480 100644 --- a/TelegramCore/ImportContact.swift +++ b/TelegramCore/ImportContact.swift @@ -10,7 +10,7 @@ public func importContact(account:Account, firstName:String, lastName:String, ph let input = Api.InputContact.inputPhoneContact(clientId: 1, phone: phoneNumber, firstName: firstName, lastName: lastName) - return account.network.request(Api.functions.contacts.importContacts(contacts: [input], replace: .boolFalse)) + return account.network.request(Api.functions.contacts.importContacts(contacts: [input])) |> map { Optional($0) } |> `catch` { _ -> Signal in return .single(nil) diff --git a/TelegramCore/InstallInteractiveReadMessagesAction.swift b/TelegramCore/InstallInteractiveReadMessagesAction.swift new file mode 100644 index 0000000000..780ef7ec01 --- /dev/null +++ b/TelegramCore/InstallInteractiveReadMessagesAction.swift @@ -0,0 +1,42 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +public func installInteractiveReadMessagesAction(postbox: Postbox, peerId: PeerId) -> Disposable { + return postbox.installStoreMessageAction(peerId: peerId, { messages, modifier in + var consumeMessageIds: [MessageId] = [] + + for message in messages { + if case let .Id(id) = message.id { + 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 + } + } + } + } + } + + for id in consumeMessageIds { + modifier.updateMessage(id, update: { currentMessage in + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute { + attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: attribute.consumed, pending: true) + break loop + } + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + + modifier.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: ConsumePersonalMessageAction()) + } + }) +} diff --git a/TelegramCore/LoadMessagesIfNecessary.swift b/TelegramCore/LoadMessagesIfNecessary.swift index 2c856c3726..07e98f2bfd 100644 --- a/TelegramCore/LoadMessagesIfNecessary.swift +++ b/TelegramCore/LoadMessagesIfNecessary.swift @@ -9,7 +9,6 @@ import Foundation import MtProtoKitDynamic #endif - public enum GetMessagesStrategy { case local case cloud diff --git a/TelegramCore/LoadedStickerPack.swift b/TelegramCore/LoadedStickerPack.swift index a31f49efce..598019808f 100644 --- a/TelegramCore/LoadedStickerPack.swift +++ b/TelegramCore/LoadedStickerPack.swift @@ -84,17 +84,16 @@ public func loadedStickerPack(account: Account, reference: StickerPackReference) 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) + 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 } } diff --git a/TelegramCore/Localizations.swift b/TelegramCore/Localizations.swift index 9a5808c521..a4485c3cc4 100644 --- a/TelegramCore/Localizations.swift +++ b/TelegramCore/Localizations.swift @@ -12,7 +12,8 @@ public func currentlySuggestedLocalization(network: Network, extractKeys: [Strin |> retryRequest |> mapToSignal { result -> Signal in switch result { - case let .config(flags, date, expires, testMode, thisDc, dcOptions, chatSizeMax, megagroupSizeMax, forwardedCountMax, onlineUpdatePeriodMs, offlineBlurTimeoutMs, offlineIdleTimeoutMs, onlineCloudTimeoutMs, notifyCloudDelayMs, notifyDefaultDelayMs, chatBigSize, pushChatPeriodMs, pushChatLimit, savedGifsLimit, editTimeLimit, ratingEDecay, stickersRecentLimit, tmpSessions, pinnedDialogsCountMax, callReceiveTimeoutMs, callRingTimeoutMs, callConnectTimeoutMs, callPacketTimeoutMs, meUrlPrefix, suggestedLangCode, _, disabledFeatures): + //config flags:# date:int expires:int test_mode:Bool this_dc:int dc_options:Vector chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int phonecalls_enabled:flags.1?true call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int disabled_features:Vector = Config; + case let .config(flags, _, _, _, _, _, chatSizeMax, megagroupSizeMax, forwardedCountMax, onlineUpdatePeriodMs, offlineBlurTimeoutMs, offlineIdleTimeoutMs, onlineCloudTimeoutMs, notifyCloudDelayMs, notifyDefaultDelayMs, chatBigSize, pushChatPeriodMs, pushChatLimit, savedGifsLimit, editTimeLimit, ratingEDecay, stickersRecentLimit, stickersFavedLimit, _, pinnedDialogsCountMax, callReceiveTimeoutMs, callRingTimeoutMs, callConnectTimeoutMs, callPacketTimeoutMs, meUrlPrefix, suggestedLangCode, _, disabledFeatures): if let suggestedLangCode = suggestedLangCode { return suggestedLocalizationInfo(network: network, languageCode: suggestedLangCode, extractKeys: extractKeys) |> map { Optional($0) } } else { diff --git a/TelegramCore/ManagedConfigurationUpdates.swift b/TelegramCore/ManagedConfigurationUpdates.swift index a933a1b615..e0f76baee0 100644 --- a/TelegramCore/ManagedConfigurationUpdates.swift +++ b/TelegramCore/ManagedConfigurationUpdates.swift @@ -14,7 +14,7 @@ func managedConfigurationUpdates(postbox: Postbox, network: Network) -> Signal retryRequest |> mapToSignal { result -> Signal in return postbox.modify { modifier -> Void in switch result { - case let .config(_, _, _, _, _, dcOptions, chatSizeMax, megagroupSizeMax, forwardedCountMax, _, _, _, onlineCloudTimeoutMs, notifyCloudDelayMs, notifyDefaultDelayMs, chatBigSize, pushChatPeriodMs, pushChatLimit, savedGifsLimit, editTimeLimit, ratingEDecay, stickersRecentLimit, tmpSessions, pinnedDialogsCountMax, callReceiveTimeoutMs, callRingTimeoutMs, callConnectTimeoutMs, callPacketTimeoutMs, meUrlPrefix, suggestedLangCode, langPackVersion, disabledFeatures): + case let .config(_, _, _, _, _, dcOptions, chatSizeMax, megagroupSizeMax, forwardedCountMax, _, _, _, onlineCloudTimeoutMs, notifyCloudDelayMs, notifyDefaultDelayMs, chatBigSize, pushChatPeriodMs, pushChatLimit, savedGifsLimit, editTimeLimit, ratingEDecay, stickersRecentLimit, stickersFavedLimit, tmpSessions, pinnedDialogsCountMax, callReceiveTimeoutMs, callRingTimeoutMs, callConnectTimeoutMs, callPacketTimeoutMs, meUrlPrefix, suggestedLangCode, langPackVersion, disabledFeatures): var addressList: [Int: [MTDatacenterAddress]] = [:] for option in dcOptions { switch option { diff --git a/TelegramCore/ManagedConsumePersonalMessagesActions.swift b/TelegramCore/ManagedConsumePersonalMessagesActions.swift new file mode 100644 index 0000000000..d07c2a3b05 --- /dev/null +++ b/TelegramCore/ManagedConsumePersonalMessagesActions.swift @@ -0,0 +1,185 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac + import MtProtoKitMac +#else + import Postbox + import SwiftSignalKit + import MtProtoKitDynamic +#endif + +private final class ManagedConsumePersonalMessagesActionsHelper { + var operationDisposables: [MessageId: Disposable] = [:] + + func update(_ entries: [PendingMessageActionsEntry]) -> (disposeOperations: [Disposable], beginOperations: [(PendingMessageActionsEntry, MetaDisposable)]) { + var disposeOperations: [Disposable] = [] + var beginOperations: [(PendingMessageActionsEntry, MetaDisposable)] = [] + + var hasRunningOperationForPeerId = Set() + var validIds = Set() + for entry in entries { + if !hasRunningOperationForPeerId.contains(entry.id.peerId) { + hasRunningOperationForPeerId.insert(entry.id.peerId) + validIds.insert(entry.id) + + if self.operationDisposables[entry.id] == nil { + let disposable = MetaDisposable() + beginOperations.append((entry, disposable)) + self.operationDisposables[entry.id] = disposable + } + } + } + + var removeMergedIds: [MessageId] = [] + for (id, disposable) in self.operationDisposables { + if !validIds.contains(id) { + removeMergedIds.append(id) + disposeOperations.append(disposable) + } + } + + for id in removeMergedIds { + self.operationDisposables.removeValue(forKey: id) + } + + return (disposeOperations, beginOperations) + } + + func reset() -> [Disposable] { + let disposables = Array(self.operationDisposables.values) + self.operationDisposables.removeAll() + return disposables + } +} + +private func withTakenAction(postbox: Postbox, type: PendingMessageActionType, id: MessageId, _ f: @escaping (Modifier, PendingMessageActionsEntry?) -> Signal) -> Signal { + return postbox.modify { modifier -> Signal in + var result: PendingMessageActionsEntry? + + if let action = modifier.getPendingMessageAction(type: type, id: id) as? ConsumePersonalMessageAction { + result = PendingMessageActionsEntry(id: id, action: action) + } + + return f(modifier, result) + } |> switchToLatest +} + + +func managedConsumePersonalMessagesActions(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal { + return Signal { _ in + let helper = Atomic(value: ManagedConsumePersonalMessagesActionsHelper()) + + let key = PostboxViewKey.pendingMessageActions(type: .consumeUnseenPersonalMessage) + let disposable = postbox.combinedView(keys: [key]).start(next: { view in + var entries: [PendingMessageActionsEntry] = [] + if let v = view.views[key] as? PendingMessageActionsView { + entries = v.entries + } + + let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PendingMessageActionsEntry, MetaDisposable)]) in + return helper.update(entries) + } + + for disposable in disposeOperations { + disposable.dispose() + } + + for (entry, disposable) in beginOperations { + let signal = withTakenAction(postbox: postbox, type: .consumeUnseenPersonalMessage, id: entry.id, { modifier, entry -> Signal in + if let entry = entry { + if let _ = entry.action as? ConsumePersonalMessageAction { + return synchronizeConsumeMessageContents(modifier: modifier, postbox: postbox, network: network, stateManager: stateManager, id: entry.id) + } else { + assertionFailure() + } + } + return .complete() + }) + |> then(postbox.modify { modifier -> Void in + modifier.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: entry.id, action: nil) + }) + + 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 func synchronizeConsumeMessageContents(modifier: Modifier, postbox: Postbox, network: Network, stateManager: AccountStateManager, id: MessageId) -> Signal { + if id.peerId.namespace == Namespaces.Peer.CloudUser || id.peerId.namespace == Namespaces.Peer.CloudGroup { + return network.request(Api.functions.messages.readMessageContents(id: [id.id])) + |> map { Optional($0) } + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + if let result = result { + switch result { + case let .affectedMessages(pts, ptsCount): + stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + } + } + return postbox.modify { modifier -> Void in + modifier.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: nil) + 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, authorSignature: forwardInfo.authorSignature) + } + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute, !attribute.consumed { + attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: true, pending: false) + break loop + } + } + var updatedTags = currentMessage.tags + updatedTags.remove(.unseenPersonalMessage) + 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: attributes, media: currentMessage.media)) + }) + } + } + } else if id.peerId.namespace == Namespaces.Peer.CloudChannel { + if let peer = modifier.getPeer(id.peerId), let inputChannel = apiInputChannel(peer) { + return network.request(Api.functions.channels.readMessageContents(channel: inputChannel, id: [id.id])) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } |> mapToSignal { result -> Signal in + return postbox.modify { modifier -> Void in + modifier.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: nil) + 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, authorSignature: forwardInfo.authorSignature) + } + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute, !attribute.consumed { + attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: true, pending: false) + break loop + } + } + var updatedTags = currentMessage.tags + updatedTags.remove(.unseenPersonalMessage) + 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: attributes, media: currentMessage.media)) + }) + } + } + } else { + return .complete() + } + } else { + return .complete() + } +} diff --git a/TelegramCore/ManagedDeviceContacts.swift b/TelegramCore/ManagedDeviceContacts.swift index fb5715816a..e4b418b7d1 100644 --- a/TelegramCore/ManagedDeviceContacts.swift +++ b/TelegramCore/ManagedDeviceContacts.swift @@ -234,7 +234,7 @@ private func applyAddedOrUpdatedContacts(network: Network, contacts: [ManagedDev apiContacts.append(.inputPhoneContact(clientId: nextId, phone: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName)) nextId += 1 } - return network.request(Api.functions.contacts.importContacts(contacts: apiContacts, replace: .boolFalse)) + return network.request(Api.functions.contacts.importContacts(contacts: apiContacts)) |> `catch` { _ -> Signal in return .fail(.generic) } diff --git a/TelegramCore/ManagedRecentStickers.swift b/TelegramCore/ManagedRecentStickers.swift index ff1830312d..5eca4563e2 100644 --- a/TelegramCore/ManagedRecentStickers.swift +++ b/TelegramCore/ManagedRecentStickers.swift @@ -7,7 +7,7 @@ import Foundation import SwiftSignalKit #endif -private func hashForIdsReverse(_ ids: [Int64]) -> Int32 { +private func hashForIds(_ ids: [Int64]) -> Int32 { var acc: UInt32 = 0 for id in ids { @@ -20,12 +20,15 @@ private func hashForIdsReverse(_ ids: [Int64]) -> Int32 { return Int32(bitPattern: acc % UInt32(0x7FFFFFFF)) } -private func managedRecentMedia(postbox: Postbox, network: Network, collectionId: Int32, fetch: @escaping (Int32) -> Signal<[OrderedItemListEntry]?, NoError>) -> Signal { +private func managedRecentMedia(postbox: Postbox, network: Network, collectionId: Int32, reverseHashOrder: Bool, fetch: @escaping (Int32) -> Signal<[OrderedItemListEntry]?, NoError>) -> Signal { return postbox.modify { modifier -> Signal in - let itemIds = modifier.getOrderedListItemIds(collectionId: collectionId).map { + var itemIds = modifier.getOrderedListItemIds(collectionId: collectionId).map { RecentMediaItemId($0).mediaId.id } - return fetch(hashForIdsReverse(itemIds)) + if reverseHashOrder { + itemIds.reverse() + } + return fetch(hashForIds(itemIds)) |> mapToSignal { items in if let items = items { return postbox.modify { modifier -> Void in @@ -39,7 +42,7 @@ private func managedRecentMedia(postbox: Postbox, network: Network, collectionId } func managedRecentStickers(postbox: Postbox, network: Network) -> Signal { - return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, fetch: { hash in + return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, reverseHashOrder: false, fetch: { hash in return network.request(Api.functions.messages.getRecentStickers(flags: 0, hash: hash)) |> retryRequest |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in @@ -60,7 +63,7 @@ func managedRecentStickers(postbox: Postbox, network: Network) -> Signal Signal { - return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentGifs, fetch: { hash in + return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentGifs, reverseHashOrder: false, fetch: { hash in return network.request(Api.functions.messages.getSavedGifs(hash: hash)) |> retryRequest |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in @@ -79,3 +82,43 @@ func managedRecentGifs(postbox: Postbox, network: Network) -> Signal Signal { + return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudSavedStickers, reverseHashOrder: true, fetch: { hash in + return network.request(Api.functions.messages.getFavedStickers(hash: hash)) + |> retryRequest + |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in + switch result { + case .favedStickersNotModified: + return .single(nil) + case let .favedStickers(_, packs, stickers): + var fileStringRepresentations: [MediaId: [String]] = [:] + for pack in packs { + switch pack { + case let .stickerPack(text, fileIds): + for fileId in fileIds { + let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) + if fileStringRepresentations[mediaId] == nil { + fileStringRepresentations[mediaId] = [text] + } else { + fileStringRepresentations[mediaId]!.append(text) + } + } + } + } + + var items: [OrderedItemListEntry] = [] + for sticker in stickers { + if let file = telegramMediaFileFromApiDocument(sticker), let id = file.id { + var stringRepresentations: [String] = [] + if let representations = fileStringRepresentations[id] { + stringRepresentations = representations + } + items.append(OrderedItemListEntry(id: RecentMediaItemId(id).rawValue, contents: SavedStickerItem(file: file, stringRepresentations: stringRepresentations))) + } + } + return .single(items) + } + } + }) +} diff --git a/TelegramCore/ManagedSecretChatOutgoingOperations.swift b/TelegramCore/ManagedSecretChatOutgoingOperations.swift index 22b7f4ccfe..8defa85128 100644 --- a/TelegramCore/ManagedSecretChatOutgoingOperations.swift +++ b/TelegramCore/ManagedSecretChatOutgoingOperations.swift @@ -417,7 +417,7 @@ private func decryptedAttributes46(_ attributes: [TelegramMediaFileAttribute]) - result.append(.documentAttributeFilename(fileName: fileName)) case .Animated: result.append(.documentAttributeAnimated) - case let .Sticker(displayText, packReference): + case let .Sticker(displayText, packReference, _): var stickerSet: SecretApi46.InputStickerSet = .inputStickerSetEmpty if let packReference = packReference, case let .name(name) = packReference { stickerSet = .inputStickerSetShortName(shortName: name) diff --git a/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift b/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift index c8b28bc06d..0ec84c9e51 100644 --- a/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift +++ b/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift @@ -150,7 +150,7 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ var apiChannelPts: Int32? let apiNotificationSettings: Api.PeerNotifySettings switch dialog { - case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, peerNotificationSettings, pts, _): + case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _): apiPeer = peer apiTopMessage = topMessage apiReadInboxMaxId = readInboxMaxId diff --git a/TelegramCore/ManagedSynchronizeSavedStickersOperations.swift b/TelegramCore/ManagedSynchronizeSavedStickersOperations.swift new file mode 100644 index 0000000000..b15a810a2d --- /dev/null +++ b/TelegramCore/ManagedSynchronizeSavedStickersOperations.swift @@ -0,0 +1,139 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac + import MtProtoKitMac +#else + import Postbox + import SwiftSignalKit + import MtProtoKitDynamic +#endif + +private final class ManagedSynchronizeSavedStickersOperationsHelper { + 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 (Modifier, PeerMergedOperationLogEntry?) -> Signal) -> Signal { + return postbox.modify { modifier -> Signal in + var result: PeerMergedOperationLogEntry? + modifier.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in + if let entry = entry, let _ = entry.mergedIndex, entry.contents is SynchronizeSavedStickersOperation { + result = entry.mergedEntry! + return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none) + } else { + return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none) + } + }) + + return f(modifier, result) + } |> switchToLatest +} + +func managedSynchronizeSavedStickersOperations(postbox: Postbox, network: Network) -> Signal { + return Signal { _ in + let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedStickers + + let helper = Atomic(value: ManagedSynchronizeSavedStickersOperationsHelper()) + + 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, { modifier, entry -> Signal in + if let entry = entry { + if let operation = entry.contents as? SynchronizeSavedStickersOperation { + return synchronizeSavedStickers(modifier: modifier, postbox: postbox, network: network, operation: operation) + } else { + assertionFailure() + } + } + return .complete() + }) + |> then(postbox.modify { modifier -> Void in + let _ = modifier.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 func synchronizeSavedStickers(modifier: Modifier, postbox: Postbox, network: Network, operation: SynchronizeSavedStickersOperation) -> Signal { + switch operation.content { + case let .add(id, accessHash): + return network.request(Api.functions.messages.faveSticker(id: .inputDocument(id: id, accessHash: accessHash), unfave: .boolFalse)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } + case let .remove(id, accessHash): + return network.request(Api.functions.messages.faveSticker(id: .inputDocument(id: id, accessHash: accessHash), unfave: .boolTrue)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } + case .sync: + return managedSavedStickers(postbox: postbox, network: network) + } +} diff --git a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift index fbeff257bc..c534e03788 100644 --- a/TelegramCore/MarkMessageContentAsConsumedInteractively.swift +++ b/TelegramCore/MarkMessageContentAsConsumedInteractively.swift @@ -66,7 +66,7 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI 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: currentMessage.media)) + 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: currentMessage.media)) }) } } diff --git a/TelegramCore/Namespaces.swift b/TelegramCore/Namespaces.swift index ebca673959..f93cd963d5 100644 --- a/TelegramCore/Namespaces.swift +++ b/TelegramCore/Namespaces.swift @@ -49,6 +49,7 @@ public struct Namespaces { public static let CloudFeaturedStickerPacks: Int32 = 4 public static let CloudArchivedStickerPacks: Int32 = 5 public static let CloudWallpapers: Int32 = 6 + public static let CloudSavedStickers: Int32 = 7 } struct CachedItemCollection { @@ -66,13 +67,14 @@ public struct Namespaces { } public extension MessageTags { - static let PhotoOrVideo = MessageTags(rawValue: 1 << 0) - static let File = MessageTags(rawValue: 1 << 1) - static let Music = MessageTags(rawValue: 1 << 2) - static let WebPage = MessageTags(rawValue: 1 << 3) - static let VoiceOrInstantVideo = MessageTags(rawValue: 1 << 4) + static let photoOrVideo = MessageTags(rawValue: 1 << 0) + static let file = MessageTags(rawValue: 1 << 1) + static let music = MessageTags(rawValue: 1 << 2) + static let webPage = MessageTags(rawValue: 1 << 3) + static let voiceOrInstantVideo = MessageTags(rawValue: 1 << 4) + static let unseenPersonalMessage = MessageTags(rawValue: 1 << 5) - static let all: MessageTags = [.PhotoOrVideo, .File, .Music, .WebPage, .VoiceOrInstantVideo] + static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage] } public extension GlobalMessageTags { @@ -82,6 +84,10 @@ public extension GlobalMessageTags { static let all: GlobalMessageTags = [.Calls, .MissedCalls] } +public extension PendingMessageActionType { + static let consumeUnseenPersonalMessage = PendingMessageActionType(rawValue: 0) +} + let peerIdNamespacesWithInitialCloudMessageHoles = [Namespaces.Peer.CloudUser, Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel] struct OperationLogTags { @@ -99,6 +105,7 @@ struct OperationLogTags { static let SynchronizeChatInputStates = PeerOperationLogTag(value: 11) static let SynchronizeSavedGifs = PeerOperationLogTag(value: 12) static let SynchronizeLocalizationUpdates = PeerOperationLogTag(value: 13) + static let SynchronizeSavedStickers = PeerOperationLogTag(value: 14) } private enum PreferencesKeyValues: Int32 { diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 9c38872445..f5b6812b4c 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -35,9 +35,12 @@ func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoin func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> PendingMessageUploadContent { var contextResult: OutgoingChatContextResultMessageAttribute? + var autoremoveAttribute: AutoremoveTimeoutMessageAttribute? for attribute in attributes { if let attribute = attribute as? OutgoingChatContextResultMessageAttribute { contextResult = attribute + } else if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { + autoremoveAttribute = attribute } } @@ -58,7 +61,7 @@ func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoin return .ready(.chatContextResult(contextResult)) } else if let media = media.first { if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) { - return .upload(uploadedMediaImageContent(network: network, postbox: postbox, peerId: peerId, image: image, text: text)) + return .upload(uploadedMediaImageContent(network: network, postbox: postbox, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute)) } else if let file = media as? TelegramMediaFile { if let resource = file.resource as? CloudDocumentMediaResource { return .ready(.media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text))) @@ -84,7 +87,7 @@ func messageContentToUpload(network: Network, postbox: Postbox, transformOutgoin } } -private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerId: PeerId, image: TelegramMediaImage, text: String) -> Signal { +private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerId: PeerId, image: TelegramMediaImage, text: String, autoremoveAttribute: AutoremoveTimeoutMessageAttribute?) -> Signal { if let largestRepresentation = largestImageRepresentation(image.representations) { return multipartUpload(network: network, postbox: postbox, source: .resource(largestRepresentation.resource), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image)) |> mapError { _ -> PendingMessageUploadError in return .generic } @@ -93,7 +96,13 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI case let .progress(progress): return .progress(progress) case let .inputFile(file): - return .content(.media(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: file, caption: text, stickers: nil, ttlSeconds: nil))) + var flags: Int32 = 0 + var ttlSeconds: Int32? + if let autoremoveAttribute = autoremoveAttribute { + flags |= 1 << 1 + ttlSeconds = autoremoveAttribute.timeout + } + return .content(.media(Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, caption: text, stickers: nil, ttlSeconds: ttlSeconds))) case let .inputSecretFile(file, size, key): return .content(.secretMedia(file, size, key)) } @@ -113,9 +122,9 @@ func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaF attributes.append(.documentAttributeFilename(fileName: fileName)) case let .ImageSize(size): attributes.append(.documentAttributeImageSize(w: Int32(size.width), h: Int32(size.height))) - case let .Sticker(displayText, packReference): + case let .Sticker(displayText, packReference, maskCoords): var stickerSet: Api.InputStickerSet = .inputStickerSetEmpty - let flags: Int32 = 0 + var flags: Int32 = 0 if let packReference = packReference { switch packReference { case let .id(id, accessHash): @@ -124,7 +133,12 @@ func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaF stickerSet = .inputStickerSetShortName(shortName: name) } } - attributes.append(.documentAttributeSticker(flags: flags, alt: displayText, stickerset: stickerSet, maskCoords: nil)) + var inputMaskCoords: Api.MaskCoords? + if let maskCoords = maskCoords { + flags |= 1 << 0 + inputMaskCoords = .maskCoords(n: maskCoords.n, x: maskCoords.x, y: maskCoords.y, zoom: maskCoords.zoom) + } + attributes.append(.documentAttributeSticker(flags: flags, alt: displayText, stickerset: stickerSet, maskCoords: inputMaskCoords)) case .HasLinkedStickers: attributes.append(.documentAttributeHasStickers) case let .Video(duration, size, videoFlags): @@ -272,11 +286,22 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, transf case let .inputFile(inputFile): if case let .done(thumbnail) = media { let inputMedia: Api.InputMedia - if let thumbnail = thumbnail { - inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: (1 << 2), file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil, ttlSeconds: nil) - } else { - inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil, ttlSeconds: nil) + var flags: Int32 = 0 + + if let _ = thumbnail { + flags |= 1 << 2 } + + var ttlSeconds: Int32? + for attribute in attributes { + if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { + flags |= 1 << 1 + ttlSeconds = attribute.timeout + } + } + + inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil, ttlSeconds: ttlSeconds) + return .single(.content(.media(inputMedia))) } else { return .complete() diff --git a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift index c1d7ce9341..1e3b7509a5 100644 --- a/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/TelegramCore/ProcessSecretChatIncomingDecryptedOperations.swift @@ -428,7 +428,7 @@ extension TelegramMediaFileAttribute { case let .inputStickerSetShortName(shortName): packReference = .name(shortName) } - self = .Sticker(displayText: alt, packReference: packReference) + self = .Sticker(displayText: alt, packReference: packReference, maskData: nil) case let .documentAttributeVideo(duration, w, h): self = .Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)), flags: []) default: diff --git a/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift b/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift index 60846cf470..b8206980af 100644 --- a/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift +++ b/TelegramCore/ProcessSecretChatIncomingEncryptedOperations.swift @@ -13,11 +13,12 @@ private enum MessagePreParsingError: Error { func processSecretChatIncomingEncryptedOperations(modifier: Modifier, peerId: PeerId) -> Bool { if let state = modifier.getPeerChatState(peerId) as? SecretChatState { + var updatedState = state var removeTagLocalIndices: [Int32] = [] var addedDecryptedOperations = false modifier.operationLogEnumerateEntries(peerId: peerId, tag: OperationLogTags.SecretIncomingEncrypted, { entry in if let operation = entry.contents as? SecretChatIncomingEncryptedOperation { - if let key = state.keychain.key(fingerprint: operation.keyFingerprint) { + if let key = updatedState.keychain.key(fingerprint: operation.keyFingerprint) { withDecryptedMessageContents(key: key, data: operation.contents, { decryptedContents in if let decryptedContents = decryptedContents { withExtendedLifetime(decryptedContents, { @@ -47,7 +48,7 @@ func processSecretChatIncomingEncryptedOperations(modifier: Modifier, peerId: Pe throw MessagePreParsingError.malformedData } - switch state.role { + switch updatedState.role { case .creator: if seqOutValue < 0 || (seqInValue >= 0 && (seqInValue & 1) == 0) || (seqOutValue & 1) != 0 { throw MessagePreParsingError.protocolViolation @@ -77,7 +78,7 @@ func processSecretChatIncomingEncryptedOperations(modifier: Modifier, peerId: Pe let entryTagLocalIndex: StorePeerOperationLogEntryTagLocalIndex - switch state.embeddedState { + switch updatedState.embeddedState { case .terminated: throw MessagePreParsingError.invalidChatState case .handshake: @@ -88,7 +89,7 @@ func processSecretChatIncomingEncryptedOperations(modifier: Modifier, peerId: Pe throw MessagePreParsingError.protocolViolation } let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: parsedLayer, locallyRequestedLayer: nil, remotelyRequestedLayer: nil), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: modifier.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) - let updatedState = state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) + updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) modifier.setPeerChatState(peerId, state: updatedState) entryTagLocalIndex = .manual(sequenceBasedLayerState.baseIncomingOperationIndex + sequenceInfo.operationIndex) } else { diff --git a/TelegramCore/RecentCalls.swift b/TelegramCore/RecentCalls.swift deleted file mode 100644 index b9187349af..0000000000 --- a/TelegramCore/RecentCalls.swift +++ /dev/null @@ -1,66 +0,0 @@ -import Foundation -#if os(macOS) - import PostboxMac - import MtProtoKitMac - import SwiftSignalKitMac -#else - import Postbox - import MtProtoKitDynamic - import SwiftSignalKit -#endif - -public func recentCalls(account: Account, limit: Int) -> Signal<[Message], NoError> { - let filter: Api.MessagesFilter = .inputMessagesFilterPhoneCalls(flags: 0) - - let searchResult = account.network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: Int32.max, offset: 0, maxId: Int32.max, limit: Int32(limit))) - |> retryRequest - - let processedSearchResult = searchResult - |> mapToSignal { result -> Signal<[Message], NoError> in - let messages: [Api.Message] - let chats: [Api.Chat] - let users: [Api.User] - switch result { - case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let .messages(apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let.messagesSlice(_, apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - } - - return account.postbox.modify { modifier -> [Message] in - var peers: [PeerId: Peer] = [:] - - for user in users { - if let user = TelegramUser.merge(modifier.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers[user.id] = user - } - } - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers[groupOrChannel.id] = groupOrChannel - } - } - - var renderedMessages: [Message] = [] - for message in messages { - if let message = StoreMessage(apiMessage: message), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { - renderedMessages.append(renderedMessage) - } - } - - return renderedMessages - } - - } - - return processedSearchResult -} diff --git a/TelegramCore/RecentMediaItem.swift b/TelegramCore/RecentMediaItem.swift index c2cd89892f..61f2206215 100644 --- a/TelegramCore/RecentMediaItem.swift +++ b/TelegramCore/RecentMediaItem.swift @@ -19,7 +19,7 @@ public struct RecentMediaItemId { self.mediaId = MediaId(namespace: mediaIdNamespace, id: mediaIdId) } - init(_ mediaId: MediaId) { + public init(_ mediaId: MediaId) { self.mediaId = mediaId var mediaIdNamespace: Int32 = mediaId.namespace var mediaIdId: Int64 = mediaId.id diff --git a/TelegramCore/RequestUserPhotos.swift b/TelegramCore/RequestUserPhotos.swift index b13a031d21..6be45d2937 100644 --- a/TelegramCore/RequestUserPhotos.swift +++ b/TelegramCore/RequestUserPhotos.swift @@ -60,7 +60,7 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra } } } else if let peer = peer, let inputPeer = apiInputPeer(peer) { - return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offset: 0, maxId: 0, limit: 1000)) |> map {Optional($0)} + return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0)) |> map {Optional($0)} |> mapError {_ in} |> `catch` { return Signal.single(nil) diff --git a/TelegramCore/RichText.swift b/TelegramCore/RichText.swift index daceffe0b0..179f9c89a8 100644 --- a/TelegramCore/RichText.swift +++ b/TelegramCore/RichText.swift @@ -172,8 +172,8 @@ public indirect enum RichText: Coding, Equatable { } } -extension RichText { - var plainText: String { +public extension RichText { + public var plainText: String { switch self { case .empty: return "" diff --git a/TelegramCore/SavedStickerItem.swift b/TelegramCore/SavedStickerItem.swift new file mode 100644 index 0000000000..c86626c560 --- /dev/null +++ b/TelegramCore/SavedStickerItem.swift @@ -0,0 +1,30 @@ +import Foundation +#if os(macOS) + import PostboxMac +#else + import Postbox +#endif + +public final class SavedStickerItem: OrderedItemListEntryContents, Equatable { + public let file: TelegramMediaFile + public let stringRepresentations: [String] + + init(file: TelegramMediaFile, stringRepresentations: [String]) { + self.file = file + self.stringRepresentations = stringRepresentations + } + + public init(decoder: Decoder) { + self.file = decoder.decodeObjectForKey("f") as! TelegramMediaFile + self.stringRepresentations = decoder.decodeStringArrayForKey("sr") + } + + public func encode(_ encoder: Encoder) { + encoder.encodeObject(self.file, forKey: "f") + encoder.encodeStringArray(self.stringRepresentations, forKey: "sr") + } + + public static func ==(lhs: SavedStickerItem, rhs: SavedStickerItem) -> Bool { + return lhs.file.isEqual(rhs.file) && lhs.stringRepresentations == rhs.stringRepresentations + } +} diff --git a/TelegramCore/SearchMessages.swift b/TelegramCore/SearchMessages.swift index fc40af43e6..ce25e0397a 100644 --- a/TelegramCore/SearchMessages.swift +++ b/TelegramCore/SearchMessages.swift @@ -20,11 +20,11 @@ public func searchMessages(account: Account, peerId: PeerId?, query: String, tag let filter: Api.MessagesFilter if let tags = tagMask { - if tags.contains(.File) { + if tags.contains(.file) { filter = .inputMessagesFilterDocument - } else if tags.contains(.Music) { + } else if tags.contains(.music) { filter = .inputMessagesFilterMusic - } else if tags.contains(.WebPage) { + } else if tags.contains(.webPage) { filter = .inputMessagesFilterUrl } else { filter = .inputMessagesFilterEmpty @@ -37,7 +37,7 @@ public func searchMessages(account: Account, peerId: PeerId?, query: String, tag searchResult = account.postbox.loadedPeerWithId(peerId) |> mapToSignal { peer -> Signal in if let inputPeer = apiInputPeer(peer) { - return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: query, fromId: nil, filter: filter, minDate: 0, maxDate: Int32.max - 1, offset: 0, maxId: Int32.max - 1, limit: 64)) + return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: query, fromId: nil, filter: filter, minDate: 0, maxDate: Int32.max - 1, offsetId: 0, addOffset: 0, limit: 64, maxId: Int32.max - 1, minId: 0)) |> retryRequest } else { return .never() diff --git a/TelegramCore/SearchStickers.swift b/TelegramCore/SearchStickers.swift index b9f4b81cf5..37e122db88 100644 --- a/TelegramCore/SearchStickers.swift +++ b/TelegramCore/SearchStickers.swift @@ -7,12 +7,56 @@ import Foundation import SwiftSignalKit #endif -public func searchStickers(postbox: Postbox, query: String) -> Signal<[StickerPackItem], NoError> { - return postbox.modify { modifier -> [StickerPackItem] in - var result: [StickerPackItem] = [] +public final class FoundStickerItem: Equatable { + public let file: TelegramMediaFile + public let stringRepresentations: [String] + + init(file: TelegramMediaFile, stringRepresentations: [String]) { + self.file = file + self.stringRepresentations = stringRepresentations + } + + public static func ==(lhs: FoundStickerItem, rhs: FoundStickerItem) -> Bool { + if !lhs.file.isEqual(rhs.file) { + return false + } + if lhs.stringRepresentations != rhs.stringRepresentations { + return false + } + return true + } +} + +public func searchStickers(postbox: Postbox, query: String) -> Signal<[FoundStickerItem], NoError> { + return postbox.modify { modifier -> [FoundStickerItem] in + var result: [FoundStickerItem] = [] + var idsSet = Set() + + for item in modifier.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudSavedStickers) { + if let stickerItem = item.contents as? SavedStickerItem { + for string in stickerItem.stringRepresentations { + if string == query { + idsSet.insert(stickerItem.file.fileId) + result.append(FoundStickerItem(file: stickerItem.file, stringRepresentations: stickerItem.stringRepresentations)) + } + } + } + } + for item in modifier.searchItemCollection(namespace: Namespaces.ItemCollection.CloudStickerPacks, key: ValueBoxKey(query).toMemoryBuffer()) { if let item = item as? StickerPackItem { - result.append(item) + if !idsSet.contains(item.file.fileId) { + idsSet.insert(item.file.fileId) + var stringRepresentations: [String] = [] + for key in item.indexKeys { + key.withDataNoCopy { data in + if let string = String(data: data, encoding: .utf8) { + stringRepresentations.append(string) + } + } + } + result.append(FoundStickerItem(file: item.file, stringRepresentations: stringRepresentations)) + } } } return result diff --git a/TelegramCore/Serialization.swift b/TelegramCore/Serialization.swift index 35eb340ec5..52e92e8576 100644 --- a/TelegramCore/Serialization.swift +++ b/TelegramCore/Serialization.swift @@ -20,7 +20,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 70 + return 71 } public func parseMessage(_ data: Data!) -> Any! { @@ -56,7 +56,7 @@ public class Serialization: NSObject, MTSerialization { return { response -> MTDatacenterAddressListData! in if let config = parse(Buffer(data: response)) { switch config { - case let .config(_, _, _, _, _, dcOptions, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .config(_, _, _, _, _, dcOptions, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): var addressDict: [NSNumber: [Any]] = [:] for option in dcOptions { switch option { diff --git a/TelegramCore/StickerPack.swift b/TelegramCore/StickerPack.swift index 8106483f37..90571bf8a3 100644 --- a/TelegramCore/StickerPack.swift +++ b/TelegramCore/StickerPack.swift @@ -37,7 +37,7 @@ public struct StickerPackCollectionInfoFlags : OptionSet { public final class StickerPackCollectionInfo: ItemCollectionInfo, Equatable { public let id: ItemCollectionId - public let flags:StickerPackCollectionInfoFlags + public let flags: StickerPackCollectionInfoFlags public let accessHash: Int64 public let title: String public let shortName: String @@ -107,7 +107,7 @@ public final class StickerPackCollectionInfo: ItemCollectionInfo, Equatable { public final class StickerPackItem: ItemCollectionItem, Equatable { public let index: ItemCollectionItemIndex public let file: TelegramMediaFile - public var indexKeys: [MemoryBuffer] + public let indexKeys: [MemoryBuffer] public init(index: ItemCollectionItemIndex, file: TelegramMediaFile, indexKeys: [MemoryBuffer]) { self.index = index @@ -131,6 +131,18 @@ public final class StickerPackItem: ItemCollectionItem, Equatable { public static func ==(lhs: StickerPackItem, rhs: StickerPackItem) -> Bool { return lhs.index == rhs.index && lhs.file == rhs.file && lhs.indexKeys == rhs.indexKeys } + + public func getStringRepresentationsOfIndexKeys() -> [String] { + var stringRepresentations: [String] = [] + for key in self.indexKeys { + key.withDataNoCopy { data in + if let string = String(data: data, encoding: .utf8) { + stringRepresentations.append(string) + } + } + } + return stringRepresentations + } } extension StickerPackCollectionInfo { diff --git a/TelegramCore/StoreMessage_Telegram.swift b/TelegramCore/StoreMessage_Telegram.swift index d317248ff5..64df8441ec 100644 --- a/TelegramCore/StoreMessage_Telegram.swift +++ b/TelegramCore/StoreMessage_Telegram.swift @@ -7,32 +7,41 @@ import Foundation public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], media: [Media], textEntities: [MessageTextEntity]?) -> (MessageTags, GlobalMessageTags) { var isSecret = false + var isUnconsumedPersonalMention = false for attribute in attributes { if let timerAttribute = attribute as? AutoremoveTimeoutMessageAttribute { if timerAttribute.timeout > 0 && timerAttribute.timeout <= 60 { isSecret = true } - break + } else if let mentionAttribute = attribute as? ConsumablePersonalMentionMessageAttribute { + if !mentionAttribute.consumed { + isUnconsumedPersonalMention = true + } } } var tags = MessageTags() var globalTags = GlobalMessageTags() + + if isUnconsumedPersonalMention { + tags.insert(.unseenPersonalMessage) + } + for attachment in media { if let _ = attachment as? TelegramMediaImage { if !isSecret { - tags.insert(.PhotoOrVideo) + tags.insert(.photoOrVideo) } } else if let file = attachment as? TelegramMediaFile { - var refinedTag: MessageTags? = .File + var refinedTag: MessageTags? = .file inner: for attribute in file.attributes { switch attribute { case let .Video(_, _, flags): if flags.contains(.instantRoundVideo) { - refinedTag = .VoiceOrInstantVideo + refinedTag = .voiceOrInstantVideo } else { if !isSecret { - refinedTag = .PhotoOrVideo + refinedTag = .photoOrVideo } else { refinedTag = nil } @@ -40,9 +49,9 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], break inner case let .Audio(isVoice, _, _, _, _): if isVoice { - refinedTag = .VoiceOrInstantVideo + refinedTag = .voiceOrInstantVideo } else { - refinedTag = .Music + refinedTag = .music } break inner case .Sticker: @@ -56,7 +65,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], tags.insert(refinedTag) } } else if let webpage = attachment as? TelegramMediaWebpage, case .Loaded = webpage.content { - tags.insert(.WebPage) + tags.insert(.webPage) } else if let action = attachment as? TelegramMediaAction { switch action.action { case let .phoneCall(_, discardReason, _): @@ -69,16 +78,17 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], } } } - if let textEntities = textEntities, !textEntities.isEmpty && !tags.contains(.WebPage) { + if let textEntities = textEntities, !textEntities.isEmpty && !tags.contains(.webPage) { for entity in textEntities { switch entity.type { case .Url, .Email: - tags.insert(.WebPage) + tags.insert(.webPage) default: break } } } + return (tags, globalTags) } @@ -474,6 +484,8 @@ extension StoreMessage { var notificationFlags: NotificationInfoMessageAttributeFlags = [] if (flags & (1 << 4)) != 0 { notificationFlags.insert(.personal) + let notConsumed = (flags & (1 << 5)) != 0 + attributes.append(ConsumablePersonalMentionMessageAttribute(consumed: !notConsumed, pending: false)) } if (flags & (1 << 13)) != 0 { notificationFlags.insert(.muted) diff --git a/TelegramCore/SynchronizePeerReadState.swift b/TelegramCore/SynchronizePeerReadState.swift index 82eca32cea..408c7cb40e 100644 --- a/TelegramCore/SynchronizePeerReadState.swift +++ b/TelegramCore/SynchronizePeerReadState.swift @@ -74,7 +74,7 @@ func fetchPeerCloudReadState(network: Network, postbox: Postbox, peerId: PeerId, let apiReadOutboxMaxId: Int32 let apiUnreadCount: Int32 switch dialog { - case let .dialog(_, _, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, _): + case let .dialog(_, _, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, _, _): apiTopMessage = topMessage apiReadInboxMaxId = readInboxMaxId apiReadOutboxMaxId = readOutboxMaxId @@ -109,7 +109,7 @@ private func dialogReadState(network: Network, postbox: Postbox, peerId: PeerId) let apiUnreadCount: Int32 var apiChannelPts: Int32 = 0 switch dialog { - case let .dialog(_, _, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, pts, _): + case let .dialog(_, _, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, pts, _): apiTopMessage = topMessage apiReadInboxMaxId = readInboxMaxId apiReadOutboxMaxId = readOutboxMaxId diff --git a/TelegramCore/SynchronizeSavedStickersOperation.swift b/TelegramCore/SynchronizeSavedStickersOperation.swift new file mode 100644 index 0000000000..8105c68af9 --- /dev/null +++ b/TelegramCore/SynchronizeSavedStickersOperation.swift @@ -0,0 +1,175 @@ +import Foundation +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +private enum SynchronizeSavedStickersOperationContentType: Int32 { + case add + case remove + case sync +} + +enum SynchronizeSavedStickersOperationContent: Coding { + case add(id: Int64, accessHash: Int64) + case remove(id: Int64, accessHash: Int64) + case sync + + init(decoder: Decoder) { + switch decoder.decodeInt32ForKey("r", orElse: 0) { + case SynchronizeSavedStickersOperationContentType.add.rawValue: + self = .add(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0)) + case SynchronizeSavedStickersOperationContentType.remove.rawValue: + self = .remove(id: decoder.decodeInt64ForKey("i", orElse: 0), accessHash: decoder.decodeInt64ForKey("h", orElse: 0)) + case SynchronizeSavedStickersOperationContentType.sync.rawValue: + self = .sync + default: + assertionFailure() + self = .sync + } + } + + func encode(_ encoder: Encoder) { + switch self { + case let .add(id, accessHash): + encoder.encodeInt32(SynchronizeSavedStickersOperationContentType.add.rawValue, forKey: "r") + encoder.encodeInt64(id, forKey: "i") + encoder.encodeInt64(accessHash, forKey: "h") + case let .remove(id, accessHash): + encoder.encodeInt32(SynchronizeSavedStickersOperationContentType.remove.rawValue, forKey: "r") + encoder.encodeInt64(id, forKey: "i") + encoder.encodeInt64(accessHash, forKey: "h") + case .sync: + encoder.encodeInt32(SynchronizeSavedStickersOperationContentType.sync.rawValue, forKey: "r") + } + } +} + +final class SynchronizeSavedStickersOperation: Coding { + let content: SynchronizeSavedStickersOperationContent + + init(content: SynchronizeSavedStickersOperationContent) { + self.content = content + } + + init(decoder: Decoder) { + self.content = decoder.decodeObjectForKey("c", decoder: { SynchronizeSavedStickersOperationContent(decoder: $0) }) as! SynchronizeSavedStickersOperationContent + } + + func encode(_ encoder: Encoder) { + encoder.encodeObject(self.content, forKey: "c") + } +} + +func addSynchronizeSavedStickersOperation(modifier: Modifier, operation: SynchronizeSavedStickersOperationContent) { + let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedStickers + let peerId = PeerId(namespace: 0, id: 0) + + var topOperation: (SynchronizeSavedStickersOperation, Int32)? + modifier.operationLogEnumerateEntries(peerId: peerId, tag: tag, { entry in + if let operation = entry.contents as? SynchronizeSavedStickersOperation { + topOperation = (operation, entry.tagLocalIndex) + } + return false + }) + + if let (topOperation, topLocalIndex) = topOperation, case .sync = topOperation.content { + let _ = modifier.operationLogRemoveEntry(peerId: peerId, tag: tag, tagLocalIndex: topLocalIndex) + } + + modifier.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeSavedStickersOperation(content: operation)) + modifier.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeSavedStickersOperation(content: .sync)) +} + +public enum AddSavedStickerError { + case generic + case notFound +} + +public func getIsStickerSaved(modifier: Modifier, fileId: MediaId) -> Bool { + if let _ = modifier.getOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudSavedStickers, itemId: RecentMediaItemId(fileId).rawValue) { + return true + } else{ + return false + } +} + +public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMediaFile) -> Signal { + return postbox.modify { modifier -> Signal in + for attribute in file.attributes { + if case let .Sticker(_, maybePackReference, _) = attribute, let packReference = maybePackReference { + var fetchReference: StickerPackReference? + switch packReference { + case .name: + fetchReference = packReference + case let .id(id, _): + let items = modifier.getItemCollectionItems(collectionId: ItemCollectionId(namespace: Namespaces.ItemCollection.CloudStickerPacks, id: id)) + inner: for item in items { + if let stickerItem = item as? StickerPackItem { + if stickerItem.file.fileId == file.fileId { + let stringRepresentations = stickerItem.getStringRepresentationsOfIndexKeys() + addSavedSticker(modifier: modifier, file: stickerItem.file, stringRepresentations: stringRepresentations) + break inner + } + } + } + } + if let fetchReference = fetchReference { + return network.request(Api.functions.messages.getStickerSet(stickerset: fetchReference.apiInputStickerSet)) + |> mapError { _ -> AddSavedStickerError in + return .generic + } + |> mapToSignal { result -> Signal in + var stickerStringRepresentations: [String]? + switch result { + case let .stickerSet(_, packs, _): + var stringRepresentationsByFile: [MediaId: [String]] = [:] + for pack in packs { + switch pack { + case let .stickerPack(text, fileIds): + for fileId in fileIds { + let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) + if stringRepresentationsByFile[mediaId] == nil { + stringRepresentationsByFile[mediaId] = [text] + } else { + stringRepresentationsByFile[mediaId]!.append(text) + } + } + } + } + stickerStringRepresentations = stringRepresentationsByFile[file.fileId] + } + if let stickerStringRepresentations = stickerStringRepresentations { + return postbox.modify { modifier -> Void in + addSavedSticker(modifier: modifier, file: file, stringRepresentations: stickerStringRepresentations) + } |> mapError { _ in return AddSavedStickerError.generic } + } else { + return .fail(.notFound) + } + } + } + return .complete() + } + } + return .complete() + } |> mapError { _ in return AddSavedStickerError.generic } |> switchToLatest +} + +public func addSavedSticker(modifier: Modifier, file: TelegramMediaFile, stringRepresentations: [String]) { + if let resource = file.resource as? CloudDocumentMediaResource { + modifier.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudSavedStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: SavedStickerItem(file: file, stringRepresentations: stringRepresentations)), removeTailIfCountExceeds: 30) + addSynchronizeSavedStickersOperation(modifier: modifier, operation: .add(id: resource.fileId, accessHash: resource.accessHash)) + } +} + +public func removeSavedSticker(modifier: Modifier, mediaId: MediaId) { + if let entry = modifier.getOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudSavedStickers, itemId: RecentMediaItemId(mediaId).rawValue), let item = entry.contents as? SavedStickerItem { + if let resource = item.file.resource as? CloudDocumentMediaResource { + modifier.removeOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudSavedStickers, itemId: entry.id) + addSynchronizeSavedStickersOperation(modifier: modifier, operation: .remove(id: resource.fileId, accessHash: resource.accessHash)) + } + } +} diff --git a/TelegramCore/TelegramMediaFile.swift b/TelegramCore/TelegramMediaFile.swift index f0a61c7072..007edd8c95 100644 --- a/TelegramCore/TelegramMediaFile.swift +++ b/TelegramCore/TelegramMediaFile.swift @@ -13,7 +13,7 @@ private let typeVideo: Int32 = 4 private let typeAudio: Int32 = 5 private let typeHasLinkedStickers: Int32 = 6 -public enum StickerPackReference: Coding { +public enum StickerPackReference: Coding, Equatable { case id(id: Int64, accessHash: Int64) case name(String) @@ -40,6 +40,23 @@ public enum StickerPackReference: Coding { encoder.encodeString(name, forKey: "n") } } + + public static func ==(lhs: StickerPackReference, rhs: StickerPackReference) -> Bool { + switch lhs { + case let .id(id, accessHash): + if case .id(id, accessHash) = rhs { + return true + } else { + return false + } + case let .name(name): + if case .name(name) = rhs { + return true + } else { + return false + } + } + } } public struct TelegramMediaVideoFlags: OptionSet { @@ -56,9 +73,37 @@ public struct TelegramMediaVideoFlags: OptionSet { public static let instantRoundVideo = TelegramMediaVideoFlags(rawValue: 1 << 0) } +public struct StickerMaskCoords: Coding { + public let n: Int32 + public let x: Double + public let y: Double + public let zoom: Double + + public init(n: Int32, x: Double, y: Double, zoom: Double) { + self.n = n + self.x = x + self.y = y + self.zoom = zoom + } + + public init(decoder: Decoder) { + self.n = decoder.decodeInt32ForKey("n", orElse: 0) + self.x = decoder.decodeDoubleForKey("x", orElse: 0.0) + self.y = decoder.decodeDoubleForKey("y", orElse: 0.0) + self.zoom = decoder.decodeDoubleForKey("z", orElse: 0.0) + } + + public func encode(_ encoder: Encoder) { + encoder.encodeInt32(self.n, forKey: "n") + encoder.encodeDouble(self.x, forKey: "x") + encoder.encodeDouble(self.y, forKey: "y") + encoder.encodeDouble(self.zoom, forKey: "z") + } +} + public enum TelegramMediaFileAttribute: Coding { case FileName(fileName: String) - case Sticker(displayText: String, packReference: StickerPackReference?) + case Sticker(displayText: String, packReference: StickerPackReference?, maskData: StickerMaskCoords?) case ImageSize(size: CGSize) case Animated case Video(duration: Int, size: CGSize, flags: TelegramMediaVideoFlags) @@ -71,7 +116,7 @@ public enum TelegramMediaFileAttribute: Coding { case typeFileName: self = .FileName(fileName: decoder.decodeStringForKey("fn", orElse: "")) case typeSticker: - self = .Sticker(displayText: decoder.decodeStringForKey("dt", orElse: ""), packReference: decoder.decodeObjectForKey("pr", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference) + self = .Sticker(displayText: decoder.decodeStringForKey("dt", orElse: ""), packReference: decoder.decodeObjectForKey("pr", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference, maskData: decoder.decodeObjectForKey("mc", decoder: { StickerMaskCoords(decoder: $0) }) as? StickerMaskCoords) case typeImageSize: self = .ImageSize(size: CGSize(width: CGFloat(decoder.decodeInt32ForKey("w", orElse: 0)), height: CGFloat(decoder.decodeInt32ForKey("h", orElse: 0)))) case typeAnimated: @@ -97,7 +142,7 @@ public enum TelegramMediaFileAttribute: Coding { case let .FileName(fileName): encoder.encodeInt32(typeFileName, forKey: "t") encoder.encodeString(fileName, forKey: "fn") - case let .Sticker(displayText, packReference): + case let .Sticker(displayText, packReference, maskCoords): encoder.encodeInt32(typeSticker, forKey: "t") encoder.encodeString(displayText, forKey: "dt") if let packReference = packReference { @@ -105,6 +150,11 @@ public enum TelegramMediaFileAttribute: Coding { } else { encoder.encodeNil(forKey: "pr") } + if let maskCoords = maskCoords { + encoder.encodeObject(maskCoords, forKey: "mc") + } else { + encoder.encodeNil(forKey: "mc") + } case let .ImageSize(size): encoder.encodeInt32(typeImageSize, forKey: "t") encoder.encodeInt32(Int32(size.width), forKey: "w") @@ -317,6 +367,28 @@ public func ==(lhs: TelegramMediaFile, rhs: TelegramMediaFile) -> Bool { return lhs.isEqual(rhs) } +extension StickerPackReference { + init?(apiInputSet: Api.InputStickerSet) { + switch apiInputSet { + case .inputStickerSetEmpty: + return nil + case let .inputStickerSetID(id, accessHash): + self = .id(id: id, accessHash: accessHash) + case let .inputStickerSetShortName(shortName): + self = .name(shortName) + } + } +} + +extension StickerMaskCoords { + init(apiMaskCoords: Api.MaskCoords) { + switch apiMaskCoords { + case let .maskCoords(n, x, y, zoom): + self.init(n: n, x: x, y: y, zoom: zoom) + } + } +} + public func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAttribute]) -> [TelegramMediaFileAttribute] { var result: [TelegramMediaFileAttribute] = [] for attribute in attributes { @@ -324,16 +396,7 @@ public func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.Docu case let .documentAttributeFilename(fileName): result.append(.FileName(fileName: fileName)) case let .documentAttributeSticker(_, alt, stickerSet, maskCoords): - let packReference: StickerPackReference? - switch stickerSet { - case .inputStickerSetEmpty: - packReference = nil - case let .inputStickerSetID(id, accessHash): - packReference = .id(id: id, accessHash: accessHash) - case let .inputStickerSetShortName(shortName): - packReference = .name(shortName) - } - result.append(.Sticker(displayText: alt, packReference: packReference)) + result.append(.Sticker(displayText: alt, packReference: StickerPackReference(apiInputSet: stickerSet), maskData: maskCoords.flatMap(StickerMaskCoords.init))) case .documentAttributeHasStickers: result.append(.HasLinkedStickers) case let .documentAttributeImageSize(w, h): diff --git a/TelegramCore/UpdateCachedPeerData.swift b/TelegramCore/UpdateCachedPeerData.swift index fd647a2b34..2150c894ef 100644 --- a/TelegramCore/UpdateCachedPeerData.swift +++ b/TelegramCore/UpdateCachedPeerData.swift @@ -210,14 +210,14 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos switch result { case let .chatFull(fullChat, chats, users): switch fullChat { - case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _): + case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _): modifier.updatePeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)]) case .chatFull: break } switch fullChat { - case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId): + case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet): var channelFlags = CachedChannelFlags() if (flags & (1 << 3)) != 0 { channelFlags.insert(.canDisplayParticipants) @@ -261,6 +261,20 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos modifier.updatePeerPresences(peerPresences) + let stickerPack: StickerPackCollectionInfo? = stickerSet.flatMap { apiSet -> StickerPackCollectionInfo in + let namespace: ItemCollectionId.Namespace + switch apiSet { + case let .stickerSet(flags, _, _, _, _, _, _): + if (flags & (1 << 3)) != 0 { + namespace = Namespaces.ItemCollection.CloudMaskPacks + } else { + namespace = Namespaces.ItemCollection.CloudStickerPacks + } + } + + return StickerPackCollectionInfo(apiSet: apiSet, namespace: namespace) + } + modifier.updatePeerCachedData(peerIds: [peerId], update: { _, current in let previous: CachedChannelData if let current = current as? CachedChannelData { @@ -275,6 +289,7 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos .withUpdatedExportedInvitation(ExportedInvitation(apiExportedInvite: apiExportedInvite)) .withUpdatedBotInfos(botInfos) .withUpdatedPinnedMessageId(pinnedMessageId) + .withUpdatedStickerPack(stickerPack) }) case .chatFull: break diff --git a/TelegramCore/UpdateContactName.swift b/TelegramCore/UpdateContactName.swift index ec1b0aa210..e2afa85b24 100644 --- a/TelegramCore/UpdateContactName.swift +++ b/TelegramCore/UpdateContactName.swift @@ -16,7 +16,7 @@ public enum UpdateContactNameError { public func updateContactName(account: Account, peerId: PeerId, firstName: String, lastName: String) -> Signal { return account.postbox.modify { modifier -> Signal in if let peer = modifier.getPeer(peerId) as? TelegramUser, let phone = peer.phone, !phone.isEmpty { - return account.network.request(Api.functions.contacts.importContacts(contacts: [Api.InputContact.inputPhoneContact(clientId: 1, phone: phone, firstName: firstName, lastName: lastName)], replace: .boolFalse)) + return account.network.request(Api.functions.contacts.importContacts(contacts: [Api.InputContact.inputPhoneContact(clientId: 1, phone: phone, firstName: firstName, lastName: lastName)])) |> mapError { _ -> UpdateContactNameError in return .generic } diff --git a/TelegramCore/UpdatesApiUtils.swift b/TelegramCore/UpdatesApiUtils.swift index 6b626b6a5f..cf5069a2af 100644 --- a/TelegramCore/UpdatesApiUtils.swift +++ b/TelegramCore/UpdatesApiUtils.swift @@ -157,7 +157,7 @@ extension Api.Peer { extension Api.Dialog { var peerId: PeerId { switch self { - case let .dialog(_, peer, _, _, _, _, _, _, _): + case let .dialog(_, peer, _, _, _, _, _, _, _, _): return peer.peerId } }