mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-31 15:37:01 +00:00
User profile info improvements
This commit is contained in:
parent
73987dff5d
commit
d677ee44bb
@ -0,0 +1,401 @@
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import TextFormat
|
||||
import UIKit
|
||||
import AppBundle
|
||||
import TelegramStringFormatting
|
||||
import ContextUI
|
||||
|
||||
final class PeerInfoScreenContactInfoItem: PeerInfoScreenItem {
|
||||
let id: AnyHashable
|
||||
let username: String
|
||||
let phoneNumber: String
|
||||
let additionalText: String?
|
||||
let usernameAction: ((ASDisplayNode) -> Void)?
|
||||
let usernameLongTapAction: ((ASDisplayNode) -> Void)?
|
||||
let phoneAction: ((ASDisplayNode) -> Void)?
|
||||
let phoneLongTapAction: ((ASDisplayNode) -> Void)?
|
||||
let linkItemAction: ((TextLinkItemActionType, TextLinkItem, ASDisplayNode, CGRect?) -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
let requestLayout: () -> Void
|
||||
|
||||
init(
|
||||
id: AnyHashable,
|
||||
username: String,
|
||||
phoneNumber: String,
|
||||
additionalText: String? = nil,
|
||||
usernameAction: ((ASDisplayNode) -> Void)?,
|
||||
usernameLongTapAction: ((ASDisplayNode) -> Void)?,
|
||||
phoneAction: ((ASDisplayNode) -> Void)?,
|
||||
phoneLongTapAction: ((ASDisplayNode) -> Void)?,
|
||||
linkItemAction: ((TextLinkItemActionType, TextLinkItem, ASDisplayNode, CGRect?) -> Void)? = nil,
|
||||
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil,
|
||||
requestLayout: @escaping () -> Void
|
||||
) {
|
||||
self.id = id
|
||||
self.username = username
|
||||
self.phoneNumber = phoneNumber
|
||||
self.additionalText = additionalText
|
||||
self.usernameAction = usernameAction
|
||||
self.usernameLongTapAction = usernameLongTapAction
|
||||
self.phoneAction = phoneAction
|
||||
self.phoneLongTapAction = phoneLongTapAction
|
||||
self.linkItemAction = linkItemAction
|
||||
self.contextAction = contextAction
|
||||
self.requestLayout = requestLayout
|
||||
}
|
||||
|
||||
func node() -> PeerInfoScreenItemNode {
|
||||
return PeerInfoScreenContactInfoItemNode()
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerInfoScreenContactInfoItemNode: PeerInfoScreenItemNode {
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
private let contextSourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
private let extractedBackgroundImageNode: ASImageNode
|
||||
|
||||
private var extractedRect: CGRect?
|
||||
private var nonExtractedRect: CGRect?
|
||||
|
||||
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
|
||||
private let maskNode: ASImageNode
|
||||
private let usernameNode: ImmediateTextNode
|
||||
private let phoneNumberNode: ImmediateTextNode
|
||||
private let additionalTextNode: ImmediateTextNode
|
||||
private let measureTextNode: ImmediateTextNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
|
||||
private var linkHighlightingNode: LinkHighlightingNode?
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var item: PeerInfoScreenContactInfoItem?
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
override init() {
|
||||
var bringToFrontForHighlightImpl: (() -> Void)?
|
||||
|
||||
self.contextSourceNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.extractedBackgroundImageNode = ASImageNode()
|
||||
self.extractedBackgroundImageNode.displaysAsynchronously = false
|
||||
self.extractedBackgroundImageNode.alpha = 0.0
|
||||
|
||||
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
|
||||
self.selectionNode.isUserInteractionEnabled = false
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
self.maskNode.isUserInteractionEnabled = false
|
||||
|
||||
self.usernameNode = ImmediateTextNode()
|
||||
self.usernameNode.displaysAsynchronously = false
|
||||
self.usernameNode.isUserInteractionEnabled = false
|
||||
|
||||
self.phoneNumberNode = ImmediateTextNode()
|
||||
self.phoneNumberNode.displaysAsynchronously = false
|
||||
self.phoneNumberNode.isUserInteractionEnabled = false
|
||||
|
||||
self.additionalTextNode = ImmediateTextNode()
|
||||
self.additionalTextNode.displaysAsynchronously = false
|
||||
self.additionalTextNode.isUserInteractionEnabled = false
|
||||
|
||||
self.measureTextNode = ImmediateTextNode()
|
||||
self.measureTextNode.displaysAsynchronously = false
|
||||
self.measureTextNode.isUserInteractionEnabled = false
|
||||
|
||||
self.bottomSeparatorNode = ASDisplayNode()
|
||||
self.bottomSeparatorNode.isLayerBacked = true
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init()
|
||||
|
||||
bringToFrontForHighlightImpl = { [weak self] in
|
||||
self?.bringToFrontForHighlight?()
|
||||
}
|
||||
|
||||
self.addSubnode(self.bottomSeparatorNode)
|
||||
self.addSubnode(self.selectionNode)
|
||||
|
||||
self.containerNode.addSubnode(self.contextSourceNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
|
||||
self.addSubnode(self.containerNode)
|
||||
|
||||
self.addSubnode(self.maskNode)
|
||||
|
||||
self.contextSourceNode.contentNode.addSubnode(self.extractedBackgroundImageNode)
|
||||
|
||||
self.contextSourceNode.contentNode.addSubnode(self.usernameNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.phoneNumberNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.additionalTextNode)
|
||||
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
self.containerNode.activated = { [weak self] gesture, _ in
|
||||
guard let strongSelf = self, let item = strongSelf.item, let contextAction = item.contextAction else {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
contextAction(strongSelf.contextSourceNode, gesture, nil)
|
||||
}
|
||||
|
||||
self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||
guard let strongSelf = self, let theme = strongSelf.theme else {
|
||||
return
|
||||
}
|
||||
|
||||
if isExtracted {
|
||||
strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.list.plainBackgroundColor)
|
||||
}
|
||||
|
||||
if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect {
|
||||
let rect = isExtracted ? extractedRect : nonExtractedRect
|
||||
transition.updateFrame(node: strongSelf.extractedBackgroundImageNode, frame: rect)
|
||||
}
|
||||
|
||||
transition.updateAlpha(node: strongSelf.extractedBackgroundImageNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in
|
||||
if !isExtracted {
|
||||
self?.extractedBackgroundImageNode.image = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
|
||||
recognizer.tapActionAtPoint = { [weak self] point in
|
||||
guard let strongSelf = self else {
|
||||
return .keepWithSingleTap
|
||||
}
|
||||
if let _ = strongSelf.linkItemAtPoint(point) {
|
||||
return .waitForSingleTap
|
||||
}
|
||||
return .waitForSingleTap
|
||||
}
|
||||
recognizer.highlight = { [weak self] point in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateTouchesAtPoint(point)
|
||||
}
|
||||
self.view.addGestureRecognizer(recognizer)
|
||||
}
|
||||
|
||||
@objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .tap, .longTap:
|
||||
if let item = self.item {
|
||||
if let linkItem = self.linkItemAtPoint(location) {
|
||||
item.linkItemAction?(gesture == .tap ? .tap : .longTap, linkItem, self.linkHighlightingNode ?? self, self.linkHighlightingNode?.rects.first)
|
||||
} else if case .longTap = gesture {
|
||||
if self.usernameNode.frame.insetBy(dx: -10.0, dy: -10.0).contains(location) {
|
||||
item.usernameLongTapAction?(self.usernameNode)
|
||||
} else if self.phoneNumberNode.frame.insetBy(dx: -10.0, dy: -10.0).contains(location) {
|
||||
item.phoneLongTapAction?(self.phoneNumberNode)
|
||||
}
|
||||
} else if case .tap = gesture {
|
||||
if self.usernameNode.frame.insetBy(dx: -10.0, dy: -10.0).contains(location) {
|
||||
item.usernameAction?(self.contextSourceNode)
|
||||
} else if self.phoneNumberNode.frame.insetBy(dx: -10.0, dy: -10.0).contains(location) {
|
||||
item.phoneAction?(self.contextSourceNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func linkItemAtPoint(_ point: CGPoint) -> TextLinkItem? {
|
||||
let additionalTextNodeFrame = self.additionalTextNode.frame
|
||||
if let (_, attributes) = self.additionalTextNode.attributesAtPoint(CGPoint(x: point.x - additionalTextNodeFrame.minX, y: point.y - additionalTextNodeFrame.minY)) {
|
||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
return .url(url: url, concealed: false)
|
||||
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
||||
return .mention(peerName)
|
||||
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
||||
return .hashtag(hashtag.peerName, hashtag.hashtag)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, hasCorners: Bool, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
guard let item = item as? PeerInfoScreenContactInfoItem else {
|
||||
return 10.0
|
||||
}
|
||||
|
||||
self.item = item
|
||||
self.theme = presentationData.theme
|
||||
|
||||
// if let action = item.action {
|
||||
// self.selectionNode.pressed = { [weak self] in
|
||||
// if let strongSelf = self {
|
||||
// action(strongSelf.contextSourceNode)
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// self.selectionNode.pressed = nil
|
||||
// }
|
||||
|
||||
let sideInset: CGFloat = 16.0 + safeInsets.left
|
||||
|
||||
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
self.usernameNode.attributedText = NSAttributedString(string: item.username, font: Font.regular(15.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||
|
||||
self.phoneNumberNode.maximumNumberOfLines = 1
|
||||
self.phoneNumberNode.cutout = nil
|
||||
self.phoneNumberNode.attributedText = NSAttributedString(string: item.phoneNumber, font: Font.regular(15.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
|
||||
let fontSize: CGFloat = 15.0
|
||||
|
||||
let baseFont = Font.regular(fontSize)
|
||||
let linkFont = baseFont
|
||||
let boldFont = Font.medium(fontSize)
|
||||
let italicFont = Font.italic(fontSize)
|
||||
let boldItalicFont = Font.semiboldItalic(fontSize)
|
||||
let titleFixedFont = Font.monospace(fontSize)
|
||||
|
||||
if let additionalText = item.additionalText {
|
||||
let entities = generateTextEntities(additionalText, enabledTypes: [.mention])
|
||||
let attributedAdditionalText = stringWithAppliedEntities(additionalText, entities: entities, baseColor: presentationData.theme.list.itemPrimaryTextColor, linkColor: presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont, underlineLinks: false, message: nil)
|
||||
|
||||
self.additionalTextNode.maximumNumberOfLines = 10
|
||||
self.additionalTextNode.attributedText = attributedAdditionalText
|
||||
} else {
|
||||
self.additionalTextNode.attributedText = nil
|
||||
}
|
||||
|
||||
|
||||
let usernameSize = self.usernameNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
let phoneSize = self.phoneNumberNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
|
||||
let additionalTextSize = self.additionalTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
|
||||
let topOffset = 12.0
|
||||
var height = topOffset * 2.0
|
||||
let usernameFrame = CGRect(origin: CGPoint(x: sideInset, y: topOffset), size: usernameSize)
|
||||
let phoneFrame = CGRect(origin: CGPoint(x: usernameSize.width > 0.0 ? width - sideInset - phoneSize.width : sideInset, y: topOffset), size: phoneSize)
|
||||
height += max(usernameSize.height, phoneSize.height)
|
||||
|
||||
let additionalTextFrame = CGRect(origin: CGPoint(x: sideInset, y: topOffset), size: additionalTextSize)
|
||||
transition.updateFrame(node: self.usernameNode, frame: usernameFrame)
|
||||
|
||||
transition.updateFrame(node: self.phoneNumberNode, frame: phoneFrame)
|
||||
|
||||
transition.updateFrame(node: self.additionalTextNode, frame: additionalTextFrame)
|
||||
|
||||
if additionalTextSize.height > 0.0 {
|
||||
height += additionalTextSize.height + 3.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)
|
||||
|
||||
let hasCorners = hasCorners && (topItem == nil || bottomItem == nil)
|
||||
let hasTopCorners = hasCorners && topItem == nil
|
||||
let hasBottomCorners = hasCorners && bottomItem == nil
|
||||
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
self.maskNode.frame = CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))
|
||||
self.bottomSeparatorNode.isHidden = hasBottomCorners
|
||||
|
||||
self.activateArea.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: height))
|
||||
self.activateArea.accessibilityLabel = item.username
|
||||
self.activateArea.accessibilityValue = item.phoneNumber
|
||||
|
||||
let contentSize = CGSize(width: width, height: height)
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
self.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
self.containerNode.isGestureEnabled = item.contextAction != nil
|
||||
|
||||
let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: contentSize.height))
|
||||
let extractedRect = nonExtractedRect
|
||||
self.extractedRect = extractedRect
|
||||
self.nonExtractedRect = nonExtractedRect
|
||||
|
||||
if self.contextSourceNode.isExtractedToContextPreview {
|
||||
self.extractedBackgroundImageNode.frame = extractedRect
|
||||
} else {
|
||||
self.extractedBackgroundImageNode.frame = nonExtractedRect
|
||||
}
|
||||
self.contextSourceNode.contentRect = extractedRect
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
private func updateTouchesAtPoint(_ point: CGPoint?) {
|
||||
guard let _ = self.item, let theme = self.theme else {
|
||||
return
|
||||
}
|
||||
var rects: [CGRect]?
|
||||
var textNode: ASDisplayNode?
|
||||
if let point = point {
|
||||
if self.usernameNode.frame.insetBy(dx: -10.0, dy: -10.0).contains(point) {
|
||||
textNode = self.usernameNode
|
||||
rects = [self.usernameNode.bounds]
|
||||
} else if self.phoneNumberNode.frame.insetBy(dx: -10.0, dy: -10.0).contains(point) {
|
||||
textNode = self.phoneNumberNode
|
||||
rects = [self.phoneNumberNode.bounds]
|
||||
} else if self.additionalTextNode.frame.contains(point) {
|
||||
let mappedPoint = CGPoint(x: point.x - self.additionalTextNode.frame.minX, y: point.y - self.additionalTextNode.frame.minY)
|
||||
if mappedPoint.y > 0.0, let (index, attributes) = self.additionalTextNode.attributesAtPoint(mappedPoint) {
|
||||
let possibleNames: [String] = [
|
||||
TelegramTextAttributes.URL,
|
||||
TelegramTextAttributes.PeerMention,
|
||||
TelegramTextAttributes.PeerTextMention,
|
||||
TelegramTextAttributes.BotCommand,
|
||||
TelegramTextAttributes.Hashtag
|
||||
]
|
||||
for name in possibleNames {
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: name)] {
|
||||
rects = self.additionalTextNode.attributeRects(name: name, at: index)
|
||||
textNode = self.additionalTextNode
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let rects = rects, let textNode = textNode {
|
||||
let linkHighlightingNode: LinkHighlightingNode
|
||||
if let current = self.linkHighlightingNode {
|
||||
linkHighlightingNode = current
|
||||
} else {
|
||||
linkHighlightingNode = LinkHighlightingNode(color: theme.list.itemAccentColor.withAlphaComponent(0.5))
|
||||
self.linkHighlightingNode = linkHighlightingNode
|
||||
self.contextSourceNode.contentNode.insertSubnode(linkHighlightingNode, belowSubnode: textNode)
|
||||
}
|
||||
linkHighlightingNode.frame = textNode.frame
|
||||
linkHighlightingNode.updateRects(rects)
|
||||
} else if let linkHighlightingNode = self.linkHighlightingNode {
|
||||
self.linkHighlightingNode = nil
|
||||
linkHighlightingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak linkHighlightingNode] _ in
|
||||
linkHighlightingNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -472,9 +472,19 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
self.expandButonNode.isHidden = true
|
||||
}
|
||||
|
||||
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)
|
||||
let additionalTextFrame = CGRect(origin: CGPoint(x: sideInset, y: textFrame.maxY + 3.0), size: additionalTextSize)
|
||||
var topOffset = 11.0
|
||||
var height = topOffset * 2.0
|
||||
let labelFrame = CGRect(origin: CGPoint(x: sideInset, y: topOffset), size: labelSize)
|
||||
if labelSize.height > 0.0 {
|
||||
topOffset += labelSize.height + 3.0
|
||||
height += labelSize.height + 3.0
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: topOffset), size: textSize)
|
||||
if textSize.height > 0.0 {
|
||||
topOffset += textSize.height + 3.0
|
||||
height += textSize.height
|
||||
}
|
||||
let additionalTextFrame = CGRect(origin: CGPoint(x: sideInset, y: topOffset), size: additionalTextSize)
|
||||
|
||||
let expandFrame = CGRect(origin: CGPoint(x: width - safeInsets.right - expandSize.width - 14.0 - UIScreenPixel, y: textFrame.maxY - expandSize.height), size: expandSize)
|
||||
self.expandNode.frame = expandFrame
|
||||
@ -496,8 +506,6 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
|
||||
transition.updateFrame(node: self.additionalTextNode, frame: additionalTextFrame)
|
||||
|
||||
var height = labelSize.height + 3.0 + textSize.height + 22.0
|
||||
|
||||
let iconButtonFrame = CGRect(x: width - safeInsets.right - height, y: 0.0, width: height, height: height)
|
||||
transition.updateFrame(node: self.iconButtonNode, frame: iconButtonFrame)
|
||||
if let iconSize = self.iconNode.image?.size {
|
||||
|
@ -1142,10 +1142,18 @@ func peerInfoHeaderButtonIsHiddenWhileExpanded(buttonKey: PeerInfoHeaderButtonKe
|
||||
return hiddenWhileExpanded
|
||||
}
|
||||
|
||||
func peerInfoHeaderActionButtons(peer: Peer?, isSecretChat: Bool, isContact: Bool) -> [PeerInfoHeaderButtonKey] {
|
||||
var result: [PeerInfoHeaderButtonKey] = []
|
||||
if !isContact && !isSecretChat, let user = peer as? TelegramUser, user.botInfo == nil {
|
||||
result = [.message, .addContact]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFromChat: Bool, isExpanded: Bool, videoCallsEnabled: Bool, isSecretChat: Bool, isContact: Bool, threadInfo: EngineMessageHistoryThread.Info?) -> [PeerInfoHeaderButtonKey] {
|
||||
var result: [PeerInfoHeaderButtonKey] = []
|
||||
if let user = peer as? TelegramUser {
|
||||
if !isOpenedFromChat {
|
||||
if !isOpenedFromChat && isContact {
|
||||
result.append(.message)
|
||||
}
|
||||
var callsAvailable = false
|
||||
|
@ -44,6 +44,7 @@ enum PeerInfoHeaderButtonKey: Hashable {
|
||||
case search
|
||||
case leave
|
||||
case stop
|
||||
case addContact
|
||||
}
|
||||
|
||||
enum PeerInfoHeaderButtonIcon {
|
||||
@ -135,7 +136,7 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
||||
self.action(self, nil)
|
||||
}
|
||||
|
||||
func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, isExpanded: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
let previousIcon = self.icon
|
||||
let themeUpdated = self.theme != presentationData.theme
|
||||
let iconUpdated = self.icon != icon
|
||||
@ -288,6 +289,88 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerInfoHeaderActionButtonNode: HighlightableButtonNode {
|
||||
let key: PeerInfoHeaderButtonKey
|
||||
private let action: (PeerInfoHeaderActionButtonNode, ContextGesture?) -> Void
|
||||
let referenceNode: ContextReferenceContentNode
|
||||
let containerNode: ContextControllerSourceNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
init(key: PeerInfoHeaderButtonKey, action: @escaping (PeerInfoHeaderActionButtonNode, ContextGesture?) -> Void) {
|
||||
self.key = key
|
||||
self.action = action
|
||||
|
||||
self.referenceNode = ContextReferenceContentNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
self.containerNode.animateScale = false
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.cornerRadius = 11.0
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.accessibilityTraits = .button
|
||||
|
||||
self.containerNode.addSubnode(self.referenceNode)
|
||||
self.referenceNode.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.containerNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.alpha = 1.0
|
||||
strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.containerNode.activated = { [weak self] gesture, _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.action(strongSelf, gesture)
|
||||
}
|
||||
}
|
||||
|
||||
self.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.action(self, nil)
|
||||
}
|
||||
|
||||
func update(size: CGSize, text: String, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
let themeUpdated = self.theme != presentationData.theme
|
||||
if themeUpdated {
|
||||
self.theme = presentationData.theme
|
||||
|
||||
self.containerNode.isGestureEnabled = false
|
||||
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.list.itemAccentColor
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.semibold(16.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
self.accessibilityLabel = text
|
||||
let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude))
|
||||
|
||||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize))
|
||||
|
||||
self.referenceNode.frame = self.containerNode.bounds
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerInfoHeaderNavigationTransition {
|
||||
let sourceNavigationBar: NavigationBar
|
||||
let sourceTitleView: ChatTitleView
|
||||
@ -2285,6 +2368,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let usernameNodeContainer: ASDisplayNode
|
||||
let usernameNodeRawContainer: ASDisplayNode
|
||||
let usernameNode: MultiScaleTextNode
|
||||
var actionButtonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderActionButtonNode] = [:]
|
||||
var buttonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderButtonNode] = [:]
|
||||
let backgroundNode: NavigationBackgroundNode
|
||||
let expandedBackgroundNode: NavigationBackgroundNode
|
||||
@ -2834,6 +2918,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight)
|
||||
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
|
||||
|
||||
let actionButtonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact)
|
||||
let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info)
|
||||
|
||||
var isPremium = false
|
||||
@ -3497,14 +3582,69 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
let buttonSpacing: CGFloat = 8.0
|
||||
let buttonSideInset = max(16.0, containerInset)
|
||||
var buttonRightOrigin = CGPoint(x: width - buttonSideInset, y: maxY + 25.0 - navigationHeight - UIScreenPixel)
|
||||
|
||||
var actionButtonRightOrigin = CGPoint(x: width - buttonSideInset, y: maxY + 24.0 - navigationHeight - UIScreenPixel)
|
||||
let actionButtonWidth = (width - buttonSideInset * 2.0 + buttonSpacing) / CGFloat(actionButtonKeys.count) - buttonSpacing
|
||||
let actionButtonSize = CGSize(width: actionButtonWidth, height: 40.0)
|
||||
|
||||
for buttonKey in actionButtonKeys.reversed() {
|
||||
let buttonNode: PeerInfoHeaderActionButtonNode
|
||||
var wasAdded = false
|
||||
if let current = self.actionButtonNodes[buttonKey] {
|
||||
buttonNode = current
|
||||
} else {
|
||||
wasAdded = true
|
||||
buttonNode = PeerInfoHeaderActionButtonNode(key: buttonKey, action: { [weak self] buttonNode, gesture in
|
||||
self?.actionButtonPressed(buttonNode, gesture: gesture)
|
||||
})
|
||||
self.actionButtonNodes[buttonKey] = buttonNode
|
||||
self.buttonsContainerNode.addSubnode(buttonNode)
|
||||
}
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: actionButtonRightOrigin.x - actionButtonSize.width, y: actionButtonRightOrigin.y), size: actionButtonSize)
|
||||
let buttonTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition
|
||||
|
||||
if additive {
|
||||
buttonTransition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame)
|
||||
} else {
|
||||
buttonTransition.updateFrame(node: buttonNode, frame: buttonFrame)
|
||||
}
|
||||
let buttonText: String
|
||||
switch buttonKey {
|
||||
case .message:
|
||||
buttonText = "Message"
|
||||
case .addContact:
|
||||
buttonText = "Add"
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
|
||||
buttonNode.update(size: buttonFrame.size, text: buttonText, presentationData: presentationData, transition: buttonTransition)
|
||||
|
||||
if wasAdded {
|
||||
buttonNode.alpha = 0.0
|
||||
}
|
||||
transition.updateAlpha(node: buttonNode, alpha: 1.0)
|
||||
actionButtonRightOrigin.x -= actionButtonSize.width + buttonSpacing
|
||||
}
|
||||
|
||||
for key in self.actionButtonNodes.keys {
|
||||
if !actionButtonKeys.contains(key) {
|
||||
if let buttonNode = self.actionButtonNodes[key] {
|
||||
self.actionButtonNodes.removeValue(forKey: key)
|
||||
transition.updateAlpha(node: buttonNode, alpha: 0.0) { [weak buttonNode] _ in
|
||||
buttonNode?.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buttonRightOrigin = CGPoint(x: width - buttonSideInset, y: maxY + 24.0 - navigationHeight - UIScreenPixel)
|
||||
if !actionButtonKeys.isEmpty {
|
||||
buttonRightOrigin.y += actionButtonSize.height + 24.0
|
||||
}
|
||||
let buttonWidth = (width - buttonSideInset * 2.0 + buttonSpacing) / CGFloat(buttonKeys.count) - buttonSpacing
|
||||
|
||||
let apparentButtonSize = CGSize(width: buttonWidth, height: 58.0)
|
||||
let buttonsAlpha: CGFloat = 1.0
|
||||
let buttonsVerticalOffset: CGFloat = 0.0
|
||||
|
||||
let buttonsAlphaTransition = transition
|
||||
let buttonSize = CGSize(width: buttonWidth, height: 58.0)
|
||||
|
||||
for buttonKey in buttonKeys.reversed() {
|
||||
let buttonNode: PeerInfoHeaderButtonNode
|
||||
@ -3520,14 +3660,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.buttonsContainerNode.addSubnode(buttonNode)
|
||||
}
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonRightOrigin.x - apparentButtonSize.width, y: buttonRightOrigin.y), size: apparentButtonSize)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonRightOrigin.x - buttonSize.width, y: buttonRightOrigin.y), size: buttonSize)
|
||||
let buttonTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition
|
||||
|
||||
let apparentButtonFrame = buttonFrame.offsetBy(dx: 0.0, dy: buttonsVerticalOffset)
|
||||
if additive {
|
||||
buttonTransition.updateFrameAdditiveToCenter(node: buttonNode, frame: apparentButtonFrame)
|
||||
buttonTransition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame)
|
||||
} else {
|
||||
buttonTransition.updateFrame(node: buttonNode, frame: apparentButtonFrame)
|
||||
buttonTransition.updateFrame(node: buttonNode, frame: buttonFrame)
|
||||
}
|
||||
let buttonText: String
|
||||
let buttonIcon: PeerInfoHeaderButtonIcon
|
||||
@ -3575,6 +3714,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
case .stop:
|
||||
buttonText = presentationData.strings.PeerInfo_ButtonStop
|
||||
buttonIcon = .stop
|
||||
case .addContact:
|
||||
fatalError()
|
||||
}
|
||||
|
||||
var isActive = true
|
||||
@ -3582,12 +3723,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
isActive = buttonKey == highlightedButton
|
||||
}
|
||||
|
||||
buttonNode.update(size: buttonFrame.size, text: buttonText, icon: buttonIcon, isActive: isActive, isExpanded: false, presentationData: presentationData, transition: buttonTransition)
|
||||
buttonNode.update(size: buttonFrame.size, text: buttonText, icon: buttonIcon, isActive: isActive, presentationData: presentationData, transition: buttonTransition)
|
||||
|
||||
if wasAdded {
|
||||
buttonNode.alpha = 0.0
|
||||
}
|
||||
buttonsAlphaTransition.updateAlpha(node: buttonNode, alpha: buttonsAlpha)
|
||||
transition.updateAlpha(node: buttonNode, alpha: 1.0)
|
||||
|
||||
if case .mute = buttonKey, buttonNode.containerNode.alpha.isZero, additive {
|
||||
if case let .animated(duration, curve) = transition {
|
||||
@ -3598,7 +3739,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
} else {
|
||||
transition.updateAlpha(node: buttonNode.containerNode, alpha: 1.0)
|
||||
}
|
||||
buttonRightOrigin.x -= apparentButtonSize.width + buttonSpacing
|
||||
buttonRightOrigin.x -= buttonSize.width + buttonSpacing
|
||||
}
|
||||
|
||||
for key in self.buttonNodes.keys {
|
||||
@ -3622,7 +3763,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let backgroundFrame: CGRect
|
||||
let separatorFrame: CGRect
|
||||
|
||||
let resolvedHeight: CGFloat
|
||||
var resolvedHeight: CGFloat
|
||||
|
||||
if state.isEditing {
|
||||
resolvedHeight = editingContentHeight
|
||||
@ -3635,7 +3776,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.regularContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: resolvedHeight)))
|
||||
transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationHeight + UIScreenPixel), size: CGSize(width: width, height: resolvedHeight - navigationHeight + 180.0)))
|
||||
transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationHeight + UIScreenPixel), size: CGSize(width: width, height: resolvedHeight - navigationHeight + 500.0)))
|
||||
|
||||
if additive {
|
||||
transition.updateFrameAdditive(node: self.backgroundNode, frame: backgroundFrame)
|
||||
@ -3651,6 +3792,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
transition.updateFrame(node: self.separatorNode, frame: separatorFrame)
|
||||
}
|
||||
|
||||
if !state.isEditing && !isSettings {
|
||||
resolvedHeight += 71.0
|
||||
|
||||
if !actionButtonKeys.isEmpty {
|
||||
resolvedHeight += 64.0
|
||||
}
|
||||
}
|
||||
|
||||
if isFirstTime {
|
||||
self.updateAvatarMask(transition: .immediate)
|
||||
}
|
||||
@ -3662,6 +3811,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.performButtonAction?(buttonNode.key, gesture)
|
||||
}
|
||||
|
||||
private func actionButtonPressed(_ buttonNode: PeerInfoHeaderActionButtonNode, gesture: ContextGesture?) {
|
||||
self.performButtonAction?(buttonNode.key, gesture)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
guard let result = super.hitTest(point, with: event) else {
|
||||
return nil
|
||||
|
@ -976,7 +976,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
|
||||
return result
|
||||
}
|
||||
|
||||
private func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], chatLocation: ChatLocation) -> [(AnyHashable, [PeerInfoScreenItem])] {
|
||||
private func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], chatLocation: ChatLocation, isOpenedFromChat: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] {
|
||||
guard let data = data else {
|
||||
return []
|
||||
}
|
||||
@ -1005,39 +1005,63 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
items[.calls]!.append(PeerInfoScreenCallListItem(id: 20, messages: callMessages))
|
||||
}
|
||||
|
||||
if let phone = user.phone {
|
||||
let formattedPhone = formatPhoneNumber(context: context, number: phone)
|
||||
let label: String
|
||||
if formattedPhone.hasPrefix("+888 ") {
|
||||
label = presentationData.strings.UserInfo_AnonymousNumberLabel
|
||||
} else {
|
||||
label = presentationData.strings.ContactInfo_PhoneLabelMobile
|
||||
}
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 2, label: label, text: formattedPhone, textColor: .accent, action: { node in
|
||||
interaction.openPhone(phone, node, nil)
|
||||
}, longTapAction: nil, contextAction: { node, gesture, _ in
|
||||
interaction.openPhone(phone, node, gesture)
|
||||
}, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
}
|
||||
var username: String?
|
||||
var additionalUsernames: String?
|
||||
var phoneNumber: String?
|
||||
if let mainUsername = user.addressName {
|
||||
var additionalUsernames: String?
|
||||
username = mainUsername
|
||||
let usernames = user.usernames.filter { $0.isActive && $0.username != mainUsername }
|
||||
if !usernames.isEmpty {
|
||||
additionalUsernames = presentationData.strings.Profile_AdditionalUsernames(String(usernames.map { "@\($0.username)" }.joined(separator: ", "))).string
|
||||
}
|
||||
|
||||
}
|
||||
if let phone = user.phone {
|
||||
phoneNumber = formatPhoneNumber(context: context, number: phone)
|
||||
if let phone = phoneNumber, !phone.isEmpty && !phone.hasPrefix("+") {
|
||||
phoneNumber = "+\(phone)"
|
||||
}
|
||||
}
|
||||
|
||||
if user.botInfo == nil {
|
||||
if username != nil || phoneNumber != nil {
|
||||
items[.peerInfo]!.append(PeerInfoScreenContactInfoItem(
|
||||
id: 1,
|
||||
username: username.flatMap { "@\($0)" } ?? "",
|
||||
phoneNumber: phoneNumber ?? "",
|
||||
additionalText: additionalUsernames,
|
||||
usernameAction: { _ in
|
||||
interaction.openUsername(username ?? "")
|
||||
},
|
||||
usernameLongTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link(customLink: nil), sourceNode, nil)
|
||||
},
|
||||
phoneAction: { node in
|
||||
interaction.openPhone(phoneNumber ?? "", node, nil)
|
||||
},
|
||||
phoneLongTapAction: { _ in },
|
||||
linkItemAction: { type, item, _, _ in
|
||||
if case .tap = type {
|
||||
if case let .mention(username) = item {
|
||||
interaction.openUsername(String(username[username.index(username.startIndex, offsetBy: 1)...]))
|
||||
}
|
||||
}
|
||||
},
|
||||
requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}
|
||||
))
|
||||
}
|
||||
} else if let username {
|
||||
items[.peerInfo]!.append(
|
||||
PeerInfoScreenLabeledValueItem(
|
||||
id: 1,
|
||||
label: presentationData.strings.Profile_Username,
|
||||
text: "@\(mainUsername)",
|
||||
label: "",
|
||||
text: "@\(username)",
|
||||
additionalText: additionalUsernames,
|
||||
textColor: .accent,
|
||||
icon: .qrCode,
|
||||
action: { _ in
|
||||
interaction.openUsername(mainUsername)
|
||||
interaction.openUsername(username)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link(customLink: nil), sourceNode, nil)
|
||||
}, linkItemAction: { type, item, _, _ in
|
||||
@ -1054,17 +1078,18 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if let cachedData = data.cachedData as? CachedUserData {
|
||||
if user.isFake {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: "", text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
} else if user.isScam {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: "", text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
} else if let about = cachedData.about, !about.isEmpty {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.isPremium ? enabledPublicBioEntities : enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: "", text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.isPremium ? enabledPublicBioEntities : enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
}
|
||||
@ -1086,14 +1111,6 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
interaction.openReport(.user)
|
||||
}))
|
||||
} else {
|
||||
if !data.isContact {
|
||||
if user.botInfo == nil {
|
||||
items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 3, text: presentationData.strings.PeerInfo_AddToContacts, action: {
|
||||
interaction.openAddContact()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
var isBlocked = false
|
||||
if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked {
|
||||
isBlocked = true
|
||||
@ -1106,7 +1123,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
} else {
|
||||
if user.flags.contains(.isSupport) || data.isContact {
|
||||
} else {
|
||||
if user.botInfo == nil {
|
||||
if user.botInfo == nil && isOpenedFromChat {
|
||||
items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 4, text: presentationData.strings.Conversation_BlockUser, color: .destructive, action: {
|
||||
interaction.updateBlocked(true)
|
||||
}))
|
||||
@ -1162,7 +1179,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
items[.peerInfo]!.append(
|
||||
PeerInfoScreenLabeledValueItem(
|
||||
id: ItemUsername,
|
||||
label: presentationData.strings.Channel_LinkItem,
|
||||
label: "",
|
||||
text: linkText,
|
||||
textColor: .accent,
|
||||
icon: .qrCode,
|
||||
@ -1214,7 +1231,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
items[.peerInfo]!.append(
|
||||
PeerInfoScreenLabeledValueItem(
|
||||
id: ItemUsername,
|
||||
label: presentationData.strings.Channel_LinkItem,
|
||||
label: "",
|
||||
text: "https://t.me/\(mainUsername)",
|
||||
additionalText: additionalUsernames,
|
||||
textColor: .accent,
|
||||
@ -1266,7 +1283,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
if case .group = channel.info {
|
||||
enabledEntities = enabledPrivateBioEntities
|
||||
}
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: "", text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
interaction.requestLayout(true)
|
||||
}))
|
||||
}
|
||||
@ -1312,7 +1329,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}
|
||||
|
||||
if let aboutText = aboutText {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: "", text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
interaction.requestLayout(true)
|
||||
}))
|
||||
}
|
||||
@ -5426,6 +5443,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
case .stop:
|
||||
self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: self.presentationData.strings.PeerInfo_BotBlockedTitle, text: self.presentationData.strings.PeerInfo_BotBlockedText, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||
self.updateBlocked(block: true)
|
||||
case .addContact:
|
||||
self.openAddContact()
|
||||
}
|
||||
}
|
||||
|
||||
@ -8886,10 +8905,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
let headerInset = sectionInset
|
||||
|
||||
var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive)
|
||||
if !self.isSettings && !self.state.isEditing {
|
||||
headerHeight += 71.0
|
||||
}
|
||||
let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive)
|
||||
let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: headerHeight))
|
||||
if additive {
|
||||
transition.updateFrameAdditive(node: self.headerNode, frame: headerFrame)
|
||||
@ -8906,11 +8922,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
insets.left += sectionInset
|
||||
insets.right += sectionInset
|
||||
|
||||
let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, chatLocation: self.chatLocation)
|
||||
let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, chatLocation: self.chatLocation, isOpenedFromChat: self.isOpenedFromChat)
|
||||
|
||||
contentHeight += headerHeight
|
||||
if !(self.isSettings && self.state.isEditing) {
|
||||
contentHeight += sectionSpacing
|
||||
contentHeight += sectionSpacing + 12.0
|
||||
}
|
||||
|
||||
for (sectionId, sectionItems) in items {
|
||||
|
Loading…
x
Reference in New Issue
Block a user