Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2021-12-21 23:27:07 +04:00
commit 8a5d0ded9f
6 changed files with 271 additions and 168 deletions

View File

@ -217,6 +217,40 @@
"PUSH_CHAT_MESSAGE_GAME_SCORE" = "%1$@ scored %4$@ in game %3$@ in the group %2$@";
"PUSH_CHAT_MESSAGE_VIDEOS" = "%1$@ sent %3$@ videos to the group %2$@";
"PUSH_REACT_TEXT" = "%1$@|%2$@ to your %3$@";
"PUSH_REACT_NOTEXT" = "%1$@|%2$@ to your message";
"PUSH_REACT_PHOTO" = "%1$@|%2$@ to your photo";
"PUSH_REACT_VIDEO" = "%1$@|%2$@ to your video";
"PUSH_REACT_ROUND" = "%1$@|%2$@ to your video message";
"PUSH_REACT_DOC" = "%1$@|%2$@ to your file";
"PUSH_REACT_STICKER" = "%1$@|%2$@ to your %3$@sticker";
"PUSH_REACT_AUDIO" = "%1$@|%2$@ to your voice message";
"PUSH_REACT_CONTACT" = "%1$@|%2$@ to your contact %3$@";
"PUSH_REACT_GEO" = "%1$@|%2$@ to your map";
"PUSH_REACT_GEOLIVE" = "%1$@|%2$@ to your live location";
"PUSH_REACT_POLL" = "%1$@|%2$@ to your poll %3$@";
"PUSH_REACT_QUIZ" = "%1$@|%2$@ to your quiz %3$@";
"PUSH_REACT_GAME" = "%1$@|%2$@ to your game";
"PUSH_REACT_INVOICE" = "%1$@|%2$@ to your invoice";
"PUSH_REACT_GIF" = "%1$@|%2$@ to your GIF";
"PUSH_CHAT_REACT_TEXT" = "%2$@|%1$@ %3$@ to your %4$@";
"PUSH_CHAT_REACT_NOTEXT" = "%2$@|%1$@ %3$@ to your message";
"PUSH_CHAT_REACT_PHOTO" = "%2$@|%1$@ %3$@ to your photo";
"PUSH_CHAT_REACT_VIDEO" = "%2$@|%1$@ %3$@ to your video";
"PUSH_CHAT_REACT_ROUND" = "%2$@|%1$@ %3$@ to your video message";
"PUSH_CHAT_REACT_DOC" = "%2$@|%1$@ %3$@ to your file";
"PUSH_CHAT_REACT_STICKER" = "%2$@|%1$@ %3$@ to your %4$@sticker";
"PUSH_CHAT_REACT_AUDIO" = "%2$@|%1$@ %3$@ to your voice message";
"PUSH_CHAT_REACT_CONTACT" = "%2$@|%1$@ %3$@ to your contact %4$@";
"PUSH_CHAT_REACT_GEO" = "%2$@|%1$@ %3$@ to your map";
"PUSH_CHAT_REACT_GEOLIVE" = "%2$@|%1$@ %3$@ to your live location";
"PUSH_CHAT_REACT_POLL" = "%2$@|%1$@ %3$@ to your poll %4$@";
"PUSH_CHAT_REACT_QUIZ" = "%2$@|%1$@ %3$@ to your quiz %4$@";
"PUSH_CHAT_REACT_GAME" = "%2$@|%1$@ %3$@ to your game";
"PUSH_CHAT_REACT_INVOICE" = "%2$@|%1$@ %3$@ to your invoice";
"PUSH_CHAT_REACT_GIF" = "%2$@|%1$@ %3$@ to your GIF";
"PUSH_REMINDER_TITLE" = "🗓 Reminder";
"PUSH_SENDER_YOU" = "📅 You";

View File

@ -8,7 +8,7 @@ import TelegramCore
import SwiftSignalKit
import ReactionSelectionNode
final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextControllerPresentationNode {
final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextControllerPresentationNode, UIScrollViewDelegate {
private final class ContentNode: ASDisplayNode {
let offsetContainerNode: ASDisplayNode
let containingNode: ContextExtractedContentContainingNode
@ -126,6 +126,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
self.scrollNode.addSubnode(self.contentRectDebugNode)
#endif*/
self.scrollNode.view.delegate = self
self.dismissTapNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dismissTapGesture(_:))))
}
@ -162,6 +164,13 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let reactionContextNode = self.reactionContextNode {
let isIntersectingContent = scrollView.contentOffset.y >= 10.0
reactionContextNode.updateIsIntersectingContent(isIntersectingContent: isIntersectingContent, transition: .animated(duration: 0.25, curve: .easeInOut))
}
}
func highlightGestureMoved(location: CGPoint) {
self.actionsStackNode.highlightGestureMoved(location: self.view.convert(location, to: self.actionsStackNode.view))

View File

@ -0,0 +1,195 @@
import Foundation
import AsyncDisplayKit
import Display
import TelegramPresentationData
import AccountContext
private func generateBackgroundImage(foreground: UIColor, diameter: CGFloat, sideInset: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + sideInset * 2.0, height: diameter + sideInset * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(foreground.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: sideInset, y: sideInset), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(sideInset + diameter / 2.0), topCapHeight: Int(sideInset + diameter / 2.0))
}
private func generateBubbleImage(foreground: UIColor, diameter: CGFloat, sideInset: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + sideInset * 2.0, height: diameter + sideInset * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(foreground.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: sideInset, y: sideInset), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + sideInset / 2.0), topCapHeight: Int(diameter / 2.0 + sideInset / 2.0))
}
private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(shadow.cgColor)
context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.setShadow(offset: CGSize(), blur: 1.0, color: shadow.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.setFillColor(UIColor.clear.cgColor)
context.setBlendMode(.copy)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(shadowBlur + diameter / 2.0), topCapHeight: Int(shadowBlur + diameter / 2.0))
}
final class ReactionContextBackgroundNode: ASDisplayNode {
private let largeCircleSize: CGFloat
private let smallCircleSize: CGFloat
private let backgroundNode: NavigationBackgroundNode
private let maskLayer: SimpleLayer
private let backgroundLayer: SimpleLayer
private let backgroundShadowLayer: SimpleLayer
private let largeCircleLayer: SimpleLayer
private let largeCircleShadowLayer: SimpleLayer
private let smallCircleLayer: SimpleLayer
private let smallCircleShadowLayer: SimpleLayer
private var theme: PresentationTheme?
init(largeCircleSize: CGFloat, smallCircleSize: CGFloat) {
self.largeCircleSize = largeCircleSize
self.smallCircleSize = smallCircleSize
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true)
self.maskLayer = SimpleLayer()
self.backgroundLayer = SimpleLayer()
self.backgroundShadowLayer = SimpleLayer()
self.largeCircleLayer = SimpleLayer()
self.largeCircleShadowLayer = SimpleLayer()
self.smallCircleLayer = SimpleLayer()
self.smallCircleShadowLayer = SimpleLayer()
self.backgroundLayer.backgroundColor = UIColor.black.cgColor
self.backgroundLayer.masksToBounds = true
self.backgroundLayer.cornerRadius = 52.0 / 2.0
self.largeCircleLayer.backgroundColor = UIColor.black.cgColor
self.largeCircleLayer.masksToBounds = true
self.largeCircleLayer.cornerRadius = largeCircleSize / 2.0
self.smallCircleLayer.backgroundColor = UIColor.black.cgColor
self.smallCircleLayer.masksToBounds = true
self.smallCircleLayer.cornerRadius = smallCircleSize / 2.0
if #available(iOS 13.0, *) {
self.backgroundLayer.cornerCurve = .circular
self.largeCircleLayer.cornerCurve = .circular
self.smallCircleLayer.cornerCurve = .circular
}
super.init()
self.layer.addSublayer(self.backgroundShadowLayer)
self.layer.addSublayer(self.smallCircleShadowLayer)
self.layer.addSublayer(self.largeCircleShadowLayer)
self.backgroundShadowLayer.opacity = 0.0
self.largeCircleShadowLayer.opacity = 0.0
self.smallCircleShadowLayer.opacity = 0.0
self.addSubnode(self.backgroundNode)
self.maskLayer.addSublayer(self.smallCircleLayer)
self.maskLayer.addSublayer(self.largeCircleLayer)
self.maskLayer.addSublayer(self.backgroundLayer)
self.backgroundNode.layer.mask = self.maskLayer
}
func updateIsIntersectingContent(isIntersectingContent: Bool, transition: ContainedViewLayoutTransition) {
let shadowAlpha: CGFloat = isIntersectingContent ? 1.0 : 0.0
transition.updateAlpha(layer: self.backgroundShadowLayer, alpha: shadowAlpha)
transition.updateAlpha(layer: self.smallCircleShadowLayer, alpha: shadowAlpha)
transition.updateAlpha(layer: self.largeCircleShadowLayer, alpha: shadowAlpha)
}
func update(
theme: PresentationTheme,
size: CGSize,
cloudSourcePoint: CGFloat,
isLeftAligned: Bool,
transition: ContainedViewLayoutTransition
) {
let shadowInset: CGFloat = 15.0
if self.theme !== theme {
self.theme = theme
self.backgroundNode.updateColor(color: theme.contextMenu.backgroundColor, transition: .immediate)
let shadowColor = UIColor(white: 0.0, alpha: 0.4)
if let image = generateBubbleShadowImage(shadow: shadowColor, diameter: 52.0, shadowBlur: shadowInset) {
ASDisplayNodeSetResizableContents(self.backgroundShadowLayer, image)
}
if let image = generateBubbleShadowImage(shadow: shadowColor, diameter: self.largeCircleSize, shadowBlur: shadowInset) {
ASDisplayNodeSetResizableContents(self.largeCircleShadowLayer, image)
}
if let image = generateBubbleShadowImage(shadow: shadowColor, diameter: self.smallCircleSize, shadowBlur: shadowInset) {
ASDisplayNodeSetResizableContents(self.smallCircleShadowLayer, image)
}
}
let backgroundFrame = CGRect(origin: CGPoint(), size: size)
let largeCircleFrame: CGRect
let smallCircleFrame: CGRect
if isLeftAligned {
largeCircleFrame = CGRect(origin: CGPoint(x: cloudSourcePoint - floor(largeCircleSize / 2.0), y: size.height - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.maxX - 3.0, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize))
} else {
largeCircleFrame = CGRect(origin: CGPoint(x: cloudSourcePoint - floor(largeCircleSize / 2.0), y: size.height - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.minX + 3.0 - smallCircleSize, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize))
}
let contentBounds = backgroundFrame.insetBy(dx: -10.0, dy: -10.0).union(largeCircleFrame).union(smallCircleFrame)
transition.updateFrame(layer: self.backgroundLayer, frame: backgroundFrame.offsetBy(dx: -contentBounds.minX, dy: -contentBounds.minY))
transition.updateFrame(layer: self.largeCircleLayer, frame: largeCircleFrame.offsetBy(dx: -contentBounds.minX, dy: -contentBounds.minY))
transition.updateFrame(layer: self.smallCircleLayer, frame: smallCircleFrame.offsetBy(dx: -contentBounds.minX, dy: -contentBounds.minY))
transition.updateFrame(layer: self.backgroundShadowLayer, frame: backgroundFrame.insetBy(dx: -shadowInset, dy: -shadowInset))
transition.updateFrame(layer: self.largeCircleShadowLayer, frame: largeCircleFrame.insetBy(dx: -shadowInset, dy: -shadowInset))
transition.updateFrame(layer: self.smallCircleShadowLayer, frame: smallCircleFrame.insetBy(dx: -shadowInset, dy: -shadowInset))
transition.updateFrame(node: self.backgroundNode, frame: contentBounds)
self.backgroundNode.update(size: contentBounds.size, transition: transition)
}
func animateIn() {
let smallCircleDuration: Double = 0.5
let largeCircleDuration: Double = 0.5
let largeCircleDelay: Double = 0.08
let mainCircleDuration: Double = 0.5
let mainCircleDelay: Double = 0.1
self.smallCircleLayer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: smallCircleDuration)
self.largeCircleLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: largeCircleDelay)
self.largeCircleLayer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay)
self.backgroundLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: mainCircleDelay)
self.backgroundLayer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay)
}
func animateInFromAnchorRect(size: CGSize, sourceBackgroundFrame: CGRect) {
let springDuration: Double = 0.42
let springDamping: CGFloat = 104.0
let springDelay: Double = 0.22
self.backgroundLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
self.backgroundLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
}
func animateOut() {
self.backgroundLayer.animateAlpha(from: CGFloat(self.backgroundLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
self.largeCircleLayer.animateAlpha(from: CGFloat(self.largeCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
self.smallCircleLayer.animateAlpha(from: CGFloat(self.smallCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false)
}
}

View File

@ -37,68 +37,11 @@ public final class ReactionContextItem {
private let largeCircleSize: CGFloat = 16.0
private let smallCircleSize: CGFloat = 8.0
private func generateBackgroundImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(foreground.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(shadowBlur + diameter / 2.0), topCapHeight: Int(shadowBlur + diameter / 2.0))
}
private func generateBackgroundShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter * 2.0 + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(shadow.cgColor)
context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur + diameter, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.fill(CGRect(origin: CGPoint(x: shadowBlur + diameter / 2.0, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.setFillColor(UIColor.clear.cgColor)
context.setBlendMode(.copy)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur + diameter, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.fill(CGRect(origin: CGPoint(x: shadowBlur + diameter / 2.0, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(diameter + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0))
}
private func generateBubbleImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(foreground.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0))
}
private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(shadow.cgColor)
context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.setShadow(offset: CGSize(), blur: 1.0, color: shadow.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.setFillColor(UIColor.clear.cgColor)
context.setBlendMode(.copy)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
})?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0))
}
public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
private let theme: PresentationTheme
private let items: [ReactionContextItem]
private let backgroundNode: ASImageNode
private let backgroundShadowNode: ASImageNode
private let backgroundContainerNode: ASDisplayNode
private let largeCircleNode: ASImageNode
private let largeCircleShadowNode: ASImageNode
private let smallCircleNode: ASImageNode
private let smallCircleShadowNode: ASImageNode
private let backgroundNode: ReactionContextBackgroundNode
private let contentContainer: ASDisplayNode
private let contentContainerMask: UIImageView
@ -122,44 +65,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
self.theme = theme
self.items = items
let shadowBlur: CGFloat = 5.0
self.backgroundNode = ASImageNode()
self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.displaysAsynchronously = false
self.backgroundShadowNode = ASImageNode()
self.backgroundShadowNode.displayWithoutProcessing = true
self.backgroundShadowNode.displaysAsynchronously = false
self.backgroundContainerNode = ASDisplayNode()
self.backgroundContainerNode.allowsGroupOpacity = true
self.largeCircleNode = ASImageNode()
self.largeCircleNode.displayWithoutProcessing = true
self.largeCircleNode.displaysAsynchronously = false
self.largeCircleShadowNode = ASImageNode()
self.largeCircleShadowNode.displayWithoutProcessing = true
self.largeCircleShadowNode.displaysAsynchronously = false
self.smallCircleNode = ASImageNode()
self.smallCircleNode.displayWithoutProcessing = true
self.smallCircleNode.displaysAsynchronously = false
self.smallCircleShadowNode = ASImageNode()
self.smallCircleShadowNode.displayWithoutProcessing = true
self.smallCircleShadowNode.displaysAsynchronously = false
self.backgroundNode.image = generateBackgroundImage(foreground: theme.contextMenu.backgroundColor.withAlphaComponent(1.0), diameter: 52.0, shadowBlur: shadowBlur)
self.backgroundShadowNode.image = generateBackgroundShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: 52.0, shadowBlur: shadowBlur)
self.largeCircleNode.image = generateBubbleImage(foreground: theme.contextMenu.backgroundColor.withAlphaComponent(1.0), diameter: largeCircleSize, shadowBlur: shadowBlur)
self.smallCircleNode.image = generateBubbleImage(foreground: theme.contextMenu.backgroundColor.withAlphaComponent(1.0), diameter: smallCircleSize, shadowBlur: shadowBlur)
self.largeCircleShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: largeCircleSize, shadowBlur: shadowBlur)
self.smallCircleShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: smallCircleSize, shadowBlur: shadowBlur)
self.backgroundNode = ReactionContextBackgroundNode(largeCircleSize: largeCircleSize, smallCircleSize: smallCircleSize)
self.scrollNode = ASScrollNode()
self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true
@ -200,18 +106,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
context.fill(CGRect(origin: CGPoint(x: maskGradientWidth, y: 0.0), size: CGSize(width: 1.0, height: size.height)))
})?.stretchableImage(withLeftCapWidth: Int(maskGradientWidth), topCapHeight: 0)
self.contentContainer.view.mask = self.contentContainerMask
//self.contentContainer.view.addSubview(self.contentContainerMask)
super.init()
self.addSubnode(self.smallCircleShadowNode)
self.addSubnode(self.largeCircleShadowNode)
self.addSubnode(self.backgroundShadowNode)
self.backgroundContainerNode.addSubnode(self.smallCircleNode)
self.backgroundContainerNode.addSubnode(self.largeCircleNode)
self.backgroundContainerNode.addSubnode(self.backgroundNode)
self.addSubnode(self.backgroundContainerNode)
self.addSubnode(self.backgroundNode)
self.scrollNode.view.delegate = self
@ -233,6 +131,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: transition, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil)
}
public func updateIsIntersectingContent(isIntersectingContent: Bool, transition: ContainedViewLayoutTransition) {
self.backgroundNode.updateIsIntersectingContent(isIntersectingContent: isIntersectingContent, transition: transition)
}
private func calculateBackgroundFrame(containerSize: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, contentSize: CGSize) -> (backgroundFrame: CGRect, isLeftAligned: Bool, cloudSourcePoint: CGFloat) {
var contentSize = contentSize
contentSize.width = max(52.0, contentSize.width)
@ -308,7 +210,6 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let sideInset: CGFloat = 11.0
let itemSpacing: CGFloat = 9.0
let itemSize: CGFloat = 40.0
let shadowBlur: CGFloat = 5.0
let verticalInset: CGFloat = 13.0
let rowHeight: CGFloat = 30.0
@ -350,34 +251,15 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
}
self.updateScrolling(transition: transition)
let isInOverflow = backgroundFrame.maxY > anchorRect.minY
let backgroundAlpha: CGFloat = isInOverflow ? 1.0 : 0.8
let shadowAlpha: CGFloat = isInOverflow ? 1.0 : 0.0
transition.updateAlpha(node: self.backgroundContainerNode, alpha: backgroundAlpha)
transition.updateAlpha(node: self.backgroundShadowNode, alpha: shadowAlpha)
transition.updateAlpha(node: self.largeCircleShadowNode, alpha: shadowAlpha)
transition.updateAlpha(node: self.smallCircleShadowNode, alpha: shadowAlpha)
transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur))
transition.updateFrame(node: self.backgroundShadowNode, frame: backgroundFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur))
let largeCircleFrame: CGRect
let smallCircleFrame: CGRect
if isLeftAligned {
largeCircleFrame = CGRect(origin: CGPoint(x: cloudSourcePoint - floor(largeCircleSize / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.maxX - 3.0, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize))
} else {
largeCircleFrame = CGRect(origin: CGPoint(x: cloudSourcePoint - floor(largeCircleSize / 2.0), y: backgroundFrame.maxY - largeCircleSize / 2.0), size: CGSize(width: largeCircleSize, height: largeCircleSize))
smallCircleFrame = CGRect(origin: CGPoint(x: largeCircleFrame.minX + 3.0 - smallCircleSize, y: largeCircleFrame.maxY + 2.0), size: CGSize(width: smallCircleSize, height: smallCircleSize))
}
transition.updateFrame(node: self.largeCircleNode, frame: largeCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur))
transition.updateFrame(node: self.largeCircleShadowNode, frame: largeCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur))
transition.updateFrame(node: self.smallCircleNode, frame: smallCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur))
transition.updateFrame(node: self.smallCircleShadowNode, frame: smallCircleFrame.insetBy(dx: -shadowBlur, dy: -shadowBlur))
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
self.backgroundNode.update(
theme: self.theme,
size: backgroundFrame.size,
cloudSourcePoint: cloudSourcePoint - backgroundFrame.minX,
isLeftAligned: isLeftAligned,
transition: transition
)
if let animateInFromAnchorRect = animateInFromAnchorRect {
let springDuration: Double = 0.42
@ -386,14 +268,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let sourceBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: backgroundInsets, anchorRect: animateInFromAnchorRect, contentSize: CGSize(width: backgroundFrame.height, height: contentHeight)).0
self.backgroundNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - backgroundFrame.midX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
self.backgroundNode.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size).insetBy(dx: -shadowBlur, dy: -shadowBlur)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -shadowBlur, dy: -shadowBlur)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
self.backgroundNode.animateInFromAnchorRect(size: backgroundFrame.size, sourceBackgroundFrame: sourceBackgroundFrame.offsetBy(dx: -backgroundFrame.minX, dy: -backgroundFrame.minY))
self.contentContainer.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - backgroundFrame.midX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
self.contentContainer.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: backgroundFrame.size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
//self.contentContainerMask.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - backgroundFrame.midX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
//self.contentContainerMask.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: backgroundFrame.size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
} else if let animateOutToAnchorRect = animateOutToAnchorRect {
let targetBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: backgroundInsets, anchorRect: animateOutToAnchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight)).0
@ -410,22 +288,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .immediate, animateInFromAnchorRect: sourceAnchorRect, animateOutToAnchorRect: nil)
}
let smallCircleDuration: Double = 0.5
let largeCircleDuration: Double = 0.5
let largeCircleDelay: Double = 0.08
let mainCircleDuration: Double = 0.5
let mainCircleDelay: Double = 0.1
self.smallCircleNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: smallCircleDuration)
self.smallCircleShadowNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: smallCircleDuration)
self.largeCircleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: largeCircleDelay)
self.largeCircleNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay)
self.largeCircleShadowNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay)
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: mainCircleDelay)
self.backgroundNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay)
self.backgroundShadowNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay)
self.backgroundNode.animateIn()
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
@ -433,22 +299,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: itemDelay)
itemNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: itemDelay, initialVelocity: 0.0)
}
/*if let itemNode = self.itemNodes.first {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: mainCircleDelay)
itemNode.didAppear()
itemNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay, completion: { _ in
})
}*/
}
public func animateOut(to targetAnchorRect: CGRect?, animatingOutToReaction: Bool) {
self.backgroundNode.layer.animateAlpha(from: self.backgroundNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.backgroundShadowNode.layer.animateAlpha(from: self.backgroundShadowNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.largeCircleNode.layer.animateAlpha(from: self.largeCircleNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.largeCircleShadowNode.layer.animateAlpha(from: self.largeCircleShadowNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.smallCircleNode.layer.animateAlpha(from: self.smallCircleNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.smallCircleShadowNode.layer.animateAlpha(from: self.smallCircleShadowNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.backgroundNode.animateOut()
for itemNode in self.itemNodes {
if itemNode.isExtracted {
continue

View File

@ -150,7 +150,7 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: LimitsCo
return false
}
private func canViewReadStats(message: Message, isMessageRead: Bool, appConfig: AppConfiguration) -> Bool {
private func canViewReadStats(message: Message, cachedData: CachedPeerData?, isMessageRead: Bool, appConfig: AppConfiguration) -> Bool {
guard let peer = message.peers[message.id.peerId] else {
return false
}
@ -199,6 +199,16 @@ private func canViewReadStats(message: Message, isMessageRead: Bool, appConfig:
case let channel as TelegramChannel:
if case .broadcast = channel.info {
return false
} else if let cachedData = cachedData as? CachedChannelData {
if let memberCount = cachedData.participantsSummary.memberCount {
if Int(memberCount) > maxParticipantCount {
return false
}
} else {
return false
}
} else {
return false
}
case let group as TelegramGroup:
if group.participantCount > maxParticipantCount {
@ -1207,7 +1217,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}
}
let canViewStats = canViewReadStats(message: message, isMessageRead: isMessageRead, appConfig: appConfig)
let canViewStats = canViewReadStats(message: message, cachedData: cachedData, isMessageRead: isMessageRead, appConfig: appConfig)
var reactionCount = 0
for reaction in mergedMessageReactionsAndPeers(message: message).reactions {
reactionCount += Int(reaction.count)

View File

@ -986,7 +986,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY - 10.0), size: reactionButtonsSizeAndApply.0)
var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY - 4.0), size: reactionButtonsSizeAndApply.0)
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
reactionButtonsFrame.origin.y += 4.0 + actionButtonsSizeAndApply.0.height
}