mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 06:10:03 +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" = "Fact Check";
|
||||||
"Message.FactCheck.WhatIsThis" = "what's this?";
|
"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.";
|
"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";
|
"FactCheck.Title" = "Fact Check";
|
||||||
|
|||||||
@ -63,7 +63,9 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.searchBar)
|
if hasCurrentChat {
|
||||||
|
self.addSubnode(self.searchBar)
|
||||||
|
}
|
||||||
|
|
||||||
self.searchBar.cancel = { [weak self] in
|
self.searchBar.cancel = { [weak self] in
|
||||||
self?.searchBar.deactivate(clear: false)
|
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) {
|
func updateTheme(_ theme: PresentationTheme) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: self.strings)
|
self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: self.strings)
|
||||||
@ -89,7 +95,11 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var nominalHeight: CGFloat {
|
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)?
|
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||||
@ -119,7 +129,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
|||||||
),
|
),
|
||||||
customLayout: TabSelectorComponent.CustomLayout(
|
customLayout: TabSelectorComponent.CustomLayout(
|
||||||
font: Font.medium(14.0),
|
font: Font.medium(14.0),
|
||||||
spacing: 24.0,
|
spacing: self.hasCurrentChat ? 24.0 : 8.0,
|
||||||
lineSelection: true
|
lineSelection: true
|
||||||
),
|
),
|
||||||
items: items,
|
items: items,
|
||||||
@ -135,7 +145,13 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: size.width, height: 44.0)
|
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 let tabSelectorView = self.tabSelector.view {
|
||||||
if tabSelectorView.superview == nil {
|
if tabSelectorView.superview == nil {
|
||||||
self.view.addSubview(tabSelectorView)
|
self.view.addSubview(tabSelectorView)
|
||||||
|
|||||||
@ -231,9 +231,10 @@ class PremiumCoinComponent: Component {
|
|||||||
self.sceneView.scene = scene
|
self.sceneView.scene = scene
|
||||||
self.sceneView.delegate = self
|
self.sceneView.delegate = self
|
||||||
|
|
||||||
self.didSetReady = true
|
let _ = self.sceneView.snapshot()
|
||||||
self._ready.set(.single(true))
|
// self.didSetReady = true
|
||||||
self.onReady()
|
// self._ready.set(.single(true))
|
||||||
|
// self.onReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var didSetReady = false
|
private var didSetReady = false
|
||||||
|
|||||||
@ -42,8 +42,11 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
private var titleBadgeButton: HighlightTrackingButtonNode?
|
private var titleBadgeButton: HighlightTrackingButtonNode?
|
||||||
private let textClippingNode: ASDisplayNode
|
private let textClippingNode: ASDisplayNode
|
||||||
private let textNode: TextNode
|
private let textNode: TextNode
|
||||||
|
private let additionalTextNode: TextNode
|
||||||
private var linkHighlightingNode: LinkHighlightingNode?
|
private var linkHighlightingNode: LinkHighlightingNode?
|
||||||
|
|
||||||
|
private let lineNode: ASDisplayNode
|
||||||
|
|
||||||
private var maskView: UIImageView?
|
private var maskView: UIImageView?
|
||||||
private var maskOverlayView: UIView?
|
private var maskOverlayView: UIView?
|
||||||
|
|
||||||
@ -55,13 +58,17 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
private var isExpanded: Bool = false
|
private var isExpanded: Bool = false
|
||||||
private var appliedIsExpanded: Bool = false
|
private var appliedIsExpanded: Bool = false
|
||||||
|
|
||||||
|
private var countryName: String?
|
||||||
|
|
||||||
required public init() {
|
required public init() {
|
||||||
self.titleNode = TextNode()
|
self.titleNode = TextNode()
|
||||||
self.titleBadgeLabel = TextNode()
|
self.titleBadgeLabel = TextNode()
|
||||||
self.textClippingNode = ASDisplayNode()
|
self.textClippingNode = ASDisplayNode()
|
||||||
self.textNode = TextNode()
|
self.textNode = TextNode()
|
||||||
|
self.additionalTextNode = TextNode()
|
||||||
self.expandIcon = ASImageNode()
|
self.expandIcon = ASImageNode()
|
||||||
self.statusNode = ChatMessageDateAndStatusNode()
|
self.statusNode = ChatMessageDateAndStatusNode()
|
||||||
|
self.lineNode = ASDisplayNode()
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -80,6 +87,14 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
self.textClippingNode.addSubnode(self.textNode)
|
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.isUserInteractionEnabled = false
|
||||||
self.titleBadgeLabel.contentMode = .topLeft
|
self.titleBadgeLabel.contentMode = .topLeft
|
||||||
self.titleBadgeLabel.contentsScale = UIScreenScale
|
self.titleBadgeLabel.contentsScale = UIScreenScale
|
||||||
@ -106,24 +121,10 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func badgePressed() {
|
@objc private func badgePressed() {
|
||||||
guard let item = self.item else {
|
guard let item = self.item, let countryName = self.countryName else {
|
||||||
return
|
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)
|
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
|
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 {
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
var concealed = true
|
return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: false)))
|
||||||
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)))
|
|
||||||
} else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention {
|
} else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention {
|
||||||
return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false))
|
return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false))
|
||||||
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
} 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 titleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
let titleBadgeLayout = TextNode.asyncLayout(self.titleBadgeLabel)
|
let titleBadgeLayout = TextNode.asyncLayout(self.titleBadgeLabel)
|
||||||
let textLayout = TextNode.asyncLayout(self.textNode)
|
let textLayout = TextNode.asyncLayout(self.textNode)
|
||||||
|
let additionalTextLayout = TextNode.asyncLayout(self.additionalTextNode)
|
||||||
let measureTextLayout = TextNode.asyncLayout(nil)
|
let measureTextLayout = TextNode.asyncLayout(nil)
|
||||||
let statusLayout = self.statusNode.asyncLayout()
|
let statusLayout = self.statusNode.asyncLayout()
|
||||||
|
|
||||||
let currentIsExpanded = self.isExpanded
|
let currentIsExpanded = self.isExpanded
|
||||||
|
let currentCountryName = self.countryName
|
||||||
|
|
||||||
return { item, layoutConstants, _, _, _, _ in
|
return { item, layoutConstants, _, _, _, _ in
|
||||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
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 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 (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
|
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))
|
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 (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 canExpand = false
|
||||||
var clippedTextHeight: CGFloat = textLayout.size.height
|
var clippedTextHeight: CGFloat = textLayout.size.height
|
||||||
if textLayout.numberOfLines > 4 {
|
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
|
canExpand = true
|
||||||
|
|
||||||
if !currentIsExpanded {
|
if !currentIsExpanded {
|
||||||
@ -341,6 +357,10 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
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))
|
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)
|
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))?
|
var statusSuggestedWidthAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> Void))?
|
||||||
if let statusType = statusType {
|
if let statusType = statusType {
|
||||||
var isReplyThread = false
|
var isReplyThread = false
|
||||||
@ -377,6 +397,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
if let statusSuggestedWidthAndContinue = statusSuggestedWidthAndContinue {
|
if let statusSuggestedWidthAndContinue = statusSuggestedWidthAndContinue {
|
||||||
suggestedBoundingWidth = max(suggestedBoundingWidth, statusSuggestedWidthAndContinue.0)
|
suggestedBoundingWidth = max(suggestedBoundingWidth, statusSuggestedWidthAndContinue.0)
|
||||||
}
|
}
|
||||||
|
suggestedBoundingWidth = max(suggestedBoundingWidth, additionalTextFrameWithoutInsets.width)
|
||||||
let sideInsets = layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
let sideInsets = layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
||||||
suggestedBoundingWidth += (sideInsets - 2.0) * 2.0
|
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)
|
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 {
|
if let statusSizeAndApply = statusSizeAndApply {
|
||||||
boundingSize.height += statusSizeAndApply.0.height
|
boundingSize.height += statusSizeAndApply.0.height
|
||||||
}
|
}
|
||||||
@ -400,6 +427,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
let themeUpdated = strongSelf.item?.presentationData.theme.theme !== item.presentationData.theme.theme
|
let themeUpdated = strongSelf.item?.presentationData.theme.theme !== item.presentationData.theme.theme
|
||||||
|
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
strongSelf.countryName = countryName
|
||||||
|
|
||||||
let backgroundView: MessageInlineBlockBackgroundView
|
let backgroundView: MessageInlineBlockBackgroundView
|
||||||
if let current = strongSelf.backgroundView {
|
if let current = strongSelf.backgroundView {
|
||||||
@ -410,6 +438,10 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
strongSelf.backgroundView = backgroundView
|
strongSelf.backgroundView = backgroundView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if themeUpdated {
|
||||||
|
strongSelf.lineNode.backgroundColor = mainColor.withAlphaComponent(0.15)
|
||||||
|
}
|
||||||
|
|
||||||
var isExpandedUpdated = false
|
var isExpandedUpdated = false
|
||||||
if strongSelf.appliedIsExpanded != currentIsExpanded {
|
if strongSelf.appliedIsExpanded != currentIsExpanded {
|
||||||
strongSelf.appliedIsExpanded = currentIsExpanded
|
strongSelf.appliedIsExpanded = currentIsExpanded
|
||||||
@ -465,55 +497,10 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
let _ = textApply()
|
let _ = textApply()
|
||||||
strongSelf.textNode.frame = CGRect(origin: .zero, size: textFrame.size)
|
strongSelf.textNode.frame = CGRect(origin: .zero, size: textFrame.size)
|
||||||
|
|
||||||
var clippingTextFrame = textFrame.offsetBy(dx: 0.0, dy: topInset)
|
let _ = additionalTextApply()
|
||||||
clippingTextFrame.size.height = clippedTextHeight - 3.0
|
strongSelf.additionalTextNode.frame = CGRect(origin: CGPoint(x: 0.0, y: textFrame.height - textInsets.bottom + textSpacing + 1.0), size: additionalTextFrame.size)
|
||||||
|
|
||||||
if canExpand {
|
let clippingTextFrame = CGRect(origin: textFrame.origin.offsetBy(dx: 0.0, dy: topInset), size: CGSize(width: boundingWidth, height: contentHeight - titleFrame.height + textSpacing))
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
var titleLineWidth: CGFloat = 0.0
|
var titleLineWidth: CGFloat = 0.0
|
||||||
if let firstLine = titleLayout.linesRects().first {
|
if let firstLine = titleLayout.linesRects().first {
|
||||||
@ -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)
|
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 {
|
if isFirstTime {
|
||||||
strongSelf.textClippingNode.frame = clippingTextFrame
|
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)
|
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 {
|
if let statusSizeAndApply = statusSizeAndApply {
|
||||||
strongSelf.statusNode.reactionSelected = { [weak strongSelf] _, value, sourceView in
|
strongSelf.statusNode.reactionSelected = { [weak strongSelf] _, value, sourceView in
|
||||||
guard let strongSelf, let item = strongSelf.item else {
|
guard let strongSelf, let item = strongSelf.item else {
|
||||||
@ -594,7 +630,7 @@ public class ChatMessageFactCheckBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
|
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 {
|
if isFirstTime {
|
||||||
strongSelf.statusNode.frame = statusFrame
|
strongSelf.statusNode.frame = statusFrame
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -11770,7 +11770,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
hasBirthdayToday = true
|
hasBirthdayToday = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasBirthdayToday, let age = ageForBirthday(birthday), age > 0 {
|
if hasBirthdayToday {
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
var birthdayItemFrame: CGRect?
|
var birthdayItemFrame: CGRect?
|
||||||
if let section = self.regularSections[InfoSection.peerInfo] {
|
if let section = self.regularSections[InfoSection.peerInfo] {
|
||||||
|
|||||||
@ -29,19 +29,22 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let cancel: (Bool) -> Void
|
let cancel: (Bool) -> Void
|
||||||
let openPeer: (EnginePeer) -> Void
|
let openPeer: (EnginePeer) -> Void
|
||||||
|
let copyTransactionId: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
subject: StarsTransactionScreen.Subject,
|
subject: StarsTransactionScreen.Subject,
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
cancel: @escaping (Bool) -> Void,
|
cancel: @escaping (Bool) -> Void,
|
||||||
openPeer: @escaping (EnginePeer) -> Void
|
openPeer: @escaping (EnginePeer) -> Void,
|
||||||
|
copyTransactionId: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.action = action
|
self.action = action
|
||||||
self.cancel = cancel
|
self.cancel = cancel
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
|
self.copyTransactionId = copyTransactionId
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
||||||
@ -163,7 +166,21 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let gloss = false
|
let gloss = false
|
||||||
switch subject {
|
switch subject {
|
||||||
case let .transaction(transaction):
|
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 {
|
if transaction.count < 0 {
|
||||||
descriptionText = "- \(transaction.count * -1) ⭐️"
|
descriptionText = "- \(transaction.count * -1) ⭐️"
|
||||||
} else {
|
} else {
|
||||||
@ -185,7 +202,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: titleText,
|
string: titleText,
|
||||||
font: Font.semibold(24.0),
|
font: Font.bold(25.0),
|
||||||
textColor: theme.actionSheet.primaryTextColor,
|
textColor: theme.actionSheet.primaryTextColor,
|
||||||
paragraphAlignment: .center
|
paragraphAlignment: .center
|
||||||
)),
|
)),
|
||||||
@ -211,16 +228,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
transition: .immediate
|
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(
|
let description = description.update(
|
||||||
component: BalancedTextComponent(
|
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,
|
horizontalAlignment: .center,
|
||||||
maximumNumberOfLines: 0,
|
maximumNumberOfLines: 0,
|
||||||
lineSpacing: 0.2
|
lineSpacing: 0.2
|
||||||
@ -240,7 +250,13 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
title: strings.Stars_Transaction_Date,
|
title: strings.Stars_Transaction_Date,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
Button(
|
Button(
|
||||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
content: AnyComponent(
|
||||||
|
PeerCellComponent(
|
||||||
|
context: component.context,
|
||||||
|
textColor: tableLinkColor,
|
||||||
|
peer: toPeer
|
||||||
|
)
|
||||||
|
),
|
||||||
action: {
|
action: {
|
||||||
if toPeer.id != accountContext.account.peerId {
|
if toPeer.id != accountContext.account.peerId {
|
||||||
component.openPeer(toPeer)
|
component.openPeer(toPeer)
|
||||||
@ -258,12 +274,16 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
id: "transaction",
|
id: "transaction",
|
||||||
title: strings.Stars_Transaction_Id,
|
title: strings.Stars_Transaction_Id,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
MultilineTextComponent(
|
TransactionCellComponent(
|
||||||
text: .plain(NSAttributedString(string: transactionId, font: Font.monospace(15.0), textColor: tableTextColor)),
|
textColor: tableTextColor,
|
||||||
truncationType: .end,
|
accentColor: tableLinkColor,
|
||||||
maximumNumberOfLines: 0
|
transactionId: transactionId,
|
||||||
|
copy: {
|
||||||
|
component.copyTransactionId()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0)
|
||||||
))
|
))
|
||||||
|
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
@ -283,6 +303,13 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
transition: .immediate
|
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(
|
let additional = additional.update(
|
||||||
component: BalancedTextComponent(
|
component: BalancedTextComponent(
|
||||||
text: .markdown(text: additionalText, attributes: markdownAttributes),
|
text: .markdown(text: additionalText, attributes: markdownAttributes),
|
||||||
@ -319,16 +346,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
animationName: nil,
|
animationName: nil,
|
||||||
iconPosition: .left,
|
iconPosition: .left,
|
||||||
isLoading: state.inProgress,
|
isLoading: state.inProgress,
|
||||||
action: { [weak state] in
|
action: {
|
||||||
if gloss {
|
component.cancel(true)
|
||||||
component.action()
|
|
||||||
if let state {
|
|
||||||
state.inProgress = true
|
|
||||||
state.updated()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
component.cancel(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||||
@ -336,7 +355,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
|
|
||||||
context.add(title
|
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
|
context.add(star
|
||||||
@ -344,12 +363,12 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var originY: CGFloat = 0.0
|
var originY: CGFloat = 0.0
|
||||||
originY += star.size.height - 32.0
|
originY += star.size.height - 23.0
|
||||||
|
|
||||||
context.add(description
|
context.add(description
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0))
|
.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
|
context.add(table
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0))
|
.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 subject: StarsTransactionScreen.Subject
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let openPeer: (EnginePeer) -> Void
|
let openPeer: (EnginePeer) -> Void
|
||||||
|
let copyTransactionId: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
subject: StarsTransactionScreen.Subject,
|
subject: StarsTransactionScreen.Subject,
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
openPeer: @escaping (EnginePeer) -> Void
|
openPeer: @escaping (EnginePeer) -> Void,
|
||||||
|
copyTransactionId: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.action = action
|
self.action = action
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
|
self.copyTransactionId = copyTransactionId
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
||||||
@ -411,6 +433,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||||
|
|
||||||
|
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
let environment = context.environment[EnvironmentType.self]
|
let environment = context.environment[EnvironmentType.self]
|
||||||
let controller = environment.controller
|
let controller = environment.controller
|
||||||
@ -423,20 +447,23 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
action: context.component.action,
|
action: context.component.action,
|
||||||
cancel: { animate in
|
cancel: { animate in
|
||||||
if animate {
|
if animate {
|
||||||
animateOut.invoke(Action { _ in
|
if let controller = controller() as? StarsTransactionScreen {
|
||||||
if let controller = controller() {
|
controller.dismissAllTooltips()
|
||||||
controller.dismiss(completion: nil)
|
animateOut.invoke(Action { [weak controller] _ in
|
||||||
}
|
controller?.dismiss(completion: nil)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
} else if let controller = controller() {
|
} else if let controller = controller() {
|
||||||
controller.dismiss(animated: false, completion: nil)
|
controller.dismiss(animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openPeer: context.component.openPeer
|
openPeer: context.component.openPeer,
|
||||||
|
copyTransactionId: context.component.copyTransactionId
|
||||||
)),
|
)),
|
||||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||||
followContentSizeChanges: true,
|
followContentSizeChanges: true,
|
||||||
clipsContent: true,
|
clipsContent: true,
|
||||||
|
externalState: sheetExternalState,
|
||||||
animateOut: animateOut
|
animateOut: animateOut
|
||||||
),
|
),
|
||||||
environment: {
|
environment: {
|
||||||
@ -448,13 +475,15 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||||
dismiss: { animated in
|
dismiss: { animated in
|
||||||
if animated {
|
if animated {
|
||||||
animateOut.invoke(Action { _ in
|
if let controller = controller() as? StarsTransactionScreen {
|
||||||
if let controller = controller() {
|
controller.dismissAllTooltips()
|
||||||
|
animateOut.invoke(Action { _ in
|
||||||
controller.dismiss(completion: nil)
|
controller.dismiss(completion: nil)
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
} else {
|
} else {
|
||||||
if let controller = controller() {
|
if let controller = controller() as? StarsTransactionScreen {
|
||||||
|
controller.dismissAllTooltips()
|
||||||
controller.dismiss(completion: nil)
|
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))
|
.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
|
return context.availableSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -493,6 +538,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
var openPeerImpl: ((EnginePeer) -> Void)?
|
var openPeerImpl: ((EnginePeer) -> Void)?
|
||||||
|
var copyTransactionIdImpl: (() -> Void)?
|
||||||
super.init(
|
super.init(
|
||||||
context: context,
|
context: context,
|
||||||
component: StarsTransactionSheetComponent(
|
component: StarsTransactionSheetComponent(
|
||||||
@ -501,6 +547,9 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
action: action,
|
action: action,
|
||||||
openPeer: { peerId in
|
openPeer: { peerId in
|
||||||
openPeerImpl?(peerId)
|
openPeerImpl?(peerId)
|
||||||
|
},
|
||||||
|
copyTransactionId: {
|
||||||
|
copyTransactionIdImpl?()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
navigationBarAppearance: .none,
|
navigationBarAppearance: .none,
|
||||||
@ -509,11 +558,14 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.navigationPresentation = .flatModal
|
self.navigationPresentation = .flatModal
|
||||||
|
self.automaticallyControlPresentationContextLayout = false
|
||||||
|
|
||||||
openPeerImpl = { [weak self] peer in
|
openPeerImpl = { [weak self] peer in
|
||||||
guard let self, let navigationController = self.navigationController as? NavigationController else {
|
guard let self, let navigationController = self.navigationController as? NavigationController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
let _ = (context.engine.data.get(
|
let _ = (context.engine.data.get(
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peer.id)
|
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))
|
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) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -546,6 +608,8 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func dismissAnimated() {
|
public func dismissAnimated() {
|
||||||
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||||
view.dismissAnimated()
|
view.dismissAnimated()
|
||||||
}
|
}
|
||||||
@ -571,11 +635,13 @@ private final class TableComponent: CombinedComponent {
|
|||||||
public let id: AnyHashable
|
public let id: AnyHashable
|
||||||
public let title: String
|
public let title: String
|
||||||
public let component: AnyComponent<Empty>
|
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.id = AnyHashable(id)
|
||||||
self.title = title
|
self.title = title
|
||||||
self.component = component
|
self.component = component
|
||||||
|
self.insets = insets
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: Item, rhs: Item) -> Bool {
|
public static func == (lhs: Item, rhs: Item) -> Bool {
|
||||||
@ -588,6 +654,9 @@ private final class TableComponent: CombinedComponent {
|
|||||||
if lhs.component != rhs.component {
|
if lhs.component != rhs.component {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.insets != rhs.insets {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -637,7 +706,7 @@ private final class TableComponent: CombinedComponent {
|
|||||||
var leftColumnWidth: CGFloat = 0.0
|
var leftColumnWidth: CGFloat = 0.0
|
||||||
|
|
||||||
var updatedTitleChildren: [_UpdatedChildComponent] = []
|
var updatedTitleChildren: [_UpdatedChildComponent] = []
|
||||||
var updatedValueChildren: [_UpdatedChildComponent] = []
|
var updatedValueChildren: [(_UpdatedChildComponent, UIEdgeInsets)] = []
|
||||||
var updatedBorderChildren: [_UpdatedChildComponent] = []
|
var updatedBorderChildren: [_UpdatedChildComponent] = []
|
||||||
|
|
||||||
for item in context.component.items {
|
for item in context.component.items {
|
||||||
@ -664,12 +733,19 @@ private final class TableComponent: CombinedComponent {
|
|||||||
|
|
||||||
for item in context.component.items {
|
for item in context.component.items {
|
||||||
let titleChild = updatedTitleChildren[i]
|
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(
|
let valueChild = valueChildren[item.id].update(
|
||||||
component: item.component,
|
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
|
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)
|
let rowHeight = max(40.0, max(titleChild.size.height, valueChild.size.height) + verticalPadding * 2.0)
|
||||||
rowHeights[i] = rowHeight
|
rowHeights[i] = rowHeight
|
||||||
@ -742,11 +818,11 @@ private final class TableComponent: CombinedComponent {
|
|||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
var originY: CGFloat = 0.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 rowHeight = rowHeights[i] ?? 0.0
|
||||||
|
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size)
|
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
|
context.add(titleChild
|
||||||
.position(titleFrame.center)
|
.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? {
|
private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
|
||||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
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",
|
"xcode": "15.2",
|
||||||
"bazel": "7.1.1",
|
"bazel": "7.1.1",
|
||||||
"macos": "13.0"
|
"macos": "13.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user