mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
bd9c495fb4
commit
4e55e3aab0
@ -12217,6 +12217,7 @@ Sorry for the inconvenience.";
|
||||
"Message.FactCheck" = "Fact Check";
|
||||
"Message.FactCheck.WhatIsThis" = "what's this?";
|
||||
|
||||
"Conversation.FactCheck.InnerDescription" = "This clarification was provided by a fact checking agency assigned by the department of the government of your country (%@) responsible for combating misinformation.";
|
||||
"Conversation.FactCheck.Description" = "This clarification was provided by a fact checking agency assigned by the department of the government of your country (%@) responsible for combating misinformation.";
|
||||
|
||||
"FactCheck.Title" = "Fact Check";
|
||||
|
@ -63,7 +63,9 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.searchBar)
|
||||
if hasCurrentChat {
|
||||
self.addSubnode(self.searchBar)
|
||||
}
|
||||
|
||||
self.searchBar.cancel = { [weak self] in
|
||||
self?.searchBar.deactivate(clear: false)
|
||||
@ -79,6 +81,10 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
override var mode: NavigationBarContentMode {
|
||||
return self.hasCurrentChat ? .replacement : .expansion
|
||||
}
|
||||
|
||||
func updateTheme(_ theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: self.strings)
|
||||
@ -89,7 +95,11 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
||||
}
|
||||
|
||||
override var nominalHeight: CGFloat {
|
||||
return 54.0 + 44.0
|
||||
if self.hasCurrentChat {
|
||||
return 54.0 + 44.0
|
||||
} else {
|
||||
return 45.0
|
||||
}
|
||||
}
|
||||
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||
@ -119,7 +129,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
||||
),
|
||||
customLayout: TabSelectorComponent.CustomLayout(
|
||||
font: Font.medium(14.0),
|
||||
spacing: 24.0,
|
||||
spacing: self.hasCurrentChat ? 24.0 : 8.0,
|
||||
lineSelection: true
|
||||
),
|
||||
items: items,
|
||||
@ -135,7 +145,13 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width, height: 44.0)
|
||||
)
|
||||
let tabSelectorFrame = CGRect(origin: CGPoint(x: floor((size.width - tabSelectorSize.width) / 2.0), y: size.height - tabSelectorSize.height - 9.0), size: tabSelectorSize)
|
||||
let tabSelectorFrameOriginX: CGFloat
|
||||
if self.hasCurrentChat || "".isEmpty {
|
||||
tabSelectorFrameOriginX = floorToScreenPixels((size.width - tabSelectorSize.width) / 2.0)
|
||||
} else {
|
||||
tabSelectorFrameOriginX = 4.0
|
||||
}
|
||||
let tabSelectorFrame = CGRect(origin: CGPoint(x: tabSelectorFrameOriginX, y: size.height - tabSelectorSize.height - 9.0), size: tabSelectorSize)
|
||||
if let tabSelectorView = self.tabSelector.view {
|
||||
if tabSelectorView.superview == nil {
|
||||
self.view.addSubview(tabSelectorView)
|
||||
|
@ -231,9 +231,10 @@ class PremiumCoinComponent: Component {
|
||||
self.sceneView.scene = scene
|
||||
self.sceneView.delegate = self
|
||||
|
||||
self.didSetReady = true
|
||||
self._ready.set(.single(true))
|
||||
self.onReady()
|
||||
let _ = self.sceneView.snapshot()
|
||||
// self.didSetReady = true
|
||||
// self._ready.set(.single(true))
|
||||
// self.onReady()
|
||||
}
|
||||
|
||||
private var didSetReady = false
|
||||
|
@ -42,8 +42,11 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
private var titleBadgeButton: HighlightTrackingButtonNode?
|
||||
private let textClippingNode: ASDisplayNode
|
||||
private let textNode: TextNode
|
||||
private let additionalTextNode: TextNode
|
||||
private var linkHighlightingNode: LinkHighlightingNode?
|
||||
|
||||
private let lineNode: ASDisplayNode
|
||||
|
||||
private var maskView: UIImageView?
|
||||
private var maskOverlayView: UIView?
|
||||
|
||||
@ -55,13 +58,17 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
private var isExpanded: Bool = false
|
||||
private var appliedIsExpanded: Bool = false
|
||||
|
||||
private var countryName: String?
|
||||
|
||||
required public init() {
|
||||
self.titleNode = TextNode()
|
||||
self.titleBadgeLabel = TextNode()
|
||||
self.textClippingNode = ASDisplayNode()
|
||||
self.textNode = TextNode()
|
||||
self.additionalTextNode = TextNode()
|
||||
self.expandIcon = ASImageNode()
|
||||
self.statusNode = ChatMessageDateAndStatusNode()
|
||||
self.lineNode = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
@ -80,6 +87,14 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textClippingNode.addSubnode(self.textNode)
|
||||
|
||||
self.additionalTextNode.isUserInteractionEnabled = false
|
||||
self.additionalTextNode.contentMode = .topLeft
|
||||
self.additionalTextNode.contentsScale = UIScreenScale
|
||||
self.additionalTextNode.displaysAsynchronously = false
|
||||
self.textClippingNode.addSubnode(self.additionalTextNode)
|
||||
|
||||
self.textClippingNode.addSubnode(self.lineNode)
|
||||
|
||||
self.titleBadgeLabel.isUserInteractionEnabled = false
|
||||
self.titleBadgeLabel.contentMode = .topLeft
|
||||
self.titleBadgeLabel.contentsScale = UIScreenScale
|
||||
@ -106,24 +121,10 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
}
|
||||
|
||||
@objc private func badgePressed() {
|
||||
guard let item = self.item else {
|
||||
guard let item = self.item, let countryName = self.countryName else {
|
||||
return
|
||||
}
|
||||
|
||||
var countryId: String?
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? FactCheckMessageAttribute, case let .Loaded(_, _, countryIdValue) = attribute.content {
|
||||
countryId = countryIdValue
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
guard let countryId else {
|
||||
return
|
||||
}
|
||||
|
||||
let locale = localeWithStrings(item.presentationData.strings)
|
||||
let countryName = displayCountryName(countryId, locale: locale)
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_FactCheck_Description(countryName).string, true, self.titleBadgeButton, nil)
|
||||
}
|
||||
|
||||
@ -148,13 +149,9 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
}
|
||||
|
||||
let textNodeFrame = self.textClippingNode.frame
|
||||
if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
var concealed = true
|
||||
if let (attributeText, fullText) = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||
concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText)
|
||||
}
|
||||
return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed)))
|
||||
return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: false)))
|
||||
} else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention {
|
||||
return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false))
|
||||
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
||||
@ -216,10 +213,12 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
let titleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let titleBadgeLayout = TextNode.asyncLayout(self.titleBadgeLabel)
|
||||
let textLayout = TextNode.asyncLayout(self.textNode)
|
||||
let additionalTextLayout = TextNode.asyncLayout(self.additionalTextNode)
|
||||
let measureTextLayout = TextNode.asyncLayout(nil)
|
||||
let statusLayout = self.statusNode.asyncLayout()
|
||||
|
||||
let currentIsExpanded = self.isExpanded
|
||||
let currentCountryName = self.countryName
|
||||
|
||||
return { item, layoutConstants, _, _, _, _ in
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
||||
@ -313,15 +312,32 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
let titleBadgeString = NSAttributedString(string: item.presentationData.strings.Message_FactCheck_WhatIsThis, font: badgeFont, textColor: mainColor)
|
||||
let (titleBadgeLayout, titleBadgeApply) = titleBadgeLayout(TextNodeLayoutArguments(attributedString: titleBadgeString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize))
|
||||
|
||||
let countryName: String
|
||||
if let currentCountryName {
|
||||
countryName = currentCountryName
|
||||
} else {
|
||||
if let attribute = item.message.factCheckAttribute, case let .Loaded(_, _, countryIdValue) = attribute.content {
|
||||
let locale = localeWithStrings(item.presentationData.strings)
|
||||
countryName = displayCountryName(countryIdValue, locale: locale)
|
||||
} else {
|
||||
countryName = ""
|
||||
}
|
||||
}
|
||||
|
||||
let finalAttributedText = stringWithAppliedEntities(rawText, entities: rawEntities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textBlockQuoteFont, message: nil) as! NSMutableAttributedString
|
||||
finalAttributedText.append(NSAttributedString(string: "__", font: textFont, textColor: .clear))
|
||||
|
||||
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: finalAttributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets, lineColor: messageTheme.accentControlColor))
|
||||
|
||||
let additionalAttributedText = NSMutableAttributedString(string: item.presentationData.strings.Conversation_FactCheck_InnerDescription(countryName).string, font: badgeFont, textColor: mainColor)
|
||||
additionalAttributedText.append(NSAttributedString(string: "__", font: badgeFont, textColor: .clear))
|
||||
|
||||
let (additionalTextLayout, additionalTextApply) = additionalTextLayout(TextNodeLayoutArguments(attributedString: additionalAttributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, lineSpacing: 0.0, cutout: nil, insets: textInsets, lineColor: messageTheme.accentControlColor))
|
||||
|
||||
var canExpand = false
|
||||
var clippedTextHeight: CGFloat = textLayout.size.height
|
||||
if textLayout.numberOfLines > 4 {
|
||||
let (measuredTextLayout, _) = measureTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 3, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets, lineColor: messageTheme.accentControlColor))
|
||||
let (measuredTextLayout, _) = measureTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 4, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets, lineColor: messageTheme.accentControlColor))
|
||||
canExpand = true
|
||||
|
||||
if !currentIsExpanded {
|
||||
@ -340,7 +356,11 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
let textFrame = CGRect(origin: CGPoint(x: titleFrame.origin.x, y: -textInsets.top + titleFrameWithoutInsets.height + textSpacing), size: textLayout.size)
|
||||
var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: clippedTextHeight - textInsets.top - textInsets.bottom))
|
||||
textFrameWithoutInsets = textFrameWithoutInsets.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top)
|
||||
|
||||
|
||||
let additionalTextFrame = CGRect(origin: CGPoint(x: titleFrame.origin.x, y: textFrame.maxY), size: additionalTextLayout.size)
|
||||
var additionalTextFrameWithoutInsets = CGRect(origin: CGPoint(x: additionalTextFrame.origin.x + textInsets.left, y: additionalTextFrame.origin.y + textInsets.top), size: CGSize(width: additionalTextFrame.width - textInsets.left - textInsets.right, height: additionalTextFrame.height - textInsets.top - textInsets.bottom))
|
||||
additionalTextFrameWithoutInsets = additionalTextFrameWithoutInsets.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top)
|
||||
|
||||
var statusSuggestedWidthAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> Void))?
|
||||
if let statusType = statusType {
|
||||
var isReplyThread = false
|
||||
@ -377,6 +397,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
if let statusSuggestedWidthAndContinue = statusSuggestedWidthAndContinue {
|
||||
suggestedBoundingWidth = max(suggestedBoundingWidth, statusSuggestedWidthAndContinue.0)
|
||||
}
|
||||
suggestedBoundingWidth = max(suggestedBoundingWidth, additionalTextFrameWithoutInsets.width)
|
||||
let sideInsets = layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
||||
suggestedBoundingWidth += (sideInsets - 2.0) * 2.0
|
||||
|
||||
@ -385,7 +406,13 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
let statusSizeAndApply = statusSuggestedWidthAndContinue?.1(boundingWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right)
|
||||
|
||||
boundingSize = CGSize(width: boundingWidth, height: topInset + titleFrameWithoutInsets.height + textFrameWithoutInsets.size.height + textSpacing)
|
||||
var contentHeight = titleFrameWithoutInsets.height + textSpacing + textFrameWithoutInsets.size.height
|
||||
if canExpand && !currentIsExpanded {
|
||||
} else {
|
||||
contentHeight += textSpacing * 2.0 + 1.0 + additionalTextFrameWithoutInsets.height
|
||||
}
|
||||
contentHeight += textSpacing
|
||||
boundingSize = CGSize(width: boundingWidth, height: topInset + contentHeight - textSpacing)
|
||||
if let statusSizeAndApply = statusSizeAndApply {
|
||||
boundingSize.height += statusSizeAndApply.0.height
|
||||
}
|
||||
@ -400,7 +427,8 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
let themeUpdated = strongSelf.item?.presentationData.theme.theme !== item.presentationData.theme.theme
|
||||
|
||||
strongSelf.item = item
|
||||
|
||||
strongSelf.countryName = countryName
|
||||
|
||||
let backgroundView: MessageInlineBlockBackgroundView
|
||||
if let current = strongSelf.backgroundView {
|
||||
backgroundView = current
|
||||
@ -410,6 +438,10 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
strongSelf.backgroundView = backgroundView
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
strongSelf.lineNode.backgroundColor = mainColor.withAlphaComponent(0.15)
|
||||
}
|
||||
|
||||
var isExpandedUpdated = false
|
||||
if strongSelf.appliedIsExpanded != currentIsExpanded {
|
||||
strongSelf.appliedIsExpanded = currentIsExpanded
|
||||
@ -464,57 +496,12 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
|
||||
let _ = textApply()
|
||||
strongSelf.textNode.frame = CGRect(origin: .zero, size: textFrame.size)
|
||||
|
||||
var clippingTextFrame = textFrame.offsetBy(dx: 0.0, dy: topInset)
|
||||
clippingTextFrame.size.height = clippedTextHeight - 3.0
|
||||
|
||||
if canExpand {
|
||||
let wasHidden = strongSelf.expandIcon.isHidden
|
||||
strongSelf.expandIcon.isHidden = false
|
||||
if strongSelf.maskView?.image == nil {
|
||||
strongSelf.maskView?.image = generateMaskImage()
|
||||
}
|
||||
strongSelf.textClippingNode.view.mask = strongSelf.maskView
|
||||
|
||||
var expandIconFrame: CGRect = .zero
|
||||
if let icon = strongSelf.expandIcon.image {
|
||||
expandIconFrame = CGRect(origin: CGPoint(x: boundingWidth - icon.size.width - 19.0, y: clippingTextFrame.maxY - icon.size.height - 5.0), size: icon.size)
|
||||
if wasHidden || isFirstTime {
|
||||
strongSelf.expandIcon.position = expandIconFrame.center
|
||||
} else {
|
||||
animation.animator.updatePosition(layer: strongSelf.expandIcon.layer, position: expandIconFrame.center, completion: nil)
|
||||
}
|
||||
strongSelf.expandIcon.bounds = CGRect(origin: .zero, size: expandIconFrame.size)
|
||||
}
|
||||
|
||||
let expandButtonFrame = expandIconFrame.insetBy(dx: -8.0, dy: -8.0)
|
||||
|
||||
let expandButton: HighlightTrackingButtonNode
|
||||
if let current = strongSelf.expandButton {
|
||||
expandButton = current
|
||||
} else {
|
||||
expandButton = HighlightTrackingButtonNode()
|
||||
expandButton.addTarget(self, action: #selector(strongSelf.expandPressed), forControlEvents: .touchUpInside)
|
||||
expandButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.expandIcon.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.expandIcon.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.expandIcon.alpha = 1.0
|
||||
strongSelf.expandIcon.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
strongSelf.expandButton = expandButton
|
||||
strongSelf.addSubnode(expandButton)
|
||||
}
|
||||
expandButton.frame = expandButtonFrame
|
||||
} else {
|
||||
strongSelf.expandIcon.isHidden = true
|
||||
strongSelf.textClippingNode.view.mask = nil
|
||||
}
|
||||
let _ = additionalTextApply()
|
||||
strongSelf.additionalTextNode.frame = CGRect(origin: CGPoint(x: 0.0, y: textFrame.height - textInsets.bottom + textSpacing + 1.0), size: additionalTextFrame.size)
|
||||
|
||||
let clippingTextFrame = CGRect(origin: textFrame.origin.offsetBy(dx: 0.0, dy: topInset), size: CGSize(width: boundingWidth, height: contentHeight - titleFrame.height + textSpacing))
|
||||
|
||||
var titleLineWidth: CGFloat = 0.0
|
||||
if let firstLine = titleLayout.linesRects().first {
|
||||
titleLineWidth = firstLine.width
|
||||
@ -559,7 +546,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
titleBadgeButton.setBackgroundImage(generateFilledCircleImage(diameter: badgeBackgroundFrame.height, color: mainColor.withMultipliedAlpha(0.1))?.stretchableImage(withLeftCapWidth: Int(badgeBackgroundFrame.height / 2), topCapHeight: Int(badgeBackgroundFrame.height / 2)), for: .normal)
|
||||
}
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + topInset), size: CGSize(width: boundingWidth - backgroundInsets.left - backgroundInsets.right, height: titleFrameWithoutInsets.height + textSpacing + textFrameWithoutInsets.height + textSpacing))
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + topInset), size: CGSize(width: boundingWidth - backgroundInsets.left - backgroundInsets.right, height: contentHeight))
|
||||
|
||||
if isFirstTime {
|
||||
strongSelf.textClippingNode.frame = clippingTextFrame
|
||||
@ -578,6 +565,55 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
}
|
||||
backgroundView.update(size: backgroundFrame.size, isTransparent: false, primaryColor: mainColor, secondaryColor: nil, thirdColor: nil, backgroundColor: nil, pattern: nil, patternTopRightPosition: nil, animation: isFirstTime ? .None : animation)
|
||||
|
||||
animation.animator.updateFrame(layer: strongSelf.lineNode.layer, frame: CGRect(origin: CGPoint(x: 0.0, y: textFrame.height - textSpacing + 1.0), size: CGSize(width: backgroundFrame.width - 9.0 - 6.0, height: 1.0 - UIScreenPixel)), completion: nil)
|
||||
|
||||
if canExpand {
|
||||
let wasHidden = strongSelf.expandIcon.isHidden
|
||||
strongSelf.expandIcon.isHidden = false
|
||||
if strongSelf.maskView?.image == nil {
|
||||
strongSelf.maskView?.image = generateMaskImage()
|
||||
}
|
||||
strongSelf.textClippingNode.view.mask = strongSelf.maskView
|
||||
|
||||
var expandIconFrame: CGRect = .zero
|
||||
if let icon = strongSelf.expandIcon.image {
|
||||
expandIconFrame = CGRect(origin: CGPoint(x: boundingWidth - icon.size.width - 19.0, y: backgroundFrame.maxY - icon.size.height - 6.0), size: icon.size)
|
||||
if wasHidden || isFirstTime {
|
||||
strongSelf.expandIcon.position = expandIconFrame.center
|
||||
} else {
|
||||
animation.animator.updatePosition(layer: strongSelf.expandIcon.layer, position: expandIconFrame.center, completion: nil)
|
||||
}
|
||||
strongSelf.expandIcon.bounds = CGRect(origin: .zero, size: expandIconFrame.size)
|
||||
}
|
||||
|
||||
let expandButtonFrame = expandIconFrame.insetBy(dx: -8.0, dy: -8.0)
|
||||
|
||||
let expandButton: HighlightTrackingButtonNode
|
||||
if let current = strongSelf.expandButton {
|
||||
expandButton = current
|
||||
} else {
|
||||
expandButton = HighlightTrackingButtonNode()
|
||||
expandButton.addTarget(self, action: #selector(strongSelf.expandPressed), forControlEvents: .touchUpInside)
|
||||
expandButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.expandIcon.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.expandIcon.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.expandIcon.alpha = 1.0
|
||||
strongSelf.expandIcon.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
strongSelf.expandButton = expandButton
|
||||
strongSelf.addSubnode(expandButton)
|
||||
}
|
||||
expandButton.frame = expandButtonFrame
|
||||
} else {
|
||||
strongSelf.expandIcon.isHidden = true
|
||||
strongSelf.textClippingNode.view.mask = nil
|
||||
}
|
||||
|
||||
if let statusSizeAndApply = statusSizeAndApply {
|
||||
strongSelf.statusNode.reactionSelected = { [weak strongSelf] _, value, sourceView in
|
||||
guard let strongSelf, let item = strongSelf.item else {
|
||||
@ -594,7 +630,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
|
||||
}
|
||||
|
||||
let statusFrame = CGRect(origin: CGPoint(x: boundingWidth - layoutConstants.text.bubbleInsets.right - statusSizeAndApply.0.width, y: topInset + textFrameWithoutInsets.maxY), size: statusSizeAndApply.0)
|
||||
let statusFrame = CGRect(origin: CGPoint(x: boundingWidth - layoutConstants.text.bubbleInsets.right - statusSizeAndApply.0.width, y: backgroundFrame.maxY + 4.0), size: statusSizeAndApply.0)
|
||||
if isFirstTime {
|
||||
strongSelf.statusNode.frame = statusFrame
|
||||
} else {
|
||||
|
@ -11770,7 +11770,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
hasBirthdayToday = true
|
||||
}
|
||||
|
||||
if hasBirthdayToday, let age = ageForBirthday(birthday), age > 0 {
|
||||
if hasBirthdayToday {
|
||||
Queue.mainQueue().after(0.3) {
|
||||
var birthdayItemFrame: CGRect?
|
||||
if let section = self.regularSections[InfoSection.peerInfo] {
|
||||
|
@ -29,19 +29,22 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let action: () -> Void
|
||||
let cancel: (Bool) -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
let copyTransactionId: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
subject: StarsTransactionScreen.Subject,
|
||||
action: @escaping () -> Void,
|
||||
cancel: @escaping (Bool) -> Void,
|
||||
openPeer: @escaping (EnginePeer) -> Void
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
copyTransactionId: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
self.action = action
|
||||
self.cancel = cancel
|
||||
self.openPeer = openPeer
|
||||
self.copyTransactionId = copyTransactionId
|
||||
}
|
||||
|
||||
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
||||
@ -163,7 +166,21 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let gloss = false
|
||||
switch subject {
|
||||
case let .transaction(transaction):
|
||||
titleText = "Product Title"
|
||||
switch transaction.peer {
|
||||
case .peer:
|
||||
titleText = "Product Title"
|
||||
case .appStore:
|
||||
titleText = "In-app Purchase"
|
||||
case .playMarket:
|
||||
titleText = "Play Market"
|
||||
case .premiumBot:
|
||||
titleText = "Premium Bot"
|
||||
case .fragment:
|
||||
titleText = "Fragment"
|
||||
case .unsupported:
|
||||
titleText = "Unsupported"
|
||||
}
|
||||
|
||||
if transaction.count < 0 {
|
||||
descriptionText = "- \(transaction.count * -1) ⭐️"
|
||||
} else {
|
||||
@ -185,7 +202,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: titleText,
|
||||
font: Font.semibold(24.0),
|
||||
font: Font.bold(25.0),
|
||||
textColor: theme.actionSheet.primaryTextColor,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
@ -211,16 +228,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let textFont = Font.regular(15.0)
|
||||
let boldTextFont = Font.semibold(15.0)
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
})
|
||||
let description = description.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: descriptionText, font: boldTextFont, textColor: descriptionText.hasPrefix("-") ? theme.list.itemDestructiveColor : theme.list.itemDisclosureActions.constructive.fillColor)),
|
||||
text: .plain(NSAttributedString(string: descriptionText, font: Font.semibold(17.0), textColor: descriptionText.hasPrefix("-") ? theme.list.itemDestructiveColor : theme.list.itemDisclosureActions.constructive.fillColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
@ -240,7 +250,13 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
title: strings.Stars_Transaction_Date,
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
context: component.context,
|
||||
textColor: tableLinkColor,
|
||||
peer: toPeer
|
||||
)
|
||||
),
|
||||
action: {
|
||||
if toPeer.id != accountContext.account.peerId {
|
||||
component.openPeer(toPeer)
|
||||
@ -258,12 +274,16 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
id: "transaction",
|
||||
title: strings.Stars_Transaction_Id,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: transactionId, font: Font.monospace(15.0), textColor: tableTextColor)),
|
||||
truncationType: .end,
|
||||
maximumNumberOfLines: 0
|
||||
TransactionCellComponent(
|
||||
textColor: tableTextColor,
|
||||
accentColor: tableLinkColor,
|
||||
transactionId: transactionId,
|
||||
copy: {
|
||||
component.copyTransactionId()
|
||||
}
|
||||
)
|
||||
)
|
||||
),
|
||||
insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0)
|
||||
))
|
||||
|
||||
tableItems.append(.init(
|
||||
@ -283,6 +303,13 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let textFont = Font.regular(15.0)
|
||||
let boldTextFont = Font.semibold(15.0)
|
||||
let textColor = theme.actionSheet.secondaryTextColor
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
})
|
||||
let additional = additional.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .markdown(text: additionalText, attributes: markdownAttributes),
|
||||
@ -319,16 +346,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
animationName: nil,
|
||||
iconPosition: .left,
|
||||
isLoading: state.inProgress,
|
||||
action: { [weak state] in
|
||||
if gloss {
|
||||
component.action()
|
||||
if let state {
|
||||
state.inProgress = true
|
||||
state.updated()
|
||||
}
|
||||
} else {
|
||||
component.cancel(true)
|
||||
}
|
||||
action: {
|
||||
component.cancel(true)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
@ -336,7 +355,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
)
|
||||
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 28.0 + 125.0))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 31.0 + 125.0))
|
||||
)
|
||||
|
||||
context.add(star
|
||||
@ -344,12 +363,12 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
)
|
||||
|
||||
var originY: CGFloat = 0.0
|
||||
originY += star.size.height - 32.0
|
||||
originY += star.size.height - 23.0
|
||||
|
||||
context.add(description
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0))
|
||||
)
|
||||
originY += description.size.height + 21.0
|
||||
originY += description.size.height + 20.0
|
||||
|
||||
context.add(table
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0))
|
||||
@ -384,17 +403,20 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
let subject: StarsTransactionScreen.Subject
|
||||
let action: () -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
let copyTransactionId: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
subject: StarsTransactionScreen.Subject,
|
||||
action: @escaping () -> Void,
|
||||
openPeer: @escaping (EnginePeer) -> Void
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
copyTransactionId: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
self.action = action
|
||||
self.openPeer = openPeer
|
||||
self.copyTransactionId = copyTransactionId
|
||||
}
|
||||
|
||||
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
||||
@ -411,6 +433,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let controller = environment.controller
|
||||
@ -423,20 +447,23 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
action: context.component.action,
|
||||
cancel: { animate in
|
||||
if animate {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
if let controller = controller() as? StarsTransactionScreen {
|
||||
controller.dismissAllTooltips()
|
||||
animateOut.invoke(Action { [weak controller] _ in
|
||||
controller?.dismiss(completion: nil)
|
||||
})
|
||||
}
|
||||
} else if let controller = controller() {
|
||||
controller.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
},
|
||||
openPeer: context.component.openPeer
|
||||
openPeer: context.component.openPeer,
|
||||
copyTransactionId: context.component.copyTransactionId
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
clipsContent: true,
|
||||
externalState: sheetExternalState,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
@ -448,13 +475,15 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
if animated {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
if let controller = controller() as? StarsTransactionScreen {
|
||||
controller.dismissAllTooltips()
|
||||
animateOut.invoke(Action { _ in
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if let controller = controller() {
|
||||
if let controller = controller() as? StarsTransactionScreen {
|
||||
controller.dismissAllTooltips()
|
||||
controller.dismiss(completion: nil)
|
||||
}
|
||||
}
|
||||
@ -469,6 +498,22 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
||||
let layout = ContainerViewLayout(
|
||||
size: context.availableSize,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0),
|
||||
safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
additionalInsets: .zero,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
)
|
||||
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
@ -493,6 +538,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
self.context = context
|
||||
|
||||
var openPeerImpl: ((EnginePeer) -> Void)?
|
||||
var copyTransactionIdImpl: (() -> Void)?
|
||||
super.init(
|
||||
context: context,
|
||||
component: StarsTransactionSheetComponent(
|
||||
@ -501,6 +547,9 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
action: action,
|
||||
openPeer: { peerId in
|
||||
openPeerImpl?(peerId)
|
||||
},
|
||||
copyTransactionId: {
|
||||
copyTransactionIdImpl?()
|
||||
}
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
@ -509,11 +558,14 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
|
||||
openPeerImpl = { [weak self] peer in
|
||||
guard let self, let navigationController = self.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
self.dismissAllTooltips()
|
||||
|
||||
let _ = (context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id)
|
||||
)
|
||||
@ -524,6 +576,16 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peer), subject: nil, botStart: nil, updateTextInputState: nil, keepStack: .always, useExisting: false, purposefulAction: nil, scrollToEndIfExists: false, activateMessageSearch: nil, animated: true))
|
||||
})
|
||||
}
|
||||
|
||||
copyTransactionIdImpl = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.dismissAllTooltips()
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Stars_Transaction_CopiedId), elevatedLayout: false, position: .bottom, action: { _ in return true }), in: .current)
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -546,6 +608,8 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
public func dismissAnimated() {
|
||||
self.dismissAllTooltips()
|
||||
|
||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
}
|
||||
@ -571,11 +635,13 @@ private final class TableComponent: CombinedComponent {
|
||||
public let id: AnyHashable
|
||||
public let title: String
|
||||
public let component: AnyComponent<Empty>
|
||||
public let insets: UIEdgeInsets?
|
||||
|
||||
public init<IdType: Hashable>(id: IdType, title: String, component: AnyComponent<Empty>) {
|
||||
public init<IdType: Hashable>(id: IdType, title: String, component: AnyComponent<Empty>, insets: UIEdgeInsets? = nil) {
|
||||
self.id = AnyHashable(id)
|
||||
self.title = title
|
||||
self.component = component
|
||||
self.insets = insets
|
||||
}
|
||||
|
||||
public static func == (lhs: Item, rhs: Item) -> Bool {
|
||||
@ -588,6 +654,9 @@ private final class TableComponent: CombinedComponent {
|
||||
if lhs.component != rhs.component {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -637,7 +706,7 @@ private final class TableComponent: CombinedComponent {
|
||||
var leftColumnWidth: CGFloat = 0.0
|
||||
|
||||
var updatedTitleChildren: [_UpdatedChildComponent] = []
|
||||
var updatedValueChildren: [_UpdatedChildComponent] = []
|
||||
var updatedValueChildren: [(_UpdatedChildComponent, UIEdgeInsets)] = []
|
||||
var updatedBorderChildren: [_UpdatedChildComponent] = []
|
||||
|
||||
for item in context.component.items {
|
||||
@ -664,12 +733,19 @@ private final class TableComponent: CombinedComponent {
|
||||
|
||||
for item in context.component.items {
|
||||
let titleChild = updatedTitleChildren[i]
|
||||
|
||||
let insets: UIEdgeInsets
|
||||
if let customInsets = item.insets {
|
||||
insets = customInsets
|
||||
} else {
|
||||
insets = UIEdgeInsets(top: 0.0, left: horizontalPadding, bottom: 0.0, right: horizontalPadding)
|
||||
}
|
||||
let valueChild = valueChildren[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: rightColumnWidth - horizontalPadding * 2.0, height: context.availableSize.height),
|
||||
availableSize: CGSize(width: rightColumnWidth - insets.left - insets.right, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedValueChildren.append(valueChild)
|
||||
updatedValueChildren.append((valueChild, insets))
|
||||
|
||||
let rowHeight = max(40.0, max(titleChild.size.height, valueChild.size.height) + verticalPadding * 2.0)
|
||||
rowHeights[i] = rowHeight
|
||||
@ -742,11 +818,11 @@ private final class TableComponent: CombinedComponent {
|
||||
|
||||
i = 0
|
||||
var originY: CGFloat = 0.0
|
||||
for (titleChild, valueChild) in zip(updatedTitleChildren, updatedValueChildren) {
|
||||
for (titleChild, (valueChild, valueInsets)) in zip(updatedTitleChildren, updatedValueChildren) {
|
||||
let rowHeight = rowHeights[i] ?? 0.0
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size)
|
||||
let valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + horizontalPadding, y: originY + verticalPadding), size: valueChild.size)
|
||||
let valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + valueInsets.left, y: originY + verticalPadding), size: valueChild.size)
|
||||
|
||||
context.add(titleChild
|
||||
.position(titleFrame.center)
|
||||
@ -866,6 +942,113 @@ private final class PeerCellComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
private final class TransactionCellComponent: Component {
|
||||
let textColor: UIColor
|
||||
let accentColor: UIColor
|
||||
let transactionId: String
|
||||
let copy: () -> Void
|
||||
|
||||
init(textColor: UIColor, accentColor: UIColor, transactionId: String, copy: @escaping () -> Void) {
|
||||
self.textColor = textColor
|
||||
self.accentColor = accentColor
|
||||
self.transactionId = transactionId
|
||||
self.copy = copy
|
||||
}
|
||||
|
||||
static func ==(lhs: TransactionCellComponent, rhs: TransactionCellComponent) -> Bool {
|
||||
if lhs.textColor !== rhs.textColor {
|
||||
return false
|
||||
}
|
||||
if lhs.accentColor != rhs.accentColor {
|
||||
return false
|
||||
}
|
||||
if lhs.transactionId != rhs.transactionId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let text = ComponentView<Empty>()
|
||||
private let button = ComponentView<Empty>()
|
||||
|
||||
private var component: TransactionCellComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: TransactionCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let spacing: CGFloat = 6.0
|
||||
|
||||
let buttonSize = self.button.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
BundleIconComponent(name: "Chat/Context Menu/Copy", tintColor: component.accentColor)
|
||||
),
|
||||
action: {
|
||||
component.copy()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: availableSize.height)
|
||||
)
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.transactionId, font: Font.monospace(15.0), textColor: component.textColor, paragraphAlignment: .left)),
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - buttonSize.width - spacing, height: availableSize.height)
|
||||
)
|
||||
|
||||
let size = CGSize(width: textSize.width + spacing + buttonSize.width, height: textSize.height)
|
||||
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: textSize.width + spacing, y: floorToScreenPixels((size.height - buttonSize.height) / 2.0)), size: buttonSize)
|
||||
if let buttonView = self.button.view {
|
||||
if buttonView.superview == nil {
|
||||
self.addSubview(buttonView)
|
||||
}
|
||||
transition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
}
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
if let textView = self.text.view {
|
||||
if textView.superview == nil {
|
||||
self.addSubview(textView)
|
||||
}
|
||||
transition.setFrame(view: textView, frame: textFrame)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"app": "10.12",
|
||||
"app": "10.13",
|
||||
"xcode": "15.2",
|
||||
"bazel": "7.1.1",
|
||||
"macos": "13.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user