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

This commit is contained in:
overtake 2019-08-20 19:05:39 +03:00
commit 74805bb045
7 changed files with 179 additions and 69 deletions

View File

@ -2,22 +2,7 @@ import Foundation
import Postbox import Postbox
import TelegramCore import TelegramCore
public struct ReactionGestureItemValue { public enum ReactionGestureItem {
public var value: String case reaction(value: String, text: String, file: TelegramMediaFile)
public var text: String case reply
public var file: TelegramMediaFile
public init(value: String, text: String, file: TelegramMediaFile) {
self.value = value
self.text = text
self.file = file
}
}
public final class ReactionGestureItem {
public let value: ReactionGestureItemValue
public init(value: ReactionGestureItemValue) {
self.value = value
}
} }

View File

@ -5,11 +5,7 @@ import Display
import Postbox import Postbox
import TelegramCore import TelegramCore
private let shadowBlur: CGFloat = 8.0 private func generateBubbleImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
private let minimizedReactionSize: CGFloat = 30.0
private let maximizedReactionSize: CGFloat = 60.0
private func generateBubbleImage(foreground: UIColor, diameter: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in 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.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(foreground.cgColor) context.setFillColor(foreground.cgColor)
@ -17,7 +13,7 @@ private func generateBubbleImage(foreground: UIColor, diameter: CGFloat) -> UIIm
})?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0)) })?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0))
} }
private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat) -> UIImage? { 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 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.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor) context.setFillColor(UIColor.white.cgColor)
@ -34,11 +30,12 @@ private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat) -> UI
private final class ReactionNode: ASDisplayNode { private final class ReactionNode: ASDisplayNode {
let reaction: ReactionGestureItem let reaction: ReactionGestureItem
private let animationNode: AnimatedStickerNode private let animationNode: AnimatedStickerNode
private let imageNode: ASImageNode
var isMaximized: Bool? var isMaximized: Bool?
private let intrinsicSize: CGSize private let intrinsicSize: CGSize
private let intrinsicOffset: CGPoint private let intrinsicOffset: CGPoint
init(account: Account, reaction: ReactionGestureItem) { init(account: Account, reaction: ReactionGestureItem, maximizedReactionSize: CGFloat) {
self.reaction = reaction self.reaction = reaction
self.animationNode = AnimatedStickerNode() self.animationNode = AnimatedStickerNode()
@ -47,18 +44,29 @@ private final class ReactionNode: ASDisplayNode {
//self.animationNode.backgroundColor = .lightGray //self.animationNode.backgroundColor = .lightGray
var intrinsicSize = CGSize(width: maximizedReactionSize + 18.0, height: maximizedReactionSize + 18.0) var intrinsicSize = CGSize(width: maximizedReactionSize + 18.0, height: maximizedReactionSize + 18.0)
switch reaction.value.value {
case "😳": self.imageNode = ASImageNode()
intrinsicSize.width += 8.0 switch reaction {
intrinsicSize.height += 8.0 case let .reaction(value, _, file):
self.intrinsicOffset = CGPoint(x: 0.0, y: -4.0) switch value {
case "👍": case "😳":
intrinsicSize.width += 20.0 intrinsicSize.width += 8.0
intrinsicSize.height += 20.0 intrinsicSize.height += 8.0
self.intrinsicOffset = CGPoint(x: 0.0, y: 4.0) self.intrinsicOffset = CGPoint(x: 0.0, y: -4.0)
default: case "👍":
intrinsicSize.width += 20.0
intrinsicSize.height += 20.0
self.intrinsicOffset = CGPoint(x: 0.0, y: 4.0)
default:
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
}
self.animationNode.visibility = true
self.animationNode.setup(account: account, resource: file.resource, width: Int(intrinsicSize.width) * 2, height: Int(intrinsicSize.height) * 2, mode: .direct)
case .reply:
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0) self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
self.imageNode.image = UIImage(named: "Chat/Context Menu/ReactionReply", in: Bundle(for: ReactionNode.self), compatibleWith: nil)
} }
self.intrinsicSize = intrinsicSize self.intrinsicSize = intrinsicSize
super.init() super.init()
@ -66,15 +74,17 @@ private final class ReactionNode: ASDisplayNode {
//self.backgroundColor = .green //self.backgroundColor = .green
self.addSubnode(self.animationNode) self.addSubnode(self.animationNode)
self.animationNode.visibility = true self.addSubnode(self.imageNode)
self.animationNode.setup(account: account, resource: reaction.value.file.resource, width: Int(self.intrinsicSize.width) * 2, height: Int(self.intrinsicSize.height) * 2, mode: .direct)
self.animationNode.updateLayout(size: self.intrinsicSize) self.animationNode.updateLayout(size: self.intrinsicSize)
self.animationNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize) self.animationNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize)
self.imageNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicSize)
} }
func updateLayout(size: CGSize, scale: CGFloat, transition: ContainedViewLayoutTransition) { func updateLayout(size: CGSize, scale: CGFloat, transition: ContainedViewLayoutTransition) {
transition.updatePosition(node: self.animationNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true) transition.updatePosition(node: self.animationNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true)
transition.updateTransformScale(node: self.animationNode, scale: scale, beginWithCurrentState: true) transition.updateTransformScale(node: self.animationNode, scale: scale, beginWithCurrentState: true)
transition.updatePosition(node: self.imageNode, position: CGPoint(x: size.width / 2.0 + self.intrinsicOffset.x * scale, y: size.height / 2.0 + self.intrinsicOffset.y * scale), beginWithCurrentState: true)
transition.updateTransformScale(node: self.imageNode, scale: scale, beginWithCurrentState: true)
} }
func updateIsAnimating(_ isAnimating: Bool, animated: Bool) { func updateIsAnimating(_ isAnimating: Bool, animated: Bool) {
@ -87,43 +97,46 @@ private final class ReactionNode: ASDisplayNode {
} }
final class ReactionSelectionNode: ASDisplayNode { final class ReactionSelectionNode: ASDisplayNode {
private let account: Account
private let reactions: [ReactionGestureItem]
private let backgroundNode: ASImageNode private let backgroundNode: ASImageNode
private let backgroundShadowNode: ASImageNode private let backgroundShadowNode: ASImageNode
private let bubbleNodes: [(ASImageNode, ASImageNode)] private let bubbleNodes: [(ASImageNode, ASImageNode)]
private let reactionNodes: [ReactionNode] private var reactionNodes: [ReactionNode] = []
private var hasSelectedNode = false private var hasSelectedNode = false
private let hapticFeedback = HapticFeedback() private let hapticFeedback = HapticFeedback()
private var shadowBlur: CGFloat = 8.0
private var minimizedReactionSize: CGFloat = 30.0
private var maximizedReactionSize: CGFloat = 60.0
private var smallCircleSize: CGFloat = 8.0
public init(account: Account, reactions: [ReactionGestureItem]) { public init(account: Account, reactions: [ReactionGestureItem]) {
self.account = account
self.reactions = reactions
self.backgroundNode = ASImageNode() self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.displayWithoutProcessing = true self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.image = generateBubbleImage(foreground: .white, diameter: 42.0)
self.backgroundShadowNode = ASImageNode() self.backgroundShadowNode = ASImageNode()
self.backgroundShadowNode.displaysAsynchronously = false self.backgroundShadowNode.displaysAsynchronously = false
self.backgroundShadowNode.displayWithoutProcessing = true self.backgroundShadowNode.displayWithoutProcessing = true
self.backgroundShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: 42.0)
self.bubbleNodes = (0 ..< 2).map { i -> (ASImageNode, ASImageNode) in self.bubbleNodes = (0 ..< 2).map { i -> (ASImageNode, ASImageNode) in
let imageNode = ASImageNode() let imageNode = ASImageNode()
imageNode.image = generateBubbleImage(foreground: .white, diameter: CGFloat(i + 1) * 8.0)
imageNode.displaysAsynchronously = false imageNode.displaysAsynchronously = false
imageNode.displayWithoutProcessing = true imageNode.displayWithoutProcessing = true
let shadowNode = ASImageNode() let shadowNode = ASImageNode()
shadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: CGFloat(i + 1) * 8.0)
shadowNode.displaysAsynchronously = false shadowNode.displaysAsynchronously = false
shadowNode.displayWithoutProcessing = true shadowNode.displayWithoutProcessing = true
return (imageNode, shadowNode) return (imageNode, shadowNode)
} }
self.reactionNodes = reactions.map { reaction -> ReactionNode in
return ReactionNode(account: account, reaction: reaction)
}
super.init() super.init()
self.bubbleNodes.forEach { _, shadow in self.bubbleNodes.forEach { _, shadow in
@ -134,18 +147,50 @@ final class ReactionSelectionNode: ASDisplayNode {
self.addSubnode(foreground) self.addSubnode(foreground)
} }
self.addSubnode(self.backgroundNode) self.addSubnode(self.backgroundNode)
self.reactionNodes.forEach(self.addSubnode(_:))
} }
func updateLayout(constrainedSize: CGSize, startingPoint: CGPoint, offsetFromStart: CGFloat, isInitial: Bool) { func updateLayout(constrainedSize: CGSize, startingPoint: CGPoint, offsetFromStart: CGFloat, isInitial: Bool) {
let backgroundHeight: CGFloat = 42.0 let initialAnchorX = startingPoint.x
let reactionSpacing: CGFloat = 6.0
if isInitial && self.reactionNodes.isEmpty {
//let contentWidth: CGFloat = CGFloat(self.reactionNodes.count - 1) * (minimizedReactionSize) + maximizedReactionSize + CGFloat(self.reactionNodes.count + 1) * reactionSpacing
//contentWidth = CGFloat(self.reactionNodes.count - 1) * X + maximizedReactionSize + CGFloat(self.reactionNodes.count + 1) * 0.2 * X
// contentWidth - maximizedReactionSize = CGFloat(self.reactionNodes.count - 1) * X + CGFloat(self.reactionNodes.count + 1) * 0.2 * X
// (contentWidth - maximizedReactionSize) / (CGFloat(self.reactionNodes.count - 1) + CGFloat(self.reactionNodes.count + 1) * 0.2) = X
let availableContentWidth = max(100.0, initialAnchorX)
var minimizedReactionSize = (availableContentWidth - self.maximizedReactionSize) / (CGFloat(self.reactions.count - 1) + CGFloat(self.reactions.count + 1) * 0.2)
minimizedReactionSize = max(16.0, floor(minimizedReactionSize))
minimizedReactionSize = min(30.0, minimizedReactionSize)
self.minimizedReactionSize = minimizedReactionSize
self.shadowBlur = floor(minimizedReactionSize * 0.26)
self.smallCircleSize = 8.0
let backgroundHeight = floor(minimizedReactionSize * 1.4)
self.backgroundNode.image = generateBubbleImage(foreground: .white, diameter: backgroundHeight, shadowBlur: self.shadowBlur)
self.backgroundShadowNode.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: backgroundHeight, shadowBlur: self.shadowBlur)
for i in 0 ..< self.bubbleNodes.count {
self.bubbleNodes[i].0.image = generateBubbleImage(foreground: .white, diameter: CGFloat(i + 1) * self.smallCircleSize, shadowBlur: self.shadowBlur)
self.bubbleNodes[i].1.image = generateBubbleShadowImage(shadow: UIColor(white: 0.0, alpha: 0.2), diameter: CGFloat(i + 1) * self.smallCircleSize, shadowBlur: self.shadowBlur)
}
self.reactionNodes = self.reactions.map { reaction -> ReactionNode in
return ReactionNode(account: self.account, reaction: reaction, maximizedReactionSize: self.maximizedReactionSize)
}
self.reactionNodes.forEach(self.addSubnode(_:))
}
let backgroundHeight: CGFloat = floor(self.minimizedReactionSize * 1.4)
let reactionSpacing: CGFloat = floor(self.minimizedReactionSize * 0.2)
let minimizedReactionVerticalInset: CGFloat = floor((backgroundHeight - minimizedReactionSize) / 2.0) let minimizedReactionVerticalInset: CGFloat = floor((backgroundHeight - minimizedReactionSize) / 2.0)
let contentWidth: CGFloat = CGFloat(self.reactionNodes.count - 1) * (minimizedReactionSize) + maximizedReactionSize + CGFloat(self.reactionNodes.count + 1) * reactionSpacing let contentWidth: CGFloat = CGFloat(self.reactionNodes.count - 1) * (minimizedReactionSize) + maximizedReactionSize + CGFloat(self.reactionNodes.count + 1) * reactionSpacing
var backgroundFrame = CGRect(origin: CGPoint(x: -shadowBlur, y: -shadowBlur), size: CGSize(width: contentWidth + shadowBlur * 2.0, height: backgroundHeight + shadowBlur * 2.0)) var backgroundFrame = CGRect(origin: CGPoint(x: -shadowBlur, y: -shadowBlur), size: CGSize(width: contentWidth + shadowBlur * 2.0, height: backgroundHeight + shadowBlur * 2.0))
backgroundFrame = backgroundFrame.offsetBy(dx: startingPoint.x - contentWidth + backgroundHeight / 2.0 - 52.0, dy: startingPoint.y - backgroundHeight - 16.0) backgroundFrame = backgroundFrame.offsetBy(dx: initialAnchorX - contentWidth + backgroundHeight / 2.0, dy: startingPoint.y - backgroundHeight - 16.0)
self.backgroundNode.frame = backgroundFrame self.backgroundNode.frame = backgroundFrame
self.backgroundShadowNode.frame = backgroundFrame self.backgroundShadowNode.frame = backgroundFrame
@ -201,11 +246,11 @@ final class ReactionSelectionNode: ASDisplayNode {
reactionX += reactionSize + reactionSpacing reactionX += reactionSize + reactionSpacing
} }
let mainBubbleFrame = CGRect(origin: CGPoint(x: anchorX - 8.0 - shadowBlur, y: backgroundFrame.maxY - shadowBlur - 8.0 - shadowBlur), size: CGSize(width: 16.0 + shadowBlur * 2.0, height: 16.0 + shadowBlur * 2.0)) let mainBubbleFrame = CGRect(origin: CGPoint(x: anchorX - self.smallCircleSize - shadowBlur, y: backgroundFrame.maxY - shadowBlur - self.smallCircleSize - shadowBlur), size: CGSize(width: self.smallCircleSize * 2.0 + shadowBlur * 2.0, height: self.smallCircleSize * 2.0 + shadowBlur * 2.0))
self.bubbleNodes[1].0.frame = mainBubbleFrame self.bubbleNodes[1].0.frame = mainBubbleFrame
self.bubbleNodes[1].1.frame = mainBubbleFrame self.bubbleNodes[1].1.frame = mainBubbleFrame
let secondaryBubbleFrame = CGRect(origin: CGPoint(x: mainBubbleFrame.midX - 9.0 - (8.0 + shadowBlur * 2.0) / 2.0, y: mainBubbleFrame.midY + 12.0 - (8.0 + shadowBlur * 2.0) / 2.0), size: CGSize(width: 8.0 + shadowBlur * 2.0, height: 8.0 + shadowBlur * 2.0)) let secondaryBubbleFrame = CGRect(origin: CGPoint(x: mainBubbleFrame.midX - floor(self.smallCircleSize * 0.88) - (self.smallCircleSize + shadowBlur * 2.0) / 2.0, y: mainBubbleFrame.midY + floor(self.smallCircleSize * 4.0 / 3.0) - (self.smallCircleSize + shadowBlur * 2.0) / 2.0), size: CGSize(width: self.smallCircleSize + shadowBlur * 2.0, height: self.smallCircleSize + shadowBlur * 2.0))
self.bubbleNodes[0].0.frame = secondaryBubbleFrame self.bubbleNodes[0].0.frame = secondaryBubbleFrame
self.bubbleNodes[0].1.frame = secondaryBubbleFrame self.bubbleNodes[0].1.frame = secondaryBubbleFrame
} }

View File

@ -6,13 +6,16 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
private var validatedGesture = false private var validatedGesture = false
private var firstLocation: CGPoint = CGPoint() private var firstLocation: CGPoint = CGPoint()
private var currentLocation: CGPoint = CGPoint()
private var currentReactions: [ReactionGestureItem] = [] private var currentReactions: [ReactionGestureItem] = []
private var isActivated = false private var isActivated = false
private var isAwaitingCompletion = false private var isAwaitingCompletion = false
private weak var currentContainer: ReactionSelectionParentNode? private weak var currentContainer: ReactionSelectionParentNode?
private var activationTimer: Timer?
public var availableReactions: (() -> [ReactionGestureItem])? public var availableReactions: (() -> [ReactionGestureItem])?
public var getReactionContainer: (() -> ReactionSelectionParentNode?)? public var getReactionContainer: (() -> ReactionSelectionParentNode?)?
public var began: (() -> Void)?
public var updateOffset: ((CGFloat, Bool) -> Void)? public var updateOffset: ((CGFloat, Bool) -> Void)?
public var completed: ((ReactionGestureItem?) -> Void)? public var completed: ((ReactionGestureItem?) -> Void)?
public var displayReply: ((CGFloat) -> Void)? public var displayReply: ((CGFloat) -> Void)?
@ -31,6 +34,8 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
self.currentReactions = [] self.currentReactions = []
self.isActivated = false self.isActivated = false
self.isAwaitingCompletion = false self.isAwaitingCompletion = false
self.activationTimer?.invalidate()
self.activationTimer = nil
} }
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
@ -40,6 +45,7 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
self.currentReactions = availableReactions self.currentReactions = availableReactions
let touch = touches.first! let touch = touches.first!
self.firstLocation = touch.location(in: nil) self.firstLocation = touch.location(in: nil)
self.currentLocation = self.firstLocation
} else { } else {
self.state = .failed self.state = .failed
} }
@ -55,6 +61,7 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
guard let location = touches.first?.location(in: nil) else { guard let location = touches.first?.location(in: nil) else {
return return
} }
self.currentLocation = location
var translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y) var translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y)
@ -72,8 +79,38 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
self.validatedGesture = true self.validatedGesture = true
self.firstLocation = location self.firstLocation = location
translation = CGPoint() translation = CGPoint()
self.began?()
self.updateOffset?(0.0, false) self.updateOffset?(0.0, false)
updatedOffset = true updatedOffset = true
self.activationTimer?.invalidate()
final class TimerTarget: NSObject {
let f: () -> Void
init(_ f: @escaping () -> Void) {
self.f = f
}
@objc func event() {
self.f()
}
}
let activationTimer = Timer(timeInterval: 0.3, target: TimerTarget { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.activationTimer = nil
if strongSelf.validatedGesture {
let location = strongSelf.currentLocation
if !strongSelf.currentReactions.isEmpty, let reactionContainer = strongSelf.getReactionContainer?() {
strongSelf.currentContainer = reactionContainer
let reactionContainerLocation = reactionContainer.view.convert(location, from: nil)
reactionContainer.displayReactions(strongSelf.currentReactions, at: reactionContainerLocation)
}
}
}, selector: #selector(TimerTarget.event), userInfo: nil, repeats: false)
self.activationTimer = activationTimer
RunLoop.main.add(activationTimer, forMode: .common)
} }
} }
@ -85,11 +122,6 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
if absTranslationX > 40.0 { if absTranslationX > 40.0 {
self.isActivated = true self.isActivated = true
self.displayReply?(-min(0.0, translation.x)) self.displayReply?(-min(0.0, translation.x))
if !self.currentReactions.isEmpty, let reactionContainer = self.getReactionContainer?() {
self.currentContainer = reactionContainer
let reactionContainerLocation = reactionContainer.view.convert(location, from: nil)
reactionContainer.displayReactions(self.currentReactions, at: reactionContainerLocation)
}
} }
} else { } else {
if let reactionContainer = self.currentContainer { if let reactionContainer = self.currentContainer {
@ -111,8 +143,8 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer {
if self.validatedGesture { if self.validatedGesture {
let translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y) let translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y)
if let reaction = self.currentContainer?.selectedReaction() { if let reaction = self.currentContainer?.selectedReaction() {
self.completed?(reaction)
self.isAwaitingCompletion = true self.isAwaitingCompletion = true
self.completed?(reaction)
} else { } else {
if translation.x < -40.0 { if translation.x < -40.0 {
self.currentContainer?.dismissReactions(into: nil, hideTarget: false) self.currentContainer?.dismissReactions(into: nil, hideTarget: false)

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ReplyReaction@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ReplyReaction@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -418,9 +418,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
} }
} }
} }
if !item.controllerInteraction.canSetupReply(item.message) {
//return []
}
let reactions: [(String, String)] = [ let reactions: [(String, String)] = [
("😒", "Sad"), ("😒", "Sad"),
@ -433,14 +430,23 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
var result: [ReactionGestureItem] = [] var result: [ReactionGestureItem] = []
for (value, text) in reactions { for (value, text) in reactions {
if let file = item.associatedData.animatedEmojiStickers[value]?.file { if let file = item.associatedData.animatedEmojiStickers[value]?.file {
result.append(ReactionGestureItem(value: ReactionGestureItemValue(value: value, text: text, file: file))) result.append(.reaction(value: value, text: text, file: file))
} }
} }
if item.controllerInteraction.canSetupReply(item.message) {
result.append(.reply)
}
return result return result
} }
reactionRecognizer.getReactionContainer = { [weak self] in reactionRecognizer.getReactionContainer = { [weak self] in
return self?.item?.controllerInteraction.reactionContainerNode() return self?.item?.controllerInteraction.reactionContainerNode()
} }
reactionRecognizer.began = { [weak self] in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
item.controllerInteraction.cancelInteractiveKeyboardGestures()
}
reactionRecognizer.updateOffset = { [weak self] offset, animated in reactionRecognizer.updateOffset = { [weak self] offset, animated in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -479,11 +485,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
guard let strongSelf = self, let item = strongSelf.item else { guard let strongSelf = self, let item = strongSelf.item else {
return return
} }
if strongSelf.swipeToReplyNode == nil { if strongSelf.swipeToReplyFeedback == nil {
if strongSelf.swipeToReplyFeedback == nil { strongSelf.swipeToReplyFeedback = HapticFeedback()
strongSelf.swipeToReplyFeedback = HapticFeedback() }
} strongSelf.swipeToReplyFeedback?.tap()
strongSelf.swipeToReplyFeedback?.tap() if strongSelf.swipeToReplyNode == nil, false {
let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper)) let swipeToReplyNode = ChatMessageSwipeToReplyNode(fillColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: item.presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: item.presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: item.presentationData.theme.wallpaper))
strongSelf.swipeToReplyNode = swipeToReplyNode strongSelf.swipeToReplyNode = swipeToReplyNode
strongSelf.insertSubnode(swipeToReplyNode, belowSubnode: strongSelf.messageAccessibilityArea) strongSelf.insertSubnode(swipeToReplyNode, belowSubnode: strongSelf.messageAccessibilityArea)
@ -497,8 +503,28 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
return return
} }
if let item = strongSelf.item, let reaction = reaction { if let item = strongSelf.item, let reaction = reaction {
strongSelf.awaitingAppliedReaction = reaction.value.value switch reaction {
item.controllerInteraction.updateMessageReaction(item.message.id, reaction.value.value) case let .reaction(value, _, _):
strongSelf.awaitingAppliedReaction = value
item.controllerInteraction.updateMessageReaction(item.message.id, value)
case .reply:
strongSelf.reactionRecognizer?.complete(into: nil, hideTarget: false)
var bounds = strongSelf.bounds
let offset = bounds.origin.x
bounds.origin.x = 0.0
strongSelf.bounds = bounds
if !offset.isZero {
strongSelf.layer.animateBoundsOriginXAdditive(from: offset, to: 0.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionSpring)
}
if let swipeToReplyNode = strongSelf.swipeToReplyNode {
strongSelf.swipeToReplyNode = nil
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in
swipeToReplyNode?.removeFromSupernode()
})
swipeToReplyNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
}
item.controllerInteraction.setupReply(item.message.id)
}
} else { } else {
strongSelf.reactionRecognizer?.complete(into: nil, hideTarget: false) strongSelf.reactionRecognizer?.complete(into: nil, hideTarget: false)
var bounds = strongSelf.bounds var bounds = strongSelf.bounds