From bb4df5229ae782b62fab19df565697cdbd797284 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 12 Feb 2020 17:22:34 +0400 Subject: [PATCH 1/6] Fix stucking PIP button in gallery --- submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index e07033342e..a6c6c9b502 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -160,7 +160,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { private let imageNode: TransformImageNode fileprivate let _ready = Promise() fileprivate let _title = Promise() - fileprivate let _rightBarButtonItem = Promise() + fileprivate let _rightBarButtonItem = Promise(nil) private let statusNodeContainer: HighlightableButtonNode private let statusNode: RadialStatusNode private let footerContentNode: ChatItemGalleryFooterContentNode From 6b728791c9f25733f2c1733c9de0017e05752bfa Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 12 Feb 2020 20:33:23 +0400 Subject: [PATCH 2/6] Fix --- submodules/TelegramCore/Sources/AccountManager.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift index 95a689aa1d..4b53e638fb 100644 --- a/submodules/TelegramCore/Sources/AccountManager.swift +++ b/submodules/TelegramCore/Sources/AccountManager.swift @@ -153,6 +153,7 @@ private var declaredEncodables: Void = { declareEncodable(TelegramMediaWebpageAttribute.self, f: { TelegramMediaWebpageAttribute(decoder: $0) }) declareEncodable(CachedPollOptionResult.self, f: { CachedPollOptionResult(decoder: $0) }) //declareEncodable(ChatListFiltersState.self, f: { ChatListFiltersState(decoder: $0) }) + declareEncodable(PeersNearbyState.self, f: { PeersNearbyState(decoder: $0) }) return }() From 76b4d87922eaebe88daa4b320a2a0116fee991d6 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 18 Feb 2020 22:15:48 +0400 Subject: [PATCH 3/6] Fixes --- .../Sources/AccountContext.swift | 1 + .../Sources/PeersNearbyManager.swift | 9 ++ .../GalleryUI/Sources/GalleryItemNode.swift | 4 - .../GalleryUI/Sources/GalleryPagerNode.swift | 150 ++++++++++++++---- .../ZoomableContentGalleryItemNode.swift | 67 +------- .../Sources/PeersNearbyController.swift | 16 +- .../TelegramCore/Sources/PeersNearby.swift | 10 +- .../TelegramUI/AccountContext.swift | 7 + .../ChatMessageAnimatedStickerItemNode.swift | 4 +- .../TelegramUI/PeersNearbyManager.swift | 80 ++++++++++ .../Sources/WalletTransactionInfoScreen.swift | 11 +- 11 files changed, 240 insertions(+), 119 deletions(-) create mode 100644 submodules/AccountContext/Sources/PeersNearbyManager.swift create mode 100644 submodules/TelegramUI/TelegramUI/PeersNearbyManager.swift diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 87383884ce..a589d54f23 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -565,6 +565,7 @@ public protocol AccountContext: class { #endif var liveLocationManager: LiveLocationManager? { get } + var peersNearbyManager: PeersNearbyManager? { get } var fetchManager: FetchManager { get } var downloadedMediaStoreManager: DownloadedMediaStoreManager { get } var peerChannelMemberCategoriesContextsManager: PeerChannelMemberCategoriesContextsManager { get } diff --git a/submodules/AccountContext/Sources/PeersNearbyManager.swift b/submodules/AccountContext/Sources/PeersNearbyManager.swift new file mode 100644 index 0000000000..a3b5fba092 --- /dev/null +++ b/submodules/AccountContext/Sources/PeersNearbyManager.swift @@ -0,0 +1,9 @@ +import Foundation +import SwiftSignalKit +import TelegramCore +import SyncCore +import TelegramPresentationData + +public protocol PeersNearbyManager { + +} diff --git a/submodules/GalleryUI/Sources/GalleryItemNode.swift b/submodules/GalleryUI/Sources/GalleryItemNode.swift index b2c513654f..8df3a446c7 100644 --- a/submodules/GalleryUI/Sources/GalleryItemNode.swift +++ b/submodules/GalleryUI/Sources/GalleryItemNode.swift @@ -21,10 +21,6 @@ open class GalleryItemNode: ASDisplayNode { } public var toggleControlsVisibility: () -> Void = { } - public var goToPreviousItem: () -> Void = { } - public var goToNextItem: () -> Void = { } - public var canGoToPreviousItem: () -> Bool = { return false } - public var canGoToNextItem: () -> Bool = { return false } public var dismiss: () -> Void = { } public var beginCustomDismiss: () -> Void = { } public var completeCustomDismiss: () -> Void = { } diff --git a/submodules/GalleryUI/Sources/GalleryPagerNode.swift b/submodules/GalleryUI/Sources/GalleryPagerNode.swift index 1b2f7e5649..e71b3d496e 100644 --- a/submodules/GalleryUI/Sources/GalleryPagerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryPagerNode.swift @@ -5,6 +5,32 @@ import Display import SwiftSignalKit import Postbox +private let leftFadeImage = generateImage(CGSize(width: 64.0, height: 1.0), opaque: false, rotatedContext: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + let gradientColors = [UIColor.black.withAlphaComponent(0.35).cgColor, UIColor.black.withAlphaComponent(0.0).cgColor] as CFArray + + var locations: [CGFloat] = [0.0, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 64.0, y: 0.0), options: CGGradientDrawingOptions()) +}) + +private let rightFadeImage = generateImage(CGSize(width: 64.0, height: 1.0), opaque: false, rotatedContext: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + let gradientColors = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.35).cgColor] as CFArray + + var locations: [CGFloat] = [0.0, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 64.0, y: 0.0), options: CGGradientDrawingOptions()) +}) + public struct GalleryPagerInsertItem { public let index: Int public let item: GalleryItem @@ -43,11 +69,16 @@ public struct GalleryPagerTransaction { } } -public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate { +public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDelegate { private let pageGap: CGFloat private let scrollView: UIScrollView + private let leftFadeNode: ASImageNode + private let rightFadeNode: ASImageNode + + private var pressGestureRecognizer: UILongPressGestureRecognizer? + public private(set) var items: [GalleryItem] = [] private var itemNodes: [GalleryItemNode] = [] private var ignoreDidScroll = false @@ -78,6 +109,16 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate { self.scrollView.contentInsetAdjustmentBehavior = .never } + self.leftFadeNode = ASImageNode() + self.leftFadeNode.contentMode = .scaleToFill + self.leftFadeNode.image = leftFadeImage + self.leftFadeNode.alpha = 0.0 + + self.rightFadeNode = ASImageNode() + self.rightFadeNode.contentMode = .scaleToFill + self.rightFadeNode.image = rightFadeImage + self.rightFadeNode.alpha = 0.0 + super.init() self.scrollView.showsVerticalScrollIndicator = false @@ -90,6 +131,55 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate { self.scrollView.scrollsToTop = false self.scrollView.delaysContentTouches = false self.view.addSubview(self.scrollView) + + self.addSubnode(self.leftFadeNode) + self.addSubnode(self.rightFadeNode) + } + + public override func didLoad() { + super.didLoad() + + let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.pressGesture(_:))) + gestureRecognizer.delegate = self + gestureRecognizer.minimumPressDuration = 0.01 + self.view.addGestureRecognizer(gestureRecognizer) + self.pressGestureRecognizer = gestureRecognizer + } + + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + @objc private func pressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) { + let edgeWidth: CGFloat = 44.0 + let location = gestureRecognizer.location(in: gestureRecognizer.view) + switch gestureRecognizer.state { + case .began: + let transition: ContainedViewLayoutTransition = .animated(duration: 0.07, curve: .easeInOut) + if location.x < edgeWidth && self.canGoToPreviousItem() { + transition.updateAlpha(node: self.leftFadeNode, alpha: 1.0) + } else if location.x > self.frame.width - edgeWidth && self.canGoToNextItem() { + transition.updateAlpha(node: self.rightFadeNode, alpha: 1.0) + } + case .ended: + let transition: ContainedViewLayoutTransition = .animated(duration: 0.1, curve: .easeInOut) + if location.x < edgeWidth && self.canGoToPreviousItem() { + transition.updateAlpha(node: self.leftFadeNode, alpha: 0.0) + self.goToPreviousItem() + } else if location.x > self.frame.width - edgeWidth && self.canGoToNextItem() { + transition.updateAlpha(node: self.rightFadeNode, alpha: 0.0) + self.goToNextItem() + } + case .cancelled: + let transition: ContainedViewLayoutTransition = .animated(duration: 0.1, curve: .easeInOut) + if location.x < edgeWidth { + transition.updateAlpha(node: self.leftFadeNode, alpha: 0.0) + } else if location.x > self.frame.width - edgeWidth { + transition.updateAlpha(node: self.rightFadeNode, alpha: 0.0) + } + default: + break + } } public var isScrollEnabled: Bool { @@ -134,6 +224,10 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate { transition.animatePosition(node: centralItemNode, from: centralItemNode.position.offsetBy(dx: -updatedCentralPoint.x + centralPoint.x, dy: -updatedCentralPoint.y + centralPoint.y)) } + + let fadeWidth = min(72.0, layout.size.width * 0.2) + self.leftFadeNode.frame = CGRect(x: 0.0, y: 0.0, width: fadeWidth, height: layout.size.height) + self.rightFadeNode.frame = CGRect(x: layout.size.width - fadeWidth, y: 0.0, width: fadeWidth, height: layout.size.height) } public func ready() -> Signal { @@ -238,35 +332,37 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate { } } + private func canGoToPreviousItem() -> Bool { + if let index = self.centralItemIndex, index > 0 { + return true + } else { + return false + } + } + + private func canGoToNextItem() -> Bool { + if let index = self.centralItemIndex, index < self.items.count - 1 { + return true + } else { + return false + } + } + + private func goToPreviousItem() { + if let index = self.centralItemIndex, index > 0 { + self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index - 1)) + } + } + + private func goToNextItem() { + if let index = self.centralItemIndex, index < self.items.count - 1 { + self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index + 1)) + } + } + private func makeNodeForItem(at index: Int) -> GalleryItemNode { let node = self.items[index].node() node.toggleControlsVisibility = self.toggleControlsVisibility - node.goToPreviousItem = { [weak self] in - if let strongSelf = self { - if let index = strongSelf.centralItemIndex, index > 0 { - strongSelf.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index - 1)) - } - } - } - node.goToNextItem = { [weak self] in - if let strongSelf = self { - if let index = strongSelf.centralItemIndex, index < strongSelf.items.count - 1 { - strongSelf.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index + 1)) - } - } - } - node.canGoToPreviousItem = { [weak self] in - if let strongSelf = self, let index = strongSelf.centralItemIndex, index > 0 { - return true - } - return false - } - node.canGoToNextItem = { [weak self] in - if let strongSelf = self, let index = strongSelf.centralItemIndex, index < strongSelf.items.count - 1 { - return true - } - return false - } node.dismiss = self.dismiss node.beginCustomDismiss = self.beginCustomDismiss node.completeCustomDismiss = self.completeCustomDismiss diff --git a/submodules/GalleryUI/Sources/ZoomableContentGalleryItemNode.swift b/submodules/GalleryUI/Sources/ZoomableContentGalleryItemNode.swift index f65769e863..504b7c6cbf 100644 --- a/submodules/GalleryUI/Sources/ZoomableContentGalleryItemNode.swift +++ b/submodules/GalleryUI/Sources/ZoomableContentGalleryItemNode.swift @@ -3,36 +3,8 @@ import UIKit import Display import AsyncDisplayKit -private let leftFadeImage = generateImage(CGSize(width: 64.0, height: 1.0), opaque: false, rotatedContext: { size, context in - let bounds = CGRect(origin: CGPoint(), size: size) - context.clear(bounds) - - let gradientColors = [UIColor.black.withAlphaComponent(0.35).cgColor, UIColor.black.withAlphaComponent(0.0).cgColor] as CFArray - - var locations: [CGFloat] = [0.0, 1.0] - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! - - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 64.0, y: 0.0), options: CGGradientDrawingOptions()) -}) - -private let rightFadeImage = generateImage(CGSize(width: 64.0, height: 1.0), opaque: false, rotatedContext: { size, context in - let bounds = CGRect(origin: CGPoint(), size: size) - context.clear(bounds) - - let gradientColors = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.35).cgColor] as CFArray - - var locations: [CGFloat] = [0.0, 1.0] - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! - - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 64.0, y: 0.0), options: CGGradientDrawingOptions()) -}) - open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate { public let scrollNode: ASScrollNode - private let leftFadeNode: ASImageNode - private let rightFadeNode: ASImageNode private var containerLayout: ContainerViewLayout? @@ -59,17 +31,7 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { self.scrollNode.view.contentInsetAdjustmentBehavior = .never } - - self.leftFadeNode = ASImageNode() - self.leftFadeNode.contentMode = .scaleToFill - self.leftFadeNode.image = leftFadeImage - self.leftFadeNode.alpha = 0.0 - - self.rightFadeNode = ASImageNode() - self.rightFadeNode.contentMode = .scaleToFill - self.rightFadeNode.image = rightFadeImage - self.rightFadeNode.alpha = 0.0 - + super.init() self.scrollNode.view.delegate = self @@ -91,38 +53,17 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate } return .waitForDoubleTap } - tapRecognizer.highlight = { [weak self] location in - if let strongSelf = self { - let pointInNode = location.flatMap { strongSelf.scrollNode.view.convert($0, to: strongSelf.view) } - let transition: ContainedViewLayoutTransition = .animated(duration: 0.07, curve: .easeInOut) - if let location = pointInNode, location.x < edgeWidth && strongSelf.canGoToPreviousItem() { - transition.updateAlpha(node: strongSelf.leftFadeNode, alpha: 1.0) - } else { - transition.updateAlpha(node: strongSelf.leftFadeNode, alpha: 0.0) - } - if let location = pointInNode, location.x > strongSelf.frame.width - edgeWidth && strongSelf.canGoToNextItem() { - transition.updateAlpha(node: strongSelf.rightFadeNode, alpha: 1.0) - } else { - transition.updateAlpha(node: strongSelf.rightFadeNode, alpha: 0.0) - } - } - } self.scrollNode.view.addGestureRecognizer(tapRecognizer) self.addSubnode(self.scrollNode) - self.addSubnode(self.leftFadeNode) - self.addSubnode(self.rightFadeNode) } @objc open func contentTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { if recognizer.state == .ended { if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { let pointInNode = self.scrollNode.view.convert(location, to: self.view) - if pointInNode.x < 44.0 { - self.goToPreviousItem() - } else if pointInNode.x > self.frame.width - 44.0 { - self.goToNextItem() + if pointInNode.x < 44.0 || pointInNode.x > self.frame.width - 44.0 { } else { switch gesture { case .tap: @@ -164,10 +105,6 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate } self.containerLayout = layout - let fadeWidth = min(72.0, layout.size.width * 0.2) - self.leftFadeNode.frame = CGRect(x: 0.0, y: 0.0, width: fadeWidth, height: layout.size.height) - self.rightFadeNode.frame = CGRect(x: layout.size.width - fadeWidth, y: 0.0, width: fadeWidth, height: layout.size.height) - if shouldResetContents { var previousFrame: CGRect? var previousScale: CGFloat? diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift index 1c8e880f0f..5a9890b840 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift @@ -324,7 +324,7 @@ private func peersNearbyControllerEntries(data: PeersNearbyData?, state: PeersNe entries.append(.visibility(presentationData.theme, visible ? presentationData.strings.PeopleNearby_MakeInvisible : presentationData.strings.PeopleNearby_MakeVisible, visible)) if let data = data, !data.users.isEmpty { - var i: Int32 = 0 + var index: Int32 = 0 var users = data.users var effectiveExpanded = expanded if users.count > maxUsersDisplayedLimit && !expanded { @@ -335,8 +335,8 @@ private func peersNearbyControllerEntries(data: PeersNearbyData?, state: PeersNe for user in users { if user.peer.0.id != data.accountPeerId { - entries.append(.user(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, user)) - i += 1 + entries.append(.user(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, user)) + index += 1 } } @@ -475,14 +475,14 @@ public func peersNearbyController(context: AccountContext) -> ViewController { let _ = (coordinatePromise.get() |> deliverOnMainQueue).start(next: { coordinate in if let coordinate = coordinate { - let _ = peersNearbyUpdateVisibility(account: context.account, update: .visible(latitude: coordinate.latitude, longitude: coordinate.longitude), background: false).start() + let _ = updatePeersNearbyVisibility(account: context.account, update: .visible(latitude: coordinate.latitude, longitude: coordinate.longitude), background: false).start() } }) })]), nil) } else { - let _ = peersNearbyUpdateVisibility(account: context.account, update: .invisible, background: false).start() + let _ = updatePeersNearbyVisibility(account: context.account, update: .invisible, background: false).start() } }, openProfile: { peer in navigateToProfileImpl?(peer) @@ -642,11 +642,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController { } navigateToChatImpl = { [weak controller] peer in if let navigationController = controller?.navigationController as? NavigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: { [weak navigationController] in - if let navigationController = navigationController, let chatController = navigationController.viewControllers.last as? ChatController { - replaceAllButRootControllerImpl?(chatController, false) - } - })) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer.id), keepStack: .always, purposefulAction: {})) } } pushControllerImpl = { [weak controller] c in diff --git a/submodules/TelegramCore/Sources/PeersNearby.swift b/submodules/TelegramCore/Sources/PeersNearby.swift index 99e8dedf7e..83d82b6e8a 100644 --- a/submodules/TelegramCore/Sources/PeersNearby.swift +++ b/submodules/TelegramCore/Sources/PeersNearby.swift @@ -25,7 +25,7 @@ public enum PeerNearbyVisibilityUpdate { case invisible } -public func peersNearbyUpdateVisibility(account: Account, update: PeerNearbyVisibilityUpdate, background: Bool) -> Signal { +public func updatePeersNearbyVisibility(account: Account, update: PeerNearbyVisibilityUpdate, background: Bool) -> Signal { var flags: Int32 = 0 var geoPoint: Api.InputGeoPoint var selfExpires: Int32? @@ -61,8 +61,12 @@ public func peersNearbyUpdateVisibility(account: Account, update: PeerNearbyVisi return account.network.request(Api.functions.contacts.getLocated(flags: flags, geoPoint: geoPoint, selfExpires: selfExpires)) |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> `catch` { error -> Signal in + if error.errorCode == 406 { + return .single(nil) + } else { + return .single(nil) + } } |> mapToSignal { updates -> Signal in if let updates = updates { diff --git a/submodules/TelegramUI/TelegramUI/AccountContext.swift b/submodules/TelegramUI/TelegramUI/AccountContext.swift index 02fefc79c0..6d47f48f3c 100644 --- a/submodules/TelegramUI/TelegramUI/AccountContext.swift +++ b/submodules/TelegramUI/TelegramUI/AccountContext.swift @@ -120,6 +120,7 @@ public final class AccountContextImpl: AccountContext { public let downloadedMediaStoreManager: DownloadedMediaStoreManager public let liveLocationManager: LiveLocationManager? + public let peersNearbyManager: PeersNearbyManager? public let wallpaperUploadManager: WallpaperUploadManager? private let themeUpdateManager: ThemeUpdateManager? @@ -197,6 +198,12 @@ public final class AccountContextImpl: AccountContext { self.themeUpdateManager = nil } + if let locationManager = self.sharedContextImpl.locationManager, sharedContext.applicationBindings.isMainApp && !temp { + self.peersNearbyManager = PeersNearbyManagerImpl(account: account, locationManager: locationManager) + } else { + self.peersNearbyManager = nil + } + let updatedLimitsConfiguration = account.postbox.preferencesView(keys: [PreferencesKeys.limitsConfiguration]) |> map { preferences -> LimitsConfiguration in return preferences.values[PreferencesKeys.limitsConfiguration] as? LimitsConfiguration ?? LimitsConfiguration.defaultValue diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index a6ac25c1ff..0731b00c15 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -933,7 +933,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { |> deliverOnMainQueue } - if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, firstScalar.value == 0x2764 { + let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D] + + if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) { let _ = startTime.start(next: { [weak self] time in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/TelegramUI/PeersNearbyManager.swift b/submodules/TelegramUI/TelegramUI/PeersNearbyManager.swift new file mode 100644 index 0000000000..cc899c1608 --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/PeersNearbyManager.swift @@ -0,0 +1,80 @@ +import Foundation +import SwiftSignalKit +import Postbox +import SyncCore +import TelegramCore +import TelegramApi +import DeviceLocationManager +import CoreLocation +import AccountContext + +private let locationUpdateTimePeriod: Double = 1.0 * 60.0 * 60.0 +private let locationDistanceUpdateThreshold: Double = 1000 + +final class PeersNearbyManagerImpl: PeersNearbyManager { + private let account: Account + private let locationManager: DeviceLocationManager + + private var preferencesDisposable: Disposable? + private var locationDisposable = MetaDisposable() + private var updateDisposable = MetaDisposable() + + private var previousLocation: CLLocation? + + init(account: Account, locationManager: DeviceLocationManager) { + self.account = account + self.locationManager = locationManager + + self.preferencesDisposable = (account.postbox.preferencesView(keys: [PreferencesKeys.peersNearby]) + |> map { view -> Int32? in + let state = view.values[PreferencesKeys.peersNearby] as? PeersNearbyState ?? .default + return state.visibilityExpires + } + |> distinctUntilChanged).start(next: { [weak self] visibility in + if let strongSelf = self { + strongSelf.visibilityUpdated(visible: visibility != nil) + } + }) + } + + deinit { + self.preferencesDisposable?.dispose() + self.locationDisposable.dispose() + self.updateDisposable.dispose() + } + + private func visibilityUpdated(visible: Bool) { + if visible { + let account = self.account + let poll = currentLocationManagerCoordinate(manager: self.locationManager, timeout: 5.0) + let signal = (poll |> then(.complete() |> suspendAwareDelay(locationUpdateTimePeriod, queue: Queue.concurrentDefaultQueue()))) |> restart + self.locationDisposable.set(signal.start(next: { [weak self] coordinate in + if let strongSelf = self, let coordinate = coordinate { + let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) + var update = true + if let previousLocation = strongSelf.previousLocation, location.distance(from: previousLocation) < locationDistanceUpdateThreshold { + update = false + } + if update { + strongSelf.updateLocation(location) + strongSelf.previousLocation = location + } + } + })) + } else { + self.previousLocation = nil + self.locationDisposable.set(nil) + self.updateDisposable.set(nil) + } + } + + private func updateLocation(_ location: CLLocation) { + self.updateDisposable.set(updatePeersNearbyVisibility(account: self.account, update: .location(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude), background: true).start(error: { [weak self] _ in + if let strongSelf = self { + let _ = updatePeersNearbyVisibility(account: strongSelf.account, update: .invisible, background: false).start() + strongSelf.locationDisposable.set(nil) + strongSelf.updateDisposable.set(nil) + } + })) + } +} diff --git a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift index d09e8f2097..6d420a7ded 100644 --- a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift +++ b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift @@ -153,11 +153,7 @@ final class WalletTransactionInfoScreen: ViewController { private var walletStateDisposable: Disposable? private var combinedState: CombinedWalletState? private var reloadingState = false - - private var previousScreenBrightness: CGFloat? - private var displayLinkAnimator: DisplayLinkAnimator? - private let idleTimerExtensionDisposable: Disposable - + public init(context: WalletContext, walletInfo: WalletInfo?, walletTransaction: WalletInfoTransaction, walletState: Signal<(CombinedWalletState, Bool), NoError>, enableDebugActions: Bool) { self.context = context self.walletInfo = walletInfo @@ -168,9 +164,7 @@ final class WalletTransactionInfoScreen: ViewController { let defaultTheme = self.presentationData.theme.navigationBar let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) - - self.idleTimerExtensionDisposable = context.idleTimerExtension() - + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Wallet_Navigation_Back, close: self.presentationData.strings.Wallet_Navigation_Close))) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) @@ -193,7 +187,6 @@ final class WalletTransactionInfoScreen: ViewController { } deinit { - self.idleTimerExtensionDisposable.dispose() self.walletStateDisposable?.dispose() } From 507e17f40f548033ddd9b051d4f710a7c719acc3 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 19 Feb 2020 00:10:28 +0400 Subject: [PATCH 4/6] Tab support --- .../Sources/ChatListController.swift | 163 ++++++- .../Sources/ChatListControllerNode.swift | 24 +- .../ChatListFilterPresetController.swift | 59 ++- .../ChatListFilterPresetListController.swift | 152 ++++++- .../ChatListFilterPresetListItem.swift | 6 +- .../ChatListFilterTabContainerNode.swift | 336 ++++++++++++++ .../Sources/Node/ChatListNode.swift | 40 +- .../Sources/Node/ChatListViewTransition.swift | 6 +- .../TabBarChatListFilterController.swift | 117 +++++ submodules/Display/Display/ListView.swift | 12 + .../Display/Display/NavigationBar.swift | 71 ++- .../Display/Display/TabBarController.swift | 12 +- .../Display/Display/ViewController.swift | 24 +- .../Sources/ItemListPeerActionItem.swift | 2 +- .../LegacyComponents/TGMediaVideoConverter.m | 7 + .../FFMpegMediaFrameSourceContext.swift | 2 +- .../Sources/SoftwareVideoSource.swift | 2 +- .../UniversalSoftwareVideoSource.swift | 2 +- .../Sources/MutableBasicPeerView.swift | 8 + submodules/TelegramApi/Sources/Api0.swift | 30 +- submodules/TelegramApi/Sources/Api1.swift | 412 +++++++++++++++++- submodules/TelegramApi/Sources/Api2.swift | 124 ++++++ submodules/TelegramApi/Sources/Api3.swift | 78 ++++ .../TelegramCore/Sources/AccountManager.swift | 2 +- .../Sources/ChatListFiltering.swift | 10 +- .../Sources/ManagedChatListHoles.swift | 17 +- .../Sources/StoreMessage_Telegram.swift | 122 +++--- .../Sources/UpdateCachedPeerData.swift | 2 +- .../TelegramUI/DeclareEncodables.swift | 1 + .../TelegramUI/FetchVideoMediaResource.swift | 2 + .../Sources/ChatListFilterSettings.swift | 43 ++ submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.h | 2 +- submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.m | 4 +- submodules/ffmpeg/FFMpeg/FFMpegRemuxer.m | 66 ++- 34 files changed, 1759 insertions(+), 201 deletions(-) create mode 100644 submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift create mode 100644 submodules/TelegramUIPreferences/Sources/ChatListFilterSettings.swift diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 1eb8d5bb87..ce8ab94f0b 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -95,7 +95,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent } } -public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate/*, TabBarContainedController*/ { +public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate, TabBarContainedController { private var validLayout: ContainerViewLayout? public let context: AccountContext @@ -133,9 +133,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, private var presentationDataDisposable: Disposable? private let stateDisposable = MetaDisposable() + private var filterDisposable: Disposable? private var searchContentNode: NavigationBarSearchContentNode? + private let tabContainerNode: ChatListFilterTabContainerNode + private var tabContainerData: ([ChatListFilterTabEntry], ChatListFilterTabEntryId)? + + private let chatListFilterValue = Promise() + public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { if self.isNodeLoaded { self.chatListDisplayNode.chatListNode.updateSelectedChatLocation(data as? ChatLocation, progress: progress, transition: transition) @@ -156,6 +162,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, self.titleView = ChatListTitleView(theme: self.presentationData.theme, strings: self.presentationData.strings) + self.tabContainerNode = ChatListFilterTabContainerNode() + super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -265,14 +273,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, context.account.networkState, hasProxy, passcode, - self.chatListDisplayNode.chatListNode.state, - self.chatListDisplayNode.chatListNode.chatListFilterSignal - ).start(next: { [weak self] networkState, proxy, passcode, state, chatListFilter in + self.chatListDisplayNode.chatListNode.state + ).start(next: { [weak self] networkState, proxy, passcode, state in if let strongSelf = self { let defaultTitle: String if strongSelf.groupId == .root { - if let chatListFilter = chatListFilter { - let title: String = chatListFilter.title ?? "" + if let chatListFilter = strongSelf.filter { + let title: String = chatListFilter.title ?? strongSelf.presentationData.strings.DialogList_Title defaultTitle = title } else { defaultTitle = strongSelf.presentationData.strings.DialogList_Title @@ -397,6 +404,107 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, preconditionFailure("debug tap") } } + + if self.filter == nil { + let preferencesKey: PostboxViewKey = .preferences(keys: Set([ + ApplicationSpecificPreferencesKeys.chatListFilterSettings + ])) + let filterItems = chatListFilterItems(context: context) + |> map { items -> [ChatListFilterTabEntry] in + var result: [ChatListFilterTabEntry] = [] + result.append(.all) + for (filter, unreadCount) in items { + result.append(.filter(id: filter.id, text: filter.title ?? "", unreadCount: unreadCount)) + } + return result + } + |> distinctUntilChanged + self.filterDisposable = (combineLatest(queue: .mainQueue(), + context.account.postbox.combinedView(keys: [ + preferencesKey + ]), + filterItems, + self.chatListFilterValue.get() |> map { $0?.id } |> distinctUntilChanged + ) + |> deliverOnMainQueue).start(next: { [weak self] combinedView, filterItems, selectedFilter in + guard let strongSelf = self else { + return + } + var filterSettings: ChatListFilterSettings = .default + if let preferencesView = combinedView.views[preferencesKey] as? PreferencesView { + if let value = preferencesView.values[ApplicationSpecificPreferencesKeys.chatListFilterSettings] as? ChatListFilterSettings { + filterSettings = value + } + } + + var resolvedItems = filterItems + if !filterSettings.displayTabs { + resolvedItems = [] + } + + var wasEmpty = false + if let tabContainerData = strongSelf.tabContainerData { + wasEmpty = tabContainerData.0.count <= 1 + } else { + wasEmpty = true + } + let selectedEntryId: ChatListFilterTabEntryId = selectedFilter.flatMap { .filter($0) } ?? .all + strongSelf.tabContainerData = (resolvedItems, selectedEntryId) + + let isEmpty = resolvedItems.count <= 1 + + if wasEmpty != isEmpty { + strongSelf.navigationBar?.setSecondaryContentNode(isEmpty ? nil : strongSelf.tabContainerNode) + if let parentController = strongSelf.parent as? TabBarController { + parentController.navigationBar?.setSecondaryContentNode(isEmpty ? nil : strongSelf.tabContainerNode) + } + } + + if let layout = strongSelf.validLayout { + if wasEmpty != isEmpty { + strongSelf.containerLayoutUpdated(layout, transition: .immediate) + (strongSelf.parent as? TabBarController)?.updateLayout() + } else { + strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + } + } + }) + } + + self.tabContainerNode.tabSelected = { [weak self] id in + guard let strongSelf = self else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction -> [ChatListFilter] in + let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default + return settings.filters + } + |> deliverOnMainQueue).start(next: { [weak self] filters in + guard let strongSelf = self else { + return + } + switch id { + case .all: + strongSelf.chatListDisplayNode.chatListNode.chatListFilter = nil + case let .filter(id): + var found = false + for filter in filters { + if filter.id == id { + strongSelf.chatListDisplayNode.chatListNode.chatListFilter = filter + found = true + break + } + } + if !found { + strongSelf.chatListDisplayNode.chatListNode.chatListFilter = nil + } + } + }) + } + + self.tabContainerNode.addFilter = { [weak self] in + self?.openFilterSettings() + } } required public init(coder aDecoder: NSCoder) { @@ -412,6 +520,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, self.suggestLocalizationDisposable.dispose() self.presentationDataDisposable?.dispose() self.stateDisposable.dispose() + self.filterDisposable?.dispose() } private func updateThemeAndStrings() { @@ -461,6 +570,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, override public func loadDisplayNode() { self.displayNode = ChatListControllerNode(context: self.context, groupId: self.groupId, filter: self.filter, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, controller: self) + self.chatListFilterValue.set(self.chatListDisplayNode.chatListNode.chatListFilterSignal) self.chatListDisplayNode.navigationBar = self.navigationBar @@ -726,7 +836,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } } - self.stateDisposable.set(combineLatest(queue: .mainQueue(), self.presentationDataValue.get(), peerIdsAndOptions).start(next: { [weak self] presentationData, peerIdsAndOptions in + self.stateDisposable.set(combineLatest(queue: .mainQueue(), + self.presentationDataValue.get(), + peerIdsAndOptions + ).start(next: { [weak self] presentationData, peerIdsAndOptions in guard let strongSelf = self else { return } @@ -735,13 +848,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, if let (options, peerIds) = peerIdsAndOptions { let leftAction: ToolbarAction switch options.read { - case let .all(enabled): - leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled) - case let .selective(enabled): - leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled) + case let .all(enabled): + leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled) + case let .selective(enabled): + leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled) } var archiveEnabled = options.delete - if archiveEnabled { + if archiveEnabled && strongSelf.filter == nil { for peerId in peerIds { if peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: 777000) { archiveEnabled = false @@ -919,12 +1032,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, self.validLayout = layout + var tabContainerOffset: CGFloat = 0.0 + if !self.displayNavigationBar { + tabContainerOffset += layout.statusBarHeight ?? 0.0 + tabContainerOffset += 44.0 + 44.0 + 44.0 + } + + transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: self.visualNavigationInsetHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0))) + self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.tabContainerData?.1, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + if let searchContentNode = self.searchContentNode, layout.inVoiceOver != wasInVoiceOver { searchContentNode.updateListVisibleContentOffset(.known(0.0)) self.chatListDisplayNode.chatListNode.scrollToPosition(.top) } - self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, visualNavigationHeight: self.visualNavigationInsetHeight, transition: transition) + self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, visualNavigationHeight: self.visualNavigationInsetHeight, cleanNavigationBarHeight: self.cleanNavigationHeight, transition: transition) } override public func navigationStackConfigurationUpdated(next: [ViewController]) { @@ -934,7 +1056,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, @objc private func editPressed() { 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 .root = self.groupId { + if case .root = self.groupId, self.filter == nil { self.navigationItem.leftBarButtonItem = editItem (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(.details, transition: .animated(duration: 0.5, curve: .spring)) } else { @@ -954,7 +1076,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, @objc private func donePressed() { let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) editItem.accessibilityLabel = self.presentationData.strings.Common_Edit - if case .root = self.groupId { + if case .root = self.groupId, self.filter == nil { self.navigationItem.leftBarButtonItem = editItem } else { self.navigationItem.rightBarButtonItem = editItem @@ -1788,7 +1910,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } override public func setToolbar(_ toolbar: Toolbar?, transition: ContainedViewLayoutTransition) { - if case .root = self.groupId { + if case .root = self.groupId, self.filter == nil { super.setToolbar(toolbar, transition: transition) } else { self.chatListDisplayNode.toolbar = toolbar @@ -1804,7 +1926,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } } - /*public func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode]) { + private func openFilterSettings() { + self.push(chatListFilterPresetListController(context: self.context, updated: { _ in + })) + } + + public func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode]) { if self.isNodeLoaded { let _ = (self.context.account.postbox.transaction { transaction -> [ChatListFilter] in let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default @@ -1849,5 +1976,5 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, public func updateTabBarPreviewingControllerPresentation(_ update: TabBarContainedControllerPresentationUpdate) { - }*/ + } } diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 9eb336e963..9c6b8d1af1 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -59,7 +59,7 @@ final class ChatListControllerNode: ASDisplayNode { private(set) var searchDisplayController: SearchDisplayController? - private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat)? + private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat)? var requestDeactivateSearch: (() -> Void)? var requestOpenPeerFromSearch: ((Peer, Bool) -> Void)? @@ -102,8 +102,8 @@ final class ChatListControllerNode: ASDisplayNode { let chatListEmptyNode = ChatListEmptyNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings) strongSelf.chatListEmptyNode = chatListEmptyNode strongSelf.insertSubnode(chatListEmptyNode, belowSubnode: strongSelf.chatListNode) - if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate) + if let (layout, navigationHeight, visualNavigationHeight, cleanNavigationBarHeight) = strongSelf.containerLayout { + strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, transition: .immediate) } strongSelf.isEmptyUpdated?(true) } @@ -114,9 +114,7 @@ final class ChatListControllerNode: ASDisplayNode { default: if let chatListEmptyNode = strongSelf.chatListEmptyNode { strongSelf.chatListEmptyNode = nil - chatListEmptyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak chatListEmptyNode] _ in - chatListEmptyNode?.removeFromSupernode() - }) + chatListEmptyNode.removeFromSupernode() } } switch isEmptyState { @@ -125,8 +123,8 @@ final class ChatListControllerNode: ASDisplayNode { let chatListEmptyIndicator = ActivityIndicator(type: .custom(strongSelf.presentationData.theme.list.itemSecondaryTextColor, 22.0, 1.0, false)) strongSelf.chatListEmptyIndicator = chatListEmptyIndicator strongSelf.insertSubnode(chatListEmptyIndicator, belowSubnode: strongSelf.chatListNode) - if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate) + if let (layout, navigationHeight, visualNavigationHeight, cleanNavigationBarHeight) = strongSelf.containerLayout { + strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, transition: .immediate) } } default: @@ -160,8 +158,8 @@ final class ChatListControllerNode: ASDisplayNode { } } - func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { - self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight) + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight) var insets = layout.insets(options: [.input]) insets.top += navigationBarHeight @@ -233,12 +231,12 @@ final class ChatListControllerNode: ASDisplayNode { } if let searchDisplayController = self.searchDisplayController { - searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) + searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition) } } func activateSearch(placeholderNode: SearchBarPlaceholderNode) { - guard let (containerLayout, navigationBarHeight, _) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else { + guard let (containerLayout, navigationBarHeight, _, cleanNavigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else { return } @@ -262,7 +260,7 @@ final class ChatListControllerNode: ASDisplayNode { }) self.chatListNode.accessibilityElementsHidden = true - self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) + self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: cleanNavigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in if let strongSelf = self, let strongPlaceholderNode = placeholderNode { if isSearchBar { diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index 321f7685c2..49fd02eac3 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -1,4 +1,4 @@ -/*import Foundation +import Foundation import UIKit import Display import SwiftSignalKit @@ -58,6 +58,7 @@ private enum ChatListFilterPresetEntryStableId: Hashable { private enum ChatListFilterPresetEntry: ItemListNodeEntry { case nameHeader(String) case name(placeholder: String, value: String) + case typesHeader(text: String) case filterPrivateChats(title: String, value: Bool) case filterSecretChats(title: String, value: Bool) case filterPrivateGroups(title: String, value: Bool) @@ -75,7 +76,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { switch self { case .nameHeader, .name: return ChatListFilterPresetControllerSection.name.rawValue - case .filterPrivateChats, .filterSecretChats, .filterPrivateGroups, .filterBots, .filterPublicGroups, .filterChannels: + case .typesHeader, .filterPrivateChats, .filterSecretChats, .filterPrivateGroups, .filterBots, .filterPublicGroups, .filterChannels: return ChatListFilterPresetControllerSection.categories.rawValue case .filterMuted, .filterRead: return ChatListFilterPresetControllerSection.excludeCategories.rawValue @@ -90,26 +91,28 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return .index(0) case .name: return .index(1) - case .filterPrivateChats: + case .typesHeader: return .index(2) - case .filterSecretChats: + case .filterPrivateChats: return .index(3) - case .filterPrivateGroups: + case .filterSecretChats: return .index(4) - case .filterBots: + case .filterPrivateGroups: return .index(5) case .filterPublicGroups: return .index(6) case .filterChannels: return .index(7) - case .filterMuted: + case .filterBots: return .index(8) - case .filterRead: + case .filterMuted: return .index(9) - case .additionalPeersHeader: + case .filterRead: return .index(10) - case .addAdditionalPeer: + case .additionalPeersHeader: return .index(11) + case .addAdditionalPeer: + return .index(12) case let .additionalPeer(additionalPeer): return .peer(additionalPeer.peer.peerId) case .additionalPeerInfo: @@ -172,6 +175,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return state } }, action: {}) + case let .typesHeader(text): + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .filterPrivateChats(title, value): return filterEntry(presentationData: presentationData, arguments: arguments, title: title, value: value, filter: .privateChats, section: self.section) case let .filterSecretChats(title, value): @@ -243,21 +248,22 @@ private struct ChatListFilterPresetControllerState: Equatable { private func chatListFilterPresetControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetControllerState, peers: [RenderedPeer]) -> [ChatListFilterPresetEntry] { var entries: [ChatListFilterPresetEntry] = [] - entries.append(.nameHeader("NAME")) - entries.append(.name(placeholder: "Preset Name", value: state.name)) + entries.append(.nameHeader("FILTER NAME")) + entries.append(.name(placeholder: "Filter Name", value: state.name)) + entries.append(.typesHeader(text: "INCLUDE CHAT TYPES")) entries.append(.filterPrivateChats(title: "Private Chats", value: state.includeCategories.contains(.privateChats))) entries.append(.filterSecretChats(title: "Secret Chats", value: state.includeCategories.contains(.secretChats))) entries.append(.filterPrivateGroups(title: "Private Groups", value: state.includeCategories.contains(.privateGroups))) - entries.append(.filterBots(title: "Bots", value: state.includeCategories.contains(.bots))) entries.append(.filterPublicGroups(title: "Public Groups", value: state.includeCategories.contains(.publicGroups))) entries.append(.filterChannels(title: "Channels", value: state.includeCategories.contains(.channels))) + entries.append(.filterBots(title: "Bots", value: state.includeCategories.contains(.bots))) entries.append(.filterMuted(title: "Exclude Muted", value: state.excludeMuted)) entries.append(.filterRead(title: "Exclude Read", value: state.excludeRead)) entries.append(.additionalPeersHeader("ALWAYS INCLUDE")) - entries.append(.addAdditionalPeer(title: "Add")) + entries.append(.addAdditionalPeer(title: "Add Chats")) for peer in peers { entries.append(.additionalPeer(index: entries.count, peer: peer, isRevealed: state.revealedPeerId == peer.peerId)) @@ -273,7 +279,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat if let currentPreset = currentPreset { initialName = currentPreset.title ?? "" } else { - initialName = "New Preset" + initialName = "New Filter" } let initialState = ChatListFilterPresetControllerState(name: initialName, includeCategories: currentPreset?.categories ?? .all, excludeMuted: currentPreset?.excludeMuted ?? false, excludeRead: currentPreset?.excludeRead ?? false, additionallyIncludePeers: currentPreset?.includePeers ?? []) let stateValue = Atomic(value: initialState) @@ -365,17 +371,29 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { dismissImpl?() }) - let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: { + let rightNavigationButton = ItemListNavigationButton(content: .text("Create"), style: .bold, enabled: state.isComplete, action: { let state = stateValue.with { $0 } let preset = ChatListFilter(id: currentPreset?.id ?? -1, title: state.name, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, includePeers: state.additionallyIncludePeers) let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in var preset = preset if currentPreset == nil { - preset.id = max(2, settings.filters.map({ $0.id }).max() ?? 2) + preset.id = max(2, settings.filters.map({ $0.id + 1 }).max() ?? 2) } var settings = settings - settings.filters = settings.filters.filter { $0 != preset && $0 != currentPreset } - settings.filters.append(preset) + if let currentPreset = currentPreset { + var found = false + for i in 0 ..< settings.filters.count { + if settings.filters[i].id == preset.id { + settings.filters[i] = preset + found = true + } + } + if !found { + settings.filters.append(preset) + } + } else { + settings.filters.append(preset) + } return settings }) |> deliverOnMainQueue).start(next: { settings in @@ -384,7 +402,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat }) }) - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.SocksProxySetup_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(currentPreset != nil ? "Filter" : "Create Filter"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetControllerEntries(presentationData: presentationData, state: state, peers: statePeers), style: .blocks, emptyStateItem: nil, animateChanges: true) return (controllerState, (listState, arguments)) @@ -405,4 +423,3 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat return controller } -*/ diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index 3ca80247d1..4099b13d37 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -1,4 +1,4 @@ -/*import Foundation +import Foundation import UIKit import Display import SwiftSignalKit @@ -9,17 +9,20 @@ import TelegramPresentationData import TelegramUIPreferences import ItemListUI import AccountContext +import ItemListPeerActionItem private final class ChatListFilterPresetListControllerArguments { let context: AccountContext + let toggleEnableTabs: (Bool) -> Void let openPreset: (ChatListFilter) -> Void let addNew: () -> Void let setItemWithRevealedOptions: (Int32?, Int32?) -> Void let removePreset: (Int32) -> Void - init(context: AccountContext, openPreset: @escaping (ChatListFilter) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void, removePreset: @escaping (Int32) -> Void) { + init(context: AccountContext, toggleEnableTabs: @escaping (Bool) -> Void, openPreset: @escaping (ChatListFilter) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void, removePreset: @escaping (Int32) -> Void) { self.context = context + self.toggleEnableTabs = toggleEnableTabs self.openPreset = openPreset self.addNew = addNew self.setItemWithRevealedOptions = setItemWithRevealedOptions @@ -28,6 +31,7 @@ private final class ChatListFilterPresetListControllerArguments { } private enum ChatListFilterPresetListSection: Int32 { + case tabs case list } @@ -44,6 +48,8 @@ private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings } private enum ChatListFilterPresetListEntryStableId: Hashable { + case displayTabs + case displayTabsFooter case listHeader case preset(Int32) case addItem @@ -51,13 +57,17 @@ private enum ChatListFilterPresetListEntryStableId: Hashable { } private enum ChatListFilterPresetListEntry: ItemListNodeEntry { + case displayTabs(String, Bool) + case displayTabsFooter(String) case listHeader(String) - case preset(index: Int, title: String?, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool) - case addItem(String) + case preset(index: Int, title: String?, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool, isEditing: Bool) + case addItem(text: String, isEditing: Bool) case listFooter(String) var section: ItemListSectionId { switch self { + case .displayTabs, .displayTabsFooter: + return ChatListFilterPresetListSection.tabs.rawValue case .listHeader, .preset, .addItem, .listFooter: return ChatListFilterPresetListSection.list.rawValue } @@ -65,10 +75,14 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { var sortId: Int { switch self { - case .listHeader: + case .displayTabs: return 0 + case .displayTabsFooter: + return 1 + case .listHeader: + return 2 case let .preset(preset): - return 1 + preset.index + return 3 + preset.index case .addItem: return 1000 case .listFooter: @@ -78,6 +92,10 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { var stableId: ChatListFilterPresetListEntryStableId { switch self { + case .displayTabs: + return .displayTabs + case .displayTabsFooter: + return .displayTabsFooter case .listHeader: return .listHeader case let .preset(preset): @@ -96,18 +114,24 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! ChatListFilterPresetListControllerArguments switch self { + case let .displayTabs(title, value): + return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.toggleEnableTabs(value) + }) + case let .displayTabsFooter(text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .listHeader(text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section) - case let .preset(index, title, preset, canBeReordered, canBeDeleted): - return ChatListFilterPresetListItem(presentationData: presentationData, preset: preset, title: title ?? "", editing: ChatListFilterPresetListItemEditing(editable: true, editing: false, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, sectionId: self.section, action: { + case let .preset(index, title, preset, canBeReordered, canBeDeleted, isEditing): + return ChatListFilterPresetListItem(presentationData: presentationData, preset: preset, title: title ?? "", editing: ChatListFilterPresetListItemEditing(editable: true, editing: isEditing, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, sectionId: self.section, action: { arguments.openPreset(preset) }, setItemWithRevealedOptions: { lhs, rhs in arguments.setItemWithRevealedOptions(lhs, rhs) }, remove: { arguments.removePreset(preset.id) }) - case let .addItem(text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { + case let .addItem(text, isEditing): + return ItemListPeerActionItem(presentationData: presentationData, icon: nil, title: text, sectionId: self.section, height: .generic, editing: isEditing, action: { arguments.addNew() }) case let .listFooter(text): @@ -117,18 +141,22 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { } private struct ChatListFilterPresetListControllerState: Equatable { + var isEditing: Bool = false var revealedPreset: Int32? = nil } -private func chatListFilterPresetListControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetListControllerState, settings: ChatListFiltersState) -> [ChatListFilterPresetListEntry] { +private func chatListFilterPresetListControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetListControllerState, filtersState: ChatListFiltersState, settings: ChatListFilterSettings) -> [ChatListFilterPresetListEntry] { var entries: [ChatListFilterPresetListEntry] = [] + + entries.append(.displayTabs("Show Tabs", settings.displayTabs)) + entries.append(.displayTabsFooter("Display filter tabs on the main screen for quick switching.")) entries.append(.listHeader("FILTERS")) - for preset in settings.filters { - entries.append(.preset(index: entries.count, title: preset.title, preset: preset, canBeReordered: settings.filters.count > 1, canBeDeleted: true)) + for preset in filtersState.filters { + entries.append(.preset(index: entries.count, title: preset.title, preset: preset, canBeReordered: filtersState.filters.count > 1, canBeDeleted: true, isEditing: state.isEditing)) } - entries.append(.addItem("Add New")) - entries.append(.listFooter("Add custom presets")) + entries.append(.addItem(text: "Create New Filter", isEditing: state.isEditing)) + entries.append(.listFooter("Tap \"Edit\" to change the order or delete filters.")) return entries } @@ -145,7 +173,15 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap var pushControllerImpl: ((ViewController) -> Void)? var presentControllerImpl: ((ViewController, Any?) -> Void)? - let arguments = ChatListFilterPresetListControllerArguments(context: context, openPreset: { preset in + let arguments = ChatListFilterPresetListControllerArguments(context: context, toggleEnableTabs: { value in + let _ = context.account.postbox.transaction({ transaction -> Void in + let _ = updateChatListFilterSettings(transaction: transaction, { settings in + var settings = settings + settings.displayTabs = value + return settings + }) + }).start() + }, openPreset: { preset in pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: preset, updated: updated)) }, addNew: { pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: nil, updated: updated)) @@ -170,7 +206,7 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap }) }) - let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.chatListFilters]) + let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.chatListFilters, ApplicationSpecificPreferencesKeys.chatListFilterSettings]) let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, @@ -178,14 +214,33 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap preferences ) |> map { presentationData, state, preferences -> (ItemListControllerState, (ItemListNodeState, Any)) in - let settings = preferences.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState ?? ChatListFiltersState.default + let filtersState = preferences.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState ?? ChatListFiltersState.default + let filterSettings = preferences.values[ApplicationSpecificPreferencesKeys.chatListFilterSettings] as? ChatListFilterSettings ?? ChatListFilterSettings.default let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Close), style: .regular, enabled: true, action: { let _ = replaceRemoteChatListFilters(account: context.account).start() dismissImpl?() }) + let rightNavigationButton: ItemListNavigationButton + if state.isEditing { + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { + updateState { state in + var state = state + state.isEditing = false + return state + } + }) + } else { + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: { + updateState { state in + var state = state + state.isEditing = true + return state + } + }) + } - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Filter Presets"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetListControllerEntries(presentationData: presentationData, state: state, settings: settings), style: .blocks, animateChanges: true) + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Filters"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetListControllerEntries(presentationData: presentationData, state: state, filtersState: filtersState, settings: filterSettings), style: .blocks, animateChanges: true) return (controllerState, (listState, arguments)) } @@ -203,7 +258,62 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap dismissImpl = { [weak controller] in controller?.dismiss() } + controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [ChatListFilterPresetListEntry]) -> Signal in + let fromEntry = entries[fromIndex] + guard case let .preset(fromFilter) = fromEntry else { + return .single(false) + } + var referenceFilter: ChatListFilter? + var beforeAll = false + var afterAll = false + if toIndex < entries.count { + switch entries[toIndex] { + case let .preset(toFilter): + referenceFilter = toFilter.preset + default: + if entries[toIndex] < fromEntry { + beforeAll = true + } else { + afterAll = true + } + } + } else { + afterAll = true + } + + return updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { filtersState in + var filtersState = filtersState + if let index = filtersState.filters.firstIndex(where: { $0.id == fromFilter.preset.id }) { + filtersState.filters.remove(at: index) + } + if let referenceFilter = referenceFilter { + var inserted = false + for i in 0 ..< filtersState.filters.count { + if filtersState.filters[i].id == referenceFilter.id { + if fromIndex < toIndex { + filtersState.filters.insert(fromFilter.preset, at: i + 1) + } else { + filtersState.filters.insert(fromFilter.preset, at: i) + } + inserted = true + break + } + } + if !inserted { + filtersState.filters.append(fromFilter.preset) + } + } else if beforeAll { + filtersState.filters.insert(fromFilter.preset, at: 0) + } else if afterAll { + filtersState.filters.append(fromFilter.preset) + } + return filtersState + }) + |> map { _ -> Bool in + return false + } + }) return controller } -*/ + diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift index adad852881..be38d24b1d 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift @@ -184,13 +184,13 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)? var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? - let editingOffset: CGFloat = 0.0 + var editingOffset: CGFloat = 0.0 var reorderInset: CGFloat = 0.0 if item.editing.editing && item.canBeReordered { - /*let sizeAndApply = editableControlLayout(item.presentationData.theme, false) + let sizeAndApply = editableControlLayout(item.presentationData.theme, false) editableControlSizeAndApply = sizeAndApply - editingOffset = sizeAndApply.0*/ + editingOffset = sizeAndApply.0 let reorderSizeAndApply = reorderControlLayout(item.presentationData.theme) reorderControlSizeAndApply = reorderSizeAndApply diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift new file mode 100644 index 0000000000..c7b610e409 --- /dev/null +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -0,0 +1,336 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import SyncCore +import Postbox +import TelegramCore +import TelegramPresentationData + +private final class ItemNode: ASDisplayNode { + private let pressed: () -> Void + + private let titleNode: ImmediateTextNode + private let badgeTextNode: ImmediateTextNode + private let badgeBackgroundNode: ASImageNode + private let buttonNode: HighlightTrackingButtonNode + + private var isSelected: Bool = false + private var unreadCount: Int = 0 + + private var theme: PresentationTheme? + + init(pressed: @escaping () -> Void) { + self.pressed = pressed + + self.titleNode = ImmediateTextNode() + self.titleNode.displaysAsynchronously = false + + self.badgeTextNode = ImmediateTextNode() + self.badgeTextNode.displaysAsynchronously = false + + self.badgeBackgroundNode = ASImageNode() + self.badgeBackgroundNode.displaysAsynchronously = false + self.badgeBackgroundNode.displayWithoutProcessing = true + + self.buttonNode = HighlightTrackingButtonNode() + + super.init() + + self.addSubnode(self.titleNode) + self.addSubnode(self.badgeBackgroundNode) + self.addSubnode(self.badgeTextNode) + self.addSubnode(self.buttonNode) + + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + } + + @objc private func buttonPressed() { + self.pressed() + } + + func updateText(title: String, unreadCount: Int, isSelected: Bool, presentationData: PresentationData) { + if self.theme !== presentationData.theme { + self.theme = presentationData.theme + + self.badgeBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.rootController.navigationBar.badgeBackgroundColor) + } + + self.isSelected = isSelected + self.unreadCount = unreadCount + + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(14.0), textColor: isSelected ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemSecondaryTextColor) + if unreadCount != 0 { + self.badgeTextNode.attributedText = NSAttributedString(string: "\(unreadCount)", font: Font.regular(14.0), textColor: presentationData.theme.rootController.navigationBar.badgeTextColor) + } + } + + func updateLayout(height: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { + let titleSize = self.titleNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) + self.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((height - titleSize.height) / 2.0)), size: titleSize) + + let badgeSize = self.badgeTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) + let badgeInset: CGFloat = 5.0 + let badgeBackgroundFrame = CGRect(origin: CGPoint(x: titleSize.width + 5.0, y: floor((height - 18.0) / 2.0)), size: CGSize(width: max(18.0, badgeSize.width + badgeInset * 2.0), height: 18.0)) + self.badgeBackgroundNode.frame = badgeBackgroundFrame + self.badgeTextNode.frame = CGRect(origin: CGPoint(x: badgeBackgroundFrame.minX + floor((badgeBackgroundFrame.width - badgeSize.width) / 2.0), y: badgeBackgroundFrame.minY + floor((badgeBackgroundFrame.height - badgeSize.height) / 2.0)), size: badgeSize) + + if self.unreadCount == 0 { + self.badgeBackgroundNode.alpha = 0.0 + self.badgeTextNode.alpha = 0.0 + return titleSize.width + } else { + self.badgeBackgroundNode.alpha = 1.0 + self.badgeTextNode.alpha = 1.0 + return badgeBackgroundFrame.maxX + } + } + + func updateArea(size: CGSize, sideInset: CGFloat) { + self.buttonNode.frame = CGRect(origin: CGPoint(x: -sideInset, y: 0.0), size: CGSize(width: size.width + sideInset * 2.0, height: size.height)) + } +} + +enum ChatListFilterTabEntryId: Hashable { + case all + case filter(Int32) +} + +enum ChatListFilterTabEntry: Equatable { + case all + case filter(id: Int32, text: String, unreadCount: Int) + + var id: ChatListFilterTabEntryId { + switch self { + case .all: + return .all + case let .filter(filter): + return .filter(filter.id) + } + } + + func title(strings: PresentationStrings) -> String { + switch self { + case .all: + return "All Chats" + case let .filter(filter): + return filter.text + } + } +} + +private final class AddItemNode: HighlightableButtonNode { + private let iconNode: ASImageNode + + var pressed: (() -> Void)? + + private var theme: PresentationTheme? + + override init() { + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + + super.init() + + self.addSubnode(self.iconNode) + + self.addTarget(self, action: #selector(self.onPressed), forControlEvents: .touchUpInside) + } + + @objc private func onPressed() { + self.pressed?() + } + + func update(size: CGSize, theme: PresentationTheme) { + if self.theme !== theme { + self.theme = theme + self.iconNode.image = PresentationResourcesItemList.plusIconImage(theme) + } + + if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size) + } + } +} + +final class ChatListFilterTabContainerNode: ASDisplayNode { + private let scrollNode: ASScrollNode + private let selectedLineNode: ASImageNode + private var itemNodes: [ChatListFilterTabEntryId: ItemNode] = [:] + private let addNode: AddItemNode + + var tabSelected: ((ChatListFilterTabEntryId) -> Void)? + var addFilter: (() -> Void)? + + private var currentParams: (size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, presentationData: PresentationData)? + + override init() { + self.scrollNode = ASScrollNode() + + self.selectedLineNode = ASImageNode() + self.selectedLineNode.displaysAsynchronously = false + self.selectedLineNode.displayWithoutProcessing = true + + self.addNode = AddItemNode() + + super.init() + + self.scrollNode.view.showsHorizontalScrollIndicator = false + self.scrollNode.view.scrollsToTop = false + self.scrollNode.view.delaysContentTouches = false + self.scrollNode.view.canCancelContentTouches = true + if #available(iOS 11.0, *) { + self.scrollNode.view.contentInsetAdjustmentBehavior = .never + } + + self.addSubnode(self.scrollNode) + self.scrollNode.addSubnode(self.selectedLineNode) + self.scrollNode.addSubnode(self.addNode) + + self.addNode.pressed = { [weak self] in + self?.addFilter?() + } + } + + func update(size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { + let focusOnSelectedFilter = self.currentParams?.selectedFilter != selectedFilter + + if self.currentParams?.presentationData.theme !== presentationData.theme { + self.selectedLineNode.image = generateImage(CGSize(width: 7.0, height: 4.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width))) + })?.stretchableImage(withLeftCapWidth: 4, topCapHeight: 1) + } + + self.currentParams = (size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, presentationData: presentationData) + + transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size)) + + for filter in filters { + let itemNode: ItemNode + var wasAdded = false + if let current = self.itemNodes[filter.id] { + itemNode = current + } else { + wasAdded = true + itemNode = ItemNode(pressed: { [weak self] in + self?.tabSelected?(filter.id) + }) + self.itemNodes[filter.id] = itemNode + } + let unreadCount: Int + switch filter { + case .all: + unreadCount = 0 + case let .filter(filter): + unreadCount = filter.unreadCount + } + itemNode.updateText(title: filter.title(strings: presentationData.strings), unreadCount: unreadCount, isSelected: selectedFilter == filter.id, presentationData: presentationData) + } + var removeKeys: [ChatListFilterTabEntryId] = [] + for (id, _) in self.itemNodes { + if !filters.contains(where: { $0.id == id }) { + removeKeys.append(id) + } + } + for id in removeKeys { + if let itemNode = self.itemNodes.removeValue(forKey: id) { + itemNode.removeFromSupernode() + } + } + + var tabSizes: [(CGSize, ItemNode, Bool)] = [] + var totalRawTabSize: CGFloat = 0.0 + var selectionFrames: [CGRect] = [] + + for filter in filters { + guard let itemNode = self.itemNodes[filter.id] else { + continue + } + let wasAdded = itemNode.supernode == nil + if wasAdded { + self.scrollNode.addSubnode(itemNode) + } + let paneNodeWidth = itemNode.updateLayout(height: size.height, transition: transition) + let paneNodeSize = CGSize(width: paneNodeWidth, height: size.height) + tabSizes.append((paneNodeSize, itemNode, wasAdded)) + totalRawTabSize += paneNodeSize.width + } + + let minSpacing: CGFloat = 30.0 + + let sideInset: CGFloat = 16.0 + var leftOffset: CGFloat = sideInset + for i in 0 ..< tabSizes.count { + let (paneNodeSize, paneNode, wasAdded) = tabSizes[i] + let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize) + if wasAdded { + paneNode.frame = paneFrame + paneNode.alpha = 0.0 + transition.updateAlpha(node: paneNode, alpha: 1.0) + } else { + transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame) + } + paneNode.updateArea(size: paneFrame.size, sideInset: minSpacing / 2.0) + paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -minSpacing / 2.0, bottom: 0.0, right: -minSpacing / 2.0) + + selectionFrames.append(paneFrame) + + leftOffset += paneNodeSize.width + minSpacing + } + + let addSize = CGSize(width: 32.0, height: size.height) + transition.updateFrame(node: self.addNode, frame: CGRect(origin: CGPoint(x: max(leftOffset, size.width - sideInset - addSize.width + 6.0), y: 0.0), size: addSize)) + self.addNode.update(size: addSize, theme: presentationData.theme) + leftOffset += addSize.width + minSpacing + + self.scrollNode.view.contentSize = CGSize(width: leftOffset - minSpacing + sideInset, height: size.height) + + let transitionFraction: CGFloat = 0.0 + var selectedFrame: CGRect? + if let selectedFilter = selectedFilter, let currentIndex = filters.index(where: { $0.id == selectedFilter }) { + func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect { + return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t))) + } + + if currentIndex != 0 && transitionFraction > 0.0 { + let currentFrame = selectionFrames[currentIndex] + let previousFrame = selectionFrames[currentIndex - 1] + selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) + } else if currentIndex != filters.count - 1 && transitionFraction < 0.0 { + let currentFrame = selectionFrames[currentIndex] + let previousFrame = selectionFrames[currentIndex + 1] + selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) + } else { + selectedFrame = selectionFrames[currentIndex] + } + } + + if let selectedFrame = selectedFrame { + let wasAdded = self.selectedLineNode.isHidden + self.selectedLineNode.isHidden = false + let lineFrame = CGRect(origin: CGPoint(x: selectedFrame.minX, y: size.height - 4.0), size: CGSize(width: selectedFrame.width, height: 4.0)) + if wasAdded { + self.selectedLineNode.frame = lineFrame + self.selectedLineNode.alpha = 0.0 + transition.updateAlpha(node: self.selectedLineNode, alpha: 1.0) + } else { + transition.updateFrame(node: self.selectedLineNode, frame: lineFrame) + } + if focusOnSelectedFilter { + if selectedFilter == filters.first?.id { + transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size)) + } else if selectedFilter == filters.last?.id { + transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: max(0.0, self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width), y: 0.0), size: self.scrollNode.bounds.size)) + } else { + let contentOffsetX = max(0.0, min(self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, floor(selectedFrame.midX - self.scrollNode.bounds.width / 2.0))) + transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: contentOffsetX, y: 0.0), size: self.scrollNode.bounds.size)) + } + } + } else { + self.selectedLineNode.isHidden = true + } + } +} diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index fdd814e57a..f3aaa459bd 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -13,6 +13,7 @@ import TelegramNotices import ContactsPeerItem import ContextUI import ItemListUI +import SearchUI public enum ChatListNodeMode { case chatList @@ -27,6 +28,7 @@ struct ChatListNodeListViewTransition { let options: ListViewDeleteAndInsertOptions let scrollToItem: ListViewScrollToItem? let stationaryItemRange: (Int, Int)? + let adjustScrollToFirstItem: Bool } final class ChatListHighlightedLocation { @@ -281,7 +283,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL } private func mappedChatListNodeViewListTransition(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, mode: ChatListNodeMode, transition: ChatListNodeViewTransition) -> ChatListNodeListViewTransition { - return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange) + return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, adjustScrollToFirstItem: transition.adjustScrollToFirstItem) } private final class ChatListOpaqueTransactionState { @@ -366,7 +368,17 @@ public final class ChatListNode: ListView { } private var currentLocation: ChatListNodeLocation? - let chatListFilter: ChatListFilter? + var chatListFilter: ChatListFilter? { + didSet { + self.chatListFilterValue.set(.single(self.chatListFilter)) + + if self.chatListFilter != oldValue { + if let currentLocation = self.currentLocation { + self.setChatListLocation(.initial(count: 50, filter: self.chatListFilter)) + } + } + } + } private let chatListFilterValue = Promise() var chatListFilterSignal: Signal { return self.chatListFilterValue.get() @@ -431,6 +443,8 @@ public final class ChatListNode: ListView { self.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor self.verticalScrollIndicatorFollowsOverscroll = true + self.keepMinimalScrollHeightWithTopInset = navigationBarSearchContentHeight + let nodeInteraction = ChatListNodeInteraction(activateSearch: { [weak self] in if let strongSelf = self, let activateSearch = strongSelf.activateSearch { activateSearch() @@ -489,6 +503,10 @@ public final class ChatListNode: ListView { } }) }, setPeerMuted: { [weak self] peerId, _ in + guard let strongSelf = self else { + return + } + strongSelf.setCurrentRemovingPeerId(peerId) let _ = (togglePeerMuted(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(completed: { self?.updateState { state in @@ -496,6 +514,7 @@ public final class ChatListNode: ListView { state.peerIdWithRevealedOptions = nil return state } + self?.setCurrentRemovingPeerId(nil) }) }, deletePeer: { [weak self] peerId in self?.deletePeerChat?(peerId) @@ -505,7 +524,7 @@ public final class ChatListNode: ListView { guard let context = context else { return } - + self?.setCurrentRemovingPeerId(peerId) let _ = (togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId) |> deliverOnMainQueue).start(completed: { self?.updateState { state in @@ -513,6 +532,7 @@ public final class ChatListNode: ListView { state.peerIdWithRevealedOptions = nil return state } + self?.setCurrentRemovingPeerId(nil) }) }, toggleArchivedFolderHiddenByDefault: { [weak self] in self?.toggleArchivedFolderHiddenByDefault?() @@ -1279,7 +1299,19 @@ public final class ChatListNode: ListView { options.insert(.PreferSynchronousDrawing) } - self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: ChatListOpaqueTransactionState(chatListView: transition.chatListView), completion: completion) + var scrollToItem = transition.scrollToItem + if transition.adjustScrollToFirstItem { + var offset: CGFloat = 0.0 + switch self.visibleContentOffset() { + case let .known(value) where abs(value) < .ulpOfOne: + offset = 0.0 + default: + offset = -navigationBarSearchContentHeight + } + scrollToItem = ListViewScrollToItem(index: 0, position: .top(offset), animated: false, curve: .Default(duration: 0.0), directionHint: .Up) + } + + self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: ChatListOpaqueTransactionState(chatListView: transition.chatListView), completion: completion) } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListViewTransition.swift b/submodules/ChatListUI/Sources/Node/ChatListViewTransition.swift index d7997ee48c..ae320a81e9 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListViewTransition.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListViewTransition.swift @@ -44,6 +44,7 @@ struct ChatListNodeViewTransition { let options: ListViewDeleteAndInsertOptions let scrollToItem: ListViewScrollToItem? let stationaryItemRange: (Int, Int)? + let adjustScrollToFirstItem: Bool } enum ChatListNodeViewScrollPosition { @@ -176,11 +177,12 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV fromEmptyView = true } + var adjustScrollToFirstItem = false if !previewing && !searchMode && fromEmptyView && scrollToItem == nil && toView.filteredEntries.count >= 1 { - scrollToItem = ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up) + adjustScrollToFirstItem = true } - subscriber.putNext(ChatListNodeViewTransition(chatListView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange)) + subscriber.putNext(ChatListNodeViewTransition(chatListView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, adjustScrollToFirstItem: adjustScrollToFirstItem)) subscriber.putCompletion() return EmptyDisposable diff --git a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift index 6cb265b924..0845aa6539 100644 --- a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift +++ b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift @@ -304,6 +304,123 @@ private final class FilterItemNode: ASDisplayNode, AbstractTabBarChatListFilterI } } +func chatListFilterItems(context: AccountContext) -> Signal<[(ChatListFilter, Int)], NoError> { + let preferencesKey: PostboxViewKey = .preferences(keys: [PreferencesKeys.chatListFilters]) + return context.account.postbox.combinedView(keys: [preferencesKey]) + |> map { combinedView -> [ChatListFilter] in + if let filtersState = (combinedView.views[preferencesKey] as? PreferencesView)?.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState { + return filtersState.filters + } else { + return [] + } + } + |> distinctUntilChanged + |> mapToSignal { filters -> Signal<[(ChatListFilter, Int)], NoError> in + var unreadCountItems: [UnreadMessageCountsItem] = [] + unreadCountItems.append(.total(nil)) + var additionalPeerIds = Set() + for filter in filters { + additionalPeerIds.formUnion(filter.includePeers) + } + if !additionalPeerIds.isEmpty { + for peerId in additionalPeerIds { + unreadCountItems.append(.peer(peerId)) + } + } + let unreadKey: PostboxViewKey = .unreadCounts(items: unreadCountItems) + var keys: [PostboxViewKey] = [] + keys.append(unreadKey) + for peerId in additionalPeerIds { + keys.append(.basicPeer(peerId)) + } + + return context.account.postbox.combinedView(keys: keys) + |> map { view -> [(ChatListFilter, Int)] in + guard let unreadCounts = view.views[unreadKey] as? UnreadMessageCountsView else { + return [] + } + + var result: [(ChatListFilter, Int)] = [] + + var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int)] = [:] + + var totalState: ChatListTotalUnreadState? + for entry in unreadCounts.entries { + switch entry { + case let .total(_, totalStateValue): + totalState = totalStateValue + case let .peer(peerId, state): + if let state = state, state.isUnread { + if let peerView = view.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer { + let tag = context.account.postbox.seedConfiguration.peerSummaryCounterTags(peer) + if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings, case .muted = notificationSettings.muteState { + peerTagAndCount[peerId] = (tag, 0) + } else { + var peerCount = Int(state.count) + if state.isUnread { + peerCount = max(1, peerCount) + } + peerTagAndCount[peerId] = (tag, peerCount) + } + } + } + } + } + + var totalUnreadChatCount = 0 + if let totalState = totalState { + for (_, counters) in totalState.filteredCounters { + totalUnreadChatCount += Int(counters.chatCount) + } + } + + var shouldUpdateLayout = false + for filter in filters { + var tags: [PeerSummaryCounterTags] = [] + if filter.categories.contains(.privateChats) { + tags.append(.privateChat) + } + if filter.categories.contains(.secretChats) { + tags.append(.secretChat) + } + if filter.categories.contains(.privateGroups) { + tags.append(.privateGroup) + } + if filter.categories.contains(.bots) { + tags.append(.bot) + } + if filter.categories.contains(.publicGroups) { + tags.append(.publicGroup) + } + if filter.categories.contains(.channels) { + tags.append(.channel) + } + + var count = 0 + if let totalState = totalState { + for tag in tags { + if let value = totalState.filteredCounters[tag] { + count += Int(value.chatCount) + } + } + } + for peerId in filter.includePeers { + if let (tag, peerCount) = peerTagAndCount[peerId] { + if !tags.contains(tag) { + if peerCount != 0 { + count += 1 + } + } + } + } + result.append((filter, count)) + } + + return result + } + } +} + private final class TabBarChatListFilterControllerNode: ViewControllerTracingNode { private let presentationData: PresentationData private let cancel: () -> Void diff --git a/submodules/Display/Display/ListView.swift b/submodules/Display/Display/ListView.swift index 9e231040b7..d884a353ee 100644 --- a/submodules/Display/Display/ListView.swift +++ b/submodules/Display/Display/ListView.swift @@ -168,6 +168,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } + public final var keepMinimalScrollHeightWithTopInset: CGFloat? + public final var stackFromBottom: Bool = false public final var stackFromBottomInsetItemFactor: CGFloat = 0.0 public final var limitHitTestToNodes: Bool = false @@ -986,6 +988,11 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } + if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset { + completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset) + bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) + } + var transition: ContainedViewLayoutTransition = .immediate if let updateSizeAndInsets = updateSizeAndInsets { if !updateSizeAndInsets.duration.isZero && !isExperimentalSnapToScrollToItem { @@ -1383,6 +1390,11 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture completeHeight += itemNode.apparentBounds.height } + if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset { + completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset) + bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) + } + if self.stackFromBottom { let updatedCompleteHeight = max(completeHeight, self.visibleSize.height) let deltaCompleteHeight = updatedCompleteHeight - completeHeight diff --git a/submodules/Display/Display/NavigationBar.swift b/submodules/Display/Display/NavigationBar.swift index 9aaa5d5b01..11fbb90fa3 100644 --- a/submodules/Display/Display/NavigationBar.swift +++ b/submodules/Display/Display/NavigationBar.swift @@ -105,7 +105,7 @@ enum NavigationPreviousAction: Equatable { open class NavigationBar: ASDisplayNode { private var presentationData: NavigationBarPresentationData - private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat)? + private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, Bool)? private var requestedLayout: Bool = false var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in } @@ -124,6 +124,7 @@ open class NavigationBar: ASDisplayNode { private let clippingNode: ASDisplayNode public private(set) var contentNode: NavigationBarContentNode? + public private(set) var secondaryContentNode: ASDisplayNode? private var itemTitleListenerKey: Int? private var itemTitleViewListenerKey: Int? @@ -801,16 +802,22 @@ open class NavigationBar: ASDisplayNode { if let validLayout = self.validLayout, self.requestedLayout { self.requestedLayout = false - self.updateLayout(size: validLayout.0, defaultHeight: validLayout.1, leftInset: validLayout.2, rightInset: validLayout.3, transition: .immediate) + self.updateLayout(size: validLayout.0, defaultHeight: validLayout.1, additionalHeight: validLayout.2, leftInset: validLayout.3, rightInset: validLayout.4, appearsHidden: validLayout.5, transition: .immediate) } } - func updateLayout(size: CGSize, defaultHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, transition: ContainedViewLayoutTransition) { if self.layoutSuspended { return } - self.validLayout = (size, defaultHeight, leftInset, rightInset) + self.validLayout = (size, defaultHeight, additionalHeight, leftInset, rightInset, appearsHidden) + + if let secondaryContentNode = self.secondaryContentNode { + transition.updateAlpha(node: secondaryContentNode, alpha: appearsHidden ? 0.0 : 1.0) + } + + let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? 46.0 : 0.0 let leftButtonInset: CGFloat = leftInset + 16.0 let backButtonInset: CGFloat = leftInset + 27.0 @@ -818,14 +825,19 @@ open class NavigationBar: ASDisplayNode { transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size)) var expansionHeight: CGFloat = 0.0 if let contentNode = self.contentNode { - let contentNodeFrame: CGRect + var contentNodeFrame: CGRect switch contentNode.mode { - case .replacement: - expansionHeight = contentNode.height - defaultHeight - contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) - case .expansion: - expansionHeight = contentNode.height - contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - expansionHeight), size: CGSize(width: size.width, height: expansionHeight)) + case .replacement: + expansionHeight = contentNode.height - defaultHeight + contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) + case .expansion: + expansionHeight = contentNode.height + contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - expansionHeight - apparentAdditionalHeight), size: CGSize(width: size.width, height: expansionHeight)) + if appearsHidden { + if self.secondaryContentNode != nil { + contentNodeFrame.origin.y += 46.0 + } + } } transition.updateFrame(node: contentNode, frame: contentNodeFrame) contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) @@ -833,8 +845,8 @@ open class NavigationBar: ASDisplayNode { transition.updateFrame(node: self.stripeNode, frame: CGRect(x: 0.0, y: size.height, width: size.width, height: UIScreenPixel)) - let nominalHeight: CGFloat = defaultHeight - let contentVerticalOrigin = size.height - nominalHeight - expansionHeight + let nominalHeight: CGFloat = defaultHeight - additionalHeight + let contentVerticalOrigin = size.height - nominalHeight - expansionHeight - additionalHeight - apparentAdditionalHeight var leftTitleInset: CGFloat = leftInset + 1.0 var rightTitleInset: CGFloat = rightInset + 1.0 @@ -1034,7 +1046,7 @@ open class NavigationBar: ASDisplayNode { let node = NavigationButtonNode() node.updateManualText(self.backButtonNode.manualText) node.color = accentColor - if let (size, defaultHeight, _, _) = self.validLayout { + if let (size, defaultHeight, _, _, _, _) = self.validLayout { node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight)) node.frame = self.backButtonNode.frame } @@ -1057,7 +1069,7 @@ open class NavigationBar: ASDisplayNode { } node.updateItems(items) node.color = accentColor - if let (size, defaultHeight, _, _) = self.validLayout { + if let (size, defaultHeight, _, _, _, _) = self.validLayout { node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight)) node.frame = self.backButtonNode.frame } @@ -1103,16 +1115,23 @@ open class NavigationBar: ASDisplayNode { } public func contentHeight(defaultHeight: CGFloat) -> CGFloat { + var result: CGFloat = 0.0 if let contentNode = self.contentNode { switch contentNode.mode { - case .expansion: - return defaultHeight + contentNode.height - case .replacement: - return contentNode.height + case .expansion: + result += defaultHeight + contentNode.height + case .replacement: + result += contentNode.height } } else { - return defaultHeight + result += defaultHeight } + + if let _ = self.secondaryContentNode { + result += 46.0 + } + + return result } public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) { @@ -1164,6 +1183,18 @@ open class NavigationBar: ASDisplayNode { } } + public func setSecondaryContentNode(_ secondatryContentNode: ASDisplayNode?) { + if self.secondaryContentNode !== secondatryContentNode { + if let previous = self.secondaryContentNode { + previous.removeFromSupernode() + } + self.secondaryContentNode = secondatryContentNode + if let secondaryContentNode = secondatryContentNode { + self.clippingNode.addSubnode(secondaryContentNode) + } + } + } + public func executeBack() -> Bool { if self.backButtonNode.isInHierarchy { self.backButtonNode.pressed(0) diff --git a/submodules/Display/Display/TabBarController.swift b/submodules/Display/Display/TabBarController.swift index 5c16bda583..f5a28af7b6 100644 --- a/submodules/Display/Display/TabBarController.swift +++ b/submodules/Display/Display/TabBarController.swift @@ -269,6 +269,7 @@ open class TabBarController: ViewController { currentController.navigationItem.setTarget(self.navigationItem) displayNavigationBar = currentController.displayNavigationBar self.navigationBar?.setContentNode(currentController.navigationBar?.contentNode, animated: false) + self.navigationBar?.setSecondaryContentNode(currentController.navigationBar?.secondaryContentNode) currentController.displayNode.recursivelyEnsureDisplaySynchronously(true) self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle } else { @@ -278,14 +279,21 @@ open class TabBarController: ViewController { self.navigationItem.titleView = nil self.navigationItem.backBarButtonItem = nil self.navigationBar?.setContentNode(nil, animated: false) + self.navigationBar?.setSecondaryContentNode(nil) displayNavigationBar = false } if self.displayNavigationBar != displayNavigationBar { self.setDisplayNavigationBar(displayNavigationBar) } - if let validLayout = self.validLayout { - self.containerLayoutUpdated(validLayout, transition: .immediate) + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: .immediate) + } + } + + public func updateLayout() { + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: .immediate) } } diff --git a/submodules/Display/Display/ViewController.swift b/submodules/Display/Display/ViewController.swift index a8d2ca9516..800b9ecceb 100644 --- a/submodules/Display/Display/ViewController.swift +++ b/submodules/Display/Display/ViewController.swift @@ -213,6 +213,21 @@ public enum ViewControllerNavigationPresentation { } } + open var cleanNavigationHeight: CGFloat { + if let navigationBar = self.navigationBar { + var height = navigationBar.frame.maxY + if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode { + height += contentNode.nominalHeight - contentNode.height + } + if navigationBar.secondaryContentNode != nil { + //height -= 46.0 + } + return height + } else { + return 0.0 + } + } + open var visualNavigationInsetHeight: CGFloat { if let navigationBar = self.navigationBar { let height = navigationBar.frame.maxY @@ -225,6 +240,8 @@ public enum ViewControllerNavigationPresentation { } } + public var additionalNavigationBarHeight: CGFloat = 0.0 + private let _ready = Promise(true) open var ready: Promise { return self._ready @@ -338,7 +355,7 @@ public enum ViewControllerNavigationPresentation { private func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0 - let defaultNavigationBarHeight: CGFloat + var defaultNavigationBarHeight: CGFloat if self._presentedInModal { defaultNavigationBarHeight = 56.0 } else { @@ -353,9 +370,6 @@ public enum ViewControllerNavigationPresentation { navigationBarOffset = 0.0 } var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationBarOffset), size: CGSize(width: layout.size.width, height: navigationBarHeight)) - if layout.statusBarHeight == nil { - //navigationBarFrame.size.height = (self.navigationBar?.contentHeight ?? 44.0) + 20.0 - } if !self.displayNavigationBar { navigationBarFrame.origin.y = -navigationBarFrame.size.height @@ -368,7 +382,7 @@ public enum ViewControllerNavigationPresentation { if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode, !self.displayNavigationBar { navigationBarFrame.origin.y += contentNode.height + statusBarHeight } - navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: defaultNavigationBarHeight, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) + navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: defaultNavigationBarHeight, additionalHeight: 0.0, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, transition: transition) if !transition.isAnimated { navigationBar.layer.cancelAnimationsRecursive(key: "bounds") navigationBar.layer.cancelAnimationsRecursive(key: "position") diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index b9da7f99eb..429045ced4 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -165,7 +165,7 @@ class ItemListPeerActionItemNode: ListViewItemNode { case .generic: verticalInset = 11.0 verticalOffset = 0.0 - leftInset = 59.0 + params.leftInset + leftInset = (item.icon == nil ? 16.0 : 59.0) + params.leftInset case .peerList: verticalInset = 14.0 verticalOffset = 0.0 diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaVideoConverter.m b/submodules/LegacyComponents/LegacyComponents/TGMediaVideoConverter.m index d42351543d..cdccc53bae 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaVideoConverter.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaVideoConverter.m @@ -442,6 +442,9 @@ SSubscriber *subscriber = context.subscriber; [context.videoProcessor startWithTimeRange:context.timeRange progressBlock:^(CGFloat progress) { +#if DEBUG + printf("Video progress: %f\n", progress); +#endif [subscriber putNext:@(progress)]; } completionBlock:^ { @@ -1032,7 +1035,11 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer, NSDictionary *codecSettings = @ { +#if DEBUG + AVVideoAverageBitRateKey: @([self _videoBitrateKbpsForPreset:preset] * 500), +#else AVVideoAverageBitRateKey: @([self _videoBitrateKbpsForPreset:preset] * 1000), +#endif AVVideoCleanApertureKey: videoCleanApertureSettings, AVVideoPixelAspectRatioKey: videoAspectRatioSettings }; diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift index af0ff9cdbf..23cc63a345 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift @@ -351,7 +351,7 @@ final class FFMpegMediaFrameSourceContext: NSObject { let avFormatContext = FFMpegAVFormatContext() - guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(self.ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) else { + guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(self.ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) else { self.readingError = true return } diff --git a/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift index 216070c84f..25fb2ad9b0 100644 --- a/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/SoftwareVideoSource.swift @@ -74,7 +74,7 @@ public final class SoftwareVideoSource { let ioBufferSize = 64 * 1024 - let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) + let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) self.avIoContext = avIoContext avFormatContext.setIO(self.avIoContext!) diff --git a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift index fdaa4d1b8e..3d796ac046 100644 --- a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift @@ -123,7 +123,7 @@ private final class UniversalSoftwareVideoSourceImpl { let ioBufferSize = 1 * 1024 - guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) else { + guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) else { return nil } self.avIoContext = avIoContext diff --git a/submodules/Postbox/Sources/MutableBasicPeerView.swift b/submodules/Postbox/Sources/MutableBasicPeerView.swift index a126580e3e..55316f2067 100644 --- a/submodules/Postbox/Sources/MutableBasicPeerView.swift +++ b/submodules/Postbox/Sources/MutableBasicPeerView.swift @@ -3,10 +3,12 @@ import Foundation final class MutableBasicPeerView: MutablePostboxView { private let peerId: PeerId fileprivate var peer: Peer? + fileprivate var notificationSettings: PeerNotificationSettings? init(postbox: Postbox, peerId: PeerId) { self.peerId = peerId self.peer = postbox.peerTable.get(peerId) + self.notificationSettings = postbox.peerNotificationSettingsTable.getEffective(peerId) } func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { @@ -15,6 +17,10 @@ final class MutableBasicPeerView: MutablePostboxView { self.peer = peer updated = true } + if transaction.currentUpdatedPeerNotificationSettings[self.peerId] != nil { + self.notificationSettings = postbox.peerNotificationSettingsTable.getEffective(peerId) + updated = true + } return updated } @@ -26,8 +32,10 @@ final class MutableBasicPeerView: MutablePostboxView { public final class BasicPeerView: PostboxView { public let peer: Peer? + public let notificationSettings: PeerNotificationSettings? init(_ view: MutableBasicPeerView) { self.peer = view.peer + self.notificationSettings = view.notificationSettings } } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index da72996990..8e0cbac997 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -10,7 +10,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-206066487] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) } dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } dict[461151667] = { return Api.ChatFull.parse_chatFull($0) } - dict[763976820] = { return Api.ChatFull.parse_channelFull($0) } + dict[-253335766] = { return Api.ChatFull.parse_channelFull($0) } dict[-932174686] = { return Api.PollResults.parse_pollResults($0) } dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) } dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) } @@ -247,6 +247,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) } dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) } dict[1123585836] = { return Api.Update.parse_updateMessagePollVote($0) } + dict[654302845] = { return Api.Update.parse_updateDialogFilter($0) } + dict[-1512627963] = { return Api.Update.parse_updateDialogFilterOrder($0) } + dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } @@ -357,6 +360,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) } dict[-833715459] = { return Api.InputMedia.parse_inputMediaGeoLive($0) } dict[-1410741723] = { return Api.InputMedia.parse_inputMediaPoll($0) } + dict[-1358977017] = { return Api.InputMedia.parse_inputMediaDice($0) } dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) } dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) } dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) } @@ -412,6 +416,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[2103482845] = { return Api.SecurePlainData.parse_securePlainPhone($0) } dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) } dict[-1269012015] = { return Api.messages.AffectedHistory.parse_affectedHistory($0) } + dict[1244130093] = { return Api.StatsGraph.parse_statsGraphAsync($0) } + dict[-1092839390] = { return Api.StatsGraph.parse_statsGraphError($0) } + dict[-1057809608] = { return Api.StatsGraph.parse_statsGraph($0) } dict[-1036572727] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) } dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) } dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) } @@ -480,6 +487,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-668391402] = { return Api.InputUser.parse_inputUser($0) } dict[-1366746132] = { return Api.Page.parse_page($0) } dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($0) } + dict[-875679776] = { return Api.StatsPercentValue.parse_statsPercentValue($0) } dict[157948117] = { return Api.upload.File.parse_file($0) } dict[-242427324] = { return Api.upload.File.parse_fileCdnRedirect($0) } dict[-1078612597] = { return Api.ChannelLocation.parse_channelLocationEmpty($0) } @@ -506,6 +514,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) } dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) } dict[-1564789301] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($0) } + dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) } dict[-1567175714] = { return Api.MessageFwdAuthor.parse_messageFwdAuthor($0) } dict[-1539849235] = { return Api.WallPaper.parse_wallPaper($0) } dict[-1963717851] = { return Api.WallPaper.parse_wallPaperNoFile($0) } @@ -520,6 +529,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1837345356] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) } dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) } dict[-368917890] = { return Api.PaymentCharge.parse_paymentCharge($0) } + dict[205195937] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) } dict[-484987010] = { return Api.Updates.parse_updatesTooLong($0) } dict[-1857044719] = { return Api.Updates.parse_updateShortMessage($0) } dict[377562760] = { return Api.Updates.parse_updateShortChatMessage($0) } @@ -527,6 +537,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1918567619] = { return Api.Updates.parse_updatesCombined($0) } dict[1957577280] = { return Api.Updates.parse_updates($0) } dict[301019932] = { return Api.Updates.parse_updateShortSentMessage($0) } + dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) } dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) } dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) } dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) } @@ -539,6 +550,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1666158377] = { return Api.MessageMedia.parse_messageMediaDocument($0) } dict[-873313984] = { return Api.MessageMedia.parse_messageMediaContact($0) } dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) } + dict[1670374507] = { return Api.MessageMedia.parse_messageMediaDice($0) } dict[-842892769] = { return Api.PaymentSavedCredentials.parse_paymentSavedCredentialsCard($0) } dict[1450380236] = { return Api.Null.parse_null($0) } dict[1923290508] = { return Api.auth.CodeType.parse_codeTypeSms($0) } @@ -590,6 +602,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1551583367] = { return Api.ReceivedNotifyMessage.parse_receivedNotifyMessage($0) } dict[-57668565] = { return Api.ChatParticipants.parse_chatParticipantsForbidden($0) } dict[1061556205] = { return Api.ChatParticipants.parse_chatParticipants($0) } + dict[351868460] = { return Api.DialogFilter.parse_dialogFilter($0) } dict[-1056001329] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentialsSaved($0) } dict[873977640] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentials($0) } dict[178373535] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentialsApplePay($0) } @@ -762,6 +775,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) } dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) } dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) } + dict[-581804346] = { return Api.StatsRowAbsValueAndPrev.parse_statsRowAbsValueAndPrev($0) } dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) } dict[-264117680] = { return Api.ChatOnlines.parse_chatOnlines($0) } dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) } @@ -1085,6 +1099,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.AffectedHistory: _1.serialize(buffer, boxed) + case let _1 as Api.StatsGraph: + _1.serialize(buffer, boxed) case let _1 as Api.account.PasswordInputSettings: _1.serialize(buffer, boxed) case let _1 as Api.PageTableCell: @@ -1155,6 +1171,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.SecureCredentialsEncrypted: _1.serialize(buffer, boxed) + case let _1 as Api.StatsPercentValue: + _1.serialize(buffer, boxed) case let _1 as Api.upload.File: _1.serialize(buffer, boxed) case let _1 as Api.ChannelLocation: @@ -1189,6 +1207,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.PhoneCallProtocol: _1.serialize(buffer, boxed) + case let _1 as Api.StatsDateRangeDays: + _1.serialize(buffer, boxed) case let _1 as Api.MessageFwdAuthor: _1.serialize(buffer, boxed) case let _1 as Api.WallPaper: @@ -1205,8 +1225,12 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.PaymentCharge: _1.serialize(buffer, boxed) + case let _1 as Api.stats.BroadcastStats: + _1.serialize(buffer, boxed) case let _1 as Api.Updates: _1.serialize(buffer, boxed) + case let _1 as Api.StatsAbsValueAndPrev: + _1.serialize(buffer, boxed) case let _1 as Api.MessageMedia: _1.serialize(buffer, boxed) case let _1 as Api.PaymentSavedCredentials: @@ -1257,6 +1281,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.ChatParticipants: _1.serialize(buffer, boxed) + case let _1 as Api.DialogFilter: + _1.serialize(buffer, boxed) case let _1 as Api.InputPaymentCredentials: _1.serialize(buffer, boxed) case let _1 as Api.ShippingOption: @@ -1387,6 +1413,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.updates.ChannelDifference: _1.serialize(buffer, boxed) + case let _1 as Api.StatsRowAbsValueAndPrev: + _1.serialize(buffer, boxed) case let _1 as Api.channels.AdminLogResults: _1.serialize(buffer, boxed) case let _1 as Api.ChatOnlines: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index be520b8f94..1ce0613a23 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -1805,7 +1805,7 @@ public extension Api { } public enum ChatFull: TypeConstructorDescription { case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?) - case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, pts: Int32) + case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -1828,9 +1828,9 @@ public extension Api { if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} break - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let pts): + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts): if boxed { - buffer.appendInt32(763976820) + buffer.appendInt32(-253335766) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -1861,6 +1861,7 @@ public extension Api { if Int(flags) & Int(1 << 15) != 0 {location!.serialize(buffer, true)} if Int(flags) & Int(1 << 17) != 0 {serializeInt32(slowmodeSeconds!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 18) != 0 {serializeInt32(slowmodeNextSendDate!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 12) != 0 {serializeInt32(statsDc!, buffer: buffer, boxed: false)} serializeInt32(pts, buffer: buffer, boxed: false) break } @@ -1870,8 +1871,8 @@ public extension Api { switch self { case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId): return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId)]) - case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let pts): - return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("pts", pts)]) + case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts): + return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts)]) } } @@ -1987,7 +1988,9 @@ public extension Api { var _25: Int32? if Int(_1!) & Int(1 << 18) != 0 {_25 = reader.readInt32() } var _26: Int32? - _26 = reader.readInt32() + if Int(_1!) & Int(1 << 12) != 0 {_26 = reader.readInt32() } + var _27: Int32? + _27 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -2013,9 +2016,10 @@ public extension Api { let _c23 = (Int(_1!) & Int(1 << 15) == 0) || _23 != nil let _c24 = (Int(_1!) & Int(1 << 17) == 0) || _24 != nil let _c25 = (Int(_1!) & Int(1 << 18) == 0) || _25 != nil - let _c26 = _26 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 { - return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, pts: _26!) + let _c26 = (Int(_1!) & Int(1 << 12) == 0) || _26 != nil + let _c27 = _27 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 { + return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!) } else { return nil @@ -5861,6 +5865,9 @@ public extension Api { case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32) case updateLoginToken case updateMessagePollVote(pollId: Int64, userId: Int32, options: [Buffer]) + case updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) + case updateDialogFilterOrder(order: [Int32]) + case updateDialogFilters public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -6509,6 +6516,30 @@ public extension Api { for item in options { serializeBytes(item, buffer: buffer, boxed: false) } + break + case .updateDialogFilter(let flags, let id, let filter): + if boxed { + buffer.appendInt32(654302845) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)} + break + case .updateDialogFilterOrder(let order): + if boxed { + buffer.appendInt32(-1512627963) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt32(item, buffer: buffer, boxed: false) + } + break + case .updateDialogFilters: + if boxed { + buffer.appendInt32(889491791) + } + break } } @@ -6669,6 +6700,12 @@ public extension Api { return ("updateLoginToken", []) case .updateMessagePollVote(let pollId, let userId, let options): return ("updateMessagePollVote", [("pollId", pollId), ("userId", userId), ("options", options)]) + case .updateDialogFilter(let flags, let id, let filter): + return ("updateDialogFilter", [("flags", flags), ("id", id), ("filter", filter)]) + case .updateDialogFilterOrder(let order): + return ("updateDialogFilterOrder", [("order", order)]) + case .updateDialogFilters: + return ("updateDialogFilters", []) } } @@ -7965,6 +8002,41 @@ public extension Api { return nil } } + public static func parse_updateDialogFilter(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Api.DialogFilter? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.DialogFilter + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateDialogFilter(flags: _1!, id: _2!, filter: _3) + } + else { + return nil + } + } + public static func parse_updateDialogFilterOrder(_ reader: BufferReader) -> Update? { + var _1: [Int32]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateDialogFilterOrder(order: _1!) + } + else { + return nil + } + } + public static func parse_updateDialogFilters(_ reader: BufferReader) -> Update? { + return Api.Update.updateDialogFilters + } } public enum PopularContact: TypeConstructorDescription { @@ -10365,6 +10437,7 @@ public extension Api { case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String) case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, period: Int32?) case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?) + case inputMediaDice public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -10511,6 +10584,12 @@ public extension Api { for item in correctAnswers! { serializeBytes(item, buffer: buffer, boxed: false) }} + break + case .inputMediaDice: + if boxed { + buffer.appendInt32(-1358977017) + } + break } } @@ -10547,6 +10626,8 @@ public extension Api { return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("period", period)]) case .inputMediaPoll(let flags, let poll, let correctAnswers): return ("inputMediaPoll", [("flags", flags), ("poll", poll), ("correctAnswers", correctAnswers)]) + case .inputMediaDice: + return ("inputMediaDice", []) } } @@ -10855,6 +10936,9 @@ public extension Api { return nil } } + public static func parse_inputMediaDice(_ reader: BufferReader) -> InputMedia? { + return Api.InputMedia.inputMediaDice + } } public enum InputPeer: TypeConstructorDescription { @@ -11956,6 +12040,82 @@ public extension Api { } } + } + public enum StatsGraph: TypeConstructorDescription { + case statsGraphAsync(token: String) + case statsGraphError(error: String) + case statsGraph(json: Api.DataJSON) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsGraphAsync(let token): + if boxed { + buffer.appendInt32(1244130093) + } + serializeString(token, buffer: buffer, boxed: false) + break + case .statsGraphError(let error): + if boxed { + buffer.appendInt32(-1092839390) + } + serializeString(error, buffer: buffer, boxed: false) + break + case .statsGraph(let json): + if boxed { + buffer.appendInt32(-1057809608) + } + json.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsGraphAsync(let token): + return ("statsGraphAsync", [("token", token)]) + case .statsGraphError(let error): + return ("statsGraphError", [("error", error)]) + case .statsGraph(let json): + return ("statsGraph", [("json", json)]) + } + } + + public static func parse_statsGraphAsync(_ reader: BufferReader) -> StatsGraph? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.StatsGraph.statsGraphAsync(token: _1!) + } + else { + return nil + } + } + public static func parse_statsGraphError(_ reader: BufferReader) -> StatsGraph? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.StatsGraph.statsGraphError(error: _1!) + } + else { + return nil + } + } + public static func parse_statsGraph(_ reader: BufferReader) -> StatsGraph? { + var _1: Api.DataJSON? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + let _c1 = _1 != nil + if _c1 { + return Api.StatsGraph.statsGraph(json: _1!) + } + else { + return nil + } + } + } public enum PageTableCell: TypeConstructorDescription { case pageTableCell(flags: Int32, text: Api.RichText?, colspan: Int32?, rowspan: Int32?) @@ -13662,6 +13822,44 @@ public extension Api { } } + } + public enum StatsPercentValue: TypeConstructorDescription { + case statsPercentValue(part: Double, total: Double) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsPercentValue(let part, let total): + if boxed { + buffer.appendInt32(-875679776) + } + serializeDouble(part, buffer: buffer, boxed: false) + serializeDouble(total, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsPercentValue(let part, let total): + return ("statsPercentValue", [("part", part), ("total", total)]) + } + } + + public static func parse_statsPercentValue(_ reader: BufferReader) -> StatsPercentValue? { + var _1: Double? + _1 = reader.readDouble() + var _2: Double? + _2 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!) + } + else { + return nil + } + } + } public enum ChannelLocation: TypeConstructorDescription { case channelLocationEmpty @@ -14434,6 +14632,44 @@ public extension Api { } } + } + public enum StatsDateRangeDays: TypeConstructorDescription { + case statsDateRangeDays(minDate: Int32, maxDate: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsDateRangeDays(let minDate, let maxDate): + if boxed { + buffer.appendInt32(-1237848657) + } + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(maxDate, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsDateRangeDays(let minDate, let maxDate): + return ("statsDateRangeDays", [("minDate", minDate), ("maxDate", maxDate)]) + } + } + + public static func parse_statsDateRangeDays(_ reader: BufferReader) -> StatsDateRangeDays? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StatsDateRangeDays.statsDateRangeDays(minDate: _1!, maxDate: _2!) + } + else { + return nil + } + } + } public enum MessageFwdAuthor: TypeConstructorDescription { case messageFwdAuthor(channelId: Int32) @@ -15098,6 +15334,44 @@ public extension Api { } } + } + public enum StatsAbsValueAndPrev: TypeConstructorDescription { + case statsAbsValueAndPrev(current: Double, previous: Double) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsAbsValueAndPrev(let current, let previous): + if boxed { + buffer.appendInt32(-884757282) + } + serializeDouble(current, buffer: buffer, boxed: false) + serializeDouble(previous, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsAbsValueAndPrev(let current, let previous): + return ("statsAbsValueAndPrev", [("current", current), ("previous", previous)]) + } + } + + public static func parse_statsAbsValueAndPrev(_ reader: BufferReader) -> StatsAbsValueAndPrev? { + var _1: Double? + _1 = reader.readDouble() + var _2: Double? + _2 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StatsAbsValueAndPrev.statsAbsValueAndPrev(current: _1!, previous: _2!) + } + else { + return nil + } + } + } public enum MessageMedia: TypeConstructorDescription { case messageMediaEmpty @@ -15112,6 +15386,7 @@ public extension Api { case messageMediaDocument(flags: Int32, document: Api.Document?, ttlSeconds: Int32?) case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int32) case messageMediaPoll(poll: Api.Poll, results: Api.PollResults) + case messageMediaDice(value: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -15209,6 +15484,12 @@ public extension Api { poll.serialize(buffer, true) results.serialize(buffer, true) break + case .messageMediaDice(let value): + if boxed { + buffer.appendInt32(1670374507) + } + serializeInt32(value, buffer: buffer, boxed: false) + break } } @@ -15238,6 +15519,8 @@ public extension Api { return ("messageMediaContact", [("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("userId", userId)]) case .messageMediaPoll(let poll, let results): return ("messageMediaPoll", [("poll", poll), ("results", results)]) + case .messageMediaDice(let value): + return ("messageMediaDice", [("value", value)]) } } @@ -15443,6 +15726,17 @@ public extension Api { return nil } } + public static func parse_messageMediaDice(_ reader: BufferReader) -> MessageMedia? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.MessageMedia.messageMediaDice(value: _1!) + } + else { + return nil + } + } } public enum PaymentSavedCredentials: TypeConstructorDescription { @@ -16852,6 +17146,58 @@ public extension Api { } } + } + public enum DialogFilter: TypeConstructorDescription { + case dialogFilter(flags: Int32, id: Int32, title: String, includePeers: [Api.InputPeer]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .dialogFilter(let flags, let id, let title, let includePeers): + if boxed { + buffer.appendInt32(351868460) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(includePeers.count)) + for item in includePeers { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .dialogFilter(let flags, let id, let title, let includePeers): + return ("dialogFilter", [("flags", flags), ("id", id), ("title", title), ("includePeers", includePeers)]) + } + } + + public static func parse_dialogFilter(_ reader: BufferReader) -> DialogFilter? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + var _4: [Api.InputPeer]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputPeer.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.DialogFilter.dialogFilter(flags: _1!, id: _2!, title: _3!, includePeers: _4!) + } + else { + return nil + } + } + } public enum InputPaymentCredentials: TypeConstructorDescription { case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer) @@ -20918,6 +21264,54 @@ public extension Api { } } + } + public enum StatsRowAbsValueAndPrev: TypeConstructorDescription { + case statsRowAbsValueAndPrev(id: String, title: String, shortTitle: String, values: Api.StatsAbsValueAndPrev) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .statsRowAbsValueAndPrev(let id, let title, let shortTitle, let values): + if boxed { + buffer.appendInt32(-581804346) + } + serializeString(id, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(shortTitle, buffer: buffer, boxed: false) + values.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .statsRowAbsValueAndPrev(let id, let title, let shortTitle, let values): + return ("statsRowAbsValueAndPrev", [("id", id), ("title", title), ("shortTitle", shortTitle), ("values", values)]) + } + } + + public static func parse_statsRowAbsValueAndPrev(_ reader: BufferReader) -> StatsRowAbsValueAndPrev? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + var _4: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.StatsRowAbsValueAndPrev.statsRowAbsValueAndPrev(id: _1!, title: _2!, shortTitle: _3!, values: _4!) + } + else { + return nil + } + } + } public enum ChatOnlines: TypeConstructorDescription { case chatOnlines(onlines: Int32) diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index b83eff3dc4..c4a1ac4fc5 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -537,6 +537,130 @@ public struct payments { } } public extension Api { +public struct stats { + public enum BroadcastStats: TypeConstructorDescription { + case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: Api.StatsAbsValueAndPrev, enabledNotifications: Api.StatsPercentValue, viewsBySource: [Api.StatsRowAbsValueAndPrev], newFollowersBySource: [Api.StatsRowAbsValueAndPrev], languages: [Api.StatsRowAbsValueAndPrev], growthGraph: Api.StatsGraph, followersGraph: Api.StatsGraph, muteGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, interactionsGraph: Api.StatsGraph) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let viewsBySource, let newFollowersBySource, let languages, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph): + if boxed { + buffer.appendInt32(205195937) + } + period.serialize(buffer, true) + followers.serialize(buffer, true) + viewsPerPost.serialize(buffer, true) + sharesPerPost.serialize(buffer, true) + enabledNotifications.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(viewsBySource.count)) + for item in viewsBySource { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newFollowersBySource.count)) + for item in newFollowersBySource { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(languages.count)) + for item in languages { + item.serialize(buffer, true) + } + growthGraph.serialize(buffer, true) + followersGraph.serialize(buffer, true) + muteGraph.serialize(buffer, true) + topHoursGraph.serialize(buffer, true) + interactionsGraph.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let viewsBySource, let newFollowersBySource, let languages, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph): + return ("broadcastStats", [("period", period), ("followers", followers), ("viewsPerPost", viewsPerPost), ("sharesPerPost", sharesPerPost), ("enabledNotifications", enabledNotifications), ("viewsBySource", viewsBySource), ("newFollowersBySource", newFollowersBySource), ("languages", languages), ("growthGraph", growthGraph), ("followersGraph", followersGraph), ("muteGraph", muteGraph), ("topHoursGraph", topHoursGraph), ("interactionsGraph", interactionsGraph)]) + } + } + + public static func parse_broadcastStats(_ reader: BufferReader) -> BroadcastStats? { + var _1: Api.StatsDateRangeDays? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays + } + var _2: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _3: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _4: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _5: Api.StatsPercentValue? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue + } + var _6: [Api.StatsRowAbsValueAndPrev]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self) + } + var _7: [Api.StatsRowAbsValueAndPrev]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self) + } + var _8: [Api.StatsRowAbsValueAndPrev]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self) + } + var _9: Api.StatsGraph? + if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _10: Api.StatsGraph? + if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _11: Api.StatsGraph? + if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _12: Api.StatsGraph? + if let signature = reader.readInt32() { + _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _13: Api.StatsGraph? + if let signature = reader.readInt32() { + _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { + return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, enabledNotifications: _5!, viewsBySource: _6!, newFollowersBySource: _7!, languages: _8!, growthGraph: _9!, followersGraph: _10!, muteGraph: _11!, topHoursGraph: _12!, interactionsGraph: _13!) + } + else { + return nil + } + } + + } +} +} +public extension Api { public struct auth { public enum LoginToken: TypeConstructorDescription { case loginToken(expires: Int32, token: Buffer) diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 9d5351bb9e..106c437be6 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -3214,6 +3214,54 @@ public extension Api { return result }) } + + public static func getDialogFilters() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogFilter]>) { + let buffer = Buffer() + buffer.appendInt32(-241247891) + + return (FunctionDescription(name: "messages.getDialogFilters", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogFilter]? in + let reader = BufferReader(buffer) + var result: [Api.DialogFilter]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilter.self) + } + return result + }) + } + + public static func updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(450142282) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.updateDialogFilter", parameters: [("flags", flags), ("id", id), ("filter", filter)]), 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 static func updateDialogFiltersOrder(order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-983318044) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "messages.updateDialogFiltersOrder", parameters: [("order", 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 struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -3900,6 +3948,36 @@ public extension Api { }) } } + public struct stats { + public static func getBroadcastStats(flags: Int32, channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1421720550) + serializeInt32(flags, buffer: buffer, boxed: false) + channel.serialize(buffer, true) + return (FunctionDescription(name: "stats.getBroadcastStats", parameters: [("flags", flags), ("channel", channel)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.BroadcastStats? in + let reader = BufferReader(buffer) + var result: Api.stats.BroadcastStats? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stats.BroadcastStats + } + return result + }) + } + + public static func loadAsyncGraph(token: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1749505346) + serializeString(token, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stats.loadAsyncGraph", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StatsGraph? in + let reader = BufferReader(buffer) + var result: Api.StatsGraph? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + return result + }) + } + } public struct auth { public static func checkPhone(phoneNumber: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift index 95a689aa1d..eb61eb0fec 100644 --- a/submodules/TelegramCore/Sources/AccountManager.swift +++ b/submodules/TelegramCore/Sources/AccountManager.swift @@ -152,7 +152,7 @@ private var declaredEncodables: Void = { declareEncodable(EmbeddedMediaStickersMessageAttribute.self, f: { EmbeddedMediaStickersMessageAttribute(decoder: $0) }) declareEncodable(TelegramMediaWebpageAttribute.self, f: { TelegramMediaWebpageAttribute(decoder: $0) }) declareEncodable(CachedPollOptionResult.self, f: { CachedPollOptionResult(decoder: $0) }) - //declareEncodable(ChatListFiltersState.self, f: { ChatListFiltersState(decoder: $0) }) + declareEncodable(ChatListFiltersState.self, f: { ChatListFiltersState(decoder: $0) }) return }() diff --git a/submodules/TelegramCore/Sources/ChatListFiltering.swift b/submodules/TelegramCore/Sources/ChatListFiltering.swift index f9966602c1..c5c6822fdd 100644 --- a/submodules/TelegramCore/Sources/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/ChatListFiltering.swift @@ -140,7 +140,7 @@ public struct ChatListFilter: PostboxCoding, Equatable { } } -/*extension ChatListFilter { +extension ChatListFilter { init(apiFilter: Api.DialogFilter) { switch apiFilter { case let .dialogFilter(flags, id, title, includePeers): @@ -179,13 +179,13 @@ public struct ChatListFilter: PostboxCoding, Equatable { return transaction.getPeer(peerId).flatMap(apiInputPeer) }) } -}*/ +} public enum RequestUpdateChatListFilterError { case generic } -/*public func requestUpdateChatListFilter(account: Account, id: Int32, filter: ChatListFilter?) -> Signal { +public func requestUpdateChatListFilter(account: Account, id: Int32, filter: ChatListFilter?) -> Signal { return account.postbox.transaction { transaction -> Api.DialogFilter? in return filter?.apiFilter(transaction: transaction) } @@ -270,6 +270,8 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa } func managedChatListFilters(postbox: Postbox, network: Network) -> Signal { + return .complete() + return requestChatListFilters(postbox: postbox, network: network) |> `catch` { _ -> Signal<[ChatListFilter], NoError> in return .complete() @@ -376,4 +378,4 @@ public func updateChatListFilterSettingsInteractively(postbox: Postbox, _ f: @es return result ?? .default } } -*/ + diff --git a/submodules/TelegramCore/Sources/ManagedChatListHoles.swift b/submodules/TelegramCore/Sources/ManagedChatListHoles.swift index c0a8885162..a1388d0fce 100644 --- a/submodules/TelegramCore/Sources/ManagedChatListHoles.swift +++ b/submodules/TelegramCore/Sources/ManagedChatListHoles.swift @@ -1,6 +1,7 @@ import Foundation import Postbox import SwiftSignalKit +import SyncCore private final class ManagedChatListHolesState { private var holeDisposables: [ChatListHolesEntry: Disposable] = [:] @@ -53,15 +54,17 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee return Signal { _ in let state = Atomic(value: ManagedChatListHolesState()) - let topRootHoleKey = PostboxViewKey.allChatListHoles(.root) - let topRootHole = postbox.combinedView(keys: [topRootHoleKey]) + let topRootHoleKey: PostboxViewKey = .allChatListHoles(.root) + let filtersKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.chatListFilters])) + let combinedView = postbox.combinedView(keys: [topRootHoleKey, filtersKey]) - let disposable = combineLatest(postbox.chatListHolesView(), topRootHole).start(next: { view, topRootHoleView in + let disposable = combineLatest(postbox.chatListHolesView(), combinedView).start(next: { view, combinedView in var additionalLatestHole: ChatListHole? - if let topRootHole = topRootHoleView.views[topRootHoleKey] as? AllChatListHolesView { - #if os(macOS) - additionalLatestHole = topRootHole.latestHole - #endif + + if let preferencesView = combinedView.views[filtersKey] as? PreferencesView, let filtersState = preferencesView.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState, !filtersState.filters.isEmpty { + if let topRootHole = combinedView.views[topRootHoleKey] as? AllChatListHolesView { + additionalLatestHole = topRootHole.latestHole + } } let (removed, added, addedAdditionalLatestHole) = state.with { state in diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift index 44751ce910..500ec09591 100644 --- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift @@ -277,71 +277,73 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? { func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (Media?, Int32?) { if let media = media { switch media { - case let .messageMediaPhoto(_, photo, ttlSeconds): - if let photo = photo { - if let mediaImage = telegramMediaImageFromApiPhoto(photo) { - return (mediaImage, ttlSeconds) - } - } else { - return (TelegramMediaExpiredContent(data: .image), nil) + case let .messageMediaPhoto(_, photo, ttlSeconds): + if let photo = photo { + if let mediaImage = telegramMediaImageFromApiPhoto(photo) { + return (mediaImage, ttlSeconds) } - case let .messageMediaContact(phoneNumber, firstName, lastName, vcard, userId): - let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId, vCardData: vcard.isEmpty ? nil : vcard) - return (mediaContact, nil) - case let .messageMediaGeo(geo): - let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil) - return (mediaMap, nil) - case let .messageMediaVenue(geo, title, address, provider, venueId, venueType): - let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil) - return (mediaMap, nil) - case let .messageMediaGeoLive(geo, period): - let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period) - return (mediaMap, nil) - case let .messageMediaDocument(_, document, ttlSeconds): - if let document = document { - if let mediaFile = telegramMediaFileFromApiDocument(document) { - return (mediaFile, ttlSeconds) - } - } else { - return (TelegramMediaExpiredContent(data: .file), nil) - } - case let .messageMediaWebPage(webpage): - if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage, url: nil) { - return (mediaWebpage, nil) - } - case .messageMediaUnsupported: - return (TelegramMediaUnsupported(), nil) - case .messageMediaEmpty: - break - case let .messageMediaGame(game): - return (TelegramMediaGame(apiGame: game), nil) - case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam): - var parsedFlags = TelegramMediaInvoiceFlags() - if (flags & (1 << 3)) != 0 { - parsedFlags.insert(.isTest) + } else { + return (TelegramMediaExpiredContent(data: .image), nil) + } + case let .messageMediaContact(phoneNumber, firstName, lastName, vcard, userId): + let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId, vCardData: vcard.isEmpty ? nil : vcard) + return (mediaContact, nil) + case let .messageMediaGeo(geo): + let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil) + return (mediaMap, nil) + case let .messageMediaVenue(geo, title, address, provider, venueId, venueType): + let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil) + return (mediaMap, nil) + case let .messageMediaGeoLive(geo, period): + let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period) + return (mediaMap, nil) + case let .messageMediaDocument(_, document, ttlSeconds): + if let document = document { + if let mediaFile = telegramMediaFileFromApiDocument(document) { + return (mediaFile, ttlSeconds) } + } else { + return (TelegramMediaExpiredContent(data: .file), nil) + } + case let .messageMediaWebPage(webpage): + if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage, url: nil) { + return (mediaWebpage, nil) + } + case .messageMediaUnsupported: + return (TelegramMediaUnsupported(), nil) + case .messageMediaEmpty: + break + case let .messageMediaGame(game): + return (TelegramMediaGame(apiGame: game), nil) + case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam): + var parsedFlags = TelegramMediaInvoiceFlags() + if (flags & (1 << 3)) != 0 { + parsedFlags.insert(.isTest) + } + if (flags & (1 << 1)) != 0 { + parsedFlags.insert(.shippingAddressRequested) + } + return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil) + case let .messageMediaPoll(poll, results): + switch poll { + case let .poll(id, flags, question, answers): + let publicity: TelegramMediaPollPublicity if (flags & (1 << 1)) != 0 { - parsedFlags.insert(.shippingAddressRequested) + publicity = .public + } else { + publicity = .anonymous } - return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil) - case let .messageMediaPoll(poll, results): - switch poll { - case let .poll(id, flags, question, answers): - let publicity: TelegramMediaPollPublicity - if (flags & (1 << 1)) != 0 { - publicity = .public - } else { - publicity = .anonymous - } - let kind: TelegramMediaPollKind - if (flags & (1 << 3)) != 0 { - kind = .quiz - } else { - kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0) - } - return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0), nil) + let kind: TelegramMediaPollKind + if (flags & (1 << 3)) != 0 { + kind = .quiz + } else { + kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0) } + return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0), nil) + } + case .messageMediaDice: + break } } diff --git a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift index d0f10bfb51..e5c836e12e 100644 --- a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift @@ -319,7 +319,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI } switch fullChat { - case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, pts): + case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, _, pts): var channelFlags = CachedChannelFlags() if (flags & (1 << 3)) != 0 { channelFlags.insert(.canDisplayParticipants) diff --git a/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift b/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift index 8de131f329..01c3ef9546 100644 --- a/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift +++ b/submodules/TelegramUI/TelegramUI/DeclareEncodables.swift @@ -54,6 +54,7 @@ private var telegramUIDeclaredEncodables: Void = { declareEncodable(WebBrowserSettings.self, f: { WebBrowserSettings(decoder: $0) }) declareEncodable(IntentsSettings.self, f: { IntentsSettings(decoder: $0) }) declareEncodable(CachedGeocode.self, f: { CachedGeocode(decoder: $0) }) + declareEncodable(ChatListFilterSettings.self, f: { ChatListFilterSettings(decoder: $0) }) return }() diff --git a/submodules/TelegramUI/TelegramUI/FetchVideoMediaResource.swift b/submodules/TelegramUI/TelegramUI/FetchVideoMediaResource.swift index 74896b11b0..b45263840c 100644 --- a/submodules/TelegramUI/TelegramUI/FetchVideoMediaResource.swift +++ b/submodules/TelegramUI/TelegramUI/FetchVideoMediaResource.swift @@ -65,6 +65,8 @@ struct VideoConversionConfiguration { } static func with(appConfiguration: AppConfiguration) -> VideoConversionConfiguration { + return VideoConversionConfiguration(remuxToFMp4: true) + if let data = appConfiguration.data, let conversion = data["video_conversion"] as? [String: Any] { let remuxToFMp4 = conversion["remux_fmp4"] as? Bool ?? VideoConversionConfiguration.defaultValue.remuxToFMp4 return VideoConversionConfiguration(remuxToFMp4: remuxToFMp4) diff --git a/submodules/TelegramUIPreferences/Sources/ChatListFilterSettings.swift b/submodules/TelegramUIPreferences/Sources/ChatListFilterSettings.swift new file mode 100644 index 0000000000..014bd26eb9 --- /dev/null +++ b/submodules/TelegramUIPreferences/Sources/ChatListFilterSettings.swift @@ -0,0 +1,43 @@ +import Foundation +import Postbox +import SwiftSignalKit + +public struct ChatListFilterSettings: Equatable, PreferencesEntry { + public var displayTabs: Bool + + public static var `default`: ChatListFilterSettings { + return ChatListFilterSettings(displayTabs: true) + } + + public init(displayTabs: Bool) { + self.displayTabs = displayTabs + } + + public init(decoder: PostboxDecoder) { + self.displayTabs = decoder.decodeInt32ForKey("displayTabs", orElse: 1) != 0 + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.displayTabs ? 1 : 0, forKey: "displayTabs") + } + + public func isEqual(to: PreferencesEntry) -> Bool { + if let to = to as? ChatListFilterSettings { + return self == to + } else { + return false + } + } +} + +public func updateChatListFilterSettings(transaction: Transaction, _ f: @escaping (ChatListFilterSettings) -> ChatListFilterSettings) { + transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatListFilterSettings, { entry in + let currentSettings: ChatListFilterSettings + if let entry = entry as? ChatListFilterSettings { + currentSettings = entry + } else { + currentSettings = .default + } + return f(currentSettings) + }) +} diff --git a/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.h b/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.h index 6d5b7e6a11..f4a4411175 100644 --- a/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.h +++ b/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.h @@ -6,7 +6,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FFMpegAVIOContext : NSObject -- (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (*)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek; +- (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket writePacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))writePacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek; - (void *)impl; diff --git a/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.m b/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.m index 2bce9764d1..cdc46664f0 100644 --- a/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.m +++ b/submodules/ffmpeg/FFMpeg/FFMpegAVIOContext.m @@ -10,11 +10,11 @@ @implementation FFMpegAVIOContext -- (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (*)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek { +- (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket writePacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))writePacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek { self = [super init]; if (self != nil) { void *avIoBuffer = av_malloc(bufferSize); - _impl = avio_alloc_context(avIoBuffer, bufferSize, 0, opaqueContext, readPacket, nil, seek); + _impl = avio_alloc_context(avIoBuffer, bufferSize, 0, opaqueContext, readPacket, writePacket, seek); _impl->direct = 1; if (_impl == nil) { av_free(avIoBuffer); diff --git a/submodules/ffmpeg/FFMpeg/FFMpegRemuxer.m b/submodules/ffmpeg/FFMpeg/FFMpegRemuxer.m index 9a468be36b..c3adbf8f66 100644 --- a/submodules/ffmpeg/FFMpeg/FFMpegRemuxer.m +++ b/submodules/ffmpeg/FFMpeg/FFMpegRemuxer.m @@ -1,11 +1,64 @@ #import "FFMpegRemuxer.h" +#import "FFMpegAVIOContext.h" + #include "libavutil/timestamp.h" #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" #define MOV_TIMESCALE 1000 +@interface FFMpegRemuxerContext : NSObject { + @public + int _fd; + int64_t _offset; +} + +@end + +@implementation FFMpegRemuxerContext + +- (instancetype)initWithFileName:(NSString *)fileName { + self = [super init]; + if (self != nil) { + _fd = open(fileName.UTF8String, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + } + return self; +} + +- (void)dealloc { + if (_fd > 0) { + close(_fd); + } +} + +@end + +static int readPacketImpl(void * _Nullable opaque, uint8_t * _Nullable buffer, int length) { + FFMpegRemuxerContext *context = (__bridge FFMpegRemuxerContext *)opaque; + context->_offset += length; + printf("read %lld bytes (offset is now %lld)\n", length, context->_offset); + return read(context->_fd, buffer, length); +} + +static int writePacketImpl(void * _Nullable opaque, uint8_t * _Nullable buffer, int length) { + FFMpegRemuxerContext *context = (__bridge FFMpegRemuxerContext *)opaque; + context->_offset += length; + printf("write %lld bytes (offset is now %lld)\n", length, context->_offset); + return write(context->_fd, buffer, length); +} + +static int64_t seekImpl(void * _Nullable opaque, int64_t offset, int whence) { + FFMpegRemuxerContext *context = (__bridge FFMpegRemuxerContext *)opaque; + printf("seek to %lld\n", offset); + if (whence == FFMPEG_AVSEEK_SIZE) { + return 0; + } else { + context->_offset = offset; + return lseek(context->_fd, offset, SEEK_SET); + } +} + @implementation FFMpegRemuxer + (bool)remux:(NSString * _Nonnull)path to:(NSString * _Nonnull)outPath { @@ -21,6 +74,9 @@ in_filename = [path UTF8String]; out_filename = [outPath UTF8String]; + //FFMpegRemuxerContext *outputContext = [[FFMpegRemuxerContext alloc] initWithFileName:outPath]; + //FFMpegAVIOContext *outputIoContext = [[FFMpegAVIOContext alloc] initWithBufferSize:1024 opaqueContext:(__bridge void *)outputContext readPacket:&readPacketImpl writePacket:&writePacketImpl seek:&seekImpl]; + if ((ret = avformat_open_input(&input_format_context, in_filename, av_find_input_format("mov"), NULL)) < 0) { fprintf(stderr, "Could not open input file '%s'", in_filename); goto end; @@ -31,6 +87,11 @@ } avformat_alloc_output_context2(&output_format_context, NULL, NULL, out_filename); + //output_format_context = avformat_alloc_context(); + //output_format_context->pb = outputIoContext.impl; + //output_format_context->flags |= AVFMT_FLAG_CUSTOM_IO; + //output_format_context->oformat = av_guess_format("mp4", NULL, NULL); + if (!output_format_context) { fprintf(stderr, "Could not create output context\n"); ret = AVERROR_UNKNOWN; @@ -103,7 +164,7 @@ // https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API/Transcoding_assets_for_MSE av_dict_set(&opts, "movflags", "dash+faststart+global_sidx+skip_trailer", 0); if (maxTrackLength > 0) { - av_dict_set_int(&opts, "custom_maxTrackLength", maxTrackLength, 0); + //av_dict_set_int(&opts, "custom_maxTrackLength", maxTrackLength, 0); } } // https://ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga18b7b10bb5b94c4842de18166bc677cb @@ -144,8 +205,9 @@ end: avformat_close_input(&input_format_context); /* close output */ - if (output_format_context && !(output_format_context->oformat->flags & AVFMT_NOFILE)) + if (output_format_context && !(output_format_context->oformat->flags & AVFMT_NOFILE)) { avio_closep(&output_format_context->pb); + } avformat_free_context(output_format_context); av_freep(&streams_list); if (ret < 0 && ret != AVERROR_EOF) { From 7c7b91194d4d430e56765b89178a52a4e912e4b4 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 19 Feb 2020 00:26:16 +0400 Subject: [PATCH 5/6] Fix member list gestures --- .../Sources/ItemListPeerItem.swift | 18 +++++++++++++++++- .../PeerInfo/Panes/PeerInfoMembersPane.swift | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 8c8fcf6b0f..743a3dc6a6 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -335,8 +335,9 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { let header: ListViewItemHeader? let shimmering: ItemListPeerItemShimmering? let displayDecorations: Bool + let disableInteractiveTransitionIfNecessary: Bool - public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true) { + public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) { self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder @@ -367,6 +368,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { self.header = header self.shimmering = shimmering self.displayDecorations = displayDecorations + self.disableInteractiveTransitionIfNecessary = disableInteractiveTransitionIfNecessary } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -536,6 +538,20 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo } } + override public func didLoad() { + super.didLoad() + + self.updateEnableGestures() + } + + private func updateEnableGestures() { + if let item = self.layoutParams?.0, item.disableInteractiveTransitionIfNecessary, let revealOptions = item.revealOptions, !revealOptions.options.isEmpty { + self.view.disablesInteractiveTransitionGestureRecognizer = true + } else { + self.view.disablesInteractiveTransitionGestureRecognizer = false + } + } + public func asyncLayout() -> (_ item: ItemListPeerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors, _ headerAtTop: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeStatusLayout = TextNode.asyncLayout(self.statusNode) diff --git a/submodules/TelegramUI/TelegramUI/PeerInfo/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/TelegramUI/PeerInfo/Panes/PeerInfoMembersPane.swift index 5bc764ed19..4b0cfc6748 100644 --- a/submodules/TelegramUI/TelegramUI/PeerInfo/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/TelegramUI/PeerInfo/Panes/PeerInfoMembersPane.swift @@ -84,7 +84,7 @@ private struct PeerMembersListEntry: Comparable, Identifiable { }, removePeer: { _ in }, contextAction: nil/*{ node, gesture in openPeerContextAction(peer, node, gesture) - }*/, hasTopStripe: false, noInsets: true) + }*/, hasTopStripe: false, noInsets: true, disableInteractiveTransitionIfNecessary: true) } } From 7e857af95f0f2574209de115a89c50d68b992814 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 19 Feb 2020 03:28:43 +0400 Subject: [PATCH 6/6] Fix build --- Wallet.makefile | 2 +- Wallet/Sources/AppDelegate.swift | 6 ++++-- submodules/WalletUI/BUCK | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Wallet.makefile b/Wallet.makefile index 707bcffd39..20ead97244 100644 --- a/Wallet.makefile +++ b/Wallet.makefile @@ -12,7 +12,7 @@ WALLET_BUCK_OPTIONS=\ --config custom.distributionProvisioningProfileApp="${WALLET_DISTRIBUTION_PROVISIONING_PROFILE_APP}" \ --config custom.apiId="${API_ID}" \ --config custom.apiHash="${API_HASH}" \ - --config custom.hockeyAppId="${HOCKEYAPP_ID}" \ + --config custom.appCenterId="0" \ --config custom.isInternalBuild="${IS_INTERNAL_BUILD}" \ --config custom.isAppStoreBuild="${IS_APPSTORE_BUILD}" \ --config custom.appStoreId="${APPSTORE_ID}" \ diff --git a/Wallet/Sources/AppDelegate.swift b/Wallet/Sources/AppDelegate.swift index ebc8cc17ed..cefa741684 100644 --- a/Wallet/Sources/AppDelegate.swift +++ b/Wallet/Sources/AppDelegate.swift @@ -595,7 +595,8 @@ final class AppDelegate: NSObject, UIApplicationDelegate { secondaryColor: UIColor(rgb: 0x5e5e5e), accentColor: accentColor, destructiveColor: UIColor(rgb: 0xff3b30), - disabledColor: UIColor(rgb: 0xd0d0d0) + disabledColor: UIColor(rgb: 0xd0d0d0), + baseFontSize: 17.0 ), actionSheet: ActionSheetControllerTheme( dimColor: UIColor(white: 0.0, alpha: 0.4), @@ -611,7 +612,8 @@ final class AppDelegate: NSObject, UIApplicationDelegate { controlColor: UIColor(rgb: 0x7e8791), switchFrameColor: UIColor(rgb: 0xe0e0e0), switchContentColor: UIColor(rgb: 0x77d572), - switchHandleColor: UIColor(rgb: 0xffffff) + switchHandleColor: UIColor(rgb: 0xffffff), + baseFontSize: 17.0 ) ), strings: WalletStrings( primaryComponent: WalletStringsComponent( diff --git a/submodules/WalletUI/BUCK b/submodules/WalletUI/BUCK index cbda117102..5194a2e218 100644 --- a/submodules/WalletUI/BUCK +++ b/submodules/WalletUI/BUCK @@ -37,8 +37,8 @@ static_library( "//submodules/LocalAuth:LocalAuth", "//submodules/ScreenCaptureDetection:ScreenCaptureDetection", "//submodules/AnimatedStickerNode:AnimatedStickerNode", - #"//submodules/WalletUrl:WalletUrl", - #"//submodules/WalletCore:WalletCore", + "//submodules/WalletUrl:WalletUrl", + "//submodules/WalletCore:WalletCore", "//submodules/StringPluralization:StringPluralization", "//submodules/ActivityIndicator:ActivityIndicator", "//submodules/ProgressNavigationButtonNode:ProgressNavigationButtonNode",