mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-13 09:50:17 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
d2d458f70c
@ -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
|
||||||
|
|||||||
@ -77,11 +77,19 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut
|
|||||||
if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
|
if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
|
||||||
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
||||||
context.setBlendMode(.copy)
|
context.setBlendMode(.copy)
|
||||||
|
|
||||||
|
if round && displayDimensions.width != 60.0 {
|
||||||
|
context.addEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||||
|
context.clip()
|
||||||
|
}
|
||||||
|
|
||||||
context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||||
if round {
|
if round {
|
||||||
|
if displayDimensions.width == 60.0 {
|
||||||
context.setBlendMode(.destinationOut)
|
context.setBlendMode(.destinationOut)
|
||||||
context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if let emptyColor = emptyColor {
|
if let emptyColor = emptyColor {
|
||||||
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -141,6 +141,27 @@ public extension ContainedViewLayoutTransition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateFrameAdditiveToCenter(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||||
|
if node.frame.equalTo(frame) && !force {
|
||||||
|
completion?(true)
|
||||||
|
} else {
|
||||||
|
switch self {
|
||||||
|
case .immediate:
|
||||||
|
node.position = frame.center
|
||||||
|
node.bounds = CGRect(origin: node.bounds.origin, size: frame.size)
|
||||||
|
if let completion = completion {
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
case let .animated(duration, curve):
|
||||||
|
let previousBounds = node.bounds
|
||||||
|
let previousCenter = node.frame.center
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||||
if node.bounds.equalTo(bounds) && !force {
|
if node.bounds.equalTo(bounds) && !force {
|
||||||
completion?(true)
|
completion?(true)
|
||||||
@ -637,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)
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -125,7 +125,7 @@ public enum GeneralScrollDirection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGestureRecognizerDelegate {
|
open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGestureRecognizerDelegate {
|
||||||
final let scroller: ListViewScroller
|
public final let scroller: ListViewScroller
|
||||||
private final var visibleSize: CGSize = CGSize()
|
private final var visibleSize: CGSize = CGSize()
|
||||||
public private(set) final var insets = UIEdgeInsets()
|
public private(set) final var insets = UIEdgeInsets()
|
||||||
public final var visualInsets: UIEdgeInsets?
|
public final var visualInsets: UIEdgeInsets?
|
||||||
|
|||||||
@ -1,29 +1,27 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate {
|
public final class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate {
|
||||||
override init(frame: CGRect) {
|
override public init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
self.scrollsToTop = false
|
self.scrollsToTop = false
|
||||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||||
self.contentInsetAdjustmentBehavior = .never
|
self.contentInsetAdjustmentBehavior = .never
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
if otherGestureRecognizer is ListViewTapGestureRecognizer {
|
if otherGestureRecognizer is ListViewTapGestureRecognizer {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
if gestureRecognizer is UIPanGestureRecognizer, let gestureRecognizers = gestureRecognizer.view?.gestureRecognizers {
|
if gestureRecognizer is UIPanGestureRecognizer, let gestureRecognizers = gestureRecognizer.view?.gestureRecognizers {
|
||||||
for otherGestureRecognizer in gestureRecognizers {
|
for otherGestureRecognizer in gestureRecognizers {
|
||||||
if otherGestureRecognizer !== gestureRecognizer, let panGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer, panGestureRecognizer.minimumNumberOfTouches == 2 {
|
if otherGestureRecognizer !== gestureRecognizer, let panGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer, panGestureRecognizer.minimumNumberOfTouches == 2 {
|
||||||
@ -36,9 +34,7 @@ class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
override public func touchesShouldCancel(in view: UIView) -> Bool {
|
||||||
override func touchesShouldCancel(in view: UIView) -> Bool {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -7,17 +7,3 @@ import TelegramCore
|
|||||||
import SyncCore
|
import SyncCore
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode) -> ViewController? {
|
|
||||||
if let _ = peer as? TelegramGroup {
|
|
||||||
return groupInfoController(context: context, peerId: peer.id)
|
|
||||||
} else if let channel = peer as? TelegramChannel {
|
|
||||||
if case .group = channel.info {
|
|
||||||
return groupInfoController(context: context, peerId: peer.id)
|
|
||||||
} else {
|
|
||||||
return channelInfoController(context: context, peerId: peer.id)
|
|
||||||
}
|
|
||||||
} else if peer is TelegramUser || peer is TelegramSecretChat {
|
|
||||||
return userInfoController(context: context, peerId: peer.id, mode: mode)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonAddMember.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonAddMember.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_pf_addmember.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonAddMember.imageset/ic_pf_addmember.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonAddMember.imageset/ic_pf_addmember.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonCall.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonCall.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_pf_call.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonCall.imageset/ic_pf_call.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonCall.imageset/ic_pf_call.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMessage.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMessage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_pf_message.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMessage.imageset/ic_pf_message.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMessage.imageset/ic_pf_message.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMore.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMore.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_pf_more.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMore.imageset/ic_pf_more.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMore.imageset/ic_pf_more.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMute.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMute.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_pf_mute.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMute.imageset/ic_pf_mute.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonMute.imageset/ic_pf_mute.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonUnmute.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonUnmute.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_pf_unmute.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonUnmute.imageset/ic_pf_unmute.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonUnmute.imageset/ic_pf_unmute.pdf
vendored
Normal file
Binary file not shown.
@ -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
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
174
submodules/TelegramUI/TelegramUI/PeerInfoFilesPane.swift
Normal file
174
submodules/TelegramUI/TelegramUI/PeerInfoFilesPane.swift
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import TelegramCore
|
||||||
|
import SyncCore
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramPresentationData
|
||||||
|
import AccountContext
|
||||||
|
import ContextUI
|
||||||
|
import PhotoResources
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
|
final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||||
|
private let context: AccountContext
|
||||||
|
private let peerId: PeerId
|
||||||
|
|
||||||
|
private let listNode: ChatHistoryListNode
|
||||||
|
|
||||||
|
private var currentParams: (size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData)?
|
||||||
|
|
||||||
|
private let ready = Promise<Bool>()
|
||||||
|
private var didSetReady: Bool = false
|
||||||
|
var isReady: Signal<Bool, NoError> {
|
||||||
|
return self.ready.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var hiddenMediaDisposable: Disposable?
|
||||||
|
|
||||||
|
init(context: AccountContext, openMessage: @escaping (MessageId) -> Bool, peerId: PeerId, tagMask: MessageTags) {
|
||||||
|
self.context = context
|
||||||
|
self.peerId = peerId
|
||||||
|
|
||||||
|
var openMessageImpl: ((MessageId) -> Bool)?
|
||||||
|
let controllerInteraction = ChatControllerInteraction(openMessage: { message, _ in
|
||||||
|
return openMessageImpl?(message.id) ?? false
|
||||||
|
}, openPeer: { _, _, _ in
|
||||||
|
}, openPeerMention: { _ in
|
||||||
|
}, openMessageContextMenu: { _, _, _, _, _ in
|
||||||
|
}, openMessageContextActions: { _, _, _, _ in
|
||||||
|
}, navigateToMessage: { _, _ in
|
||||||
|
}, tapMessage: nil, clickThroughMessage: {
|
||||||
|
}, toggleMessagesSelection: { _, _ in
|
||||||
|
}, sendCurrentMessage: { _ in
|
||||||
|
}, sendMessage: { _ in
|
||||||
|
}, sendSticker: { _, _, _, _ in
|
||||||
|
return false
|
||||||
|
}, sendGif: { _, _, _ in
|
||||||
|
return false
|
||||||
|
}, requestMessageActionCallback: { _, _, _ in
|
||||||
|
}, requestMessageActionUrlAuth: { _, _, _ in
|
||||||
|
}, activateSwitchInline: { _, _ in
|
||||||
|
}, openUrl: { _, _, _, _ in
|
||||||
|
}, shareCurrentLocation: {
|
||||||
|
}, shareAccountContact: {
|
||||||
|
}, sendBotCommand: { _, _ in
|
||||||
|
}, openInstantPage: { _, _ in
|
||||||
|
}, openWallpaper: { _ in
|
||||||
|
}, openTheme: {_ in
|
||||||
|
}, openHashtag: { _, _ in
|
||||||
|
}, updateInputState: { _ in
|
||||||
|
}, updateInputMode: { _ in
|
||||||
|
}, openMessageShareMenu: { _ in
|
||||||
|
}, presentController: { _, _ in
|
||||||
|
}, navigationController: {
|
||||||
|
return nil
|
||||||
|
}, chatControllerNode: {
|
||||||
|
return nil
|
||||||
|
}, reactionContainerNode: {
|
||||||
|
return nil
|
||||||
|
}, presentGlobalOverlayController: { _, _ in
|
||||||
|
}, callPeer: { _ in
|
||||||
|
}, longTap: { _, _ in
|
||||||
|
}, openCheckoutOrReceipt: { _ in
|
||||||
|
}, openSearch: {
|
||||||
|
}, setupReply: { _ in
|
||||||
|
}, canSetupReply: { _ in
|
||||||
|
return false
|
||||||
|
}, navigateToFirstDateMessage: { _ in
|
||||||
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
|
}, addContact: { _ in
|
||||||
|
}, rateCall: { _, _ in
|
||||||
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
|
}, openAppStorePage: {
|
||||||
|
}, displayMessageTooltip: { _, _, _, _ in
|
||||||
|
}, seekToTimecode: { _, _, _ in
|
||||||
|
}, scheduleCurrentMessage: {
|
||||||
|
}, sendScheduledMessagesNow: { _ in
|
||||||
|
}, editScheduledMessagesTime: { _ in
|
||||||
|
}, performTextSelectionAction: { _, _, _ in
|
||||||
|
}, updateMessageReaction: { _, _ in
|
||||||
|
}, openMessageReactions: { _ in
|
||||||
|
}, displaySwipeToReplyHint: {
|
||||||
|
}, dismissReplyMarkupMessage: { _ in
|
||||||
|
}, openMessagePollResults: { _, _ in
|
||||||
|
}, openPollCreation: { _ in
|
||||||
|
}, requestMessageUpdate: { _ in
|
||||||
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||||
|
|
||||||
|
self.listNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: tagMask, subject: nil, controllerInteraction: controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: false))
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
openMessageImpl = { id in
|
||||||
|
return openMessage(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var hiddenMedia: [MessageId: [Media]] = [:]
|
||||||
|
for id in ids {
|
||||||
|
if case let .chat(accountId, messageId, media) = id, accountId == strongSelf.context.account.id {
|
||||||
|
hiddenMedia[messageId] = [media]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controllerInteraction.hiddenMedia = hiddenMedia
|
||||||
|
strongSelf.listNode.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? ListMessageNode {
|
||||||
|
itemNode.updateHiddenMedia()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.listNode.preloadPages = true
|
||||||
|
self.addSubnode(self.listNode)
|
||||||
|
|
||||||
|
self.ready.set(self.listNode.historyState.get()
|
||||||
|
|> take(1)
|
||||||
|
|> map { _ -> Bool in true })
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.hiddenMediaDisposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollToTop() -> Bool {
|
||||||
|
let offset = self.listNode.visibleContentOffset()
|
||||||
|
switch offset {
|
||||||
|
case let .known(value) where value <= CGFloat.ulpOfOne:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
self.listNode.scrollToEndOfHistory()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.currentParams = (size, isScrollingLockedAtTop, presentationData)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||||
|
self.listNode.updateLayout(transition: transition, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: size, insets: UIEdgeInsets(), duration: duration, curve: curve))
|
||||||
|
self.listNode.scrollEnabled = !isScrollingLockedAtTop
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLoadedMessage(id: MessageId) -> Message? {
|
||||||
|
self.listNode.messageInCurrentHistoryView(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
|
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||||
|
self.listNode.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? ListMessageNode {
|
||||||
|
if let result = itemNode.transitionNode(id: messageId, media: media) {
|
||||||
|
transitionNode = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transitionNode
|
||||||
|
}
|
||||||
|
}
|
||||||
2435
submodules/TelegramUI/TelegramUI/PeerInfoScreen.swift
Normal file
2435
submodules/TelegramUI/TelegramUI/PeerInfoScreen.swift
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,123 @@
|
|||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import TelegramPresentationData
|
||||||
|
|
||||||
|
enum PeerInfoScreenLabeledValueTextColor {
|
||||||
|
case primary
|
||||||
|
case accent
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PeerInfoScreenLabeledValueTextBehavior: Equatable {
|
||||||
|
case singleLine
|
||||||
|
case multiLine(maxLines: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class PeerInfoScreenLabeledValueItem: PeerInfoScreenItem {
|
||||||
|
let id: AnyHashable
|
||||||
|
let label: String
|
||||||
|
let text: String
|
||||||
|
let textColor: PeerInfoScreenLabeledValueTextColor
|
||||||
|
let textBehavior: PeerInfoScreenLabeledValueTextBehavior
|
||||||
|
let action: (() -> Void)?
|
||||||
|
|
||||||
|
init(id: AnyHashable, label: String, text: String, textColor: PeerInfoScreenLabeledValueTextColor = .primary, textBehavior: PeerInfoScreenLabeledValueTextBehavior = .singleLine, action: (() -> Void)?) {
|
||||||
|
self.id = id
|
||||||
|
self.label = label
|
||||||
|
self.text = text
|
||||||
|
self.textColor = textColor
|
||||||
|
self.textBehavior = textBehavior
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func node() -> PeerInfoScreenItemNode {
|
||||||
|
return PeerInfoScreenLabeledValueItemNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||||
|
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
|
||||||
|
private let labelNode: ImmediateTextNode
|
||||||
|
private let textNode: ImmediateTextNode
|
||||||
|
private let bottomSeparatorNode: ASDisplayNode
|
||||||
|
|
||||||
|
private var item: PeerInfoScreenLabeledValueItem?
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
var bringToFrontForHighlightImpl: (() -> Void)?
|
||||||
|
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
|
||||||
|
|
||||||
|
self.labelNode = ImmediateTextNode()
|
||||||
|
self.labelNode.displaysAsynchronously = false
|
||||||
|
self.labelNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.displaysAsynchronously = false
|
||||||
|
self.textNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.bottomSeparatorNode = ASDisplayNode()
|
||||||
|
self.bottomSeparatorNode.isLayerBacked = true
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
bringToFrontForHighlightImpl = { [weak self] in
|
||||||
|
self?.bringToFrontForHighlight?()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addSubnode(self.bottomSeparatorNode)
|
||||||
|
self.addSubnode(self.selectionNode)
|
||||||
|
self.addSubnode(self.labelNode)
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
|
guard let item = item as? PeerInfoScreenLabeledValueItem else {
|
||||||
|
return 10.0
|
||||||
|
}
|
||||||
|
|
||||||
|
self.item = item
|
||||||
|
|
||||||
|
self.selectionNode.pressed = item.action
|
||||||
|
|
||||||
|
let sideInset: CGFloat = 16.0
|
||||||
|
|
||||||
|
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
|
let textColorValue: UIColor
|
||||||
|
switch item.textColor {
|
||||||
|
case .primary:
|
||||||
|
textColorValue = presentationData.theme.list.itemPrimaryTextColor
|
||||||
|
case .accent:
|
||||||
|
textColorValue = presentationData.theme.list.itemAccentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||||
|
|
||||||
|
switch item.textBehavior {
|
||||||
|
case .singleLine:
|
||||||
|
self.textNode.maximumNumberOfLines = 1
|
||||||
|
case let .multiLine(maxLines):
|
||||||
|
self.textNode.maximumNumberOfLines = maxLines
|
||||||
|
}
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
||||||
|
|
||||||
|
let labelSize = self.labelNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||||
|
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||||
|
|
||||||
|
let labelFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: labelSize)
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: labelFrame.maxY + 3.0), size: textSize)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.labelNode, frame: labelFrame)
|
||||||
|
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||||
|
|
||||||
|
let height = labelSize.height + 3.0 + textSize.height + 22.0
|
||||||
|
|
||||||
|
let highlightNodeOffset: CGFloat = topItem == nil ? 0.0 : UIScreenPixel
|
||||||
|
self.selectionNode.update(size: CGSize(width: width, height: height + highlightNodeOffset), theme: presentationData.theme, transition: transition)
|
||||||
|
transition.updateFrame(node: self.selectionNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -highlightNodeOffset), size: CGSize(width: width, height: height + highlightNodeOffset)))
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
|
||||||
|
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
|
||||||
|
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import TelegramPresentationData
|
||||||
|
|
||||||
|
final class PeerInfoScreenSelectableBackgroundNode: ASDisplayNode {
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
|
private let buttonNode: HighlightTrackingButtonNode
|
||||||
|
|
||||||
|
let bringToFrontForHighlight: () -> Void
|
||||||
|
|
||||||
|
var pressed: (() -> Void)? {
|
||||||
|
didSet {
|
||||||
|
self.buttonNode.isUserInteractionEnabled = self.pressed != nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(bringToFrontForHighlight: @escaping () -> Void) {
|
||||||
|
self.bringToFrontForHighlight = bringToFrontForHighlight
|
||||||
|
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
self.backgroundNode.alpha = 0.0
|
||||||
|
|
||||||
|
self.buttonNode = HighlightTrackingButtonNode()
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
|
self.addSubnode(self.buttonNode)
|
||||||
|
|
||||||
|
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||||
|
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if highlighted {
|
||||||
|
strongSelf.bringToFrontForHighlight()
|
||||||
|
strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||||
|
strongSelf.backgroundNode.alpha = 1.0
|
||||||
|
} else {
|
||||||
|
strongSelf.backgroundNode.alpha = 0.0
|
||||||
|
strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func buttonPressed() {
|
||||||
|
self.pressed?()
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(size: CGSize, theme: PresentationTheme, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.backgroundNode.backgroundColor = theme.list.itemHighlightedBackgroundColor
|
||||||
|
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
}
|
||||||
|
}
|
||||||
458
submodules/TelegramUI/TelegramUI/PeerInfoVisualMediaPane.swift
Normal file
458
submodules/TelegramUI/TelegramUI/PeerInfoVisualMediaPane.swift
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import TelegramCore
|
||||||
|
import SyncCore
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramPresentationData
|
||||||
|
import AccountContext
|
||||||
|
import ContextUI
|
||||||
|
import PhotoResources
|
||||||
|
|
||||||
|
private final class VisualMediaItemInteraction {
|
||||||
|
let openMessage: (MessageId) -> Void
|
||||||
|
var hiddenMedia: [MessageId: [Media]] = [:]
|
||||||
|
|
||||||
|
init(openMessage: @escaping (MessageId) -> Void) {
|
||||||
|
self.openMessage = openMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class VisualMediaItemNode: ASDisplayNode {
|
||||||
|
private let context: AccountContext
|
||||||
|
private let interaction: VisualMediaItemInteraction
|
||||||
|
|
||||||
|
private let containerNode: ContextControllerSourceNode
|
||||||
|
private let imageNode: TransformImageNode
|
||||||
|
|
||||||
|
private let fetchStatusDisposable = MetaDisposable()
|
||||||
|
private let fetchDisposable = MetaDisposable()
|
||||||
|
private var resourceStatus: MediaResourceStatus?
|
||||||
|
|
||||||
|
private var item: (VisualMediaItem, Media?, CGSize, CGSize?)?
|
||||||
|
|
||||||
|
init(context: AccountContext, interaction: VisualMediaItemInteraction) {
|
||||||
|
self.context = context
|
||||||
|
self.interaction = interaction
|
||||||
|
|
||||||
|
self.containerNode = ContextControllerSourceNode()
|
||||||
|
self.imageNode = TransformImageNode()
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.containerNode)
|
||||||
|
self.containerNode.addSubnode(self.imageNode)
|
||||||
|
|
||||||
|
self.containerNode.isGestureEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.fetchStatusDisposable.dispose()
|
||||||
|
self.fetchDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
|
if case .ended = recognizer.state {
|
||||||
|
if let (item, _, _, _) = self.item {
|
||||||
|
self.interaction.openMessage(item.message.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(size: CGSize, item: VisualMediaItem, theme: PresentationTheme, synchronousLoad: Bool) {
|
||||||
|
if item === self.item?.0 && size == self.item?.2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var media: Media?
|
||||||
|
for value in item.message.media {
|
||||||
|
if let image = value as? TelegramMediaImage {
|
||||||
|
media = image
|
||||||
|
break
|
||||||
|
} else if let file = value as? TelegramMediaFile {
|
||||||
|
media = file
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let media = media, (self.item?.1 == nil || !media.isEqual(to: self.item!.1!)) {
|
||||||
|
var mediaDimensions: CGSize?
|
||||||
|
if let image = media as? TelegramMediaImage, let largestSize = largestImageRepresentation(image.representations)?.dimensions {
|
||||||
|
mediaDimensions = largestSize.cgSize
|
||||||
|
|
||||||
|
self.imageNode.setSignal(mediaGridMessagePhoto(account: context.account, photoReference: .message(message: MessageReference(item.message), media: image), fullRepresentationSize: CGSize(width: 300.0, height: 300.0), synchronousLoad: synchronousLoad), attemptSynchronously: synchronousLoad, dispatchOnDisplayLink: true)
|
||||||
|
|
||||||
|
self.fetchStatusDisposable.set(nil)
|
||||||
|
/*self.statusNode.transitionToState(.none, completion: { [weak self] in
|
||||||
|
self?.statusNode.isHidden = true
|
||||||
|
})*/
|
||||||
|
//self.mediaBadgeNode.isHidden = true
|
||||||
|
self.resourceStatus = nil
|
||||||
|
} else if let file = media as? TelegramMediaFile, file.isVideo {
|
||||||
|
mediaDimensions = file.dimensions?.cgSize
|
||||||
|
self.imageNode.setSignal(mediaGridMessageVideo(postbox: context.account.postbox, videoReference: .message(message: MessageReference(item.message), media: file), synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: true), attemptSynchronously: synchronousLoad)
|
||||||
|
|
||||||
|
/*self.mediaBadgeNode.isHidden = false
|
||||||
|
|
||||||
|
self.resourceStatus = nil
|
||||||
|
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: messageId, file: file) |> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
|
strongSelf.resourceStatus = status
|
||||||
|
|
||||||
|
let isStreamable = isMediaStreamable(message: item.message, media: file)
|
||||||
|
|
||||||
|
let statusState: RadialStatusNodeState
|
||||||
|
if isStreamable {
|
||||||
|
statusState = .none
|
||||||
|
} else {
|
||||||
|
switch status {
|
||||||
|
case let .Fetching(_, progress):
|
||||||
|
let adjustedProgress = max(progress, 0.027)
|
||||||
|
statusState = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||||
|
case .Local:
|
||||||
|
statusState = .none
|
||||||
|
case .Remote:
|
||||||
|
statusState = .download(.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch statusState {
|
||||||
|
case .none:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
strongSelf.statusNode.isHidden = false
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.statusNode.transitionToState(statusState, animated: true, completion: {
|
||||||
|
if let strongSelf = self {
|
||||||
|
if case .none = statusState {
|
||||||
|
strongSelf.statusNode.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if let duration = file.duration {
|
||||||
|
let durationString = stringForDuration(duration)
|
||||||
|
|
||||||
|
var badgeContent: ChatMessageInteractiveMediaBadgeContent?
|
||||||
|
var mediaDownloadState: ChatMessageInteractiveMediaDownloadState?
|
||||||
|
|
||||||
|
if isStreamable {
|
||||||
|
switch status {
|
||||||
|
case let .Fetching(_, progress):
|
||||||
|
let progressString = String(format: "%d%%", Int(progress * 100.0))
|
||||||
|
badgeContent = .text(inset: 12.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: progressString))
|
||||||
|
mediaDownloadState = .compactFetching(progress: 0.0)
|
||||||
|
case .Local:
|
||||||
|
badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString))
|
||||||
|
case .Remote:
|
||||||
|
badgeContent = .text(inset: 12.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString))
|
||||||
|
mediaDownloadState = .compactRemote
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
badgeContent = .text(inset: 0.0, backgroundColor: mediaBadgeBackgroundColor, foregroundColor: mediaBadgeTextColor, text: NSAttributedString(string: durationString))
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.mediaBadgeNode.update(theme: item.theme, content: badgeContent, mediaDownloadState: mediaDownloadState, alignment: .right, animated: false, badgeAnimated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
if self.statusNode.supernode == nil {
|
||||||
|
self.imageNode.addSubnode(self.statusNode)
|
||||||
|
}*/
|
||||||
|
} else {
|
||||||
|
//self.mediaBadgeNode.isHidden = true
|
||||||
|
}
|
||||||
|
self.item = (item, media, size, mediaDimensions)
|
||||||
|
|
||||||
|
self.updateHiddenMedia()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (item, media, _, mediaDimensions) = self.item {
|
||||||
|
self.item = (item, media, size, mediaDimensions)
|
||||||
|
|
||||||
|
let imageFrame = CGRect(origin: CGPoint(), size: size)
|
||||||
|
|
||||||
|
self.containerNode.frame = imageFrame
|
||||||
|
self.imageNode.frame = imageFrame
|
||||||
|
|
||||||
|
if let mediaDimensions = mediaDimensions {
|
||||||
|
let imageSize = mediaDimensions.aspectFilled(imageFrame.size)
|
||||||
|
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageFrame.size, intrinsicInsets: UIEdgeInsets(), emptyColor: theme.list.mediaPlaceholderColor))()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionNode() -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
|
let imageNode = self.imageNode
|
||||||
|
return (self.imageNode, self.imageNode.bounds, { [weak self, weak imageNode] in
|
||||||
|
var statusNodeHidden = false
|
||||||
|
var accessoryHidden = false
|
||||||
|
if let strongSelf = self {
|
||||||
|
//statusNodeHidden = strongSelf.statusNode.isHidden
|
||||||
|
//accessoryHidden = strongSelf.mediaBadgeNode.isHidden
|
||||||
|
//strongSelf.statusNode.isHidden = true
|
||||||
|
//strongSelf.mediaBadgeNode.isHidden = true
|
||||||
|
}
|
||||||
|
let view = imageNode?.view.snapshotContentTree(unhide: true)
|
||||||
|
if let strongSelf = self {
|
||||||
|
//strongSelf.statusNode.isHidden = statusNodeHidden
|
||||||
|
//strongSelf.mediaBadgeNode.isHidden = accessoryHidden
|
||||||
|
}
|
||||||
|
return (view, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateHiddenMedia() {
|
||||||
|
if let (item, _, _, _) = self.item {
|
||||||
|
if let _ = self.interaction.hiddenMedia[item.message.id] {
|
||||||
|
self.isHidden = true
|
||||||
|
} else {
|
||||||
|
self.isHidden = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class VisualMediaItem {
|
||||||
|
let message: Message
|
||||||
|
|
||||||
|
init(message: Message) {
|
||||||
|
self.message = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
||||||
|
private let context: AccountContext
|
||||||
|
private let peerId: PeerId
|
||||||
|
private let scrollNode: ASScrollNode
|
||||||
|
|
||||||
|
private var _itemInteraction: VisualMediaItemInteraction?
|
||||||
|
private var itemInteraction: VisualMediaItemInteraction {
|
||||||
|
return self._itemInteraction!
|
||||||
|
}
|
||||||
|
|
||||||
|
private var currentParams: (size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData)?
|
||||||
|
|
||||||
|
private let ready = Promise<Bool>()
|
||||||
|
private var didSetReady: Bool = false
|
||||||
|
var isReady: Signal<Bool, NoError> {
|
||||||
|
return self.ready.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
private let listDisposable = MetaDisposable()
|
||||||
|
private var hiddenMediaDisposable: Disposable?
|
||||||
|
private var mediaItems: [VisualMediaItem] = []
|
||||||
|
private var visibleMediaItems: [UInt32: VisualMediaItemNode] = [:]
|
||||||
|
|
||||||
|
private var numberOfItemsToRequest: Int = 50
|
||||||
|
private var currentView: MessageHistoryView?
|
||||||
|
private var isRequestingView: Bool = false
|
||||||
|
private var isFirstHistoryView: Bool = true
|
||||||
|
|
||||||
|
init(context: AccountContext, openMessage: @escaping (MessageId) -> Bool, peerId: PeerId) {
|
||||||
|
self.context = context
|
||||||
|
self.peerId = peerId
|
||||||
|
|
||||||
|
self.scrollNode = ASScrollNode()
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self._itemInteraction = VisualMediaItemInteraction(openMessage: { id in
|
||||||
|
openMessage(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
self.scrollNode.view.showsVerticalScrollIndicator = false
|
||||||
|
if #available(iOS 11.0, *) {
|
||||||
|
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
|
||||||
|
}
|
||||||
|
self.scrollNode.view.scrollsToTop = false
|
||||||
|
self.scrollNode.view.delegate = self
|
||||||
|
|
||||||
|
self.addSubnode(self.scrollNode)
|
||||||
|
|
||||||
|
self.requestHistoryAroundVisiblePosition()
|
||||||
|
|
||||||
|
self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var hiddenMedia: [MessageId: [Media]] = [:]
|
||||||
|
for id in ids {
|
||||||
|
if case let .chat(accountId, messageId, media) = id, accountId == strongSelf.context.account.id {
|
||||||
|
hiddenMedia[messageId] = [media]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strongSelf.itemInteraction.hiddenMedia = hiddenMedia
|
||||||
|
for (_, itemNode) in strongSelf.visibleMediaItems {
|
||||||
|
itemNode.updateHiddenMedia()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.listDisposable.dispose()
|
||||||
|
self.hiddenMediaDisposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func requestHistoryAroundVisiblePosition() {
|
||||||
|
if self.isRequestingView {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isRequestingView = true
|
||||||
|
self.listDisposable.set((self.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(self.peerId), index: .upperBound, anchorIndex: .upperBound, count: self.numberOfItemsToRequest, fixedCombinedReadStates: nil, tagMask: .photoOrVideo)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] (view, updateType, _) in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.updateHistory(view: view, updateType: updateType)
|
||||||
|
strongSelf.isRequestingView = false
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateHistory(view: MessageHistoryView, updateType: ViewUpdateType) {
|
||||||
|
self.currentView = view
|
||||||
|
|
||||||
|
self.mediaItems.removeAll()
|
||||||
|
switch updateType {
|
||||||
|
case .FillHole:
|
||||||
|
self.requestHistoryAroundVisiblePosition()
|
||||||
|
default:
|
||||||
|
for entry in view.entries.reversed() {
|
||||||
|
self.mediaItems.append(VisualMediaItem(message: entry.message))
|
||||||
|
}
|
||||||
|
|
||||||
|
let wasFirstHistoryView = self.isFirstHistoryView
|
||||||
|
self.isFirstHistoryView = false
|
||||||
|
|
||||||
|
if let (size, isScrollingLockedAtTop, presentationData) = self.currentParams {
|
||||||
|
self.update(size: size, isScrollingLockedAtTop: isScrollingLockedAtTop, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate)
|
||||||
|
if !self.didSetReady {
|
||||||
|
self.didSetReady = true
|
||||||
|
self.ready.set(.single(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollToTop() -> Bool {
|
||||||
|
if self.scrollNode.view.contentOffset.y > 0.0 {
|
||||||
|
self.scrollNode.view.setContentOffset(CGPoint(), animated: true)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLoadedMessage(id: MessageId) -> Message? {
|
||||||
|
for item in self.mediaItems {
|
||||||
|
if item.message.id == id {
|
||||||
|
return item.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
|
for item in self.mediaItems {
|
||||||
|
if item.message.id == messageId {
|
||||||
|
if let itemNode = self.visibleMediaItems[item.message.stableId] {
|
||||||
|
return itemNode.transitionNode()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.currentParams = (size, isScrollingLockedAtTop, presentationData)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
let itemSpacing: CGFloat = 1.0
|
||||||
|
let itemsInRow: Int = max(3, min(6, Int(size.width / 100.0)))
|
||||||
|
let itemSize: CGFloat = floor(size.width / CGFloat(itemsInRow))
|
||||||
|
|
||||||
|
let rowCount: Int = self.mediaItems.count / itemsInRow + (self.mediaItems.count % itemsInRow == 0 ? 0 : 1)
|
||||||
|
let contentHeight = CGFloat(rowCount + 1) * itemSpacing + CGFloat(rowCount) * itemSize
|
||||||
|
|
||||||
|
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
|
||||||
|
self.updateVisibleItems(size: size, theme: presentationData.theme, synchronousLoad: synchronous)
|
||||||
|
|
||||||
|
if isScrollingLockedAtTop {
|
||||||
|
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size))
|
||||||
|
}
|
||||||
|
self.scrollNode.view.isScrollEnabled = !isScrollingLockedAtTop
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
if let (size, _, presentationData) = self.currentParams {
|
||||||
|
self.updateVisibleItems(size: size, theme: presentationData.theme, synchronousLoad: false)
|
||||||
|
|
||||||
|
if scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.bounds.height * 2.0, let currentView = self.currentView, currentView.earlierId != nil {
|
||||||
|
if !self.isRequestingView {
|
||||||
|
self.numberOfItemsToRequest += 50
|
||||||
|
self.requestHistoryAroundVisiblePosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateVisibleItems(size: CGSize, theme: PresentationTheme, synchronousLoad: Bool) {
|
||||||
|
let itemSpacing: CGFloat = 1.0
|
||||||
|
let itemsInRow: Int = max(3, min(6, Int(size.width / 100.0)))
|
||||||
|
let itemSize: CGFloat = floor(size.width / CGFloat(itemsInRow))
|
||||||
|
|
||||||
|
let rowCount: Int = self.mediaItems.count / itemsInRow + (self.mediaItems.count % itemsInRow == 0 ? 0 : 1)
|
||||||
|
|
||||||
|
let visibleRect = self.scrollNode.view.bounds
|
||||||
|
var minVisibleRow = Int(floor((visibleRect.minY - itemSpacing) / (itemSize + itemSpacing)))
|
||||||
|
minVisibleRow = max(0, minVisibleRow)
|
||||||
|
var maxVisibleRow = Int(ceil((visibleRect.maxY - itemSpacing) / (itemSize + itemSpacing)))
|
||||||
|
maxVisibleRow = min(rowCount - 1, maxVisibleRow)
|
||||||
|
|
||||||
|
let minVisibleIndex = minVisibleRow * itemsInRow
|
||||||
|
let maxVisibleIndex = min(self.mediaItems.count - 1, maxVisibleRow * itemsInRow - 1)
|
||||||
|
|
||||||
|
var validIds = Set<UInt32>()
|
||||||
|
if minVisibleIndex < maxVisibleIndex {
|
||||||
|
for i in minVisibleIndex ... maxVisibleIndex {
|
||||||
|
let stableId = self.mediaItems[i].message.stableId
|
||||||
|
validIds.insert(stableId)
|
||||||
|
let rowIndex = i / Int(itemsInRow)
|
||||||
|
let columnIndex = i % Int(itemsInRow)
|
||||||
|
let itemOrigin = CGPoint(x: CGFloat(columnIndex) * (itemSize + itemSpacing), y: itemSpacing + CGFloat(rowIndex) * (itemSize + itemSpacing))
|
||||||
|
let itemFrame = CGRect(origin: itemOrigin, size: CGSize(width: columnIndex == itemsInRow ? (size.width - itemOrigin.x) : itemSize, height: itemSize))
|
||||||
|
let itemNode: VisualMediaItemNode
|
||||||
|
if let current = self.visibleMediaItems[stableId] {
|
||||||
|
itemNode = current
|
||||||
|
} else {
|
||||||
|
itemNode = VisualMediaItemNode(context: self.context, interaction: self.itemInteraction)
|
||||||
|
self.visibleMediaItems[stableId] = itemNode
|
||||||
|
self.scrollNode.addSubnode(itemNode)
|
||||||
|
}
|
||||||
|
itemNode.frame = itemFrame
|
||||||
|
itemNode.update(size: itemFrame.size, item: self.mediaItems[i], theme: theme, synchronousLoad: synchronousLoad)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var removeKeys: [UInt32] = []
|
||||||
|
for (id, _) in self.visibleMediaItems {
|
||||||
|
if !validIds.contains(id) {
|
||||||
|
removeKeys.append(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for id in removeKeys {
|
||||||
|
if let itemNode = self.visibleMediaItems.removeValue(forKey: id) {
|
||||||
|
itemNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -1244,3 +1244,20 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let defaultChatControllerInteraction = ChatControllerInteraction.default
|
private let defaultChatControllerInteraction = ChatControllerInteraction.default
|
||||||
|
|
||||||
|
private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool) -> ViewController? {
|
||||||
|
if let _ = peer as? TelegramGroup {
|
||||||
|
return groupInfoController(context: context, peerId: peer.id)
|
||||||
|
} else if let channel = peer as? TelegramChannel {
|
||||||
|
if case .group = channel.info {
|
||||||
|
return groupInfoController(context: context, peerId: peer.id)
|
||||||
|
} else {
|
||||||
|
return channelInfoController(context: context, peerId: peer.id)
|
||||||
|
}
|
||||||
|
} else if peer is TelegramUser {
|
||||||
|
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded)
|
||||||
|
} else if peer is TelegramSecretChat {
|
||||||
|
return userInfoController(context: context, peerId: peer.id, mode: mode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user