ipad fixes

This commit is contained in:
overtake 2019-06-25 17:01:19 +02:00
parent bb94bd7e88
commit 10e3733a88
21 changed files with 488 additions and 154 deletions

View File

@ -10,21 +10,38 @@ import DisplayPrivate
public final class NavigationControllerTheme { public final class NavigationControllerTheme {
public let navigationBar: NavigationBarTheme public let navigationBar: NavigationBarTheme
public let emptyAreaColor: UIColor 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.navigationBar = navigationBar
self.emptyAreaColor = emptyAreaColor 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 { private final class NavigationControllerContainerView: UIView {
override class var layerClass: AnyClass { override class var layerClass: AnyClass {
return CATracingLayer.self return CATracingLayer.self
} }
} }
public enum NavigationEmptyDetailsBackgoundMode {
case image(UIImage)
case wallpaper(UIImage)
}
private final class NavigationControllerView: UITracingLayerView { private final class NavigationControllerView: UITracingLayerView {
var inTransition = false var inTransition = false
@ -34,7 +51,8 @@ private final class NavigationControllerView: UITracingLayerView {
var navigationBackgroundView: UIView? var navigationBackgroundView: UIView?
var navigationSeparatorView: UIView? var navigationSeparatorView: UIView?
var emptyDetailView: UIImageView? var emptyDetailView: UIImageView?
var detailsBackground: WallpaperbackgroundNode?
var masterDetailsBlackout: ASDisplayNode?
var topControllerNode: ASDisplayNode? var topControllerNode: ASDisplayNode?
/*override var accessibilityElements: [Any]? { /*override var accessibilityElements: [Any]? {
@ -98,12 +116,20 @@ public enum NavigationControllerMode {
case automaticMasterDetail case automaticMasterDetail
} }
public enum MasterDetailLayoutBlackout : Equatable {
case master
case details
}
open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate { open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate {
public var isOpaqueWhenInOverlay: Bool = true public var isOpaqueWhenInOverlay: Bool = true
public var blocksBackgroundWhenInOverlay: Bool = true public var blocksBackgroundWhenInOverlay: Bool = true
public var ready: Promise<Bool> = Promise(true) public var ready: Promise<Bool> = Promise(true)
private var masterDetailsBlackout: MasterDetailLayoutBlackout?
private var backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode?
public var lockOrientation: Bool = false public var lockOrientation: Bool = false
public var deferScreenEdgeGestures: UIRectEdge = UIRectEdge() public var deferScreenEdgeGestures: UIRectEdge = UIRectEdge()
@ -150,10 +176,23 @@ open class NavigationController: UINavigationController, ContainableController,
return self._displayNode! 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.mode = mode
self.theme = theme self.theme = theme
self.backgroundDetailsMode = backgroundDetailsMode
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
} }
@ -195,12 +234,12 @@ open class NavigationController: UINavigationController, ContainableController,
self.controllerView.separatorView.backgroundColor = theme.navigationBar.separatorColor self.controllerView.separatorView.backgroundColor = theme.navigationBar.separatorColor
self.controllerView.navigationBackgroundView?.backgroundColor = theme.navigationBar.backgroundColor self.controllerView.navigationBackgroundView?.backgroundColor = theme.navigationBar.backgroundColor
self.controllerView.navigationSeparatorView?.backgroundColor = theme.navigationBar.separatorColor self.controllerView.navigationSeparatorView?.backgroundColor = theme.navigationBar.separatorColor
if let emptyDetailView = self.controllerView.emptyDetailView { // if let emptyDetailView = self.controllerView.emptyDetailView {
emptyDetailView.image = theme.emptyDetailIcon // emptyDetailView.image = theme.emptyDetailIcon
if let 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) // 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) 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 { if let backgroundDetailsMode = self.backgroundDetailsMode {
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
self.controllerView.navigationBackgroundView = navigationBackgroundView switch backgroundDetailsMode {
self.controllerView.navigationSeparatorView = navigationSeparatorView case let .image(image):
self.controllerView.emptyDetailView = emptyDetailView if let detailsBackground = self.controllerView.detailsBackground {
self.controllerView.detailsBackground = nil
self.controllerView.insertSubview(navigationBackgroundView, at: 0) transition.updateAlpha(node: detailsBackground, alpha: 0.0, completion: { [weak detailsBackground] _ in
self.controllerView.insertSubview(navigationSeparatorView, at: 1) detailsBackground?.removeFromSupernode()
self.controllerView.insertSubview(emptyDetailView, at: 2) })
}
navigationBackgroundView.frame = navigationBackgroundFrame let emptyDetailView: UIImageView
navigationSeparatorView.frame = CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel)) if let emptyView = self.controllerView.emptyDetailView {
emptyDetailView = emptyView
transition.animatePositionAdditive(layer: navigationBackgroundView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) } else {
transition.animatePositionAdditive(layer: navigationSeparatorView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) emptyDetailView = UIImageView()
emptyDetailView.alpha = 0.0
if let image = emptyDetailView.image { 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) 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))) 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: case .single:
self.viewControllers.first?.view.clipsToBounds = false self.viewControllers.first?.view.clipsToBounds = false
@ -310,6 +445,12 @@ open class NavigationController: UINavigationController, ContainableController,
emptyDetailView?.removeFromSuperview() 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 self.controllerView.containerView.clipsToBounds = false
lastControllerFrameAndLayout = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 1) lastControllerFrameAndLayout = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 1)
@ -348,6 +489,11 @@ open class NavigationController: UINavigationController, ContainableController,
} else { } else {
controller.navigationBar?.previousItem = .item(viewControllers[i - 1].navigationItem) 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) 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<Bool>? = nil, completion: @escaping () -> Void = {}) { public func replaceControllersAndPush(controllers: [UIViewController], controller: ViewController, animated: Bool, options: NavigationAnimationOptions = [], ready: ValuePromise<Bool>? = nil, completion: @escaping () -> Void = {}) {
self.view.endEditing(true) self.view.endEditing(true)
var animated = animated
self.scheduleAfterLayout { [weak self] in self.scheduleAfterLayout { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if let validLayout = strongSelf.validLayout { 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 controllerLayout.inputHeight = nil
if options.contains(.removeOnMasterDetails) {
switch configuration {
case .masterDetail:
animated = false
default:
break
}
}
controller.containerLayoutUpdated(controllerLayout, transition: .immediate) controller.containerLayoutUpdated(controllerLayout, transition: .immediate)
} }
strongSelf.currentPushDisposable.set((controller.ready.get() strongSelf.currentPushDisposable.set((controller.ready.get()
@ -898,16 +1055,26 @@ open class NavigationController: UINavigationController, ContainableController,
} }
} }
public func replaceAllButRootController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil, completion: @escaping () -> Void = {}) { public func replaceAllButRootController(_ controller: ViewController, animated: Bool, animationOptions: NavigationAnimationOptions = [], ready: ValuePromise<Bool>? = nil, completion: @escaping () -> Void = {}) {
self.view.endEditing(true) self.view.endEditing(true)
self.scheduleAfterLayout { [weak self] in self.scheduleAfterLayout { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
var animated = animated
if let validLayout = strongSelf.validLayout { 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 controllerLayout.inputHeight = nil
controller.containerLayoutUpdated(controllerLayout, transition: .immediate) controller.containerLayoutUpdated(controllerLayout, transition: .immediate)
switch configuration {
case .masterDetail:
if animationOptions.contains(.removeOnMasterDetails) {
animated = false
}
case .single:
break
}
} }
strongSelf.currentPushDisposable.set((controller.ready.get() strongSelf.currentPushDisposable.set((controller.ready.get()
|> deliverOnMainQueue |> deliverOnMainQueue

View File

@ -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] = [] public private(set) var controllers: [ViewController] = []
private let _ready = Promise<Bool>() private let _ready = Promise<Bool>()

View File

@ -206,6 +206,13 @@ open class ViewControllerPresentationArguments {
public var scrollToTopWithTabBar: (() -> Void)? public var scrollToTopWithTabBar: (() -> Void)?
public var longTapWithTabBar: (() -> Void)? public var longTapWithTabBar: (() -> Void)?
open var navigationCustomData: Any?
open var customData: Any? {
get {
return nil
}
}
public var attemptNavigation: (@escaping () -> Void) -> Bool = { _ in public var attemptNavigation: (@escaping () -> Void) -> Bool = { _ in
return true return true
} }

View File

@ -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()
}
}

View File

@ -23,6 +23,7 @@
D015F7581D1B467200E269B5 /* ActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7571D1B467200E269B5 /* ActionSheetController.swift */; }; D015F7581D1B467200E269B5 /* ActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7571D1B467200E269B5 /* ActionSheetController.swift */; };
D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7591D1B46B600E269B5 /* ActionSheetControllerNode.swift */; }; D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7591D1B46B600E269B5 /* ActionSheetControllerNode.swift */; };
D01847661FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847651FFA72E000075256 /* ContainedViewLayoutTransition.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 */; }; D01E2BDE1D9049620066BF65 /* GridNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDD1D9049620066BF65 /* GridNode.swift */; };
D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */; }; D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */; };
D01E2BE21D9049F60066BF65 /* GridItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BE11D9049F60066BF65 /* GridItemNode.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 = "<group>"; }; D015F7591D1B46B600E269B5 /* ActionSheetControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetControllerNode.swift; sourceTree = "<group>"; };
D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainedViewLayoutTransition.swift; sourceTree = "<group>"; }; D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainedViewLayoutTransition.swift; sourceTree = "<group>"; };
D01C06C61FC2558F001561AB /* SwiftSignalKitMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSignalKitMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; };
D01E2BDD1D9049620066BF65 /* GridNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNode.swift; sourceTree = "<group>"; }; D01E2BDD1D9049620066BF65 /* GridNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNode.swift; sourceTree = "<group>"; };
D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNodeScroller.swift; sourceTree = "<group>"; }; D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNodeScroller.swift; sourceTree = "<group>"; };
D01E2BE11D9049F60066BF65 /* GridItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridItemNode.swift; sourceTree = "<group>"; }; D01E2BE11D9049F60066BF65 /* GridItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridItemNode.swift; sourceTree = "<group>"; };
@ -462,6 +464,7 @@
D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */, D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */,
D04554A921BDB93E007A6DD9 /* CollectionIndexNode.swift */, D04554A921BDB93E007A6DD9 /* CollectionIndexNode.swift */,
09DD88EA21BCA5E0000766BC /* EditableTextNode.swift */, 09DD88EA21BCA5E0000766BC /* EditableTextNode.swift */,
D01E1F0122B28D9400AD6DAE /* WallpaperBackgroundNode.swift */,
); );
name = Nodes; name = Nodes;
sourceTree = "<group>"; sourceTree = "<group>";
@ -939,6 +942,7 @@
D033874E223D3E86007A2CE4 /* AccessibilityAreaNode.swift in Sources */, D033874E223D3E86007A2CE4 /* AccessibilityAreaNode.swift in Sources */,
D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */, D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */,
D01847661FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */, D01847661FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */,
D01E1F0222B28D9400AD6DAE /* WallpaperBackgroundNode.swift in Sources */,
D03725C11D6DF594007FC290 /* ContextMenuNode.swift in Sources */, D03725C11D6DF594007FC290 /* ContextMenuNode.swift in Sources */,
D03AA4DB202DA6D60056C405 /* PeekControllerContent.swift in Sources */, D03AA4DB202DA6D60056C405 /* PeekControllerContent.swift in Sources */,
D053CB611D22B4F200DD41DF /* CATracingLayer.m in Sources */, D053CB611D22B4F200DD41DF /* CATracingLayer.m in Sources */,

View File

@ -6,7 +6,7 @@
}, },
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "DetailLogoBlank@2x.png", "filename" : "EmptyChat@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -47,7 +47,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
self.theme = theme self.theme = theme
self.openUrl = openUrl 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() self.stateDisposable = (account.postbox.stateView()
|> map { view -> InnerState in |> map { view -> InnerState in

View File

@ -227,6 +227,10 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
var purposefulAction: (() -> Void)? 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)) { public init(context: AccountContext, chatLocation: ChatLocation, messageId: MessageId? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false)) {
let _ = ChatControllerCount.modify { value in let _ = ChatControllerCount.modify { value in
return value + 1 return value + 1
@ -4164,6 +4168,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState { if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState {
controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState
self.updateItemNodesSelectionStates(animated: transition.isAnimated) 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() self.chatDisplayNode.historyNode.scrollToEndOfHistory()
} }
public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { 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) self.navigateToMessage(from: nil, to: messageLocation, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, animated: animated, completion: completion, customPresentProgress: customPresentProgress)
} }

View File

@ -6,69 +6,6 @@ import Display
import SwiftSignalKit import SwiftSignalKit
import Postbox 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 backgroundImageForWallpaper: (TelegramWallpaper, Bool, UIImage)?
private var serviceBackgroundColorForWallpaper: (TelegramWallpaper, UIColor)? private var serviceBackgroundColorForWallpaper: (TelegramWallpaper, UIColor)?

View File

@ -69,7 +69,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
} }
let backgroundNode: ChatBackgroundNode let backgroundNode: WallpaperbackgroundNode
let historyNode: ChatHistoryListNode let historyNode: ChatHistoryListNode
let historyNodeContainer: ASDisplayNode let historyNodeContainer: ASDisplayNode
let loadingNode: ChatLoadingNode let loadingNode: ChatLoadingNode
@ -191,7 +191,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.navigationBar = navigationBar self.navigationBar = navigationBar
self.controller = controller self.controller = controller
self.backgroundNode = ChatBackgroundNode() self.backgroundNode = WallpaperbackgroundNode()
self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displaysAsynchronously = false
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer() self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()

View File

@ -94,6 +94,12 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
private var searchContentNode: NavigationBarSearchContentNode? 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) { public init(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool = false) {
self.context = context self.context = context
self.controlsHistoryPreload = controlsHistoryPreload self.controlsHistoryPreload = controlsHistoryPreload
@ -684,9 +690,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
scrollToEndIfExists = true scrollToEndIfExists = true
} }
let animated: Bool = !scrollToEndIfExists || strongSelf.groupId != PeerGroupId.root navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] in
navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), scrollToEndIfExists: animated, animated: animated, parentGroupId: strongSelf.groupId, completion: { [weak self] in
self?.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) self?.chatListDisplayNode.chatListNode.clearHighlightAnimated(true)
}) })
} }
@ -726,9 +730,16 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
|> deliverOnMainQueue).start(next: { [weak strongSelf] actualPeerId in |> deliverOnMainQueue).start(next: { [weak strongSelf] actualPeerId in
if let strongSelf = strongSelf { if let strongSelf = strongSelf {
if let navigationController = strongSelf.navigationController as? NavigationController { 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: { navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeerId), messageId: messageId, purposefulAction: {
self?.deactivateSearch(animated: false) self?.deactivateSearch(animated: false)
}) }, scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [])
strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true)
} }
} }
@ -748,12 +759,18 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
strongSelf.openMessageFromSearchDisposable.set((storedPeer |> deliverOnMainQueue).start(completed: { [weak strongSelf] in strongSelf.openMessageFromSearchDisposable.set((storedPeer |> deliverOnMainQueue).start(completed: { [weak strongSelf] in
if let strongSelf = strongSelf { if let strongSelf = strongSelf {
if dismissSearch { 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 { if let navigationController = strongSelf.navigationController as? NavigationController {
navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in
self?.deactivateSearch(animated: false) self?.deactivateSearch(animated: false)
}) }, scrollToEndIfExists: scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [])
strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true) strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true)
} }
} }
@ -1095,10 +1112,6 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
override public func navigationStackConfigurationUpdated(next: [ViewController]) { override public func navigationStackConfigurationUpdated(next: [ViewController]) {
super.navigationStackConfigurationUpdated(next: next) super.navigationStackConfigurationUpdated(next: next)
let chatLocation = (next.first as? ChatController)?.chatLocation
self.chatListDisplayNode.chatListNode.updateSelectedChatLocation(chatLocation, progress: 1.0, transition: .immediate)
} }
@objc func editPressed() { @objc func editPressed() {
@ -1110,6 +1123,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
self.navigationItem.rightBarButtonItem = editItem self.navigationItem.rightBarButtonItem = editItem
} }
self.searchContentNode?.setIsEnabled(false, animated: true) 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 self.chatListDisplayNode.chatListNode.updateState { state in
var state = state var state = state
state.editing = true state.editing = true
@ -1126,6 +1140,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
} else { } else {
self.navigationItem.rightBarButtonItem = editItem self.navigationItem.rightBarButtonItem = editItem
} }
(self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(nil, transition: .animated(duration: 0.4, curve: .spring))
self.searchContentNode?.setIsEnabled(true, animated: true) self.searchContentNode?.setIsEnabled(true, animated: true)
self.chatListDisplayNode.chatListNode.updateState { state in self.chatListDisplayNode.chatListNode.updateState { state in
var state = state var state = state

View File

@ -504,7 +504,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.updateIsHighlighted(transition: (animated && !highlighted) ? .animated(duration: 0.3, curve: .easeInOut) : .immediate) self.updateIsHighlighted(transition: (animated && !highlighted) ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
} }
func updateIsHighlighted(transition: ContainedViewLayoutTransition) { var reallyHighlighted: Bool {
var reallyHighlighted = self.isHighlighted var reallyHighlighted = self.isHighlighted
if let item = self.item { if let item = self.item {
if let itemChatLocation = item.content.chatLocation { if let itemChatLocation = item.content.chatLocation {
@ -513,6 +513,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} }
} }
} }
return reallyHighlighted
}
func updateIsHighlighted(transition: ContainedViewLayoutTransition) {
if reallyHighlighted { if reallyHighlighted {
if self.highlightedBackgroundNode.supernode == nil { if self.highlightedBackgroundNode.supernode == nil {
@ -1193,7 +1198,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
transition.updateFrame(node: strongSelf.onlineNode, frame: onlineFrame) transition.updateFrame(node: strongSelf.onlineNode, frame: onlineFrame)
let onlineIcon: UIImage? let onlineIcon: UIImage?
if strongSelf.isHighlighted { if strongSelf.reallyHighlighted {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted) onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted)
} else if item.index.pinningIndex != nil { } else if item.index.pinningIndex != nil {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned) onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned)

View File

@ -57,6 +57,6 @@ extension PeekControllerTheme {
public extension NavigationControllerTheme { public extension NavigationControllerTheme {
convenience init(presentationTheme: PresentationTheme) { 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)
} }
} }

View File

@ -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 { private final class ContactListNodeInteraction {
let activateSearch: () -> Void fileprivate let activateSearch: () -> Void
let openSortMenu: () -> Void fileprivate let openSortMenu: () -> Void
let authorize: () -> Void fileprivate let authorize: () -> Void
let suppressWarning: () -> Void fileprivate let suppressWarning: () -> Void
let openPeer: (ContactListPeer) -> 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) { init(activateSearch: @escaping () -> Void, openSortMenu: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer) -> Void) {
self.activateSearch = activateSearch self.activateSearch = activateSearch
@ -191,6 +200,8 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
} }
} }
func item(account: Account, interaction: ContactListNodeInteraction) -> ListViewItem { func item(account: Account, interaction: ContactListNodeInteraction) -> ListViewItem {
switch self { switch self {
case let .search(theme, strings): 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 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) interaction.openPeer(peer)
}) }, itemHighlighting: interaction.itemHighlighting)
} }
} }
@ -775,6 +786,7 @@ final class ContactListNode: ASDisplayNode {
var selectionState: ContactListNodeGroupSelectionState? { var selectionState: ContactListNodeGroupSelectionState? {
return self.selectionStateValue return self.selectionStateValue
} }
private var interaction: ContactListNodeInteraction?
private var enableUpdatesValue = false private var enableUpdatesValue = false
var enableUpdates: Bool { var enableUpdates: Bool {
@ -929,6 +941,8 @@ final class ContactListNode: ASDisplayNode {
} }
} }
self.interaction = interaction
let context = self.context let context = self.context
var firstTime: Int32 = 1 var firstTime: Int32 = 1
let selectionStateSignal = self.selectionStatePromise.get() let selectionStateSignal = self.selectionStatePromise.get()
@ -1318,6 +1332,17 @@ final class ContactListNode: ASDisplayNode {
self.enableUpdates = true 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 { deinit {
self.disposable.dispose() self.disposable.dispose()
self.presentationDataDisposable?.dispose() self.presentationDataDisposable?.dispose()

View File

@ -56,6 +56,7 @@ public class ContactsController: ViewController {
private var contactsNode: ContactsControllerNode { private var contactsNode: ContactsControllerNode {
return self.displayNode as! ContactsControllerNode return self.displayNode as! ContactsControllerNode
} }
private var validLayout: ContainerViewLayout?
private let index: PeerNameIndex = .lastNameFirst private let index: PeerNameIndex = .lastNameFirst
@ -73,6 +74,12 @@ public class ContactsController: ViewController {
var switchToChatsController: (() -> Void)? 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) { public init(context: AccountContext) {
self.context = context self.context = context
@ -195,12 +202,19 @@ public class ContactsController: ViewController {
switch peer { switch peer {
case let .peer(peer, _, _): case let .peer(peer, _, _):
if let navigationController = strongSelf.navigationController as? NavigationController { 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 navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in
if fromSearch { if fromSearch {
self?.deactivateSearch(animated: false) self?.deactivateSearch(animated: false)
self?.switchToChatsController?() self?.switchToChatsController?()
} }
}, completion: { [weak self] in }, scrollToEndIfExists: scrollToEndIfExists, options: [.removeOnMasterDetails], completion: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
} }

View File

@ -125,6 +125,7 @@ class ContactsPeerItem: ListViewItem {
let action: (ContactsPeerItemPeer) -> Void let action: (ContactsPeerItemPeer) -> Void
let setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? let setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)?
let deletePeer: ((PeerId) -> Void)? let deletePeer: ((PeerId) -> Void)?
let itemHighlighting: ContactItemHighlighting?
let selectable: Bool let selectable: Bool
@ -132,7 +133,7 @@ class ContactsPeerItem: ListViewItem {
let header: ListViewItemHeader? 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.theme = theme
self.strings = strings self.strings = strings
self.sortOrder = sortOrder self.sortOrder = sortOrder
@ -151,7 +152,7 @@ class ContactsPeerItem: ListViewItem {
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
self.deletePeer = deletePeer self.deletePeer = deletePeer
self.header = header self.header = header
self.itemHighlighting = itemHighlighting
self.selectable = self.enabled self.selectable = self.enabled
if let index = index { if let index = index {
@ -298,6 +299,9 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
private var avatarState: (Account, Peer?)? private var avatarState: (Account, Peer?)?
private var isHighlighted: Bool = false
private var peerPresenceManager: PeerPresenceStatusManager? private var peerPresenceManager: PeerPresenceStatusManager?
private var layoutParams: (ContactsPeerItem, ListViewItemLayoutParams, Bool, Bool, Bool)? private var layoutParams: (ContactsPeerItem, ListViewItemLayoutParams, Bool, Bool, Bool)?
var chatPeer: Peer? { var chatPeer: Peer? {
@ -365,25 +369,65 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
super.setHighlighted(highlighted, at: point, animated: animated) super.setHighlighted(highlighted, at: point, animated: animated)
if highlighted && self.selectionNode == nil { self.isHighlighted = highlighted
self.highlightedBackgroundNode.alpha = 1.0
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 { if self.highlightedBackgroundNode.supernode == nil {
self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: self.separatorNode) 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 { } else {
if self.highlightedBackgroundNode.supernode != nil { if self.highlightedBackgroundNode.supernode != nil {
if animated { transition.updateAlpha(layer: self.highlightedBackgroundNode.layer, alpha: 0.0, completion: { [weak self] completed in
self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in if let strongSelf = self {
if let strongSelf = self { if completed {
if completed { strongSelf.highlightedBackgroundNode.removeFromSupernode()
strongSelf.highlightedBackgroundNode.removeFromSupernode()
}
} }
}) }
self.highlightedBackgroundNode.alpha = 0.0 })
} else {
self.highlightedBackgroundNode.removeFromSupernode()
}
} }
} }
} }

View File

@ -10,7 +10,7 @@ public enum NavigateToChatKeepStack {
case never 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 found = false
var isFirst = true var isFirst = true
for controller in navigationController.viewControllers.reversed() { for controller in navigationController.viewControllers.reversed() {
@ -72,9 +72,9 @@ public func navigateToChatController(navigationController: NavigationController,
} }
}) })
if viewControllers.isEmpty { if viewControllers.isEmpty {
navigationController.replaceAllButRootController(controller, animated: animated, completion: completion) navigationController.replaceAllButRootController(controller, animated: animated, animationOptions: options, completion: completion)
} else { } else {
navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: animated, completion: completion) navigationController.replaceControllersAndPush(controllers: viewControllers, controller: controller, animated: animated, options: options, completion: completion)
} }
} }
} }

View File

@ -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()) let controller = SettingsControllerImpl(currentContext: context, contextValue: contextValue, state: signal, tabBarItem: tabBarItem, accountsAndPeers: accountsAndPeers.get())
pushControllerImpl = { [weak controller] value in 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 presentControllerImpl = { [weak controller] value, arguments in
controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true) controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true)

View File

@ -26,16 +26,44 @@ public final class TelegramRootController: NavigationController {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } 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 self.presentationDataDisposable = (context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { 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 let previousTheme = strongSelf.presentationData.theme
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme { if previousTheme !== presentationData.theme {
strongSelf.rootTabController?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme)) strongSelf.rootTabController?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme))
strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBar.style.style strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBar.style.style
} }
} }
}) })