mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Fix context menu animations
This commit is contained in:
parent
9c7692e21e
commit
6692a819f2
@ -217,6 +217,8 @@
|
||||
"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_REMINDER_TITLE" = "🗓 Reminder";
|
||||
|
||||
"LOCAL_MESSAGE_FWDS" = "%1$@ forwarded you %2$d messages";
|
||||
"LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded messages";
|
||||
"LOCAL_CHAT_MESSAGE_FWDS" = "%1$@ forwarded %2$d messages";
|
||||
@ -4529,7 +4531,7 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"VoiceOver.Chat.RecordModeVoiceMessage" = "Voice message";
|
||||
"VoiceOver.Chat.RecordModeVoiceMessageInfo" = "Double tap and hold to record voice message. Slide up to pin recording, slide left to cancel. Double tap to switch to video.";
|
||||
"VoiceOver.Chat.RecordModeVideoMessage" = "Video message";
|
||||
"VoiceOver.Chat.RecordModeVideoMessageInfo" = "Double tap and hold to record voice message. Slide up to pin recording, slide left to cancel. Double tap to switch to audio.";
|
||||
"VoiceOver.Chat.RecordModeVideoMessageInfo" = "Double tap and hold to record video message. Slide up to pin recording, slide left to cancel. Double tap to switch to audio.";
|
||||
"VoiceOver.Chat.Message" = "Message";
|
||||
"VoiceOver.Chat.YourMessage" = "Your message";
|
||||
"VoiceOver.Chat.ReplyFrom" = "Reply to message from: %@";
|
||||
|
@ -23,6 +23,7 @@ public enum ContextMenuActionItemTextColor {
|
||||
public enum ContextMenuActionResult {
|
||||
case `default`
|
||||
case dismissWithoutContent
|
||||
case custom(ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
public final class ContextMenuActionItem {
|
||||
@ -338,7 +339,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
if let _ = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.25 * animationDurationFactor, from: 0.0, to: 1.0, update: { [weak self] value in
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 1.0, update: { [weak self] value in
|
||||
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
|
||||
}, completion: { [weak self] in
|
||||
self?.didCompleteAnimationIn = true
|
||||
@ -372,9 +373,27 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
|
||||
func animateOut(result: ContextMenuActionResult, completion: @escaping () -> Void) {
|
||||
var transitionDuration: Double = 0.2
|
||||
var transitionCurve: ContainedViewLayoutTransitionCurve = .easeInOut
|
||||
|
||||
switch result {
|
||||
case let .custom(value):
|
||||
switch value {
|
||||
case let .animated(duration, curve):
|
||||
transitionDuration = duration
|
||||
transitionCurve = curve
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
self.isAnimatingOut = true
|
||||
|
||||
self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false)
|
||||
|
||||
var completedEffect = false
|
||||
var completedContentNode = false
|
||||
var completedActionsNode = false
|
||||
@ -384,8 +403,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if let putBackInfo = putBackInfo, let contentParentNode = self.contentParentNode, let parentSupernode = contentParentNode.supernode {
|
||||
self.originalProjectedContentViewFrame = (parentSupernode.view.convert(contentParentNode.frame, to: self.view), contentParentNode.view.convert(contentParentNode.contentRect, to: self.view))
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: putBackInfo.contentAreaInScreenSpace, duration: 0.2 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: putBackInfo.contentAreaInScreenSpace.minY, duration: 0.2 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: putBackInfo.contentAreaInScreenSpace, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: putBackInfo.contentAreaInScreenSpace.minY, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
let contentParentNode = self.contentParentNode
|
||||
@ -395,7 +414,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let intermediateCompletion: () -> Void = { [weak contentParentNode] in
|
||||
if completedEffect && completedContentNode && completedActionsNode {
|
||||
switch result {
|
||||
case .default:
|
||||
case .default, .custom:
|
||||
if let contentParentNode = contentParentNode {
|
||||
contentParentNode.addSubnode(contentParentNode.contentNode)
|
||||
contentParentNode.isExtractedToContextPreview = false
|
||||
@ -414,7 +433,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut, animations: { [weak self] in
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration, curve: .easeInOut, animations: { [weak self] in
|
||||
self?.effectView.effect = nil
|
||||
})
|
||||
}
|
||||
@ -442,22 +461,31 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
})
|
||||
}
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false)
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completedActionsNode = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
self.actionsContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2 * animationDurationFactor, removeOnCompletion: false)
|
||||
if case .default = result, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode {
|
||||
|
||||
let animateOutToItem: Bool
|
||||
switch result {
|
||||
case .default, .custom:
|
||||
animateOutToItem = true
|
||||
case .dismissWithoutContent:
|
||||
animateOutToItem = false
|
||||
}
|
||||
|
||||
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode {
|
||||
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
|
||||
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: 0.2 * animationDurationFactor, removeOnCompletion: false, additive: true)
|
||||
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
|
||||
let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY)
|
||||
self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: contentContainerOffset, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: contentContainerOffset, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completedContentNode = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
contentParentNode.updateAbsoluteRect?(self.contentContainerNode.frame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y + contentContainerOffset.y), self.bounds.size)
|
||||
contentParentNode.applyAbsoluteOffset?(-contentContainerOffset.y, .easeInOut, 0.2)
|
||||
contentParentNode.applyAbsoluteOffset?(-contentContainerOffset.y, transitionCurve, transitionDuration)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.animateOut(to: CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: contentParentNode.contentRect.size), animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
||||
@ -471,10 +499,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
contentParentNode.isExtractedToContextPreview = false
|
||||
contentParentNode.isExtractedToContextPreviewUpdated?(false)
|
||||
|
||||
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
self.contentContainerNode.allowsGroupOpacity = true
|
||||
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completedContentNode = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
//self.contentContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.animateOut(to: nil, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
||||
@ -535,6 +565,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition, previousActionsContainerNode: ContextActionsContainerNode?) {
|
||||
if self.isAnimatingOut {
|
||||
return
|
||||
}
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
var actionsContainerTransition = transition
|
||||
|
@ -160,7 +160,7 @@ public extension CALayer {
|
||||
self.add(animation, forKey: keyPath)
|
||||
}
|
||||
|
||||
public func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
public func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double = 0.0, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
let animation: CABasicAnimation
|
||||
if #available(iOS 9.0, *) {
|
||||
animation = makeSpringBounceAnimation(keyPath, initialVelocity, damping)
|
||||
@ -181,6 +181,11 @@ public extension CALayer {
|
||||
speed = Float(1.0) / k
|
||||
}
|
||||
|
||||
if !delay.isZero {
|
||||
animation.beginTime = CACurrentMediaTime() + delay
|
||||
animation.fillMode = .both
|
||||
}
|
||||
|
||||
animation.speed = speed * Float(animation.duration / duration)
|
||||
animation.isAdditive = additive
|
||||
|
||||
|
@ -54,7 +54,7 @@ final class ContextMenuActionNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = UIColor(white: 0.0, alpha: 0.8)
|
||||
self.backgroundColor = UIColor(rgb: 0x2f2f2f)
|
||||
if let textNode = self.textNode {
|
||||
self.addSubnode(textNode)
|
||||
}
|
||||
@ -63,7 +63,7 @@ final class ContextMenuActionNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
self?.backgroundColor = highlighted ? UIColor(white: 0.0, alpha: 0.4) : UIColor(white: 0.0, alpha: 0.8)
|
||||
self?.backgroundColor = highlighted ? UIColor(rgb: 0x8c8e8e) : UIColor(rgb: 0x2f2f2f)
|
||||
}
|
||||
self.view.addSubview(self.button)
|
||||
self.addSubnode(self.actionArea)
|
||||
|
@ -27,7 +27,7 @@ public final class ContextMenuContainerNode: ASDisplayNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = UIColor(rgb: 0xeaecec)
|
||||
self.backgroundColor = UIColor(rgb: 0x8c8e8e)
|
||||
//self.view.addSubview(self.effectView)
|
||||
//self.effectView.mask = self.maskView
|
||||
self.view.mask = self.maskView
|
||||
|
@ -38,9 +38,9 @@ private final class ContextMenuContentScrollNode: ASDisplayNode {
|
||||
self.rightShadow.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
|
||||
self.leftOverscrollNode = ASDisplayNode()
|
||||
self.leftOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8)
|
||||
//self.leftOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8)
|
||||
self.rightOverscrollNode = ASDisplayNode()
|
||||
self.rightOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8)
|
||||
//self.rightOverscrollNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -55,8 +55,8 @@ private final class ContextMenuContentScrollNode: ASDisplayNode {
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
self.view.addGestureRecognizer(panRecognizer)
|
||||
//let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
//self.view.addGestureRecognizer(panRecognizer)
|
||||
}
|
||||
|
||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
@ -228,9 +228,12 @@ final class ContextMenuNode: ASDisplayNode {
|
||||
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: containerPosition.x, y: containerPosition.y + (self.arrowOnBottom ? 1.0 : -1.0) * self.containerNode.bounds.size.height / 2.0)), to: NSValue(cgPoint: containerPosition), keyPath: "position", duration: 0.4)
|
||||
}
|
||||
|
||||
self.containerNode.allowsGroupOpacity = true
|
||||
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak self] _ in
|
||||
self?.containerNode.allowsGroupOpacity = false
|
||||
self.allowsGroupOpacity = true
|
||||
self.layer.rasterizationScale = UIScreen.main.scale
|
||||
self.layer.shouldRasterize = true
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak self] _ in
|
||||
self?.allowsGroupOpacity = false
|
||||
self?.layer.shouldRasterize = false
|
||||
})
|
||||
|
||||
if let feedback = self.feedback {
|
||||
@ -239,9 +242,12 @@ final class ContextMenuNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func animateOut(bounce: Bool, completion: @escaping () -> Void) {
|
||||
self.containerNode.allowsGroupOpacity = true
|
||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
self?.containerNode.allowsGroupOpacity = false
|
||||
self.allowsGroupOpacity = true
|
||||
self.layer.rasterizationScale = UIScreen.main.scale
|
||||
self.layer.shouldRasterize = true
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
self?.allowsGroupOpacity = false
|
||||
self?.layer.shouldRasterize = false
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
@ -107,6 +107,9 @@ void MyAesIgeEncrypt(const void *inBytes, int length, void *outBytes, const void
|
||||
}
|
||||
|
||||
void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) {
|
||||
assert(length % 16 == 0);
|
||||
assert(length >= 0);
|
||||
|
||||
unsigned char aesIv[AES_BLOCK_SIZE];
|
||||
memcpy(aesIv, iv, AES_BLOCK_SIZE);
|
||||
unsigned char ccIv[AES_BLOCK_SIZE];
|
||||
|
@ -32,6 +32,99 @@
|
||||
# import <MtProtoKit/MTDNS.h>
|
||||
#endif
|
||||
|
||||
#import <openssl/bn.h>
|
||||
|
||||
static BIGNUM *get_y2(BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) {
|
||||
// returns y^2 = x^3 + 486662 * x^2 + x
|
||||
BIGNUM *y = BN_dup(x);
|
||||
assert(y != NULL);
|
||||
BIGNUM *coef = BN_new();
|
||||
BN_set_word(coef, 486662);
|
||||
BN_mod_add(y, y, coef, mod, big_num_context);
|
||||
BN_mod_mul(y, y, x, mod, big_num_context);
|
||||
BN_one(coef);
|
||||
BN_mod_add(y, y, coef, mod, big_num_context);
|
||||
BN_mod_mul(y, y, x, mod, big_num_context);
|
||||
BN_clear_free(coef);
|
||||
return y;
|
||||
}
|
||||
|
||||
static BIGNUM *get_double_x(BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) {
|
||||
// returns x_2 =(x^2 - 1)^2/(4*y^2)
|
||||
BIGNUM *denominator = get_y2(x, mod, big_num_context);
|
||||
assert(denominator != NULL);
|
||||
BIGNUM *coef = BN_new();
|
||||
BN_set_word(coef, 4);
|
||||
BN_mod_mul(denominator, denominator, coef, mod, big_num_context);
|
||||
|
||||
BIGNUM *numerator = BN_new();
|
||||
assert(numerator != NULL);
|
||||
BN_mod_mul(numerator, x, x, mod, big_num_context);
|
||||
BN_one(coef);
|
||||
BN_mod_sub(numerator, numerator, coef, mod, big_num_context);
|
||||
BN_mod_mul(numerator, numerator, numerator, mod, big_num_context);
|
||||
|
||||
BN_mod_inverse(denominator, denominator, mod, big_num_context);
|
||||
BN_mod_mul(numerator, numerator, denominator, mod, big_num_context);
|
||||
|
||||
BN_clear_free(coef);
|
||||
BN_clear_free(denominator);
|
||||
return numerator;
|
||||
}
|
||||
|
||||
static void generate_public_key(unsigned char key[32]) {
|
||||
BIGNUM *mod = NULL;
|
||||
BN_hex2bn(&mod, "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed");
|
||||
BIGNUM *pow = NULL;
|
||||
BN_hex2bn(&pow, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6");
|
||||
BN_CTX *big_num_context = BN_CTX_new();
|
||||
assert(big_num_context != NULL);
|
||||
|
||||
BIGNUM *x = BN_new();
|
||||
while (1) {
|
||||
int randomResult = SecRandomCopyBytes(kSecRandomDefault, 32, key);
|
||||
assert(randomResult == errSecSuccess);
|
||||
|
||||
key[31] &= 127;
|
||||
BN_bin2bn(key, 32, x);
|
||||
assert(x != NULL);
|
||||
BN_mod_mul(x, x, x, mod, big_num_context);
|
||||
|
||||
BIGNUM *y = get_y2(x, mod, big_num_context);
|
||||
|
||||
BIGNUM *r = BN_new();
|
||||
BN_mod_exp(r, y, pow, mod, big_num_context);
|
||||
BN_clear_free(y);
|
||||
if (BN_is_one(r)) {
|
||||
BN_clear_free(r);
|
||||
break;
|
||||
}
|
||||
BN_clear_free(r);
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
BIGNUM *x2 = get_double_x(x, mod, big_num_context);
|
||||
BN_clear_free(x);
|
||||
x = x2;
|
||||
}
|
||||
|
||||
int num_size = BN_num_bytes(x);
|
||||
assert(num_size <= 32);
|
||||
memset(key, '\0', 32 - num_size);
|
||||
BN_bn2bin(x, key + (32 - num_size));
|
||||
for (i = 0; i < 16; i++) {
|
||||
unsigned char t = key[i];
|
||||
key[i] = key[31 - i];
|
||||
key[31 - i] = t;
|
||||
}
|
||||
|
||||
BN_clear_free(x);
|
||||
BN_CTX_free(big_num_context);
|
||||
BN_clear_free(pow);
|
||||
BN_clear_free(mod);
|
||||
}
|
||||
|
||||
@interface MTTcpConnectionData : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSString *ip;
|
||||
@ -506,8 +599,8 @@ struct ctr_state {
|
||||
[helloData appendBytes:s6 length:117];
|
||||
|
||||
uint8_t r2[32];
|
||||
result = SecRandomCopyBytes(nil, 32, r2);
|
||||
assert(result == errSecSuccess);
|
||||
generate_public_key(r2);
|
||||
|
||||
[helloData appendBytes:r2 length:32];
|
||||
|
||||
uint8_t s9[35] = { 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x15 };
|
||||
|
@ -81,8 +81,8 @@ func syncContactsOnce(network: Network, postbox: Postbox, accountPeerId: PeerId)
|
||||
|
||||
if wasEmpty {
|
||||
var insertSignal: Signal<Void, NoError> = .complete()
|
||||
for s in stride(from: 0, to: peers.count, by: 100) {
|
||||
let partPeers = Array(peers[s ..< min(s + 100, peers.count)])
|
||||
for s in stride(from: 0, to: peers.count, by: 500) {
|
||||
let partPeers = Array(peers[s ..< min(s + 500, peers.count)])
|
||||
let partSignal = postbox.transaction { transaction -> Void in
|
||||
updatePeers(transaction: transaction, peers: partPeers, update: { return $1 })
|
||||
var updatedIds = transaction.getContactPeerIds()
|
||||
|
@ -308,7 +308,7 @@ private func pushDeviceContacts(postbox: Postbox, network: Network, importableCo
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
private let importBatchCount: Int = 100
|
||||
private let importBatchCount: Int = 500
|
||||
|
||||
private func pushDeviceContactData(postbox: Postbox, network: Network, contacts: [(DeviceContactNormalizedPhoneNumber, ImportableDeviceContactData)]) -> Signal<PushDeviceContactsResult, NoError> {
|
||||
var batches: Signal<PushDeviceContactsResult, NoError> = .single(PushDeviceContactsResult(addedReimportAttempts: [:]))
|
||||
|
@ -210,8 +210,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
)
|
||||
|
||||
let message = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))),
|
||||
infoPrimaryTextColor: .white,
|
||||
infoLinkTextColor: accentColor,
|
||||
|
@ -180,8 +180,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
||||
let buttonStrokeColor = accentColor.withMultiplied(hue: 1.014, saturation: 0.56, brightness: 0.64).withAlphaComponent(0.15)
|
||||
|
||||
let message = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)),
|
||||
infoPrimaryTextColor: UIColor(rgb: 0xffffff),
|
||||
infoLinkTextColor: accentColor,
|
||||
|
@ -190,8 +190,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
||||
)
|
||||
|
||||
let message = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xe8ecf0), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5).withAlphaComponent(0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE1FFC7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xe1ffc7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x008c09, alpha: 0.8), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x00a700), accentControlColor: UIColor(rgb: 0x3fc33b), mediaActiveControlColor: UIColor(rgb: 0x3fc33b), mediaInactiveControlColor: UIColor(rgb: 0x93d987), pendingActivityColor: UIColor(rgb: 0x42b649), fileTitleColor: UIColor(rgb: 0x3faa3c), fileDescriptionColor: UIColor(rgb: 0x6fb26a), fileDurationColor: UIColor(rgb: 0x008c09, alpha: 0.8), mediaPlaceholderColor: UIColor(rgb: 0xd2f2b6), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x3fc33b)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white)),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xe8ecf0), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5).withAlphaComponent(0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE1FFC7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xe1ffc7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x008c09, alpha: 0.8), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x00a700), accentControlColor: UIColor(rgb: 0x3fc33b), mediaActiveControlColor: UIColor(rgb: 0x3fc33b), mediaInactiveControlColor: UIColor(rgb: 0x93d987), pendingActivityColor: UIColor(rgb: 0x42b649), fileTitleColor: UIColor(rgb: 0x3faa3c), fileDescriptionColor: UIColor(rgb: 0x6fb26a), fileDurationColor: UIColor(rgb: 0x008c09, alpha: 0.8), mediaPlaceholderColor: UIColor(rgb: 0xd2f2b6), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x3fc33b)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))),
|
||||
infoPrimaryTextColor: UIColor(rgb: 0x000000),
|
||||
infoLinkTextColor: UIColor(rgb: 0x004bad),
|
||||
@ -208,8 +208,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
||||
)
|
||||
|
||||
let messageDay = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))),
|
||||
infoPrimaryTextColor: UIColor(rgb: 0x000000),
|
||||
infoLinkTextColor: UIColor(rgb: 0x004bad),
|
||||
|
@ -541,8 +541,10 @@ public final class PresentationThemePartedColors {
|
||||
public let actionButtonsFillColor: PresentationThemeVariableColor
|
||||
public let actionButtonsStrokeColor: PresentationThemeVariableColor
|
||||
public let actionButtonsTextColor: PresentationThemeVariableColor
|
||||
public let textSelectionColor: UIColor
|
||||
public let textSelectionKnobColor: UIColor
|
||||
|
||||
public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor) {
|
||||
public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor, textSelectionColor: UIColor, textSelectionKnobColor: UIColor) {
|
||||
self.bubble = bubble
|
||||
self.primaryTextColor = primaryTextColor
|
||||
self.secondaryTextColor = secondaryTextColor
|
||||
@ -563,6 +565,8 @@ public final class PresentationThemePartedColors {
|
||||
self.actionButtonsFillColor = actionButtonsFillColor
|
||||
self.actionButtonsStrokeColor = actionButtonsStrokeColor
|
||||
self.actionButtonsTextColor = actionButtonsTextColor
|
||||
self.textSelectionColor = textSelectionColor
|
||||
self.textSelectionKnobColor = textSelectionKnobColor
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -915,33 +915,38 @@ extension PresentationThemePartedColors: Codable {
|
||||
case actionButtonsBg
|
||||
case actionButtonsStroke
|
||||
case actionButtonsText
|
||||
case textSelection
|
||||
case textSelectionKnob
|
||||
}
|
||||
|
||||
public convenience init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.init(bubble: try values.decode(PresentationThemeBubbleColor.self, forKey: .bubble),
|
||||
primaryTextColor: try decodeColor(values, .primaryText),
|
||||
secondaryTextColor: try decodeColor(values, .secondaryText),
|
||||
linkTextColor: try decodeColor(values, .linkText),
|
||||
linkHighlightColor: try decodeColor(values, .linkHighlight),
|
||||
scamColor: try decodeColor(values, .scam),
|
||||
textHighlightColor: try decodeColor(values, .textHighlight),
|
||||
accentTextColor: try decodeColor(values, .accentText),
|
||||
accentControlColor: try decodeColor(values, .accentControl),
|
||||
mediaActiveControlColor: try decodeColor(values, .mediaActiveControl),
|
||||
mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl),
|
||||
pendingActivityColor: try decodeColor(values, .pendingActivity),
|
||||
fileTitleColor: try decodeColor(values, .fileTitle),
|
||||
fileDescriptionColor: try decodeColor(values, .fileDescription),
|
||||
fileDurationColor: try decodeColor(values, .fileDuration),
|
||||
mediaPlaceholderColor: try decodeColor(values, .mediaPlaceholder),
|
||||
polls: try values.decode(PresentationThemeChatBubblePolls.self, forKey: .polls),
|
||||
actionButtonsFillColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsBg),
|
||||
actionButtonsStrokeColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsStroke),
|
||||
actionButtonsTextColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsText))
|
||||
self.init(
|
||||
bubble: try values.decode(PresentationThemeBubbleColor.self, forKey: .bubble),
|
||||
primaryTextColor: try decodeColor(values, .primaryText),
|
||||
secondaryTextColor: try decodeColor(values, .secondaryText),
|
||||
linkTextColor: try decodeColor(values, .linkText),
|
||||
linkHighlightColor: try decodeColor(values, .linkHighlight),
|
||||
scamColor: try decodeColor(values, .scam),
|
||||
textHighlightColor: try decodeColor(values, .textHighlight),
|
||||
accentTextColor: try decodeColor(values, .accentText),
|
||||
accentControlColor: try decodeColor(values, .accentControl),
|
||||
mediaActiveControlColor: try decodeColor(values, .mediaActiveControl),
|
||||
mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl),
|
||||
pendingActivityColor: try decodeColor(values, .pendingActivity),
|
||||
fileTitleColor: try decodeColor(values, .fileTitle),
|
||||
fileDescriptionColor: try decodeColor(values, .fileDescription),
|
||||
fileDurationColor: try decodeColor(values, .fileDuration),
|
||||
mediaPlaceholderColor: try decodeColor(values, .mediaPlaceholder),
|
||||
polls: try values.decode(PresentationThemeChatBubblePolls.self, forKey: .polls),
|
||||
actionButtonsFillColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsBg),
|
||||
actionButtonsStrokeColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsStroke),
|
||||
actionButtonsTextColor: try values.decode(PresentationThemeVariableColor.self, forKey: .actionButtonsText),
|
||||
textSelectionColor: try decodeColor(values, .textSelection),
|
||||
textSelectionKnobColor: try decodeColor(values, .textSelectionKnob)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var values = encoder.container(keyedBy: CodingKeys.self)
|
||||
try values.encode(self.bubble, forKey: .bubble)
|
||||
@ -964,6 +969,8 @@ extension PresentationThemePartedColors: Codable {
|
||||
try values.encode(self.actionButtonsFillColor, forKey: .actionButtonsBg)
|
||||
try values.encode(self.actionButtonsStrokeColor, forKey: .actionButtonsStroke)
|
||||
try values.encode(self.actionButtonsTextColor, forKey: .actionButtonsText)
|
||||
try encodeColor(&values, self.textSelectionColor, .textSelection)
|
||||
try encodeColor(&values, self.textSelectionKnobColor, .textSelectionKnob)
|
||||
}
|
||||
}
|
||||
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_lt_unstar.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/ic_lt_unstar.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Unstar.imageset/ic_lt_unstar.pdf
vendored
Normal file
Binary file not shown.
@ -521,6 +521,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self, strongSelf.isNodeLoaded else {
|
||||
return
|
||||
}
|
||||
if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil {
|
||||
return
|
||||
}
|
||||
if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) {
|
||||
(strongSelf.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
||||
var updatedMessages = messages
|
||||
@ -1332,7 +1335,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, openSearch: {
|
||||
}, setupReply: { [weak self] messageId in
|
||||
self?.interfaceInteraction?.setupReplyMessage(messageId)
|
||||
self?.interfaceInteraction?.setupReplyMessage(messageId, { _ in })
|
||||
}, canSetupReply: { [weak self] message in
|
||||
if !message.flags.contains(.Incoming) {
|
||||
if !message.flags.intersection([.Failed, .Sending, .Unsent]).isEmpty {
|
||||
@ -2631,7 +2634,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var mappedTransition: (ChatHistoryListViewTransition, ListViewUpdateSizeAndInsets?)?
|
||||
|
||||
let isScheduledMessages = strongSelf.presentationInterfaceState.isScheduledMessages
|
||||
strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _ in
|
||||
strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _, _ in
|
||||
var options = transition.options
|
||||
let _ = options.insert(.Synchronous)
|
||||
let _ = options.insert(.LowLatency)
|
||||
@ -2860,15 +2863,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId in
|
||||
let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId, completion in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, canSendMessagesToChat(strongSelf.presentationInterfaceState) {
|
||||
if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageId(message.id) }).updatedSearch(nil) })
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageId(message.id) }).updatedSearch(nil) }, completion: completion)
|
||||
strongSelf.updateItemNodesSearchTextHighlightStates()
|
||||
strongSelf.chatDisplayNode.ensureInputViewFocused()
|
||||
} else {
|
||||
completion(.immediate)
|
||||
}
|
||||
} else {
|
||||
completion(.immediate)
|
||||
}
|
||||
}, setupEditMessage: { [weak self] messageId in
|
||||
}, setupEditMessage: { [weak self] messageId, completion in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded {
|
||||
guard let messageId = messageId else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
@ -2878,7 +2885,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
state = state.updatedEditMessageState(nil)
|
||||
return state
|
||||
})
|
||||
}, completion: completion)
|
||||
strongSelf.editMessageDisposable.set(nil)
|
||||
|
||||
return
|
||||
@ -2902,12 +2909,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
|
||||
return updated
|
||||
})
|
||||
}, completion: completion)
|
||||
}
|
||||
}
|
||||
}, beginMessageSelection: { [weak self] messageIds in
|
||||
}, beginMessageSelection: { [weak self] messageIds, completion in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true,{ $0.updatedInterfaceState { $0.withUpdatedSelectedMessages(messageIds) } })
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withUpdatedSelectedMessages(messageIds) } }, completion: completion)
|
||||
|
||||
if let selectionState = strongSelf.presentationInterfaceState.interfaceState.selectionState {
|
||||
let count = selectionState.selectedIds.count
|
||||
@ -2919,6 +2926,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: text)
|
||||
}
|
||||
} else {
|
||||
completion(.immediate)
|
||||
}
|
||||
}, deleteSelectedMessages: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
@ -4381,8 +4390,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
print()
|
||||
}
|
||||
|
||||
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in
|
||||
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop)
|
||||
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||
})
|
||||
}
|
||||
|
||||
@ -4402,11 +4411,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) {
|
||||
self.updateChatPresentationInterfaceState(transition: animated ? .animated(duration: 0.4, curve: .spring) : .immediate, interactive: interactive, saveInterfaceState: saveInterfaceState, f)
|
||||
func updateChatPresentationInterfaceState(animated: Bool = true, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
self.updateChatPresentationInterfaceState(transition: animated ? .animated(duration: 0.4, curve: .spring) : .immediate, interactive: interactive, saveInterfaceState: saveInterfaceState, f, completion: completion)
|
||||
}
|
||||
|
||||
func updateChatPresentationInterfaceState(transition: ContainedViewLayoutTransition, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) {
|
||||
func updateChatPresentationInterfaceState(transition: ContainedViewLayoutTransition, interactive: Bool, saveInterfaceState: Bool = false, _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState, completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void = { _ in }) {
|
||||
var completion = externalCompletion
|
||||
var temporaryChatPresentationInterfaceState = f(self.presentationInterfaceState)
|
||||
|
||||
if self.presentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup {
|
||||
@ -4454,64 +4464,64 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
for (kind, update) in contextQueryUpdates {
|
||||
switch update {
|
||||
case .remove:
|
||||
if let (_, disposable) = self.contextQueryStates[kind] {
|
||||
disposable.dispose()
|
||||
self.contextQueryStates.removeValue(forKey: kind)
|
||||
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { _ in
|
||||
return nil
|
||||
})
|
||||
}
|
||||
case let .update(query, signal):
|
||||
let currentQueryAndDisposable = self.contextQueryStates[kind]
|
||||
currentQueryAndDisposable?.1.dispose()
|
||||
case .remove:
|
||||
if let (_, disposable) = self.contextQueryStates[kind] {
|
||||
disposable.dispose()
|
||||
self.contextQueryStates.removeValue(forKey: kind)
|
||||
|
||||
var inScope = true
|
||||
var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
|
||||
self.contextQueryStates[kind] = (query, (signal |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return result(previousResult)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}, error: { [weak self] error in
|
||||
if let strongSelf = self {
|
||||
switch error {
|
||||
case let .inlineBotLocationRequest(peerId):
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: Int32(Date().timeIntervalSince1970 + 10 * 60)).start()
|
||||
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: 0).start()
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return inScopeResult(previousResult)
|
||||
})
|
||||
}
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { _ in
|
||||
return nil
|
||||
})
|
||||
}
|
||||
case let .update(query, signal):
|
||||
let currentQueryAndDisposable = self.contextQueryStates[kind]
|
||||
currentQueryAndDisposable?.1.dispose()
|
||||
|
||||
if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
if case .contextRequest = query {
|
||||
let _ = (ApplicationSpecificNotice.getSecretChatInlineBotUsage(accountManager: self.context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
if let strongSelf = self, !value {
|
||||
let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
var inScope = true
|
||||
var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
|
||||
self.contextQueryStates[kind] = (query, (signal |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
if Thread.isMainThread && inScope {
|
||||
inScope = false
|
||||
inScopeResult = result
|
||||
} else {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return result(previousResult)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}, error: { [weak self] error in
|
||||
if let strongSelf = self {
|
||||
switch error {
|
||||
case let .inlineBotLocationRequest(peerId):
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: Int32(Date().timeIntervalSince1970 + 10 * 60)).start()
|
||||
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: 0).start()
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}))
|
||||
inScope = false
|
||||
if let inScopeResult = inScopeResult {
|
||||
updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { previousResult in
|
||||
return inScopeResult(previousResult)
|
||||
})
|
||||
}
|
||||
|
||||
if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
if case .contextRequest = query {
|
||||
let _ = (ApplicationSpecificNotice.getSecretChatInlineBotUsage(accountManager: self.context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
if let strongSelf = self, !value {
|
||||
let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4665,7 +4675,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.updateSlowmodeStatus()
|
||||
|
||||
if self.isNodeLoaded {
|
||||
self.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive)
|
||||
self.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive, completion: completion)
|
||||
} else {
|
||||
completion(.immediate)
|
||||
}
|
||||
|
||||
// if let selectionState = self.presentationInterfaceState.interfaceState.selectionState, !selectionState.selectedIds.isEmpty {
|
||||
@ -4705,8 +4717,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let controllerInteraction = self.controllerInteraction {
|
||||
if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState {
|
||||
controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState
|
||||
let isBlackout = controllerInteraction.selectionState != nil
|
||||
completion = { [weak self] transition in
|
||||
completion(transition)
|
||||
(self?.navigationController as? NavigationController)?.updateMasterDetailsBlackout(isBlackout ? .master : nil, transition: transition)
|
||||
}
|
||||
self.updateItemNodesSelectionStates(animated: transition.isAnimated)
|
||||
(self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(controllerInteraction.selectionState != nil ? .master : nil, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7481,7 +7497,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if canEdit, let message = self.chatDisplayNode.historyNode.firstMessageForEditInCurrentHistoryView() {
|
||||
inputShortcuts.append(KeyShortcut(input: UIKeyCommand.inputUpArrow, action: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.interfaceInteraction?.setupEditMessage(message.id)
|
||||
strongSelf.interfaceInteraction?.setupEditMessage(message.id, { _ in })
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -183,14 +183,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var openStickersDisposable: Disposable?
|
||||
private var displayVideoUnmuteTipDisposable: Disposable?
|
||||
|
||||
/*override var accessibilityElements: [Any]? {
|
||||
get {
|
||||
var accessibilityElements: [Any] = []
|
||||
addAccessibilityChildren(of: self.historyNode, container: self.historyNode, to: &accessibilityElements)
|
||||
return accessibilityElements
|
||||
} set(value) {
|
||||
}
|
||||
}*/
|
||||
private var onLayoutCompletions: [(ContainedViewLayoutTransition) -> Void] = []
|
||||
|
||||
init(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) {
|
||||
self.context = context
|
||||
@ -420,7 +413,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction:
|
||||
(ListViewUpdateSizeAndInsets, CGFloat, Bool) -> Void) {
|
||||
(ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void) {
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if let _ = self.scheduledAnimateInAsOverlayFromNode {
|
||||
transition = .immediate
|
||||
@ -746,7 +739,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
} else if let _ = accessoryPanelNode as? ForwardAccessoryPanelNode {
|
||||
strongSelf.requestUpdateChatInterfaceState(true, false, { $0.withUpdatedForwardMessageIds(nil) })
|
||||
} else if let _ = accessoryPanelNode as? EditAccessoryPanelNode {
|
||||
strongSelf.interfaceInteraction?.setupEditMessage(nil)
|
||||
strongSelf.interfaceInteraction?.setupEditMessage(nil, { _ in })
|
||||
} else if let _ = accessoryPanelNode as? WebpagePreviewAccessoryPanelNode {
|
||||
strongSelf.dismissUrlPreview()
|
||||
}
|
||||
@ -1019,7 +1012,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
listInsets.top = listInsets.top + messageActionSheetControllerAdditionalInset
|
||||
}
|
||||
|
||||
listViewTransaction(ListViewUpdateSizeAndInsets(size: contentBounds.size, insets: listInsets, scrollIndicatorInsets: listScrollIndicatorInsets, duration: duration, curve: listViewCurve, ensureTopInsetForOverlayHighlightedItems: ensureTopInsetForOverlayHighlightedItems), additionalScrollDistance, scrollToTop)
|
||||
listViewTransaction(ListViewUpdateSizeAndInsets(size: contentBounds.size, insets: listInsets, scrollIndicatorInsets: listScrollIndicatorInsets, duration: duration, curve: listViewCurve, ensureTopInsetForOverlayHighlightedItems: ensureTopInsetForOverlayHighlightedItems), additionalScrollDistance, scrollToTop, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.notifyTransitionCompletionListeners(transition: transition)
|
||||
}
|
||||
})
|
||||
|
||||
let navigateButtonsSize = self.navigateButtons.updateLayout(transition: transition)
|
||||
var navigateButtonsFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - navigateButtonsSize.width - 6.0, y: layout.size.height - containerInsets.bottom - inputPanelsHeight - navigateButtonsSize.height - 6.0 - bottomOverflowOffset), size: navigateButtonsSize)
|
||||
@ -1285,6 +1282,16 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.derivedLayoutState = ChatControllerNodeDerivedLayoutState(inputContextPanelsFrame: inputContextPanelsFrame, inputContextPanelsOverMainPanelFrame: inputContextPanelsOverMainPanelFrame, inputNodeHeight: inputNodeHeightAndOverflow?.0, upperInputPositionBound: inputNodeHeightAndOverflow?.0 != nil ? self.upperInputPositionBound : nil)
|
||||
}
|
||||
|
||||
private func notifyTransitionCompletionListeners(transition: ContainedViewLayoutTransition) {
|
||||
if !self.onLayoutCompletions.isEmpty {
|
||||
let onLayoutCompletions = self.onLayoutCompletions
|
||||
self.onLayoutCompletions = []
|
||||
for completion in onLayoutCompletions {
|
||||
completion(transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func chatPresentationInterfaceStateRequiresInputFocus(_ state: ChatPresentationInterfaceState) -> Bool {
|
||||
switch state.inputMode {
|
||||
case .text:
|
||||
@ -1298,7 +1305,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func updateChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, transition: ContainedViewLayoutTransition, interactive: Bool) {
|
||||
func updateChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, transition: ContainedViewLayoutTransition, interactive: Bool, completion: @escaping (ContainedViewLayoutTransition) -> Void) {
|
||||
self.selectedMessages = chatPresentationInterfaceState.interfaceState.selectionState?.selectedIds
|
||||
|
||||
if let textInputPanelNode = self.textInputPanelNode {
|
||||
@ -1306,6 +1313,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
if self.chatPresentationInterfaceState != chatPresentationInterfaceState {
|
||||
self.onLayoutCompletions.append(completion)
|
||||
|
||||
let themeUpdated = self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme
|
||||
|
||||
if self.chatPresentationInterfaceState.chatWallpaper != chatPresentationInterfaceState.chatWallpaper {
|
||||
@ -1445,6 +1454,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(.immediate)
|
||||
}
|
||||
|
||||
if self.reactionContainerNode.supernode == nil {
|
||||
@ -1572,7 +1583,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
func frameForVisibleArea() -> CGRect {
|
||||
return CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom))
|
||||
let rect = CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom))
|
||||
if let containerNode = self.containerNode {
|
||||
return containerNode.view.convert(rect, to: self.view)
|
||||
} else {
|
||||
return rect
|
||||
}
|
||||
}
|
||||
|
||||
func frameForInputPanelAccessoryButton(_ item: ChatTextInputAccessoryItem) -> CGRect? {
|
||||
@ -1841,8 +1857,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in
|
||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop)
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||
})
|
||||
if animateIn, let controller = self.messageActionSheetController?.0 {
|
||||
controller.controllerNode.animateIn(transition: transition)
|
||||
@ -1926,8 +1942,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
private func updateLayoutInternal(transition: ContainedViewLayoutTransition) {
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in
|
||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop)
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2108,7 +2124,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
func animateOut(completion: (() -> Void)? = nil) {
|
||||
self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
@ -1364,15 +1364,17 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
|
||||
public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets) {
|
||||
self.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: 0.0, scrollToTop: false)
|
||||
self.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: 0.0, scrollToTop: false, completion: {})
|
||||
}
|
||||
|
||||
public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets, additionalScrollDistance: CGFloat, scrollToTop: Bool) {
|
||||
public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets, additionalScrollDistance: CGFloat, scrollToTop: Bool, completion: @escaping () -> Void) {
|
||||
var scrollToItem: ListViewScrollToItem?
|
||||
if scrollToTop, case .known = self.visibleContentOffset() {
|
||||
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: updateSizeAndInsets.duration), directionHint: .Up)
|
||||
}
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, additionalScrollDistance: scrollToTop ? 0.0 : additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, additionalScrollDistance: scrollToTop ? 0.0 : additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
|
||||
if !self.dequeuedInitialTransitionOnLayout {
|
||||
self.dequeuedInitialTransitionOnLayout = true
|
||||
|
@ -365,7 +365,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
|
||||
if let starStatus = data.starStatus {
|
||||
actions.append(.action(ContextMenuActionItem(text: starStatus ? chatPresentationInterfaceState.strings.Stickers_RemoveFromFavorites : chatPresentationInterfaceState.strings.Stickers_AddToFavorites, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor)
|
||||
return generateTintedImage(image: starStatus ? UIImage(bundleImageName: "Chat/Context Menu/Unstar") : UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
interfaceInteraction.toggleMessageStickerStarred(messages[0].id)
|
||||
f(.default)
|
||||
@ -376,8 +376,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReply, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
interfaceInteraction.setupReplyMessage(messages[0].id)
|
||||
f(.dismissWithoutContent)
|
||||
interfaceInteraction.setupReplyMessage(messages[0].id, { transition in
|
||||
f(.custom(transition))
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
@ -403,8 +404,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_MessageDialogEdit, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
interfaceInteraction.setupEditMessage(messages[0].id)
|
||||
f(.dismissWithoutContent)
|
||||
interfaceInteraction.setupEditMessage(messages[0].id, { transition in
|
||||
f(.custom(transition))
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
@ -551,13 +553,13 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
if channel.addressName == nil {
|
||||
controllerInteraction.presentController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.Conversation_PrivateMessageLinkCopied, true)), nil)
|
||||
controllerInteraction.presentGlobalOverlayController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.Conversation_PrivateMessageLinkCopied, true)), nil)
|
||||
} else {
|
||||
controllerInteraction.presentController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.GroupInfo_InviteLink_CopyAlert_Success, false)), nil)
|
||||
controllerInteraction.presentGlobalOverlayController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.GroupInfo_InviteLink_CopyAlert_Success, false)), nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
})))
|
||||
}
|
||||
|
||||
@ -667,8 +669,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuMore, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
interfaceInteraction.beginMessageSelection(selectAll ? messages.map { $0.id } : [message.id])
|
||||
f(.default)
|
||||
interfaceInteraction.beginMessageSelection(selectAll ? messages.map { $0.id } : [message.id], { transition in
|
||||
f(.custom(transition))
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
|
@ -977,12 +977,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0);
|
||||
if animated {
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4)
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
|
||||
|
||||
if !incoming {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -992,13 +992,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DIdentity
|
||||
if animated {
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in
|
||||
selectionNode?.removeFromSupernode()
|
||||
})
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
||||
}
|
||||
} else {
|
||||
selectionNode.removeFromSupernode()
|
||||
|
@ -2092,6 +2092,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
break
|
||||
}
|
||||
self.contextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0)
|
||||
if !self.contextSourceNode.isExtractedToContextPreview {
|
||||
if let (rect, size) = self.absoluteRect {
|
||||
self.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.messageAccessibilityArea.frame = backgroundFrame
|
||||
|
||||
@ -2615,12 +2620,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0);
|
||||
if animated {
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4)
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
|
||||
|
||||
if !incoming {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2630,13 +2635,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DIdentity
|
||||
if animated {
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in
|
||||
selectionNode?.removeFromSupernode()
|
||||
})
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
||||
}
|
||||
} else {
|
||||
selectionNode.removeFromSupernode()
|
||||
|
@ -773,12 +773,12 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0);
|
||||
if animated {
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4)
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
|
||||
|
||||
if !incoming {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -788,13 +788,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DIdentity
|
||||
if animated {
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in
|
||||
selectionNode?.removeFromSupernode()
|
||||
})
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
||||
}
|
||||
} else {
|
||||
selectionNode.removeFromSupernode()
|
||||
|
@ -790,12 +790,12 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0);
|
||||
if animated {
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4)
|
||||
selectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
|
||||
|
||||
if !incoming {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
selectionNode.layer.animatePosition(from: CGPoint(x: position.x - 42.0, y: position.y), to: position, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -816,13 +816,13 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
let previousSubnodeTransform = self.subnodeTransform
|
||||
self.subnodeTransform = CATransform3DIdentity
|
||||
if animated {
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, completion: { [weak selectionNode]_ in
|
||||
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak selectionNode]_ in
|
||||
selectionNode?.removeFromSupernode()
|
||||
})
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
selectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
if CGFloat(0.0).isLessThanOrEqualTo(selectionNode.frame.origin.x) {
|
||||
let position = selectionNode.layer.position
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
selectionNode.layer.animatePosition(from: position, to: CGPoint(x: position.x - 42.0, y: position.y), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false)
|
||||
}
|
||||
} else {
|
||||
selectionNode.removeFromSupernode()
|
||||
|
@ -522,7 +522,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
if !value {
|
||||
if let textSelectionNode = self.textSelectionNode {
|
||||
self.textSelectionNode = nil
|
||||
textSelectionNode.highlightAreaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in
|
||||
textSelectionNode?.highlightAreaNode.removeFromSupernode()
|
||||
textSelectionNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
@ -532,7 +534,17 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
override func updateIsExtractedToContextPreview(_ value: Bool) {
|
||||
if value {
|
||||
if self.textSelectionNode == nil, let item = self.item, let rootNode = item.controllerInteraction.chatControllerNode() {
|
||||
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: item.presentationData.theme.theme.list.itemAccentColor.withAlphaComponent(0.5), knob: item.presentationData.theme.theme.list.itemAccentColor), textNode: self.textNode, updateIsActive: { [weak self] value in
|
||||
let selectionColor: UIColor
|
||||
let knobColor: UIColor
|
||||
if item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
selectionColor = item.presentationData.theme.theme.chat.message.incoming.textSelectionColor
|
||||
knobColor = item.presentationData.theme.theme.chat.message.incoming.textSelectionKnobColor
|
||||
} else {
|
||||
selectionColor = item.presentationData.theme.theme.chat.message.outgoing.textSelectionColor
|
||||
knobColor = item.presentationData.theme.theme.chat.message.outgoing.textSelectionKnobColor
|
||||
}
|
||||
|
||||
let textSelectionNode = TextSelectionNode(theme: TextSelectionTheme(selection: selectionColor, knob: knobColor), textNode: self.textNode, updateIsActive: { [weak self] value in
|
||||
self?.updateIsTextSelectionActive?(value)
|
||||
}, present: { [weak self] c, a in
|
||||
self?.item?.controllerInteraction.presentGlobalOverlayController(c, a)
|
||||
@ -544,12 +556,16 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
})
|
||||
self.textSelectionNode = textSelectionNode
|
||||
self.addSubnode(textSelectionNode)
|
||||
self.insertSubnode(textSelectionNode.highlightAreaNode, belowSubnode: self.textNode)
|
||||
textSelectionNode.frame = self.textNode.frame
|
||||
textSelectionNode.highlightAreaNode.frame = self.textNode.frame
|
||||
}
|
||||
} else if let textSelectionNode = self.textSelectionNode {
|
||||
self.textSelectionNode = nil
|
||||
self.updateIsTextSelectionActive?(false)
|
||||
textSelectionNode.highlightAreaNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
textSelectionNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak textSelectionNode] _ in
|
||||
textSelectionNode?.highlightAreaNode.removeFromSupernode()
|
||||
textSelectionNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ enum ChatPanelRestrictionInfoDisplayType {
|
||||
}
|
||||
|
||||
final class ChatPanelInterfaceInteraction {
|
||||
let setupReplyMessage: (MessageId) -> Void
|
||||
let setupEditMessage: (MessageId?) -> Void
|
||||
let beginMessageSelection: ([MessageId]) -> Void
|
||||
let setupReplyMessage: (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
let setupEditMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
let beginMessageSelection: ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void
|
||||
let deleteSelectedMessages: () -> Void
|
||||
let reportSelectedMessages: () -> Void
|
||||
let reportMessages: ([Message], ContextController?) -> Void
|
||||
@ -112,7 +112,7 @@ final class ChatPanelInterfaceInteraction {
|
||||
let openScheduledMessages: () -> Void
|
||||
let statuses: ChatPanelInterfaceInteractionStatuses?
|
||||
|
||||
init(setupReplyMessage: @escaping (MessageId) -> Void, setupEditMessage: @escaping (MessageId?) -> Void, beginMessageSelection: @escaping ([MessageId]) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
||||
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
||||
self.setupReplyMessage = setupReplyMessage
|
||||
self.setupEditMessage = setupEditMessage
|
||||
self.beginMessageSelection = beginMessageSelection
|
||||
|
@ -42,9 +42,9 @@ final class ChatRecentActionsController: TelegramBaseController {
|
||||
}
|
||||
})
|
||||
|
||||
self.panelInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _ in
|
||||
}, setupEditMessage: { _ in
|
||||
}, beginMessageSelection: { _ in
|
||||
self.panelInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in
|
||||
}, setupEditMessage: { _, _ in
|
||||
}, beginMessageSelection: { _, _ in
|
||||
}, deleteSelectedMessages: {
|
||||
}, reportSelectedMessages: {
|
||||
}, reportMessages: { _, _ in
|
||||
|
@ -41,9 +41,9 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
||||
controller.activateInput()
|
||||
}
|
||||
if let botStart = params.botStart {
|
||||
controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in
|
||||
controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in
|
||||
return state.updatedBotStartPayload(botStart.payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
found = true
|
||||
break
|
||||
@ -56,9 +56,9 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
||||
if let chatController = params.chatController as? ChatControllerImpl {
|
||||
controller = chatController
|
||||
if let botStart = params.botStart {
|
||||
controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in
|
||||
controller.updateChatPresentationInterfaceState(interactive: false, { state -> ChatPresentationInterfaceState in
|
||||
return state.updatedBotStartPayload(botStart.payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, subject: params.subject, botStart: params.botStart)
|
||||
|
@ -296,9 +296,9 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
|
||||
self.controllerInteraction = controllerInteraction
|
||||
|
||||
self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _ in
|
||||
}, setupEditMessage: { _ in
|
||||
}, beginMessageSelection: { _ in
|
||||
self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in
|
||||
}, setupEditMessage: { _, _ in
|
||||
}, beginMessageSelection: { _, _ in
|
||||
}, deleteSelectedMessages: { [weak self] in
|
||||
if let strongSelf = self, let messageIds = strongSelf.interfaceState.selectionState?.selectedIds {
|
||||
strongSelf.deleteMessages(messageIds)
|
||||
|
@ -197,6 +197,8 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
private var currentRange: (Int, Int)?
|
||||
private var currentRects: [CGRect]?
|
||||
|
||||
public let highlightAreaNode: ASDisplayNode
|
||||
|
||||
public init(theme: TextSelectionTheme, textNode: TextNode, updateIsActive: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void, rootNode: ASDisplayNode, performAction: @escaping (String, TextSelectionAction) -> Void) {
|
||||
self.theme = theme
|
||||
self.textNode = textNode
|
||||
@ -217,6 +219,8 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
self.rightKnob.displayWithoutProcessing = true
|
||||
self.rightKnob.alpha = 0.0
|
||||
|
||||
self.highlightAreaNode = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.setViewBlock({
|
||||
@ -262,7 +266,7 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
let updatedRange = NSRange(location: min(updatedMin, updatedMax), length: max(updatedMin, updatedMax) - min(updatedMin, updatedMax))
|
||||
if strongSelf.currentRange?.0 != updatedMin || strongSelf.currentRange?.1 != updatedMax {
|
||||
strongSelf.currentRange = (updatedMin, updatedMax)
|
||||
strongSelf.updateSelection(range: updatedRange)
|
||||
strongSelf.updateSelection(range: updatedRange, animateIn: false)
|
||||
}
|
||||
|
||||
if let scrollView = findScrollView(view: strongSelf.view) {
|
||||
@ -311,7 +315,7 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
strongSelf.currentRange = resultRange.flatMap {
|
||||
($0.lowerBound, $0.upperBound)
|
||||
}
|
||||
strongSelf.updateSelection(range: resultRange)
|
||||
strongSelf.updateSelection(range: resultRange, animateIn: true)
|
||||
strongSelf.displayMenu()
|
||||
strongSelf.updateIsActive(true)
|
||||
}
|
||||
@ -322,7 +326,7 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
self.view.addGestureRecognizer(recognizer)
|
||||
}
|
||||
|
||||
private func updateSelection(range: NSRange?) {
|
||||
private func updateSelection(range: NSRange?, animateIn: Bool) {
|
||||
var rects: [CGRect]?
|
||||
|
||||
if let range = range {
|
||||
@ -342,7 +346,7 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
highlightOverlay.outerRadius = 0.0
|
||||
highlightOverlay.inset = 1.0
|
||||
self.highlightOverlay = highlightOverlay
|
||||
self.insertSubnode(highlightOverlay, at: 0)
|
||||
self.highlightAreaNode.addSubnode(highlightOverlay)
|
||||
}
|
||||
highlightOverlay.frame = self.bounds
|
||||
highlightOverlay.updateRects(rects)
|
||||
@ -351,11 +355,27 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(rects[rects.count - 1].maxX + 1.0 - image.size.width / 2.0), y: rects[rects.count - 1].maxY + 1.0 - (rects[0].height + 2.0)), size: CGSize(width: image.size.width, height: image.size.width + rects[0].height + 2.0))
|
||||
}
|
||||
if self.leftKnob.alpha.isZero {
|
||||
highlightOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
highlightOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
self.leftKnob.alpha = 1.0
|
||||
self.leftKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
self.leftKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.14, delay: 0.19)
|
||||
self.rightKnob.alpha = 1.0
|
||||
self.rightKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
self.rightKnob.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.14, delay: 0.19)
|
||||
self.leftKnob.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.2, delay: 0.25, initialVelocity: 0.0, damping: 80.0)
|
||||
self.rightKnob.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.2, delay: 0.25, initialVelocity: 0.0, damping: 80.0)
|
||||
|
||||
if animateIn {
|
||||
var result = CGRect()
|
||||
for rect in rects {
|
||||
if result.isEmpty {
|
||||
result = rect
|
||||
} else {
|
||||
result = result.union(rect)
|
||||
}
|
||||
}
|
||||
highlightOverlay.layer.animateScale(from: 2.0, to: 1.0, duration: 0.26)
|
||||
let fromResult = CGRect(origin: CGPoint(x: result.minX - result.width / 2.0, y: result.minY - result.height / 2.0), size: CGSize(width: result.width * 2.0, height: result.height * 2.0))
|
||||
highlightOverlay.layer.animatePosition(from: CGPoint(x: (-fromResult.midX + highlightOverlay.bounds.midX) / 1.0, y: (-fromResult.midY + highlightOverlay.bounds.midY) / 1.0), to: CGPoint(), duration: 0.26, additive: true)
|
||||
}
|
||||
}
|
||||
} else if let highlightOverlay = self.highlightOverlay {
|
||||
self.highlightOverlay = nil
|
||||
@ -372,7 +392,7 @@ public final class TextSelectionNode: ASDisplayNode {
|
||||
|
||||
private func dismissSelection() {
|
||||
self.currentRange = nil
|
||||
self.updateSelection(range: nil)
|
||||
self.updateSelection(range: nil, animateIn: false)
|
||||
}
|
||||
|
||||
private func displayMenu() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user