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 710194f8a8..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/DetailLogoBlank@2x.png and /dev/null differ 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 0000000000..438f7ccc12 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat List/EmptyMasterDetailIcon.imageset/EmptyChat@2x.png differ 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 + + } } })