From 95dd1913d97794dfc1c39033181b7851845c89cb Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Wed, 28 Sep 2022 13:57:21 +0400 Subject: [PATCH 1/3] macos 10_12 --- submodules/Crc32/Package.swift | 2 +- submodules/CryptoUtils/Package.swift | 2 +- submodules/EncryptionProvider/Package.swift | 2 +- submodules/FFMpegBinding/Package.swift | 2 +- submodules/GraphCore/Package.swift | 2 +- submodules/ManagedFile/Package.swift | 2 +- submodules/MtProtoKit/Package.swift | 2 +- submodules/MurMurHash32/Package.swift | 2 +- submodules/NetworkLogging/Package.swift | 2 +- submodules/OpenSSLEncryptionProvider/Package.swift | 2 +- submodules/OpusBinding/Package.swift | 2 +- submodules/Postbox/Package.swift | 2 +- submodules/Reachability/LegacyReachability/Package.swift | 2 +- submodules/Reachability/Package.swift | 2 +- submodules/SSignalKit/Package.swift | 2 +- submodules/StringTransliteration/Package.swift | 2 +- submodules/TelegramApi/Package.swift | 2 +- submodules/TelegramCore/Package.swift | 2 +- submodules/TelegramVoip/Package.swift | 2 +- submodules/Utils/RangeSet/Package.swift | 2 +- submodules/YuvConversion/Package.swift | 2 +- submodules/libphonenumber/Package.swift | 2 +- submodules/sqlcipher/Package.swift | 2 +- third-party/rnnoise/Package.swift | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/submodules/Crc32/Package.swift b/submodules/Crc32/Package.swift index 12f92ab411..9a73c17566 100644 --- a/submodules/Crc32/Package.swift +++ b/submodules/Crc32/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "Crc32", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/CryptoUtils/Package.swift b/submodules/CryptoUtils/Package.swift index 33cf2c2b95..42be8a3256 100644 --- a/submodules/CryptoUtils/Package.swift +++ b/submodules/CryptoUtils/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "CryptoUtils", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/EncryptionProvider/Package.swift b/submodules/EncryptionProvider/Package.swift index 59cfa24549..2bbc806847 100644 --- a/submodules/EncryptionProvider/Package.swift +++ b/submodules/EncryptionProvider/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "EncryptionProvider", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/FFMpegBinding/Package.swift b/submodules/FFMpegBinding/Package.swift index 558731a0ee..a7ddae8d59 100644 --- a/submodules/FFMpegBinding/Package.swift +++ b/submodules/FFMpegBinding/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "FFMpegBinding", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/GraphCore/Package.swift b/submodules/GraphCore/Package.swift index 9f749726ea..f0ee70d8de 100644 --- a/submodules/GraphCore/Package.swift +++ b/submodules/GraphCore/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "GraphCore", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/ManagedFile/Package.swift b/submodules/ManagedFile/Package.swift index 9b5ee0faf3..67e49adcbd 100644 --- a/submodules/ManagedFile/Package.swift +++ b/submodules/ManagedFile/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "ManagedFile", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/MtProtoKit/Package.swift b/submodules/MtProtoKit/Package.swift index 2d6b0da79d..d464433335 100644 --- a/submodules/MtProtoKit/Package.swift +++ b/submodules/MtProtoKit/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "MtProtoKit", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/MurMurHash32/Package.swift b/submodules/MurMurHash32/Package.swift index 0766924842..a6841471df 100644 --- a/submodules/MurMurHash32/Package.swift +++ b/submodules/MurMurHash32/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "MurMurHash32", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/NetworkLogging/Package.swift b/submodules/NetworkLogging/Package.swift index 2abba6c2ce..a3e2220488 100644 --- a/submodules/NetworkLogging/Package.swift +++ b/submodules/NetworkLogging/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "NetworkLogging", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/OpenSSLEncryptionProvider/Package.swift b/submodules/OpenSSLEncryptionProvider/Package.swift index c70a67dc33..df2556d5d5 100644 --- a/submodules/OpenSSLEncryptionProvider/Package.swift +++ b/submodules/OpenSSLEncryptionProvider/Package.swift @@ -7,7 +7,7 @@ import PackageDescription let package = Package( name: "OpenSSLEncryption", platforms: [ - .macOS(.v10_11) + .macOS(.v10_12) ], products: [ .library( diff --git a/submodules/OpusBinding/Package.swift b/submodules/OpusBinding/Package.swift index 9b3abc3e84..7ab91a7e99 100644 --- a/submodules/OpusBinding/Package.swift +++ b/submodules/OpusBinding/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "OpusBinding", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/Postbox/Package.swift b/submodules/Postbox/Package.swift index 6cbe03959d..d9bcd9b6e8 100644 --- a/submodules/Postbox/Package.swift +++ b/submodules/Postbox/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "Postbox", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/Reachability/LegacyReachability/Package.swift b/submodules/Reachability/LegacyReachability/Package.swift index 7c9c1d198d..d5d9a0aa99 100644 --- a/submodules/Reachability/LegacyReachability/Package.swift +++ b/submodules/Reachability/LegacyReachability/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "LegacyReachability", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/Reachability/Package.swift b/submodules/Reachability/Package.swift index 14badfab96..ce0c93317f 100644 --- a/submodules/Reachability/Package.swift +++ b/submodules/Reachability/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "Reachability", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/SSignalKit/Package.swift b/submodules/SSignalKit/Package.swift index 4304b4a21c..b6cf5c3a19 100644 --- a/submodules/SSignalKit/Package.swift +++ b/submodules/SSignalKit/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "SSignalKit", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/StringTransliteration/Package.swift b/submodules/StringTransliteration/Package.swift index 6cda06df0e..1066784488 100644 --- a/submodules/StringTransliteration/Package.swift +++ b/submodules/StringTransliteration/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "StringTransliteration", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/TelegramApi/Package.swift b/submodules/TelegramApi/Package.swift index 3d82e48f12..ca9c6e3171 100644 --- a/submodules/TelegramApi/Package.swift +++ b/submodules/TelegramApi/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "TelegramApi", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/TelegramCore/Package.swift b/submodules/TelegramCore/Package.swift index 027eff2198..31a9d13bba 100644 --- a/submodules/TelegramCore/Package.swift +++ b/submodules/TelegramCore/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "TelegramCore", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/TelegramVoip/Package.swift b/submodules/TelegramVoip/Package.swift index 79ba16a38c..b9ab0738de 100644 --- a/submodules/TelegramVoip/Package.swift +++ b/submodules/TelegramVoip/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "TelegramVoip", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/Utils/RangeSet/Package.swift b/submodules/Utils/RangeSet/Package.swift index 54660c2fe6..f8494e3608 100644 --- a/submodules/Utils/RangeSet/Package.swift +++ b/submodules/Utils/RangeSet/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "RangeSet", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/YuvConversion/Package.swift b/submodules/YuvConversion/Package.swift index c3c9f861f3..d3a43056cf 100644 --- a/submodules/YuvConversion/Package.swift +++ b/submodules/YuvConversion/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "YuvConversion", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/libphonenumber/Package.swift b/submodules/libphonenumber/Package.swift index ae6be2b0f3..0d64d78ced 100644 --- a/submodules/libphonenumber/Package.swift +++ b/submodules/libphonenumber/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "libphonenumber", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/submodules/sqlcipher/Package.swift b/submodules/sqlcipher/Package.swift index d29108aaca..b6c02557fb 100644 --- a/submodules/sqlcipher/Package.swift +++ b/submodules/sqlcipher/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "sqlcipher", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( diff --git a/third-party/rnnoise/Package.swift b/third-party/rnnoise/Package.swift index 8d4b24ff1b..7d0fb525f0 100644 --- a/third-party/rnnoise/Package.swift +++ b/third-party/rnnoise/Package.swift @@ -36,7 +36,7 @@ func replaceSymbols() -> [String] { let package = Package( name: "rnoise", - platforms: [.macOS(.v10_11)], + platforms: [.macOS(.v10_12)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( From b5a59ba488e2bf6bdf9352f1f0eef82c4a0dd4df Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 1 Oct 2022 00:08:07 +0200 Subject: [PATCH 2/3] [WIP] Topics --- Telegram/SiriIntents/IntentMessages.swift | 2 +- .../Sources/AccountContext.swift | 4 +- .../AvatarNode/Sources/AvatarNode.swift | 7 +- .../AvatarNode/Sources/PeerAvatar.swift | 57 +- submodules/ChatListUI/BUILD | 1 + .../Sources/ChatListController.swift | 306 ++++++++- .../Sources/ChatListControllerNode.swift | 2 +- .../Sources/ChatListSearchListPaneNode.swift | 8 +- .../Sources/Node/ChatListItem.swift | 42 +- .../Sources/Node/ChatListNode.swift | 36 +- .../Sources/Node/ChatListNodeEntries.swift | 43 +- .../Sources/Node/ChatListNodeLocation.swift | 7 +- .../Sources/ContactListNode.swift | 4 +- submodules/Postbox/Sources/ChatListView.swift | 47 +- .../Postbox/Sources/ChatListViewState.swift | 33 +- submodules/Postbox/Sources/MediaBoxFile.swift | 3 +- .../MessageHistoryThreadIndexView.swift | 48 ++ .../Sources/MessageThreadIndexTable.swift | 20 + submodules/Postbox/Sources/Postbox.swift | 5 + submodules/Postbox/Sources/Views.swift | 12 + .../TextSizeSelectionController.swift | 3 +- .../ThemeAccentColorControllerNode.swift | 3 +- .../Themes/ThemePreviewControllerNode.swift | 3 +- .../Sources/ShareController.swift | 2 +- .../Account/AccountIntermediateState.swift | 7 +- .../TelegramCore/Sources/ForumChannels.swift | 108 ++- .../State/AccountStateManagementUtils.swift | 632 +++++++++++++----- .../State/ChatHistoryPreloadManager.swift | 15 +- .../Sources/State/FetchChatList.swift | 40 +- .../Sources/State/ForumChannelState.swift | 3 + .../TelegramCore/Sources/State/Holes.swift | 6 + .../TelegramEngine/Messages/ChatList.swift | 18 +- .../Messages/ReplyThreadHistory.swift | 56 +- .../Sources/TelegramIntents.swift | 2 +- .../Sources/ForumTopicListScreen.swift | 1 - .../Sources/LottieAnimationCache.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 4 +- .../Sources/ChatHistoryViewForLocation.swift | 72 +- .../ChatSearchResultsContollerNode.swift | 5 +- .../Sources/NavigateToChatController.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 4 +- .../Sources/SharedAccountContext.swift | 4 + .../WatchBridge/Sources/WatchBridge.swift | 2 +- 43 files changed, 1274 insertions(+), 407 deletions(-) create mode 100644 submodules/TelegramCore/Sources/State/ForumChannelState.swift diff --git a/Telegram/SiriIntents/IntentMessages.swift b/Telegram/SiriIntents/IntentMessages.swift index 004248e2b1..dc102b2a57 100644 --- a/Telegram/SiriIntents/IntentMessages.swift +++ b/Telegram/SiriIntents/IntentMessages.swift @@ -36,7 +36,7 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> { |> mapToSignal { view -> Signal<[INMessage], NoError> in var signals: [Signal<[INMessage], NoError>] = [] for entry in view.0.entries { - if case let .MessageEntry(index, _, readState, isMuted, _, _, _, _, _, _) = entry { + if case let .MessageEntry(index, _, readState, isMuted, _, _, _, _, _, _, _) = entry { if index.messageIndex.id.peerId.namespace != Namespaces.Peer.CloudUser { continue } diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 345fc5b538..aa9cdfd572 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -744,10 +744,8 @@ public protocol SharedAccountContext: AnyObject { func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void) func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void) - #if ENABLE_WALLET - func openWallet(context: AccountContext, walletContext: OpenWalletContext, present: @escaping (ViewController) -> Void) - #endif func openImagePicker(context: AccountContext, completion: @escaping (UIImage) -> Void, present: @escaping (ViewController) -> Void) + func openAddPeerMembers(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) func makeRecentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext) -> ViewController & RecentSessionsController diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index d254848e87..d970e61eaa 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -23,6 +23,7 @@ public func avatarPlaceholderFont(size: CGFloat) -> UIFont { public enum AvatarNodeClipStyle { case none case round + case roundedRect } private class AvatarNodeParameters: NSObject { @@ -330,7 +331,7 @@ public final class AvatarNode: ASDisplayNode { let account = account ?? genericContext.account - if let peer = peer, let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, round: clipStyle == .round, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) { + if let peer = peer, let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) { self.contents = nil self.displaySuspended = true self.imageReady.set(self.imageNode.contentReady) @@ -432,6 +433,10 @@ public final class AvatarNode: ASDisplayNode { context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height)) context.clip() + } else if case .roundedRect = parameters.clipStyle { + context.beginPath() + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height), cornerRadius: floor(bounds.size.width * 0.25)).cgPath) + context.clip() } if let explicitColorIndex = parameters.explicitColorIndex { diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 2427f8937f..6028770f0b 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -86,7 +86,7 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?, public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, size: CGSize, round: Bool = true, font: UIFont = avatarPlaceholderFont(size: 13.0), drawLetters: Bool = true, fullSize: Bool = false, blurred: Bool = false) -> Signal { let iconSignal: Signal - if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, round: round, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { + if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, clipStyle: round ? .round : .none, blurred: blurred, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) { if fullSize, let fullSizeSignal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) { iconSignal = combineLatest(.single(nil) |> then(signal), .single(nil) |> then(fullSizeSignal)) |> mapToSignal { thumbnailImage, fullSizeImage -> Signal in @@ -131,7 +131,7 @@ public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, size: CG return iconSignal } -public func peerAvatarImage(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), round: Bool = true, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false) -> Signal<(UIImage, UIImage)?, NoError>? { +public func peerAvatarImage(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), clipStyle: AvatarNodeClipStyle = .round, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false) -> Signal<(UIImage, UIImage)?, NoError>? { if let imageData = peerAvatarImageData(account: account, peerReference: peerReference, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) { return imageData |> mapToSignal { data -> Signal<(UIImage, UIImage)?, NoError> in @@ -145,8 +145,16 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.setBlendMode(.copy) - if round && displayDimensions.width != 60.0 { - context.addEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + switch clipStyle { + case .none: + break + case .round: + if displayDimensions.width != 60.0 { + context.addEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + context.clip() + } + case .roundedRect: + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) context.clip() } @@ -186,30 +194,46 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut context.fill(CGRect(origin: CGPoint(), size: size)) context.setBlendMode(.copy) } - if round { + switch clipStyle { + case .none: + break + case .round: if displayDimensions.width == 60.0 { context.setBlendMode(.destinationOut) context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) } + case .roundedRect: + break } } else { if let emptyColor = emptyColor { context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.setFillColor(emptyColor.cgColor) - if round { - context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) - } else { + switch clipStyle { + case .none: context.fill(CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + case .round: + context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + case .roundedRect: + context.beginPath() + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) + context.fillPath() } } } } else if let emptyColor = emptyColor { context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.setFillColor(emptyColor.cgColor) - if round { - context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) - } else { + + switch clipStyle { + case .none: context.fill(CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + case .round: + context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + case .roundedRect: + context.beginPath() + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) + context.fillPath() } } }) @@ -232,10 +256,15 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut } else if let emptyColor = emptyColor { context.clear(CGRect(origin: CGPoint(), size: displayDimensions)) context.setFillColor(emptyColor.cgColor) - if round { - context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) - } else { + switch clipStyle { + case .none: context.fill(CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + case .round: + context.fillEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) + case .roundedRect: + context.beginPath() + context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) + context.fillPath() } } }) diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index 1a338b0ca4..407a87ff4c 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -78,6 +78,7 @@ swift_library( "//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/TelegramUI/Components/EmojiStatusSelectionComponent", "//submodules/TelegramUI/Components/EntityKeyboard", + "//submodules/AnimationUI:AnimationUI", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 0e6f2056aa..c7682e578a 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -36,6 +36,7 @@ import MultiAnimationRenderer import EmojiStatusSelectionComponent import EntityKeyboard import TelegramStringFormatting +import AnimationUI private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool { if listNode.scroller.isDragging { @@ -108,6 +109,161 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent } } +private final class MoreHeaderButton: HighlightableButtonNode { + enum Content { + case image(UIImage?) + case more(UIImage?) + } + + let referenceNode: ContextReferenceContentNode + let containerNode: ContextControllerSourceNode + private let iconNode: ASImageNode + private var animationNode: AnimationNode? + + var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? + + private var color: UIColor + + init(color: UIColor) { + self.color = color + + self.referenceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + self.containerNode.animateScale = false + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.contentMode = .scaleToFill + + super.init() + + self.containerNode.addSubnode(self.referenceNode) + self.referenceNode.addSubnode(self.iconNode) + self.addSubnode(self.containerNode) + + self.containerNode.shouldBegin = { [weak self] location in + guard let strongSelf = self, let _ = strongSelf.contextAction else { + return false + } + return true + } + self.containerNode.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + return + } + strongSelf.contextAction?(strongSelf.containerNode, gesture) + } + + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0)) + self.referenceNode.frame = self.containerNode.bounds + + self.iconNode.image = optionsCircleImage(color: color) + if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -4.0, bottom: 0.0, right: -4.0) + } + + private var content: Content? + func setContent(_ content: Content, animated: Bool = false) { + if case .more = content, self.animationNode == nil { + let iconColor = self.color + let animationNode = AnimationNode(animation: "anim_profilemore", colors: ["Point 2.Group 1.Fill 1": iconColor, + "Point 3.Group 1.Fill 1": iconColor, + "Point 1.Group 1.Fill 1": iconColor], scale: 1.0) + let animationSize = CGSize(width: 22.0, height: 22.0) + animationNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - animationSize.width) / 2.0), y: floor((self.containerNode.bounds.height - animationSize.height) / 2.0)), size: animationSize) + self.addSubnode(animationNode) + self.animationNode = animationNode + } + if animated { + if let snapshotView = self.referenceNode.view.snapshotContentTree() { + snapshotView.frame = self.referenceNode.frame + self.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false) + + self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.iconNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3) + + self.animationNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.animationNode?.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3) + } + + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + self.animationNode?.isHidden = true + case let .more(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + self.animationNode?.isHidden = false + } + } else { + self.content = content + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + self.animationNode?.isHidden = true + case let .more(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + self.animationNode?.isHidden = false + } + } + } + + override func didLoad() { + super.didLoad() + self.view.isOpaque = false + } + + override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { + return CGSize(width: 22.0, height: 44.0) + } + + func onLayout() { + } + + func play() { + self.animationNode?.playOnce() + } +} + +private func optionsCircleImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 22.0, height: 22.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setStrokeColor(color.cgColor) + let lineWidth: CGFloat = 1.3 + context.setLineWidth(lineWidth) + + context.strokeEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth, dy: lineWidth)) + }) +} + public class ChatListControllerImpl: TelegramBaseController, ChatListController { private var validLayout: ContainerViewLayout? @@ -174,8 +330,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private weak var emojiStatusSelectionController: ViewController? + private let moreBarButton: MoreHeaderButton + private let moreBarButtonItem: UIBarButtonItem private var forumChannelTracker: ForumChannelTopics? + private let selectAddMemberDisposable = MetaDisposable() + private let addMemberDisposable = MetaDisposable() + public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { if self.isNodeLoaded { self.chatListDisplayNode.containerNode.updateSelectedChatLocation(data: data as? ChatLocation, progress: progress, transition: transition) @@ -204,6 +365,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController animationRenderer: self.animationRenderer ) + self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.rootController.navigationBar.buttonColor) + self.moreBarButton.isUserInteractionEnabled = true + self.moreBarButton.setContent(.more(optionsCircleImage(color: self.presentationData.theme.rootController.navigationBar.buttonColor))) + + self.moreBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)! + self.tabContainerNode = ChatListFilterTabContainerNode() super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary, groupCallPanelSource: .all) @@ -225,6 +392,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController title = "Forum" self.forumChannelTracker = ForumChannelTopics(account: self.context.account, peerId: peerId) + + self.moreBarButton.contextAction = { [weak self] sourceNode, gesture in + self?.openMoreMenu(sourceNode: sourceNode, gesture: gesture) + } + self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside) } self.titleView.title = NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil) @@ -265,9 +437,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController backBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Back self.navigationItem.backBarButtonItem = backBarButtonItem } else { - let rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) - rightBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit - self.navigationItem.rightBarButtonItem = rightBarButtonItem + switch self.location { + case .chatList: + let rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) + rightBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Edit + self.navigationItem.rightBarButtonItem = rightBarButtonItem + case .forum: + self.navigationItem.rightBarButtonItem = self.moreBarButtonItem + } + let backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) backBarButtonItem.accessibilityLabel = self.presentationData.strings.Common_Back self.navigationItem.backBarButtonItem = backBarButtonItem @@ -454,9 +632,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } } else { - let editItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(strongSelf.editPressed)) - editItem.accessibilityLabel = strongSelf.presentationData.strings.Common_Edit - strongSelf.navigationItem.setRightBarButton(editItem, animated: true) + switch strongSelf.location { + case .chatList: + let editItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(strongSelf.editPressed)) + editItem.accessibilityLabel = strongSelf.presentationData.strings.Common_Edit + strongSelf.navigationItem.setRightBarButton(editItem, animated: true) + case .forum: + strongSelf.navigationItem.setRightBarButton(strongSelf.moreBarButtonItem, animated: true) + } } let (hasProxy, connectsViaProxy) = proxy @@ -865,6 +1048,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.filterDisposable.dispose() self.featuredFiltersDisposable.dispose() self.activeDownloadsDisposable?.dispose() + self.selectAddMemberDisposable.dispose() + self.addMemberDisposable.dispose() } private func openStatusSetup(sourceView: UIView) { @@ -932,8 +1117,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) editItem.accessibilityLabel = self.presentationData.strings.Common_Done } else { - editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) - editItem.accessibilityLabel = self.presentationData.strings.Common_Edit + switch self.location { + case .chatList: + editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) + editItem.accessibilityLabel = self.presentationData.strings.Common_Edit + case .forum: + editItem = self.moreBarButtonItem + } } if case .chatList(.root) = self.location { self.navigationItem.leftBarButtonItem = editItem @@ -1258,7 +1448,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } var joined = false - if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { + if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { for media in message.media { if let action = media as? TelegramMediaAction, action.action == .peerJoined { joined = true @@ -1272,7 +1462,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatListController.navigationPresentation = .master let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) - case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _): let source: ContextContentSource if let location = location { source = .location(ChatListContextLocationContentSource(controller: strongSelf, location: location)) @@ -2020,11 +2210,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } @objc private func editPressed() { - if case .forum = self.location { - self.forumChannelTracker?.createTopic(title: "Topic#\(Int.random(in: 0 ..< 100000))") - return - } - let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) editItem.accessibilityLabel = self.presentationData.strings.Common_Done if case .chatList(.root) = self.location { @@ -2125,6 +2310,83 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } + @objc private func moreButtonPressed() { + self.moreBarButton.play() + self.moreBarButton.contextAction?(self.moreBarButton.containerNode, nil) + } + + private func openMoreMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { + guard case let .forum(peerId) = self.location else { + return + } + + var items: [ContextMenuItem] = [] + + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "Group Info", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Contact List/CreateGroupActionIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + let _ = (strongSelf.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) + ) + |> deliverOnMainQueue).start(next: { peer in + guard let strongSelf = self, let peer = peer, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else { + return + } + (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) + }) + }))) + + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "Add Member", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).start(next: { peer in + guard let strongSelf = self, let peer = peer else { + return + } + + strongSelf.context.sharedContext.openAddPeerMembers(context: strongSelf.context, updatedPresentationData: nil, parentController: strongSelf, groupPeer: peer._asPeer(), selectAddMemberDisposable: strongSelf.selectAddMemberDisposable, addMemberDisposable: strongSelf.addMemberDisposable) + }) + }))) + + items.append(.separator) + + //TODO:localize + items.append(.action(ContextMenuActionItem(text: "New Topic", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] action in + action.dismissWithResult(.default) + + guard let strongSelf = self, let forumChannelTracker = strongSelf.forumChannelTracker else { + return + } + + let _ = (forumChannelTracker.createTopic(title: "Topic#\(Int.random(in: 0 ..< 100000))") + |> deliverOnMainQueue).start(next: { [weak self] topicId in + guard let strongSelf = self else { + return + } + let _ = enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: [.message(text: "First Message", attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(topicId)), localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).start() + }) + }))) + + let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceNode: self.moreBarButton.referenceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + self.presentInGlobalOverlay(contextController) + } + private var initializedFilters = false private func reloadFilters(firstUpdate: (() -> Void)? = nil) { let filterItems = chatListFilterItems(context: self.context) @@ -3606,3 +3868,17 @@ private final class ChatListContextLocationContentSource: ContextLocationContent return ContextControllerLocationViewInfo(location: self.location, contentAreaInScreenSpace: UIScreen.main.bounds) } } + +private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceNode: ContextReferenceContentNode + + init(controller: ViewController, sourceNode: ContextReferenceContentNode) { + self.controller = controller + self.sourceNode = sourceNode + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 4e04159d39..7691db6699 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -213,7 +213,7 @@ private final class ChatListShimmerNode: ASDisplayNode { ) let readState = EnginePeerReadCounters() - return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) + return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false, forumThreadTitle: nil), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } var itemNodes: [ChatListItemNode] = [] diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 04572586cc..bbcedf9462 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -694,7 +694,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { if isMedia { return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings())), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(id: peer.peerId), interaction: listInteraction, message: message._asMessage(), selection: selection, displayHeader: enableHeaders && !displayCustomHeader, customHeader: key == .downloads ? header : nil, hintIsLink: tagMask == .webPage, isGlobalSearchResult: key != .downloads, isDownloadList: key == .downloads) } else { - return ChatListItem(presentationData: presentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index)), content: .peer(messages: [message], peer: peer, threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) + return ChatListItem(presentationData: presentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index)), content: .peer(messages: [message], peer: peer, threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false, forumThreadTitle: nil), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } case let .addContact(phoneNumber, theme, strings): return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { @@ -1752,7 +1752,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { return } switch item.content { - case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _): if let peer = peer.peer, let message = messages.first { peerContextAction(peer, .search(message.id), node, gesture, location) } @@ -2794,7 +2794,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { bounds = selectedItemNode.bounds } switch item.content { - case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _): return (selectedItemNode.view, bounds, messages.last?.id ?? peer.peerId) case let .groupReference(groupId, _, _, _, _): return (selectedItemNode.view, bounds, groupId) @@ -2982,7 +2982,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { associatedMedia: [:] ) let readState = EnginePeerReadCounters() - return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) + return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false, forumThreadTitle: nil), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) case .media: return nil case .links: diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 767c8dff1d..355f99e2ac 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -49,12 +49,12 @@ public enum ChatListItemContent { } } - case peer(messages: [EngineMessage], peer: EngineRenderedPeer, threadInfo: EngineMessageHistoryThreads.Info?, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool) + case peer(messages: [EngineMessage], peer: EngineRenderedPeer, threadInfo: EngineMessageHistoryThread.Info?, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool, forumThreadTitle: String?) case groupReference(groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool) public var chatLocation: ChatLocation? { switch self { - case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _): return .peer(id: peer.peerId) case .groupReference: return nil @@ -158,7 +158,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { public func selected(listView: ListView) { switch self.content { - case let .peer(messages, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _): + case let .peer(messages, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _): if let message = messages.last, let peer = peer.peer { var threadId: Int64? if case let .forum(_, threadIdValue, _, _) = self.index { @@ -539,7 +539,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(Int32(allCount)))" } return result - case let .peer(_, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _): + case let .peer(_, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _, _): guard let chatMainPeer = peer.chatMainPeer else { return nil } @@ -599,7 +599,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } else { return item.presentationData.strings.VoiceOver_ChatList_MessageEmpty } - case let .peer(messages, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _): + case let .peer(messages, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _, _): if let message = messages.last { var result = "" if message.flags.contains(.Incoming) { @@ -792,7 +792,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var displayAsMessage = false var enablePreview = true switch item.content { - case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, _, _, displayAsMessageValue, _): + case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, _, _, displayAsMessageValue, _, _): displayAsMessage = displayAsMessageValue if displayAsMessage, case let .user(author) = messages.last?.author { peer = .user(author) @@ -819,7 +819,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } else if peer.isDeleted { overrideImage = .deletedIcon } - self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0)) + var isForum = false + if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + isForum = true + } + self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForum ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0)) if peer.isPremium && peer.id != item.context.account.peerId { let context = item.context @@ -958,7 +962,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { guard let item = self.item, item.editing else { return } - if case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _) = item.content { + if case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _) = item.content { if promoInfo == nil, let mainPeer = peer.peer { item.interaction.togglePeerSelected(mainPeer) } @@ -1007,12 +1011,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let promoInfo: ChatListNodeEntryPromoInfo? let displayAsMessage: Bool let hasFailedMessages: Bool - var threadInfo: EngineMessageHistoryThreads.Info? + var threadInfo: EngineMessageHistoryThread.Info? + var forumThreadTitle: String? var groupHiddenByDefault = false switch item.content { - case let .peer(messagesValue, peerValue, threadInfoValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, hasUnseenMentionsValue, hasUnseenReactionsValue, draftStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _): + case let .peer(messagesValue, peerValue, threadInfoValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, hasUnseenMentionsValue, hasUnseenReactionsValue, draftStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _, forumThreadTitleValue): messages = messagesValue contentPeer = .chat(peerValue) combinedReadState = combinedReadStateValue @@ -1033,6 +1038,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { threadInfo = threadInfoValue hasUnseenMentions = hasUnseenMentionsValue hasUnseenReactions = hasUnseenReactionsValue + forumThreadTitle = forumThreadTitleValue switch peerValue.peer { case .user, .secretChat: @@ -1144,7 +1150,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let leftInset: CGFloat = params.leftInset + avatarLeftInset enum ContentData { - case chat(itemPeer: EngineRenderedPeer, threadInfo: EngineMessageHistoryThreads.Info?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) + case chat(itemPeer: EngineRenderedPeer, threadInfo: EngineMessageHistoryThread.Info?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) case group(peers: [EngineChatList.GroupItem.Item]) } @@ -1231,6 +1237,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } } } + + if let forumThreadTitle = forumThreadTitle, let peerTextValue = peerText { + peerText = "\(peerTextValue) → \(forumThreadTitle)" + } let messageText: String if let currentChatListText = currentChatListText, currentChatListText.0 == text { @@ -1451,7 +1461,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { switch item.content { case let .groupReference(_, _, message, _, _): topIndex = message?.index - case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _): topIndex = messages.first?.index } if let topIndex { @@ -1588,7 +1598,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if !isPeerGroup && !isAccountPeer { if displayAsMessage { switch item.content { - case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _): if let peer = messages.last?.author { if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) @@ -1705,7 +1715,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let peerRevealOptions: [ItemListRevealOption] let peerLeftRevealOptions: [ItemListRevealOption] switch item.content { - case let .peer(_, renderedPeer, _, _, _, presence, _, _, _, _, _, _, displayAsMessage, _): + case let .peer(_, renderedPeer, _, _, _, presence, _, _, _, _, _, _, displayAsMessage, _, _): if !displayAsMessage { if case let .user(peer) = renderedPeer.chatMainPeer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId { let updatedPresence = EnginePeer.Presence(status: presence.status, lastActivity: 0) @@ -2547,7 +2557,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { close = false case RevealOptionKey.delete.rawValue: var joined = false - if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { + if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { for media in message.media { if let action = media as? TelegramMediaAction, action.action == .peerJoined { joined = true @@ -2583,7 +2593,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { item.interaction.toggleArchivedFolderHiddenByDefault() close = false case RevealOptionKey.hidePsa.rawValue: - if let item = self.item, case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _) = item.content { + if let item = self.item, case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content { item.interaction.hidePsa(peer.peerId) } close = false diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index bcd0013910..e19761f9a1 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -242,7 +242,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL nodeInteraction.additionalCategorySelected(id) } ), directionHint: entry.directionHint) - case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, threadInfo, presence, hasUnseenMentions, hasUnseenReactions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact): + case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, threadInfo, presence, hasUnseenMentions, hasUnseenReactions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact, forumThreadTitle): switch mode { case .chatList: return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem( @@ -265,7 +265,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, - hasFailedMessages: hasFailedMessages + hasFailedMessages: hasFailedMessages, + forumThreadTitle: forumThreadTitle ), editing: editing, hasActiveRevealControls: hasActiveRevealControls, @@ -432,7 +433,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, location: ChatListControllerLocation, filterData: ChatListItemFilterData?, mode: ChatListNodeMode, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] { return entries.map { entry -> ListViewUpdateItem in switch entry.entry { - case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, threadInfo, presence, hasUnseenMentions, hasUnseenReactions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact): + case let .PeerEntry(index, presentationData, messages, combinedReadState, isRemovedFromTotalUnreadCount, draftState, peer, threadInfo, presence, hasUnseenMentions, hasUnseenReactions, editing, hasActiveRevealControls, selected, inputActivities, promoInfo, hasFailedMessages, isContact, forumThreadTitle): switch mode { case .chatList: return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem( @@ -455,7 +456,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL promoInfo: promoInfo, ignoreUnreadBadge: false, displayAsMessage: false, - hasFailedMessages: hasFailedMessages + hasFailedMessages: hasFailedMessages, + forumThreadTitle: forumThreadTitle ), editing: editing, hasActiveRevealControls: hasActiveRevealControls, @@ -1053,7 +1055,7 @@ public final class ChatListNode: ListView { let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode) let entries = rawEntries.filter { entry in switch entry { - case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _): + case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _, _): switch mode { case .chatList: return true @@ -1225,7 +1227,7 @@ public final class ChatListNode: ListView { var didIncludeHiddenByDefaultArchive = false if let previous = previousView { for entry in previous.filteredEntries { - if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { + if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { if case let .chatList(chatListIndex) = index { if chatListIndex.pinningIndex != nil { previousPinnedChats.append(chatListIndex.messageIndex.id.peerId) @@ -1243,7 +1245,7 @@ public final class ChatListNode: ListView { var doesIncludeArchive = false var doesIncludeHiddenByDefaultArchive = false for entry in processedView.filteredEntries { - if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { + if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { if case let .chatList(index) = index, index.pinningIndex != nil { updatedPinnedChats.append(index.messageIndex.id.peerId) } @@ -1507,7 +1509,7 @@ public final class ChatListNode: ListView { var referenceId: EngineChatList.PinnedItem.Id? var beforeAll = false switch toEntry { - case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _): + case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _, _): if promoInfo != nil { beforeAll = true } else { @@ -1534,7 +1536,7 @@ public final class ChatListNode: ListView { var itemId: EngineChatList.PinnedItem.Id? switch fromEntry { - case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): if case let .chatList(index) = index { itemId = .peer(index.messageIndex.id.peerId) } @@ -1785,7 +1787,7 @@ public final class ChatListNode: ListView { if !transition.chatListView.originalList.hasLater { for entry in filteredEntries.reversed() { switch entry { - case let .PeerEntry(index, _, _, combinedReadState, isMuted, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _): + case let .PeerEntry(index, _, _, combinedReadState, isMuted, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _, _): if promoInfo == nil { var hasUnread = false if let combinedReadState = combinedReadState { @@ -1908,7 +1910,7 @@ public final class ChatListNode: ListView { for item in transition.insertItems { if let item = item.item as? ChatListItem { switch item.content { - case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _): insertedPeerIds.append(peer.peerId) case .groupReference: break @@ -2084,7 +2086,7 @@ public final class ChatListNode: ListView { continue } switch chatListView.filteredEntries[entryCount - i - 1] { - case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _): + case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _, _): if interaction.highlightedChatLocation?.location == ChatLocation.peer(id: peer.peerId) { current = (index, peer.peer!, entryCount - i - 1) break outer @@ -2131,10 +2133,10 @@ public final class ChatListNode: ListView { case .previous(unread: false), .next(unread: false): var target: (EngineChatList.Item.Index, EnginePeer)? = nil if let current = current, entryCount > 1 { - if current.2 > 0, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] { + if current.2 > 0, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 - 1] { next = (index, peer.peer!) } - if current.2 <= entryCount - 2, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] { + if current.2 <= entryCount - 2, case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[current.2 + 1] { previous = (index, peer.peer!) } if case .previous = option { @@ -2143,7 +2145,7 @@ public final class ChatListNode: ListView { target = next } } else if entryCount > 0 { - if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[entryCount - 1] { + if case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _, _, _, _, _, _, _, _) = chatListView.filteredEntries[entryCount - 1] { target = (index, peer.peer!) } } @@ -2221,7 +2223,7 @@ public final class ChatListNode: ListView { continue } switch chatListView.filteredEntries[entryCount - i - 1] { - case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return index default: break @@ -2237,7 +2239,7 @@ public final class ChatListNode: ListView { if resultPeer == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) { if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item { switch item.content { - case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _): resultPeer = peer.peer default: break diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index ea7d7d65e6..60590c69d8 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -47,7 +47,27 @@ public enum ChatListNodeEntryPromoInfo: Equatable { enum ChatListNodeEntry: Comparable, Identifiable { case HeaderEntry - case PeerEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, messages: [EngineMessage], readState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, draftState: ChatListItemContent.DraftState?, peer: EngineRenderedPeer, threadInfo: EngineMessageHistoryThreads.Info?, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool) + case PeerEntry( + index: EngineChatList.Item.Index, + presentationData: ChatListPresentationData, + messages: [EngineMessage], + readState: EnginePeerReadCounters?, + isRemovedFromTotalUnreadCount: Bool, + draftState: ChatListItemContent.DraftState?, + peer: EngineRenderedPeer, + threadInfo: EngineMessageHistoryThread.Info?, + presence: EnginePeer.Presence?, + hasUnseenMentions: Bool, + hasUnseenReactions: Bool, + editing: Bool, + hasActiveRevealControls: Bool, + selected: Bool, + inputActivities: [(EnginePeer, PeerInputActivity)]?, + promoInfo: ChatListNodeEntryPromoInfo?, + hasFailedMessages: Bool, + isContact: Bool, + forumThreadTitle: String? + ) case HoleEntry(EngineMessage.Index, theme: PresentationTheme) case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool) case ArchiveIntro(presentationData: ChatListPresentationData) @@ -57,7 +77,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { switch self { case .HeaderEntry: return .index(.chatList(.absoluteUpperBound)) - case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return .index(index) case let .HoleEntry(holeIndex, _): return .index(.chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: holeIndex))) @@ -74,7 +94,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { switch self { case .HeaderEntry: return .Header - case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): switch index { case let .chatList(index): return .PeerId(index.messageIndex.id.peerId.toInt64()) @@ -104,9 +124,9 @@ enum ChatListNodeEntry: Comparable, Identifiable { } else { return false } - case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessages, lhsUnreadCount, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsThreadInfo, lhsPresence, lhsHasUnseenMentions, lhsHasUnseenReactions, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd, lhsHasFailedMessages, lhsIsContact): + case let .PeerEntry(lhsIndex, lhsPresentationData, lhsMessages, lhsUnreadCount, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsThreadInfo, lhsPresence, lhsHasUnseenMentions, lhsHasUnseenReactions, lhsEditing, lhsHasRevealControls, lhsSelected, lhsInputActivities, lhsAd, lhsHasFailedMessages, lhsIsContact, lhsForumThreadTitle): switch rhs { - case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessages, rhsUnreadCount, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsThreadInfo, rhsPresence, rhsHasUnseenMentions, rhsHasUnseenReactions, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd, rhsHasFailedMessages, rhsIsContact): + case let .PeerEntry(rhsIndex, rhsPresentationData, rhsMessages, rhsUnreadCount, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsThreadInfo, rhsPresence, rhsHasUnseenMentions, rhsHasUnseenReactions, rhsEditing, rhsHasRevealControls, rhsSelected, rhsInputActivities, rhsAd, rhsHasFailedMessages, rhsIsContact, rhsForumThreadTitle): if lhsIndex != rhsIndex { return false } @@ -201,6 +221,9 @@ enum ChatListNodeEntry: Comparable, Identifiable { if lhsIsContact != rhsIsContact { return false } + if lhsForumThreadTitle != rhsForumThreadTitle { + return false + } return true default: return false @@ -355,7 +378,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState inputActivities = state.peerInputActivities?.activities[peerId] } - result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, threadInfo: entry.threadInfo, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, hasUnseenReactions: entry.hasUnseenReactions, editing: state.editing, hasActiveRevealControls: hasActiveRevealControls, selected: isSelected, inputActivities: inputActivities, promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact)) + result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, threadInfo: entry.threadInfo, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, hasUnseenReactions: entry.hasUnseenReactions, editing: state.editing, hasActiveRevealControls: hasActiveRevealControls, selected: isSelected, inputActivities: inputActivities, promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact, forumThreadTitle: entry.forumTopicTitle)) } if !view.hasLater { var pinningIndex: UInt16 = UInt16(pinnedIndexOffset == 0 ? 0 : (pinnedIndexOffset - 1)) @@ -388,7 +411,8 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState inputActivities: nil, promoInfo: nil, hasFailedMessages: false, - isContact: false + isContact: false, + forumThreadTitle: nil )) if foundPinningIndex != 0 { foundPinningIndex -= 1 @@ -396,7 +420,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState } } - result.append(.PeerEntry(index: .chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.predecessor), presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, draftState: nil, peer: EngineRenderedPeer(peerId: savedMessagesPeer.id, peers: [savedMessagesPeer.id: savedMessagesPeer], associatedMedia: [:]), threadInfo: nil, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(savedMessagesPeer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false)) + result.append(.PeerEntry(index: .chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.predecessor), presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, draftState: nil, peer: EngineRenderedPeer(peerId: savedMessagesPeer.id, peers: [savedMessagesPeer.id: savedMessagesPeer], associatedMedia: [:]), threadInfo: nil, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(savedMessagesPeer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false, forumThreadTitle: nil)) } else { if !filteredAdditionalItemEntries.isEmpty { for item in filteredAdditionalItemEntries.reversed() { @@ -434,7 +458,8 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState inputActivities: state.peerInputActivities?.activities[peerId], promoInfo: promoInfo, hasFailedMessages: item.item.hasFailed, - isContact: item.item.isContact + isContact: item.item.isContact, + forumThreadTitle: item.item.forumTopicTitle )) if pinningIndex != 0 { pinningIndex -= 1 diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift index 9ac6d566d1..f80763b74a 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift @@ -179,21 +179,22 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat guard let peer = view.peer else { continue } - guard let info = item.info.get(EngineMessageHistoryThreads.Info.self) else { + guard let data = item.info.get(MessageHistoryThreadData.self) else { continue } items.append(EngineChatList.Item( id: .forum(item.id), index: .forum(timestamp: item.index.timestamp, threadId: item.id, namespace: item.index.id.namespace, id: item.index.id.id), messages: item.topMessage.flatMap { [EngineMessage($0)] } ?? [], - readCounters: nil, + readCounters: EnginePeerReadCounters(state: CombinedPeerReadState(states: [(Namespaces.Message.Cloud, .idBased(maxIncomingReadId: 1, maxOutgoingReadId: 1, maxKnownId: 1, count: data.incomingUnreadCount, markedUnread: false))])), isMuted: false, draft: nil, - threadInfo: info, + threadInfo: data.info, renderedPeer: EngineRenderedPeer(peer: EnginePeer(peer)), presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, + forumTopicTitle: nil, hasFailed: false, isContact: false )) diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 279b65aeec..ec2fbff0ab 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -1265,7 +1265,7 @@ public final class ContactListNode: ASDisplayNode { return context.engine.data.get(EngineDataMap( view.entries.compactMap { entry -> EnginePeer.Id? in switch entry { - case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _): + case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _, _): if let peer = renderedPeer.peer { if let channel = peer as? TelegramChannel, case .group = channel.info { return peer.id @@ -1281,7 +1281,7 @@ public final class ContactListNode: ASDisplayNode { var peers: [(EnginePeer, Int32)] = [] for entry in view.entries { switch entry { - case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _): + case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _, _): if let peer = renderedPeer.peer { if peer is TelegramGroup { peers.append((EnginePeer(peer), 0)) diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index e8c11793fe..7d4ed384d9 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -101,12 +101,12 @@ public struct ChatListGroupReferenceEntry: Equatable { } public enum ChatListEntry: Comparable { - case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], hasFailed: Bool, isContact: Bool) + case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], forumTopicData: CodableEntry?, hasFailed: Bool, isContact: Bool) case HoleEntry(ChatListHole) public var index: ChatListIndex { switch self { - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _): + case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _): return index case let .HoleEntry(hole): return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) @@ -115,9 +115,9 @@ public enum ChatListEntry: Comparable { public static func ==(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool { switch lhs { - case let .MessageEntry(lhsIndex, lhsMessages, lhsReadState, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsHasFailed, lhsIsContact): + case let .MessageEntry(lhsIndex, lhsMessages, lhsReadState, lhsIsRemovedFromTotalUnreadCount, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo, lhsForumTopicData, lhsHasFailed, lhsIsContact): switch rhs { - case let .MessageEntry(rhsIndex, rhsMessages, rhsReadState, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsHasFailed, rhsIsContact): + case let .MessageEntry(rhsIndex, rhsMessages, rhsReadState, rhsIsRemovedFromTotalUnreadCount, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo, rhsForumTopicData, rhsHasFailed, rhsIsContact): if lhsIndex != rhsIndex { return false } @@ -155,6 +155,9 @@ public enum ChatListEntry: Comparable { if lhsInfo != rhsInfo { return false } + if lhsForumTopicData != rhsForumTopicData { + return false + } if lhsHasFailed != rhsHasFailed { return false } @@ -179,26 +182,9 @@ public enum ChatListEntry: Comparable { } } -/*private func processedChatListEntry(_ entry: MutableChatListEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) -> MutableChatListEntry { - switch entry { - case let .IntermediateMessageEntry(index, messageIndex): - var updatedMessage = message - if let message = message, let cachedData = cachedDataTable.get(message.id.peerId), let associatedHistoryMessageId = cachedData.associatedHistoryMessageId, message.id.id == 1 { - if let messageIndex = messageHistoryTable.messageHistoryIndexTable.earlierEntries(id: associatedHistoryMessageId, count: 1).first { - if let associatedMessage = messageHistoryTable.getMessage(messageIndex) { - updatedMessage = associatedMessage - } - } - } - return .IntermediateMessageEntry(index, updatedMessage, readState, embeddedState) - default: - return entry - } -}*/ - enum MutableChatListEntry: Equatable { case IntermediateMessageEntry(index: ChatListIndex, messageIndex: MessageIndex?) - case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], hasFailedMessages: Bool, isContact: Bool) + case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo], forumTopicData: CodableEntry?, hasFailedMessages: Bool, isContact: Bool) case HoleEntry(ChatListHole) init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) { @@ -214,7 +200,7 @@ enum MutableChatListEntry: Equatable { switch self { case let .IntermediateMessageEntry(index, _): return index - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _): + case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _): return index case let .HoleEntry(hole): return ChatListIndex(pinningIndex: nil, messageIndex: hole.index) @@ -655,7 +641,12 @@ final class MutableChatListView { } } - return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers, associatedMedia: renderAssociatedMediaForPeers(postbox: postbox, peers: peers)), presence: presence, tagSummaryInfo: [:], hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact) + var forumTopicData: CodableEntry? + if let message = renderedMessages.first, let threadId = message.threadId { + forumTopicData = postbox.messageHistoryThreadIndexTable.get(peerId: message.id.peerId, threadId: threadId) + } + + return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers, associatedMedia: renderAssociatedMediaForPeers(postbox: postbox, peers: peers)), presence: presence, tagSummaryInfo: [:], forumTopicData: forumTopicData, hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact) default: return nil } @@ -684,8 +675,8 @@ public final class ChatListView { var entries: [ChatListEntry] = [] for entry in mutableView.sampledState.entries { switch entry { - case let .MessageEntry(index, messages, combinedReadState, _, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact): - entries.append(.MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, hasFailed: hasFailed, isContact: isContact)) + case let .MessageEntry(index, messages, combinedReadState, _, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, forumTopicData, hasFailed, isContact): + entries.append(.MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, forumTopicData: forumTopicData, hasFailed: hasFailed, isContact: isContact)) case let .HoleEntry(hole): entries.append(.HoleEntry(hole)) case .IntermediateMessageEntry: @@ -702,9 +693,9 @@ public final class ChatListView { var additionalItemEntries: [ChatListAdditionalItemEntry] = [] for entry in mutableView.additionalItemEntries { switch entry.entry { - case let .MessageEntry(index, messages, combinedReadState, _, isExcludedFromUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact): + case let .MessageEntry(index, messages, combinedReadState, _, isExcludedFromUnreadCount, embeddedState, peer, peerPresence, summaryInfo, forumTopicData, hasFailed, isContact): additionalItemEntries.append(ChatListAdditionalItemEntry( - entry: .MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isExcludedFromUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, hasFailed: hasFailed, isContact: isContact), + entry: .MessageEntry(index: index, messages: messages, readState: combinedReadState, isRemovedFromTotalUnreadCount: isExcludedFromUnreadCount, embeddedInterfaceState: embeddedState, renderedPeer: peer, presence: peerPresence, summaryInfo: summaryInfo, forumTopicData: forumTopicData, hasFailed: hasFailed, isContact: isContact), info: entry.info )) case .HoleEntry: diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index c1aeaeb688..4f40177ce1 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -502,7 +502,7 @@ private final class ChatListViewSpaceState { let entryPeer: Peer let entryNotificationsPeerId: PeerId switch entry { - case let .MessageEntry(_, _, _, _, _, _, renderedPeer, _, _, _, _): + case let .MessageEntry(_, _, _, _, _, _, renderedPeer, _, _, _, _, _): if let peer = renderedPeer.peer { entryPeer = peer entryNotificationsPeerId = peer.notificationSettingsPeerId ?? peer.id @@ -604,13 +604,13 @@ private final class ChatListViewSpaceState { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, _, _, embeddedInterfaceState, renderedPeer, presence, tagSummaryInfo, hasFailedMessages, isContact): + case let .MessageEntry(index, messages, readState, _, _, embeddedInterfaceState, renderedPeer, presence, tagSummaryInfo, forumTopicData, hasFailedMessages, isContact): if let peer = renderedPeer.peer { let notificationsPeerId = peer.notificationSettingsPeerId ?? peer.id if let (_, updated) = transaction.currentUpdatedPeerNotificationSettings[notificationsPeerId] { let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: updated) - return .MessageEntry(index: index, messages: messages, readState: readState, notificationSettings: updated, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedInterfaceState, renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, hasFailedMessages: hasFailedMessages, isContact: isContact) + return .MessageEntry(index: index, messages: messages, readState: readState, notificationSettings: updated, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedInterfaceState, renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, forumTopicData: forumTopicData, hasFailedMessages: hasFailedMessages, isContact: isContact) } else { return nil } @@ -628,7 +628,7 @@ private final class ChatListViewSpaceState { if !transaction.updatedFailedMessagePeerIds.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, renderedPeer, presence, tagSummaryInfo, _, isContact): + case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, renderedPeer, presence, tagSummaryInfo, forumTopicData, _, isContact): if transaction.updatedFailedMessagePeerIds.contains(index.messageIndex.id.peerId) { return .MessageEntry( index: index, @@ -640,6 +640,7 @@ private final class ChatListViewSpaceState { renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, + forumTopicData: forumTopicData, hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact ) @@ -657,7 +658,7 @@ private final class ChatListViewSpaceState { if !transaction.currentUpdatedPeers.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, presence, tagSummaryInfo, hasFailedMessages, isContact): + case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, presence, tagSummaryInfo, forumTopicData, hasFailedMessages, isContact): var updatedMessages: [Message] = messages var hasUpdatedMessages = false for i in 0 ..< updatedMessages.count { @@ -679,6 +680,7 @@ private final class ChatListViewSpaceState { renderedPeer: renderedPeer ?? entryRenderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, + forumTopicData: forumTopicData, hasFailedMessages: hasFailedMessages, isContact: isContact) } else { @@ -695,7 +697,7 @@ private final class ChatListViewSpaceState { if !transaction.currentUpdatedPeerPresences.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, _, tagSummaryInfo, hasFailedMessages, isContact): + case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, _, tagSummaryInfo, forumTopicData, hasFailedMessages, isContact): var presencePeerId = entryRenderedPeer.peerId if let peer = entryRenderedPeer.peers[entryRenderedPeer.peerId], let associatedPeerId = peer.associatedPeerId { presencePeerId = associatedPeerId @@ -711,6 +713,7 @@ private final class ChatListViewSpaceState { renderedPeer: entryRenderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, + forumTopicData: forumTopicData, hasFailedMessages: hasFailedMessages, isContact: isContact ) @@ -731,7 +734,7 @@ private final class ChatListViewSpaceState { let entryPeer: Peer let entryNotificationsPeerId: PeerId switch entry { - case let .MessageEntry(_, _, _, _, _, _, entryRenderedPeer, _, _, _, _): + case let .MessageEntry(_, _, _, _, _, _, entryRenderedPeer, _, _, _, _, _): if let peer = entryRenderedPeer.peer { entryPeer = peer entryNotificationsPeerId = peer.notificationSettingsPeerId ?? peer.id @@ -842,7 +845,7 @@ private final class ChatListViewSpaceState { if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty { if self.orderedEntries.mutableScan({ entry in switch entry { - case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, presence, tagSummaryInfo, hasFailedMessages, isContact): + case let .MessageEntry(index, messages, readState, notificationSettings, isRemovedFromTotalUnreadCount, embeddedInterfaceState, entryRenderedPeer, presence, tagSummaryInfo, forumTopicData, hasFailedMessages, isContact): var updatedChatListMessageTagSummaryInfo: [ChatListEntryMessageTagSummaryKey: ChatListMessageTagSummaryInfo] = tagSummaryInfo var didUpdateSummaryInfo = false @@ -883,6 +886,7 @@ private final class ChatListViewSpaceState { renderedPeer: entryRenderedPeer, presence: presence, tagSummaryInfo: updatedChatListMessageTagSummaryInfo, + forumTopicData: forumTopicData, hasFailedMessages: hasFailedMessages, isContact: isContact ) @@ -1018,7 +1022,7 @@ private extension MutableChatListEntry { switch self { case let .IntermediateMessageEntry(index, _): return index.messageIndex.id.peerId - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _): + case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _): return index.messageIndex.id.peerId case .HoleEntry: return nil @@ -1029,7 +1033,7 @@ private extension MutableChatListEntry { switch self { case let .IntermediateMessageEntry(index, _): return MutableChatListEntryIndex(index: index, isMessage: true) - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _): + case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _): return MutableChatListEntryIndex(index: index, isMessage: true) case let .HoleEntry(hole): return MutableChatListEntryIndex(index: ChatListIndex(pinningIndex: nil, messageIndex: hole.index), isMessage: false) @@ -1040,7 +1044,7 @@ private extension MutableChatListEntry { switch self { case let .IntermediateMessageEntry(index, _): return .peer(index.messageIndex.id.peerId) - case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _): + case let .MessageEntry(index, _, _, _, _, _, _, _, _, _, _, _): return .peer(index.messageIndex.id.peerId) case let .HoleEntry(hole): return .hole(hole.index) @@ -1457,7 +1461,12 @@ struct ChatListViewState { } } - let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) + var forumTopicData: CodableEntry? + if let message = renderedMessages.first, let threadId = message.threadId { + forumTopicData = postbox.messageHistoryThreadIndexTable.get(peerId: message.id.peerId, threadId: threadId) + } + + let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, forumTopicData: forumTopicData, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) if directionIndex == 0 { self.stateBySpace[space]!.orderedEntries.setLowerOrAtAnchorAtArrayIndex(listIndex, to: updatedEntry) } else { diff --git a/submodules/Postbox/Sources/MediaBoxFile.swift b/submodules/Postbox/Sources/MediaBoxFile.swift index 2486bf6ac5..e05d2e2e01 100644 --- a/submodules/Postbox/Sources/MediaBoxFile.swift +++ b/submodules/Postbox/Sources/MediaBoxFile.swift @@ -590,7 +590,8 @@ final class MediaBoxPartialFile { } else { assertionFailure() } - } catch { + } catch let e { + postboxLog("moveLocalFile error: \(e)") assertionFailure() } } diff --git a/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift b/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift index 44935f0492..5afdea595d 100644 --- a/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift +++ b/submodules/Postbox/Sources/MessageHistoryThreadIndexView.swift @@ -129,3 +129,51 @@ public final class MessageHistoryThreadIndexView: PostboxView { self.items = items } } + +final class MutableMessageHistoryThreadInfoView: MutablePostboxView { + private let peerId: PeerId + private let threadId: Int64 + + fileprivate var info: CodableEntry? + + init(postbox: PostboxImpl, peerId: PeerId, threadId: Int64) { + self.peerId = peerId + self.threadId = threadId + + self.reload(postbox: postbox) + } + + private func reload(postbox: PostboxImpl) { + self.info = postbox.messageHistoryThreadIndexTable.get(peerId: self.peerId, threadId: self.threadId) + } + + func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { + var updated = false + + if transaction.updatedMessageThreadPeerIds.contains(self.peerId) { + let info = postbox.messageHistoryThreadIndexTable.get(peerId: self.peerId, threadId: self.threadId) + if self.info != info { + self.info = info + updated = true + } + } + + return updated + } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + return false + } + + func immutableView() -> PostboxView { + return MessageHistoryThreadInfoView(self) + } +} + +public final class MessageHistoryThreadInfoView: PostboxView { + public let info: CodableEntry? + + init(_ view: MutableMessageHistoryThreadInfoView) { + self.info = view.info + } +} diff --git a/submodules/Postbox/Sources/MessageThreadIndexTable.swift b/submodules/Postbox/Sources/MessageThreadIndexTable.swift index 944dd9a507..dd2c7ce086 100644 --- a/submodules/Postbox/Sources/MessageThreadIndexTable.swift +++ b/submodules/Postbox/Sources/MessageThreadIndexTable.swift @@ -110,6 +110,26 @@ class MessageHistoryThreadIndexTable: Table { return self.lowerBound(peerId: peerId).successor } + func get(peerId: PeerId, threadId: Int64) -> CodableEntry? { + if let updated = self.updatedInfoItems[MessageHistoryThreadsTable.ItemId(peerId: peerId, threadId: threadId)] { + return updated + } else { + if let itemIndex = self.reverseIndexTable.get(peerId: peerId, threadId: threadId) { + if let value = self.valueBox.get(self.table, key: self.key(peerId: itemIndex.id.peerId, timestamp: itemIndex.timestamp, threadId: threadId, namespace: itemIndex.id.namespace, id: itemIndex.id.id, key: self.sharedKey)) { + if value.length != 0 { + return CodableEntry(data: value.makeData()) + } else { + return nil + } + } else { + return nil + } + } else { + return nil + } + } + } + func set(peerId: PeerId, threadId: Int64, info: CodableEntry) { self.updatedInfoItems[MessageHistoryThreadsTable.ItemId(peerId: peerId, threadId: threadId)] = info } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 853f624dbf..922ac575eb 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1164,6 +1164,11 @@ public final class Transaction { return self.postbox!.messageHistoryThreadIndexTable.getAll(peerId: peerId) } + public func getMessageHistoryThreadInfo(peerId: PeerId, threadId: Int64) -> CodableEntry? { + assert(!self.disposed) + return self.postbox!.messageHistoryThreadIndexTable.get(peerId: peerId, threadId: threadId) + } + public func setMessageHistoryThreadInfo(peerId: PeerId, threadId: Int64, info: CodableEntry) { assert(!self.disposed) self.postbox!.messageHistoryThreadIndexTable.set(peerId: peerId, threadId: threadId, info: info) diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index 7f00923709..8c6a3e1926 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -39,6 +39,7 @@ public enum PostboxViewKey: Hashable { case chatListIndex(id: PeerId) case peerTimeoutAttributes case messageHistoryThreadIndex(id: PeerId) + case messageHistoryThreadInfo(peerId: PeerId, threadId: Int64) public func hash(into hasher: inout Hasher) { switch self { @@ -127,6 +128,9 @@ public enum PostboxViewKey: Hashable { hasher.combine(17) case let .messageHistoryThreadIndex(id): hasher.combine(id) + case let .messageHistoryThreadInfo(peerId, threadId): + hasher.combine(peerId) + hasher.combine(threadId) } } @@ -360,6 +364,12 @@ public enum PostboxViewKey: Hashable { } else { return false } + case let .messageHistoryThreadInfo(peerId, threadId): + if case .messageHistoryThreadInfo(peerId, threadId) = rhs { + return true + } else { + return false + } } } } @@ -442,5 +452,7 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost return MutablePeerTimeoutAttributesView(postbox: postbox) case let .messageHistoryThreadIndex(id): return MutableMessageHistoryThreadIndexView(postbox: postbox, peerId: id) + case let .messageHistoryThreadInfo(peerId, threadId): + return MutableMessageHistoryThreadInfoView(postbox: postbox, peerId: peerId, threadId: threadId) } } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index f312e97552..a309d0331b 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -282,7 +282,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, - hasFailedMessages: false + hasFailedMessages: false, + forumThreadTitle: nil ), editing: false, hasActiveRevealControls: false, diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index a71488a3af..c596c95d25 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -902,7 +902,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, - hasFailedMessages: false + hasFailedMessages: false, + forumThreadTitle: nil ), editing: false, hasActiveRevealControls: false, diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 7bf64fec62..7d00ec1545 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -425,7 +425,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, - hasFailedMessages: false + hasFailedMessages: false, + forumThreadTitle: nil ), editing: false, hasActiveRevealControls: false, diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 7fddfe48cb..0eebfd0697 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -991,7 +991,7 @@ public final class ShareController: ViewController { var peers: [EngineRenderedPeer] = [] for entry in view.0.entries.reversed() { switch entry { - case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _): + case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _, _): if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != accountPeer.id, canSendMessagesToPeer(peer) { peers.append(EngineRenderedPeer(renderedPeer)) } diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 03a63afb33..7e5a0b281d 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -117,6 +117,7 @@ enum AccountStateMutationOperation { case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String) case UpdateConfig case UpdateExtendedMedia(MessageId, Api.MessageExtendedMedia) + case ResetForumTopic(topicId: MessageId, data: MessageHistoryThreadData, pts: Int32) } struct HoleFromPreviousState { @@ -151,6 +152,8 @@ struct AccountMutableState { var namespacesWithHolesFromPreviousState: [PeerId: [MessageId.Namespace: HoleFromPreviousState]] var updatedOutgoingUniqueMessageIds: [Int64: Int32] + var resetForumTopicLists: [PeerId: [MessageHistoryThreadData]] = [:] + var storedMessagesByPeerIdAndTimestamp: [PeerId: Set] var displayAlerts: [(text: String, isDropAuth: Bool)] = [] var dismissBotWebViews: [Int64] = [] @@ -228,6 +231,8 @@ struct AccountMutableState { self.updatedOutgoingUniqueMessageIds.merge(other.updatedOutgoingUniqueMessageIds, uniquingKeysWith: { lhs, _ in lhs }) self.displayAlerts.append(contentsOf: other.displayAlerts) self.dismissBotWebViews.append(contentsOf: other.dismissBotWebViews) + + self.resetForumTopicLists.merge(other.resetForumTopicLists, uniquingKeysWith: { lhs, _ in lhs }) } mutating func addPreCachedResource(_ resource: MediaResource, data: Data) { @@ -530,7 +535,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic: break case let .AddMessages(messages, location): for message in messages { diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index 831b00adda..678b8ad700 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -3,8 +3,8 @@ import SwiftSignalKit import Postbox import TelegramApi -public final class EngineMessageHistoryThreads { - public final class Info: Equatable, Codable { +public extension EngineMessageHistoryThread { + final class Info: Equatable, Codable { private enum CodingKeys: String, CodingKey { case title case icon @@ -45,6 +45,18 @@ public final class EngineMessageHistoryThreads { } } +public struct MessageHistoryThreadData: Codable { + public var info: EngineMessageHistoryThread.Info + public var incomingUnreadCount: Int32 + public var maxIncomingReadId: Int32 + public var maxKnownMessageId: Int32 + public var maxOutgoingReadId: Int32 +} + +public enum CreateForumChannelTopicError { + case generic +} + func _internal_setChannelForumMode(account: Account, peerId: PeerId, isForum: Bool) -> Signal { return account.postbox.transaction { transaction -> Api.InputChannel? in return transaction.getPeer(peerId).flatMap(apiInputChannel) @@ -130,8 +142,18 @@ func _internal_loadMessageHistoryThreads(account: Account, peerId: PeerId) -> Si for topic in topics { switch topic { - case let .forumTopic(_, id, _, title, iconEmojiId, _, _, _, _): - guard let info = CodableEntry(EngineMessageHistoryThreads.Info(title: title, icon: iconEmojiId)) else { + case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount): + let data = MessageHistoryThreadData( + info: EngineMessageHistoryThread.Info( + title: title, + icon: iconEmojiId + ), + incomingUnreadCount: unreadCount, + maxIncomingReadId: readInboxMaxId, + maxKnownMessageId: topMessage, + maxOutgoingReadId: readOutboxMaxId + ) + guard let info = CodableEntry(data) else { continue } transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: info) @@ -161,7 +183,7 @@ public final class ForumChannelTopics { } private let loadMoreDisposable = MetaDisposable() - private let createTopicDisposable = MetaDisposable() + private let updateDisposable = MetaDisposable() init(queue: Queue, account: Account, peerId: PeerId) { self.queue = queue @@ -177,30 +199,32 @@ public final class ForumChannelTopics { preconditionFailure() } return State(items: view.items.compactMap { item -> ForumChannelTopics.Item? in - guard let info = item.info.get(EngineMessageHistoryThreads.Info.self) else { + guard let data = item.info.get(MessageHistoryThreadData.self) else { return nil } return ForumChannelTopics.Item( id: item.id, - info: info, + info: data.info, index: item.index, topMessage: item.topMessage.flatMap(EngineMessage.init) ) }) }) + + self.updateDisposable.set(account.viewTracker.polledChannel(peerId: peerId).start()) } deinit { assert(self.queue.isCurrent()) self.loadMoreDisposable.dispose() - self.createTopicDisposable.dispose() + self.updateDisposable.dispose() } - func createTopic(title: String) { + func createTopic(title: String) -> Signal { let peerId = self.peerId let account = self.account - let signal: Signal = self.account.postbox.transaction { transaction -> (Api.InputChannel?, Int64?) in + return self.account.postbox.transaction { transaction -> (Api.InputChannel?, Int64?) in var fileId: Int64? = nil var filteredFiles: [TelegramMediaFile] = [] @@ -223,9 +247,10 @@ public final class ForumChannelTopics { return (transaction.getPeer(peerId).flatMap(apiInputChannel), fileId) } - |> mapToSignal { inputChannel, fileId -> Signal in + |> castError(CreateForumChannelTopicError.self) + |> mapToSignal { inputChannel, fileId -> Signal in guard let inputChannel = inputChannel else { - return .single(nil) + return .fail(.generic) } var flags: Int32 = 0 if fileId != nil { @@ -239,37 +264,46 @@ public final class ForumChannelTopics { randomId: Int64.random(in: Int64.min ..< Int64.max), sendAs: nil )) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> mapError { _ -> CreateForumChannelTopicError in + return .generic } - |> mapToSignal { result -> Signal in - guard let result = result else { - return .single(nil) - } + |> mapToSignal { result -> Signal in account.stateManager.addUpdates(result) - return .single(nil) + + var topicId: Int64? + topicId = nil + for update in result.allUpdates { + switch update { + case let .updateNewChannelMessage(message, _, _): + if let message = StoreMessage(apiMessage: message) { + if case let .Id(id) = message.id { + topicId = Int64(id.id) + } + } + default: + break + } + } + + if let topicId = topicId { + return .single(topicId) + } else { + return .fail(.generic) + } } } - - self.createTopicDisposable.set((signal |> deliverOnMainQueue).start(next: { [weak self] _ in - guard let strongSelf = self else { - return - } - let _ = _internal_loadMessageHistoryThreads(account: strongSelf.account, peerId: strongSelf.peerId).start() - })) } } public struct Item: Equatable { public var id: Int64 - public var info: EngineMessageHistoryThreads.Info + public var info: EngineMessageHistoryThread.Info public var index: MessageIndex public var topMessage: EngineMessage? init( id: Int64, - info: EngineMessageHistoryThreads.Info, + info: EngineMessageHistoryThread.Info, index: MessageIndex, topMessage: EngineMessage? ) { @@ -311,9 +345,19 @@ public final class ForumChannelTopics { }) } - public func createTopic(title: String) { - self.impl.with { impl in - impl.createTopic(title: title) + public func createTopic(title: String) -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.createTopic(title: title).start(next: { value in + subscriber.putNext(value) + }, error: { error in + subscriber.putError(error) + }, completed: { + subscriber.putCompletion() + })) + } + return disposable } } } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 01fdb26065..f16fb9db35 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1586,14 +1586,15 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } else { for peerId in channelsToPoll.union(missingUpdatesFromChannels) { if let peer = updatedState.peers[peerId] { - pollChannelSignals.append(pollChannel(network: network, peer: peer, state: updatedState.branch())) + pollChannelSignals.append(pollChannel(postbox: postbox, network: network, peer: peer, state: updatedState.branch())) } else { Logger.shared.log("State", "can't poll channel \(peerId): no peer found") } } } - return combineLatest(pollChannelSignals) |> mapToSignal { states -> Signal in + return combineLatest(pollChannelSignals) + |> mapToSignal { states -> Signal in var finalState: AccountMutableState = updatedState var hadError = false @@ -1608,16 +1609,207 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } } } - return resolveAssociatedMessages(postbox: postbox, network: network, state: finalState) - |> mapToSignal { resultingState -> Signal in - return resolveMissingPeerChatInfos(network: network, state: resultingState) - |> map { resultingState, resolveError -> AccountFinalState in - return AccountFinalState(state: resultingState, shouldPoll: shouldPoll || hadError || resolveError, incomplete: missingUpdates, missingUpdatesFromChannels: Set(), discard: resolveError) + + return resolveForumThreads(postbox: postbox, network: network, state: finalState) + |> mapToSignal { finalState in + return resolveAssociatedMessages(postbox: postbox, network: network, state: finalState) + |> mapToSignal { resultingState -> Signal in + return resolveMissingPeerChatInfos(network: network, state: resultingState) + |> map { resultingState, resolveError -> AccountFinalState in + return AccountFinalState(state: resultingState, shouldPoll: shouldPoll || hadError || resolveError, incomplete: missingUpdates, missingUpdatesFromChannels: Set(), discard: resolveError) + } } } } } +func resolveForumThreads(postbox: Postbox, network: Network, state: AccountMutableState) -> Signal { + var forumThreadIds = Set() + + for operation in state.operations { + switch operation { + case let .AddMessages(messages, _): + for message in messages { + if let threadId = message.threadId { + if let channel = state.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { + forumThreadIds.insert(MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: Int32(clamping: threadId))) + } + } + } + default: + break + } + } + + if forumThreadIds.isEmpty { + return .single(state) + } else { + return postbox.transaction { transaction -> Signal in + var missingForumThreadIds: [PeerId: [Int32]] = [:] + for threadId in forumThreadIds { + if let _ = transaction.getMessageHistoryThreadInfo(peerId: threadId.peerId, threadId: Int64(threadId.id)) { + } else { + missingForumThreadIds[threadId.peerId, default: []].append(threadId.id) + } + } + + if missingForumThreadIds.isEmpty { + return .single(state) + } else { + var signals: [Signal<(PeerId, Api.messages.ForumTopics)?, NoError>] = [] + for (peerId, threadIds) in missingForumThreadIds { + guard let inputChannel = transaction.getPeer(peerId).flatMap(apiInputChannel) else { + Logger.shared.log("State", "can't fetch thread infos \(threadIds) for peer \(peerId): can't create inputChannel") + continue + } + let signal = network.request(Api.functions.channels.getForumTopicsByID(channel: inputChannel, topics: threadIds)) + |> map { result -> (PeerId, Api.messages.ForumTopics)? in + return (peerId, result) + } + |> `catch` { _ -> Signal<(PeerId, Api.messages.ForumTopics)?, NoError> in + return .single(nil) + } + signals.append(signal) + } + + return combineLatest(signals) + |> map { results -> AccountMutableState in + var state = state + + var storeMessages: [StoreMessage] = [] + + for maybeResult in results { + if let (peerId, result) = maybeResult { + switch result { + case let .forumTopics(_, _, topics, messages, chats, users, pts): + state.mergeChats(chats) + state.mergeUsers(users) + + for message in messages { + if let message = StoreMessage(apiMessage: message) { + storeMessages.append(message) + } + } + + for topic in topics { + switch topic { + case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount): + state.operations.append(.ResetForumTopic(topicId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id), data: MessageHistoryThreadData( + info: EngineMessageHistoryThread.Info( + title: title, + icon: iconEmojiId + ), + incomingUnreadCount: unreadCount, + maxIncomingReadId: readInboxMaxId, + maxKnownMessageId: topMessage, + maxOutgoingReadId: readOutboxMaxId + ), pts: pts)) + } + } + } + } + } + + state.addMessages(storeMessages, location: .Random) + + return state + } + } + } + |> switchToLatest + } +} + +func resolveForumThreads(postbox: Postbox, network: Network, fetchedChatList: FetchedChatList) -> Signal { + var forumThreadIds = Set() + + for message in fetchedChatList.storeMessages { + if let threadId = message.threadId { + if let channel = fetchedChatList.peers.first(where: { $0.id == message.id.peerId }) as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { + forumThreadIds.insert(MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: Int32(clamping: threadId))) + } + } + } + + if forumThreadIds.isEmpty { + return .single(fetchedChatList) + } else { + return postbox.transaction { transaction -> Signal in + var missingForumThreadIds: [PeerId: [Int32]] = [:] + for threadId in forumThreadIds { + if let _ = transaction.getMessageHistoryThreadInfo(peerId: threadId.peerId, threadId: Int64(threadId.id)) { + } else { + missingForumThreadIds[threadId.peerId, default: []].append(threadId.id) + } + } + + if missingForumThreadIds.isEmpty { + return .single(fetchedChatList) + } else { + var signals: [Signal<(PeerId, Api.messages.ForumTopics)?, NoError>] = [] + for (peerId, threadIds) in missingForumThreadIds { + guard let inputChannel = fetchedChatList.peers.first(where: { $0.id == peerId }).flatMap(apiInputChannel) else { + Logger.shared.log("resolveForumThreads", "can't fetch thread infos \(threadIds) for peer \(peerId): can't create inputChannel") + continue + } + let signal = network.request(Api.functions.channels.getForumTopicsByID(channel: inputChannel, topics: threadIds)) + |> map { result -> (PeerId, Api.messages.ForumTopics)? in + return (peerId, result) + } + |> `catch` { _ -> Signal<(PeerId, Api.messages.ForumTopics)?, NoError> in + return .single(nil) + } + signals.append(signal) + } + + return combineLatest(signals) + |> map { results -> FetchedChatList in + var fetchedChatList = fetchedChatList + + for maybeResult in results { + if let (peerId, result) = maybeResult { + switch result { + case let .forumTopics(_, _, topics, messages, chats, users, _): + fetchedChatList.peers.append(contentsOf: chats.compactMap { chat in + return parseTelegramGroupOrChannel(chat: chat) + }) + fetchedChatList.peers.append(contentsOf: users.compactMap { user in + return TelegramUser(user: user) + }) + + for message in messages { + if let message = StoreMessage(apiMessage: message) { + fetchedChatList.storeMessages.append(message) + } + } + + for topic in topics { + switch topic { + case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount): + fetchedChatList.threadInfos[MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)] = MessageHistoryThreadData( + info: EngineMessageHistoryThread.Info( + title: title, + icon: iconEmojiId + ), + incomingUnreadCount: unreadCount, + maxIncomingReadId: readInboxMaxId, + maxKnownMessageId: topMessage, + maxOutgoingReadId: readOutboxMaxId + ) + } + } + } + } + } + + return fetchedChatList + } + } + } + |> switchToLatest + } +} + func extractEmojiFileIds(message: StoreMessage, fileIds: inout Set) { for attribute in message.attributes { if let attribute = attribute as? TextEntitiesMessageAttribute { @@ -1673,6 +1865,9 @@ private func resolveAssociatedMessages(postbox: Postbox, network: Network, state let missingMessageIds = state.referencedMessageIds.subtracting(state.storedMessages) if missingMessageIds.isEmpty { return resolveUnknownEmojiFiles(postbox: postbox, source: .network(network), messages: messagesFromOperations(state: state), reactions: reactionsFromState(state), result: state) + |> mapToSignal { state in + return resolveForumThreads(postbox: postbox, network: network, state: state) + } } else { var missingPeers = false let _ = missingPeers @@ -1735,6 +1930,9 @@ private func resolveAssociatedMessages(postbox: Postbox, network: Network, state } |> mapToSignal { updatedState -> Signal in return resolveUnknownEmojiFiles(postbox: postbox, source: .network(network), messages: messagesFromOperations(state: updatedState), reactions: reactionsFromState(updatedState), result: updatedState) + |> mapToSignal { state in + return resolveForumThreads(postbox: postbox, network: network, state: state) + } } } } @@ -1896,7 +2094,7 @@ func pollChannelOnce(postbox: Postbox, network: Network, peerId: PeerId, stateMa } } let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), peerIdsRequiringLocalChatState: Set(), channelStates: channelStates, peerChatInfos: peerChatInfos, locallyGeneratedMessageTimestamps: [:], cloudReadStates: [:], channelsToPollExplicitely: Set()), initialPeers: initialPeers, initialReferencedMessageIds: Set(), initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:]) - return pollChannel(network: network, peer: peer, state: initialState) + return pollChannel(postbox: postbox, network: network, peer: peer, state: initialState) |> mapToSignal { (finalState, _, timeout) -> Signal in return resolveAssociatedMessages(postbox: postbox, network: network, state: finalState) |> mapToSignal { resultingState -> Signal in @@ -1950,7 +2148,7 @@ public func standalonePollChannelOnce(postbox: Postbox, network: Network, peerId } } let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), peerIdsRequiringLocalChatState: Set(), channelStates: channelStates, peerChatInfos: peerChatInfos, locallyGeneratedMessageTimestamps: [:], cloudReadStates: [:], channelsToPollExplicitely: Set()), initialPeers: initialPeers, initialReferencedMessageIds: Set(), initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:]) - return pollChannel(network: network, peer: peer, state: initialState) + return pollChannel(postbox: postbox, network: network, peer: peer, state: initialState) |> mapToSignal { (finalState, _, timeout) -> Signal in return resolveAssociatedMessages(postbox: postbox, network: network, state: finalState) |> mapToSignal { resultingState -> Signal in @@ -2132,7 +2330,7 @@ private func resetChannels(postbox: Postbox, network: Network, peers: [Peer], st } } -private func pollChannel(network: Network, peer: Peer, state: AccountMutableState) -> Signal<(AccountMutableState, Bool, Int32?), NoError> { +private func pollChannel(postbox: Postbox, network: Network, peer: Peer, state: AccountMutableState) -> Signal<(AccountMutableState, Bool, Int32?), NoError> { if let inputChannel = apiInputChannel(peer) { let limit: Int32 limit = 100 @@ -2154,161 +2352,191 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat } }) |> retryRequest - |> map { difference -> (AccountMutableState, Bool, Int32?) in - var updatedState = state - var apiTimeout: Int32? - if let difference = difference { - switch difference { - case let .channelDifference(_, pts, timeout, newMessages, otherUpdates, chats, users): - apiTimeout = timeout - let channelPts: Int32 - if let _ = updatedState.channelStates[peer.id] { - channelPts = pts - } else { - channelPts = pts - } - updatedState.updateChannelState(peer.id, pts: channelPts) - - updatedState.mergeChats(chats) - updatedState.mergeUsers(users) - - for apiMessage in newMessages { - if var message = StoreMessage(apiMessage: apiMessage) { - var attributes = message.attributes - attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) - message = message.withUpdatedAttributes(attributes) - - if let preCachedResources = apiMessage.preCachedResources { - for (resource, data) in preCachedResources { - updatedState.addPreCachedResource(resource, data: data) - } - } - updatedState.addMessages([message], location: .UpperHistoryBlock) - if case let .Id(id) = message.id { - updatedState.updateChannelSynchronizedUntilMessage(id.peerId, id: id.id) - } - } - } - for update in otherUpdates { - switch update { - case let .updateDeleteChannelMessages(_, messages, _, _): - let peerId = peer.id - updatedState.deleteMessages(messages.map({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })) - case let .updateEditChannelMessage(apiMessage, _, _): - if let message = StoreMessage(apiMessage: apiMessage), case let .Id(messageId) = message.id, messageId.peerId == peer.id { - if let preCachedResources = apiMessage.preCachedResources { - for (resource, data) in preCachedResources { - updatedState.addPreCachedResource(resource, data: data) - } - } - var attributes = message.attributes - attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) - updatedState.editMessage(messageId, message: message.withUpdatedAttributes(attributes)) - } else { - Logger.shared.log("State", "Invalid updateEditChannelMessage") - } - case let .updatePinnedChannelMessages(flags, channelId, messages, _, _): - let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)) - updatedState.updateMessagesPinned(ids: messages.map { id in - MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id) - }, pinned: (flags & (1 << 0)) != 0) - case let .updateChannelReadMessagesContents(_, messages): - updatedState.addReadMessagesContents((peer.id, messages)) - case let .updateChannelMessageViews(_, id, views): - updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views) - /*case let .updateChannelMessageForwards(_, id, views): - updatedState.addUpdateMessageForwardsCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views)*/ - case let .updateChannelWebPage(_, apiWebpage, _, _): - switch apiWebpage { - case let .webPageEmpty(id): - updatedState.updateMedia(MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), media: nil) - default: - if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage, url: nil) { - updatedState.updateMedia(webpage.webpageId, media: webpage) - } - } - case let .updateChannelAvailableMessages(_, minId): - let messageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: minId) - updatedState.updateMinAvailableMessage(messageId) - updatedState.updateCachedPeerData(peer.id, { current in - let previous: CachedChannelData - if let current = current as? CachedChannelData { - previous = current - } else { - previous = CachedChannelData() - } - return previous.withUpdatedMinAvailableMessageId(messageId) - }) - default: - break - } - } - case let .channelDifferenceEmpty(_, pts, timeout): - apiTimeout = timeout - - let channelPts: Int32 - if let _ = updatedState.channelStates[peer.id] { - channelPts = pts - } else { - channelPts = pts - } - updatedState.updateChannelState(peer.id, pts: channelPts) - case let .channelDifferenceTooLong(_, timeout, dialog, messages, chats, users): - apiTimeout = timeout - - var parameters: (peer: Api.Peer, pts: Int32, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32)? - - switch dialog { - case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, _, pts, _, _): - if let pts = pts { - parameters = (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount) - } - case .dialogFolder: - break - } - - if let (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount) = parameters { - updatedState.updateChannelState(peer.peerId, pts: pts) - updatedState.updateChannelInvalidationPts(peer.peerId, invalidationPts: pts) - - updatedState.mergeChats(chats) - updatedState.mergeUsers(users) - - updatedState.setNeedsHoleFromPreviousState(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, validateChannelPts: pts) - - for apiMessage in messages { - if var message = StoreMessage(apiMessage: apiMessage) { - var attributes = message.attributes - attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) - message = message.withUpdatedAttributes(attributes) - - if let preCachedResources = apiMessage.preCachedResources { - for (resource, data) in preCachedResources { - updatedState.addPreCachedResource(resource, data: data) - } - } - - let location: AddMessagesLocation - if case let .Id(id) = message.id, id.id == topMessage { - location = .UpperHistoryBlock - updatedState.updateChannelSynchronizedUntilMessage(id.peerId, id: id.id) - } else { - location = .Random - } - updatedState.addMessages([message], location: location) - } - } - - updatedState.resetReadState(peer.peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: nil) - - updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage)) - updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage)) - } else { - assertionFailure() - } - } + |> mapToSignal { difference -> Signal<(AccountMutableState, Bool, Int32?), NoError> in + guard let difference = difference else { + return .single((state, false, nil)) + } + + switch difference { + case let .channelDifference(_, pts, timeout, newMessages, otherUpdates, chats, users): + var updatedState = state + var apiTimeout: Int32? + + apiTimeout = timeout + let channelPts: Int32 + if let _ = updatedState.channelStates[peer.id] { + channelPts = pts + } else { + channelPts = pts + } + updatedState.updateChannelState(peer.id, pts: channelPts) + + updatedState.mergeChats(chats) + updatedState.mergeUsers(users) + + var forumThreadIds = Set() + + for apiMessage in newMessages { + if var message = StoreMessage(apiMessage: apiMessage) { + var attributes = message.attributes + attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) + message = message.withUpdatedAttributes(attributes) + + if let preCachedResources = apiMessage.preCachedResources { + for (resource, data) in preCachedResources { + updatedState.addPreCachedResource(resource, data: data) + } + } + updatedState.addMessages([message], location: .UpperHistoryBlock) + if case let .Id(id) = message.id { + updatedState.updateChannelSynchronizedUntilMessage(id.peerId, id: id.id) + + if let threadId = message.threadId { + if let channel = updatedState.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { + forumThreadIds.insert(MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: Int32(clamping: threadId))) + } + } + } + } + } + + for update in otherUpdates { + switch update { + case let .updateDeleteChannelMessages(_, messages, _, _): + let peerId = peer.id + updatedState.deleteMessages(messages.map({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })) + case let .updateEditChannelMessage(apiMessage, _, _): + if let message = StoreMessage(apiMessage: apiMessage), case let .Id(messageId) = message.id, messageId.peerId == peer.id { + if let preCachedResources = apiMessage.preCachedResources { + for (resource, data) in preCachedResources { + updatedState.addPreCachedResource(resource, data: data) + } + } + var attributes = message.attributes + attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) + updatedState.editMessage(messageId, message: message.withUpdatedAttributes(attributes)) + + if let threadId = message.threadId { + if let channel = updatedState.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { + forumThreadIds.insert(MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: Int32(clamping: threadId))) + } + } + } else { + Logger.shared.log("State", "Invalid updateEditChannelMessage") + } + case let .updatePinnedChannelMessages(flags, channelId, messages, _, _): + let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)) + updatedState.updateMessagesPinned(ids: messages.map { id in + MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id) + }, pinned: (flags & (1 << 0)) != 0) + case let .updateChannelReadMessagesContents(_, messages): + updatedState.addReadMessagesContents((peer.id, messages)) + case let .updateChannelMessageViews(_, id, views): + updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views) + case let .updateChannelWebPage(_, apiWebpage, _, _): + switch apiWebpage { + case let .webPageEmpty(id): + updatedState.updateMedia(MediaId(namespace: Namespaces.Media.CloudWebpage, id: id), media: nil) + default: + if let webpage = telegramMediaWebpageFromApiWebpage(apiWebpage, url: nil) { + updatedState.updateMedia(webpage.webpageId, media: webpage) + } + } + case let .updateChannelAvailableMessages(_, minId): + let messageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: minId) + updatedState.updateMinAvailableMessage(messageId) + updatedState.updateCachedPeerData(peer.id, { current in + let previous: CachedChannelData + if let current = current as? CachedChannelData { + previous = current + } else { + previous = CachedChannelData() + } + return previous.withUpdatedMinAvailableMessageId(messageId) + }) + default: + break + } + } + + return resolveForumThreads(postbox: postbox, network: network, state: updatedState) + |> map { updatedState -> (AccountMutableState, Bool, Int32?) in + return (updatedState, true, apiTimeout) + } + case let .channelDifferenceEmpty(_, pts, timeout): + var updatedState = state + var apiTimeout: Int32? + + apiTimeout = timeout + + let channelPts: Int32 + if let _ = updatedState.channelStates[peer.id] { + channelPts = pts + } else { + channelPts = pts + } + updatedState.updateChannelState(peer.id, pts: channelPts) + + return .single((updatedState, true, apiTimeout)) + case let .channelDifferenceTooLong(_, timeout, dialog, messages, chats, users): + var updatedState = state + var apiTimeout: Int32? + + apiTimeout = timeout + + var parameters: (peer: Api.Peer, pts: Int32, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32)? + + switch dialog { + case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, _, pts, _, _): + if let pts = pts { + parameters = (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount) + } + case .dialogFolder: + break + } + + if let (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount) = parameters { + updatedState.updateChannelState(peer.peerId, pts: pts) + updatedState.updateChannelInvalidationPts(peer.peerId, invalidationPts: pts) + + updatedState.mergeChats(chats) + updatedState.mergeUsers(users) + + updatedState.setNeedsHoleFromPreviousState(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, validateChannelPts: pts) + + for apiMessage in messages { + if var message = StoreMessage(apiMessage: apiMessage) { + var attributes = message.attributes + attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) + message = message.withUpdatedAttributes(attributes) + + if let preCachedResources = apiMessage.preCachedResources { + for (resource, data) in preCachedResources { + updatedState.addPreCachedResource(resource, data: data) + } + } + + let location: AddMessagesLocation + if case let .Id(id) = message.id, id.id == topMessage { + location = .UpperHistoryBlock + updatedState.updateChannelSynchronizedUntilMessage(id.peerId, id: id.id) + } else { + location = .Random + } + updatedState.addMessages([message], location: location) + } + } + + updatedState.resetReadState(peer.peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: nil) + + updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage)) + updatedState.resetMessageTagSummary(peer.peerId, tag: .unseenReaction, namespace: Namespaces.Message.Cloud, count: unreadReactionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: topMessage)) + } else { + assertionFailure() + } + + return .single((updatedState, true, apiTimeout)) } - return (updatedState, difference != nil, apiTimeout) } } else { Logger.shared.log("State", "can't poll channel \(peer.id): can't create inputChannel") @@ -2403,7 +2631,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddScheduledMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -2631,12 +2859,48 @@ func replayFinalState( for message in messages { if case let .Id(id) = message.id { if let threadId = message.threadId { + for media in message.media { + if let action = media as? TelegramMediaAction { + switch action.action { + case let .topicEditTitle(title): + if var data = transaction.getMessageHistoryThreadInfo(peerId: id.peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) { + data.info = EngineMessageHistoryThread.Info(title: title, icon: data.info.icon) + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: id.peerId, threadId: threadId, info: entry) + } + } + case let .topicEditIcon(fileId): + if var data = transaction.getMessageHistoryThreadInfo(peerId: id.peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) { + data.info = EngineMessageHistoryThread.Info(title: data.info.title, icon: fileId == 0 ? nil : fileId) + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: id.peerId, threadId: threadId, info: entry) + } + } + default: + break + } + } + } + let messageThreadId = makeThreadIdMessageId(peerId: message.id.peerId, threadId: threadId) if id.peerId.namespace == Namespaces.Peer.CloudChannel { if !transaction.messageExists(id: id) { addMessageThreadStatsDifference(threadMessageId: messageThreadId, remove: 0, addedMessagePeer: message.authorId, addedMessageId: id, isOutgoing: !message.flags.contains(.Incoming)) } } + + if message.flags.contains(.Incoming) { + if var data = transaction.getMessageHistoryThreadInfo(peerId: id.peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) { + if id.id >= data.maxKnownMessageId { + data.maxKnownMessageId = id.id + data.incomingUnreadCount += 1 + + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: id.peerId, threadId: threadId, info: entry) + } + } + } + } } } } @@ -2885,7 +3149,6 @@ func replayFinalState( } case .ReadGroupFeedInbox: break - //transaction.applyGroupFeedReadMaxIndex(groupId: groupId, index: index) case let .UpdateReadThread(threadMessageId, readMaxId, isIncoming, mainChannelMessage): if isIncoming { if let currentId = updatedIncomingThreadReadStates[threadMessageId] { @@ -2895,6 +3158,23 @@ func replayFinalState( } else { updatedIncomingThreadReadStates[threadMessageId] = readMaxId } + if let channel = transaction.getPeer(threadMessageId.peerId) as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { + if var data = transaction.getMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id))?.get(MessageHistoryThreadData.self) { + if readMaxId > data.maxIncomingReadId { + if let toIndex = transaction.getMessage(MessageId(peerId: threadMessageId.peerId, namespace: threadMessageId.namespace, id: readMaxId))?.index { + if let count = transaction.getThreadMessageCount(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), namespace: threadMessageId.namespace, fromIdExclusive: data.maxIncomingReadId, toIndex: toIndex) { + data.incomingUnreadCount = max(0, data.incomingUnreadCount - Int32(count)) + } + } + + data.maxKnownMessageId = max(data.maxKnownMessageId, readMaxId) + + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), info: entry) + } + } + } + } if let mainChannelMessage = mainChannelMessage { transaction.updateMessage(mainChannelMessage, update: { currentMessage in let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) @@ -2919,6 +3199,17 @@ func replayFinalState( } else { updatedOutgoingThreadReadStates[threadMessageId] = readMaxId } + if let channel = transaction.getPeer(threadMessageId.peerId) as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isForum) { + if var data = transaction.getMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id))?.get(MessageHistoryThreadData.self) { + if readMaxId >= data.maxOutgoingReadId { + data.maxOutgoingReadId = readMaxId + + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), info: entry) + } + } + } + } } case let .ResetReadState(peerId, namespace, maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread): var markedUnreadValue: Bool = false @@ -3522,6 +3813,15 @@ func replayFinalState( return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media)) }) + case let .ResetForumTopic(topicId, data, pts): + if finalState.state.resetForumTopicLists[topicId.peerId] == nil { + let _ = pts + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: topicId.peerId, threadId: Int64(topicId.id), info: entry) + } else { + assertionFailure() + } + } } } diff --git a/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift b/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift index 252e688601..f60ae21e00 100644 --- a/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift +++ b/submodules/TelegramCore/Sources/State/ChatHistoryPreloadManager.swift @@ -287,10 +287,10 @@ final class ChatHistoryPreloadManager { self.canPreloadHistoryDisposable = (networkState |> map { state -> Bool in switch state { - case .online: - return true - default: - return false + case .online: + return true + default: + return false } } |> distinctUntilChanged @@ -298,6 +298,13 @@ final class ChatHistoryPreloadManager { guard let strongSelf = self, strongSelf.canPreloadHistoryValue != value else { return } + + #if DEBUG + if "".isEmpty { + return + } + #endif + strongSelf.canPreloadHistoryValue = value if value { for i in 0 ..< min(3, strongSelf.entries.count) { diff --git a/submodules/TelegramCore/Sources/State/FetchChatList.swift b/submodules/TelegramCore/Sources/State/FetchChatList.swift index ccf46bec66..767a23cbbd 100644 --- a/submodules/TelegramCore/Sources/State/FetchChatList.swift +++ b/submodules/TelegramCore/Sources/State/FetchChatList.swift @@ -187,22 +187,23 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], } struct FetchedChatList { - let chatPeerIds: [PeerId] - let peers: [Peer] - let peerPresences: [PeerId: Api.User] - let notificationSettings: [PeerId: PeerNotificationSettings] - let readStates: [PeerId: [MessageId.Namespace: PeerReadState]] - let mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] - let reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] - let channelStates: [PeerId: Int32] - let storeMessages: [StoreMessage] - let topMessageIds: [PeerId: MessageId] + var chatPeerIds: [PeerId] + var peers: [Peer] + var peerPresences: [PeerId: Api.User] + var notificationSettings: [PeerId: PeerNotificationSettings] + var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] + var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] + var reactionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] + var channelStates: [PeerId: Int32] + var storeMessages: [StoreMessage] + var topMessageIds: [PeerId: MessageId] - let lowerNonPinnedIndex: MessageIndex? + var lowerNonPinnedIndex: MessageIndex? - let pinnedItemIds: [PeerId]? - let folderSummaries: [PeerGroupId: PeerGroupUnreadCountersSummary] - let peerGroupIds: [PeerId: PeerGroupId] + var pinnedItemIds: [PeerId]? + var folderSummaries: [PeerGroupId: PeerGroupUnreadCountersSummary] + var peerGroupIds: [PeerId: PeerGroupId] + var threadInfos: [MessageId: MessageHistoryThreadData] } func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLocation, upperBound: MessageIndex, hash: Int64, limit: Int32) -> Signal { @@ -386,9 +387,18 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo pinnedItemIds: pinnedItemIds, folderSummaries: folderSummaries, - peerGroupIds: peerGroupIds + peerGroupIds: peerGroupIds, + threadInfos: [:] ) return resolveUnknownEmojiFiles(postbox: postbox, source: .network(network), messages: storeMessages, reactions: [], result: result) + |> mapToSignal { result in + if let result = result { + return resolveForumThreads(postbox: postbox, network: network, fetchedChatList: result) + |> map(Optional.init) + } else { + return .single(result) + } + } } } } diff --git a/submodules/TelegramCore/Sources/State/ForumChannelState.swift b/submodules/TelegramCore/Sources/State/ForumChannelState.swift new file mode 100644 index 0000000000..9a30829567 --- /dev/null +++ b/submodules/TelegramCore/Sources/State/ForumChannelState.swift @@ -0,0 +1,3 @@ +import Foundation + + diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index a5f80f53be..e13da94172 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -735,6 +735,12 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId return updated }) + for (threadMessageId, data) in fetchedChats.threadInfos { + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), info: entry) + } + } + updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: fetchedChats.peerPresences) transaction.updateCurrentPeerNotificationSettings(fetchedChats.notificationSettings) let _ = transaction.addMessages(fetchedChats.storeMessages, location: .UpperHistoryBlock) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift index d672e690ef..7ae60b1296 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -72,11 +72,12 @@ public final class EngineChatList: Equatable { public let readCounters: EnginePeerReadCounters? public let isMuted: Bool public let draft: Draft? - public let threadInfo: EngineMessageHistoryThreads.Info? + public let threadInfo: EngineMessageHistoryThread.Info? public let renderedPeer: EngineRenderedPeer public let presence: EnginePeer.Presence? public let hasUnseenMentions: Bool public let hasUnseenReactions: Bool + public let forumTopicTitle: String? public let hasFailed: Bool public let isContact: Bool @@ -87,11 +88,12 @@ public final class EngineChatList: Equatable { readCounters: EnginePeerReadCounters?, isMuted: Bool, draft: Draft?, - threadInfo: EngineMessageHistoryThreads.Info?, + threadInfo: EngineMessageHistoryThread.Info?, renderedPeer: EngineRenderedPeer, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, + forumTopicTitle: String?, hasFailed: Bool, isContact: Bool ) { @@ -106,6 +108,7 @@ public final class EngineChatList: Equatable { self.presence = presence self.hasUnseenMentions = hasUnseenMentions self.hasUnseenReactions = hasUnseenReactions + self.forumTopicTitle = forumTopicTitle self.hasFailed = hasFailed self.isContact = isContact } @@ -144,6 +147,9 @@ public final class EngineChatList: Equatable { if lhs.hasUnseenReactions != rhs.hasUnseenReactions { return false } + if lhs.forumTopicTitle != rhs.forumTopicTitle { + return false + } if lhs.hasFailed != rhs.hasFailed { return false } @@ -351,7 +357,7 @@ public extension EngineChatList.RelativePosition { extension EngineChatList.Item { convenience init?(_ entry: ChatListEntry) { switch entry { - case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedState, renderedPeer, presence, tagSummaryInfo, hasFailed, isContact): + case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedState, renderedPeer, presence, tagSummaryInfo, forumTopicData, hasFailed, isContact): var draft: EngineChatList.Draft? if let embeddedState = embeddedState, let _ = embeddedState.overrideChatTimestamp { if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) { @@ -377,6 +383,11 @@ extension EngineChatList.Item { hasUnseenReactions = (info.tagSummaryCount ?? 0) != 0// > (info.actionsSummaryCount ?? 0) } + var forumTopicTitle: String? + if let forumTopicData = forumTopicData?.get(MessageHistoryThreadData.self) { + forumTopicTitle = forumTopicData.info.title + } + self.init( id: .chatList(index.messageIndex.id.peerId), index: .chatList(index), @@ -389,6 +400,7 @@ extension EngineChatList.Item { presence: presence.flatMap(EnginePeer.Presence.init), hasUnseenMentions: hasUnseenMentions, hasUnseenReactions: hasUnseenReactions, + forumTopicTitle: forumTopicTitle, hasFailed: hasFailed, isContact: isContact ) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index f11c6beddf..5da63daeb9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -332,6 +332,20 @@ private class ReplyThreadHistoryContextImpl { let account = self.account let _ = (self.account.postbox.transaction { transaction -> (Api.InputPeer?, MessageId?, Int?) in + if var data = transaction.getMessageHistoryThreadInfo(peerId: messageId.peerId, threadId: Int64(messageId.id))?.get(MessageHistoryThreadData.self) { + if messageIndex.id.id >= data.maxIncomingReadId { + if let count = transaction.getThreadMessageCount(peerId: messageId.peerId, threadId: Int64(messageId.id), namespace: messageId.namespace, fromIdExclusive: data.maxIncomingReadId, toIndex: messageIndex) { + data.incomingUnreadCount = max(0, data.incomingUnreadCount - Int32(count)) + } + + data.maxKnownMessageId = max(data.maxKnownMessageId, messageIndex.id.id) + + if let entry = CodableEntry(data) { + transaction.setMessageHistoryThreadInfo(peerId: messageId.peerId, threadId: Int64(messageId.id), info: entry) + } + } + } + if let message = transaction.getMessage(messageId) { for attribute in message.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { @@ -574,8 +588,10 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa return .fail(.generic) } - let replyInfo = Promise() - replyInfo.set(.single(nil)) + let replyInfo = Promise() + replyInfo.set(account.postbox.transaction { transaction -> MessageHistoryThreadData? in + return transaction.getMessageHistoryThreadInfo(peerId: messageId.peerId, threadId: Int64(messageId.id))?.get(MessageHistoryThreadData.self) + }) let remoteDiscussionMessageSignal: Signal = account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: messageId.id)) |> map(Optional.init) @@ -664,12 +680,22 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa } let discussionMessageSignal = (replyInfo.get() |> take(1) - |> mapToSignal { replyInfo -> Signal in - guard let replyInfo = replyInfo else { + |> mapToSignal { threadData -> Signal in + guard let threadData = threadData else { return .single(nil) } return account.postbox.transaction { transaction -> DiscussionMessage? in - var foundDiscussionMessageId: MessageId? + return DiscussionMessage( + messageId: messageId, + channelMessageId: nil, + isChannelPost: false, + maxMessage: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxKnownMessageId), + maxReadIncomingMessageId: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxIncomingReadId), + maxReadOutgoingMessageId: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxOutgoingReadId), + unreadCount: Int(threadData.incomingUnreadCount) + ) + + /*var foundDiscussionMessageId: MessageId? transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in for attribute in attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { @@ -696,7 +722,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa maxReadIncomingMessageId: replyInfo.maxReadIncomingMessageId, maxReadOutgoingMessageId: nil, unreadCount: 0 - ) + )*/ } }) |> mapToSignal { result -> Signal in @@ -718,12 +744,13 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa let preloadedHistoryPosition: Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, Anchor, MessageId?), FetchChannelReplyThreadMessageError> = replyInfo.get() |> take(1) |> castError(FetchChannelReplyThreadMessageError.self) - |> mapToSignal { replyInfo -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, Anchor, MessageId?), FetchChannelReplyThreadMessageError> in - if let replyInfo = replyInfo { - return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, Anchor, MessageId?) in + |> mapToSignal { threadData -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, Anchor, MessageId?), FetchChannelReplyThreadMessageError> in + if let _ = threadData, !"".isEmpty { + return .fail(.generic) + /*return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, Anchor, MessageId?) in var threadInput: FetchMessageHistoryHoleThreadInput = .threadFromChannel(channelMessageId: messageId) var threadMessageId: MessageId? - transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in + transaction.scanMessageAttributes(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in for attribute in attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { if attribute.messageId == messageId { @@ -745,7 +772,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa } return (threadInput, replyInfo.commentsPeerId, threadMessageId, anchor, replyInfo.maxMessageId) } - |> castError(FetchChannelReplyThreadMessageError.self) + |> castError(FetchChannelReplyThreadMessageError.self)*/ } else { return discussionMessage.get() |> take(1) @@ -770,7 +797,10 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa } } - let preloadedHistory = preloadedHistoryPosition + let preloadedHistory: Signal<(FetchMessageHistoryHoleResult?, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> + + + preloadedHistory = preloadedHistoryPosition |> mapToSignal { peerInput, commentsPeerId, threadMessageId, anchor, maxMessageId -> Signal<(FetchMessageHistoryHoleResult?, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in guard let maxMessageId = maxMessageId else { return .single((FetchMessageHistoryHoleResult(removedIndices: IndexSet(integersIn: 1 ..< Int(Int32.max - 1)), strictRemovedIndices: IndexSet(), actualPeerId: nil, actualThreadId: nil, ids: []), .automatic)) @@ -814,7 +844,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa anchor: inputAnchor, namespaces: .not(Namespaces.Message.allScheduled) ) - if !testView.isLoading { + if !testView.isLoading || transaction.getMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id)) != nil { let initialAnchor: ChatReplyThreadMessage.Anchor switch anchor { case .lowerBound: diff --git a/submodules/TelegramIntents/Sources/TelegramIntents.swift b/submodules/TelegramIntents/Sources/TelegramIntents.swift index 3a6dbd83c6..fb11b19cb6 100644 --- a/submodules/TelegramIntents/Sources/TelegramIntents.swift +++ b/submodules/TelegramIntents/Sources/TelegramIntents.swift @@ -140,7 +140,7 @@ public func donateSendMessageIntent(account: Account, sharedContext: SharedAccou if peer.id == account.peerId { signals.append(.single((peer, subject, savedMessagesAvatar))) } else { - let peerAndAvatar = (peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil)) + let peerAndAvatar = (peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.smallProfileImage, clipStyle: .none) ?? .single(nil)) |> map { imageVersions -> (Peer, SendMessageIntentSubject, UIImage?) in var avatarImage: UIImage? if let image = imageVersions?.0 { diff --git a/submodules/TelegramUI/Components/ForumTopicListScreen/Sources/ForumTopicListScreen.swift b/submodules/TelegramUI/Components/ForumTopicListScreen/Sources/ForumTopicListScreen.swift index 31a6d4066e..05f622b17a 100644 --- a/submodules/TelegramUI/Components/ForumTopicListScreen/Sources/ForumTopicListScreen.swift +++ b/submodules/TelegramUI/Components/ForumTopicListScreen/Sources/ForumTopicListScreen.swift @@ -407,7 +407,6 @@ public final class ForumTopicListScreen: ViewController { } func createPressed() { - self.forumChannelContext.createTopic(title: "Topic#\(Int.random(in: 0 ..< 100000))") } private func update(transition: Transition) { diff --git a/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift b/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift index b95c727f54..7762f7aaa4 100644 --- a/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift +++ b/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift @@ -8,7 +8,7 @@ import WebPBinding public func cacheLottieAnimation(data: Data, width: Int, height: Int, keyframeOnly: Bool, writer: AnimationCacheItemWriter, firstFrameOnly: Bool) { let work: () -> Void = { - let decompressedData = TGGUnzipData(data, 1 * 1024 * 1024) ?? data + let decompressedData = TGGUnzipData(data, 2 * 1024 * 1024) ?? data guard let animation = LottieInstance(data: decompressedData, fitzModifier: .none, colorReplacements: nil, cacheKey: "") else { writer.finish() return diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f0ba19dfd8..3826c93ab3 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -8969,7 +8969,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !(peer is TelegramGroup || peer is TelegramChannel) { return } - presentAddMembers(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, parentController: strongSelf, groupPeer: peer, selectAddMemberDisposable: strongSelf.selectAddMemberDisposable, addMemberDisposable: strongSelf.addMemberDisposable) + presentAddMembersImpl(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, parentController: strongSelf, groupPeer: peer, selectAddMemberDisposable: strongSelf.selectAddMemberDisposable, addMemberDisposable: strongSelf.addMemberDisposable) }, presentGigagroupHelp: { [weak self] in if let strongSelf = self { strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Conversation_GigagroupDescription), elevatedLayout: false, action: { _ in return true }), in: .current) @@ -14422,7 +14422,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G present(statusController, nil) } - let disposable = (fetchAndPreloadReplyThreadInfo(context: context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId), atMessageId: atMessageId) + let disposable = (fetchAndPreloadReplyThreadInfo(context: context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId), atMessageId: atMessageId, preload: true) |> deliverOnMainQueue).start(next: { [weak statusController] result in if displayModalProgress { statusController?.dismiss() diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index c41b2aaeed..cdcd4e4a5b 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -336,7 +336,7 @@ enum ReplyThreadSubject { case groupMessage(MessageId) } -func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject, atMessageId: MessageId?) -> Signal { +func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject, atMessageId: MessageId?, preload: Bool) -> Signal { let message: Signal switch subject { case .channelPost(let messageId), .groupMessage(let messageId): @@ -380,42 +380,52 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea )) } - let preloadSignal = preloadedChatHistoryViewForLocation( - input, - context: context, - chatLocation: .replyThread(message: replyThreadMessage), - subject: nil, - chatLocationContextHolder: chatLocationContextHolder, - fixedCombinedReadStates: nil, - tagMask: nil, - additionalData: [] - ) - return preloadSignal - |> map { historyView -> Bool? in - switch historyView { - case .Loading: - return nil - case let .HistoryView(view, _, _, _, _, _, _): - return view.entries.isEmpty + if preload { + let preloadSignal = preloadedChatHistoryViewForLocation( + input, + context: context, + chatLocation: .replyThread(message: replyThreadMessage), + subject: nil, + chatLocationContextHolder: chatLocationContextHolder, + fixedCombinedReadStates: nil, + tagMask: nil, + additionalData: [] + ) + return preloadSignal + |> map { historyView -> Bool? in + switch historyView { + case .Loading: + return nil + case let .HistoryView(view, _, _, _, _, _, _): + return view.entries.isEmpty + } } - } - |> mapToSignal { value -> Signal in - if let value = value { - return .single(value) - } else { - return .complete() + |> mapToSignal { value -> Signal in + if let value = value { + return .single(value) + } else { + return .complete() + } } - } - |> take(1) - |> map { isEmpty -> ReplyThreadInfo in - return ReplyThreadInfo( + |> take(1) + |> map { isEmpty -> ReplyThreadInfo in + return ReplyThreadInfo( + message: replyThreadMessage, + isChannelPost: replyThreadMessage.isChannelPost, + isEmpty: isEmpty, + scrollToLowerBoundMessage: scrollToLowerBoundMessage, + contextHolder: chatLocationContextHolder + ) + } + |> castError(FetchChannelReplyThreadMessageError.self) + } else { + return .single(ReplyThreadInfo( message: replyThreadMessage, isChannelPost: replyThreadMessage.isChannelPost, - isEmpty: isEmpty, + isEmpty: false, scrollToLowerBoundMessage: scrollToLowerBoundMessage, contextHolder: chatLocationContextHolder - ) + )) } - |> castError(FetchChannelReplyThreadMessageError.self) } } diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 06212a8476..3ad78aa314 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -99,7 +99,8 @@ private enum ChatListSearchEntry: Comparable, Identifiable { promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: true, - hasFailedMessages: false + hasFailedMessages: false, + forumThreadTitle: nil ), editing: false, hasActiveRevealControls: false, @@ -239,7 +240,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe return } switch item.content { - case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _): + case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _): if let message = messages.first { let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index 24b2eabd23..1939963b76 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -225,7 +225,7 @@ public func isOverlayControllerForChatNotificationOverlayPresentation(_ controll } public func navigateToForumThreadImpl(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, navigationController: NavigationController) -> Signal { - return fetchAndPreloadReplyThreadInfo(context: context, subject: .groupMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))), atMessageId: nil) + return fetchAndPreloadReplyThreadInfo(context: context, subject: .groupMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))), atMessageId: nil, preload: false) |> deliverOnMainQueue |> beforeNext { [weak context, weak navigationController] result in guard let context = context, let navigationController = navigationController else { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index ce6d41eb5c..3df31790ec 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -6359,7 +6359,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return } - presentAddMembers(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, parentController: controller, groupPeer: groupPeer, selectAddMemberDisposable: self.selectAddMemberDisposable, addMemberDisposable: self.addMemberDisposable) + presentAddMembersImpl(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, parentController: controller, groupPeer: groupPeer, selectAddMemberDisposable: self.selectAddMemberDisposable, addMemberDisposable: self.addMemberDisposable) } private func openQrCode() { @@ -8828,7 +8828,7 @@ private final class PeerInfoContextReferenceContentSource: ContextReferenceConte } } -func presentAddMembers(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { +func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { let members: Promise<[PeerId]> = Promise() if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { /*var membersDisposable: Disposable? diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index a763117ccd..3c3fd02ec5 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1278,6 +1278,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { return PeerSelectionControllerImpl(params) } + public func openAddPeerMembers(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { + return presentAddMembersImpl(context: context, updatedPresentationData: updatedPresentationData, parentController: parentController, groupPeer: groupPeer, selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable) + } + public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, isCentered: Bool) -> ListViewItem { let controllerInteraction: ChatControllerInteraction diff --git a/submodules/WatchBridge/Sources/WatchBridge.swift b/submodules/WatchBridge/Sources/WatchBridge.swift index 47f77d9d1f..805dc7541c 100644 --- a/submodules/WatchBridge/Sources/WatchBridge.swift +++ b/submodules/WatchBridge/Sources/WatchBridge.swift @@ -325,7 +325,7 @@ func makeBridgeMedia(message: Message, strings: PresentationStrings, chatPeer: P } func makeBridgeChat(_ entry: ChatListEntry, strings: PresentationStrings) -> (TGBridgeChat, [Int64 : TGBridgeUser])? { - if case let .MessageEntry(index, messages, readState, _, _, renderedPeer, _, _, hasFailed, _) = entry { + if case let .MessageEntry(index, messages, readState, _, _, renderedPeer, _, _, _, hasFailed, _) = entry { guard index.messageIndex.id.peerId.namespace != Namespaces.Peer.SecretChat else { return nil } From 4f973162ccbc8324f2b4c01ef4a696b6b21a03da Mon Sep 17 00:00:00 2001 From: Mike Renoir <> Date: Mon, 3 Oct 2022 15:03:53 +0400 Subject: [PATCH 3/3] update layer + multiply usernames for channels and users --- submodules/TelegramApi/Sources/Api0.swift | 17 +- submodules/TelegramApi/Sources/Api20.swift | 38 +++ submodules/TelegramApi/Sources/Api21.swift | 134 +++++++---- submodules/TelegramApi/Sources/Api24.swift | 218 ++++++++---------- submodules/TelegramApi/Sources/Api25.swift | 122 ++++++++++ submodules/TelegramApi/Sources/Api29.swift | 117 +++++++++- submodules/TelegramApi/Sources/Api3.swift | 88 ++++--- submodules/TelegramApi/Sources/Api5.swift | 114 ++++----- submodules/TelegramApi/Sources/Api6.swift | 68 ++++++ .../Account/AccountIntermediateState.swift | 4 +- .../Sources/ApiUtils/ApiGroupOrChannel.swift | 13 +- .../Sources/ApiUtils/TelegramUser.swift | 14 +- .../ApiUtils/TelegramUserPresence.swift | 2 +- .../State/AccountStateManagementUtils.swift | 2 +- .../Sources/State/UpdatesApiUtils.swift | 4 +- .../SyncCore/SyncCore_CachedUserData.swift | 41 ++++ .../SyncCore/SyncCore_TelegramChannel.swift | 26 ++- .../SyncCore/SyncCore_TelegramUser.swift | 27 ++- .../TelegramEngine/Calls/GroupCalls.swift | 2 +- .../TelegramEngine/Messages/AdMessages.swift | 3 +- .../TelegramEngine/Messages/SendAsPeers.swift | 2 +- .../Peers/InactiveChannels.swift | 2 +- .../TelegramEngine/Peers/SearchPeers.swift | 2 +- .../UpdatedAccountPrivacySettings.swift | 2 +- submodules/TelegramCore/Sources/Themes.swift | 4 +- .../TelegramCore/Sources/UpdatePeers.swift | 4 +- .../Sources/Utils/PeerUtils.swift | 2 + 27 files changed, 759 insertions(+), 313 deletions(-) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index dc9b72b62f..8fcce13189 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -4,6 +4,7 @@ public enum Api { public enum auth {} public enum channels {} public enum contacts {} + public enum feed {} public enum help {} public enum messages {} public enum payments {} @@ -21,6 +22,7 @@ public enum Api { public enum bots {} public enum channels {} public enum contacts {} + public enum feed {} public enum folders {} public enum help {} public enum langpack {} @@ -143,7 +145,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) } dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) } dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) } - dict[-2107528095] = { return Api.Chat.parse_channel($0) } + dict[-2094689180] = { return Api.Chat.parse_channel($0) } dict[399807445] = { return Api.Chat.parse_channelForbidden($0) } dict[1103884886] = { return Api.Chat.parse_chat($0) } dict[693512293] = { return Api.Chat.parse_chatEmpty($0) } @@ -219,6 +221,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[179611673] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) } dict[-317687113] = { return Api.ExportedChatInvite.parse_chatInvitePublicJoinRequests($0) } dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) } + dict[1348066419] = { return Api.FeedPosition.parse_feedPosition($0) } dict[-207944868] = { return Api.FileHash.parse_fileHash($0) } dict[-11252123] = { return Api.Folder.parse_folder($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } @@ -838,6 +841,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1218471511] = { return Api.Update.parse_updateReadChannelOutbox($0) } dict[-78886548] = { return Api.Update.parse_updateReadFeaturedEmojiStickers($0) } dict[1461528386] = { return Api.Update.parse_updateReadFeaturedStickers($0) } + dict[1951948721] = { return Api.Update.parse_updateReadFeed($0) } dict[-1667805217] = { return Api.Update.parse_updateReadHistoryInbox($0) } dict[791617983] = { return Api.Update.parse_updateReadHistoryOutbox($0) } dict[1757493555] = { return Api.Update.parse_updateReadMessagesContents($0) } @@ -869,7 +873,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1886646706] = { return Api.UrlAuthResult.parse_urlAuthResultAccepted($0) } dict[-1445536993] = { return Api.UrlAuthResult.parse_urlAuthResultDefault($0) } dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } - dict[1570352622] = { return Api.User.parse_user($0) } + dict[-1885878744] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } dict[-994968513] = { return Api.UserFull.parse_userFull($0) } dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } @@ -880,6 +884,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[9203775] = { return Api.UserStatus.parse_userStatusOffline($0) } dict[-306628279] = { return Api.UserStatus.parse_userStatusOnline($0) } dict[-496024847] = { return Api.UserStatus.parse_userStatusRecently($0) } + dict[-1274595769] = { return Api.Username.parse_username($0) } dict[-567037804] = { return Api.VideoSize.parse_videoSize($0) } dict[-1539849235] = { return Api.WallPaper.parse_wallPaper($0) } dict[-528465642] = { return Api.WallPaper.parse_wallPaperNoFile($0) } @@ -956,6 +961,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1891070632] = { return Api.contacts.TopPeers.parse_topPeers($0) } dict[-1255369827] = { return Api.contacts.TopPeers.parse_topPeersDisabled($0) } dict[-567906571] = { return Api.contacts.TopPeers.parse_topPeersNotModified($0) } + dict[-587770695] = { return Api.feed.FeedMessages.parse_feedMessages($0) } + dict[-619039485] = { return Api.feed.FeedMessages.parse_feedMessagesNotModified($0) } dict[-860107216] = { return Api.help.AppUpdate.parse_appUpdate($0) } dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) } dict[-2016381538] = { return Api.help.CountriesList.parse_countriesList($0) } @@ -1280,6 +1287,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.ExportedMessageLink: _1.serialize(buffer, boxed) + case let _1 as Api.FeedPosition: + _1.serialize(buffer, boxed) case let _1 as Api.FileHash: _1.serialize(buffer, boxed) case let _1 as Api.Folder: @@ -1624,6 +1633,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.UserStatus: _1.serialize(buffer, boxed) + case let _1 as Api.Username: + _1.serialize(buffer, boxed) case let _1 as Api.VideoSize: _1.serialize(buffer, boxed) case let _1 as Api.WallPaper: @@ -1716,6 +1727,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.contacts.TopPeers: _1.serialize(buffer, boxed) + case let _1 as Api.feed.FeedMessages: + _1.serialize(buffer, boxed) case let _1 as Api.help.AppUpdate: _1.serialize(buffer, boxed) case let _1 as Api.help.CountriesList: diff --git a/submodules/TelegramApi/Sources/Api20.swift b/submodules/TelegramApi/Sources/Api20.swift index ce4602c150..1c4824fd64 100644 --- a/submodules/TelegramApi/Sources/Api20.swift +++ b/submodules/TelegramApi/Sources/Api20.swift @@ -740,6 +740,7 @@ public extension Api { case updateReadChannelOutbox(channelId: Int64, maxId: Int32) case updateReadFeaturedEmojiStickers case updateReadFeaturedStickers + case updateReadFeed(flags: Int32, filterId: Int32, maxPosition: Api.FeedPosition, unreadCount: Int32?, unreadMutedCount: Int32?) case updateReadHistoryInbox(flags: Int32, folderId: Int32?, peer: Api.Peer, maxId: Int32, stillUnreadCount: Int32, pts: Int32, ptsCount: Int32) case updateReadHistoryOutbox(peer: Api.Peer, maxId: Int32, pts: Int32, ptsCount: Int32) case updateReadMessagesContents(messages: [Int32], pts: Int32, ptsCount: Int32) @@ -1509,6 +1510,16 @@ public extension Api { buffer.appendInt32(1461528386) } + break + case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount): + if boxed { + buffer.appendInt32(1951948721) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(filterId, buffer: buffer, boxed: false) + maxPosition.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadCount!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadMutedCount!, buffer: buffer, boxed: false)} break case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): if boxed { @@ -1858,6 +1869,8 @@ public extension Api { return ("updateReadFeaturedEmojiStickers", []) case .updateReadFeaturedStickers: return ("updateReadFeaturedStickers", []) + case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount): + return ("updateReadFeed", [("flags", String(describing: flags)), ("filterId", String(describing: filterId)), ("maxPosition", String(describing: maxPosition)), ("unreadCount", String(describing: unreadCount)), ("unreadMutedCount", String(describing: unreadMutedCount))]) case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount): return ("updateReadHistoryInbox", [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("peer", String(describing: peer)), ("maxId", String(describing: maxId)), ("stillUnreadCount", String(describing: stillUnreadCount)), ("pts", String(describing: pts)), ("ptsCount", String(describing: ptsCount))]) case .updateReadHistoryOutbox(let peer, let maxId, let pts, let ptsCount): @@ -3439,6 +3452,31 @@ public extension Api { public static func parse_updateReadFeaturedStickers(_ reader: BufferReader) -> Update? { return Api.Update.updateReadFeaturedStickers } + public static func parse_updateReadFeed(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.FeedPosition? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } + var _4: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() } + var _5: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateReadFeed(flags: _1!, filterId: _2!, maxPosition: _3!, unreadCount: _4, unreadMutedCount: _5) + } + else { + return nil + } + } public static func parse_updateReadHistoryInbox(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api21.swift b/submodules/TelegramApi/Sources/Api21.swift index 9b04790c3e..1e7d3e5c54 100644 --- a/submodules/TelegramApi/Sources/Api21.swift +++ b/submodules/TelegramApi/Sources/Api21.swift @@ -452,16 +452,17 @@ public extension Api { } public extension Api { enum User: TypeConstructorDescription { - case user(flags: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?) + case user(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?, usernames: [Api.Username]?) case userEmpty(id: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .user(let flags, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus): + case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames): if boxed { - buffer.appendInt32(1570352622) + buffer.appendInt32(-1885878744) } serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(flags2, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt64(accessHash!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeString(firstName!, buffer: buffer, boxed: false)} @@ -479,6 +480,11 @@ public extension Api { if Int(flags) & Int(1 << 19) != 0 {serializeString(botInlinePlaceholder!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 22) != 0 {serializeString(langCode!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 30) != 0 {emojiStatus!.serialize(buffer, true)} + if Int(flags2) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(usernames!.count)) + for item in usernames! { + item.serialize(buffer, true) + }} break case .userEmpty(let id): if boxed { @@ -491,8 +497,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .user(let flags, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus): - return ("user", [("flags", String(describing: flags)), ("id", String(describing: id)), ("accessHash", String(describing: accessHash)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("username", String(describing: username)), ("phone", String(describing: phone)), ("photo", String(describing: photo)), ("status", String(describing: status)), ("botInfoVersion", String(describing: botInfoVersion)), ("restrictionReason", String(describing: restrictionReason)), ("botInlinePlaceholder", String(describing: botInlinePlaceholder)), ("langCode", String(describing: langCode)), ("emojiStatus", String(describing: emojiStatus))]) + case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames): + return ("user", [("flags", String(describing: flags)), ("flags2", String(describing: flags2)), ("id", String(describing: id)), ("accessHash", String(describing: accessHash)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("username", String(describing: username)), ("phone", String(describing: phone)), ("photo", String(describing: photo)), ("status", String(describing: status)), ("botInfoVersion", String(describing: botInfoVersion)), ("restrictionReason", String(describing: restrictionReason)), ("botInlinePlaceholder", String(describing: botInlinePlaceholder)), ("langCode", String(describing: langCode)), ("emojiStatus", String(describing: emojiStatus)), ("usernames", String(describing: usernames))]) case .userEmpty(let id): return ("userEmpty", [("id", String(describing: id))]) } @@ -501,56 +507,64 @@ public extension Api { public static func parse_user(_ reader: BufferReader) -> User? { var _1: Int32? _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() var _3: Int64? - if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt64() } - var _4: String? - if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } + _3 = reader.readInt64() + var _4: Int64? + if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt64() } var _5: String? - if Int(_1!) & Int(1 << 2) != 0 {_5 = parseString(reader) } + if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } var _6: String? - if Int(_1!) & Int(1 << 3) != 0 {_6 = parseString(reader) } + if Int(_1!) & Int(1 << 2) != 0 {_6 = parseString(reader) } var _7: String? - if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) } - var _8: Api.UserProfilePhoto? + if Int(_1!) & Int(1 << 3) != 0 {_7 = parseString(reader) } + var _8: String? + if Int(_1!) & Int(1 << 4) != 0 {_8 = parseString(reader) } + var _9: Api.UserProfilePhoto? if Int(_1!) & Int(1 << 5) != 0 {if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.UserProfilePhoto + _9 = Api.parse(reader, signature: signature) as? Api.UserProfilePhoto } } - var _9: Api.UserStatus? + var _10: Api.UserStatus? if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.UserStatus + _10 = Api.parse(reader, signature: signature) as? Api.UserStatus } } - var _10: Int32? - if Int(_1!) & Int(1 << 14) != 0 {_10 = reader.readInt32() } - var _11: [Api.RestrictionReason]? + var _11: Int32? + if Int(_1!) & Int(1 << 14) != 0 {_11 = reader.readInt32() } + var _12: [Api.RestrictionReason]? if Int(_1!) & Int(1 << 18) != 0 {if let _ = reader.readInt32() { - _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) + _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) } } - var _12: String? - if Int(_1!) & Int(1 << 19) != 0 {_12 = parseString(reader) } var _13: String? - if Int(_1!) & Int(1 << 22) != 0 {_13 = parseString(reader) } - var _14: Api.EmojiStatus? + if Int(_1!) & Int(1 << 19) != 0 {_13 = parseString(reader) } + var _14: String? + if Int(_1!) & Int(1 << 22) != 0 {_14 = parseString(reader) } + var _15: Api.EmojiStatus? if Int(_1!) & Int(1 << 30) != 0 {if let signature = reader.readInt32() { - _14 = Api.parse(reader, signature: signature) as? Api.EmojiStatus + _15 = Api.parse(reader, signature: signature) as? Api.EmojiStatus + } } + var _16: [Api.Username]? + if Int(_2!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Username.self) } } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil - let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 18) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 19) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 22) == 0) || _13 != nil - let _c14 = (Int(_1!) & Int(1 << 30) == 0) || _14 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.User.user(flags: _1!, id: _2!, accessHash: _3, firstName: _4, lastName: _5, username: _6, phone: _7, photo: _8, status: _9, botInfoVersion: _10, restrictionReason: _11, botInlinePlaceholder: _12, langCode: _13, emojiStatus: _14) + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 6) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 14) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 18) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 19) == 0) || _13 != nil + let _c14 = (Int(_1!) & Int(1 << 22) == 0) || _14 != nil + let _c15 = (Int(_1!) & Int(1 << 30) == 0) || _15 != nil + let _c16 = (Int(_2!) & Int(1 << 0) == 0) || _16 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 { + return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16) } else { return nil @@ -848,6 +862,46 @@ public extension Api { } } +public extension Api { + enum Username: TypeConstructorDescription { + case username(flags: Int32, username: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .username(let flags, let username): + if boxed { + buffer.appendInt32(-1274595769) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(username, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .username(let flags, let username): + return ("username", [("flags", String(describing: flags)), ("username", String(describing: username))]) + } + } + + public static func parse_username(_ reader: BufferReader) -> Username? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Username.username(flags: _1!, username: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum VideoSize: TypeConstructorDescription { case videoSize(flags: Int32, type: String, w: Int32, h: Int32, size: Int32, videoStartTs: Double?) diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index f3e8113f14..f32b951004 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -1,3 +1,99 @@ +public extension Api.feed { + enum FeedMessages: TypeConstructorDescription { + case feedMessages(flags: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, readMaxPosition: Api.FeedPosition?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case feedMessagesNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-587770695) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {maxPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {minPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {readMaxPosition!.serialize(buffer, true)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .feedMessagesNotModified: + if boxed { + buffer.appendInt32(-619039485) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users): + return ("feedMessages", [("flags", String(describing: flags)), ("maxPosition", String(describing: maxPosition)), ("minPosition", String(describing: minPosition)), ("readMaxPosition", String(describing: readMaxPosition)), ("messages", String(describing: messages)), ("chats", String(describing: chats)), ("users", String(describing: users))]) + case .feedMessagesNotModified: + return ("feedMessagesNotModified", []) + } + } + + public static func parse_feedMessages(_ reader: BufferReader) -> FeedMessages? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.FeedPosition? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _3: Api.FeedPosition? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _4: Api.FeedPosition? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.FeedPosition + } } + var _5: [Api.Message]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _6: [Api.Chat]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.feed.FeedMessages.feedMessages(flags: _1!, maxPosition: _2, minPosition: _3, readMaxPosition: _4, messages: _5!, chats: _6!, users: _7!) + } + else { + return nil + } + } + public static func parse_feedMessagesNotModified(_ reader: BufferReader) -> FeedMessages? { + return Api.feed.FeedMessages.feedMessagesNotModified + } + + } +} public extension Api.help { enum AppUpdate: TypeConstructorDescription { case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?, sticker: Api.Document?) @@ -1220,125 +1316,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum BotCallbackAnswer: TypeConstructorDescription { - case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .botCallbackAnswer(let flags, let message, let url, let cacheTime): - if boxed { - buffer.appendInt32(911761060) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} - serializeInt32(cacheTime, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .botCallbackAnswer(let flags, let message, let url, let cacheTime): - return ("botCallbackAnswer", [("flags", String(describing: flags)), ("message", String(describing: message)), ("url", String(describing: url)), ("cacheTime", String(describing: cacheTime))]) - } - } - - public static func parse_botCallbackAnswer(_ reader: BufferReader) -> BotCallbackAnswer? { - var _1: Int32? - _1 = reader.readInt32() - var _2: String? - if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } - var _3: String? - if Int(_1!) & Int(1 << 2) != 0 {_3 = parseString(reader) } - var _4: Int32? - _4 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum BotResults: TypeConstructorDescription { - case botResults(flags: Int32, queryId: Int64, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, results: [Api.BotInlineResult], cacheTime: Int32, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users): - if boxed { - buffer.appendInt32(-1803769784) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(queryId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {switchPm!.serialize(buffer, true)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(results.count)) - for item in results { - item.serialize(buffer, true) - } - serializeInt32(cacheTime, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users): - return ("botResults", [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("nextOffset", String(describing: nextOffset)), ("switchPm", String(describing: switchPm)), ("results", String(describing: results)), ("cacheTime", String(describing: cacheTime)), ("users", String(describing: users))]) - } - } - - public static func parse_botResults(_ reader: BufferReader) -> BotResults? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } - var _4: Api.InlineBotSwitchPM? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.InlineBotSwitchPM - } } - var _5: [Api.BotInlineResult]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInlineResult.self) - } - var _6: Int32? - _6 = reader.readInt32() - var _7: [Api.User]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, results: _5!, cacheTime: _6!, users: _7!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index e3b58abe33..8cb241bd5b 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,3 +1,125 @@ +public extension Api.messages { + enum BotCallbackAnswer: TypeConstructorDescription { + case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botCallbackAnswer(let flags, let message, let url, let cacheTime): + if boxed { + buffer.appendInt32(911761060) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + serializeInt32(cacheTime, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botCallbackAnswer(let flags, let message, let url, let cacheTime): + return ("botCallbackAnswer", [("flags", String(describing: flags)), ("message", String(describing: message)), ("url", String(describing: url)), ("cacheTime", String(describing: cacheTime))]) + } + } + + public static func parse_botCallbackAnswer(_ reader: BufferReader) -> BotCallbackAnswer? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } + var _3: String? + if Int(_1!) & Int(1 << 2) != 0 {_3 = parseString(reader) } + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum BotResults: TypeConstructorDescription { + case botResults(flags: Int32, queryId: Int64, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, results: [Api.BotInlineResult], cacheTime: Int32, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users): + if boxed { + buffer.appendInt32(-1803769784) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(queryId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {switchPm!.serialize(buffer, true)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(results.count)) + for item in results { + item.serialize(buffer, true) + } + serializeInt32(cacheTime, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users): + return ("botResults", [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("nextOffset", String(describing: nextOffset)), ("switchPm", String(describing: switchPm)), ("results", String(describing: results)), ("cacheTime", String(describing: cacheTime)), ("users", String(describing: users))]) + } + } + + public static func parse_botResults(_ reader: BufferReader) -> BotResults? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: String? + if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) } + var _4: Api.InlineBotSwitchPM? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.InlineBotSwitchPM + } } + var _5: [Api.BotInlineResult]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInlineResult.self) + } + var _6: Int32? + _6 = reader.readInt32() + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, results: _5!, cacheTime: _6!, users: _7!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum ChatAdminsWithInvites: TypeConstructorDescription { case chatAdminsWithInvites(admins: [Api.ChatAdminWithInvites], users: [Api.User]) diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 5069b67b53..b74cf74da5 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -518,13 +518,12 @@ public extension Api.functions.account { } } public extension Api.functions.account { - static func getTheme(format: String, theme: Api.InputTheme, documentId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func getTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-1919060949) + buffer.appendInt32(978872812) serializeString(format, buffer: buffer, boxed: false) theme.serialize(buffer, true) - serializeInt64(documentId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "account.getTheme", parameters: [("format", String(describing: format)), ("theme", String(describing: theme)), ("documentId", String(describing: documentId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + return (FunctionDescription(name: "account.getTheme", parameters: [("format", String(describing: format)), ("theme", String(describing: theme))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in let reader = BufferReader(buffer) var result: Api.Theme? if let signature = reader.readInt32() { @@ -685,6 +684,25 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func reorderUsernames(order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-279966037) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "account.reorderUsernames", parameters: [("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (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 extension Api.functions.account { static func reportPeer(peer: Api.InputPeer, reason: Api.ReportReason, message: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -1065,6 +1083,22 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func toggleUsername(username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1490465654) + serializeString(username, buffer: buffer, boxed: false) + active.serialize(buffer, true) + return (FunctionDescription(name: "account.toggleUsername", parameters: [("username", String(describing: username)), ("active", String(describing: active))]), buffer, DeserializeFunctionResponse { (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 extension Api.functions.account { static func unregisterDevice(tokenType: Int32, token: String, otherUids: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -2391,6 +2425,26 @@ public extension Api.functions.channels { }) } } +public extension Api.functions.channels { + static func reorderUsernames(channel: Api.InputChannel, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1268978403) + channel.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeString(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.reorderUsernames", parameters: [("channel", String(describing: channel)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (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 extension Api.functions.channels { static func reportSpam(channel: Api.InputChannel, participant: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -2540,6 +2594,23 @@ public extension Api.functions.channels { }) } } +public extension Api.functions.channels { + static func toggleUsername(channel: Api.InputChannel, username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1358053637) + channel.serialize(buffer, true) + serializeString(username, buffer: buffer, boxed: false) + active.serialize(buffer, true) + return (FunctionDescription(name: "channels.toggleUsername", parameters: [("channel", String(describing: channel)), ("username", String(describing: username)), ("active", String(describing: active))]), buffer, DeserializeFunctionResponse { (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 extension Api.functions.channels { static func updateUsername(channel: Api.InputChannel, username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -2912,6 +2983,44 @@ public extension Api.functions.contacts { }) } } +public extension Api.functions.feed { + static func getFeed(flags: Int32, filterId: Int32, offsetPosition: Api.FeedPosition?, addOffset: Int32, limit: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2121717715) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(filterId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {offsetPosition!.serialize(buffer, true)} + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {maxPosition!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {minPosition!.serialize(buffer, true)} + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "feed.getFeed", parameters: [("flags", String(describing: flags)), ("filterId", String(describing: filterId)), ("offsetPosition", String(describing: offsetPosition)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxPosition", String(describing: maxPosition)), ("minPosition", String(describing: minPosition)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.feed.FeedMessages? in + let reader = BufferReader(buffer) + var result: Api.feed.FeedMessages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.feed.FeedMessages + } + return result + }) + } +} +public extension Api.functions.feed { + static func readFeed(filterId: Int32, maxPosition: Api.FeedPosition) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1271479809) + serializeInt32(filterId, buffer: buffer, boxed: false) + maxPosition.serialize(buffer, true) + return (FunctionDescription(name: "feed.readFeed", parameters: [("filterId", String(describing: filterId)), ("maxPosition", String(describing: maxPosition))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} public extension Api.functions.folders { static func deleteFolder(folderId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index e4d56e4997..0c497a877f 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -522,7 +522,7 @@ public extension Api { } public extension Api { indirect enum Chat: TypeConstructorDescription { - case channel(flags: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?) + case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?) case channelForbidden(flags: Int32, id: Int64, accessHash: Int64, title: String, untilDate: Int32?) case chat(flags: Int32, id: Int64, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?) case chatEmpty(id: Int64) @@ -530,11 +530,12 @@ public extension Api { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channel(let flags, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount): + case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames): if boxed { - buffer.appendInt32(-2107528095) + buffer.appendInt32(-2094689180) } serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(flags2, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 13) != 0 {serializeInt64(accessHash!, buffer: buffer, boxed: false)} serializeString(title, buffer: buffer, boxed: false) @@ -550,6 +551,11 @@ public extension Api { if Int(flags) & Int(1 << 15) != 0 {bannedRights!.serialize(buffer, true)} if Int(flags) & Int(1 << 18) != 0 {defaultBannedRights!.serialize(buffer, true)} if Int(flags) & Int(1 << 17) != 0 {serializeInt32(participantsCount!, buffer: buffer, boxed: false)} + if Int(flags2) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(usernames!.count)) + for item in usernames! { + item.serialize(buffer, true) + }} break case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate): if boxed { @@ -594,8 +600,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channel(let flags, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount): - return ("channel", [("flags", String(describing: flags)), ("id", String(describing: id)), ("accessHash", String(describing: accessHash)), ("title", String(describing: title)), ("username", String(describing: username)), ("photo", String(describing: photo)), ("date", String(describing: date)), ("restrictionReason", String(describing: restrictionReason)), ("adminRights", String(describing: adminRights)), ("bannedRights", String(describing: bannedRights)), ("defaultBannedRights", String(describing: defaultBannedRights)), ("participantsCount", String(describing: participantsCount))]) + case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames): + return ("channel", [("flags", String(describing: flags)), ("flags2", String(describing: flags2)), ("id", String(describing: id)), ("accessHash", String(describing: accessHash)), ("title", String(describing: title)), ("username", String(describing: username)), ("photo", String(describing: photo)), ("date", String(describing: date)), ("restrictionReason", String(describing: restrictionReason)), ("adminRights", String(describing: adminRights)), ("bannedRights", String(describing: bannedRights)), ("defaultBannedRights", String(describing: defaultBannedRights)), ("participantsCount", String(describing: participantsCount)), ("usernames", String(describing: usernames))]) case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate): return ("channelForbidden", [("flags", String(describing: flags)), ("id", String(describing: id)), ("accessHash", String(describing: accessHash)), ("title", String(describing: title)), ("untilDate", String(describing: untilDate))]) case .chat(let flags, let id, let title, let photo, let participantsCount, let date, let version, let migratedTo, let adminRights, let defaultBannedRights): @@ -610,52 +616,60 @@ public extension Api { public static func parse_channel(_ reader: BufferReader) -> Chat? { var _1: Int32? _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() var _3: Int64? - if Int(_1!) & Int(1 << 13) != 0 {_3 = reader.readInt64() } - var _4: String? - _4 = parseString(reader) + _3 = reader.readInt64() + var _4: Int64? + if Int(_1!) & Int(1 << 13) != 0 {_4 = reader.readInt64() } var _5: String? - if Int(_1!) & Int(1 << 6) != 0 {_5 = parseString(reader) } - var _6: Api.ChatPhoto? + _5 = parseString(reader) + var _6: String? + if Int(_1!) & Int(1 << 6) != 0 {_6 = parseString(reader) } + var _7: Api.ChatPhoto? if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.ChatPhoto + _7 = Api.parse(reader, signature: signature) as? Api.ChatPhoto } - var _7: Int32? - _7 = reader.readInt32() - var _8: [Api.RestrictionReason]? + var _8: Int32? + _8 = reader.readInt32() + var _9: [Api.RestrictionReason]? if Int(_1!) & Int(1 << 9) != 0 {if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) } } - var _9: Api.ChatAdminRights? + var _10: Api.ChatAdminRights? if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } } - var _10: Api.ChatBannedRights? - if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.ChatBannedRights + _10 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights } } var _11: Api.ChatBannedRights? - if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() { + if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() { _11 = Api.parse(reader, signature: signature) as? Api.ChatBannedRights } } - var _12: Int32? - if Int(_1!) & Int(1 << 17) != 0 {_12 = reader.readInt32() } + var _12: Api.ChatBannedRights? + if Int(_1!) & Int(1 << 18) != 0 {if let signature = reader.readInt32() { + _12 = Api.parse(reader, signature: signature) as? Api.ChatBannedRights + } } + var _13: Int32? + if Int(_1!) & Int(1 << 17) != 0 {_13 = reader.readInt32() } + var _14: [Api.Username]? + if Int(_2!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Username.self) + } } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 13) == 0) || _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 6) == 0) || _5 != nil - let _c6 = _6 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 13) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 6) == 0) || _6 != nil let _c7 = _7 != nil - let _c8 = (Int(_1!) & Int(1 << 9) == 0) || _8 != nil - let _c9 = (Int(_1!) & Int(1 << 14) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 15) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 18) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 17) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.Chat.channel(flags: _1!, id: _2!, accessHash: _3, title: _4!, username: _5, photo: _6!, date: _7!, restrictionReason: _8, adminRights: _9, bannedRights: _10, defaultBannedRights: _11, participantsCount: _12) + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 9) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 15) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 18) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 17) == 0) || _13 != nil + let _c14 = (Int(_2!) & Int(1 << 0) == 0) || _14 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { + return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api5.swift b/submodules/TelegramApi/Sources/Api5.swift index 96cffb5c4b..ad3c60b58d 100644 --- a/submodules/TelegramApi/Sources/Api5.swift +++ b/submodules/TelegramApi/Sources/Api5.swift @@ -956,6 +956,52 @@ public extension Api { } } +public extension Api { + enum FeedPosition: TypeConstructorDescription { + case feedPosition(date: Int32, peer: Api.Peer, id: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .feedPosition(let date, let peer, let id): + if boxed { + buffer.appendInt32(1348066419) + } + serializeInt32(date, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .feedPosition(let date, let peer, let id): + return ("feedPosition", [("date", String(describing: date)), ("peer", String(describing: peer)), ("id", String(describing: id))]) + } + } + + public static func parse_feedPosition(_ reader: BufferReader) -> FeedPosition? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.FeedPosition.feedPosition(date: _1!, peer: _2!, id: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum FileHash: TypeConstructorDescription { case fileHash(offset: Int64, limit: Int32, hash: Buffer) @@ -1092,71 +1138,3 @@ public extension Api { } } -public extension Api { - enum ForumTopic: TypeConstructorDescription { - case forumTopic(flags: Int32, id: Int32, date: Int32, title: String, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount): - if boxed { - buffer.appendInt32(1885902651) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(id, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)} - serializeInt32(topMessage, buffer: buffer, boxed: false) - serializeInt32(readInboxMaxId, buffer: buffer, boxed: false) - serializeInt32(readOutboxMaxId, buffer: buffer, boxed: false) - serializeInt32(unreadCount, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount): - return ("forumTopic", [("flags", String(describing: flags)), ("id", String(describing: id)), ("date", String(describing: date)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("topMessage", String(describing: topMessage)), ("readInboxMaxId", String(describing: readInboxMaxId)), ("readOutboxMaxId", String(describing: readOutboxMaxId)), ("unreadCount", String(describing: unreadCount))]) - } - } - - public static func parse_forumTopic(_ reader: BufferReader) -> ForumTopic? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: String? - _4 = parseString(reader) - var _5: Int64? - if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt64() } - var _6: Int32? - _6 = reader.readInt32() - var _7: Int32? - _7 = reader.readInt32() - var _8: Int32? - _8 = reader.readInt32() - var _9: Int32? - _9 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, title: _4!, iconEmojiId: _5, topMessage: _6!, readInboxMaxId: _7!, readOutboxMaxId: _8!, unreadCount: _9!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api6.swift b/submodules/TelegramApi/Sources/Api6.swift index 7177c5d984..0276d0e26e 100644 --- a/submodules/TelegramApi/Sources/Api6.swift +++ b/submodules/TelegramApi/Sources/Api6.swift @@ -1,3 +1,71 @@ +public extension Api { + enum ForumTopic: TypeConstructorDescription { + case forumTopic(flags: Int32, id: Int32, date: Int32, title: String, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount): + if boxed { + buffer.appendInt32(1885902651) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)} + serializeInt32(topMessage, buffer: buffer, boxed: false) + serializeInt32(readInboxMaxId, buffer: buffer, boxed: false) + serializeInt32(readOutboxMaxId, buffer: buffer, boxed: false) + serializeInt32(unreadCount, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount): + return ("forumTopic", [("flags", String(describing: flags)), ("id", String(describing: id)), ("date", String(describing: date)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("topMessage", String(describing: topMessage)), ("readInboxMaxId", String(describing: readInboxMaxId)), ("readOutboxMaxId", String(describing: readOutboxMaxId)), ("unreadCount", String(describing: unreadCount))]) + } + } + + public static func parse_forumTopic(_ reader: BufferReader) -> ForumTopic? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: String? + _4 = parseString(reader) + var _5: Int64? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt64() } + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() + var _8: Int32? + _8 = reader.readInt32() + var _9: Int32? + _9 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, title: _4!, iconEmojiId: _5, topMessage: _6!, readInboxMaxId: _7!, readOutboxMaxId: _8!, unreadCount: _9!) + } + else { + return nil + } + } + + } +} public extension Api { enum Game: TypeConstructorDescription { case game(flags: Int32, id: Int64, accessHash: Int64, shortName: String, title: String, description: String, photo: Api.Photo, document: Api.Document?) diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 7e5a0b281d..e7bbd4a10c 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -371,7 +371,7 @@ struct AccountMutableState { for chat in chats { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, participantsCount): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): if let participantsCount = participantsCount { self.addOperation(.UpdateCachedPeerData(chat.peerId, { current in var previous: CachedChannelData @@ -431,7 +431,7 @@ struct AccountMutableState { var presences: [PeerId: Api.UserStatus] = [:] for user in users { switch user { - case let .user(_, id, _, _, _, _, _, _, status, _, _, _, _, _): + case let .user(_, _, id, _, _, _, _, _, _, status, _, _, _, _, _, _): if let status = status { presences[PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))] = status } diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index 58dc7cdd94..2162898dfd 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -60,7 +60,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: "", photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) case let .chatForbidden(id, title): return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: title, photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) - case let .channel(flags, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _): + case let .channel(flags, _, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames): let isMin = (flags & (1 << 12)) != 0 let participationStatus: TelegramChannelParticipationStatus @@ -143,7 +143,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { } } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init)) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.compactMap(TelegramPeerUsername.init)) case let .channelForbidden(flags, id, accessHash, title, untilDate): let info: TelegramChannelInfo if (flags & Int32(1 << 8)) != 0 { @@ -152,7 +152,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { info = .broadcast(TelegramChannelBroadcastInfo(flags: [])) } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: nil) } } @@ -160,7 +160,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { switch rhs { case .chat, .chatEmpty, .chatForbidden, .channelForbidden: return parseTelegramGroupOrChannel(chat: rhs) - case let .channel(flags, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _): + case let .channel(flags, _, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames): let isMin = (flags & (1 << 12)) != 0 if accessHash != nil && !isMin { return parseTelegramGroupOrChannel(chat: rhs) @@ -193,7 +193,8 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { info = .group(TelegramChannelGroupInfo(flags: infoFlags)) } - return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init)) + + return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.compactMap(TelegramPeerUsername.init)) } else { return parseTelegramGroupOrChannel(chat: rhs) } @@ -244,6 +245,6 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne accessHash = rhs.accessHash ?? lhs.accessHash } - return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights) + return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift index 668e275a21..d472e9fb2f 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift @@ -26,7 +26,7 @@ func parsedTelegramProfilePhoto(_ photo: Api.UserProfilePhoto) -> [TelegramMedia extension TelegramUser { convenience init(user: Api.User) { switch user { - case let .user(flags, id, accessHash, firstName, lastName, username, phone, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus): + case let .user(flags, _, id, accessHash, firstName, lastName, username, phone, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus, usernames): let representations: [TelegramMediaImageRepresentation] = photo.flatMap(parsedTelegramProfilePhoto) ?? [] let isMin = (flags & (1 << 20)) != 0 @@ -55,6 +55,8 @@ extension TelegramUser { userFlags.insert(.isPremium) } + let mappedUsernames = username + var botInfo: BotUserInfo? if (flags & (1 << 14)) != 0 { var botFlags = BotUserInfoFlags() @@ -75,15 +77,15 @@ extension TelegramUser { let restrictionInfo: PeerAccessRestrictionInfo? = restrictionReason.flatMap(PeerAccessRestrictionInfo.init(apiReasons:)) - self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:))) + self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.compactMap(TelegramPeerUsername.init)) case let .userEmpty(id): - self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil) + self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: nil) } } static func merge(_ lhs: TelegramUser?, rhs: Api.User) -> TelegramUser? { switch rhs { - case let .user(flags, _, rhsAccessHash, _, _, username, _, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus): + case let .user(flags, _, _, rhsAccessHash, _, _, username, _, photo, _, _, restrictionReason, botInlinePlaceholder, _, emojiStatus, usernames): let isMin = (flags & (1 << 20)) != 0 if !isMin { return TelegramUser(user: rhs) @@ -150,7 +152,7 @@ extension TelegramUser { accessHash = lhs.accessHash ?? rhsAccessHashValue } - return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:))) + return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.compactMap(TelegramPeerUsername.init)) } else { return TelegramUser(user: rhs) } @@ -197,7 +199,7 @@ extension TelegramUser { accessHash = lhs.accessHash ?? rhs.accessHash } - return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: rhs.username, phone: lhs.phone, photo: rhs.photo.isEmpty ? lhs.photo : rhs.photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus) + return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: rhs.username, phone: lhs.phone, photo: rhs.photo.isEmpty ? lhs.photo : rhs.photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus, usernames: rhs.usernames) } } } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift index 47a188b2fa..4e5614b081 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramUserPresence.swift @@ -23,7 +23,7 @@ extension TelegramUserPresence { convenience init?(apiUser: Api.User) { switch apiUser { - case let .user(_, _, _, _, _, _, _, _, status, _, _, _, _, _): + case let .user(_, _, _, _, _, _, _, _, _, status, _, _, _, _, _, _): if let status = status { self.init(apiStatus: status) } else { diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index f16fb9db35..b8da3557d1 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1020,7 +1020,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo if updatedState.peers[peerId] == nil { updatedState.updatePeer(peerId, { peer in if peer == nil { - return TelegramUser(id: peerId, accessHash: nil, firstName: "Telegram Notifications", lastName: nil, username: nil, phone: nil, photo: [], botInfo: BotUserInfo(flags: [], inlinePlaceholder: nil), restrictionInfo: nil, flags: [.isVerified], emojiStatus: nil) + return TelegramUser(id: peerId, accessHash: nil, firstName: "Telegram Notifications", lastName: nil, username: nil, phone: nil, photo: [], botInfo: BotUserInfo(flags: [], inlinePlaceholder: nil), restrictionInfo: nil, flags: [.isVerified], emojiStatus: nil, usernames: nil) } else { return peer } diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index 679b713a3f..765c519afa 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -146,7 +146,7 @@ extension Api.Chat { return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)) case let .chatForbidden(id, _): return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)) - case let .channel(_, id, _, _, _, _, _, _, _, _, _, _): + case let .channel(_, _, id, _, _, _, _, _, _, _, _, _, _, _): return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)) case let .channelForbidden(_, id, _, _, _): return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)) @@ -157,7 +157,7 @@ extension Api.Chat { extension Api.User { var peerId: PeerId { switch self { - case .user(_, let id, _, _, _, _, _, _, _, _, _, _, _, _): + case .user(_, _, let id, _, _, _, _, _, _, _, _, _, _, _, _, _): return PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)) case let .userEmpty(id): return PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index 8e7087c4f8..74103f9a0e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -109,6 +109,47 @@ extension PeerEmojiStatus { } } + +public struct TelegramPeerUsername : PostboxCoding,Equatable { + public struct Flags: OptionSet { + public var rawValue: Int32 + public init() { + self.rawValue = 0 + } + public init(rawValue: Int32) { + self.rawValue = rawValue + } + public static let isEditable = Flags(rawValue: 1 << 0) + } + + public let flags: Flags + public let value: String + + init(flags: Flags, value: String) { + self.flags = flags + self.value = value + } + + public init(decoder: PostboxDecoder) { + self.flags = Flags(rawValue: decoder.decodeInt32ForKey("fl", orElse: 0)) + self.value = decoder.decodeStringForKey("v", orElse: "") + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.flags.rawValue, forKey: "fl") + encoder.encodeString(self.value, forKey: "v") + } +} + +extension TelegramPeerUsername { + init(api apiValue: Api.Username) { + switch apiValue { + case let .username(flags, username): + self.init(flags: .init(rawValue: flags), value: username) + } + } +} + public final class CachedUserData: CachedPeerData { public let about: String? public let botInfo: BotInfo? diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift index dad575fa0e..df2a2f37d3 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift @@ -165,7 +165,8 @@ public final class TelegramChannel: Peer, Equatable { public let adminRights: TelegramChatAdminRights? public let bannedRights: TelegramChatBannedRights? public let defaultBannedRights: TelegramChatBannedRights? - + public let usernames: [TelegramPeerUsername]? + public var indexName: PeerIndexNameRepresentation { return .title(title: self.title, addressName: self.username) } @@ -176,8 +177,7 @@ public final class TelegramChannel: Peer, Equatable { public let notificationSettingsPeerId: PeerId? = nil public var timeoutAttribute: UInt32? { return nil } - - public init(id: PeerId, accessHash: TelegramPeerAccessHash?, title: String, username: String?, photo: [TelegramMediaImageRepresentation], creationDate: Int32, version: Int32, participationStatus: TelegramChannelParticipationStatus, info: TelegramChannelInfo, flags: TelegramChannelFlags, restrictionInfo: PeerAccessRestrictionInfo?, adminRights: TelegramChatAdminRights?, bannedRights: TelegramChatBannedRights?, defaultBannedRights: TelegramChatBannedRights?) { + public init(id: PeerId, accessHash: TelegramPeerAccessHash?, title: String, username: String?, photo: [TelegramMediaImageRepresentation], creationDate: Int32, version: Int32, participationStatus: TelegramChannelParticipationStatus, info: TelegramChannelInfo, flags: TelegramChannelFlags, restrictionInfo: PeerAccessRestrictionInfo?, adminRights: TelegramChatAdminRights?, bannedRights: TelegramChatBannedRights?, defaultBannedRights: TelegramChatBannedRights?, usernames: [TelegramPeerUsername]?) { self.id = id self.accessHash = accessHash self.title = title @@ -192,6 +192,7 @@ public final class TelegramChannel: Peer, Equatable { self.adminRights = adminRights self.bannedRights = bannedRights self.defaultBannedRights = defaultBannedRights + self.usernames = usernames } public init(decoder: PostboxDecoder) { @@ -219,6 +220,10 @@ public final class TelegramChannel: Peer, Equatable { self.adminRights = decoder.decodeObjectForKey("ar", decoder: { TelegramChatAdminRights(decoder: $0) }) as? TelegramChatAdminRights self.bannedRights = decoder.decodeObjectForKey("br", decoder: { TelegramChatBannedRights(decoder: $0) }) as? TelegramChatBannedRights self.defaultBannedRights = decoder.decodeObjectForKey("dbr", decoder: { TelegramChatBannedRights(decoder: $0) }) as? TelegramChatBannedRights + + self.usernames = try? decoder.decodeObjectArrayWithCustomDecoderForKey("un", decoder: { + TelegramPeerUsername(decoder: $0) + }) } public func encode(_ encoder: PostboxEncoder) { @@ -267,6 +272,12 @@ public final class TelegramChannel: Peer, Equatable { } else { encoder.encodeNil(forKey: "dbr") } + + if let usernames = self.usernames { + encoder.encodeObjectArray(usernames, forKey: "un") + } else { + encoder.encodeNil(forKey: "un") + } } public func isEqual(_ other: Peer) -> Bool { @@ -301,19 +312,22 @@ public final class TelegramChannel: Peer, Equatable { if lhs.defaultBannedRights != rhs.defaultBannedRights { return false } + if lhs.usernames != rhs.usernames { + return false + } return true } public func withUpdatedAddressName(_ addressName: String?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames) } public func withUpdatedDefaultBannedRights(_ defaultBannedRights: TelegramChatBannedRights?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames) } public func withUpdatedFlags(_ flags: TelegramChannelFlags) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift index 5d9a5b0ed8..67a70220d9 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramUser.swift @@ -71,6 +71,7 @@ public final class TelegramUser: Peer, Equatable { public let restrictionInfo: PeerAccessRestrictionInfo? public let flags: UserInfoFlags public let emojiStatus: PeerEmojiStatus? + public let usernames: [TelegramPeerUsername]? public var nameOrPhone: String { if let firstName = self.firstName { @@ -127,7 +128,7 @@ public final class TelegramUser: Peer, Equatable { } } - public init(id: PeerId, accessHash: TelegramPeerAccessHash?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: [TelegramMediaImageRepresentation], botInfo: BotUserInfo?, restrictionInfo: PeerAccessRestrictionInfo?, flags: UserInfoFlags, emojiStatus: PeerEmojiStatus?) { + public init(id: PeerId, accessHash: TelegramPeerAccessHash?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: [TelegramMediaImageRepresentation], botInfo: BotUserInfo?, restrictionInfo: PeerAccessRestrictionInfo?, flags: UserInfoFlags, emojiStatus: PeerEmojiStatus?, usernames: [TelegramPeerUsername]?) { self.id = id self.accessHash = accessHash self.firstName = firstName @@ -139,6 +140,7 @@ public final class TelegramUser: Peer, Equatable { self.restrictionInfo = restrictionInfo self.flags = flags self.emojiStatus = emojiStatus + self.usernames = usernames } public init(decoder: PostboxDecoder) { @@ -175,6 +177,10 @@ public final class TelegramUser: Peer, Equatable { self.flags = UserInfoFlags(rawValue: decoder.decodeInt32ForKey("fl", orElse: 0)) self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs") + + self.usernames = try? decoder.decodeObjectArrayWithCustomDecoderForKey("un", decoder: { + TelegramPeerUsername(decoder: $0) + }) } public func encode(_ encoder: PostboxEncoder) { @@ -226,6 +232,12 @@ public final class TelegramUser: Peer, Equatable { } else { encoder.encodeNil(forKey: "emjs") } + + if let usernames = self.usernames { + encoder.encodeObjectArray(usernames, forKey: "un") + } else { + encoder.encodeNil(forKey: "un") + } } public func isEqual(_ other: Peer) -> Bool { @@ -275,27 +287,30 @@ public final class TelegramUser: Peer, Equatable { if lhs.emojiStatus != rhs.emojiStatus { return false } + if lhs.usernames != rhs.usernames { + return false + } return true } public func withUpdatedUsername(_ username:String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) } public func withUpdatedNames(firstName: String?, lastName: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: firstName, lastName: lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: firstName, lastName: lastName, username: self.username, phone: self.phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) } public func withUpdatedPhone(_ phone: String?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) } public func withUpdatedPhoto(_ representations: [TelegramMediaImageRepresentation]) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: representations, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: representations, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: self.emojiStatus, usernames: self.usernames) } public func withUpdatedEmojiStatus(_ emojiStatus: PeerEmojiStatus?) -> TelegramUser { - return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus) + return TelegramUser(id: self.id, accessHash: self.accessHash, firstName: self.firstName, lastName: self.lastName, username: self.username, phone: phone, photo: self.photo, botInfo: self.botInfo, restrictionInfo: self.restrictionInfo, flags: self.flags, emojiStatus: emojiStatus, usernames: self.usernames) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index c0f5431130..644ca73fa9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -2298,7 +2298,7 @@ func _internal_groupCallDisplayAsAvailablePeers(network: Network, postbox: Postb for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, participantsCount): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 3ab8de154a..961308a741 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -221,7 +221,8 @@ private class AdMessagesHistoryContextImpl { restrictionInfo: nil, adminRights: nil, bannedRights: nil, - defaultBannedRights: nil + defaultBannedRights: nil, + usernames: nil ) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift index c4836afdf7..7323d49f9a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift @@ -126,7 +126,7 @@ func _internal_peerSendAsAvailablePeers(network: Network, postbox: Postbox, peer for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, participantsCount): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift index da0d646349..c7c1b2c786 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift @@ -31,7 +31,7 @@ func _internal_inactiveChannelList(network: Network) -> Signal<[InactiveChannel] var participantsCounts: [PeerId: Int32] = [:] for chat in chats { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, participantsCountValue): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCountValue, _): if let participantsCountValue = participantsCountValue { participantsCounts[chat.peerId] = participantsCountValue } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift index c6bc6cd994..1157f25495 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift @@ -44,7 +44,7 @@ func _internal_searchPeers(account: Account, query: String) -> Signal<([FoundPee peers[groupOrChannel.id] = groupOrChannel switch chat { /*feed*/ - case let .channel(_, _, _, _, _, _, _, _, _, _, _, participantsCount): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift index 468b557688..433cdeeda7 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift @@ -115,7 +115,7 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal Signal { - return account.network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputThemeSlug(slug: slug), documentId: 0)) + return account.network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputThemeSlug(slug: slug))) |> mapError { error -> GetThemeError in if error.errorDescription == "THEME_FORMAT_INVALID" { return .unsupported @@ -118,7 +118,7 @@ private func checkThemeUpdated(network: Network, theme: TelegramTheme) -> Signal guard let documentId = id else { return .fail(.generic) } - return network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), documentId: documentId)) + return network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash))) |> mapError { _ -> GetThemeError in return .generic } |> map { theme -> ThemeUpdatedResult in return .updated(TelegramTheme(apiTheme: theme)) diff --git a/submodules/TelegramCore/Sources/UpdatePeers.swift b/submodules/TelegramCore/Sources/UpdatePeers.swift index ed09bec470..27c3271085 100644 --- a/submodules/TelegramCore/Sources/UpdatePeers.swift +++ b/submodules/TelegramCore/Sources/UpdatePeers.swift @@ -117,7 +117,7 @@ func updatePeerPresences(transaction: Transaction, accountPeerId: PeerId, peerPr parsedPresences[peerId] = presence default: switch user { - case let .user(flags, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .user(flags, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let isMin = (flags & (1 << 20)) != 0 if isMin, let _ = transaction.getPeerPresence(peerId: peerId) { } else { @@ -185,7 +185,7 @@ func updateContacts(transaction: Transaction, apiUsers: [Api.User]) { for user in apiUsers { var isContact: Bool? switch user { - case let .user(flags, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .user(flags, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): if (flags & (1 << 20)) == 0 { isContact = (flags & (1 << 11)) != 0 } diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index aac2589ae8..843390c668 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -2,6 +2,8 @@ import Foundation import Postbox public extension Peer { + + var debugDisplayTitle: String { switch self { case let user as TelegramUser: