From 10e3733a88d86b0dc98c85d0f432bcf4e40edd5e Mon Sep 17 00:00:00 2001 From: overtake <> Date: Tue, 25 Jun 2019 17:01:19 +0200 Subject: [PATCH] ipad fixes --- .../Display/NavigationController.swift | 261 ++++++++++++++---- .../Display/Display/TabBarController.swift | 8 + .../Display/Display/ViewController.swift | 7 + .../Display/WallpaperBackgroundNode.swift | 73 +++++ .../Display_Xcode.xcodeproj/project.pbxproj | 4 + .../Contents.json | 2 +- .../DetailLogoBlank@2x.png | Bin 7805 -> 0 bytes .../EmptyChat@2x.png | Bin 0 -> 22731 bytes .../AuthorizationSequenceController.swift | 2 +- .../TelegramUI/ChatController.swift | 7 + .../ChatControllerBackgroundNode.swift | 63 ----- .../TelegramUI/ChatControllerNode.swift | 4 +- .../TelegramUI/ChatListController.swift | 35 ++- .../TelegramUI/TelegramUI/ChatListItem.swift | 9 +- .../TelegramUI/ComponentsThemes.swift | 2 +- .../TelegramUI/ContactListNode.swift | 37 ++- .../TelegramUI/ContactsController.swift | 16 +- .../TelegramUI/ContactsPeerItem.swift | 74 ++++- .../TelegramUI/NavigateToChatController.swift | 6 +- .../TelegramUI/SettingsController.swift | 2 +- .../TelegramUI/TelegramRootController.swift | 30 +- 21 files changed, 488 insertions(+), 154 deletions(-) create mode 100644 submodules/Display/Display/WallpaperBackgroundNode.swift delete mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/DetailLogoBlank@2x.png create mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/EmptyChat@2x.png diff --git a/submodules/Display/Display/NavigationController.swift b/submodules/Display/Display/NavigationController.swift index e0e2965134..96670d5cbd 100644 --- a/submodules/Display/Display/NavigationController.swift +++ b/submodules/Display/Display/NavigationController.swift @@ -10,21 +10,38 @@ import DisplayPrivate public final class NavigationControllerTheme { public let navigationBar: NavigationBarTheme public let emptyAreaColor: UIColor - public let emptyDetailIcon: UIImage? - public init(navigationBar: NavigationBarTheme, emptyAreaColor: UIColor, emptyDetailIcon: UIImage?) { + public init(navigationBar: NavigationBarTheme, emptyAreaColor: UIColor) { self.navigationBar = navigationBar self.emptyAreaColor = emptyAreaColor - self.emptyDetailIcon = emptyDetailIcon } } +public struct NavigationAnimationOptions : OptionSet { + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = rawValue + } + + public init() { + self.rawValue = 0 + } + + public static let removeOnMasterDetails = NavigationAnimationOptions(rawValue: 1 << 0) +} + private final class NavigationControllerContainerView: UIView { override class var layerClass: AnyClass { return CATracingLayer.self } } +public enum NavigationEmptyDetailsBackgoundMode { + case image(UIImage) + case wallpaper(UIImage) +} + private final class NavigationControllerView: UITracingLayerView { var inTransition = false @@ -34,7 +51,8 @@ private final class NavigationControllerView: UITracingLayerView { var navigationBackgroundView: UIView? var navigationSeparatorView: UIView? var emptyDetailView: UIImageView? - + var detailsBackground: WallpaperbackgroundNode? + var masterDetailsBlackout: ASDisplayNode? var topControllerNode: ASDisplayNode? /*override var accessibilityElements: [Any]? { @@ -98,12 +116,20 @@ public enum NavigationControllerMode { case automaticMasterDetail } +public enum MasterDetailLayoutBlackout : Equatable { + case master + case details +} + open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate { public var isOpaqueWhenInOverlay: Bool = true public var blocksBackgroundWhenInOverlay: Bool = true public var ready: Promise = Promise(true) + private var masterDetailsBlackout: MasterDetailLayoutBlackout? + private var backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode? + public var lockOrientation: Bool = false public var deferScreenEdgeGestures: UIRectEdge = UIRectEdge() @@ -150,10 +176,23 @@ open class NavigationController: UINavigationController, ContainableController, return self._displayNode! } - public init(mode: NavigationControllerMode, theme: NavigationControllerTheme) { + public func updateMasterDetailsBlackout(_ blackout: MasterDetailLayoutBlackout?, transition: ContainedViewLayoutTransition) { + self.masterDetailsBlackout = blackout + if isViewLoaded { + self.view.endEditing(true) + } + self.requestLayout(transition: transition) + } + public func updateBackgroundDetailsMode(_ mode: NavigationEmptyDetailsBackgoundMode?, transition: ContainedViewLayoutTransition) { + self.backgroundDetailsMode = mode + self.requestLayout(transition: transition) + } + + + public init(mode: NavigationControllerMode, theme: NavigationControllerTheme, backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode? = nil) { self.mode = mode self.theme = theme - + self.backgroundDetailsMode = backgroundDetailsMode super.init(nibName: nil, bundle: nil) } @@ -195,12 +234,12 @@ open class NavigationController: UINavigationController, ContainableController, self.controllerView.separatorView.backgroundColor = theme.navigationBar.separatorColor self.controllerView.navigationBackgroundView?.backgroundColor = theme.navigationBar.backgroundColor self.controllerView.navigationSeparatorView?.backgroundColor = theme.navigationBar.separatorColor - if let emptyDetailView = self.controllerView.emptyDetailView { - emptyDetailView.image = theme.emptyDetailIcon - if let image = theme.emptyDetailIcon { - emptyDetailView.frame = CGRect(origin: CGPoint(x: floor(emptyDetailView.center.x - image.size.width / 2.0), y: floor(emptyDetailView.center.y - image.size.height / 2.0)), size: image.size) - } - } +// if let emptyDetailView = self.controllerView.emptyDetailView { +// emptyDetailView.image = theme.emptyDetailIcon +// if let image = theme.emptyDetailIcon { +// emptyDetailView.frame = CGRect(origin: CGPoint(x: floor(emptyDetailView.center.x - image.size.width / 2.0), y: floor(emptyDetailView.center.y - image.size.height / 2.0)), size: image.size) +// } +// } } } @@ -254,43 +293,139 @@ open class NavigationController: UINavigationController, ContainableController, self.controllerView.addSubview(self.controllerView.separatorView) } - let navigationBackgroundFrame = CGRect(origin: CGPoint(x: masterData.0.maxX, y: 0.0), size: CGSize(width: lastControllerFrameAndLayout.0.width, height: (layout.statusBarHeight ?? 0.0) + 44.0)) + let navigationBackgroundFrame = CGRect(origin: CGPoint(x: masterData.0.maxX, y: 0.0), size: CGSize(width: lastControllerFrameAndLayout.0.width, height: (layout.statusBarHeight ?? 0.0) + 44.0)) - if let navigationBackgroundView = self.controllerView.navigationBackgroundView, let navigationSeparatorView = self.controllerView.navigationSeparatorView, let emptyDetailView = self.controllerView.emptyDetailView { - transition.updateFrame(view: navigationBackgroundView, frame: navigationBackgroundFrame) - transition.updateFrame(view: navigationSeparatorView, frame: CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel))) - if let image = emptyDetailView.image { - transition.updateFrame(view: emptyDetailView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size)) - } - } else { - let navigationBackgroundView = UIView() - navigationBackgroundView.backgroundColor = self.theme.navigationBar.backgroundColor - let navigationSeparatorView = UIView() - navigationSeparatorView.backgroundColor = self.theme.navigationBar.separatorColor - let emptyDetailView = UIImageView() - emptyDetailView.image = self.theme.emptyDetailIcon - emptyDetailView.alpha = 0.0 + if let backgroundDetailsMode = self.backgroundDetailsMode { - self.controllerView.navigationBackgroundView = navigationBackgroundView - self.controllerView.navigationSeparatorView = navigationSeparatorView - self.controllerView.emptyDetailView = emptyDetailView - - self.controllerView.insertSubview(navigationBackgroundView, at: 0) - self.controllerView.insertSubview(navigationSeparatorView, at: 1) - self.controllerView.insertSubview(emptyDetailView, at: 2) - - navigationBackgroundView.frame = navigationBackgroundFrame - navigationSeparatorView.frame = CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel)) - - transition.animatePositionAdditive(layer: navigationBackgroundView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) - transition.animatePositionAdditive(layer: navigationSeparatorView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) - - if let image = emptyDetailView.image { + switch backgroundDetailsMode { + case let .image(image): + if let detailsBackground = self.controllerView.detailsBackground { + self.controllerView.detailsBackground = nil + transition.updateAlpha(node: detailsBackground, alpha: 0.0, completion: { [weak detailsBackground] _ in + detailsBackground?.removeFromSupernode() + }) + } + let emptyDetailView: UIImageView + if let emptyView = self.controllerView.emptyDetailView { + emptyDetailView = emptyView + } else { + emptyDetailView = UIImageView() + emptyDetailView.alpha = 0.0 + self.controllerView.emptyDetailView = emptyDetailView + } + emptyDetailView.image = image + if emptyDetailView.superview == nil { + self.controllerView.insertSubview(emptyDetailView, at: 0) + } + transition.updateAlpha(layer: emptyDetailView.layer, alpha: 1.0) + emptyDetailView.frame = CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size) + + + case let .wallpaper(image): + if let emptyDetailView = self.controllerView.emptyDetailView { + self.controllerView.emptyDetailView = nil + transition.updateAlpha(layer: emptyDetailView.layer, alpha: 0.0, completion: { [weak emptyDetailView] _ in + emptyDetailView?.removeFromSuperview() + }) + } + let detailsBackground: WallpaperbackgroundNode + if let background = self.controllerView.detailsBackground { + detailsBackground = background + } else { + detailsBackground = WallpaperbackgroundNode() + detailsBackground.alpha = 0.0 + self.controllerView.detailsBackground = detailsBackground + } + detailsBackground.image = image + if detailsBackground.supernode == nil { + self.controllerView.insertSubview(detailsBackground.view, at: 0) + } + transition.updateAlpha(node: detailsBackground, alpha: 1.0) + detailsBackground.frame = CGRect(origin: CGPoint(x: masterData.0.maxX, y: 0.0), size: lastControllerFrameAndLayout.0.size) } - transition.updateAlpha(layer: emptyDetailView.layer, alpha: 1.0) + } else { + if let emptyDetailView = self.controllerView.emptyDetailView { + self.controllerView.emptyDetailView = nil + transition.updateAlpha(layer: emptyDetailView.layer, alpha: 0.0, completion: { [weak emptyDetailView] _ in + emptyDetailView?.removeFromSuperview() + }) + } + if let detailsBackground = self.controllerView.detailsBackground { + self.controllerView.detailsBackground = nil + transition.updateAlpha(node: detailsBackground, alpha: 0.0, completion: { [weak detailsBackground] _ in + detailsBackground?.removeFromSupernode() + }) + } } + + if let emptyDetailView = self.controllerView.emptyDetailView { +// transition.updateFrame(view: navigationBackgroundView, frame: navigationBackgroundFrame) +// transition.updateFrame(view: navigationSeparatorView, frame: CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel))) +// if let image = emptyDetailView.image { +// transition.updateFrame(view: emptyDetailView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size)) +// } + } else { + // let navigationBackgroundView = UIView() +// navigationBackgroundView.backgroundColor = self.theme.navigationBar.c +// let navigationSeparatorView = UIView() +// navigationSeparatorView.backgroundColor = self.theme.navigationBar.separatorColor +// let emptyDetailView = UIImageView() +// emptyDetailView.image = self.theme.emptyDetailIcon +// emptyDetailView.alpha = 0.0 +// +// self.controllerView.navigationBackgroundView = navigationBackgroundView +// self.controllerView.navigationSeparatorView = navigationSeparatorView +// self.controllerView.emptyDetailView = emptyDetailView +// +// self.controllerView.insertSubview(navigationBackgroundView, at: 0) +// self.controllerView.insertSubview(navigationSeparatorView, at: 1) + // self.controllerView.insertSubview(emptyDetailView, at: 0) + +// navigationBackgroundView.frame = navigationBackgroundFrame +// navigationSeparatorView.frame = CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel)) +// +// transition.animatePositionAdditive(layer: navigationBackgroundView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) +// transition.animatePositionAdditive(layer: navigationSeparatorView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) + +// if let image = emptyDetailView.image { +// emptyDetailView.frame = CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size) +// } +// +// transition.updateAlpha(layer: emptyDetailView.layer, alpha: 1.0) + } + + if let blackout = self.masterDetailsBlackout { + + let blackoutFrame: CGRect + switch blackout { + case .details: + blackoutFrame = CGRect(origin: CGPoint(x: masterData.0.maxX, y: 0.0), size: lastControllerFrameAndLayout.0.size) + case .master: + blackoutFrame = masterData.0 + } + if self.controllerView.masterDetailsBlackout == nil { + self.controllerView.masterDetailsBlackout = ASDisplayNode() + self.controllerView.masterDetailsBlackout?.backgroundColor = UIColor.black + self.controllerView.masterDetailsBlackout?.alpha = 0 + self.controllerView.masterDetailsBlackout?.frame = blackoutFrame + } + let blackoutNode = self.controllerView.masterDetailsBlackout! + if blackoutNode.supernode == nil { + self.controllerView.addSubnode(blackoutNode) + } + transition.updateFrame(node: blackoutNode, frame: blackoutFrame) + transition.updateAlpha(node: blackoutNode, alpha: 0.2) + } else { + if let blackout = self.controllerView.masterDetailsBlackout { + self.controllerView.masterDetailsBlackout = nil + transition.updateAlpha(node: blackout, alpha: 0.0, completion: { [weak blackout] _ in + blackout?.removeFromSupernode() + }) + } + } + transition.updateFrame(view: self.controllerView.separatorView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height))) case .single: self.viewControllers.first?.view.clipsToBounds = false @@ -310,6 +445,12 @@ open class NavigationController: UINavigationController, ContainableController, emptyDetailView?.removeFromSuperview() }) } + if let blackout = self.controllerView.masterDetailsBlackout { + self.controllerView.masterDetailsBlackout = nil + transition.updateAlpha(node: blackout, alpha: 0.0, completion: { [weak blackout] _ in + blackout?.removeFromSupernode() + }) + } } self.controllerView.containerView.clipsToBounds = false lastControllerFrameAndLayout = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 1) @@ -348,6 +489,11 @@ open class NavigationController: UINavigationController, ContainableController, } else { controller.navigationBar?.previousItem = .item(viewControllers[i - 1].navigationItem) } + if i < self._viewControllers.count - 1 { + controller.navigationCustomData = (viewControllers[i + 1] as? ViewController)?.customData + } else { + controller.navigationCustomData = nil + } } viewControllers[i].navigation_setNavigationController(self) @@ -872,15 +1018,26 @@ open class NavigationController: UINavigationController, ContainableController, } } - public func replaceControllersAndPush(controllers: [UIViewController], controller: ViewController, animated: Bool, ready: ValuePromise? = nil, completion: @escaping () -> Void = {}) { + public func replaceControllersAndPush(controllers: [UIViewController], controller: ViewController, animated: Bool, options: NavigationAnimationOptions = [], ready: ValuePromise? = nil, completion: @escaping () -> Void = {}) { self.view.endEditing(true) + var animated = animated + self.scheduleAfterLayout { [weak self] in guard let strongSelf = self else { return } if let validLayout = strongSelf.validLayout { - var (_, controllerLayout) = strongSelf.layoutDataForConfiguration(strongSelf.layoutConfiguration(for: validLayout), layout: validLayout, index: strongSelf.viewControllers.count) + let configuration = strongSelf.layoutConfiguration(for: validLayout) + var (_, controllerLayout) = strongSelf.layoutDataForConfiguration(configuration, layout: validLayout, index: strongSelf.viewControllers.count) controllerLayout.inputHeight = nil + if options.contains(.removeOnMasterDetails) { + switch configuration { + case .masterDetail: + animated = false + default: + break + } + } controller.containerLayoutUpdated(controllerLayout, transition: .immediate) } strongSelf.currentPushDisposable.set((controller.ready.get() @@ -898,16 +1055,26 @@ open class NavigationController: UINavigationController, ContainableController, } } - public func replaceAllButRootController(_ controller: ViewController, animated: Bool, ready: ValuePromise? = nil, completion: @escaping () -> Void = {}) { + public func replaceAllButRootController(_ controller: ViewController, animated: Bool, animationOptions: NavigationAnimationOptions = [], ready: ValuePromise? = nil, completion: @escaping () -> Void = {}) { self.view.endEditing(true) self.scheduleAfterLayout { [weak self] in guard let strongSelf = self else { return } + var animated = animated if let validLayout = strongSelf.validLayout { - var (_, controllerLayout) = strongSelf.layoutDataForConfiguration(strongSelf.layoutConfiguration(for: validLayout), layout: validLayout, index: strongSelf.viewControllers.count) + let configuration = strongSelf.layoutConfiguration(for: validLayout) + var (_, controllerLayout) = strongSelf.layoutDataForConfiguration(configuration, layout: validLayout, index: strongSelf.viewControllers.count) controllerLayout.inputHeight = nil controller.containerLayoutUpdated(controllerLayout, transition: .immediate) + switch configuration { + case .masterDetail: + if animationOptions.contains(.removeOnMasterDetails) { + animated = false + } + case .single: + break + } } strongSelf.currentPushDisposable.set((controller.ready.get() |> deliverOnMainQueue diff --git a/submodules/Display/Display/TabBarController.swift b/submodules/Display/Display/TabBarController.swift index 900e286a55..698cf8f93c 100644 --- a/submodules/Display/Display/TabBarController.swift +++ b/submodules/Display/Display/TabBarController.swift @@ -73,6 +73,14 @@ open class TabBarController: ViewController { } } + open override var navigationCustomData: Any? { + didSet { + for controller in controllers { + controller.navigationCustomData = navigationCustomData + } + } + } + public private(set) var controllers: [ViewController] = [] private let _ready = Promise() diff --git a/submodules/Display/Display/ViewController.swift b/submodules/Display/Display/ViewController.swift index fea78f9d79..76159a21fe 100644 --- a/submodules/Display/Display/ViewController.swift +++ b/submodules/Display/Display/ViewController.swift @@ -206,6 +206,13 @@ open class ViewControllerPresentationArguments { public var scrollToTopWithTabBar: (() -> Void)? public var longTapWithTabBar: (() -> Void)? + open var navigationCustomData: Any? + open var customData: Any? { + get { + return nil + } + } + public var attemptNavigation: (@escaping () -> Void) -> Bool = { _ in return true } diff --git a/submodules/Display/Display/WallpaperBackgroundNode.swift b/submodules/Display/Display/WallpaperBackgroundNode.swift new file mode 100644 index 0000000000..c86228a504 --- /dev/null +++ b/submodules/Display/Display/WallpaperBackgroundNode.swift @@ -0,0 +1,73 @@ +// +// WallpaperBackgroundNode.swift +// Display +// +// Created by Mikhail Filimonov on 13/06/2019. +// Copyright © 2019 Telegram. All rights reserved. +// + +import UIKit + +private let motionAmount: CGFloat = 32.0 + +public final class WallpaperbackgroundNode: ASDisplayNode { + let contentNode: ASDisplayNode + + public var motionEnabled: Bool = false { + didSet { + if oldValue != self.motionEnabled { + if self.motionEnabled { + let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis) + horizontal.minimumRelativeValue = motionAmount + horizontal.maximumRelativeValue = -motionAmount + + let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis) + vertical.minimumRelativeValue = motionAmount + vertical.maximumRelativeValue = -motionAmount + + let group = UIMotionEffectGroup() + group.motionEffects = [horizontal, vertical] + self.contentNode.view.addMotionEffect(group) + } else { + for effect in self.contentNode.view.motionEffects { + self.contentNode.view.removeMotionEffect(effect) + } + } + self.updateScale() + } + } + } + + public var image: UIImage? { + didSet { + self.contentNode.contents = self.image?.cgImage + } + } + + func updateScale() { + if self.motionEnabled { + let scale = (self.frame.width + motionAmount * 2.0) / self.frame.width + self.contentNode.transform = CATransform3DMakeScale(scale, scale, 1.0) + } else { + self.contentNode.transform = CATransform3DIdentity + } + } + + public override init() { + self.contentNode = ASDisplayNode() + self.contentNode.contentMode = .scaleAspectFill + + super.init() + + self.clipsToBounds = true + self.contentNode.frame = self.bounds + self.addSubnode(self.contentNode) + } + + override public func layout() { + super.layout() + self.contentNode.bounds = self.bounds + self.contentNode.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY) + self.updateScale() + } +} diff --git a/submodules/Display/Display_Xcode.xcodeproj/project.pbxproj b/submodules/Display/Display_Xcode.xcodeproj/project.pbxproj index 8edc164afd..972eda7c58 100644 --- a/submodules/Display/Display_Xcode.xcodeproj/project.pbxproj +++ b/submodules/Display/Display_Xcode.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ D015F7581D1B467200E269B5 /* ActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7571D1B467200E269B5 /* ActionSheetController.swift */; }; D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7591D1B46B600E269B5 /* ActionSheetControllerNode.swift */; }; D01847661FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */; }; + D01E1F0222B28D9400AD6DAE /* WallpaperBackgroundNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E1F0122B28D9400AD6DAE /* WallpaperBackgroundNode.swift */; }; D01E2BDE1D9049620066BF65 /* GridNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDD1D9049620066BF65 /* GridNode.swift */; }; D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */; }; D01E2BE21D9049F60066BF65 /* GridItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BE11D9049F60066BF65 /* GridItemNode.swift */; }; @@ -195,6 +196,7 @@ D015F7591D1B46B600E269B5 /* ActionSheetControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetControllerNode.swift; sourceTree = ""; }; D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainedViewLayoutTransition.swift; sourceTree = ""; }; D01C06C61FC2558F001561AB /* SwiftSignalKitMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSignalKitMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D01E1F0122B28D9400AD6DAE /* WallpaperBackgroundNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallpaperBackgroundNode.swift; sourceTree = ""; }; D01E2BDD1D9049620066BF65 /* GridNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNode.swift; sourceTree = ""; }; D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNodeScroller.swift; sourceTree = ""; }; D01E2BE11D9049F60066BF65 /* GridItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridItemNode.swift; sourceTree = ""; }; @@ -462,6 +464,7 @@ D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */, D04554A921BDB93E007A6DD9 /* CollectionIndexNode.swift */, 09DD88EA21BCA5E0000766BC /* EditableTextNode.swift */, + D01E1F0122B28D9400AD6DAE /* WallpaperBackgroundNode.swift */, ); name = Nodes; sourceTree = ""; @@ -939,6 +942,7 @@ D033874E223D3E86007A2CE4 /* AccessibilityAreaNode.swift in Sources */, D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */, D01847661FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */, + D01E1F0222B28D9400AD6DAE /* WallpaperBackgroundNode.swift in Sources */, D03725C11D6DF594007FC290 /* ContextMenuNode.swift in Sources */, D03AA4DB202DA6D60056C405 /* PeekControllerContent.swift in Sources */, D053CB611D22B4F200DD41DF /* CATracingLayer.m in Sources */, diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/Contents.json index 350a5ab12a..83abcba088 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/Contents.json @@ -6,7 +6,7 @@ }, { "idiom" : "universal", - "filename" : "DetailLogoBlank@2x.png", + "filename" : "EmptyChat@2x.png", "scale" : "2x" }, { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/DetailLogoBlank@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/DetailLogoBlank@2x.png deleted file mode 100644 index 710194f8a82fda495b8a0a7ec59e1879dce38bf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7805 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 ScyRxXhNGdPtK6h$6Z1c?+iMX3 diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/EmptyChat@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/EmptyChat@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..438f7ccc12c53906be6057d69ee268b5db60d57c GIT binary patch literal 22731 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 bn03OWBMTFI_Ve9GD&V22qNV&*(K6(Jm|N)$ literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceController.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceController.swift index 3640cf9bf1..579216fa02 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceController.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceController.swift @@ -47,7 +47,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail self.theme = theme self.openUrl = openUrl - super.init(mode: .single, theme: NavigationControllerTheme(navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black, emptyDetailIcon: nil)) + super.init(mode: .single, theme: NavigationControllerTheme(navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black)) self.stateDisposable = (account.postbox.stateView() |> map { view -> InnerState in diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index 8dbec28fa6..f1cb13049b 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -227,6 +227,10 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, var purposefulAction: (() -> Void)? + public override var customData: Any? { + return self.chatLocation + } + public init(context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false)) { let _ = ChatControllerCount.modify { value in return value + 1 @@ -4164,6 +4168,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState { controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState self.updateItemNodesSelectionStates(animated: transition.isAnimated) + (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(controllerInteraction.selectionState != nil ? .master : nil, transition: transition) } } @@ -5369,6 +5374,8 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, self.chatDisplayNode.historyNode.scrollToEndOfHistory() } + + public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { self.navigateToMessage(from: nil, to: messageLocation, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, animated: animated, completion: completion, customPresentProgress: customPresentProgress) } diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerBackgroundNode.swift b/submodules/TelegramUI/TelegramUI/ChatControllerBackgroundNode.swift index 2ab85d748e..0e2bd8f68e 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerBackgroundNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerBackgroundNode.swift @@ -6,69 +6,6 @@ import Display import SwiftSignalKit import Postbox -private let motionAmount: CGFloat = 32.0 - -final class ChatBackgroundNode: ASDisplayNode { - let contentNode: ASDisplayNode - - var motionEnabled: Bool = false { - didSet { - if oldValue != self.motionEnabled { - if self.motionEnabled { - let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis) - horizontal.minimumRelativeValue = motionAmount - horizontal.maximumRelativeValue = -motionAmount - - let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis) - vertical.minimumRelativeValue = motionAmount - vertical.maximumRelativeValue = -motionAmount - - let group = UIMotionEffectGroup() - group.motionEffects = [horizontal, vertical] - self.contentNode.view.addMotionEffect(group) - } else { - for effect in self.contentNode.view.motionEffects { - self.contentNode.view.removeMotionEffect(effect) - } - } - self.updateScale() - } - } - } - - var image: UIImage? { - didSet { - self.contentNode.contents = self.image?.cgImage - } - } - - func updateScale() { - if self.motionEnabled { - let scale = (self.frame.width + motionAmount * 2.0) / self.frame.width - self.contentNode.transform = CATransform3DMakeScale(scale, scale, 1.0) - } else { - self.contentNode.transform = CATransform3DIdentity - } - } - - override init() { - self.contentNode = ASDisplayNode() - self.contentNode.contentMode = .scaleAspectFill - - super.init() - - self.clipsToBounds = true - self.contentNode.frame = self.bounds - self.addSubnode(self.contentNode) - } - - override func layout() { - super.layout() - self.contentNode.bounds = self.bounds - self.contentNode.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY) - self.updateScale() - } -} private var backgroundImageForWallpaper: (TelegramWallpaper, Bool, UIImage)? private var serviceBackgroundColorForWallpaper: (TelegramWallpaper, UIColor)? diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift index f9fd8af55f..92408a4752 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift @@ -69,7 +69,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - let backgroundNode: ChatBackgroundNode + let backgroundNode: WallpaperbackgroundNode let historyNode: ChatHistoryListNode let historyNodeContainer: ASDisplayNode let loadingNode: ChatLoadingNode @@ -191,7 +191,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.navigationBar = navigationBar self.controller = controller - self.backgroundNode = ChatBackgroundNode() + self.backgroundNode = WallpaperbackgroundNode() self.backgroundNode.displaysAsynchronously = false self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer() diff --git a/submodules/TelegramUI/TelegramUI/ChatListController.swift b/submodules/TelegramUI/TelegramUI/ChatListController.swift index e9789beb0d..5ed2c70ba4 100644 --- a/submodules/TelegramUI/TelegramUI/ChatListController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatListController.swift @@ -94,6 +94,12 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD private var searchContentNode: NavigationBarSearchContentNode? + public override var navigationCustomData: Any? { + didSet { + self.chatListDisplayNode.chatListNode.updateSelectedChatLocation(self.navigationCustomData as? ChatLocation, progress: 1, transition: .immediate) + } + } + public init(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool = false) { self.context = context self.controlsHistoryPreload = controlsHistoryPreload @@ -684,9 +690,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD scrollToEndIfExists = true } - let animated: Bool = !scrollToEndIfExists || strongSelf.groupId != PeerGroupId.root - - navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: animated, animated: animated, parentGroupId: strongSelf.groupId, completion: { [weak self] in + navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] in self?.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) }) } @@ -726,9 +730,16 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD |> deliverOnMainQueue).start(next: { [weak strongSelf] actualPeerId in if let strongSelf = strongSelf { if let navigationController = strongSelf.navigationController as? NavigationController { + + var scrollToEndIfExists = false + if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { + scrollToEndIfExists = true + } + + navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), messageId: messageId, purposefulAction: { self?.deactivateSearch(animated: false) - }) + }, scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : []) strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) } } @@ -748,12 +759,18 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD strongSelf.openMessageFromSearchDisposable.set((storedPeer |> deliverOnMainQueue).start(completed: { [weak strongSelf] in if let strongSelf = strongSelf { if dismissSearch { - strongSelf.dismissSearchOnDisappear = true + strongSelf.deactivateSearch(animated: true) } + + var scrollToEndIfExists = false + if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { + scrollToEndIfExists = true + } + if let navigationController = strongSelf.navigationController as? NavigationController { navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in self?.deactivateSearch(animated: false) - }) + }, scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : []) strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) } } @@ -1095,10 +1112,6 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD override public func navigationStackConfigurationUpdated(next: [ViewController]) { super.navigationStackConfigurationUpdated(next: next) - - let chatLocation = (next.first as? ChatController)?.chatLocation - - self.chatListDisplayNode.chatListNode.updateSelectedChatLocation(chatLocation, progress: 1.0, transition: .immediate) } @objc func editPressed() { @@ -1110,6 +1123,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD self.navigationItem.rightBarButtonItem = editItem } self.searchContentNode?.setIsEnabled(false, animated: true) + (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(.details, transition: .animated(duration: 0.5, curve: .spring)) self.chatListDisplayNode.chatListNode.updateState { state in var state = state state.editing = true @@ -1126,6 +1140,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD } else { self.navigationItem.rightBarButtonItem = editItem } + (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(nil, transition: .animated(duration: 0.4, curve: .spring)) self.searchContentNode?.setIsEnabled(true, animated: true) self.chatListDisplayNode.chatListNode.updateState { state in var state = state diff --git a/submodules/TelegramUI/TelegramUI/ChatListItem.swift b/submodules/TelegramUI/TelegramUI/ChatListItem.swift index c7a13c8892..1606f5bb74 100644 --- a/submodules/TelegramUI/TelegramUI/ChatListItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatListItem.swift @@ -504,7 +504,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { self.updateIsHighlighted(transition: (animated && !highlighted) ? .animated(duration: 0.3, curve: .easeInOut) : .immediate) } - func updateIsHighlighted(transition: ContainedViewLayoutTransition) { + var reallyHighlighted: Bool { var reallyHighlighted = self.isHighlighted if let item = self.item { if let itemChatLocation = item.content.chatLocation { @@ -513,6 +513,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } } } + return reallyHighlighted + } + + func updateIsHighlighted(transition: ContainedViewLayoutTransition) { + if reallyHighlighted { if self.highlightedBackgroundNode.supernode == nil { @@ -1193,7 +1198,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: strongSelf.onlineNode, frame: onlineFrame) let onlineIcon: UIImage? - if strongSelf.isHighlighted { + if strongSelf.reallyHighlighted { onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted) } else if item.index.pinningIndex != nil { onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned) diff --git a/submodules/TelegramUI/TelegramUI/ComponentsThemes.swift b/submodules/TelegramUI/TelegramUI/ComponentsThemes.swift index 87014b2691..68be053da1 100644 --- a/submodules/TelegramUI/TelegramUI/ComponentsThemes.swift +++ b/submodules/TelegramUI/TelegramUI/ComponentsThemes.swift @@ -57,6 +57,6 @@ extension PeekControllerTheme { public extension NavigationControllerTheme { convenience init(presentationTheme: PresentationTheme) { - self.init(navigationBar: NavigationBarTheme(rootControllerTheme: presentationTheme), emptyAreaColor: presentationTheme.chatList.backgroundColor, emptyDetailIcon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationTheme.chatList.messageTextColor.withAlphaComponent(0.2))) + self.init(navigationBar: NavigationBarTheme(rootControllerTheme: presentationTheme), emptyAreaColor: presentationTheme.chatList.backgroundColor) } } diff --git a/submodules/TelegramUI/TelegramUI/ContactListNode.swift b/submodules/TelegramUI/TelegramUI/ContactListNode.swift index b0d16459a9..b58280f980 100644 --- a/submodules/TelegramUI/TelegramUI/ContactListNode.swift +++ b/submodules/TelegramUI/TelegramUI/ContactListNode.swift @@ -94,12 +94,21 @@ private enum ContactListNodeEntryId: Hashable { } } +final class ContactItemHighlighting { + var chatLocation: ChatLocation? + init(chatLocation: ChatLocation? = nil) { + self.chatLocation = chatLocation + } +} + private final class ContactListNodeInteraction { - let activateSearch: () -> Void - let openSortMenu: () -> Void - let authorize: () -> Void - let suppressWarning: () -> Void - let openPeer: (ContactListPeer) -> Void + fileprivate let activateSearch: () -> Void + fileprivate let openSortMenu: () -> Void + fileprivate let authorize: () -> Void + fileprivate let suppressWarning: () -> Void + fileprivate let openPeer: (ContactListPeer) -> Void + + let itemHighlighting = ContactItemHighlighting() init(activateSearch: @escaping () -> Void, openSortMenu: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer) -> Void) { self.activateSearch = activateSearch @@ -191,6 +200,8 @@ private enum ContactListNodeEntry: Comparable, Identifiable { } } + + func item(account: Account, interaction: ContactListNodeInteraction) -> ListViewItem { switch self { case let .search(theme, strings): @@ -245,7 +256,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { } return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in interaction.openPeer(peer) - }) + }, itemHighlighting: interaction.itemHighlighting) } } @@ -775,6 +786,7 @@ final class ContactListNode: ASDisplayNode { var selectionState: ContactListNodeGroupSelectionState? { return self.selectionStateValue } + private var interaction: ContactListNodeInteraction? private var enableUpdatesValue = false var enableUpdates: Bool { @@ -929,6 +941,8 @@ final class ContactListNode: ASDisplayNode { } } + self.interaction = interaction + let context = self.context var firstTime: Int32 = 1 let selectionStateSignal = self.selectionStatePromise.get() @@ -1318,6 +1332,17 @@ final class ContactListNode: ASDisplayNode { self.enableUpdates = true } + func updateSelectedChatLocation(_ chatLocation: ChatLocation?, progress: CGFloat, transition: ContainedViewLayoutTransition) { + + self.interaction?.itemHighlighting.chatLocation = chatLocation + + self.listNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ContactsPeerItemNode { + itemNode.updateIsHighlighted(transition: transition) + } + } + } + deinit { self.disposable.dispose() self.presentationDataDisposable?.dispose() diff --git a/submodules/TelegramUI/TelegramUI/ContactsController.swift b/submodules/TelegramUI/TelegramUI/ContactsController.swift index c84f656741..a211b96b86 100644 --- a/submodules/TelegramUI/TelegramUI/ContactsController.swift +++ b/submodules/TelegramUI/TelegramUI/ContactsController.swift @@ -56,6 +56,7 @@ public class ContactsController: ViewController { private var contactsNode: ContactsControllerNode { return self.displayNode as! ContactsControllerNode } + private var validLayout: ContainerViewLayout? private let index: PeerNameIndex = .lastNameFirst @@ -73,6 +74,12 @@ public class ContactsController: ViewController { var switchToChatsController: (() -> Void)? + public override var navigationCustomData: Any? { + didSet { + self.contactsNode.contactListNode.updateSelectedChatLocation(self.navigationCustomData as? ChatLocation, progress: 1, transition: .immediate) + } + } + public init(context: AccountContext) { self.context = context @@ -195,12 +202,19 @@ public class ContactsController: ViewController { switch peer { case let .peer(peer, _, _): if let navigationController = strongSelf.navigationController as? NavigationController { + + var scrollToEndIfExists = false + if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { + scrollToEndIfExists = true + } + + navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in if fromSearch { self?.deactivateSearch(animated: false) self?.switchToChatsController?() } - }, completion: { [weak self] in + }, scrollToEndIfExists: scrollToEndIfExists, options: [.removeOnMasterDetails], completion: { [weak self] in if let strongSelf = self { strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) } diff --git a/submodules/TelegramUI/TelegramUI/ContactsPeerItem.swift b/submodules/TelegramUI/TelegramUI/ContactsPeerItem.swift index 2591722525..29d1fafdc9 100644 --- a/submodules/TelegramUI/TelegramUI/ContactsPeerItem.swift +++ b/submodules/TelegramUI/TelegramUI/ContactsPeerItem.swift @@ -125,6 +125,7 @@ class ContactsPeerItem: ListViewItem { let action: (ContactsPeerItemPeer) -> Void let setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? let deletePeer: ((PeerId) -> Void)? + let itemHighlighting: ContactItemHighlighting? let selectable: Bool @@ -132,7 +133,7 @@ class ContactsPeerItem: ListViewItem { let header: ListViewItemHeader? - init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil) { + init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil) { self.theme = theme self.strings = strings self.sortOrder = sortOrder @@ -151,7 +152,7 @@ class ContactsPeerItem: ListViewItem { self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.deletePeer = deletePeer self.header = header - + self.itemHighlighting = itemHighlighting self.selectable = self.enabled if let index = index { @@ -298,6 +299,9 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { private var avatarState: (Account, Peer?)? + private var isHighlighted: Bool = false + + private var peerPresenceManager: PeerPresenceStatusManager? private var layoutParams: (ContactsPeerItem, ListViewItemLayoutParams, Bool, Bool, Bool)? var chatPeer: Peer? { @@ -365,25 +369,65 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { super.setHighlighted(highlighted, at: point, animated: animated) - if highlighted && self.selectionNode == nil { - self.highlightedBackgroundNode.alpha = 1.0 + self.isHighlighted = highlighted + + self.updateIsHighlighted(transition: (animated && !highlighted) ? .animated(duration: 0.3, curve: .easeInOut) : .immediate) + +// if highlighted && self.selectionNode == nil { +// self.highlightedBackgroundNode.alpha = 1.0 +// if self.highlightedBackgroundNode.supernode == nil { +// self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: self.separatorNode) +// } +// } else { +// if self.highlightedBackgroundNode.supernode != nil { +// if animated { +// self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in +// if let strongSelf = self { +// if completed { +// strongSelf.highlightedBackgroundNode.removeFromSupernode() +// } +// } +// }) +// self.highlightedBackgroundNode.alpha = 0.0 +// } else { +// self.highlightedBackgroundNode.removeFromSupernode() +// } +// } +// } + } + + + func updateIsHighlighted(transition: ContainedViewLayoutTransition) { + var reallyHighlighted = self.isHighlighted + if let item = self.item { + switch item.peer { + case let .peer(_, chatPeer): + if let peer = chatPeer { + if ChatLocation.peer(peer.id) == item.itemHighlighting?.chatLocation { + reallyHighlighted = true + } + } + default: + break + } + } + + if reallyHighlighted { if self.highlightedBackgroundNode.supernode == nil { self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: self.separatorNode) + self.highlightedBackgroundNode.alpha = 0.0 } + self.highlightedBackgroundNode.layer.removeAllAnimations() + transition.updateAlpha(layer: self.highlightedBackgroundNode.layer, alpha: 1.0) } else { if self.highlightedBackgroundNode.supernode != nil { - if animated { - self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in - if let strongSelf = self { - if completed { - strongSelf.highlightedBackgroundNode.removeFromSupernode() - } + transition.updateAlpha(layer: self.highlightedBackgroundNode.layer, alpha: 0.0, completion: { [weak self] completed in + if let strongSelf = self { + if completed { + strongSelf.highlightedBackgroundNode.removeFromSupernode() } - }) - self.highlightedBackgroundNode.alpha = 0.0 - } else { - self.highlightedBackgroundNode.removeFromSupernode() - } + } + }) } } } diff --git a/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift b/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift index 57a0a775e9..50471249d7 100644 --- a/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift +++ b/submodules/TelegramUI/TelegramUI/NavigateToChatController.swift @@ -10,7 +10,7 @@ public enum NavigateToChatKeepStack { case never } -public func navigateToChatController(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) { +public func navigateToChatController(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, keepStack: NavigateToChatKeepStack = .default, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping () -> Void = {}) { var found = false var isFirst = true for controller in navigationController.viewControllers.reversed() { @@ -72,9 +72,9 @@ public func navigateToChatController(navigationController: NavigationController, } }) if viewControllers.isEmpty { - navigationController.replaceAllButRootController(controller, animated: animated, completion: completion) + navigationController.replaceAllButRootController(controller, animated: animated, animationOptions: options, completion: completion) } else { - navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: animated, completion: completion) + navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: animated, options: options, completion: completion) } } } diff --git a/submodules/TelegramUI/TelegramUI/SettingsController.swift b/submodules/TelegramUI/TelegramUI/SettingsController.swift index d1c1786bca..79d16b5410 100644 --- a/submodules/TelegramUI/TelegramUI/SettingsController.swift +++ b/submodules/TelegramUI/TelegramUI/SettingsController.swift @@ -1243,7 +1243,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM let controller = SettingsControllerImpl(currentContext: context, contextValue: contextValue, state: signal, tabBarItem: tabBarItem, accountsAndPeers: accountsAndPeers.get()) pushControllerImpl = { [weak controller] value in - (controller?.navigationController as? NavigationController)?.replaceAllButRootController(value, animated: true) + (controller?.navigationController as? NavigationController)?.replaceAllButRootController(value, animated: true, animationOptions: [.removeOnMasterDetails]) } presentControllerImpl = { [weak controller] value, arguments in controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true) diff --git a/submodules/TelegramUI/TelegramUI/TelegramRootController.swift b/submodules/TelegramUI/TelegramUI/TelegramRootController.swift index ea90ee2296..14fa0a112b 100644 --- a/submodules/TelegramUI/TelegramUI/TelegramRootController.swift +++ b/submodules/TelegramUI/TelegramUI/TelegramRootController.swift @@ -26,16 +26,44 @@ public final class TelegramRootController: NavigationController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(mode: .automaticMasterDetail, theme: NavigationControllerTheme(presentationTheme: self.presentationData.theme)) + let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode? + switch presentationData.chatWallpaper { + case .color: + let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2)) + navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil + default: + let image = chatControllerBackgroundImage(wallpaper: presentationData.chatWallpaper, mediaBox: context.account.postbox.mediaBox) + navigationDetailsBackgroundMode = image != nil ? .wallpaper(image!) : nil + } + + super.init(mode: .automaticMasterDetail, theme: NavigationControllerTheme(presentationTheme: self.presentationData.theme), backgroundDetailsMode: navigationDetailsBackgroundMode) self.presentationDataDisposable = (context.sharedContext.presentationData |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self { + + if presentationData.chatWallpaper != strongSelf.presentationData.chatWallpaper { + let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode? + switch presentationData.chatWallpaper { + case .color: + let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2)) + navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil + default: + let image = chatControllerBackgroundImage(wallpaper: presentationData.chatWallpaper, mediaBox: strongSelf.context.account.postbox.mediaBox) + navigationDetailsBackgroundMode = image != nil ? .wallpaper(image!) : nil + } + strongSelf.updateBackgroundDetailsMode(navigationDetailsBackgroundMode, transition: .immediate) + } + + + let previousTheme = strongSelf.presentationData.theme strongSelf.presentationData = presentationData if previousTheme !== presentationData.theme { strongSelf.rootTabController?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme)) strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBar.style.style + + } } })