Initial implementation of the PSA API

This commit is contained in:
Ali
2020-04-24 23:18:44 +04:00
parent faf022b9ec
commit c4004a23ee
68 changed files with 4928 additions and 4378 deletions

View File

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