mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
8a5d0ded9f
@ -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";
|
||||
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user