mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge commit '9d5604d81d7c7cd25b07b8f82f50f353609b8e54'
This commit is contained in:
commit
f5a552ce96
Binary file not shown.
@ -5827,3 +5827,13 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Conversation.PinOlderMessageAlertText" = "Do you want to pin an older message while leaving a more recent one pinned?";
|
"Conversation.PinOlderMessageAlertText" = "Do you want to pin an older message while leaving a more recent one pinned?";
|
||||||
"Conversation.PinMessageAlertPin" = "Pin";
|
"Conversation.PinMessageAlertPin" = "Pin";
|
||||||
|
|
||||||
|
"Conversation.ContextMenuSelect" = "Select";
|
||||||
|
|
||||||
|
"Conversation.ContextMenuSelectAll_0" = "Select All %@ Items";
|
||||||
|
"Conversation.ContextMenuSelectAll_1" = "Select All %@ Items";
|
||||||
|
"Conversation.ContextMenuSelectAll_2" = "Select All %@ Items";
|
||||||
|
"Conversation.ContextMenuSelectAll_3_10" = "Select All %@ Items";
|
||||||
|
"Conversation.ContextMenuSelectAll_many" = "Select All %@ Items";
|
||||||
|
"Conversation.ContextMenuSelectAll_any" = "Select All %@ Items";
|
||||||
|
|
||||||
|
"Conversation.Dice.u1F3B0" = "Send a slot machine emoji to try your luck.";
|
||||||
|
@ -465,7 +465,7 @@ public protocol ChatController: ViewController {
|
|||||||
func displayPromoAnnouncement(text: String)
|
func displayPromoAnnouncement(text: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol ChatMessagePrevewItemNode: class {
|
public protocol ChatMessagePreviewItemNode: class {
|
||||||
var forwardInfoReferenceNode: ASDisplayNode? { get }
|
var forwardInfoReferenceNode: ASDisplayNode? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,7 +666,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
})))
|
})))
|
||||||
|
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuMore, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||||
c.dismiss(completion: {
|
c.dismiss(completion: {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.dismissInput()
|
strongSelf.dismissInput()
|
||||||
@ -720,8 +720,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
})))
|
})))
|
||||||
|
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuMore, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuSelect, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.dismissInput()
|
strongSelf.dismissInput()
|
||||||
|
@ -61,13 +61,13 @@ public final class ContextControllerSourceNode: ASDisplayNode {
|
|||||||
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
|
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
|
||||||
|
|
||||||
let originalCenterOffsetX: CGFloat = targetNode.bounds.width / 2.0 - targetContentRect.midX
|
let originalCenterOffsetX: CGFloat = targetNode.bounds.width / 2.0 - targetContentRect.midX
|
||||||
let scaledCaneterOffsetX: CGFloat = originalCenterOffsetX * currentScale
|
let scaledCenterOffsetX: CGFloat = originalCenterOffsetX * currentScale
|
||||||
|
|
||||||
let originalCenterOffsetY: CGFloat = targetNode.bounds.height / 2.0 - targetContentRect.midY
|
let originalCenterOffsetY: CGFloat = targetNode.bounds.height / 2.0 - targetContentRect.midY
|
||||||
let scaledCaneterOffsetY: CGFloat = originalCenterOffsetY * currentScale
|
let scaledCenterOffsetY: CGFloat = originalCenterOffsetY * currentScale
|
||||||
|
|
||||||
let scaleMidX: CGFloat = scaledCaneterOffsetX - originalCenterOffsetX
|
let scaleMidX: CGFloat = scaledCenterOffsetX - originalCenterOffsetX
|
||||||
let scaleMidY: CGFloat = scaledCaneterOffsetY - originalCenterOffsetY
|
let scaleMidY: CGFloat = scaledCenterOffsetY - originalCenterOffsetY
|
||||||
|
|
||||||
switch update {
|
switch update {
|
||||||
case .update:
|
case .update:
|
||||||
|
@ -425,7 +425,7 @@ typedef enum
|
|||||||
if (gestureRecognizer == _panGestureRecognizer)
|
if (gestureRecognizer == _panGestureRecognizer)
|
||||||
{
|
{
|
||||||
NSString *viewClassName = NSStringFromClass(otherGestureRecognizer.view.class);
|
NSString *viewClassName = NSStringFromClass(otherGestureRecognizer.view.class);
|
||||||
if ([viewClassName rangeOfString:@"WKScroll"].location != NSNotFound || [viewClassName rangeOfString:@"UIWebViewScroll"].location != NSNotFound)
|
if ([viewClassName rangeOfString:@"WKScroll"].location != NSNotFound)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
@property (nonatomic, assign) bool isOwn;
|
@property (nonatomic, assign) bool isOwn;
|
||||||
@property (nonatomic, assign) bool hasSession;
|
@property (nonatomic, assign) bool hasSession;
|
||||||
@property (nonatomic, assign) bool isExpired;
|
@property (nonatomic, assign) bool isExpired;
|
||||||
|
@property (nonatomic, strong) NSNumber *heading;
|
||||||
|
|
||||||
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location;
|
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location;
|
||||||
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location color:(UIColor *)color;
|
- (instancetype)initWithLocation:(TGLocationMediaAttachment *)location color:(UIColor *)color;
|
||||||
|
@ -82,6 +82,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setHeading:(NSNumber *)heading
|
||||||
|
{
|
||||||
|
[self willChangeValueForKey:@"heading"];
|
||||||
|
_heading = heading;
|
||||||
|
[self didChangeValueForKey:@"heading"];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setHasSession:(bool)hasSession
|
- (void)setHasSession:(bool)hasSession
|
||||||
{
|
{
|
||||||
if (hasSession != _hasSession)
|
if (hasSession != _hasSession)
|
||||||
|
@ -25,6 +25,8 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
TGImageView *_iconView;
|
TGImageView *_iconView;
|
||||||
UIImageView *_dotView;
|
UIImageView *_dotView;
|
||||||
TGLetteredAvatarView *_avatarView;
|
TGLetteredAvatarView *_avatarView;
|
||||||
|
UIImageView *_smallArrowView;
|
||||||
|
UIImageView *_arrowView;
|
||||||
|
|
||||||
bool _liveLocation;
|
bool _liveLocation;
|
||||||
SMetaDisposable *_userDisposable;
|
SMetaDisposable *_userDisposable;
|
||||||
@ -56,7 +58,10 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
[_backgroundView addSubview:_iconView];
|
[_backgroundView addSubview:_iconView];
|
||||||
|
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
static UIImage *dotImage;
|
static UIImage *smallDotImage;
|
||||||
|
static UIImage *largeDotImage;
|
||||||
|
static UIImage *smallHeadingArrowImage;
|
||||||
|
static UIImage *largeHeadingArrowImage;
|
||||||
dispatch_once(&onceToken, ^
|
dispatch_once(&onceToken, ^
|
||||||
{
|
{
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
|
||||||
@ -64,17 +69,91 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
CGContextSetFillColorWithColor(context, UIColorRGB(0x008df2).CGColor);
|
CGContextSetFillColorWithColor(context, UIColorRGB(0x008df2).CGColor);
|
||||||
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
|
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
|
||||||
|
|
||||||
dotImage = UIGraphicsGetImageFromCurrentImageContext();
|
smallDotImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
|
|
||||||
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
|
||||||
|
context = UIGraphicsGetCurrentContext();
|
||||||
|
|
||||||
|
CGContextSaveGState(context);
|
||||||
|
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1.0f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor);
|
||||||
|
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(2, 2, 12.0, 12.0));
|
||||||
|
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
|
||||||
|
CGContextSetFillColorWithColor(context, UIColorRGB(0x008df2).CGColor);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(4.0f, 4.0f, 8.0f, 8.0f));
|
||||||
|
|
||||||
|
largeDotImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
|
|
||||||
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
|
||||||
|
context = UIGraphicsGetCurrentContext();
|
||||||
|
|
||||||
|
CGContextClearRect(context, CGRectMake(0, 0, 18.0f, 18.0f));
|
||||||
|
|
||||||
|
CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
|
||||||
|
|
||||||
|
CGContextMoveToPoint(context, 9, 0);
|
||||||
|
CGContextAddLineToPoint(context, 13, 7);
|
||||||
|
CGContextAddLineToPoint(context, 5, 7);
|
||||||
|
CGContextClosePath(context);
|
||||||
|
CGContextFillPath(context);
|
||||||
|
|
||||||
|
CGContextSetBlendMode(context, kCGBlendModeClear);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(3.0, 3.0, 12.0, 12.0));
|
||||||
|
|
||||||
|
smallHeadingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
|
|
||||||
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(33.0f, 33.0f), false, 0.0f);
|
||||||
|
context = UIGraphicsGetCurrentContext();
|
||||||
|
|
||||||
|
CGContextClearRect(context, CGRectMake(0, 0, 16.0f, 16.0f));
|
||||||
|
|
||||||
|
CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
|
||||||
|
|
||||||
|
CGContextMoveToPoint(context, 16.5, 0);
|
||||||
|
CGContextAddLineToPoint(context, 21.5, 6);
|
||||||
|
CGContextAddLineToPoint(context, 11.5, 6);
|
||||||
|
CGContextClosePath(context);
|
||||||
|
CGContextFillPath(context);
|
||||||
|
|
||||||
|
CGContextSetBlendMode(context, kCGBlendModeClear);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(3.0, 3.0, 27.0, 27.0));
|
||||||
|
|
||||||
|
largeHeadingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
UIGraphicsEndImageContext();
|
UIGraphicsEndImageContext();
|
||||||
});
|
});
|
||||||
|
|
||||||
_smallView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"LocationSmallCircle")];
|
_smallView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"LocationSmallCircle")];
|
||||||
_smallView.hidden = true;
|
_smallView.hidden = true;
|
||||||
[self addSubview:_smallView];
|
[self addSubview:_smallView];
|
||||||
|
|
||||||
|
UIImage *dotImage = smallDotImage;
|
||||||
|
if ([annotation isKindOfClass:[TGLocationAnnotation class]])
|
||||||
|
{
|
||||||
|
TGLocationAnnotation *locationAnnotation = (TGLocationAnnotation *)annotation;
|
||||||
|
if (locationAnnotation.location.period > 0) {
|
||||||
|
dotImage = largeDotImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_dotView = [[UIImageView alloc] initWithImage:dotImage];
|
_dotView = [[UIImageView alloc] initWithImage:dotImage];
|
||||||
[self addSubview:_dotView];
|
[self addSubview:_dotView];
|
||||||
|
|
||||||
|
_smallArrowView = [[UIImageView alloc] initWithImage:smallHeadingArrowImage];
|
||||||
|
_smallArrowView.hidden = true;
|
||||||
|
[self addSubview:_smallArrowView];
|
||||||
|
|
||||||
|
_arrowView = [[UIImageView alloc] initWithImage:largeHeadingArrowImage];
|
||||||
|
_arrowView.hidden = true;
|
||||||
|
[self addSubview:_arrowView];
|
||||||
|
|
||||||
[self setAnnotation:annotation];
|
[self setAnnotation:annotation];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@ -99,6 +178,7 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
return;
|
return;
|
||||||
_observingExpiration = true;
|
_observingExpiration = true;
|
||||||
[self addObserver:self forKeyPath:@"annotation.isExpired" options:NSKeyValueObservingOptionNew context:NULL];
|
[self addObserver:self forKeyPath:@"annotation.isExpired" options:NSKeyValueObservingOptionNew context:NULL];
|
||||||
|
[self addObserver:self forKeyPath:@"annotation.heading" options:NSKeyValueObservingOptionNew context:NULL];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unsubscribeFromExpiration
|
- (void)unsubscribeFromExpiration
|
||||||
@ -107,6 +187,7 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
return;
|
return;
|
||||||
_observingExpiration = false;
|
_observingExpiration = false;
|
||||||
[self removeObserver:self forKeyPath:@"annotation.isExpired"];
|
[self removeObserver:self forKeyPath:@"annotation.isExpired"];
|
||||||
|
[self removeObserver:self forKeyPath:@"annotation.heading"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
|
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
|
||||||
@ -214,6 +295,8 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
_smallView.alpha = 1.0f;
|
_smallView.alpha = 1.0f;
|
||||||
[self layoutSubviews];
|
[self layoutSubviews];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self updateHeading];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPallete:(TGLocationPallete *)pallete
|
- (void)setPallete:(TGLocationPallete *)pallete
|
||||||
@ -223,13 +306,38 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
|
|
||||||
_pallete = pallete;
|
_pallete = pallete;
|
||||||
|
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
|
UIImage *dotImage;
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
if ([self.annotation isKindOfClass:[TGLocationAnnotation class]])
|
||||||
CGContextSetFillColorWithColor(context, pallete.locationColor.CGColor);
|
{
|
||||||
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
|
TGLocationAnnotation *locationAnnotation = (TGLocationAnnotation *)self.annotation;
|
||||||
|
if (locationAnnotation.location.period > 0) {
|
||||||
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(16.0f, 16.0f), false, 0.0f);
|
||||||
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
|
||||||
|
CGContextSaveGState(context);
|
||||||
|
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 1.0f, [UIColor colorWithWhite:0.0f alpha:0.22f].CGColor);
|
||||||
|
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(2, 2, 12.0, 12.0));
|
||||||
|
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
|
||||||
|
CGContextSetFillColorWithColor(context, pallete.locationColor.CGColor);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(4.0f, 4.0f, 8.0f, 8.0f));
|
||||||
|
|
||||||
|
dotImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UIImage *dotImage = UIGraphicsGetImageFromCurrentImageContext();
|
if (dotImage == nil) {
|
||||||
UIGraphicsEndImageContext();
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(6.0f, 6.0f), false, 0.0f);
|
||||||
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
CGContextSetFillColorWithColor(context, pallete.locationColor.CGColor);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 6.0f, 6.0f));
|
||||||
|
|
||||||
|
dotImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
}
|
||||||
|
|
||||||
_dotView.image = dotImage;
|
_dotView.image = dotImage;
|
||||||
|
|
||||||
@ -324,6 +432,26 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)updateHeading
|
||||||
|
{
|
||||||
|
if ([self.annotation isKindOfClass:[TGLocationAnnotation class]]) {
|
||||||
|
NSNumber *heading = ((TGLocationAnnotation *)self.annotation).heading;
|
||||||
|
|
||||||
|
if (heading != nil) {
|
||||||
|
_arrowView.hidden = self.isSelected;
|
||||||
|
_smallArrowView.hidden = !self.isSelected;
|
||||||
|
_arrowView.transform = CGAffineTransformMakeRotation(heading.floatValue);
|
||||||
|
_smallArrowView.transform = CGAffineTransformMakeRotation(heading.floatValue);
|
||||||
|
} else {
|
||||||
|
_arrowView.hidden = true;
|
||||||
|
_smallArrowView.hidden = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_arrowView.hidden = true;
|
||||||
|
_smallArrowView.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
|
||||||
{
|
{
|
||||||
if ([keyPath isEqualToString:@"annotation.isExpired"])
|
if ([keyPath isEqualToString:@"annotation.isExpired"])
|
||||||
@ -339,6 +467,10 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
_avatarView.alpha = 1.0f;
|
_avatarView.alpha = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ([keyPath isEqual:@"annotation.heading"])
|
||||||
|
{
|
||||||
|
[self updateHeading];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||||
@ -410,6 +542,8 @@ NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation";
|
|||||||
|
|
||||||
_dotView.center = CGPointZero;
|
_dotView.center = CGPointZero;
|
||||||
_smallView.center = CGPointZero;
|
_smallView.center = CGPointZero;
|
||||||
|
_arrowView.center = CGPointZero;
|
||||||
|
_smallArrowView.center = CGPointZero;
|
||||||
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
|
_shadowView.center = CGPointMake(TGScreenPixel, -36.0f);
|
||||||
_backgroundView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f);
|
_backgroundView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f);
|
||||||
_iconView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f - 5.0f);
|
_iconView.center = CGPointMake(_shadowView.frame.size.width / 2.0f, _shadowView.frame.size.height / 2.0f - 5.0f);
|
||||||
|
@ -370,6 +370,26 @@
|
|||||||
}]];
|
}]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double DegreesToRadians(double degrees) {return degrees * M_PI / 180.0;};
|
||||||
|
double RadiansToDegrees(double radians) {return radians * 180.0/M_PI;};
|
||||||
|
|
||||||
|
- (double)bearingFromLocation:(CLLocationCoordinate2D)fromLocation toLocation:(CLLocationCoordinate2D)toLocation
|
||||||
|
{
|
||||||
|
double lat1 = DegreesToRadians(fromLocation.latitude);
|
||||||
|
double lon1 = DegreesToRadians(fromLocation.longitude);
|
||||||
|
|
||||||
|
double lat2 = DegreesToRadians(toLocation.latitude);
|
||||||
|
double lon2 = DegreesToRadians(toLocation.longitude);
|
||||||
|
|
||||||
|
double dLon = lon2 - lon1;
|
||||||
|
|
||||||
|
double y = sin(dLon) * cos(lat2);
|
||||||
|
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
|
||||||
|
double radiansBearing = atan2(y, x);
|
||||||
|
|
||||||
|
return radiansBearing;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)updateAnnotations
|
- (void)updateAnnotations
|
||||||
{
|
{
|
||||||
NSMutableDictionary *liveLocations = [[NSMutableDictionary alloc] init];
|
NSMutableDictionary *liveLocations = [[NSMutableDictionary alloc] init];
|
||||||
@ -388,7 +408,20 @@
|
|||||||
|
|
||||||
if (liveLocations[@(annotation.messageId)] != nil)
|
if (liveLocations[@(annotation.messageId)] != nil)
|
||||||
{
|
{
|
||||||
annotation.coordinate = [(TGLiveLocation *)liveLocations[@(annotation.messageId)] location].coordinate;
|
[UIView animateWithDuration:0.3 animations:^{
|
||||||
|
CLLocationCoordinate2D previousCoordinate = annotation.coordinate;
|
||||||
|
annotation.coordinate = [(TGLiveLocation *)liveLocations[@(annotation.messageId)] location].coordinate;
|
||||||
|
|
||||||
|
|
||||||
|
CLLocation *previous = [[CLLocation alloc] initWithLatitude:previousCoordinate.latitude longitude:previousCoordinate.longitude];
|
||||||
|
CLLocation *new = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
|
||||||
|
// if ([new distanceFromLocation:previous] > 20) {
|
||||||
|
CGFloat hdg = [self bearingFromLocation:previousCoordinate toLocation:annotation.coordinate];
|
||||||
|
if (hdg != 0.0) {
|
||||||
|
annotation.heading = @(hdg);
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}];
|
||||||
annotation.isExpired = [(TGLiveLocation *)liveLocations[@(annotation.messageId)] isExpired];
|
annotation.isExpired = [(TGLiveLocation *)liveLocations[@(annotation.messageId)] isExpired];
|
||||||
[liveLocations removeObjectForKey:@(annotation.messageId)];
|
[liveLocations removeObjectForKey:@(annotation.messageId)];
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import TelegramPresentationData
|
|||||||
import AppBundle
|
import AppBundle
|
||||||
|
|
||||||
private let panelInset: CGFloat = 4.0
|
private let panelInset: CGFloat = 4.0
|
||||||
private let panelSize = CGSize(width: 46.0, height: 90.0)
|
private let panelButtonSize = CGSize(width: 46.0, height: 46.0)
|
||||||
|
|
||||||
private func generateBackgroundImage(theme: PresentationTheme) -> UIImage? {
|
private func generateBackgroundImage(theme: PresentationTheme) -> UIImage? {
|
||||||
let cornerRadius: CGFloat = 9.0
|
let cornerRadius: CGFloat = 9.0
|
||||||
@ -38,24 +38,29 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
private let toggleMapModeSelection: () -> Void
|
private let toggleMapModeSelection: () -> Void
|
||||||
private let goToUserLocation: () -> Void
|
private let goToUserLocation: () -> Void
|
||||||
private let showPlacesInThisArea: () -> Void
|
private let showPlacesInThisArea: () -> Void
|
||||||
|
private let setupProximityNotification: () -> Void
|
||||||
|
|
||||||
private var displayingPlacesButton = false
|
private var displayingPlacesButton = false
|
||||||
|
private var proximityNotification: Bool?
|
||||||
|
|
||||||
let mapNode: LocationMapNode
|
let mapNode: LocationMapNode
|
||||||
private let optionsBackgroundNode: ASImageNode
|
private let optionsBackgroundNode: ASImageNode
|
||||||
private let optionsSeparatorNode: ASDisplayNode
|
private let optionsSeparatorNode: ASDisplayNode
|
||||||
|
private let optionsSecondSeparatorNode: ASDisplayNode
|
||||||
private let infoButtonNode: HighlightableButtonNode
|
private let infoButtonNode: HighlightableButtonNode
|
||||||
private let locationButtonNode: HighlightableButtonNode
|
private let locationButtonNode: HighlightableButtonNode
|
||||||
|
private let notificationButtonNode: HighlightableButtonNode
|
||||||
private let placesBackgroundNode: ASImageNode
|
private let placesBackgroundNode: ASImageNode
|
||||||
private let placesButtonNode: HighlightableButtonNode
|
private let placesButtonNode: HighlightableButtonNode
|
||||||
private let shadowNode: ASImageNode
|
private let shadowNode: ASImageNode
|
||||||
|
|
||||||
private var validLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat, CGSize)?
|
private var validLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat, CGSize)?
|
||||||
|
|
||||||
init(presentationData: PresentationData, toggleMapModeSelection: @escaping () -> Void, goToUserLocation: @escaping () -> Void, showPlacesInThisArea: @escaping () -> Void = {}) {
|
init(presentationData: PresentationData, toggleMapModeSelection: @escaping () -> Void, goToUserLocation: @escaping () -> Void, setupProximityNotification: @escaping () -> Void = {}, showPlacesInThisArea: @escaping () -> Void = {}) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.toggleMapModeSelection = toggleMapModeSelection
|
self.toggleMapModeSelection = toggleMapModeSelection
|
||||||
self.goToUserLocation = goToUserLocation
|
self.goToUserLocation = goToUserLocation
|
||||||
|
self.setupProximityNotification = setupProximityNotification
|
||||||
self.showPlacesInThisArea = showPlacesInThisArea
|
self.showPlacesInThisArea = showPlacesInThisArea
|
||||||
|
|
||||||
self.mapNode = LocationMapNode()
|
self.mapNode = LocationMapNode()
|
||||||
@ -70,6 +75,9 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
self.optionsSeparatorNode = ASDisplayNode()
|
self.optionsSeparatorNode = ASDisplayNode()
|
||||||
self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
|
self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
|
||||||
|
|
||||||
|
self.optionsSecondSeparatorNode = ASDisplayNode()
|
||||||
|
self.optionsSecondSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
|
||||||
|
|
||||||
self.infoButtonNode = HighlightableButtonNode()
|
self.infoButtonNode = HighlightableButtonNode()
|
||||||
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||||
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
||||||
@ -78,6 +86,9 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
self.locationButtonNode = HighlightableButtonNode()
|
self.locationButtonNode = HighlightableButtonNode()
|
||||||
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||||
|
|
||||||
|
self.notificationButtonNode = HighlightableButtonNode()
|
||||||
|
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/NotificationIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||||
|
|
||||||
self.placesBackgroundNode = ASImageNode()
|
self.placesBackgroundNode = ASImageNode()
|
||||||
self.placesBackgroundNode.contentMode = .scaleToFill
|
self.placesBackgroundNode.contentMode = .scaleToFill
|
||||||
self.placesBackgroundNode.displaysAsynchronously = false
|
self.placesBackgroundNode.displaysAsynchronously = false
|
||||||
@ -101,23 +112,27 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.mapNode)
|
self.addSubnode(self.mapNode)
|
||||||
self.addSubnode(self.optionsBackgroundNode)
|
self.addSubnode(self.optionsBackgroundNode)
|
||||||
self.optionsBackgroundNode.addSubnode(self.optionsSeparatorNode)
|
self.optionsBackgroundNode.addSubnode(self.optionsSeparatorNode)
|
||||||
|
self.optionsBackgroundNode.addSubnode(self.optionsSecondSeparatorNode)
|
||||||
self.optionsBackgroundNode.addSubnode(self.infoButtonNode)
|
self.optionsBackgroundNode.addSubnode(self.infoButtonNode)
|
||||||
self.optionsBackgroundNode.addSubnode(self.locationButtonNode)
|
self.optionsBackgroundNode.addSubnode(self.locationButtonNode)
|
||||||
|
self.optionsBackgroundNode.addSubnode(self.notificationButtonNode)
|
||||||
self.addSubnode(self.placesBackgroundNode)
|
self.addSubnode(self.placesBackgroundNode)
|
||||||
self.placesBackgroundNode.addSubnode(self.placesButtonNode)
|
self.placesBackgroundNode.addSubnode(self.placesButtonNode)
|
||||||
self.addSubnode(self.shadowNode)
|
self.addSubnode(self.shadowNode)
|
||||||
|
|
||||||
self.infoButtonNode.addTarget(self, action: #selector(self.infoPressed), forControlEvents: .touchUpInside)
|
self.infoButtonNode.addTarget(self, action: #selector(self.infoPressed), forControlEvents: .touchUpInside)
|
||||||
self.locationButtonNode.addTarget(self, action: #selector(self.locationPressed), forControlEvents: .touchUpInside)
|
self.locationButtonNode.addTarget(self, action: #selector(self.locationPressed), forControlEvents: .touchUpInside)
|
||||||
|
self.notificationButtonNode.addTarget(self, action: #selector(self.notificationPressed), forControlEvents: .touchUpInside)
|
||||||
self.placesButtonNode.addTarget(self, action: #selector(self.placesPressed), forControlEvents: .touchUpInside)
|
self.placesButtonNode.addTarget(self, action: #selector(self.placesPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateState(mapMode: LocationMapMode, displayingMapModeOptions: Bool, displayingPlacesButton: Bool, animated: Bool) {
|
func updateState(mapMode: LocationMapMode, displayingMapModeOptions: Bool, displayingPlacesButton: Bool, proximityNotification: Bool?, animated: Bool) {
|
||||||
self.mapNode.mapMode = mapMode
|
self.mapNode.mapMode = mapMode
|
||||||
self.infoButtonNode.isSelected = displayingMapModeOptions
|
self.infoButtonNode.isSelected = displayingMapModeOptions
|
||||||
|
|
||||||
let updateLayout = self.displayingPlacesButton != displayingPlacesButton
|
let updateLayout = self.displayingPlacesButton != displayingPlacesButton || self.proximityNotification != proximityNotification
|
||||||
self.displayingPlacesButton = displayingPlacesButton
|
self.displayingPlacesButton = displayingPlacesButton
|
||||||
|
self.proximityNotification = proximityNotification
|
||||||
|
|
||||||
if updateLayout, let (layout, navigationBarHeight, topPadding, offset, size) = self.validLayout {
|
if updateLayout, let (layout, navigationBarHeight, topPadding, offset, size) = self.validLayout {
|
||||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .spring) : .immediate
|
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .spring) : .immediate
|
||||||
@ -130,10 +145,14 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
|
self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
|
||||||
self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
|
self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
|
||||||
|
self.optionsSecondSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
|
||||||
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||||
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
||||||
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
|
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
|
||||||
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||||
|
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
|
||||||
|
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
|
||||||
|
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
|
||||||
self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
|
self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
|
||||||
self.shadowNode.image = generateShadowImage(theme: presentationData.theme, highlighted: false)
|
self.shadowNode.image = generateShadowImage(theme: presentationData.theme, highlighted: false)
|
||||||
}
|
}
|
||||||
@ -155,10 +174,20 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
transition.updateFrame(node: self.shadowNode, frame: CGRect(x: 0.0, y: size.height - 14.0, width: size.width, height: 14.0))
|
transition.updateFrame(node: self.shadowNode, frame: CGRect(x: 0.0, y: size.height - 14.0, width: size.width, height: 14.0))
|
||||||
|
|
||||||
transition.updateFrame(node: self.optionsBackgroundNode, frame: CGRect(x: size.width - inset - panelSize.width - panelInset * 2.0, y: navigationBarHeight + topPadding + inset, width: panelSize.width + panelInset * 2.0, height: panelSize.height + panelInset * 2.0))
|
transition.updateFrame(node: self.infoButtonNode, frame: CGRect(x: panelInset, y: panelInset, width: panelButtonSize.width, height: panelButtonSize.height))
|
||||||
transition.updateFrame(node: self.infoButtonNode, frame: CGRect(x: panelInset, y: panelInset, width: panelSize.width, height: panelSize.height / 2.0))
|
transition.updateFrame(node: self.locationButtonNode, frame: CGRect(x: panelInset, y: panelInset + panelButtonSize.height, width: panelButtonSize.width, height: panelButtonSize.height))
|
||||||
transition.updateFrame(node: self.locationButtonNode, frame: CGRect(x: panelInset, y: panelInset + panelSize.height / 2.0, width: panelSize.width, height: panelSize.height / 2.0))
|
transition.updateFrame(node: self.notificationButtonNode, frame: CGRect(x: panelInset, y: panelInset + panelButtonSize.height * 2.0, width: panelButtonSize.width, height: panelButtonSize.height))
|
||||||
transition.updateFrame(node: self.optionsSeparatorNode, frame: CGRect(x: panelInset, y: panelInset + panelSize.height / 2.0, width: panelSize.width, height: UIScreenPixel))
|
transition.updateFrame(node: self.optionsSeparatorNode, frame: CGRect(x: panelInset, y: panelInset + panelButtonSize.height, width: panelButtonSize.width, height: UIScreenPixel))
|
||||||
|
transition.updateFrame(node: self.optionsSecondSeparatorNode, frame: CGRect(x: panelInset, y: panelInset + panelButtonSize.height * 2.0, width: panelButtonSize.width, height: UIScreenPixel))
|
||||||
|
|
||||||
|
var panelHeight: CGFloat = panelButtonSize.height * 2.0
|
||||||
|
if self.proximityNotification != nil {
|
||||||
|
panelHeight += panelButtonSize.height
|
||||||
|
}
|
||||||
|
transition.updateAlpha(node: self.notificationButtonNode, alpha: self.proximityNotification != nil ? 1.0 : 0.0)
|
||||||
|
transition.updateAlpha(node: self.optionsSecondSeparatorNode, alpha: self.proximityNotification != nil ? 1.0 : 0.0)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.optionsBackgroundNode, frame: CGRect(x: size.width - inset - panelButtonSize.width - panelInset * 2.0, y: navigationBarHeight + topPadding + inset, width: panelButtonSize.width + panelInset * 2.0, height: panelHeight + panelInset * 2.0))
|
||||||
|
|
||||||
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||||
let optionsAlpha: CGFloat = size.height > 160.0 + navigationBarHeight ? 1.0 : 0.0
|
let optionsAlpha: CGFloat = size.height > 160.0 + navigationBarHeight ? 1.0 : 0.0
|
||||||
@ -177,6 +206,10 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
self.goToUserLocation()
|
self.goToUserLocation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func notificationPressed() {
|
||||||
|
self.setupProximityNotification()
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func placesPressed() {
|
@objc private func placesPressed() {
|
||||||
self.showPlacesInThisArea()
|
self.showPlacesInThisArea()
|
||||||
}
|
}
|
||||||
|
@ -568,7 +568,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|
|||||||
strongSelf.headerNode.mapNode.setMapCenter(coordinate: venue.coordinate, hidePicker: true, animated: true)
|
strongSelf.headerNode.mapNode.setMapCenter(coordinate: venue.coordinate, hidePicker: true, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: displayingPlacesButton, animated: true)
|
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: displayingPlacesButton, proximityNotification: nil, animated: true)
|
||||||
|
|
||||||
let annotations: [LocationPinAnnotation]
|
let annotations: [LocationPinAnnotation]
|
||||||
if let venues = displayedVenues {
|
if let venues = displayedVenues {
|
||||||
|
@ -35,14 +35,16 @@ class LocationViewInteraction {
|
|||||||
let goToCoordinate: (CLLocationCoordinate2D) -> Void
|
let goToCoordinate: (CLLocationCoordinate2D) -> Void
|
||||||
let requestDirections: () -> Void
|
let requestDirections: () -> Void
|
||||||
let share: () -> Void
|
let share: () -> Void
|
||||||
|
let setupProximityNotification: () -> Void
|
||||||
|
|
||||||
init(toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, requestDirections: @escaping () -> Void, share: @escaping () -> Void) {
|
init(toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, requestDirections: @escaping () -> Void, share: @escaping () -> Void, setupProximityNotification: @escaping () -> Void) {
|
||||||
self.toggleMapModeSelection = toggleMapModeSelection
|
self.toggleMapModeSelection = toggleMapModeSelection
|
||||||
self.updateMapMode = updateMapMode
|
self.updateMapMode = updateMapMode
|
||||||
self.goToUserLocation = goToUserLocation
|
self.goToUserLocation = goToUserLocation
|
||||||
self.goToCoordinate = goToCoordinate
|
self.goToCoordinate = goToCoordinate
|
||||||
self.requestDirections = requestDirections
|
self.requestDirections = requestDirections
|
||||||
self.share = share
|
self.share = share
|
||||||
|
self.setupProximityNotification = setupProximityNotification
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +142,11 @@ public final class LocationViewController: ViewController {
|
|||||||
strongSelf.present(ShareController(context: context, subject: .mapMedia(mapMedia), externalShare: true), in: .window(.root), with: nil)
|
strongSelf.present(ShareController(context: context, subject: .mapMedia(mapMedia), externalShare: true), in: .window(.root), with: nil)
|
||||||
})
|
})
|
||||||
strongSelf.present(OpenInActionSheetController(context: context, item: .location(location: mapMedia, withDirections: false), additionalAction: shareAction, openUrl: params.openUrl), in: .window(.root), with: nil)
|
strongSelf.present(OpenInActionSheetController(context: context, item: .location(location: mapMedia, withDirections: false), additionalAction: shareAction, openUrl: params.openUrl), in: .window(.root), with: nil)
|
||||||
|
}, setupProximityNotification: { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
self.scrollToTop = { [weak self] in
|
self.scrollToTop = { [weak self] in
|
||||||
|
@ -200,7 +200,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode {
|
|||||||
self.listNode.verticalScrollIndicatorColor = UIColor(white: 0.0, alpha: 0.3)
|
self.listNode.verticalScrollIndicatorColor = UIColor(white: 0.0, alpha: 0.3)
|
||||||
self.listNode.verticalScrollIndicatorFollowsOverscroll = true
|
self.listNode.verticalScrollIndicatorFollowsOverscroll = true
|
||||||
|
|
||||||
self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation)
|
self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation, setupProximityNotification: interaction.setupProximityNotification)
|
||||||
self.headerNode.mapNode.isRotateEnabled = false
|
self.headerNode.mapNode.isRotateEnabled = false
|
||||||
|
|
||||||
self.optionsNode = LocationOptionsNode(presentationData: presentationData, updateMapMode: interaction.updateMapMode)
|
self.optionsNode = LocationOptionsNode(presentationData: presentationData, updateMapMode: interaction.updateMapMode)
|
||||||
@ -258,7 +258,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode {
|
|||||||
let transition = preparedTransition(from: previousEntries ?? [], to: entries, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction)
|
let transition = preparedTransition(from: previousEntries ?? [], to: entries, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction)
|
||||||
strongSelf.enqueueTransition(transition)
|
strongSelf.enqueueTransition(transition)
|
||||||
|
|
||||||
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, animated: false)
|
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: false, animated: false)
|
||||||
|
|
||||||
switch state.selectedLocation {
|
switch state.selectedLocation {
|
||||||
case .initial:
|
case .initial:
|
||||||
|
@ -2602,7 +2602,7 @@ private func drawAlbumArtPlaceholder(into c: CGContext, arguments: TransformImag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil, drawPlaceholderWhenEmpty: Bool = true) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
var fileArtworkData: Signal<Data?, NoError> = .single(nil)
|
var fileArtworkData: Signal<Data?, NoError> = .single(nil)
|
||||||
if let fileReference = fileReference {
|
if let fileReference = fileReference {
|
||||||
let size = thumbnail ? CGSize(width: 48.0, height: 48.0) : CGSize(width: 320.0, height: 320.0)
|
let size = thumbnail ? CGSize(width: 48.0, height: 48.0) : CGSize(width: 320.0, height: 320.0)
|
||||||
@ -2685,10 +2685,12 @@ public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?,
|
|||||||
c.setFillColor(emptyColor.cgColor)
|
c.setFillColor(emptyColor.cgColor)
|
||||||
c.fill(rect)
|
c.fill(rect)
|
||||||
}
|
}
|
||||||
} else {
|
} else if drawPlaceholderWhenEmpty {
|
||||||
context.withFlippedContext { c in
|
context.withFlippedContext { c in
|
||||||
drawAlbumArtPlaceholder(into: c, arguments: arguments, thumbnail: thumbnail)
|
drawAlbumArtPlaceholder(into: c, arguments: arguments, thumbnail: thumbnail)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
public enum SemanticStatusNodeState: Equatable {
|
public enum SemanticStatusNodeState: Equatable {
|
||||||
public struct ProgressAppearance: Equatable {
|
public struct ProgressAppearance: Equatable {
|
||||||
@ -14,10 +15,19 @@ public enum SemanticStatusNodeState: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct CheckAppearance: Equatable {
|
||||||
|
public var lineWidth: CGFloat
|
||||||
|
|
||||||
|
public init(lineWidth: CGFloat) {
|
||||||
|
self.lineWidth = lineWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case none
|
case none
|
||||||
case download
|
case download
|
||||||
case play
|
case play
|
||||||
case pause
|
case pause
|
||||||
|
case check(appearance: CheckAppearance?)
|
||||||
case progress(value: CGFloat?, cancelEnabled: Bool, appearance: ProgressAppearance?)
|
case progress(value: CGFloat?, cancelEnabled: Bool, appearance: ProgressAppearance?)
|
||||||
case customIcon(UIImage)
|
case customIcon(UIImage)
|
||||||
}
|
}
|
||||||
@ -390,7 +400,7 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
|
|||||||
let previousValue = value
|
let previousValue = value
|
||||||
self.value = value
|
self.value = value
|
||||||
let timestamp = CACurrentMediaTime()
|
let timestamp = CACurrentMediaTime()
|
||||||
if let value = value, let previousValue = previousValue {
|
if let _ = value, let previousValue = previousValue {
|
||||||
if let transition = self.transition {
|
if let transition = self.transition {
|
||||||
self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: transition.valueAt(timestamp: timestamp, actualValue: previousValue))
|
self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: transition.valueAt(timestamp: timestamp, actualValue: previousValue))
|
||||||
} else {
|
} else {
|
||||||
@ -403,6 +413,116 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class SemanticStatusNodeCheckContext: SemanticStatusNodeStateContext {
|
||||||
|
final class DrawingState: NSObject, SemanticStatusNodeStateDrawingState {
|
||||||
|
let transitionFraction: CGFloat
|
||||||
|
let value: CGFloat
|
||||||
|
let appearance: SemanticStatusNodeState.CheckAppearance?
|
||||||
|
|
||||||
|
init(transitionFraction: CGFloat, value: CGFloat, appearance: SemanticStatusNodeState.CheckAppearance?) {
|
||||||
|
self.transitionFraction = transitionFraction
|
||||||
|
self.value = value
|
||||||
|
self.appearance = appearance
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func draw(context: CGContext, size: CGSize, foregroundColor: UIColor) {
|
||||||
|
let diameter = size.width
|
||||||
|
|
||||||
|
let factor = diameter / 50.0
|
||||||
|
|
||||||
|
context.saveGState()
|
||||||
|
|
||||||
|
if foregroundColor.alpha.isZero {
|
||||||
|
context.setBlendMode(.destinationOut)
|
||||||
|
context.setFillColor(UIColor(white: 0.0, alpha: self.transitionFraction).cgColor)
|
||||||
|
context.setStrokeColor(UIColor(white: 0.0, alpha: self.transitionFraction).cgColor)
|
||||||
|
} else {
|
||||||
|
context.setBlendMode(.normal)
|
||||||
|
context.setFillColor(foregroundColor.withAlphaComponent(foregroundColor.alpha * self.transitionFraction).cgColor)
|
||||||
|
context.setStrokeColor(foregroundColor.withAlphaComponent(foregroundColor.alpha * self.transitionFraction).cgColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
let center = CGPoint(x: diameter / 2.0, y: diameter / 2.0)
|
||||||
|
|
||||||
|
let lineWidth: CGFloat
|
||||||
|
if let appearance = self.appearance {
|
||||||
|
lineWidth = appearance.lineWidth
|
||||||
|
} else {
|
||||||
|
lineWidth = max(1.6, 2.25 * factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setLineWidth(max(1.7, lineWidth * factor))
|
||||||
|
context.setLineCap(.round)
|
||||||
|
context.setLineJoin(.round)
|
||||||
|
context.setMiterLimit(10.0)
|
||||||
|
|
||||||
|
let progress = self.value
|
||||||
|
let firstSegment: CGFloat = max(0.0, min(1.0, progress * 3.0))
|
||||||
|
|
||||||
|
var s = CGPoint(x: center.x - 10.0 * factor, y: center.y + 1.0 * factor)
|
||||||
|
var p1 = CGPoint(x: 7.0 * factor, y: 7.0 * factor)
|
||||||
|
var p2 = CGPoint(x: 13.0 * factor, y: -15.0 * factor)
|
||||||
|
|
||||||
|
if diameter < 36.0 {
|
||||||
|
s = CGPoint(x: center.x - 7.0 * factor, y: center.y + 1.0 * factor)
|
||||||
|
p1 = CGPoint(x: 4.5 * factor, y: 4.5 * factor)
|
||||||
|
p2 = CGPoint(x: 10.0 * factor, y: -11.0 * factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !firstSegment.isZero {
|
||||||
|
if firstSegment < 1.0 {
|
||||||
|
context.move(to: CGPoint(x: s.x + p1.x * firstSegment, y: s.y + p1.y * firstSegment))
|
||||||
|
context.addLine(to: s)
|
||||||
|
} else {
|
||||||
|
let secondSegment = (progress - 0.33) * 1.5
|
||||||
|
context.move(to: CGPoint(x: s.x + p1.x + p2.x * secondSegment, y: s.y + p1.y + p2.y * secondSegment))
|
||||||
|
context.addLine(to: CGPoint(x: s.x + p1.x, y: s.y + p1.y))
|
||||||
|
context.addLine(to: s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.strokePath()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var value: CGFloat
|
||||||
|
let appearance: SemanticStatusNodeState.CheckAppearance?
|
||||||
|
var transition: SemanticStatusNodeProgressTransition?
|
||||||
|
|
||||||
|
var isAnimating: Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
init(value: CGFloat, appearance: SemanticStatusNodeState.CheckAppearance?) {
|
||||||
|
self.value = value
|
||||||
|
self.appearance = appearance
|
||||||
|
|
||||||
|
self.animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeStateDrawingState {
|
||||||
|
let timestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
|
let resolvedValue: CGFloat
|
||||||
|
if let transition = self.transition {
|
||||||
|
resolvedValue = transition.valueAt(timestamp: timestamp, actualValue: value)
|
||||||
|
} else {
|
||||||
|
resolvedValue = value
|
||||||
|
}
|
||||||
|
return DrawingState(transitionFraction: transitionFraction, value: resolvedValue, appearance: self.appearance)
|
||||||
|
}
|
||||||
|
|
||||||
|
func animate() {
|
||||||
|
guard self.value < 1.0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let timestamp = CACurrentMediaTime()
|
||||||
|
self.value = 1.0
|
||||||
|
self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private extension SemanticStatusNodeState {
|
private extension SemanticStatusNodeState {
|
||||||
func context(current: SemanticStatusNodeStateContext?) -> SemanticStatusNodeStateContext {
|
func context(current: SemanticStatusNodeStateContext?) -> SemanticStatusNodeStateContext {
|
||||||
switch self {
|
switch self {
|
||||||
@ -427,6 +547,12 @@ private extension SemanticStatusNodeState {
|
|||||||
} else {
|
} else {
|
||||||
return SemanticStatusNodeIconContext(icon: icon)
|
return SemanticStatusNodeIconContext(icon: icon)
|
||||||
}
|
}
|
||||||
|
case let .check(appearance):
|
||||||
|
if let current = current as? SemanticStatusNodeCheckContext {
|
||||||
|
return current
|
||||||
|
} else {
|
||||||
|
return SemanticStatusNodeCheckContext(value: 0.0, appearance: appearance)
|
||||||
|
}
|
||||||
case let .progress(value, cancelEnabled, appearance):
|
case let .progress(value, cancelEnabled, appearance):
|
||||||
if let current = current as? SemanticStatusNodeProgressContext, current.displayCancel == cancelEnabled {
|
if let current = current as? SemanticStatusNodeProgressContext, current.displayCancel == cancelEnabled {
|
||||||
current.updateValue(value: value)
|
current.updateValue(value: value)
|
||||||
@ -454,14 +580,18 @@ private final class SemanticStatusNodeDrawingState: NSObject {
|
|||||||
let hollow: Bool
|
let hollow: Bool
|
||||||
let transitionState: SemanticStatusNodeTransitionDrawingState?
|
let transitionState: SemanticStatusNodeTransitionDrawingState?
|
||||||
let drawingState: SemanticStatusNodeStateDrawingState
|
let drawingState: SemanticStatusNodeStateDrawingState
|
||||||
|
let backgroundImage: UIImage?
|
||||||
|
let overlayForeground: UIColor?
|
||||||
let cutout: SemanticStatusNode.Cutout?
|
let cutout: SemanticStatusNode.Cutout?
|
||||||
|
|
||||||
init(background: UIColor, foreground: UIColor, hollow: Bool, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState, cutout: SemanticStatusNode.Cutout?) {
|
init(background: UIColor, foreground: UIColor, hollow: Bool, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: SemanticStatusNode.Cutout?) {
|
||||||
self.background = background
|
self.background = background
|
||||||
self.foreground = foreground
|
self.foreground = foreground
|
||||||
self.hollow = hollow
|
self.hollow = hollow
|
||||||
self.transitionState = transitionState
|
self.transitionState = transitionState
|
||||||
self.drawingState = drawingState
|
self.drawingState = drawingState
|
||||||
|
self.backgroundImage = backgroundImage
|
||||||
|
self.overlayForeground = overlayForeground
|
||||||
self.cutout = cutout
|
self.cutout = cutout
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -503,6 +633,14 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var overlayForegroundNodeColor: UIColor? {
|
||||||
|
didSet {
|
||||||
|
if !(self.overlayForegroundNodeColor?.isEqual(oldValue) ?? true) {
|
||||||
|
self.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let hollow: Bool
|
private let hollow: Bool
|
||||||
|
|
||||||
private var animator: ConstantDisplayLinkAnimator?
|
private var animator: ConstantDisplayLinkAnimator?
|
||||||
@ -512,9 +650,13 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
private var transtionContext: SemanticStatusNodeTransitionContext?
|
private var transtionContext: SemanticStatusNodeTransitionContext?
|
||||||
private var stateContext: SemanticStatusNodeStateContext
|
private var stateContext: SemanticStatusNodeStateContext
|
||||||
|
|
||||||
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, hollow: Bool = false) {
|
private var disposable: Disposable?
|
||||||
|
private var backgroundNodeImage: UIImage?
|
||||||
|
|
||||||
|
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil, overlayForegroundNodeColor: UIColor? = nil, hollow: Bool = false) {
|
||||||
self.backgroundNodeColor = backgroundNodeColor
|
self.backgroundNodeColor = backgroundNodeColor
|
||||||
self.foregroundNodeColor = foregroundNodeColor
|
self.foregroundNodeColor = foregroundNodeColor
|
||||||
|
self.overlayForegroundNodeColor = overlayForegroundNodeColor
|
||||||
self.hollow = hollow
|
self.hollow = hollow
|
||||||
self.state = .none
|
self.state = .none
|
||||||
self.stateContext = self.state.context(current: nil)
|
self.stateContext = self.state.context(current: nil)
|
||||||
@ -523,6 +665,22 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
|
|
||||||
self.isOpaque = false
|
self.isOpaque = false
|
||||||
self.displaysAsynchronously = true
|
self.displaysAsynchronously = true
|
||||||
|
|
||||||
|
if let image = image {
|
||||||
|
self.disposable = (image
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] transform in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let context = transform(TransformImageArguments(corners: ImageCorners(radius: strongSelf.bounds.width / 2.0), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets()))
|
||||||
|
self?.backgroundNodeImage = context?.generateImage()
|
||||||
|
self?.setNeedsDisplay()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.disposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateAnimations() {
|
private func updateAnimations() {
|
||||||
@ -595,7 +753,7 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext.drawingState(transitionFraction: 1.0 - t))
|
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext.drawingState(transitionFraction: 1.0 - t))
|
||||||
}
|
}
|
||||||
|
|
||||||
return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, hollow: self.hollow, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), cutout: nil)
|
return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, hollow: self.hollow, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), backgroundImage: self.backgroundNodeImage, overlayForeground: self.overlayForegroundNodeColor, cutout: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
||||||
@ -610,14 +768,29 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
guard let parameters = parameters as? SemanticStatusNodeDrawingState else {
|
guard let parameters = parameters as? SemanticStatusNodeDrawingState else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
context.setFillColor(parameters.background.cgColor)
|
var foregroundColor = parameters.foreground
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: bounds.size))
|
if let backgroundImage = parameters.backgroundImage?.cgImage {
|
||||||
if let transitionState = parameters.transitionState {
|
context.saveGState()
|
||||||
transitionState.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.foreground)
|
context.translateBy(x: 0.0, y: bounds.height)
|
||||||
|
context.scaleBy(x: 1.0, y: -1.0)
|
||||||
|
context.draw(backgroundImage, in: bounds)
|
||||||
|
context.restoreGState()
|
||||||
|
|
||||||
|
if let overlayForegroundColor = parameters.overlayForeground {
|
||||||
|
foregroundColor = overlayForegroundColor
|
||||||
|
} else {
|
||||||
|
foregroundColor = .white
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.setFillColor(parameters.background.cgColor)
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(), size: bounds.size))
|
||||||
}
|
}
|
||||||
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.foreground)
|
if let transitionState = parameters.transitionState {
|
||||||
|
transitionState.drawingState.draw(context: context, size: bounds.size, foregroundColor: foregroundColor)
|
||||||
|
}
|
||||||
|
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: foregroundColor)
|
||||||
|
|
||||||
if parameters.hollow {
|
if parameters.hollow {
|
||||||
context.setBlendMode(.clear)
|
context.setBlendMode(.clear)
|
||||||
context.fillEllipse(in: bounds.insetBy(dx: 8.0, dy: 8.0))
|
context.fillEllipse(in: bounds.insetBy(dx: 8.0, dy: 8.0))
|
||||||
|
@ -287,7 +287,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
|||||||
let contentSize = CGSize(width: textSize.width + 12.0, height: textSize.height + 34.0)
|
let contentSize = CGSize(width: textSize.width + 12.0, height: textSize.height + 34.0)
|
||||||
|
|
||||||
var sourceRect: CGRect
|
var sourceRect: CGRect
|
||||||
if let messageNode = strongSelf.messageNode as? ChatMessagePrevewItemNode, let forwardInfoNode = messageNode.forwardInfoReferenceNode {
|
if let messageNode = strongSelf.messageNode as? ChatMessagePreviewItemNode, let forwardInfoNode = messageNode.forwardInfoReferenceNode {
|
||||||
sourceRect = forwardInfoNode.convert(forwardInfoNode.bounds, to: strongSelf)
|
sourceRect = forwardInfoNode.convert(forwardInfoNode.bounds, to: strongSelf)
|
||||||
if let authorNameCenter = authorNameCenter {
|
if let authorNameCenter = authorNameCenter {
|
||||||
sourceRect.origin = CGPoint(x: sourceRect.minX + authorNameCenter, y: sourceRect.minY)
|
sourceRect.origin = CGPoint(x: sourceRect.minX + authorNameCenter, y: sourceRect.minY)
|
||||||
|
@ -147,7 +147,7 @@ public func requestMessageActionCallback(account: Account, messageId: MessageId,
|
|||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
switch result {
|
switch result {
|
||||||
case let .botCallbackAnswer(flags, message, url, cacheTime):
|
case let .botCallbackAnswer(flags, message, url, _):
|
||||||
if let message = message {
|
if let message = message {
|
||||||
if (flags & (1 << 1)) != 0 {
|
if (flags & (1 << 1)) != 0 {
|
||||||
return .alert(message)
|
return .alert(message)
|
||||||
|
@ -10,6 +10,7 @@ public enum MessageBubbleImageNeighbors {
|
|||||||
case bottom
|
case bottom
|
||||||
case both
|
case both
|
||||||
case side
|
case side
|
||||||
|
case extracted
|
||||||
}
|
}
|
||||||
|
|
||||||
public func messageSingleBubbleLikeImage(fillColor: UIColor, strokeColor: UIColor) -> UIImage {
|
public func messageSingleBubbleLikeImage(fillColor: UIColor, strokeColor: UIColor) -> UIImage {
|
||||||
@ -117,6 +118,12 @@ public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloa
|
|||||||
bottomLeftRadius = side ? minCornerRadius : maxCornerRadius
|
bottomLeftRadius = side ? minCornerRadius : maxCornerRadius
|
||||||
bottomRightRadius = minCornerRadius
|
bottomRightRadius = minCornerRadius
|
||||||
drawTail = false
|
drawTail = false
|
||||||
|
case .extracted:
|
||||||
|
topLeftRadius = maxCornerRadius
|
||||||
|
topRightRadius = maxCornerRadius
|
||||||
|
bottomLeftRadius = maxCornerRadius
|
||||||
|
bottomRightRadius = maxCornerRadius
|
||||||
|
drawTail = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let fixedMainDiameter: CGFloat = 33.0
|
let fixedMainDiameter: CGFloat = 33.0
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -78,7 +78,9 @@ public func chatBubbleActionButtonImage(fillColor: UIColor, strokeColor: UIColor
|
|||||||
public final class PrincipalThemeEssentialGraphics {
|
public final class PrincipalThemeEssentialGraphics {
|
||||||
public let chatMessageBackgroundIncomingMaskImage: UIImage
|
public let chatMessageBackgroundIncomingMaskImage: UIImage
|
||||||
public let chatMessageBackgroundIncomingImage: UIImage
|
public let chatMessageBackgroundIncomingImage: UIImage
|
||||||
|
public let chatMessageBackgroundIncomingExtractedImage: UIImage
|
||||||
public let chatMessageBackgroundIncomingOutlineImage: UIImage
|
public let chatMessageBackgroundIncomingOutlineImage: UIImage
|
||||||
|
public let chatMessageBackgroundIncomingExtractedOutlineImage: UIImage
|
||||||
public let chatMessageBackgroundIncomingShadowImage: UIImage
|
public let chatMessageBackgroundIncomingShadowImage: UIImage
|
||||||
public let chatMessageBackgroundIncomingHighlightedImage: UIImage
|
public let chatMessageBackgroundIncomingHighlightedImage: UIImage
|
||||||
public let chatMessageBackgroundIncomingMergedTopMaskImage: UIImage
|
public let chatMessageBackgroundIncomingMergedTopMaskImage: UIImage
|
||||||
@ -109,7 +111,9 @@ public final class PrincipalThemeEssentialGraphics {
|
|||||||
|
|
||||||
public let chatMessageBackgroundOutgoingMaskImage: UIImage
|
public let chatMessageBackgroundOutgoingMaskImage: UIImage
|
||||||
public let chatMessageBackgroundOutgoingImage: UIImage
|
public let chatMessageBackgroundOutgoingImage: UIImage
|
||||||
|
public let chatMessageBackgroundOutgoingExtractedImage: UIImage
|
||||||
public let chatMessageBackgroundOutgoingOutlineImage: UIImage
|
public let chatMessageBackgroundOutgoingOutlineImage: UIImage
|
||||||
|
public let chatMessageBackgroundOutgoingExtractedOutlineImage: UIImage
|
||||||
public let chatMessageBackgroundOutgoingShadowImage: UIImage
|
public let chatMessageBackgroundOutgoingShadowImage: UIImage
|
||||||
public let chatMessageBackgroundOutgoingHighlightedImage: UIImage
|
public let chatMessageBackgroundOutgoingHighlightedImage: UIImage
|
||||||
public let chatMessageBackgroundOutgoingMergedTopMaskImage: UIImage
|
public let chatMessageBackgroundOutgoingMergedTopMaskImage: UIImage
|
||||||
@ -238,11 +242,15 @@ public final class PrincipalThemeEssentialGraphics {
|
|||||||
if preview {
|
if preview {
|
||||||
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||||
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||||
|
self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||||
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
|
self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||||
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||||
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||||
|
self.chatMessageBackgroundOutgoingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||||
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
|
self.chatMessageBackgroundOutgoingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||||
self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)!
|
self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)!
|
||||||
self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)!
|
self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)!
|
||||||
@ -330,7 +338,9 @@ public final class PrincipalThemeEssentialGraphics {
|
|||||||
} else {
|
} else {
|
||||||
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||||
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||||
|
self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||||
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
|
self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
|
||||||
self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
|
||||||
self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||||
@ -356,7 +366,9 @@ public final class PrincipalThemeEssentialGraphics {
|
|||||||
|
|
||||||
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||||
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||||
|
self.chatMessageBackgroundOutgoingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||||
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
|
self.chatMessageBackgroundOutgoingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
|
||||||
self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
self.chatMessageBackgroundOutgoingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyShadow: true)
|
||||||
self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
|
||||||
self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Select.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Select.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_select.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Select.imageset/ic_select.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Select.imageset/ic_select.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/SelectAll.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/SelectAll.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_selectall.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/SelectAll.imageset/ic_selectall.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/SelectAll.imageset/ic_selectall.pdf
vendored
Normal file
Binary file not shown.
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
},
|
},
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"provides-namespace" : true
|
"provides-namespace" : true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Location/NotificationIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Location/NotificationIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_radiusnotify.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Location/NotificationIcon.imageset/ic_radiusnotify.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Location/NotificationIcon.imageset/ic_radiusnotify.pdf
vendored
Normal file
Binary file not shown.
Binary file not shown.
@ -669,7 +669,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, gesture: gesture, displayTextSelectionTip: displayTextSelectionTip)
|
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message, selectAll: selectAll)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, gesture: gesture, displayTextSelectionTip: displayTextSelectionTip)
|
||||||
strongSelf.currentContextController = controller
|
strongSelf.currentContextController = controller
|
||||||
controller.reactionSelected = { [weak controller] value in
|
controller.reactionSelected = { [weak controller] value in
|
||||||
guard let strongSelf = self, let message = updatedMessages.first else {
|
guard let strongSelf = self, let message = updatedMessages.first else {
|
||||||
@ -882,7 +882,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
||||||
} else {
|
} else {
|
||||||
strongSelf.openUrl(url, concealed: false)
|
strongSelf.openUrl(url, concealed: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1686,7 +1686,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
|
|
||||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: [], recognizer: nil)
|
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil)
|
||||||
strongSelf.currentContextController = controller
|
strongSelf.currentContextController = controller
|
||||||
strongSelf.forEachController({ controller in
|
strongSelf.forEachController({ controller in
|
||||||
if let controller = controller as? TooltipScreen {
|
if let controller = controller as? TooltipScreen {
|
||||||
@ -1763,7 +1763,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
})))
|
})))
|
||||||
|
|
||||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: topMessage)), items: .single(actions), reactionItems: [], recognizer: nil)
|
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: topMessage, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil)
|
||||||
strongSelf.currentContextController = controller
|
strongSelf.currentContextController = controller
|
||||||
strongSelf.forEachController({ controller in
|
strongSelf.forEachController({ controller in
|
||||||
if let controller = controller as? TooltipScreen {
|
if let controller = controller as? TooltipScreen {
|
||||||
@ -2221,10 +2221,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, openMessageStats: { [weak self] id in
|
}, openMessageStats: { [weak self] id in
|
||||||
let _ = (context.account.postbox.transaction { transaction -> CachedPeerData? in
|
let _ = (context.account.postbox.transaction { transaction -> (MessageId, CachedPeerData?)? in
|
||||||
return transaction.getPeerCachedData(peerId: id.peerId)
|
if let message = transaction.getMessage(id), let sourceMessageId = message.forwardInfo?.sourceMessageId {
|
||||||
} |> deliverOnMainQueue).start(next: { [weak self] cachedPeerData in
|
return (sourceMessageId, transaction.getPeerCachedData(peerId: sourceMessageId.peerId))
|
||||||
guard let strongSelf = self, let cachedPeerData = cachedPeerData else {
|
} else {
|
||||||
|
return (id, transaction.getPeerCachedData(peerId: id.peerId))
|
||||||
|
}
|
||||||
|
} |> deliverOnMainQueue).start(next: { [weak self] messageIdAndCachedPeerData in
|
||||||
|
guard let strongSelf = self, let (id, cachedPeerDataValue) = messageIdAndCachedPeerData, let cachedPeerData = cachedPeerDataValue else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.push(messageStatsController(context: context, messageId: id, cachedPeerData: cachedPeerData))
|
strongSelf.push(messageStatsController(context: context, messageId: id, cachedPeerData: cachedPeerData))
|
||||||
@ -6573,7 +6577,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
let peerId = peer.id
|
let peerId = peer.id
|
||||||
|
|
||||||
let cacheUsageStats = (collectCacheUsageStats(account: strongSelf.context.account, peerId: peer.id)
|
let _ = (collectCacheUsageStats(account: strongSelf.context.account, peerId: peer.id)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self, weak controller] result in
|
|> deliverOnMainQueue).start(next: { [weak self, weak controller] result in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
|
|
||||||
@ -7085,17 +7089,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
||||||
|
|
||||||
var groupingKey: Int64?
|
var groupingKey: Int64?
|
||||||
var allItemsAreAudio = true
|
var allItemsAreSame = true
|
||||||
for item in results {
|
for item in results {
|
||||||
if let item = item {
|
if let item = item {
|
||||||
let pathExtension = (item.fileName as NSString).pathExtension.lowercased()
|
let pathExtension = (item.fileName as NSString).pathExtension.lowercased()
|
||||||
if !["mp3", "m4a"].contains(pathExtension) {
|
if !["mp3", "m4a"].contains(pathExtension) {
|
||||||
allItemsAreAudio = false
|
allItemsAreSame = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
allItemsAreSame = true
|
||||||
if allItemsAreAudio {
|
if allItemsAreSame {
|
||||||
groupingKey = arc4random64()
|
groupingKey = arc4random64()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7768,6 +7772,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
value = self.presentationData.strings.Conversation_Dice_u1F3C0
|
value = self.presentationData.strings.Conversation_Dice_u1F3C0
|
||||||
case "⚽":
|
case "⚽":
|
||||||
value = self.presentationData.strings.Conversation_Dice_u26BD
|
value = self.presentationData.strings.Conversation_Dice_u26BD
|
||||||
|
case "🎰":
|
||||||
|
value = self.presentationData.strings.Conversation_Dice_u1F3B0
|
||||||
default:
|
default:
|
||||||
let emojiHex = emoji.unicodeScalars.map({ String(format:"%02x", $0.value) }).joined().uppercased()
|
let emojiHex = emoji.unicodeScalars.map({ String(format:"%02x", $0.value) }).joined().uppercased()
|
||||||
let key = "Conversation.Dice.u\(emojiHex)"
|
let key = "Conversation.Dice.u\(emojiHex)"
|
||||||
|
@ -814,7 +814,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReport, icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReport, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { controller, f in
|
}, action: { controller, f in
|
||||||
interfaceInteraction.reportMessages(selectAll ? messages : [message], controller)
|
interfaceInteraction.reportMessages(messages, controller)
|
||||||
})))
|
})))
|
||||||
} else if message.id.peerId.isReplies {
|
} else if message.id.peerId.isReplies {
|
||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuBlock, textColor: .destructive, icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuBlock, textColor: .destructive, icon: { theme in
|
||||||
@ -875,13 +875,25 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||||||
if !actions.isEmpty {
|
if !actions.isEmpty {
|
||||||
actions.append(.separator)
|
actions.append(.separator)
|
||||||
}
|
}
|
||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuMore, icon: { theme in
|
if !selectAll || messages.count == 1 {
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.actionSheet.primaryTextColor)
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSelect, icon: { theme in
|
||||||
}, action: { _, f in
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.actionSheet.primaryTextColor)
|
||||||
interfaceInteraction.beginMessageSelection(selectAll ? messages.map { $0.id } : [message.id], { transition in
|
}, action: { _, f in
|
||||||
f(.custom(transition))
|
interfaceInteraction.beginMessageSelection(selectAll ? messages.map { $0.id } : [message.id], { transition in
|
||||||
})
|
f(.custom(transition))
|
||||||
})))
|
})
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
if messages.count > 1 {
|
||||||
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuSelectAll(Int32(messages.count)), icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SelectAll"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { _, f in
|
||||||
|
interfaceInteraction.beginMessageSelection(messages.map { $0.id }, { transition in
|
||||||
|
f(.custom(transition))
|
||||||
|
})
|
||||||
|
})))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions
|
return actions
|
||||||
|
@ -327,10 +327,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let telegramDice = self.telegramDice {
|
if let telegramDice = self.telegramDice {
|
||||||
// if telegramDice.emoji == "🎲" {
|
if telegramDice.emoji == "🎰" {
|
||||||
// let animationNode = SlotMachineAnimationNode(context: item.context)
|
let animationNode = SlotMachineAnimationNode(context: item.context)
|
||||||
// self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
// } else {
|
} else {
|
||||||
let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
|
let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
|
||||||
if !item.message.effectivelyIncoming(item.context.account.peerId) {
|
if !item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||||
animationNode.success = { [weak self] in
|
animationNode.success = { [weak self] in
|
||||||
@ -340,7 +340,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
// }
|
}
|
||||||
} else {
|
} else {
|
||||||
let animationNode: AnimatedStickerNode
|
let animationNode: AnimatedStickerNode
|
||||||
if let (node, parentNode, listNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
|
if let (node, parentNode, listNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
|
||||||
@ -1478,7 +1478,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func getMessageContextSourceNode() -> ContextExtractedContentContainingNode? {
|
override func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
|
||||||
return self.contextSourceNode
|
return self.contextSourceNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import Display
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
|
||||||
enum ChatMessageBackgroundMergeType: Equatable {
|
enum ChatMessageBackgroundMergeType: Equatable {
|
||||||
case None, Side, Top(side: Bool), Bottom, Both
|
case None, Side, Top(side: Bool), Bottom, Both, Extracted
|
||||||
|
|
||||||
init(top: Bool, bottom: Bool, side: Bool) {
|
init(top: Bool, bottom: Bool, side: Bool) {
|
||||||
if top && bottom {
|
if top && bottom {
|
||||||
@ -131,6 +131,8 @@ class ChatMessageBackground: ASDisplayNode {
|
|||||||
image = highlighted ? graphics.chatMessageBackgroundIncomingMergedBothHighlightedImage : graphics.chatMessageBackgroundIncomingMergedBothImage
|
image = highlighted ? graphics.chatMessageBackgroundIncomingMergedBothHighlightedImage : graphics.chatMessageBackgroundIncomingMergedBothImage
|
||||||
case .Side:
|
case .Side:
|
||||||
image = highlighted ? graphics.chatMessageBackgroundIncomingMergedSideHighlightedImage : graphics.chatMessageBackgroundIncomingMergedSideImage
|
image = highlighted ? graphics.chatMessageBackgroundIncomingMergedSideHighlightedImage : graphics.chatMessageBackgroundIncomingMergedSideImage
|
||||||
|
case .Extracted:
|
||||||
|
image = graphics.chatMessageBackgroundIncomingExtractedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .outgoing(mergeType):
|
case let .outgoing(mergeType):
|
||||||
@ -152,6 +154,8 @@ class ChatMessageBackground: ASDisplayNode {
|
|||||||
image = highlighted ? graphics.chatMessageBackgroundOutgoingMergedBothHighlightedImage : graphics.chatMessageBackgroundOutgoingMergedBothImage
|
image = highlighted ? graphics.chatMessageBackgroundOutgoingMergedBothHighlightedImage : graphics.chatMessageBackgroundOutgoingMergedBothImage
|
||||||
case .Side:
|
case .Side:
|
||||||
image = highlighted ? graphics.chatMessageBackgroundOutgoingMergedSideHighlightedImage : graphics.chatMessageBackgroundOutgoingMergedSideImage
|
image = highlighted ? graphics.chatMessageBackgroundOutgoingMergedSideHighlightedImage : graphics.chatMessageBackgroundOutgoingMergedSideImage
|
||||||
|
case .Extracted:
|
||||||
|
image = graphics.chatMessageBackgroundOutgoingExtractedImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,6 +182,8 @@ class ChatMessageBackground: ASDisplayNode {
|
|||||||
outlineImage = graphics.chatMessageBackgroundIncomingMergedBothOutlineImage
|
outlineImage = graphics.chatMessageBackgroundIncomingMergedBothOutlineImage
|
||||||
case .Side:
|
case .Side:
|
||||||
outlineImage = graphics.chatMessageBackgroundIncomingMergedSideOutlineImage
|
outlineImage = graphics.chatMessageBackgroundIncomingMergedSideOutlineImage
|
||||||
|
case .Extracted:
|
||||||
|
outlineImage = graphics.chatMessageBackgroundIncomingExtractedOutlineImage
|
||||||
}
|
}
|
||||||
case let .outgoing(mergeType):
|
case let .outgoing(mergeType):
|
||||||
switch mergeType {
|
switch mergeType {
|
||||||
@ -195,6 +201,8 @@ class ChatMessageBackground: ASDisplayNode {
|
|||||||
outlineImage = graphics.chatMessageBackgroundOutgoingMergedBothOutlineImage
|
outlineImage = graphics.chatMessageBackgroundOutgoingMergedBothOutlineImage
|
||||||
case .Side:
|
case .Side:
|
||||||
outlineImage = graphics.chatMessageBackgroundOutgoingMergedSideOutlineImage
|
outlineImage = graphics.chatMessageBackgroundOutgoingMergedSideOutlineImage
|
||||||
|
case .Extracted:
|
||||||
|
outlineImage = graphics.chatMessageBackgroundOutgoingExtractedOutlineImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -269,6 +277,8 @@ final class ChatMessageShadowNode: ASDisplayNode {
|
|||||||
shadowImage = graphics.chatMessageBackgroundIncomingMergedBothShadowImage
|
shadowImage = graphics.chatMessageBackgroundIncomingMergedBothShadowImage
|
||||||
case .Side:
|
case .Side:
|
||||||
shadowImage = graphics.chatMessageBackgroundIncomingMergedSideShadowImage
|
shadowImage = graphics.chatMessageBackgroundIncomingMergedSideShadowImage
|
||||||
|
case .Extracted:
|
||||||
|
shadowImage = nil
|
||||||
}
|
}
|
||||||
case let .outgoing(mergeType):
|
case let .outgoing(mergeType):
|
||||||
switch mergeType {
|
switch mergeType {
|
||||||
@ -286,6 +296,8 @@ final class ChatMessageShadowNode: ASDisplayNode {
|
|||||||
shadowImage = graphics.chatMessageBackgroundOutgoingMergedBothShadowImage
|
shadowImage = graphics.chatMessageBackgroundOutgoingMergedBothShadowImage
|
||||||
case .Side:
|
case .Side:
|
||||||
shadowImage = graphics.chatMessageBackgroundOutgoingMergedSideShadowImage
|
shadowImage = graphics.chatMessageBackgroundOutgoingMergedSideShadowImage
|
||||||
|
case .Extracted:
|
||||||
|
shadowImage = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,6 +6,53 @@ import TelegramPresentationData
|
|||||||
|
|
||||||
private let maskInset: CGFloat = 1.0
|
private let maskInset: CGFloat = 1.0
|
||||||
|
|
||||||
|
func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThemeEssentialGraphics) -> UIImage? {
|
||||||
|
let image: UIImage?
|
||||||
|
switch type {
|
||||||
|
case .none:
|
||||||
|
image = nil
|
||||||
|
case let .incoming(mergeType):
|
||||||
|
switch mergeType {
|
||||||
|
case .None:
|
||||||
|
image = graphics.chatMessageBackgroundIncomingMaskImage
|
||||||
|
case let .Top(side):
|
||||||
|
if side {
|
||||||
|
image = graphics.chatMessageBackgroundIncomingMergedTopSideMaskImage
|
||||||
|
} else {
|
||||||
|
image = graphics.chatMessageBackgroundIncomingMergedTopMaskImage
|
||||||
|
}
|
||||||
|
case .Bottom:
|
||||||
|
image = graphics.chatMessageBackgroundIncomingMergedBottomMaskImage
|
||||||
|
case .Both:
|
||||||
|
image = graphics.chatMessageBackgroundIncomingMergedBothMaskImage
|
||||||
|
case .Side:
|
||||||
|
image = graphics.chatMessageBackgroundIncomingMergedSideMaskImage
|
||||||
|
case .Extracted:
|
||||||
|
image = nil
|
||||||
|
}
|
||||||
|
case let .outgoing(mergeType):
|
||||||
|
switch mergeType {
|
||||||
|
case .None:
|
||||||
|
image = graphics.chatMessageBackgroundOutgoingMaskImage
|
||||||
|
case let .Top(side):
|
||||||
|
if side {
|
||||||
|
image = graphics.chatMessageBackgroundOutgoingMergedTopSideMaskImage
|
||||||
|
} else {
|
||||||
|
image = graphics.chatMessageBackgroundOutgoingMergedTopMaskImage
|
||||||
|
}
|
||||||
|
case .Bottom:
|
||||||
|
image = graphics.chatMessageBackgroundOutgoingMergedBottomMaskImage
|
||||||
|
case .Both:
|
||||||
|
image = graphics.chatMessageBackgroundOutgoingMergedBothMaskImage
|
||||||
|
case .Side:
|
||||||
|
image = graphics.chatMessageBackgroundOutgoingMergedSideMaskImage
|
||||||
|
case .Extracted:
|
||||||
|
image = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||||
private let backgroundContent: ASDisplayNode
|
private let backgroundContent: ASDisplayNode
|
||||||
|
|
||||||
@ -41,49 +88,6 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
|||||||
self.addSubnode(self.backgroundContent)
|
self.addSubnode(self.backgroundContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func maskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThemeEssentialGraphics) -> UIImage? {
|
|
||||||
let image: UIImage?
|
|
||||||
switch type {
|
|
||||||
case .none:
|
|
||||||
image = nil
|
|
||||||
case let .incoming(mergeType):
|
|
||||||
switch mergeType {
|
|
||||||
case .None:
|
|
||||||
image = graphics.chatMessageBackgroundIncomingMaskImage
|
|
||||||
case let .Top(side):
|
|
||||||
if side {
|
|
||||||
image = graphics.chatMessageBackgroundIncomingMergedTopSideMaskImage
|
|
||||||
} else {
|
|
||||||
image = graphics.chatMessageBackgroundIncomingMergedTopMaskImage
|
|
||||||
}
|
|
||||||
case .Bottom:
|
|
||||||
image = graphics.chatMessageBackgroundIncomingMergedBottomMaskImage
|
|
||||||
case .Both:
|
|
||||||
image = graphics.chatMessageBackgroundIncomingMergedBothMaskImage
|
|
||||||
case .Side:
|
|
||||||
image = graphics.chatMessageBackgroundIncomingMergedSideMaskImage
|
|
||||||
}
|
|
||||||
case let .outgoing(mergeType):
|
|
||||||
switch mergeType {
|
|
||||||
case .None:
|
|
||||||
image = graphics.chatMessageBackgroundOutgoingMaskImage
|
|
||||||
case let .Top(side):
|
|
||||||
if side {
|
|
||||||
image = graphics.chatMessageBackgroundOutgoingMergedTopSideMaskImage
|
|
||||||
} else {
|
|
||||||
image = graphics.chatMessageBackgroundOutgoingMergedTopMaskImage
|
|
||||||
}
|
|
||||||
case .Bottom:
|
|
||||||
image = graphics.chatMessageBackgroundOutgoingMergedBottomMaskImage
|
|
||||||
case .Both:
|
|
||||||
image = graphics.chatMessageBackgroundOutgoingMergedBothMaskImage
|
|
||||||
case .Side:
|
|
||||||
image = graphics.chatMessageBackgroundOutgoingMergedSideMaskImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
|
|
||||||
func setMaskMode(_ maskMode: Bool, mediaBox: MediaBox) {
|
func setMaskMode(_ maskMode: Bool, mediaBox: MediaBox) {
|
||||||
if let currentType = self.currentType, let theme = self.theme, let essentialGraphics = self.essentialGraphics {
|
if let currentType = self.currentType, let theme = self.theme, let essentialGraphics = self.essentialGraphics {
|
||||||
self.setType(type: currentType, theme: theme, mediaBox: mediaBox, essentialGraphics: essentialGraphics, maskMode: maskMode)
|
self.setType(type: currentType, theme: theme, mediaBox: mediaBox, essentialGraphics: essentialGraphics, maskMode: maskMode)
|
||||||
@ -127,7 +131,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let maskView = self.maskView {
|
if let maskView = self.maskView {
|
||||||
maskView.image = self.maskForType(type, graphics: essentialGraphics)
|
maskView.image = bubbleMaskForType(type, graphics: essentialGraphics)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ private struct BubbleItemAttributes {
|
|||||||
var neighborSpacing: ChatMessageBubbleRelativePosition.NeighbourSpacing
|
var neighborSpacing: ChatMessageBubbleRelativePosition.NeighbourSpacing
|
||||||
}
|
}
|
||||||
|
|
||||||
private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)] {
|
private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)], Bool) {
|
||||||
var result: [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)] = []
|
var result: [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)] = []
|
||||||
var skipText = false
|
var skipText = false
|
||||||
var messageWithCaptionToAdd: (Message, ChatMessageEntryAttributes)?
|
var messageWithCaptionToAdd: (Message, ChatMessageEntryAttributes)?
|
||||||
@ -43,6 +43,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
var isAction = false
|
var isAction = false
|
||||||
|
|
||||||
var previousItemIsMusic = false
|
var previousItemIsMusic = false
|
||||||
|
var hasFiles = false
|
||||||
|
|
||||||
outer: for (message, itemAttributes) in item.content {
|
outer: for (message, itemAttributes) in item.content {
|
||||||
for attribute in message.attributes {
|
for attribute in message.attributes {
|
||||||
@ -52,8 +53,9 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isMusic = false
|
||||||
|
var isFile = false
|
||||||
inner: for media in message.media {
|
inner: for media in message.media {
|
||||||
var isMusic = false
|
|
||||||
if let _ = media as? TelegramMediaImage {
|
if let _ = media as? TelegramMediaImage {
|
||||||
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default)))
|
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default)))
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
@ -66,6 +68,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
neighborSpacing = .condensed
|
neighborSpacing = .condensed
|
||||||
}
|
}
|
||||||
isMusic = file.isMusic
|
isMusic = file.isMusic
|
||||||
|
isFile = true
|
||||||
|
hasFiles = true
|
||||||
result.append((message, ChatMessageFileBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: neighborSpacing)))
|
result.append((message, ChatMessageFileBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: neighborSpacing)))
|
||||||
}
|
}
|
||||||
} else if let action = media as? TelegramMediaAction {
|
} else if let action = media as? TelegramMediaAction {
|
||||||
@ -90,7 +94,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
} else if let _ = media as? TelegramMediaExpiredContent {
|
} else if let _ = media as? TelegramMediaExpiredContent {
|
||||||
result.removeAll()
|
result.removeAll()
|
||||||
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||||
return result
|
return (result, false)
|
||||||
} else if let _ = media as? TelegramMediaPoll {
|
} else if let _ = media as? TelegramMediaPoll {
|
||||||
result.append((message, ChatMessagePollBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
result.append((message, ChatMessagePollBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||||
} else if let _ = media as? TelegramMediaUnsupported {
|
} else if let _ = media as? TelegramMediaUnsupported {
|
||||||
@ -106,11 +110,11 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
|
|
||||||
if !messageText.isEmpty || isUnsupportedMedia {
|
if !messageText.isEmpty || isUnsupportedMedia {
|
||||||
if !skipText {
|
if !skipText {
|
||||||
if case .group = item.content {
|
if case .group = item.content, !isFile {
|
||||||
messageWithCaptionToAdd = (message, itemAttributes)
|
messageWithCaptionToAdd = (message, itemAttributes)
|
||||||
skipText = true
|
skipText = true
|
||||||
} else {
|
} else {
|
||||||
result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: isFile ? .condensed : .default)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if case .group = item.content {
|
if case .group = item.content {
|
||||||
@ -184,7 +188,12 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
var needSeparateContainers = false
|
||||||
|
if case .group = item.content, hasFiles {
|
||||||
|
needSeparateContainers = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result, needSeparateContainers)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let chatMessagePeerIdColors: [UIColor] = [
|
private let chatMessagePeerIdColors: [UIColor] = [
|
||||||
@ -202,9 +211,86 @@ private enum ContentNodeOperation {
|
|||||||
case insert(index: Int, node: ChatMessageBubbleContentNode)
|
case insert(index: Int, node: ChatMessageBubbleContentNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode {
|
class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode {
|
||||||
private let contextSourceNode: ContextExtractedContentContainingNode
|
class ContentContainer {
|
||||||
private let containerNode: ContextControllerSourceNode
|
let contentMessageStableId: UInt32
|
||||||
|
let sourceNode: ContextExtractedContentContainingNode
|
||||||
|
let containerNode: ContextControllerSourceNode
|
||||||
|
var backgroundNode: ChatMessageBackground?
|
||||||
|
var selectionBackgroundNode: ASDisplayNode?
|
||||||
|
|
||||||
|
var currentParams: (size: CGSize, contentOrigin: CGPoint, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, Bool?)?
|
||||||
|
|
||||||
|
init(contentMessageStableId: UInt32) {
|
||||||
|
self.contentMessageStableId = contentMessageStableId
|
||||||
|
|
||||||
|
self.sourceNode = ContextExtractedContentContainingNode()
|
||||||
|
self.containerNode = ContextControllerSourceNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func willUpdateIsExtractedToContextPreview(isExtractedToContextPreview: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
|
if isExtractedToContextPreview {
|
||||||
|
if let _ = self.backgroundNode {
|
||||||
|
} else if let currentParams = self.currentParams {
|
||||||
|
let backgroundNode = ChatMessageBackground()
|
||||||
|
backgroundNode.alpha = 0.0
|
||||||
|
|
||||||
|
var type: ChatMessageBackgroundType
|
||||||
|
if case .incoming = currentParams.backgroundType {
|
||||||
|
type = .incoming(.Extracted)
|
||||||
|
} else {
|
||||||
|
type = .outgoing(.Extracted)
|
||||||
|
}
|
||||||
|
|
||||||
|
backgroundNode.setType(type: type, highlighted: false, graphics: currentParams.graphics, maskMode: false, hasWallpaper: currentParams.presentationData.theme.wallpaper.hasWallpaper, transition: .immediate)
|
||||||
|
self.sourceNode.contentNode.insertSubnode(backgroundNode, at: 0)
|
||||||
|
self.backgroundNode = backgroundNode
|
||||||
|
|
||||||
|
transition.updateAlpha(node: backgroundNode, alpha: 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let currentParams = self.currentParams {
|
||||||
|
self.backgroundNode?.updateLayout(size: currentParams.size, transition: .immediate)
|
||||||
|
self.backgroundNode?.frame = CGRect(origin: currentParams.contentOrigin, size: currentParams.size)
|
||||||
|
}
|
||||||
|
} else if let backgroundNode = self.backgroundNode {
|
||||||
|
self.backgroundNode = nil
|
||||||
|
transition.updateAlpha(node: backgroundNode, alpha: 0.0, completion: { [weak backgroundNode] _ in
|
||||||
|
backgroundNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(size: CGSize, contentOrigin: CGPoint, presentationData: ChatPresentationData, graphics: PrincipalThemeEssentialGraphics, backgroundType: ChatMessageBackgroundType, messageSelection: Bool?) {
|
||||||
|
self.currentParams = (size, contentOrigin, presentationData, graphics, backgroundType, messageSelection)
|
||||||
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
|
|
||||||
|
var incoming: Bool = false
|
||||||
|
if case .incoming = backgroundType {
|
||||||
|
incoming = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing
|
||||||
|
|
||||||
|
if let messageSelection = messageSelection, messageSelection {
|
||||||
|
if let _ = self.selectionBackgroundNode {
|
||||||
|
} else {
|
||||||
|
let selectionBackgroundNode = ASDisplayNode()
|
||||||
|
self.sourceNode.contentNode.insertSubnode(selectionBackgroundNode, at: 0)
|
||||||
|
self.selectionBackgroundNode = selectionBackgroundNode
|
||||||
|
}
|
||||||
|
|
||||||
|
self.selectionBackgroundNode?.backgroundColor = messageTheme.accentTextColor.withAlphaComponent(0.08)
|
||||||
|
self.selectionBackgroundNode?.frame = bounds.offsetBy(dx: contentOrigin.x, dy: contentOrigin.y)
|
||||||
|
} else if let selectionBackgroundNode = self.selectionBackgroundNode {
|
||||||
|
self.selectionBackgroundNode = nil
|
||||||
|
selectionBackgroundNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let mainContextSourceNode: ContextExtractedContentContainingNode
|
||||||
|
private let mainContainerNode: ContextControllerSourceNode
|
||||||
private let backgroundWallpaperNode: ChatMessageBubbleBackdrop
|
private let backgroundWallpaperNode: ChatMessageBubbleBackdrop
|
||||||
private let backgroundNode: ChatMessageBackground
|
private let backgroundNode: ChatMessageBackground
|
||||||
private let shadowNode: ChatMessageShadowNode
|
private let shadowNode: ChatMessageShadowNode
|
||||||
@ -228,6 +314,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
private var replyInfoNode: ChatMessageReplyInfoNode?
|
private var replyInfoNode: ChatMessageReplyInfoNode?
|
||||||
|
|
||||||
|
private var contentContainersMaskView: UIImageView?
|
||||||
|
private var contentContainersWrapperNode: ASDisplayNode
|
||||||
|
private var contentContainers: [ContentContainer] = []
|
||||||
private(set) var contentNodes: [ChatMessageBubbleContentNode] = []
|
private(set) var contentNodes: [ChatMessageBubbleContentNode] = []
|
||||||
private var mosaicStatusNode: ChatMessageDateAndStatusNode?
|
private var mosaicStatusNode: ChatMessageDateAndStatusNode?
|
||||||
private var actionButtonsNode: ChatMessageActionButtonsNode?
|
private var actionButtonsNode: ChatMessageActionButtonsNode?
|
||||||
@ -262,9 +351,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
required init() {
|
required init() {
|
||||||
self.contextSourceNode = ContextExtractedContentContainingNode()
|
self.mainContextSourceNode = ContextExtractedContentContainingNode()
|
||||||
self.containerNode = ContextControllerSourceNode()
|
self.mainContainerNode = ContextControllerSourceNode()
|
||||||
self.backgroundWallpaperNode = ChatMessageBubbleBackdrop()
|
self.backgroundWallpaperNode = ChatMessageBubbleBackdrop()
|
||||||
|
self.contentContainersWrapperNode = ASDisplayNode()
|
||||||
|
|
||||||
self.backgroundNode = ChatMessageBackground()
|
self.backgroundNode = ChatMessageBackground()
|
||||||
self.shadowNode = ChatMessageShadowNode()
|
self.shadowNode = ChatMessageShadowNode()
|
||||||
@ -272,13 +362,16 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
|
|
||||||
super.init(layerBacked: false)
|
super.init(layerBacked: false)
|
||||||
|
|
||||||
self.containerNode.shouldBegin = { [weak self] location in
|
self.mainContainerNode.shouldBegin = { [weak self] location in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !strongSelf.backgroundNode.frame.contains(location) {
|
if !strongSelf.backgroundNode.frame.contains(location) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if strongSelf.selectionNode != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) {
|
if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) {
|
||||||
if case .action = action {
|
if case .action = action {
|
||||||
return false
|
return false
|
||||||
@ -288,14 +381,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
switch action {
|
switch action {
|
||||||
case .action, .optionalAction:
|
case .action, .optionalAction:
|
||||||
return false
|
return false
|
||||||
case .openContextMenu:
|
case let .openContextMenu(_, selectAll, _):
|
||||||
return true
|
return selectAll || strongSelf.contentContainers.isEmpty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.containerNode.activated = { [weak self] gesture, location in
|
self.mainContainerNode.activated = { [weak self] gesture, location in
|
||||||
guard let strongSelf = self, let item = strongSelf.item else {
|
guard let strongSelf = self, let item = strongSelf.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -310,12 +403,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.containerNode.addSubnode(self.contextSourceNode)
|
self.mainContainerNode.addSubnode(self.mainContextSourceNode)
|
||||||
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
|
self.mainContainerNode.targetNodeForActivationProgress = self.mainContextSourceNode.contentNode
|
||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.mainContainerNode)
|
||||||
|
|
||||||
self.contextSourceNode.contentNode.addSubnode(self.backgroundWallpaperNode)
|
self.mainContextSourceNode.contentNode.addSubnode(self.backgroundWallpaperNode)
|
||||||
self.contextSourceNode.contentNode.addSubnode(self.backgroundNode)
|
self.mainContextSourceNode.contentNode.addSubnode(self.backgroundNode)
|
||||||
|
self.mainContextSourceNode.contentNode.addSubnode(self.contentContainersWrapperNode)
|
||||||
self.addSubnode(self.messageAccessibilityArea)
|
self.addSubnode(self.messageAccessibilityArea)
|
||||||
|
|
||||||
self.messageAccessibilityArea.activate = { [weak self] in
|
self.messageAccessibilityArea.activate = { [weak self] in
|
||||||
@ -332,15 +426,15 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
self?.accessibilityElementDidBecomeFocused()
|
self?.accessibilityElementDidBecomeFocused()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtractedToContextPreview, _ in
|
self.mainContextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtractedToContextPreview, _ in
|
||||||
guard let strongSelf = self, let item = strongSelf.item else {
|
guard let strongSelf = self, let _ = strongSelf.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for contentNode in strongSelf.contentNodes {
|
for contentNode in strongSelf.contentNodes {
|
||||||
contentNode.willUpdateIsExtractedToContextPreview(isExtractedToContextPreview)
|
contentNode.willUpdateIsExtractedToContextPreview(isExtractedToContextPreview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.contextSourceNode.isExtractedToContextPreviewUpdated = { [weak self] isExtractedToContextPreview in
|
self.mainContextSourceNode.isExtractedToContextPreviewUpdated = { [weak self] isExtractedToContextPreview in
|
||||||
guard let strongSelf = self, let item = strongSelf.item else {
|
guard let strongSelf = self, let item = strongSelf.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -355,20 +449,20 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contextSourceNode.updateAbsoluteRect = { [weak self] rect, size in
|
self.mainContextSourceNode.updateAbsoluteRect = { [weak self] rect, size in
|
||||||
guard let strongSelf = self, strongSelf.contextSourceNode.isExtractedToContextPreview else {
|
guard let strongSelf = self, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.updateAbsoluteRectInternal(rect, within: size)
|
strongSelf.updateAbsoluteRectInternal(rect, within: size)
|
||||||
}
|
}
|
||||||
self.contextSourceNode.applyAbsoluteOffset = { [weak self] value, animationCurve, duration in
|
self.mainContextSourceNode.applyAbsoluteOffset = { [weak self] value, animationCurve, duration in
|
||||||
guard let strongSelf = self, strongSelf.contextSourceNode.isExtractedToContextPreview else {
|
guard let strongSelf = self, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.applyAbsoluteOffsetInternal(value: value, animationCurve: animationCurve, duration: duration)
|
strongSelf.applyAbsoluteOffsetInternal(value: value, animationCurve: animationCurve, duration: duration)
|
||||||
}
|
}
|
||||||
self.contextSourceNode.applyAbsoluteOffsetSpring = { [weak self] value, duration, damping in
|
self.mainContextSourceNode.applyAbsoluteOffsetSpring = { [weak self] value, duration, damping in
|
||||||
guard let strongSelf = self, strongSelf.contextSourceNode.isExtractedToContextPreview else {
|
guard let strongSelf = self, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.applyAbsoluteOffsetSpringInternal(value: value, duration: duration, damping: damping)
|
strongSelf.applyAbsoluteOffsetSpringInternal(value: value, duration: duration, damping: damping)
|
||||||
@ -510,8 +604,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
for contentNode in strongSelf.contentNodes {
|
for contentNode in strongSelf.contentNodes {
|
||||||
var translatedPoint: CGPoint?
|
var translatedPoint: CGPoint?
|
||||||
if let point = point, contentNode.frame.insetBy(dx: -4.0, dy: -4.0).contains(point) {
|
let convertedNodeFrame = contentNode.convert(contentNode.bounds, to: strongSelf)
|
||||||
translatedPoint = CGPoint(x: point.x - contentNode.frame.minX, y: point.y - contentNode.frame.minY)
|
if let point = point, convertedNodeFrame.insetBy(dx: -4.0, dy: -4.0).contains(point) {
|
||||||
|
translatedPoint = strongSelf.convert(point, to: contentNode)
|
||||||
}
|
}
|
||||||
contentNode.updateTouchesAtPoint(translatedPoint)
|
contentNode.updateTouchesAtPoint(translatedPoint)
|
||||||
}
|
}
|
||||||
@ -1003,7 +1098,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
var contentPropertiesAndPrepareLayouts: [(Message, Bool, ChatMessageEntryAttributes, BubbleItemAttributes, (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))))] = []
|
var contentPropertiesAndPrepareLayouts: [(Message, Bool, ChatMessageEntryAttributes, BubbleItemAttributes, (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))))] = []
|
||||||
var addedContentNodes: [(Message, ChatMessageBubbleContentNode)]?
|
var addedContentNodes: [(Message, ChatMessageBubbleContentNode)]?
|
||||||
|
|
||||||
let contentNodeMessagesAndClasses = contentNodeMessagesAndClassesForItem(item)
|
let (contentNodeMessagesAndClasses, needSeparateContainers) = contentNodeMessagesAndClassesForItem(item)
|
||||||
for (contentNodeMessage, contentNodeClass, attributes, bubbleAttributes) in contentNodeMessagesAndClasses {
|
for (contentNodeMessage, contentNodeClass, attributes, bubbleAttributes) in contentNodeMessagesAndClasses {
|
||||||
var found = false
|
var found = false
|
||||||
for (currentMessage, currentClass, supportsMosaic, currentLayout) in currentContentClassesPropertiesAndLayouts {
|
for (currentMessage, currentClass, supportsMosaic, currentLayout) in currentContentClassesPropertiesAndLayouts {
|
||||||
@ -1076,7 +1171,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
inlineBotNameString = nil
|
inlineBotNameString = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentPropertiesAndLayouts: [(CGSize?, ChatMessageBubbleContentProperties, ChatMessageBubblePreparePosition, BubbleItemAttributes, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void)))] = []
|
var contentPropertiesAndLayouts: [(CGSize?, ChatMessageBubbleContentProperties, ChatMessageBubblePreparePosition, BubbleItemAttributes, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void)), UInt32?, Bool?)] = []
|
||||||
|
|
||||||
let topNodeMergeStatus: ChatMessageBubbleMergeStatus = mergedTop.merged ? (incoming ? .Left : .Right) : .None(incoming ? .Incoming : .Outgoing)
|
let topNodeMergeStatus: ChatMessageBubbleMergeStatus = mergedTop.merged ? (incoming ? .Left : .Right) : .None(incoming ? .Incoming : .Outgoing)
|
||||||
let bottomNodeMergeStatus: ChatMessageBubbleMergeStatus = mergedBottom.merged ? (incoming ? .Left : .Right) : .None(incoming ? .Incoming : .Outgoing)
|
let bottomNodeMergeStatus: ChatMessageBubbleMergeStatus = mergedBottom.merged ? (incoming ? .Left : .Right) : .None(incoming ? .Incoming : .Outgoing)
|
||||||
@ -1178,7 +1273,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
let (properties, unboundSize, maxNodeWidth, nodeLayout) = prepareLayout(contentItem, layoutConstants, prepareContentPosition, itemSelection, CGSize(width: maximumContentWidth, height: CGFloat.greatestFiniteMagnitude))
|
let (properties, unboundSize, maxNodeWidth, nodeLayout) = prepareLayout(contentItem, layoutConstants, prepareContentPosition, itemSelection, CGSize(width: maximumContentWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||||
maximumNodeWidth = min(maximumNodeWidth, maxNodeWidth)
|
maximumNodeWidth = min(maximumNodeWidth, maxNodeWidth)
|
||||||
|
|
||||||
contentPropertiesAndLayouts.append((unboundSize, properties, prepareContentPosition, bubbleAttributes, nodeLayout))
|
contentPropertiesAndLayouts.append((unboundSize, properties, prepareContentPosition, bubbleAttributes, nodeLayout, needSeparateContainers ? message.stableId : nil, itemSelection))
|
||||||
|
|
||||||
switch properties.hidesBackground {
|
switch properties.hidesBackground {
|
||||||
case .never:
|
case .never:
|
||||||
@ -1503,7 +1598,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentNodePropertiesAndFinalize: [(ChatMessageBubbleContentProperties, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))] = []
|
var contentNodePropertiesAndFinalize: [(ChatMessageBubbleContentProperties, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void), UInt32?, Bool?)] = []
|
||||||
|
|
||||||
var maxContentWidth: CGFloat = headerSize.width
|
var maxContentWidth: CGFloat = headerSize.width
|
||||||
|
|
||||||
@ -1515,7 +1610,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 ..< contentPropertiesAndLayouts.count {
|
for i in 0 ..< contentPropertiesAndLayouts.count {
|
||||||
let (_, contentNodeProperties, preparePosition, isAttachment, contentNodeLayout) = contentPropertiesAndLayouts[i]
|
let (_, contentNodeProperties, preparePosition, _, contentNodeLayout, contentGroupId, itemSelection) = contentPropertiesAndLayouts[i]
|
||||||
|
|
||||||
if let mosaicRange = mosaicRange, mosaicRange.contains(i), let (framesAndPositions, size) = calculatedGroupFramesAndSize {
|
if let mosaicRange = mosaicRange, mosaicRange.contains(i), let (framesAndPositions, size) = calculatedGroupFramesAndSize {
|
||||||
let mosaicIndex = i - mosaicRange.lowerBound
|
let mosaicIndex = i - mosaicRange.lowerBound
|
||||||
@ -1619,7 +1714,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
|
|
||||||
let (_, contentNodeFinalize) = contentNodeLayout(framesAndPositions[mosaicIndex].0.size, .mosaic(position: ChatMessageBubbleContentMosaicPosition(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight), wide: position.isWide))
|
let (_, contentNodeFinalize) = contentNodeLayout(framesAndPositions[mosaicIndex].0.size, .mosaic(position: ChatMessageBubbleContentMosaicPosition(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight), wide: position.isWide))
|
||||||
|
|
||||||
contentNodePropertiesAndFinalize.append((contentNodeProperties, contentNodeFinalize))
|
contentNodePropertiesAndFinalize.append((contentNodeProperties, contentNodeFinalize, contentGroupId, itemSelection))
|
||||||
|
|
||||||
maxContentWidth = max(maxContentWidth, size.width)
|
maxContentWidth = max(maxContentWidth, size.width)
|
||||||
} else {
|
} else {
|
||||||
@ -1663,16 +1758,22 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
#endif
|
#endif
|
||||||
maxContentWidth = max(maxContentWidth, contentNodeWidth)
|
maxContentWidth = max(maxContentWidth, contentNodeWidth)
|
||||||
|
|
||||||
contentNodePropertiesAndFinalize.append((contentNodeProperties, contentNodeFinalize))
|
contentNodePropertiesAndFinalize.append((contentNodeProperties, contentNodeFinalize, contentGroupId, itemSelection))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentSize = CGSize(width: maxContentWidth, height: 0.0)
|
var contentSize = CGSize(width: maxContentWidth, height: 0.0)
|
||||||
var contentNodeFramesPropertiesAndApply: [(CGRect, ChatMessageBubbleContentProperties, (ListViewItemUpdateAnimation, Bool) -> Void)] = []
|
var contentNodeFramesPropertiesAndApply: [(CGRect, ChatMessageBubbleContentProperties, (ListViewItemUpdateAnimation, Bool) -> Void)] = []
|
||||||
|
var contentContainerNodeFrames: [(UInt32, CGRect, Bool?)] = []
|
||||||
|
var currentContainerGroupId: UInt32?
|
||||||
|
var currentItemSelection: Bool?
|
||||||
|
|
||||||
var contentNodesHeight: CGFloat = 0.0
|
var contentNodesHeight: CGFloat = 0.0
|
||||||
|
var totalContentNodesHeight: CGFloat = 0.0
|
||||||
|
|
||||||
var mosaicStatusOrigin: CGPoint?
|
var mosaicStatusOrigin: CGPoint?
|
||||||
for i in 0 ..< contentNodePropertiesAndFinalize.count {
|
for i in 0 ..< contentNodePropertiesAndFinalize.count {
|
||||||
let (properties, finalize) = contentNodePropertiesAndFinalize[i]
|
let (properties, finalize, contentGroupId, itemSelection) = contentNodePropertiesAndFinalize[i]
|
||||||
|
|
||||||
if let mosaicRange = mosaicRange, mosaicRange.contains(i), let (framesAndPositions, size) = calculatedGroupFramesAndSize {
|
if let mosaicRange = mosaicRange, mosaicRange.contains(i), let (framesAndPositions, size) = calculatedGroupFramesAndSize {
|
||||||
let mosaicIndex = i - mosaicRange.lowerBound
|
let mosaicIndex = i - mosaicRange.lowerBound
|
||||||
@ -1689,21 +1790,37 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
|
|
||||||
if mosaicIndex == mosaicRange.upperBound - 1 {
|
if mosaicIndex == mosaicRange.upperBound - 1 {
|
||||||
contentNodesHeight += size.height
|
contentNodesHeight += size.height
|
||||||
|
totalContentNodesHeight += size.height
|
||||||
|
|
||||||
mosaicStatusOrigin = contentNodeFrame.bottomRight
|
mosaicStatusOrigin = contentNodeFrame.bottomRight
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if i == 0 && !headerSize.height.isZero {
|
if i == 0 && !headerSize.height.isZero {
|
||||||
contentNodesHeight += properties.headerSpacing
|
contentNodesHeight += properties.headerSpacing
|
||||||
|
totalContentNodesHeight += contentNodesHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentContainerGroupId != contentGroupId {
|
||||||
|
if let containerGroupId = currentContainerGroupId {
|
||||||
|
contentContainerNodeFrames.append((containerGroupId, CGRect(x: 0.0, y: totalContentNodesHeight - contentNodesHeight, width: maxContentWidth, height: contentNodesHeight), currentItemSelection))
|
||||||
|
}
|
||||||
|
contentNodesHeight = 0.0
|
||||||
|
currentContainerGroupId = contentGroupId
|
||||||
|
currentItemSelection = itemSelection
|
||||||
}
|
}
|
||||||
|
|
||||||
let (size, apply) = finalize(maxContentWidth)
|
let (size, apply) = finalize(maxContentWidth)
|
||||||
contentNodeFramesPropertiesAndApply.append((CGRect(origin: CGPoint(x: 0.0, y: contentNodesHeight), size: size), properties, apply))
|
contentNodeFramesPropertiesAndApply.append((CGRect(origin: CGPoint(x: 0.0, y: contentNodesHeight), size: size), properties, apply))
|
||||||
|
|
||||||
contentNodesHeight += size.height
|
contentNodesHeight += size.height
|
||||||
|
totalContentNodesHeight += size.height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
contentSize.height += contentNodesHeight
|
contentSize.height += totalContentNodesHeight
|
||||||
|
|
||||||
|
if let containerGroupId = currentContainerGroupId {
|
||||||
|
contentContainerNodeFrames.append((containerGroupId, CGRect(x: 0.0, y: totalContentNodesHeight - contentNodesHeight, width: maxContentWidth, height: contentNodesHeight), currentItemSelection))
|
||||||
|
}
|
||||||
|
|
||||||
var actionButtonsSizeAndApply: (CGSize, (Bool) -> ChatMessageActionButtonsNode)?
|
var actionButtonsSizeAndApply: (CGSize, (Bool) -> ChatMessageActionButtonsNode)?
|
||||||
if let actionButtonsFinalize = actionButtonsFinalize {
|
if let actionButtonsFinalize = actionButtonsFinalize {
|
||||||
@ -1829,6 +1946,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
addedContentNodes: addedContentNodes,
|
addedContentNodes: addedContentNodes,
|
||||||
contentNodeMessagesAndClasses: contentNodeMessagesAndClasses,
|
contentNodeMessagesAndClasses: contentNodeMessagesAndClasses,
|
||||||
contentNodeFramesPropertiesAndApply: contentNodeFramesPropertiesAndApply,
|
contentNodeFramesPropertiesAndApply: contentNodeFramesPropertiesAndApply,
|
||||||
|
contentContainerNodeFrames: contentContainerNodeFrames,
|
||||||
mosaicStatusOrigin: mosaicStatusOrigin,
|
mosaicStatusOrigin: mosaicStatusOrigin,
|
||||||
mosaicStatusSizeAndApply: mosaicStatusSizeAndApply,
|
mosaicStatusSizeAndApply: mosaicStatusSizeAndApply,
|
||||||
updatedShareButtonNode: updatedShareButtonNode,
|
updatedShareButtonNode: updatedShareButtonNode,
|
||||||
@ -1868,6 +1986,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
addedContentNodes: [(Message, ChatMessageBubbleContentNode)]?,
|
addedContentNodes: [(Message, ChatMessageBubbleContentNode)]?,
|
||||||
contentNodeMessagesAndClasses: [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)],
|
contentNodeMessagesAndClasses: [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)],
|
||||||
contentNodeFramesPropertiesAndApply: [(CGRect, ChatMessageBubbleContentProperties, (ListViewItemUpdateAnimation, Bool) -> Void)],
|
contentNodeFramesPropertiesAndApply: [(CGRect, ChatMessageBubbleContentProperties, (ListViewItemUpdateAnimation, Bool) -> Void)],
|
||||||
|
contentContainerNodeFrames: [(UInt32, CGRect, Bool?)],
|
||||||
mosaicStatusOrigin: CGPoint?,
|
mosaicStatusOrigin: CGPoint?,
|
||||||
mosaicStatusSizeAndApply: (CGSize, (Bool) -> ChatMessageDateAndStatusNode)?,
|
mosaicStatusSizeAndApply: (CGSize, (Bool) -> ChatMessageDateAndStatusNode)?,
|
||||||
updatedShareButtonNode: HighlightableButtonNode?,
|
updatedShareButtonNode: HighlightableButtonNode?,
|
||||||
@ -1876,10 +1995,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
guard let strongSelf = selfReference.value else {
|
guard let strongSelf = selfReference.value else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let previousContextFrame = strongSelf.containerNode.frame
|
let previousContextFrame = strongSelf.mainContainerNode.frame
|
||||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
strongSelf.mainContainerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||||
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
strongSelf.mainContextSourceNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||||
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
strongSelf.mainContextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||||
|
strongSelf.contentContainersWrapperNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||||
|
|
||||||
strongSelf.appliedItem = item
|
strongSelf.appliedItem = item
|
||||||
strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature)
|
strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature)
|
||||||
@ -1922,7 +2042,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
isAppearing = true
|
isAppearing = true
|
||||||
deliveryFailedNode = ChatMessageDeliveryFailedNode(tapped: { [weak strongSelf] in
|
deliveryFailedNode = ChatMessageDeliveryFailedNode(tapped: { [weak strongSelf] in
|
||||||
if let item = strongSelf?.item {
|
if let item = strongSelf?.item {
|
||||||
item.controllerInteraction.requestRedeliveryOfFailedMessages(item.content.firstMessage.id)
|
item.controllerInteraction.requestRedeliveryOfFailedMessages(item.content.firstMessage.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
strongSelf.deliveryFailedNode = deliveryFailedNode
|
strongSelf.deliveryFailedNode = deliveryFailedNode
|
||||||
@ -1950,7 +2070,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
if !nameNode.isNodeLoaded {
|
if !nameNode.isNodeLoaded {
|
||||||
nameNode.isUserInteractionEnabled = false
|
nameNode.isUserInteractionEnabled = false
|
||||||
}
|
}
|
||||||
strongSelf.contextSourceNode.contentNode.addSubnode(nameNode)
|
strongSelf.mainContextSourceNode.contentNode.addSubnode(nameNode)
|
||||||
}
|
}
|
||||||
nameNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + nameNodeOriginY), size: nameNodeSizeApply.0)
|
nameNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + nameNodeOriginY), size: nameNodeSizeApply.0)
|
||||||
nameNode.displaysAsynchronously = !item.presentationData.isPreview
|
nameNode.displaysAsynchronously = !item.presentationData.isPreview
|
||||||
@ -1962,7 +2082,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
} else {
|
} else {
|
||||||
credibilityIconNode = ASImageNode()
|
credibilityIconNode = ASImageNode()
|
||||||
strongSelf.credibilityIconNode = credibilityIconNode
|
strongSelf.credibilityIconNode = credibilityIconNode
|
||||||
strongSelf.contextSourceNode.contentNode.addSubnode(credibilityIconNode)
|
strongSelf.mainContextSourceNode.contentNode.addSubnode(credibilityIconNode)
|
||||||
}
|
}
|
||||||
credibilityIconNode.frame = CGRect(origin: CGPoint(x: nameNode.frame.maxX + 4.0, y: nameNode.frame.minY), size: credibilityIconImage.size)
|
credibilityIconNode.frame = CGRect(origin: CGPoint(x: nameNode.frame.maxX + 4.0, y: nameNode.frame.minY), size: credibilityIconImage.size)
|
||||||
credibilityIconNode.image = credibilityIconImage
|
credibilityIconNode.image = credibilityIconImage
|
||||||
@ -1978,7 +2098,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
if !adminBadgeNode.isNodeLoaded {
|
if !adminBadgeNode.isNodeLoaded {
|
||||||
adminBadgeNode.isUserInteractionEnabled = false
|
adminBadgeNode.isUserInteractionEnabled = false
|
||||||
}
|
}
|
||||||
strongSelf.contextSourceNode.contentNode.addSubnode(adminBadgeNode)
|
strongSelf.mainContextSourceNode.contentNode.addSubnode(adminBadgeNode)
|
||||||
adminBadgeNode.frame = adminBadgeFrame
|
adminBadgeNode.frame = adminBadgeFrame
|
||||||
} else {
|
} else {
|
||||||
let previousAdminBadgeFrame = adminBadgeNode.frame
|
let previousAdminBadgeFrame = adminBadgeNode.frame
|
||||||
@ -2000,7 +2120,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
strongSelf.forwardInfoNode = forwardInfoNode
|
strongSelf.forwardInfoNode = forwardInfoNode
|
||||||
var animateFrame = true
|
var animateFrame = true
|
||||||
if forwardInfoNode.supernode == nil {
|
if forwardInfoNode.supernode == nil {
|
||||||
strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
|
strongSelf.mainContextSourceNode.contentNode.addSubnode(forwardInfoNode)
|
||||||
animateFrame = false
|
animateFrame = false
|
||||||
forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in
|
forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in
|
||||||
guard let strongSelf = strongSelf, let item = strongSelf.item else {
|
guard let strongSelf = strongSelf, let item = strongSelf.item else {
|
||||||
@ -2025,7 +2145,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
strongSelf.replyInfoNode = replyInfoNode
|
strongSelf.replyInfoNode = replyInfoNode
|
||||||
var animateFrame = true
|
var animateFrame = true
|
||||||
if replyInfoNode.supernode == nil {
|
if replyInfoNode.supernode == nil {
|
||||||
strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode)
|
strongSelf.mainContextSourceNode.contentNode.addSubnode(replyInfoNode)
|
||||||
animateFrame = false
|
animateFrame = false
|
||||||
}
|
}
|
||||||
let previousReplyInfoNodeFrame = replyInfoNode.frame
|
let previousReplyInfoNodeFrame = replyInfoNode.frame
|
||||||
@ -2040,6 +2160,148 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
strongSelf.replyInfoNode = nil
|
strongSelf.replyInfoNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var incomingOffset: CGFloat = 0.0
|
||||||
|
switch backgroundType {
|
||||||
|
case .incoming:
|
||||||
|
incomingOffset = 5.0
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasSelection = false
|
||||||
|
for (stableId, relativeFrame, itemSelection) in contentContainerNodeFrames {
|
||||||
|
if let itemSelection = itemSelection, itemSelection {
|
||||||
|
hasSelection = true
|
||||||
|
}
|
||||||
|
var contentContainer: ContentContainer? = strongSelf.contentContainers.first(where: { $0.contentMessageStableId == stableId })
|
||||||
|
|
||||||
|
let previousContextFrame = contentContainer?.containerNode.frame
|
||||||
|
let previousContextContentFrame = contentContainer?.sourceNode.contentRect
|
||||||
|
|
||||||
|
if contentContainer == nil {
|
||||||
|
let container = ContentContainer(contentMessageStableId: stableId)
|
||||||
|
let contextSourceNode = container.sourceNode
|
||||||
|
let containerNode = container.containerNode
|
||||||
|
|
||||||
|
container.containerNode.shouldBegin = { [weak strongSelf, weak containerNode] location in
|
||||||
|
guard let strongSelf = strongSelf, let strongContainerNode = containerNode else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let location = location.offsetBy(dx: 0.0, dy: strongContainerNode.frame.minY)
|
||||||
|
if !strongSelf.backgroundNode.frame.contains(location) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strongSelf.selectionNode != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) {
|
||||||
|
if case .action = action {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
|
||||||
|
switch action {
|
||||||
|
case .action, .optionalAction:
|
||||||
|
return false
|
||||||
|
case let .openContextMenu(_, selectAll, _):
|
||||||
|
return !selectAll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
containerNode.activated = { [weak strongSelf, weak containerNode] gesture, location in
|
||||||
|
guard let strongSelf = strongSelf, let strongContainerNode = containerNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let location = location.offsetBy(dx: 0.0, dy: strongContainerNode.frame.minY)
|
||||||
|
strongSelf.mainContainerNode.activated?(gesture, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
containerNode.addSubnode(contextSourceNode)
|
||||||
|
containerNode.targetNodeForActivationProgress = contextSourceNode.contentNode
|
||||||
|
strongSelf.contentContainersWrapperNode.addSubnode(containerNode)
|
||||||
|
|
||||||
|
contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak strongSelf, weak container, weak contextSourceNode] isExtractedToContextPreview, transition in
|
||||||
|
guard let strongSelf = strongSelf, let strongContextSourceNode = contextSourceNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
container?.willUpdateIsExtractedToContextPreview(isExtractedToContextPreview: isExtractedToContextPreview, transition: transition)
|
||||||
|
for contentNode in strongSelf.contentNodes {
|
||||||
|
if contentNode.supernode === strongContextSourceNode.contentNode {
|
||||||
|
contentNode.willUpdateIsExtractedToContextPreview(isExtractedToContextPreview)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contextSourceNode.isExtractedToContextPreviewUpdated = { [weak strongSelf, weak contextSourceNode] isExtractedToContextPreview in
|
||||||
|
guard let strongSelf = strongSelf, let strongContextSourceNode = contextSourceNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if !isExtractedToContextPreview, let (rect, size) = strongSelf.absoluteRect {
|
||||||
|
// strongSelf.updateAbsoluteRect(rect, within: size)
|
||||||
|
// }
|
||||||
|
|
||||||
|
for contentNode in strongSelf.contentNodes {
|
||||||
|
if contentNode.supernode === strongContextSourceNode.contentNode {
|
||||||
|
contentNode.updateIsExtractedToContextPreview(isExtractedToContextPreview)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contextSourceNode.updateAbsoluteRect = { [weak strongSelf] rect, size in
|
||||||
|
guard let strongSelf = strongSelf, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// strongSelf.updateAbsoluteRectInternal(rect, within: size)
|
||||||
|
}
|
||||||
|
contextSourceNode.applyAbsoluteOffset = { [weak strongSelf] value, animationCurve, duration in
|
||||||
|
guard let strongSelf = strongSelf, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// strongSelf.applyAbsoluteOffsetInternal(value: value, animationCurve: animationCurve, duration: duration)
|
||||||
|
}
|
||||||
|
contextSourceNode.applyAbsoluteOffsetSpring = { [weak strongSelf] value, duration, damping in
|
||||||
|
guard let strongSelf = strongSelf, strongSelf.mainContextSourceNode.isExtractedToContextPreview else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// strongSelf.applyAbsoluteOffsetSpringInternal(value: value, duration: duration, damping: damping)
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.contentContainers.append(container)
|
||||||
|
contentContainer = container
|
||||||
|
}
|
||||||
|
|
||||||
|
contentContainer?.sourceNode.frame = CGRect(origin: CGPoint(), size: relativeFrame.size)
|
||||||
|
contentContainer?.sourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: relativeFrame.size)
|
||||||
|
contentContainer?.containerNode.frame = relativeFrame
|
||||||
|
|
||||||
|
contentContainer?.sourceNode.contentRect = CGRect(origin: CGPoint(x: backgroundFrame.minX + incomingOffset, y: 0.0), size: relativeFrame.size)
|
||||||
|
contentContainer?.containerNode.targetNodeForActivationProgressContentRect = relativeFrame.offsetBy(dx: backgroundFrame.minX + incomingOffset, dy: 0.0)
|
||||||
|
|
||||||
|
if previousContextFrame?.size != contentContainer?.containerNode.bounds.size || previousContextContentFrame != contentContainer?.sourceNode.contentRect {
|
||||||
|
contentContainer?.sourceNode.layoutUpdated?(relativeFrame.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentContainer?.update(size: relativeFrame.size, contentOrigin: contentOrigin, presentationData: item.presentationData, graphics: graphics, backgroundType: backgroundType, messageSelection: itemSelection)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasSelection {
|
||||||
|
var currentMaskView: UIImageView?
|
||||||
|
if let maskView = strongSelf.contentContainersWrapperNode.view.mask as? UIImageView {
|
||||||
|
currentMaskView = maskView
|
||||||
|
} else {
|
||||||
|
currentMaskView = UIImageView()
|
||||||
|
strongSelf.contentContainersWrapperNode.view.mask = currentMaskView
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMaskView?.frame = CGRect(origin: contentOrigin, size: backgroundFrame.size).insetBy(dx: -1.0, dy: -1.0)
|
||||||
|
currentMaskView?.image = bubbleMaskForType(backgroundType, graphics: graphics)
|
||||||
|
} else {
|
||||||
|
strongSelf.contentContainersWrapperNode.view.mask = nil
|
||||||
|
}
|
||||||
|
|
||||||
if removedContentNodeIndices?.count ?? 0 != 0 || addedContentNodes?.count ?? 0 != 0 {
|
if removedContentNodeIndices?.count ?? 0 != 0 || addedContentNodes?.count ?? 0 != 0 {
|
||||||
var updatedContentNodes = strongSelf.contentNodes
|
var updatedContentNodes = strongSelf.contentNodes
|
||||||
|
|
||||||
@ -2060,15 +2322,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let addedContentNodes = addedContentNodes {
|
if let addedContentNodes = addedContentNodes {
|
||||||
for (_, contentNode) in addedContentNodes {
|
for (contentNodeMessage, contentNode) in addedContentNodes {
|
||||||
updatedContentNodes.append(contentNode)
|
updatedContentNodes.append(contentNode)
|
||||||
strongSelf.contextSourceNode.contentNode.addSubnode(contentNode)
|
|
||||||
|
let contextSourceNode: ContextExtractedContentContainingNode = strongSelf.contentContainers.first(where: { $0.contentMessageStableId == contentNodeMessage.stableId })?.sourceNode ?? strongSelf.mainContextSourceNode
|
||||||
|
|
||||||
|
contextSourceNode.contentNode.addSubnode(contentNode)
|
||||||
|
|
||||||
contentNode.visibility = strongSelf.visibility
|
contentNode.visibility = strongSelf.visibility
|
||||||
contentNode.updateIsTextSelectionActive = { [weak strongSelf] value in
|
contentNode.updateIsTextSelectionActive = { [weak contextSourceNode] value in
|
||||||
strongSelf?.contextSourceNode.updateDistractionFreeMode?(value)
|
contextSourceNode?.updateDistractionFreeMode?(value)
|
||||||
}
|
}
|
||||||
contentNode.updateIsExtractedToContextPreview(strongSelf.contextSourceNode.isExtractedToContextPreview)
|
contentNode.updateIsExtractedToContextPreview(contextSourceNode.isExtractedToContextPreview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2138,7 +2403,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
if mosaicStatusNode !== strongSelf.mosaicStatusNode {
|
if mosaicStatusNode !== strongSelf.mosaicStatusNode {
|
||||||
strongSelf.mosaicStatusNode?.removeFromSupernode()
|
strongSelf.mosaicStatusNode?.removeFromSupernode()
|
||||||
strongSelf.mosaicStatusNode = mosaicStatusNode
|
strongSelf.mosaicStatusNode = mosaicStatusNode
|
||||||
strongSelf.contextSourceNode.contentNode.addSubnode(mosaicStatusNode)
|
strongSelf.mainContextSourceNode.contentNode.addSubnode(mosaicStatusNode)
|
||||||
}
|
}
|
||||||
let absoluteOrigin = mosaicStatusOrigin.offsetBy(dx: contentOrigin.x, dy: contentOrigin.y)
|
let absoluteOrigin = mosaicStatusOrigin.offsetBy(dx: contentOrigin.x, dy: contentOrigin.y)
|
||||||
mosaicStatusNode.frame = CGRect(origin: CGPoint(x: absoluteOrigin.x - layoutConstants.image.statusInsets.right - size.width, y: absoluteOrigin.y - layoutConstants.image.statusInsets.bottom - size.height), size: size)
|
mosaicStatusNode.frame = CGRect(origin: CGPoint(x: absoluteOrigin.x - layoutConstants.image.statusInsets.right - size.width, y: absoluteOrigin.y - layoutConstants.image.statusInsets.bottom - size.height), size: size)
|
||||||
@ -2164,7 +2429,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
strongSelf.shareButtonNode = nil
|
strongSelf.shareButtonNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .System = animation, !strongSelf.contextSourceNode.isExtractedToContextPreview {
|
if case .System = animation, !strongSelf.mainContextSourceNode.isExtractedToContextPreview {
|
||||||
if !strongSelf.backgroundNode.frame.equalTo(backgroundFrame) {
|
if !strongSelf.backgroundNode.frame.equalTo(backgroundFrame) {
|
||||||
strongSelf.backgroundFrameTransition = (strongSelf.backgroundNode.frame, backgroundFrame)
|
strongSelf.backgroundFrameTransition = (strongSelf.backgroundNode.frame, backgroundFrame)
|
||||||
strongSelf.enableTransitionClippingNode()
|
strongSelf.enableTransitionClippingNode()
|
||||||
@ -2184,7 +2449,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
strongSelf.disableTransitionClippingNode()
|
strongSelf.disableTransitionClippingNode()
|
||||||
|
|
||||||
if case .System = animation, strongSelf.contextSourceNode.isExtractedToContextPreview {
|
if case .System = animation, strongSelf.mainContextSourceNode.isExtractedToContextPreview {
|
||||||
transition.updateFrame(node: strongSelf.backgroundNode, frame: backgroundFrame)
|
transition.updateFrame(node: strongSelf.backgroundNode, frame: backgroundFrame)
|
||||||
strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: transition)
|
strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: transition)
|
||||||
strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition)
|
strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition)
|
||||||
@ -2238,20 +2503,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
strongSelf.actionButtonsNode = nil
|
strongSelf.actionButtonsNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var incomingOffset: CGFloat = 0.0
|
let previousContextContentFrame = strongSelf.mainContextSourceNode.contentRect
|
||||||
switch backgroundType {
|
strongSelf.mainContextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0)
|
||||||
case .incoming:
|
strongSelf.mainContainerNode.targetNodeForActivationProgressContentRect = strongSelf.mainContextSourceNode.contentRect
|
||||||
incomingOffset = 5.0
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
let previousContextContentFrame = strongSelf.contextSourceNode.contentRect
|
if previousContextFrame.size != strongSelf.mainContextSourceNode.bounds.size || previousContextContentFrame != strongSelf.mainContextSourceNode.contentRect {
|
||||||
strongSelf.contextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0)
|
strongSelf.mainContextSourceNode.layoutUpdated?(strongSelf.mainContextSourceNode.bounds.size)
|
||||||
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
|
||||||
|
|
||||||
if previousContextFrame.size != strongSelf.contextSourceNode.bounds.size || previousContextContentFrame != strongSelf.contextSourceNode.contentRect {
|
|
||||||
strongSelf.contextSourceNode.layoutUpdated?(strongSelf.contextSourceNode.bounds.size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.updateSearchTextHighlightState()
|
strongSelf.updateSearchTextHighlightState()
|
||||||
@ -2336,14 +2593,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addContentNode(node: ChatMessageBubbleContentNode) {
|
|
||||||
if let transitionClippingNode = self.transitionClippingNode {
|
|
||||||
transitionClippingNode.addSubnode(node)
|
|
||||||
} else {
|
|
||||||
self.contextSourceNode.contentNode.addSubnode(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func enableTransitionClippingNode() {
|
private func enableTransitionClippingNode() {
|
||||||
if self.transitionClippingNode == nil {
|
if self.transitionClippingNode == nil {
|
||||||
let node = ASDisplayNode()
|
let node = ASDisplayNode()
|
||||||
@ -2361,7 +2610,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
for contentNode in self.contentNodes {
|
for contentNode in self.contentNodes {
|
||||||
node.addSubnode(contentNode)
|
node.addSubnode(contentNode)
|
||||||
}
|
}
|
||||||
self.contextSourceNode.contentNode.addSubnode(node)
|
self.mainContextSourceNode.contentNode.addSubnode(node)
|
||||||
self.transitionClippingNode = node
|
self.transitionClippingNode = node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2369,13 +2618,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
private func disableTransitionClippingNode() {
|
private func disableTransitionClippingNode() {
|
||||||
if let transitionClippingNode = self.transitionClippingNode {
|
if let transitionClippingNode = self.transitionClippingNode {
|
||||||
if let forwardInfoNode = self.forwardInfoNode {
|
if let forwardInfoNode = self.forwardInfoNode {
|
||||||
self.contextSourceNode.contentNode.addSubnode(forwardInfoNode)
|
self.mainContextSourceNode.contentNode.addSubnode(forwardInfoNode)
|
||||||
}
|
}
|
||||||
if let replyInfoNode = self.replyInfoNode {
|
if let replyInfoNode = self.replyInfoNode {
|
||||||
self.contextSourceNode.contentNode.addSubnode(replyInfoNode)
|
self.mainContextSourceNode.contentNode.addSubnode(replyInfoNode)
|
||||||
}
|
}
|
||||||
for contentNode in self.contentNodes {
|
for contentNode in self.contentNodes {
|
||||||
self.contextSourceNode.contentNode.addSubnode(contentNode)
|
self.mainContextSourceNode.contentNode.addSubnode(contentNode)
|
||||||
}
|
}
|
||||||
transitionClippingNode.removeFromSupernode()
|
transitionClippingNode.removeFromSupernode()
|
||||||
self.transitionClippingNode = nil
|
self.transitionClippingNode = nil
|
||||||
@ -2408,9 +2657,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
self.contextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0)
|
self.mainContextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0)
|
||||||
self.containerNode.targetNodeForActivationProgressContentRect = self.contextSourceNode.contentRect
|
self.mainContainerNode.targetNodeForActivationProgressContentRect = self.mainContextSourceNode.contentRect
|
||||||
if !self.contextSourceNode.isExtractedToContextPreview {
|
if !self.mainContextSourceNode.isExtractedToContextPreview {
|
||||||
if let (rect, size) = self.absoluteRect {
|
if let (rect, size) = self.absoluteRect {
|
||||||
self.updateAbsoluteRect(rect, within: size)
|
self.updateAbsoluteRect(rect, within: size)
|
||||||
}
|
}
|
||||||
@ -2446,7 +2695,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||||
if let action = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil) {
|
if let action = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil) {
|
||||||
if case .doubleTap = gesture {
|
if case .doubleTap = gesture {
|
||||||
self.containerNode.cancelGesture()
|
self.mainContainerNode.cancelGesture()
|
||||||
}
|
}
|
||||||
switch action {
|
switch action {
|
||||||
case let .action(f):
|
case let .action(f):
|
||||||
@ -2602,7 +2851,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
loop: for contentNode in self.contentNodes {
|
loop: for contentNode in self.contentNodes {
|
||||||
let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture, isEstimating: false)
|
let convertedLocation = self.convert(location, to: contentNode)
|
||||||
|
|
||||||
|
let tapAction = contentNode.tapActionAtPoint(convertedLocation, gesture: gesture, isEstimating: false)
|
||||||
switch tapAction {
|
switch tapAction {
|
||||||
case .none:
|
case .none:
|
||||||
if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage {
|
if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage {
|
||||||
@ -2709,13 +2960,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
var tapMessage: Message? = item.content.firstMessage
|
var tapMessage: Message? = item.content.firstMessage
|
||||||
var selectAll = true
|
var selectAll = true
|
||||||
loop: for contentNode in self.contentNodes {
|
loop: for contentNode in self.contentNodes {
|
||||||
if !contentNode.frame.contains(location) {
|
let convertedLocation = self.convert(location, to: contentNode)
|
||||||
|
|
||||||
|
let convertedNodeFrame = contentNode.convert(contentNode.bounds, to: self)
|
||||||
|
if !convertedNodeFrame.contains(location) {
|
||||||
continue loop
|
continue loop
|
||||||
} else if contentNode is ChatMessageMediaBubbleContentNode {
|
} else if contentNode is ChatMessageMediaBubbleContentNode {
|
||||||
selectAll = false
|
selectAll = false
|
||||||
|
} else if contentNode is ChatMessageFileBubbleContentNode {
|
||||||
|
selectAll = false
|
||||||
}
|
}
|
||||||
tapMessage = contentNode.item?.message
|
tapMessage = contentNode.item?.message
|
||||||
let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture, isEstimating: false)
|
let tapAction = contentNode.tapActionAtPoint(convertedLocation, gesture: gesture, isEstimating: false)
|
||||||
switch tapAction {
|
switch tapAction {
|
||||||
case .none, .ignore:
|
case .none, .ignore:
|
||||||
break
|
break
|
||||||
@ -2807,7 +3063,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.contextSourceNode.isExtractedToContextPreview {
|
if self.mainContextSourceNode.isExtractedToContextPreview {
|
||||||
if let result = super.hitTest(point, with: event) as? TextSelectionNodeView {
|
if let result = super.hitTest(point, with: event) as? TextSelectionNodeView {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -3090,7 +3346,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners)
|
||||||
|
|
||||||
let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper
|
let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper
|
||||||
self.backgroundNode.setType(type: backgroundType, highlighted: highlighted, graphics: graphics, maskMode: self.contextSourceNode.isExtractedToContextPreview, hasWallpaper: hasWallpaper, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
|
self.backgroundNode.setType(type: backgroundType, highlighted: highlighted, graphics: graphics, maskMode: self.mainContextSourceNode.isExtractedToContextPreview, hasWallpaper: hasWallpaper, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3196,7 +3452,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
|
|
||||||
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||||
self.absoluteRect = (rect, containerSize)
|
self.absoluteRect = (rect, containerSize)
|
||||||
if !self.contextSourceNode.isExtractedToContextPreview {
|
if !self.mainContextSourceNode.isExtractedToContextPreview {
|
||||||
var rect = rect
|
var rect = rect
|
||||||
rect.origin.y = containerSize.height - rect.maxY + self.insets.top
|
rect.origin.y = containerSize.height - rect.maxY + self.insets.top
|
||||||
self.updateAbsoluteRectInternal(rect, within: containerSize)
|
self.updateAbsoluteRectInternal(rect, within: containerSize)
|
||||||
@ -3209,7 +3465,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func applyAbsoluteOffset(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
override func applyAbsoluteOffset(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||||
if !self.contextSourceNode.isExtractedToContextPreview {
|
if !self.mainContextSourceNode.isExtractedToContextPreview {
|
||||||
self.applyAbsoluteOffsetInternal(value: -value, animationCurve: animationCurve, duration: duration)
|
self.applyAbsoluteOffsetInternal(value: -value, animationCurve: animationCurve, duration: duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3222,12 +3478,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
self.backgroundWallpaperNode.offsetSpring(value: value, duration: duration, damping: damping)
|
self.backgroundWallpaperNode.offsetSpring(value: value, duration: duration, damping: damping)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func getMessageContextSourceNode() -> ContextExtractedContentContainingNode? {
|
override func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
|
||||||
return self.contextSourceNode
|
return self.contentContainers.first(where: { $0.contentMessageStableId == stableId })?.sourceNode ?? self.mainContextSourceNode
|
||||||
}
|
}
|
||||||
|
|
||||||
override func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) {
|
override func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) {
|
||||||
self.contextSourceNode.contentNode.addSubnode(accessoryItemNode)
|
self.mainContextSourceNode.contentNode.addSubnode(accessoryItemNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func targetReactionNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
|
override func targetReactionNode(value: String) -> (ASDisplayNode, ASDisplayNode)? {
|
||||||
@ -3242,7 +3498,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
private var backgroundMaskMode: Bool {
|
private var backgroundMaskMode: Bool {
|
||||||
let hasWallpaper = self.item?.presentationData.theme.wallpaper.hasWallpaper ?? false
|
let hasWallpaper = self.item?.presentationData.theme.wallpaper.hasWallpaper ?? false
|
||||||
let isPreview = self.item?.presentationData.isPreview ?? false
|
let isPreview = self.item?.presentationData.isPreview ?? false
|
||||||
return self.contextSourceNode.isExtractedToContextPreview || hasWallpaper || isPreview
|
return self.mainContextSourceNode.isExtractedToContextPreview || hasWallpaper || isPreview
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateQuizInvalidOptionSelected() {
|
func animateQuizInvalidOptionSelected() {
|
||||||
|
@ -10,10 +10,12 @@ final class ChatMessageContextExtractedContentSource: ContextExtractedContentSou
|
|||||||
|
|
||||||
private weak var chatNode: ChatControllerNode?
|
private weak var chatNode: ChatControllerNode?
|
||||||
private let message: Message
|
private let message: Message
|
||||||
|
private let selectAll: Bool
|
||||||
|
|
||||||
init(chatNode: ChatControllerNode, message: Message) {
|
init(chatNode: ChatControllerNode, message: Message, selectAll: Bool) {
|
||||||
self.chatNode = chatNode
|
self.chatNode = chatNode
|
||||||
self.message = message
|
self.message = message
|
||||||
|
self.selectAll = selectAll
|
||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
func takeView() -> ContextControllerTakeViewInfo? {
|
||||||
@ -29,7 +31,7 @@ final class ChatMessageContextExtractedContentSource: ContextExtractedContentSou
|
|||||||
guard let item = itemNode.item else {
|
guard let item = itemNode.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode() {
|
if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode(stableId: self.selectAll ? nil : self.message.stableId) {
|
||||||
result = ContextControllerTakeViewInfo(contentContainingNode: contentNode, contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
result = ContextControllerTakeViewInfo(contentContainingNode: contentNode, contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,11 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
if case let .linear(_, bottom) = position {
|
if case let .linear(_, bottom) = position {
|
||||||
if case .Neighbour(_, _, .condensed) = bottom {
|
if case .Neighbour(_, _, .condensed) = bottom {
|
||||||
bottomInset -= 24.0
|
if selectedFile?.isMusic ?? false {
|
||||||
|
bottomInset -= 14.0
|
||||||
|
} else {
|
||||||
|
bottomInset -= 10.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,7 +944,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||||||
return self.interactiveVideoNode.playMediaWithSound()
|
return self.interactiveVideoNode.playMediaWithSound()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func getMessageContextSourceNode() -> ContextExtractedContentContainingNode? {
|
override func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
|
||||||
return self.contextSourceNode
|
return self.contextSourceNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import RadialStatusNode
|
|||||||
import SemanticStatusNode
|
import SemanticStatusNode
|
||||||
import FileMediaResourceStatus
|
import FileMediaResourceStatus
|
||||||
import CheckNode
|
import CheckNode
|
||||||
|
import MusicAlbumArtResources
|
||||||
|
|
||||||
private struct FetchControls {
|
private struct FetchControls {
|
||||||
let fetch: () -> Void
|
let fetch: () -> Void
|
||||||
@ -22,7 +23,6 @@ private struct FetchControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||||
private var selectionBackgroundNode: ASDisplayNode?
|
|
||||||
private var selectionNode: FileMessageSelectionNode?
|
private var selectionNode: FileMessageSelectionNode?
|
||||||
private var cutoutNode: ASDisplayNode?
|
private var cutoutNode: ASDisplayNode?
|
||||||
|
|
||||||
@ -488,7 +488,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
controlAreaWidth = 86.0
|
controlAreaWidth = 86.0
|
||||||
} else {
|
} else {
|
||||||
progressFrame = CGRect(
|
progressFrame = CGRect(
|
||||||
origin: CGPoint(x: 3.0, y: -3.0),
|
origin: CGPoint(x: 3.0, y: isVoice ? -3.0 : 0.0),
|
||||||
size: CGSize(width: progressDiameter, height: progressDiameter)
|
size: CGSize(width: progressDiameter, height: progressDiameter)
|
||||||
)
|
)
|
||||||
controlAreaWidth = progressFrame.maxX + 8.0
|
controlAreaWidth = progressFrame.maxX + 8.0
|
||||||
@ -519,7 +519,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
fittedLayoutSize = CGSize(width: minLayoutWidth, height: 38.0)
|
fittedLayoutSize = CGSize(width: minLayoutWidth, height: 38.0)
|
||||||
} else {
|
} else {
|
||||||
let unionSize = titleFrame.union(descriptionFrame).union(progressFrame).size
|
let unionSize = titleFrame.union(descriptionFrame).union(progressFrame).size
|
||||||
fittedLayoutSize = CGSize(width: unionSize.width, height: unionSize.height + 6.0)
|
fittedLayoutSize = CGSize(width: unionSize.width, height: unionSize.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
var statusFrame: CGRect?
|
var statusFrame: CGRect?
|
||||||
@ -540,7 +540,6 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
|
|
||||||
if isAudio && !isVoice {
|
if isAudio && !isVoice {
|
||||||
streamingCacheStatusFrame = CGRect(origin: CGPoint(x: progressFrame.maxX - streamingProgressDiameter + 2.0, y: progressFrame.maxY - streamingProgressDiameter + 2.0), size: CGSize(width: streamingProgressDiameter, height: streamingProgressDiameter))
|
streamingCacheStatusFrame = CGRect(origin: CGPoint(x: progressFrame.maxX - streamingProgressDiameter + 2.0, y: progressFrame.maxY - streamingProgressDiameter + 2.0), size: CGSize(width: streamingProgressDiameter, height: streamingProgressDiameter))
|
||||||
fittedLayoutSize.width = max(fittedLayoutSize.width, boundingWidth + 2.0)
|
|
||||||
} else {
|
} else {
|
||||||
streamingCacheStatusFrame = CGRect()
|
streamingCacheStatusFrame = CGRect()
|
||||||
}
|
}
|
||||||
@ -701,7 +700,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
selectionNode.frame = selectionFrame
|
selectionNode.frame = selectionFrame
|
||||||
selectionNode.updateSelected(selection, animated: isAnimated)
|
selectionNode.updateSelected(selection, animated: isAnimated)
|
||||||
} else {
|
} else {
|
||||||
let selectionNode = FileMessageSelectionNode(theme: presentationData.theme.theme, incoming: incoming, toggle: { [weak self] value in
|
let selectionNode = FileMessageSelectionNode(theme: presentationData.theme.theme, incoming: incoming, isMusic: file.isMusic, toggle: { [weak self] value in
|
||||||
self?.toggleSelection(value)
|
self?.toggleSelection(value)
|
||||||
})
|
})
|
||||||
strongSelf.selectionNode = selectionNode
|
strongSelf.selectionNode = selectionNode
|
||||||
@ -712,19 +711,6 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
selectionNode.animateIn()
|
selectionNode.animateIn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectionBackgroundFrame = CGRect(origin: CGPoint(x: -8.0, y: -9.0), size: CGSize(width: fittedLayoutSize.width + 16.0, height: fittedLayoutSize.height + 6.0))
|
|
||||||
if let selectionBackgroundNode = strongSelf.selectionBackgroundNode {
|
|
||||||
selectionBackgroundNode.frame = selectionBackgroundFrame
|
|
||||||
selectionBackgroundNode.isHidden = !selection
|
|
||||||
} else {
|
|
||||||
let selectionBackgroundNode = ASDisplayNode()
|
|
||||||
selectionBackgroundNode.backgroundColor = messageTheme.accentControlColor.withAlphaComponent(0.08)
|
|
||||||
selectionBackgroundNode.frame = selectionBackgroundFrame
|
|
||||||
selectionBackgroundNode.isHidden = !selection
|
|
||||||
strongSelf.selectionBackgroundNode = selectionBackgroundNode
|
|
||||||
strongSelf.insertSubnode(selectionBackgroundNode, at: 0)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if let streamingStatusNode = strongSelf.streamingStatusNode {
|
if let streamingStatusNode = strongSelf.streamingStatusNode {
|
||||||
transition.updateAlpha(node: streamingStatusNode, alpha: 1.0)
|
transition.updateAlpha(node: streamingStatusNode, alpha: 1.0)
|
||||||
@ -740,10 +726,6 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
selectionNode.removeFromSupernode()
|
selectionNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let selectionBackgroundNode = strongSelf.selectionBackgroundNode {
|
|
||||||
strongSelf.selectionBackgroundNode = nil
|
|
||||||
selectionBackgroundNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.updateStatus(animated: isAnimated)
|
strongSelf.updateStatus(animated: isAnimated)
|
||||||
@ -834,20 +816,6 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAudio && !isVoice && !isSending {
|
|
||||||
switch resourceStatus.fetchStatus {
|
|
||||||
case let .Fetching(_, progress):
|
|
||||||
let adjustedProgress = max(progress, 0.027)
|
|
||||||
streamingState = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: .init(inset: 1.0, lineWidth: 2.0))
|
|
||||||
case .Local:
|
|
||||||
streamingState = .none
|
|
||||||
case .Remote:
|
|
||||||
streamingState = .download
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
streamingState = .none
|
|
||||||
}
|
|
||||||
|
|
||||||
switch resourceStatus.mediaStatus {
|
switch resourceStatus.mediaStatus {
|
||||||
case var .fetchStatus(fetchStatus):
|
case var .fetchStatus(fetchStatus):
|
||||||
if self.message?.forwardInfo != nil {
|
if self.message?.forwardInfo != nil {
|
||||||
@ -857,7 +825,16 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
switch fetchStatus {
|
switch fetchStatus {
|
||||||
case let .Fetching(_, progress):
|
case let .Fetching(_, progress):
|
||||||
let adjustedProgress = max(progress, 0.027)
|
let adjustedProgress = max(progress, 0.027)
|
||||||
state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil)
|
var wasCheck = false
|
||||||
|
if let statusNode = self.statusNode, case .check = statusNode.state {
|
||||||
|
wasCheck = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) {
|
||||||
|
state = .check(appearance: nil)
|
||||||
|
} else {
|
||||||
|
state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil)
|
||||||
|
}
|
||||||
case .Local:
|
case .Local:
|
||||||
if isAudio {
|
if isAudio {
|
||||||
state = .play
|
state = .play
|
||||||
@ -883,6 +860,20 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isAudio && !isVoice && !isSending && state != .pause {
|
||||||
|
switch resourceStatus.fetchStatus {
|
||||||
|
case let .Fetching(_, progress):
|
||||||
|
let adjustedProgress = max(progress, 0.027)
|
||||||
|
streamingState = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: .init(inset: 1.0, lineWidth: 2.0))
|
||||||
|
case .Local:
|
||||||
|
streamingState = .none
|
||||||
|
case .Remote:
|
||||||
|
streamingState = .download
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
streamingState = .none
|
||||||
|
}
|
||||||
|
|
||||||
let backgroundNodeColor: UIColor
|
let backgroundNodeColor: UIColor
|
||||||
let foregroundNodeColor: UIColor
|
let foregroundNodeColor: UIColor
|
||||||
if self.iconNode != nil {
|
if self.iconNode != nil {
|
||||||
@ -894,7 +885,22 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if state != .none && self.statusNode == nil {
|
if state != .none && self.statusNode == nil {
|
||||||
let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor)
|
var image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil
|
||||||
|
if file.isMusic {
|
||||||
|
var title: String?
|
||||||
|
var performer: String?
|
||||||
|
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .Audio(_, _, titleValue, performerValue, _) = attribute {
|
||||||
|
title = titleValue
|
||||||
|
performer = performerValue
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image = playerAlbumArt(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false)
|
||||||
|
}
|
||||||
|
let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor)
|
||||||
self.statusNode = statusNode
|
self.statusNode = statusNode
|
||||||
statusNode.frame = progressFrame
|
statusNode.frame = progressFrame
|
||||||
self.addSubnode(statusNode)
|
self.addSubnode(statusNode)
|
||||||
@ -1114,10 +1120,12 @@ final class FileMessageSelectionNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var selected = false
|
private var selected = false
|
||||||
private let checkNode: CheckNode
|
private let checkNode: CheckNode
|
||||||
|
private let isMusic: Bool
|
||||||
|
|
||||||
public init(theme: PresentationTheme, incoming: Bool, toggle: @escaping (Bool) -> Void) {
|
public init(theme: PresentationTheme, incoming: Bool, isMusic: Bool, toggle: @escaping (Bool) -> Void) {
|
||||||
|
self.isMusic = isMusic
|
||||||
self.toggle = toggle
|
self.toggle = toggle
|
||||||
self.checkNode = CheckNode(strokeColor: incoming ? theme.chat.message.incoming.mediaPlaceholderColor : theme.chat.message.outgoing.mediaPlaceholderColor, fillColor: theme.list.itemCheckColors.fillColor, foregroundColor: theme.list.itemCheckColors.foregroundColor, style: .compact)
|
self.checkNode = CheckNode(strokeColor: incoming ? theme.chat.message.incoming.mediaPlaceholderColor : theme.chat.message.outgoing.mediaPlaceholderColor, fillColor: theme.list.itemCheckColors.fillColor, foregroundColor: theme.list.itemCheckColors.foregroundColor, style: isMusic ? .compact : .overlay)
|
||||||
self.checkNode.isUserInteractionEnabled = false
|
self.checkNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -1160,7 +1168,13 @@ final class FileMessageSelectionNode: ASDisplayNode {
|
|||||||
super.layout()
|
super.layout()
|
||||||
|
|
||||||
let checkSize = CGSize(width: 30.0, height: 30.0)
|
let checkSize = CGSize(width: 30.0, height: 30.0)
|
||||||
self.checkNode.frame = CGRect(origin: CGPoint(x: 23.0, y: 17.0), size: checkSize)
|
let checkOrigin: CGPoint
|
||||||
|
if self.isMusic {
|
||||||
|
checkOrigin = CGPoint(x: 23.0, y: 20.0)
|
||||||
|
} else {
|
||||||
|
checkOrigin = CGPoint(x: 39.0, y: -5.0)
|
||||||
|
}
|
||||||
|
self.checkNode.frame = CGRect(origin: checkOrigin, size: checkSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +716,7 @@ public class ChatMessageItemView: ListViewItemNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMessageContextSourceNode() -> ContextExtractedContentContainingNode? {
|
func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1051,7 +1051,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func getMessageContextSourceNode() -> ContextExtractedContentContainingNode? {
|
override func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
|
||||||
return self.contextSourceNode
|
return self.contextSourceNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,25 +66,30 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||||||
case let .map(mapMedia):
|
case let .map(mapMedia):
|
||||||
params.dismissInput()
|
params.dismissInput()
|
||||||
|
|
||||||
// let controllerParams = LocationViewParams(sendLiveLocation: { location in
|
if mapMedia.liveBroadcastingTimeout == nil {
|
||||||
// let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: nil, localGroupingKey: nil)
|
let controllerParams = LocationViewParams(sendLiveLocation: { location in
|
||||||
// params.enqueueMessage(outMessage)
|
let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: nil, localGroupingKey: nil)
|
||||||
// }, stopLiveLocation: {
|
params.enqueueMessage(outMessage)
|
||||||
// params.context.liveLocationManager?.cancelLiveLocation(peerId: params.message.id.peerId)
|
}, stopLiveLocation: {
|
||||||
// }, openUrl: params.openUrl, openPeer: { peer in
|
params.context.liveLocationManager?.cancelLiveLocation(peerId: params.message.id.peerId)
|
||||||
// params.openPeer(peer, .info)
|
}, openUrl: params.openUrl, openPeer: { peer in
|
||||||
// })
|
params.openPeer(peer, .info)
|
||||||
// let controller = LocationViewController(context: params.context, mapMedia: mapMedia, params: controllerParams)
|
})
|
||||||
let controller = legacyLocationController(message: params.message, mapMedia: mapMedia, context: params.context, openPeer: { peer in
|
let controller = LocationViewController(context: params.context, mapMedia: mapMedia, params: controllerParams)
|
||||||
params.openPeer(peer, .info)
|
controller.navigationPresentation = .modal
|
||||||
}, sendLiveLocation: { coordinate, period in
|
params.navigationController?.pushViewController(controller)
|
||||||
let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, geoPlace: nil, venue: nil, liveBroadcastingTimeout: period)), replyToMessageId: nil, localGroupingKey: nil)
|
} else {
|
||||||
params.enqueueMessage(outMessage)
|
let controller = legacyLocationController(message: params.message, mapMedia: mapMedia, context: params.context, openPeer: { peer in
|
||||||
}, stopLiveLocation: {
|
params.openPeer(peer, .info)
|
||||||
params.context.liveLocationManager?.cancelLiveLocation(peerId: params.message.id.peerId)
|
}, sendLiveLocation: { coordinate, period in
|
||||||
}, openUrl: params.openUrl)
|
let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, geoPlace: nil, venue: nil, liveBroadcastingTimeout: period)), replyToMessageId: nil, localGroupingKey: nil)
|
||||||
controller.navigationPresentation = .modal
|
params.enqueueMessage(outMessage)
|
||||||
params.navigationController?.pushViewController(controller)
|
}, stopLiveLocation: {
|
||||||
|
params.context.liveLocationManager?.cancelLiveLocation(peerId: params.message.id.peerId)
|
||||||
|
}, openUrl: params.openUrl)
|
||||||
|
controller.navigationPresentation = .modal
|
||||||
|
params.navigationController?.pushViewController(controller)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
case let .stickerPack(reference):
|
case let .stickerPack(reference):
|
||||||
let controller = StickerPackScreen(context: params.context, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, actionPerformed: { info, items, action in
|
let controller = StickerPackScreen(context: params.context, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, actionPerformed: { info, items, action in
|
||||||
|
@ -22,8 +22,6 @@ private func commitOwnershipTransferController(context: AccountContext, present:
|
|||||||
var dismissImpl: (() -> Void)?
|
var dismissImpl: (() -> Void)?
|
||||||
var proceedImpl: (() -> Void)?
|
var proceedImpl: (() -> Void)?
|
||||||
|
|
||||||
var pushControllerImpl: ((ViewController) -> Void)?
|
|
||||||
|
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
@ -56,6 +54,7 @@ private func commitOwnershipTransferController(context: AccountContext, present:
|
|||||||
contentNode.updateIsChecking(true)
|
contentNode.updateIsChecking(true)
|
||||||
|
|
||||||
disposable.set((commit(contentNode.password) |> deliverOnMainQueue).start(next: { result in
|
disposable.set((commit(contentNode.password) |> deliverOnMainQueue).start(next: { result in
|
||||||
|
completion(result)
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
}, error: { [weak contentNode] error in
|
}, error: { [weak contentNode] error in
|
||||||
var errorTextAndActions: (String, [TextAlertAction])?
|
var errorTextAndActions: (String, [TextAlertAction])?
|
||||||
@ -78,10 +77,6 @@ private func commitOwnershipTransferController(context: AccountContext, present:
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pushControllerImpl = { [weak controller] c in
|
|
||||||
controller?.push(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1668,7 +1668,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
if strongSelf.searchDisplayController == nil {
|
if strongSelf.searchDisplayController == nil {
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuMore, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { c, _ in
|
||||||
c.dismiss(completion: {
|
c.dismiss(completion: {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.chatInterfaceInteraction.toggleMessagesSelection([message.id], true)
|
strongSelf.chatInterfaceInteraction.toggleMessagesSelection([message.id], true)
|
||||||
@ -1776,8 +1776,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuMore, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuSelect, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
@ -9,16 +9,81 @@ import AccountContext
|
|||||||
import StickerResources
|
import StickerResources
|
||||||
import ManagedAnimationNode
|
import ManagedAnimationNode
|
||||||
|
|
||||||
enum ReelValue {
|
private struct SlotMachineValue {
|
||||||
case rolling
|
enum ReelValue {
|
||||||
case bar
|
case rolling
|
||||||
case berries
|
case bar
|
||||||
case lemon
|
case berries
|
||||||
case seven
|
case lemon
|
||||||
case sevenWin
|
case seven
|
||||||
|
case sevenWin
|
||||||
|
|
||||||
|
var isResult: Bool {
|
||||||
|
if case .rolling = self {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let left: ReelValue
|
||||||
|
let center: ReelValue
|
||||||
|
let right: ReelValue
|
||||||
|
|
||||||
|
init(rawValue: Int32?) {
|
||||||
|
if let rawValue = rawValue, rawValue > 0 {
|
||||||
|
let rawValue = rawValue - 1
|
||||||
|
|
||||||
|
let leftRawValue = rawValue & 3
|
||||||
|
let centerRawValue = rawValue >> 2 & 3
|
||||||
|
let rightRawValue = rawValue >> 4
|
||||||
|
|
||||||
|
func reelValue(for rawValue: Int32) -> ReelValue {
|
||||||
|
switch rawValue {
|
||||||
|
case 0:
|
||||||
|
return .bar
|
||||||
|
case 1:
|
||||||
|
return .berries
|
||||||
|
case 2:
|
||||||
|
return .lemon
|
||||||
|
case 3:
|
||||||
|
return .seven
|
||||||
|
default:
|
||||||
|
return .rolling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftReelValue = reelValue(for: leftRawValue)
|
||||||
|
var centerReelValue = reelValue(for: centerRawValue)
|
||||||
|
var rightReelValue = reelValue(for: rightRawValue)
|
||||||
|
|
||||||
|
if leftReelValue == .seven && centerReelValue == .seven && rightReelValue == .seven {
|
||||||
|
leftReelValue = .sevenWin
|
||||||
|
centerReelValue = .sevenWin
|
||||||
|
rightReelValue = .sevenWin
|
||||||
|
}
|
||||||
|
|
||||||
|
self.left = leftReelValue
|
||||||
|
self.center = centerReelValue
|
||||||
|
self.right = rightReelValue
|
||||||
|
} else {
|
||||||
|
self.left = .rolling
|
||||||
|
self.center = .rolling
|
||||||
|
self.right = .rolling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isThreeOfSame: Bool {
|
||||||
|
return self.left == self.center && self.center == self.right && self.left.isResult
|
||||||
|
}
|
||||||
|
|
||||||
|
var is777: Bool {
|
||||||
|
return self.left == .sevenWin && self.center == .sevenWin && self.right == .sevenWin
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func leftReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
|
private func leftReelAnimationItem(value: SlotMachineValue.ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
|
||||||
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
||||||
switch value {
|
switch value {
|
||||||
case .rolling:
|
case .rolling:
|
||||||
@ -36,7 +101,7 @@ private func leftReelAnimationItem(value: ReelValue, immediate: Bool = false) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func centerReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
|
private func centerReelAnimationItem(value: SlotMachineValue.ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
|
||||||
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
||||||
switch value {
|
switch value {
|
||||||
case .rolling:
|
case .rolling:
|
||||||
@ -54,7 +119,7 @@ private func centerReelAnimationItem(value: ReelValue, immediate: Bool = false)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func rightReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
|
private func rightReelAnimationItem(value: SlotMachineValue.ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
|
||||||
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
||||||
switch value {
|
switch value {
|
||||||
case .rolling:
|
case .rolling:
|
||||||
@ -126,49 +191,17 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
|||||||
case .rolling:
|
case .rolling:
|
||||||
switch diceState {
|
switch diceState {
|
||||||
case let .value(value, _):
|
case let .value(value, _):
|
||||||
let l: ReelValue
|
let slotValue = SlotMachineValue(rawValue: value)
|
||||||
let c: ReelValue
|
if slotValue.isThreeOfSame {
|
||||||
let r: ReelValue
|
|
||||||
switch value {
|
|
||||||
case 1:
|
|
||||||
l = .seven
|
|
||||||
c = .berries
|
|
||||||
r = .bar
|
|
||||||
case 2:
|
|
||||||
l = .berries
|
|
||||||
c = .berries
|
|
||||||
r = .bar
|
|
||||||
case 3:
|
|
||||||
l = .seven
|
|
||||||
c = .berries
|
|
||||||
r = .seven
|
|
||||||
case 4:
|
|
||||||
l = .bar
|
|
||||||
c = .lemon
|
|
||||||
r = .seven
|
|
||||||
case 5:
|
|
||||||
l = .berries
|
|
||||||
c = .berries
|
|
||||||
r = .berries
|
|
||||||
case 6:
|
|
||||||
l = .sevenWin
|
|
||||||
c = .sevenWin
|
|
||||||
r = .sevenWin
|
|
||||||
default:
|
|
||||||
l = .sevenWin
|
|
||||||
c = .sevenWin
|
|
||||||
r = .sevenWin
|
|
||||||
}
|
|
||||||
if value == 6 {
|
|
||||||
Queue.mainQueue().after(1.5) {
|
Queue.mainQueue().after(1.5) {
|
||||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), loop: false))
|
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), loop: false))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
|
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
|
||||||
}
|
}
|
||||||
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: l))
|
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: slotValue.left))
|
||||||
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: c))
|
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: slotValue.center))
|
||||||
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: r))
|
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: slotValue.right))
|
||||||
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: .still(.end), loop: false))
|
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: .still(.end), loop: false))
|
||||||
case .rolling:
|
case .rolling:
|
||||||
break
|
break
|
||||||
@ -176,7 +209,7 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
|||||||
case .value:
|
case .value:
|
||||||
switch diceState {
|
switch diceState {
|
||||||
case .rolling:
|
case .rolling:
|
||||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
|
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
|
||||||
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
|
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
|
||||||
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
|
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
|
||||||
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
|
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
|
||||||
@ -188,49 +221,17 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
|
|||||||
} else {
|
} else {
|
||||||
switch diceState {
|
switch diceState {
|
||||||
case let .value(value, immediate):
|
case let .value(value, immediate):
|
||||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
|
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
|
||||||
|
|
||||||
let l: ReelValue
|
let slotValue = SlotMachineValue(rawValue: value)
|
||||||
let c: ReelValue
|
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: slotValue.left, immediate: immediate))
|
||||||
let r: ReelValue
|
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: slotValue.center, immediate: immediate))
|
||||||
switch value {
|
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: slotValue.right, immediate: immediate))
|
||||||
case 1:
|
|
||||||
l = .seven
|
|
||||||
c = .berries
|
|
||||||
r = .bar
|
|
||||||
case 2:
|
|
||||||
l = .berries
|
|
||||||
c = .berries
|
|
||||||
r = .bar
|
|
||||||
case 3:
|
|
||||||
l = .seven
|
|
||||||
c = .berries
|
|
||||||
r = .seven
|
|
||||||
case 4:
|
|
||||||
l = .bar
|
|
||||||
c = .lemon
|
|
||||||
r = .seven
|
|
||||||
case 5:
|
|
||||||
l = .berries
|
|
||||||
c = .berries
|
|
||||||
r = .berries
|
|
||||||
case 6:
|
|
||||||
l = .sevenWin
|
|
||||||
c = .sevenWin
|
|
||||||
r = .sevenWin
|
|
||||||
default:
|
|
||||||
l = .sevenWin
|
|
||||||
c = .sevenWin
|
|
||||||
r = .sevenWin
|
|
||||||
}
|
|
||||||
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: l, immediate: immediate))
|
|
||||||
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: c, immediate: immediate))
|
|
||||||
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: r, immediate: immediate))
|
|
||||||
|
|
||||||
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
|
||||||
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: frames, loop: false))
|
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: frames, loop: false))
|
||||||
case .rolling:
|
case .rolling:
|
||||||
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
|
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
|
||||||
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
|
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
|
||||||
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
|
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
|
||||||
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
|
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user