mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Initial implementation of the PSA API
This commit is contained in:
@@ -8,23 +8,94 @@ import SyncCore
|
||||
import TelegramPresentationData
|
||||
import LocalizedPeerData
|
||||
|
||||
enum ChatMessageForwardInfoType {
|
||||
enum ChatMessageForwardInfoType: Equatable {
|
||||
case bubble(incoming: Bool)
|
||||
case standalone
|
||||
}
|
||||
|
||||
private final class InfoButtonNode: HighlightableButtonNode {
|
||||
private let pressed: () -> Void
|
||||
let iconNode: ASImageNode
|
||||
|
||||
private var theme: ChatPresentationThemeData?
|
||||
private var type: ChatMessageForwardInfoType?
|
||||
|
||||
init(pressed: @escaping () -> Void) {
|
||||
self.pressed = pressed
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.iconNode)
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressedEvent), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func pressedEvent() {
|
||||
self.pressed()
|
||||
}
|
||||
|
||||
func update(size: CGSize, theme: ChatPresentationThemeData, type: ChatMessageForwardInfoType) {
|
||||
if self.theme !== theme || self.type != type {
|
||||
self.theme = theme
|
||||
self.type = type
|
||||
let color: UIColor
|
||||
switch type {
|
||||
case let .bubble(incoming):
|
||||
color = incoming ? theme.theme.chat.message.incoming.accentControlColor : theme.theme.chat.message.outgoing.accentControlColor
|
||||
case .standalone:
|
||||
let serviceColor = serviceMessageColorComponents(theme: theme.theme, wallpaper: theme.wallpaper)
|
||||
color = serviceColor.primaryText
|
||||
}
|
||||
self.iconNode.image = PresentationResourcesChat.chatPsaInfo(theme.theme, color: color.argb)
|
||||
}
|
||||
|
||||
if let image = self.iconNode.image {
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChatMessageForwardInfoNode: ASDisplayNode {
|
||||
private var textNode: TextNode?
|
||||
private var credibilityIconNode: ASImageNode?
|
||||
private var infoNode: InfoButtonNode?
|
||||
|
||||
var openPsa: ((String, ASDisplayNode) -> Void)?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
class func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer?, _ authorName: String?, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageForwardInfoNode) {
|
||||
func hasAction(at point: CGPoint) -> Bool {
|
||||
if let infoNode = self.infoNode, infoNode.frame.contains(point) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func updatePsaButtonDisplay(isVisible: Bool, animated: Bool) {
|
||||
if let infoNode = self.infoNode {
|
||||
if isVisible != !infoNode.iconNode.alpha.isZero {
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if animated {
|
||||
transition = .animated(duration: 0.25, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
transition.updateAlpha(node: infoNode.iconNode, alpha: isVisible ? 1.0 : 0.0)
|
||||
transition.updateSublayerTransformScale(node: infoNode, scale: isVisible ? 1.0 : 0.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer?, _ authorName: String?, _ psaType: String?, _ constrainedSize: CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode) {
|
||||
let textNodeLayout = TextNode.asyncLayout(maybeNode?.textNode)
|
||||
|
||||
return { presentationData, strings, type, peer, authorName, constrainedSize in
|
||||
return { presentationData, strings, type, peer, authorName, psaType, constrainedSize in
|
||||
let fontSize = floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0)
|
||||
let prefixFont = Font.regular(fontSize)
|
||||
let peerFont = Font.medium(fontSize)
|
||||
@@ -42,13 +113,23 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
|
||||
peerString = ""
|
||||
}
|
||||
|
||||
var hasPsaInfo = false
|
||||
if let _ = psaType {
|
||||
hasPsaInfo = true
|
||||
}
|
||||
|
||||
let titleColor: UIColor
|
||||
let completeSourceString: (String, [(Int, NSRange)])
|
||||
|
||||
switch type {
|
||||
case let .bubble(incoming):
|
||||
titleColor = incoming ? presentationData.theme.theme.chat.message.incoming.accentTextColor : presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||
completeSourceString = strings.Message_ForwardedMessage(peerString)
|
||||
if let _ = psaType {
|
||||
titleColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.barPositive : presentationData.theme.theme.chat.message.outgoing.polls.barPositive
|
||||
completeSourceString = strings.Message_GenericForwardedPsa(peerString)
|
||||
} else {
|
||||
titleColor = incoming ? presentationData.theme.theme.chat.message.incoming.accentTextColor : presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||
completeSourceString = strings.Message_ForwardedMessage(peerString)
|
||||
}
|
||||
case .standalone:
|
||||
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||
titleColor = serviceColor.primaryText
|
||||
@@ -91,9 +172,14 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
|
||||
credibilityIconWidth += icon.size.width + 4.0
|
||||
}
|
||||
|
||||
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - credibilityIconWidth, height: constrainedSize.height), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
var infoWidth: CGFloat = 0.0
|
||||
if hasPsaInfo {
|
||||
infoWidth += 44.0
|
||||
}
|
||||
|
||||
return (CGSize(width: textLayout.size.width + credibilityIconWidth, height: textLayout.size.height), {
|
||||
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - credibilityIconWidth - infoWidth, height: constrainedSize.height), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
return (CGSize(width: textLayout.size.width + credibilityIconWidth + infoWidth, height: textLayout.size.height), { width in
|
||||
let node: ChatMessageForwardInfoNode
|
||||
if let maybeNode = maybeNode {
|
||||
node = maybeNode
|
||||
@@ -125,6 +211,31 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
|
||||
node.credibilityIconNode = nil
|
||||
}
|
||||
|
||||
if hasPsaInfo {
|
||||
let infoNode: InfoButtonNode
|
||||
if let current = node.infoNode {
|
||||
infoNode = current
|
||||
} else {
|
||||
infoNode = InfoButtonNode(pressed: { [weak node] in
|
||||
guard let node = node else {
|
||||
return
|
||||
}
|
||||
if let psaType = psaType, let infoNode = node.infoNode {
|
||||
node.openPsa?(psaType, infoNode)
|
||||
}
|
||||
})
|
||||
node.infoNode = infoNode
|
||||
node.addSubnode(infoNode)
|
||||
}
|
||||
let infoButtonSize = CGSize(width: 32.0, height: 32.0)
|
||||
let infoButtonFrame = CGRect(origin: CGPoint(x: width - infoButtonSize.width - 2.0, y: 1.0), size: infoButtonSize)
|
||||
infoNode.frame = infoButtonFrame
|
||||
infoNode.update(size: infoButtonFrame.size, theme: presentationData.theme, type: type)
|
||||
} else if let infoNode = node.infoNode {
|
||||
node.infoNode = nil
|
||||
infoNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
return node
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user