User info screen design update

This commit is contained in:
Ali 2020-02-05 01:38:55 +00:00
parent 46ffb4e125
commit 6758002ade
49 changed files with 1543 additions and 439 deletions

View File

@ -440,7 +440,7 @@ public protocol SharedAccountContext: class {
func openChatMessage(_ params: OpenChatMessageParams) -> Bool func openChatMessage(_ params: OpenChatMessageParams) -> Bool
func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, account: Account, chatLocation: ChatLocation, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError> func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, account: Account, chatLocation: ChatLocation, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError>
func makeOverlayAudioPlayerController(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController func makeOverlayAudioPlayerController(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController
func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode) -> ViewController? func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool) -> ViewController?
func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController
func makePeersNearbyController(context: AccountContext) -> ViewController func makePeersNearbyController(context: AccountContext) -> ViewController
func makeComposeController(context: AccountContext) -> ViewController func makeComposeController(context: AccountContext) -> ViewController

View File

@ -149,7 +149,7 @@ public final class CallListController: ViewController {
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId) let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId)
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let strongSelf = self, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .calls(messages: messages)) { if let strongSelf = self, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .calls(messages: messages), avatarInitiallyExpanded: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller) (strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
} }
}) })

View File

@ -95,6 +95,15 @@ public class ChatTitleActivityContentNode: ASDisplayNode {
self.textNode.attributedText = text self.textNode.attributedText = text
} }
func makeCopy() -> ASDisplayNode {
let node = ASDisplayNode()
let textNode = self.textNode.makeCopy()
textNode.frame = self.textNode.frame
node.addSubnode(textNode)
node.frame = self.frame
return node
}
public func animateOut(to: ChatTitleActivityNodeState, style: ChatTitleActivityAnimationStyle, completion: @escaping () -> Void) { public func animateOut(to: ChatTitleActivityNodeState, style: ChatTitleActivityAnimationStyle, completion: @escaping () -> Void) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration, removeOnCompletion: false, completion: { _ in self.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration, removeOnCompletion: false, completion: { _ in
completion() completion()

View File

@ -61,6 +61,15 @@ public class ChatTitleActivityNode: ASDisplayNode {
super.init() super.init()
} }
public func makeCopy() -> ASDisplayNode {
let node = ASDisplayNode()
if let contentNode = self.contentNode {
node.addSubnode(contentNode.makeCopy())
}
node.frame = self.frame
return node
}
public func transitionToState(_ state: ChatTitleActivityNodeState, animation: ChatTitleActivityAnimationStyle = .crossfade, completion: @escaping () -> Void = {}) -> Bool { public func transitionToState(_ state: ChatTitleActivityNodeState, animation: ChatTitleActivityAnimationStyle = .crossfade, completion: @escaping () -> Void = {}) -> Bool {
if self.state != state { if self.state != state {
let currentState = self.state let currentState = self.state

View File

@ -530,7 +530,7 @@ public class ContactsController: ViewController {
return return
} }
if let peer = peer { if let peer = peer {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
} }
} else { } else {

View File

@ -147,14 +147,17 @@ public extension ContainedViewLayoutTransition {
} else { } else {
switch self { switch self {
case .immediate: case .immediate:
node.frame = frame node.position = frame.center
node.bounds = CGRect(origin: node.bounds.origin, size: frame.size)
if let completion = completion { if let completion = completion {
completion(true) completion(true)
} }
case .animated: case let .animated(duration, curve):
let previousFrame = node.frame let previousBounds = node.bounds
node.frame = frame let previousCenter = node.frame.center
self.animatePositionAdditive(node: node, offset: CGPoint(x: previousFrame.midX - frame.midX, y: previousFrame.midY - frame.midY)) node.position = frame.center
node.bounds = CGRect(origin: node.bounds.origin, size: frame.size)
self.animatePositionAdditive(node: node, offset: CGPoint(x: previousCenter.x - frame.midX, y: previousCenter.y - frame.midY))
} }
} }
} }
@ -655,6 +658,40 @@ public extension ContainedViewLayoutTransition {
} }
} }
func updateSublayerTransformScaleAdditive(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
completion?(true)
return
}
let t = node.layer.sublayerTransform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: scale) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let t = node.layer.sublayerTransform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
node.layer.animate(from: -(scale - currentScale) as NSNumber, to: 0.0 as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: true, completion: {
result in
if let completion = completion {
completion(result)
}
})
}
}
func updateSublayerTransformScaleAndOffset(node: ASDisplayNode, scale: CGFloat, offset: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) { func updateSublayerTransformScaleAndOffset(node: ASDisplayNode, scale: CGFloat, offset: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded { if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0) node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)

View File

@ -34,6 +34,13 @@ public class ImmediateTextNode: TextNode {
public var tapAttributeAction: (([NSAttributedString.Key: Any]) -> Void)? public var tapAttributeAction: (([NSAttributedString.Key: Any]) -> Void)?
public var longTapAttributeAction: (([NSAttributedString.Key: Any]) -> Void)? public var longTapAttributeAction: (([NSAttributedString.Key: Any]) -> Void)?
public func makeCopy() -> TextNode {
let node = TextNode()
node.cachedLayout = self.cachedLayout
node.frame = self.frame
return node
}
public func updateLayout(_ constrainedSize: CGSize) -> CGSize { public func updateLayout(_ constrainedSize: CGSize) -> CGSize {
let makeLayout = TextNode.asyncLayout(self) let makeLayout = TextNode.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: nil, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke)) let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: nil, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke))

View File

@ -111,6 +111,9 @@ open class NavigationBar: ASDisplayNode {
public var backPressed: () -> () = { } public var backPressed: () -> () = { }
public var userInfo: Any?
public var makeCustomTransitionNode: ((NavigationBar) -> CustomNavigationTransitionNode?)?
private var collapsed: Bool { private var collapsed: Bool {
get { get {
return self.frame.size.height.isLess(than: 44.0) return self.frame.size.height.isLess(than: 44.0)
@ -243,6 +246,8 @@ open class NavigationBar: ASDisplayNode {
} }
} }
public var customBackButtonText: String?
private var title: String? { private var title: String? {
didSet { didSet {
if let title = self.title { if let title = self.title {
@ -261,7 +266,7 @@ open class NavigationBar: ASDisplayNode {
} }
} }
private var titleView: UIView? { public private(set) var titleView: UIView? {
didSet { didSet {
if let oldValue = oldValue { if let oldValue = oldValue {
oldValue.removeFromSuperview() oldValue.removeFromSuperview()
@ -377,7 +382,9 @@ open class NavigationBar: ASDisplayNode {
case let .item(itemValue): case let .item(itemValue):
self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in
if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem {
if let backBarButtonItem = itemValue.backBarButtonItem { if let customBackButtonText = strongSelf.customBackButtonText {
strongSelf.backButtonNode.updateManualText(customBackButtonText)
} else if let backBarButtonItem = itemValue.backBarButtonItem {
strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "") strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "")
} else { } else {
strongSelf.backButtonNode.updateManualText(itemValue.title ?? "") strongSelf.backButtonNode.updateManualText(itemValue.title ?? "")
@ -389,7 +396,9 @@ open class NavigationBar: ASDisplayNode {
self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in
if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem {
if let backBarButtonItem = itemValue.backBarButtonItem { if let customBackButtonText = strongSelf.customBackButtonText {
strongSelf.backButtonNode.updateManualText(customBackButtonText)
} else if let backBarButtonItem = itemValue.backBarButtonItem {
strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "") strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "")
} else { } else {
strongSelf.backButtonNode.updateManualText(itemValue.title ?? "") strongSelf.backButtonNode.updateManualText(itemValue.title ?? "")
@ -505,7 +514,9 @@ open class NavigationBar: ASDisplayNode {
self.leftButtonNode.removeFromSupernode() self.leftButtonNode.removeFromSupernode()
var backTitle: String? var backTitle: String?
if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { if let customBackButtonText = self.customBackButtonText {
backTitle = customBackButtonText
} else if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance {
backTitle = leftBarButtonItem.title backTitle = leftBarButtonItem.title
} else if let previousItem = self.previousItem { } else if let previousItem = self.previousItem {
switch previousItem { switch previousItem {
@ -589,12 +600,11 @@ open class NavigationBar: ASDisplayNode {
self.updateAccessibilityElements() self.updateAccessibilityElements()
} }
private let backButtonNode: NavigationButtonNode public let backButtonNode: NavigationButtonNode
private let badgeNode: NavigationBarBadgeNode public let badgeNode: NavigationBarBadgeNode
private let backButtonArrow: ASImageNode public let backButtonArrow: ASImageNode
private let leftButtonNode: NavigationButtonNode public let leftButtonNode: NavigationButtonNode
private let rightButtonNode: NavigationButtonNode public let rightButtonNode: NavigationButtonNode
private var _transitionState: NavigationBarTransitionState? private var _transitionState: NavigationBarTransitionState?
var transitionState: NavigationBarTransitionState? { var transitionState: NavigationBarTransitionState? {
@ -694,6 +704,7 @@ open class NavigationBar: ASDisplayNode {
self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor
self.rightButtonNode.color = self.presentationData.theme.buttonColor self.rightButtonNode.color = self.presentationData.theme.buttonColor
self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor
self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05)
self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor) self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor)
if let title = self.title { if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.presentationData.theme.primaryTextColor) self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.presentationData.theme.primaryTextColor)
@ -768,6 +779,7 @@ open class NavigationBar: ASDisplayNode {
self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor
self.rightButtonNode.color = self.presentationData.theme.buttonColor self.rightButtonNode.color = self.presentationData.theme.buttonColor
self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor
self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05)
self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor) self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor)
if let title = self.title { if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.presentationData.theme.primaryTextColor) self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.presentationData.theme.primaryTextColor)
@ -821,7 +833,7 @@ open class NavigationBar: ASDisplayNode {
transition.updateFrame(node: self.stripeNode, frame: CGRect(x: 0.0, y: size.height, width: size.width, height: UIScreenPixel)) transition.updateFrame(node: self.stripeNode, frame: CGRect(x: 0.0, y: size.height, width: size.width, height: UIScreenPixel))
let nominalHeight: CGFloat = self.collapsed ? 32.0 : defaultHeight let nominalHeight: CGFloat = defaultHeight
let contentVerticalOrigin = size.height - nominalHeight - expansionHeight let contentVerticalOrigin = size.height - nominalHeight - expansionHeight
var leftTitleInset: CGFloat = leftInset + 1.0 var leftTitleInset: CGFloat = leftInset + 1.0
@ -958,7 +970,7 @@ open class NavigationBar: ASDisplayNode {
if let titleView = self.titleView { if let titleView = self.titleView {
let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight) let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)
let titleFrame = CGRect(origin: CGPoint(x: leftTitleInset, y: contentVerticalOrigin), size: titleSize) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
titleView.frame = titleFrame titleView.frame = titleFrame
if let titleView = titleView as? NavigationBarTitleView { if let titleView = titleView as? NavigationBarTitleView {
@ -996,7 +1008,7 @@ open class NavigationBar: ASDisplayNode {
} }
} }
titleView.alpha = 1.0 titleView.alpha = 1.0
titleView.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) titleView.frame = titleFrame
} }
} }
} }
@ -1017,18 +1029,45 @@ open class NavigationBar: ASDisplayNode {
} }
} }
private func makeTransitionBackButtonNode(accentColor: UIColor) -> NavigationButtonNode? { public func makeTransitionBackButtonNode(accentColor: UIColor) -> NavigationButtonNode? {
if self.backButtonNode.supernode != nil { if self.backButtonNode.supernode != nil {
let node = NavigationButtonNode() let node = NavigationButtonNode()
node.updateManualText(self.backButtonNode.manualText) node.updateManualText(self.backButtonNode.manualText)
node.color = accentColor node.color = accentColor
if let (size, defaultHeight, _, _) = self.validLayout {
node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight))
node.frame = self.backButtonNode.frame
}
return node return node
} else { } else {
return nil return nil
} }
} }
private func makeTransitionBackArrowNode(accentColor: UIColor) -> ASDisplayNode? { public func makeTransitionRightButtonNode(accentColor: UIColor) -> NavigationButtonNode? {
if self.rightButtonNode.supernode != nil {
let node = NavigationButtonNode()
var items: [UIBarButtonItem] = []
if let item = self.item {
if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty {
items = rightBarButtonItems
} else if let rightBarButtonItem = item.rightBarButtonItem {
items = [rightBarButtonItem]
}
}
node.updateItems(items)
node.color = accentColor
if let (size, defaultHeight, _, _) = self.validLayout {
node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight))
node.frame = self.backButtonNode.frame
}
return node
} else {
return nil
}
}
public func makeTransitionBackArrowNode(accentColor: UIColor) -> ASDisplayNode? {
if self.backButtonArrow.supernode != nil { if self.backButtonArrow.supernode != nil {
let node = ASImageNode() let node = ASImageNode()
node.image = backArrowImage(color: accentColor) node.image = backArrowImage(color: accentColor)
@ -1041,7 +1080,7 @@ open class NavigationBar: ASDisplayNode {
} }
} }
private func makeTransitionBadgeNode() -> ASDisplayNode? { public func makeTransitionBadgeNode() -> ASDisplayNode? {
if self.badgeNode.supernode != nil && !self.badgeNode.isHidden { if self.badgeNode.supernode != nil && !self.badgeNode.isHidden {
let node = NavigationBarBadgeNode(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor) let node = NavigationBarBadgeNode(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor)
node.text = self.badgeNode.text node.text = self.badgeNode.text

View File

@ -2,7 +2,7 @@ import Foundation
import UIKit import UIKit
import AsyncDisplayKit import AsyncDisplayKit
final class NavigationBarBadgeNode: ASDisplayNode { public final class NavigationBarBadgeNode: ASDisplayNode {
private var fillColor: UIColor private var fillColor: UIColor
private var strokeColor: UIColor private var strokeColor: UIColor
private var textColor: UIColor private var textColor: UIColor
@ -19,7 +19,7 @@ final class NavigationBarBadgeNode: ASDisplayNode {
} }
} }
init(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) { public init(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) {
self.fillColor = fillColor self.fillColor = fillColor
self.strokeColor = strokeColor self.strokeColor = strokeColor
self.textColor = textColor self.textColor = textColor
@ -48,7 +48,7 @@ final class NavigationBarBadgeNode: ASDisplayNode {
self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor) self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor)
} }
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
let badgeSize = self.textNode.measure(constrainedSize) let badgeSize = self.textNode.measure(constrainedSize)
let backgroundSize = CGSize(width: max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0) let backgroundSize = CGSize(width: max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0)
let backgroundFrame = CGRect(origin: CGPoint(), size: backgroundSize) let backgroundFrame = CGRect(origin: CGPoint(), size: backgroundSize)

View File

@ -53,6 +53,7 @@ private final class NavigationButtonItemNode: ASTextNode {
} }
private var imageNode: ASImageNode? private var imageNode: ASImageNode?
private let imageRippleNode: ASImageNode
private var _image: UIImage? private var _image: UIImage?
public var image: UIImage? { public var image: UIImage? {
@ -61,18 +62,34 @@ private final class NavigationButtonItemNode: ASTextNode {
} set(value) { } set(value) {
_image = value _image = value
if let _ = value { if let value = value {
if self.imageNode == nil { if self.imageNode == nil {
let imageNode = ASImageNode() let imageNode = ASImageNode()
imageNode.displayWithoutProcessing = true imageNode.displayWithoutProcessing = true
imageNode.displaysAsynchronously = false imageNode.displaysAsynchronously = false
self.imageNode = imageNode self.imageNode = imageNode
if value.size == CGSize(width: 30.0, height: 30.0) {
if self.imageRippleNode.supernode == nil {
self.addSubnode(self.imageRippleNode)
self.imageRippleNode.image = generateFilledCircleImage(diameter: 30.0, color: self.rippleColor)
}
} else {
if self.imageRippleNode.supernode != nil {
self.imageRippleNode.image = nil
self.imageRippleNode.removeFromSupernode()
}
}
self.addSubnode(imageNode) self.addSubnode(imageNode)
} }
self.imageNode?.image = image self.imageNode?.image = image
} else if let imageNode = self.imageNode { } else if let imageNode = self.imageNode {
imageNode.removeFromSupernode() imageNode.removeFromSupernode()
self.imageNode = nil self.imageNode = nil
if self.imageRippleNode.supernode != nil {
self.imageRippleNode.image = nil
self.imageRippleNode.removeFromSupernode()
}
} }
self.invalidateCalculatedLayout() self.invalidateCalculatedLayout()
@ -101,6 +118,14 @@ private final class NavigationButtonItemNode: ASTextNode {
} }
} }
public var rippleColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.05) {
didSet {
if self.imageRippleNode.image != nil {
self.imageRippleNode.image = generateFilledCircleImage(diameter: 30.0, color: self.rippleColor)
}
}
}
public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) { public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) {
didSet { didSet {
if let text = self._text { if let text = self._text {
@ -160,6 +185,11 @@ private final class NavigationButtonItemNode: ASTextNode {
} }
override public init() { override public init() {
self.imageRippleNode = ASImageNode()
self.imageRippleNode.displaysAsynchronously = false
self.imageRippleNode.displayWithoutProcessing = true
self.imageRippleNode.alpha = 0.0
super.init() super.init()
self.isAccessibilityElement = true self.isAccessibilityElement = true
@ -183,7 +213,9 @@ private final class NavigationButtonItemNode: ASTextNode {
} else if let imageNode = self.imageNode { } else if let imageNode = self.imageNode {
let nodeSize = imageNode.image?.size ?? CGSize() let nodeSize = imageNode.image?.size ?? CGSize()
let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(nodeSize.height, superSize.height)) let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(nodeSize.height, superSize.height))
imageNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeSize.width) / 2.0) + 5.0, y: floorToScreenPixels((size.height - nodeSize.height) / 2.0)), size: nodeSize) let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeSize.width) / 2.0) + 5.0, y: floorToScreenPixels((size.height - nodeSize.height) / 2.0)), size: nodeSize)
imageNode.frame = imageFrame
self.imageRippleNode.frame = imageFrame
return size return size
} }
return superSize return superSize
@ -242,7 +274,15 @@ private final class NavigationButtonItemNode: ASTextNode {
} }
if shouldChangeHighlight { if shouldChangeHighlight {
if let imageNode = self.imageNode {
let previousAlpha = self.imageRippleNode.alpha
self.imageRippleNode.alpha = highlighted ? 1.0 : 0.0
if !highlighted {
self.imageRippleNode.layer.animateAlpha(from: previousAlpha, to: self.imageRippleNode.alpha, duration: 0.25)
}
} else {
self.alpha = !self.isEnabled ? 1.0 : (highlighted ? 0.4 : 1.0) self.alpha = !self.isEnabled ? 1.0 : (highlighted ? 0.4 : 1.0)
}
self.highlightChanged(highlighted) self.highlightChanged(highlighted)
} }
} }
@ -263,7 +303,7 @@ private final class NavigationButtonItemNode: ASTextNode {
} }
final class NavigationButtonNode: ASDisplayNode { public final class NavigationButtonNode: ASDisplayNode {
private var nodes: [NavigationButtonItemNode] = [] private var nodes: [NavigationButtonItemNode] = []
public var pressed: (Int) -> () = { _ in } public var pressed: (Int) -> () = { _ in }
@ -279,6 +319,16 @@ final class NavigationButtonNode: ASDisplayNode {
} }
} }
public var rippleColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.05) {
didSet {
if !self.rippleColor.isEqual(oldValue) {
for node in self.nodes {
node.rippleColor = self.rippleColor
}
}
}
}
public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) { public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) {
didSet { didSet {
if !self.disabledColor.isEqual(oldValue) { if !self.disabledColor.isEqual(oldValue) {
@ -296,7 +346,7 @@ final class NavigationButtonNode: ASDisplayNode {
} }
} }
override init() { override public init() {
super.init() super.init()
self.isAccessibilityElement = false self.isAccessibilityElement = false
@ -313,6 +363,7 @@ final class NavigationButtonNode: ASDisplayNode {
} else { } else {
node = NavigationButtonItemNode() node = NavigationButtonItemNode()
node.color = self.color node.color = self.color
node.rippleColor = self.rippleColor
node.highlightChanged = { [weak node, weak self] value in node.highlightChanged = { [weak node, weak self] value in
if let strongSelf = self, let node = node { if let strongSelf = self, let node = node {
if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) {
@ -353,6 +404,7 @@ final class NavigationButtonNode: ASDisplayNode {
} else { } else {
node = NavigationButtonItemNode() node = NavigationButtonItemNode()
node.color = self.color node.color = self.color
node.rippleColor = self.rippleColor
node.highlightChanged = { [weak node, weak self] value in node.highlightChanged = { [weak node, weak self] value in
if let strongSelf = self, let node = node { if let strongSelf = self, let node = node {
if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) {
@ -385,7 +437,7 @@ final class NavigationButtonNode: ASDisplayNode {
} }
} }
func updateLayout(constrainedSize: CGSize) -> CGSize { public func updateLayout(constrainedSize: CGSize) -> CGSize {
var nodeOrigin = CGPoint() var nodeOrigin = CGPoint()
var totalSize = CGSize() var totalSize = CGSize()
for node in self.nodes { for node in self.nodes {

View File

@ -15,12 +15,17 @@ private func generateShadow() -> UIImage? {
context.setShadow(offset: CGSize(), blur: 16.0, color: UIColor(white: 0.0, alpha: 0.5).cgColor) context.setShadow(offset: CGSize(), blur: 16.0, color: UIColor(white: 0.0, alpha: 0.5).cgColor)
context.fill(CGRect(origin: CGPoint(x: size.width, y: 0.0), size: CGSize(width: 16.0, height: 1.0))) context.fill(CGRect(origin: CGPoint(x: size.width, y: 0.0), size: CGSize(width: 16.0, height: 1.0)))
}) })
//return UIImage(named: "NavigationShadow", in: getAppBundle(), compatibleWith: nil)?.precomposed().resizableImage(withCapInsets: UIEdgeInsets(), resizingMode: .tile)
} }
private let shadowImage = generateShadow() private let shadowImage = generateShadow()
class NavigationTransitionCoordinator { public protocol CustomNavigationTransitionNode: ASDisplayNode {
func setup(topNavigationBar: NavigationBar, bottomNavigationBar: NavigationBar)
func update(containerSize: CGSize, fraction: CGFloat, transition: ContainedViewLayoutTransition)
func restore()
}
final class NavigationTransitionCoordinator {
private var _progress: CGFloat = 0.0 private var _progress: CGFloat = 0.0
var progress: CGFloat { var progress: CGFloat {
get { get {
@ -36,6 +41,7 @@ class NavigationTransitionCoordinator {
private let bottomNavigationBar: NavigationBar? private let bottomNavigationBar: NavigationBar?
private let dimNode: ASDisplayNode private let dimNode: ASDisplayNode
private let shadowNode: ASImageNode private let shadowNode: ASImageNode
private let customTransitionNode: CustomNavigationTransitionNode?
private let inlineNavigationBarTransition: Bool private let inlineNavigationBarTransition: Bool
@ -58,14 +64,29 @@ class NavigationTransitionCoordinator {
self.shadowNode.displayWithoutProcessing = true self.shadowNode.displayWithoutProcessing = true
self.shadowNode.image = shadowImage self.shadowNode.image = shadowImage
if let topNavigationBar = topNavigationBar, let bottomNavigationBar = bottomNavigationBar, !topNavigationBar.isHidden, !bottomNavigationBar.isHidden, topNavigationBar.canTransitionInline, bottomNavigationBar.canTransitionInline, topNavigationBar.item?.leftBarButtonItem == nil { if let topNavigationBar = topNavigationBar, let bottomNavigationBar = bottomNavigationBar {
if let customTransitionNode = topNavigationBar.makeCustomTransitionNode?(bottomNavigationBar) {
self.inlineNavigationBarTransition = false
customTransitionNode.setup(topNavigationBar: topNavigationBar, bottomNavigationBar: bottomNavigationBar)
self.customTransitionNode = customTransitionNode
} else if let customTransitionNode = bottomNavigationBar.makeCustomTransitionNode?(topNavigationBar) {
self.inlineNavigationBarTransition = false
customTransitionNode.setup(topNavigationBar: topNavigationBar, bottomNavigationBar: bottomNavigationBar)
self.customTransitionNode = customTransitionNode
} else if !topNavigationBar.isHidden, !bottomNavigationBar.isHidden, topNavigationBar.canTransitionInline, bottomNavigationBar.canTransitionInline, topNavigationBar.item?.leftBarButtonItem == nil {
var topFrame = topNavigationBar.view.convert(topNavigationBar.bounds, to: container.view) var topFrame = topNavigationBar.view.convert(topNavigationBar.bounds, to: container.view)
var bottomFrame = bottomNavigationBar.view.convert(bottomNavigationBar.bounds, to: container.view) var bottomFrame = bottomNavigationBar.view.convert(bottomNavigationBar.bounds, to: container.view)
topFrame.origin.x = 0.0 topFrame.origin.x = 0.0
bottomFrame.origin.x = 0.0 bottomFrame.origin.x = 0.0
self.inlineNavigationBarTransition = true// topFrame.equalTo(bottomFrame) self.inlineNavigationBarTransition = true
self.customTransitionNode = nil
} else { } else {
self.inlineNavigationBarTransition = false self.inlineNavigationBarTransition = false
self.customTransitionNode = nil
}
} else {
self.inlineNavigationBarTransition = false
self.customTransitionNode = nil
} }
switch transition { switch transition {
@ -76,7 +97,10 @@ class NavigationTransitionCoordinator {
} }
self.container.insertSubnode(self.dimNode, belowSubnode: topNode) self.container.insertSubnode(self.dimNode, belowSubnode: topNode)
self.container.insertSubnode(self.shadowNode, belowSubnode: dimNode) self.container.insertSubnode(self.shadowNode, belowSubnode: self.dimNode)
if let customTransitionNode = self.customTransitionNode {
self.container.addSubnode(customTransitionNode)
}
self.maybeCreateNavigationBarTransition() self.maybeCreateNavigationBarTransition()
self.updateProgress(0.0, transition: .immediate, completion: {}) self.updateProgress(0.0, transition: .immediate, completion: {})
@ -119,10 +143,15 @@ class NavigationTransitionCoordinator {
self.updateNavigationBarTransition(transition: transition) self.updateNavigationBarTransition(transition: transition)
if let customTransitionNode = self.customTransitionNode {
customTransitionNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerSize.width, height: containerSize.height))
customTransitionNode.update(containerSize: containerSize, fraction: position, transition: transition)
}
self.didUpdateProgress?(self.progress, transition, topFrame, bottomFrame) self.didUpdateProgress?(self.progress, transition, topFrame, bottomFrame)
} }
func updateNavigationBarTransition(transition: ContainedViewLayoutTransition) { private func updateNavigationBarTransition(transition: ContainedViewLayoutTransition) {
if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar, self.inlineNavigationBarTransition { if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar, self.inlineNavigationBarTransition {
let position: CGFloat let position: CGFloat
switch self.transition { switch self.transition {
@ -178,6 +207,9 @@ class NavigationTransitionCoordinator {
strongSelf.dimNode.removeFromSupernode() strongSelf.dimNode.removeFromSupernode()
strongSelf.shadowNode.removeFromSupernode() strongSelf.shadowNode.removeFromSupernode()
strongSelf.customTransitionNode?.restore()
strongSelf.customTransitionNode?.removeFromSupernode()
strongSelf.endNavigationBarTransition() strongSelf.endNavigationBarTransition()
if let currentCompletion = strongSelf.currentCompletion { if let currentCompletion = strongSelf.currentCompletion {
@ -195,6 +227,9 @@ class NavigationTransitionCoordinator {
self.dimNode.removeFromSupernode() self.dimNode.removeFromSupernode()
self.shadowNode.removeFromSupernode() self.shadowNode.removeFromSupernode()
self.customTransitionNode?.restore()
self.customTransitionNode?.removeFromSupernode()
self.endNavigationBarTransition() self.endNavigationBarTransition()
if let currentCompletion = self.currentCompletion { if let currentCompletion = self.currentCompletion {
@ -209,6 +244,9 @@ class NavigationTransitionCoordinator {
strongSelf.dimNode.removeFromSupernode() strongSelf.dimNode.removeFromSupernode()
strongSelf.shadowNode.removeFromSupernode() strongSelf.shadowNode.removeFromSupernode()
strongSelf.customTransitionNode?.restore()
strongSelf.customTransitionNode?.removeFromSupernode()
strongSelf.endNavigationBarTransition() strongSelf.endNavigationBarTransition()
if let currentCompletion = strongSelf.currentCompletion { if let currentCompletion = strongSelf.currentCompletion {
@ -228,6 +266,9 @@ class NavigationTransitionCoordinator {
self.dimNode.removeFromSupernode() self.dimNode.removeFromSupernode()
self.shadowNode.removeFromSupernode() self.shadowNode.removeFromSupernode()
self.customTransitionNode?.restore()
self.customTransitionNode?.removeFromSupernode()
self.endNavigationBarTransition() self.endNavigationBarTransition()
if let currentCompletion = self.currentCompletion { if let currentCompletion = self.currentCompletion {

View File

@ -771,7 +771,7 @@ public final class TextAccessibilityOverlayNode: ASDisplayNode {
} }
public class TextNode: ASDisplayNode { public class TextNode: ASDisplayNode {
public private(set) var cachedLayout: TextNodeLayout? public internal(set) var cachedLayout: TextNodeLayout?
override public init() { override public init() {
super.init() super.init()

View File

@ -1180,7 +1180,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId) let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let strongSelf = self { if let strongSelf = self {
if let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
strongSelf.getNavigationController()?.pushViewController(controller) strongSelf.getNavigationController()?.pushViewController(controller)
} }
} }

View File

@ -330,7 +330,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
} }
}) })

View File

@ -61,7 +61,7 @@ public final class AvatarGalleryControllerPresentationArguments {
} }
} }
private func initialAvatarGalleryEntries(peer: Peer) -> [AvatarGalleryEntry]{ private func initialAvatarGalleryEntries(peer: Peer) -> [AvatarGalleryEntry] {
var initialEntries: [AvatarGalleryEntry] = [] var initialEntries: [AvatarGalleryEntry] = []
if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) { if !peer.profileImageRepresentations.isEmpty, let peerReference = PeerReference(peer) {
initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), nil)) initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), nil))
@ -70,7 +70,10 @@ private func initialAvatarGalleryEntries(peer: Peer) -> [AvatarGalleryEntry]{
} }
public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> { public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<[AvatarGalleryEntry], NoError> {
return requestPeerPhotos(account: account, peerId: peer.id) let initialEntries = initialAvatarGalleryEntries(peer: peer)
return Signal<[AvatarGalleryEntry], NoError>.single(initialEntries)
|> then(
requestPeerPhotos(account: account, peerId: peer.id)
|> map { photos -> [AvatarGalleryEntry] in |> map { photos -> [AvatarGalleryEntry] in
var result: [AvatarGalleryEntry] = [] var result: [AvatarGalleryEntry] = []
let initialEntries = initialAvatarGalleryEntries(peer: peer) let initialEntries = initialAvatarGalleryEntries(peer: peer)
@ -90,6 +93,7 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
} }
return result return result
} }
)
} }
public class AvatarGalleryController: ViewController, StandalonePresentableController { public class AvatarGalleryController: ViewController, StandalonePresentableController {

View File

@ -366,7 +366,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
} }
items.append(ActionSheetButtonItem(title: presentationData.strings.GroupRemoved_ViewUserInfo, action: { [weak actionSheet] in items.append(ActionSheetButtonItem(title: presentationData.strings.GroupRemoved_ViewUserInfo, action: { [weak actionSheet] in
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: participant.peer, mode: .generic) { if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: participant.peer, mode: .generic, avatarInitiallyExpanded: false) {
pushControllerImpl?(infoController) pushControllerImpl?(infoController)
} }
})) }))

View File

@ -450,7 +450,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
} }
})) }))
}, openPeer: { peer in }, openPeer: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
}, inviteViaLink: { }, inviteViaLink: {
@ -502,7 +502,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
return state.withUpdatedSearchingMembers(false) return state.withUpdatedSearchingMembers(false)
} }
}, openPeer: { peer, _ in }, openPeer: { peer, _ in
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
pushControllerImpl?(infoController) pushControllerImpl?(infoController)
} }
}, pushController: { c in }, pushController: { c in

View File

@ -666,7 +666,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}) })
}, openPeerInfo: { peer in }, openPeerInfo: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
}, openKicked: { }, openKicked: {

View File

@ -599,7 +599,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
})) }))
} }
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!, .standard), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, selectable: selectable, sectionId: self.section, action: { return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!, .standard), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, selectable: selectable, sectionId: self.section, action: {
if let infoController = arguments.context.sharedContext.makePeerInfoController(context: arguments.context, peer: peer, mode: .generic), selectable { if let infoController = arguments.context.sharedContext.makePeerInfoController(context: arguments.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false), selectable {
arguments.pushController(infoController) arguments.pushController(infoController)
} }
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
@ -2342,7 +2342,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
return state.withUpdatedSearchingMembers(false) return state.withUpdatedSearchingMembers(false)
} }
}, openPeer: { peer, _ in }, openPeer: { peer, _ in
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
arguments.pushController(infoController) arguments.pushController(infoController)
} }
}, pushController: { c in }, pushController: { c in

View File

@ -259,7 +259,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
} }
})) }))
}, openPeer: { peer in }, openPeer: { peer in
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
}) })

View File

@ -112,7 +112,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
private var disabledOverlayNode: ASDisplayNode? private var disabledOverlayNode: ASDisplayNode?
private let maskNode: ASImageNode private let maskNode: ASImageNode
private let avatarNode: AvatarNode let avatarNode: AvatarNode
private let titleNode: TextNode private let titleNode: TextNode
private let appNode: TextNode private let appNode: TextNode
private let locationNode: TextNode private let locationNode: TextNode

View File

@ -341,7 +341,7 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
return transaction.getPeer(peerId) return transaction.getPeer(peerId)
} }
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) else { guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) else {
return return
} }
pushControllerImpl?(controller) pushControllerImpl?(controller)

View File

@ -20,6 +20,7 @@ public enum PresentationResourceKey: Int32 {
case navigationShareIcon case navigationShareIcon
case navigationSearchIcon case navigationSearchIcon
case navigationCompactSearchIcon case navigationCompactSearchIcon
case navigationMoreIcon
case navigationAddIcon case navigationAddIcon
case navigationPlayerCloseButton case navigationPlayerCloseButton

View File

@ -71,6 +71,19 @@ public struct PresentationResourcesRootController {
}) })
} }
public static func navigationMoreIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.navigationMoreIcon.rawValue, { theme in
return generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(theme.rootController.navigationBar.accentTextColor.cgColor)
let dotSize: CGFloat = 4.0
context.fillEllipse(in: CGRect(origin: CGPoint(x: 6.0, y: floor((size.height - dotSize) / 2.0)), size: CGSize(width: dotSize, height: dotSize)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: 13.0, y: floor((size.height - dotSize) / 2.0)), size: CGSize(width: dotSize, height: dotSize)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: 20.0, y: floor((size.height - dotSize) / 2.0)), size: CGSize(width: dotSize, height: dotSize)))
})
})
}
public static func navigationAddIcon(_ theme: PresentationTheme) -> UIImage? { public static func navigationAddIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.navigationAddIcon.rawValue, { theme in return theme.image(PresentationResourceKey.navigationAddIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: theme.rootController.navigationBar.accentTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: theme.rootController.navigationBar.accentTextColor)

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_pf_addmember.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_pf_call.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -2,15 +2,7 @@
"images" : [ "images" : [
{ {
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "filename" : "ic_pf_message.pdf"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_pf_more.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_pf_mute.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_pf_unmute.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -48,6 +48,8 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
} }
} }
var tapped: (() -> Void)?
override init() { override init() {
self.containerNode = ContextControllerSourceNode() self.containerNode = ContextControllerSourceNode()
self.avatarNode = AvatarNode(font: normalFont) self.avatarNode = AvatarNode(font: normalFont)
@ -67,6 +69,9 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
} }
strongSelf.contextAction?(strongSelf.containerNode, gesture) strongSelf.contextAction?(strongSelf.containerNode, gesture)
} }
self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 37.0, height: 37.0))
self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 37.0, height: 37.0))
} }
override func didLoad() { override func didLoad() {
@ -74,6 +79,14 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
self.view.isOpaque = false self.view.isOpaque = false
(self.view as? ChatAvatarNavigationNodeView)?.targetNode = self (self.view as? ChatAvatarNavigationNodeView)?.targetNode = self
(self.view as? ChatAvatarNavigationNodeView)?.chatController = self.chatController (self.view as? ChatAvatarNavigationNodeView)?.chatController = self.chatController
self.avatarNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.avatarTapGesture(_:))))
}
@objc private func avatarTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.tapped?()
}
} }
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
@ -85,7 +98,7 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
} }
func onLayout() { func onLayout() {
let bounds = self.bounds /*let bounds = self.bounds
if self.bounds.size.height.isLessThanOrEqualTo(26.0) { if self.bounds.size.height.isLessThanOrEqualTo(26.0) {
if !self.avatarNode.bounds.size.equalTo(bounds.size) { if !self.avatarNode.bounds.size.equalTo(bounds.size) {
self.avatarNode.font = smallFont self.avatarNode.font = smallFont
@ -98,6 +111,6 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
} }
self.containerNode.frame = bounds.offsetBy(dx: 10.0, dy: 1.0) self.containerNode.frame = bounds.offsetBy(dx: 10.0, dy: 1.0)
self.avatarNode.frame = bounds self.avatarNode.frame = bounds
} }*/
} }
} }

View File

@ -363,12 +363,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource) super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
/*switch mode { self.navigationBar?.customBackButtonText = ""
case .overlay:
self.navigationPresentation = .standaloneModal
default:
break
}*/
self.blocksBackgroundWhenInOverlay = true self.blocksBackgroundWhenInOverlay = true
@ -1871,41 +1866,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
self.chatTitleView = ChatTitleView(account: self.context.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder) var displayNavigationAvatar = false
self.navigationItem.titleView = self.chatTitleView if case let .peer(peerId) = chatLocation, peerId != context.account.peerId {
self.chatTitleView?.pressed = { [weak self] in displayNavigationAvatar = true
if let strongSelf = self { self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
if strongSelf.chatLocation == .peer(strongSelf.context.account.peerId) {
strongSelf.effectiveNavigationController?.pushViewController(PeerMediaCollectionController(context: strongSelf.context, peerId: strongSelf.context.account.peerId))
} else {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if let index = $0.firstIndex(where: {
switch $0 {
case .chatInfo:
return true
default:
return false
} }
}) { self.chatTitleView = ChatTitleView(account: self.context.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, displayAvatar: displayNavigationAvatar)
var updatedContexts = $0 if let avatarNode = self.chatTitleView?.avatarNode {
updatedContexts.remove(at: index)
return updatedContexts
} else {
var updatedContexts = $0
updatedContexts.append(.chatInfo)
return updatedContexts.sorted()
}
}
})
}
}
}
let chatInfoButtonItem: UIBarButtonItem
switch chatLocation {
case .peer:
let avatarNode = ChatAvatarNavigationNode()
avatarNode.chatController = self avatarNode.chatController = self
avatarNode.contextAction = { [weak self] node, gesture in avatarNode.contextAction = { [weak self] node, gesture in
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, peer.smallProfileImage != nil else { guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, peer.smallProfileImage != nil else {
@ -1918,20 +1885,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let items: [ContextMenuItem] = [ let items: [ContextMenuItem] = [
.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in
f(.dismissWithoutContent) f(.dismissWithoutContent)
self?.navigationButtonAction(.openChatInfo) self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
})) }))
] ]
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController) strongSelf.presentInGlobalOverlay(contextController)
} }
chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)! avatarNode.tapped = { [weak self] in
/*case .group: self?.navigationButtonAction(.openChatInfo(expandAvatar: true))
chatInfoButtonItem = UIBarButtonItem(customDisplayNode: ChatMultipleAvatarsNavigationNode())!*/
} }
chatInfoButtonItem.target = self }
chatInfoButtonItem.action = #selector(self.rightNavigationButtonAction) self.navigationItem.titleView = self.chatTitleView
chatInfoButtonItem.accessibilityLabel = self.presentationData.strings.Conversation_Info self.chatTitleView?.pressed = { [weak self] in
self.chatInfoNavigationButton = ChatNavigationButton(action: .openChatInfo, buttonItem: chatInfoButtonItem) self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
}
let buttonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationMoreIcon(presentationInterfaceState.theme), style: .plain, target: self, action: #selector(self.rightNavigationButtonAction))
//buttonItem.accessibilityLabel = strings.Conversation_Search
chatInfoNavigationButton = ChatNavigationButton(action: .toggleInfoPanel, buttonItem: buttonItem)
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
if let botStart = botStart, case .interactive = botStart.behavior { if let botStart = botStart, case .interactive = botStart.behavior {
@ -2010,7 +1981,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self { if let strongSelf = self {
if let peer = peerViewMainPeer(peerView) { if let peer = peerViewMainPeer(peerView) {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages) strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages)
(strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: peer, overrideImage: peer.isDeleted ? .deletedIcon : .none) strongSelf.chatTitleView?.avatarNode?.avatarNode.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: peer, overrideImage: peer.isDeleted ? .deletedIcon : .none)
(strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil
} }
@ -3545,7 +3516,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always)) strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always))
} }
}, openPeerInfo: { [weak self] in }, openPeerInfo: { [weak self] in
self?.navigationButtonAction(.openChatInfo) self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
}, togglePeerNotifications: { [weak self] in }, togglePeerNotifications: { [weak self] in
if let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation { if let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation {
let _ = togglePeerMuted(account: strongSelf.context.account, peerId: peerId).start() let _ = togglePeerMuted(account: strongSelf.context.account, peerId: peerId).start()
@ -5319,17 +5290,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.chatDisplayNode.dismissInput() self.chatDisplayNode.dismissInput()
self.present(actionSheet, in: .window(.root)) self.present(actionSheet, in: .window(.root))
} }
case .openChatInfo: case let .openChatInfo(expandAvatar):
switch self.chatLocationInfoData { switch self.chatLocationInfoData {
case let .peer(peerView): case let .peer(peerView):
self.navigationActionDisposable.set((peerView.get() self.navigationActionDisposable.set((peerView.get()
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peerView in |> deliverOnMainQueue).start(next: { [weak self] peerView in
if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible { if let strongSelf = self, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if peer.id == strongSelf.context.account.peerId {
strongSelf.effectiveNavigationController?.pushViewController(PeerMediaCollectionController(context: strongSelf.context, peerId: strongSelf.context.account.peerId))
} else {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: expandAvatar) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} }
}
})) }))
} }
case .search: case .search:
@ -5538,6 +5513,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
})) }))
} }
case .toggleInfoPanel:
self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if let index = $0.firstIndex(where: {
switch $0 {
case .chatInfo:
return true
default:
return false
}
}) {
var updatedContexts = $0
updatedContexts.remove(at: index)
return updatedContexts
} else {
var updatedContexts = $0
updatedContexts.append(.chatInfo)
return updatedContexts.sorted()
}
}
})
} }
} }
@ -7025,11 +7021,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.effectiveNavigationController?.pushViewController(controller) self.effectiveNavigationController?.pushViewController(controller)
} }
private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: Message?) { private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: Message?, expandAvatar: Bool = false) {
if case let .peer(currentPeerId) = self.chatLocation, peerId == currentPeerId { if case let .peer(currentPeerId) = self.chatLocation, peerId == currentPeerId {
switch navigation { switch navigation {
case .info: case .info:
self.navigationButtonAction(.openChatInfo) self.navigationButtonAction(.openChatInfo(expandAvatar: expandAvatar))
case let .chat(textInputState, _): case let .chat(textInputState, _):
if let textInputState = textInputState { if let textInputState = textInputState {
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
@ -7061,7 +7057,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
self.navigationActionDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in self.navigationActionDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, let peer = peer { if let strongSelf = self, let peer = peer {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: expandAvatar) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} }
@ -7478,7 +7474,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil { if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
strongSelf.effectiveNavigationController?.pushViewController(infoController) strongSelf.effectiveNavigationController?.pushViewController(infoController)
} }
} }

View File

@ -6,13 +6,14 @@ import SyncCore
import TelegramPresentationData import TelegramPresentationData
import AccountContext import AccountContext
enum ChatNavigationButtonAction { enum ChatNavigationButtonAction: Equatable {
case openChatInfo case openChatInfo(expandAvatar: Bool)
case clearHistory case clearHistory
case clearCache case clearCache
case cancelMessageSelection case cancelMessageSelection
case search case search
case dismiss case dismiss
case toggleInfoPanel
} }
struct ChatNavigationButton: Equatable { struct ChatNavigationButton: Equatable {

View File

@ -659,7 +659,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
if peer is TelegramChannel, let navigationController = strongSelf.getNavigationController() { if peer is TelegramChannel, let navigationController = strongSelf.getNavigationController() {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: true)) strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: true))
} else { } else {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
strongSelf.pushController(infoController) strongSelf.pushController(infoController)
} }
} }
@ -681,7 +681,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self { if let strongSelf = self {
if let peer = peer { if let peer = peer {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
strongSelf.pushController(infoController) strongSelf.pushController(infoController)
} }
} }

View File

@ -15,6 +15,7 @@ import PeerPresenceStatusManager
import ChatTitleActivityNode import ChatTitleActivityNode
import LocalizedPeerData import LocalizedPeerData
import PhoneNumberFormat import PhoneNumberFormat
import ChatTitleActivityNode
enum ChatTitleContent { enum ChatTitleContent {
case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool) case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool)
@ -92,14 +93,16 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
private var nameDisplayOrder: PresentationPersonNameOrder private var nameDisplayOrder: PresentationPersonNameOrder
private let contentContainer: ASDisplayNode private let contentContainer: ASDisplayNode
private let titleNode: ImmediateTextNode let titleNode: ImmediateTextNode
private let titleLeftIconNode: ASImageNode let titleLeftIconNode: ASImageNode
private let titleRightIconNode: ASImageNode let titleRightIconNode: ASImageNode
private let titleCredibilityIconNode: ASImageNode let titleCredibilityIconNode: ASImageNode
private let activityNode: ChatTitleActivityNode let activityNode: ChatTitleActivityNode
private let button: HighlightTrackingButtonNode private let button: HighlightTrackingButtonNode
let avatarNode: ChatAvatarNavigationNode?
private var validLayout: (CGSize, CGRect)? private var validLayout: (CGSize, CGRect)?
private var titleLeftIcon: ChatTitleIcon = .none private var titleLeftIcon: ChatTitleIcon = .none
@ -136,7 +139,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
} else { } else {
statusNode = ChatTitleNetworkStatusNode(theme: self.theme) statusNode = ChatTitleNetworkStatusNode(theme: self.theme)
self.networkStatusNode = statusNode self.networkStatusNode = statusNode
self.insertSubview(statusNode.view, belowSubview: self.button.view) self.insertSubview(statusNode.view, aboveSubview: self.contentContainer.view)
} }
switch self.networkState { switch self.networkState {
case .waitingForNetwork: case .waitingForNetwork:
@ -451,7 +454,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
} }
} }
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) { init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, displayAvatar: Bool) {
self.account = account self.account = account
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
@ -482,6 +485,11 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
self.activityNode = ChatTitleActivityNode() self.activityNode = ChatTitleActivityNode()
self.button = HighlightTrackingButtonNode() self.button = HighlightTrackingButtonNode()
if displayAvatar {
self.avatarNode = ChatAvatarNavigationNode()
} else {
self.avatarNode = nil
}
super.init(frame: CGRect()) super.init(frame: CGRect())
@ -492,12 +500,13 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
self.contentContainer.addSubnode(self.titleNode) self.contentContainer.addSubnode(self.titleNode)
self.contentContainer.addSubnode(self.activityNode) self.contentContainer.addSubnode(self.activityNode)
self.addSubnode(self.button) self.addSubnode(self.button)
self.avatarNode.flatMap(self.contentContainer.addSubnode)
self.presenceManager = PeerPresenceStatusManager(update: { [weak self] in self.presenceManager = PeerPresenceStatusManager(update: { [weak self] in
self?.updateStatus() self?.updateStatus()
}) })
self.button.addTarget(self, action: #selector(buttonPressed), forControlEvents: [.touchUpInside]) self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: [.touchUpInside])
self.button.highligthedChanged = { [weak self] highlighted in self.button.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self { if let strongSelf = self {
if highlighted { if highlighted {
@ -558,7 +567,6 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
let transition: ContainedViewLayoutTransition = .immediate let transition: ContainedViewLayoutTransition = .immediate
self.button.frame = clearBounds
self.contentContainer.frame = clearBounds self.contentContainer.frame = clearBounds
var leftIconWidth: CGFloat = 0.0 var leftIconWidth: CGFloat = 0.0
@ -592,35 +600,35 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleRightIconNode.removeFromSupernode() self.titleRightIconNode.removeFromSupernode()
} }
var leftInset: CGFloat = 12.0
if let avatarNode = self.avatarNode {
let avatarSize = CGSize(width: 37.0, height: 37.0)
let avatarFrame = CGRect(origin: CGPoint(x: leftInset + 10.0, y: floor((size.height - avatarSize.height) / 2.0)), size: avatarSize)
avatarNode.frame = avatarFrame
leftInset += avatarSize.width + 10.0 + 8.0
}
self.button.frame = CGRect(origin: CGPoint(x: leftInset - 20.0, y: 0.0), size: CGSize(width: clearBounds.width - leftInset, height: size.height))
let titleSideInset: CGFloat = 3.0 let titleSideInset: CGFloat = 3.0
if size.height > 40.0 { if size.height > 40.0 {
var titleSize = self.titleNode.updateLayout(CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height)) var titleSize = self.titleNode.updateLayout(CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height))
titleSize.width += credibilityIconWidth titleSize.width += credibilityIconWidth
let activitySize = self.activityNode.updateLayout(clearBounds.size, alignment: .center) let activitySize = self.activityNode.updateLayout(clearBounds.size, alignment: .left)
let titleInfoSpacing: CGFloat = 0.0 let titleInfoSpacing: CGFloat = 0.0
var titleFrame: CGRect var titleFrame: CGRect
if activitySize.height.isZero { if activitySize.height.isZero {
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) titleFrame = CGRect(origin: CGPoint(x: leftInset + leftIconWidth, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
if titleFrame.size.width < size.width {
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
}
self.titleNode.frame = titleFrame self.titleNode.frame = titleFrame
} else { } else {
let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize) titleFrame = CGRect(origin: CGPoint(x: leftInset + leftIconWidth, y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
if titleFrame.size.width < size.width {
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
}
titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth)
self.titleNode.frame = titleFrame self.titleNode.frame = titleFrame
var activityFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize) var activityFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize)
if activitySize.width < size.width {
activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0)
}
self.activityNode.frame = activityFrame self.activityNode.frame = activityFrame
} }
@ -662,13 +670,18 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
} }
@objc func buttonPressed() { @objc func buttonPressed() {
if let pressed = self.pressed { self.pressed?()
pressed()
}
} }
func animateLayoutTransition() { func animateLayoutTransition() {
UIView.transition(with: self, duration: 0.25, options: [.transitionCrossDissolve], animations: { UIView.transition(with: self, duration: 0.25, options: [.transitionCrossDissolve], animations: {
}, completion: nil) }, completion: nil)
} }
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.button.frame.contains(point) {
return self.button.view
}
return super.hitTest(point, with: event)
}
} }

View File

@ -18,7 +18,7 @@ func openAddContactImpl(context: AccountContext, firstName: String = "", lastNam
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "") let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
present(deviceContactInfoController(context: context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in present(deviceContactInfoController(context: context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
if let peer = peer { if let peer = peer {
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
pushController(infoController) pushController(infoController)
} }
} else { } else {

View File

@ -209,7 +209,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
case .info: case .info:
let _ = (context.account.postbox.loadedPeerWithId(peerId) let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
context.sharedContext.applicationBindings.dismissNativeController() context.sharedContext.applicationBindings.dismissNativeController()
navigationController?.pushViewController(infoController) navigationController?.pushViewController(infoController)
} }
@ -491,7 +491,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
return transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: idValue)) return transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: idValue))
} }
|> deliverOnMainQueue).start(next: { peer in |> deliverOnMainQueue).start(next: { peer in
if let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
navigationController?.pushViewController(controller) navigationController?.pushViewController(controller)
} }
}) })

File diff suppressed because it is too large Load Diff

View File

@ -759,7 +759,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil { if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic) { if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
} }
} }

View File

@ -303,7 +303,7 @@ public func pollResultsController(context: AccountContext, messageId: MessageId,
}) })
}, openPeer: { peer in }, openPeer: { peer in
if let peer = peer.peers[peer.peerId] { if let peer = peer.peers[peer.peerId] {
if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
pushControllerImpl?(controller) pushControllerImpl?(controller)
} }
} }

View File

@ -1004,8 +1004,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
handleTextLinkActionImpl(context: context, peerId: peerId, navigateDisposable: navigateDisposable, controller: controller, action: action, itemLink: itemLink) handleTextLinkActionImpl(context: context, peerId: peerId, navigateDisposable: navigateDisposable, controller: controller, action: action, itemLink: itemLink)
} }
public func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode) -> ViewController? { public func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool) -> ViewController? {
let controller = peerInfoControllerImpl(context: context, peer: peer, mode: mode) let controller = peerInfoControllerImpl(context: context, peer: peer, mode: mode, avatarInitiallyExpanded: avatarInitiallyExpanded)
controller?.navigationPresentation = .modalInLargeLayout controller?.navigationPresentation = .modalInLargeLayout
return controller return controller
} }
@ -1245,7 +1245,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
private let defaultChatControllerInteraction = ChatControllerInteraction.default private let defaultChatControllerInteraction = ChatControllerInteraction.default
private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode) -> ViewController? { private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool) -> ViewController? {
if let _ = peer as? TelegramGroup { if let _ = peer as? TelegramGroup {
return groupInfoController(context: context, peerId: peer.id) return groupInfoController(context: context, peerId: peer.id)
} else if let channel = peer as? TelegramChannel { } else if let channel = peer as? TelegramChannel {
@ -1255,7 +1255,7 @@ private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: P
return channelInfoController(context: context, peerId: peer.id) return channelInfoController(context: context, peerId: peer.id)
} }
} else if peer is TelegramUser { } else if peer is TelegramUser {
return PeerInfoScreen(context: context, peerId: peer.id) return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded)
} else if peer is TelegramSecretChat { } else if peer is TelegramSecretChat {
return userInfoController(context: context, peerId: peer.id, mode: mode) return userInfoController(context: context, peerId: peer.id, mode: mode)
} }

View File

@ -315,7 +315,7 @@ public final class SharedWakeupManager {
if let taskId = self.beginBackgroundTask("background-wakeup", { if let taskId = self.beginBackgroundTask("background-wakeup", {
handleExpiration() handleExpiration()
}) { }) {
let timer = SwiftSignalKit.Timer(timeout: min(30.0, self.backgroundTimeRemaining()), repeat: false, completion: { let timer = SwiftSignalKit.Timer(timeout: min(30.0, max(0.0, self.backgroundTimeRemaining() - 5.0)), repeat: false, completion: {
handleExpiration() handleExpiration()
}, queue: Queue.mainQueue()) }, queue: Queue.mainQueue())
self.currentTask = (taskId, currentTime, timer) self.currentTask = (taskId, currentTime, timer)

View File

@ -32,7 +32,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
peerSignal = context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init) peerSignal = context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init)
navigateDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { peer in navigateDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { peer in
if let controller = controller, let peer = peer { if let controller = controller, let peer = peer {
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
(controller.navigationController as? NavigationController)?.pushViewController(infoController) (controller.navigationController as? NavigationController)?.pushViewController(infoController)
} }
} }