mirror of
https://github.com/danog/Telegram.git
synced 2024-11-30 04:29:15 +01:00
1650 lines
63 KiB
Objective-C
1650 lines
63 KiB
Objective-C
#import "TGDialogListCell.h"
|
|
|
|
#import "TGDateUtils.h"
|
|
#import "TGStringUtils.h"
|
|
|
|
#import "TGReusableLabel.h"
|
|
#import "TGLabel.h"
|
|
#import "TGLetteredAvatarView.h"
|
|
#import "TGImageUtils.h"
|
|
|
|
#import "TGMessage.h"
|
|
#import "TGUser.h"
|
|
|
|
#import "TGDateLabel.h"
|
|
|
|
#import "TGViewController.h"
|
|
|
|
#import "TGFont.h"
|
|
#import "TGTimerTarget.h"
|
|
|
|
#import "TGPeerIdAdapter.h"
|
|
|
|
#import "TGTelegraph.h"
|
|
|
|
static UIImage *deliveredCheckmark()
|
|
{
|
|
static UIImage *image = nil;
|
|
if (image == nil)
|
|
{
|
|
image = [UIImage imageNamed:@"ModernConversationListIconDelivered.png"];
|
|
}
|
|
return image;
|
|
}
|
|
|
|
static UIImage *readCheckmark()
|
|
{
|
|
static UIImage *image = nil;
|
|
if (image == nil)
|
|
{
|
|
image = [UIImage imageNamed:@"ModernConversationListIconRead.png"];
|
|
}
|
|
return image;
|
|
}
|
|
|
|
static UIColor *normalTextColor = nil;
|
|
static UIColor *actionTextColor = nil;
|
|
static UIColor *mediaTextColor = nil;
|
|
|
|
@interface TGDialogListTextView : UIView
|
|
{
|
|
}
|
|
|
|
@property (nonatomic, strong) NSString *title;
|
|
@property (nonatomic) CGRect titleFrame;
|
|
@property (nonatomic, strong) UIFont *titleFont;
|
|
@property (nonatomic, strong) UIImage *mediaIcon;
|
|
@property (nonatomic, strong) NSString *text;
|
|
@property (nonatomic, strong) UIColor *textColor;
|
|
@property (nonatomic) CGRect textFrame;
|
|
@property (nonatomic, strong) UIFont *textFont;
|
|
@property (nonatomic , strong) NSString *authorName;
|
|
@property (nonatomic) CGRect authorNameFrame;
|
|
@property (nonatomic, strong) UIFont *authorNameFont;
|
|
|
|
@property (nonatomic) CGRect typingFrame;
|
|
@property (nonatomic) bool showTyping;
|
|
@property (nonatomic, strong) NSString *typingText;
|
|
|
|
@property (nonatomic) bool isMultichat;
|
|
@property (nonatomic) bool isEncrypted;
|
|
@property (nonatomic) bool isMuted;
|
|
@property (nonatomic) bool isVerified;
|
|
|
|
@end
|
|
|
|
@implementation TGDialogListTextView
|
|
|
|
- (void)drawRect:(CGRect)rect
|
|
{
|
|
static UIColor *nTitleColor = nil;
|
|
static CGColorRef titleColor = nil;
|
|
static UIColor *nEncryptedTitleColor = nil;
|
|
static CGColorRef encryptedTitleColor = nil;
|
|
static CGColorRef authorNameColor = nil;
|
|
static UIColor *nAuthorNameColor = nil;
|
|
if (titleColor == nil)
|
|
{
|
|
nTitleColor = [UIColor blackColor];
|
|
titleColor = CGColorRetain([nTitleColor CGColor]);
|
|
|
|
nEncryptedTitleColor = UIColorRGB(0x00a629);
|
|
encryptedTitleColor = CGColorRetain([nEncryptedTitleColor CGColor]);
|
|
|
|
nAuthorNameColor = [UIColor blackColor];
|
|
authorNameColor = CGColorRetain([nAuthorNameColor CGColor]);
|
|
}
|
|
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGRect frame = self.frame;
|
|
CGRect titleFrame = CGRectOffset(_titleFrame, -frame.origin.x, -frame.origin.y);
|
|
CGRect textFrame = CGRectOffset(_textFrame, -frame.origin.x, -frame.origin.y);
|
|
CGRect authorNameFrame = CGRectOffset(_authorNameFrame, -frame.origin.x, -frame.origin.y);
|
|
CGRect typingFrame = CGRectOffset(_typingFrame, -frame.origin.x, -frame.origin.y);
|
|
|
|
if (_isEncrypted)
|
|
{
|
|
UIImage *image = nil;
|
|
|
|
static UIImage *multichatImage = nil;
|
|
if (multichatImage == nil)
|
|
multichatImage = [UIImage imageNamed:@"ModernConversationListIconLock.png"];
|
|
image = multichatImage;
|
|
|
|
[image drawAtPoint:CGPointMake(1.0f, 6.0f) blendMode:kCGBlendModeNormal alpha:1.0f];
|
|
}
|
|
else if (false && _isMultichat)
|
|
{
|
|
UIImage *image = nil;
|
|
|
|
static UIImage *multichatImage = nil;
|
|
if (multichatImage == nil)
|
|
multichatImage = [UIImage imageNamed:@"DialogListGroupChatIcon.png"];
|
|
image = multichatImage;
|
|
|
|
[image drawAtPoint:CGPointMake(1, 6.0) blendMode:kCGBlendModeNormal alpha:1.0f];
|
|
}
|
|
|
|
CGContextSetFillColorWithColor(context, _isEncrypted ? encryptedTitleColor : titleColor);
|
|
if (CGRectIntersectsRect(rect, titleFrame))
|
|
{
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
|
style.lineBreakMode = NSLineBreakByTruncatingTail;
|
|
style.alignment = NSTextAlignmentLeft;
|
|
|
|
NSDictionary *attributes = @{
|
|
NSParagraphStyleAttributeName: style,
|
|
NSFontAttributeName: _titleFont,
|
|
NSForegroundColorAttributeName:_isEncrypted ? nEncryptedTitleColor : nTitleColor
|
|
};
|
|
|
|
[_title drawWithRect:titleFrame options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
|
|
}
|
|
else
|
|
{
|
|
CGSize titleSize = [_title sizeWithFont:_titleFont];
|
|
[_title drawInRect:CGRectMake(titleFrame.origin.x, titleFrame.origin.y, MIN(titleSize.width, titleFrame.size.width), titleFrame.size.height) withFont:_titleFont lineBreakMode:NSLineBreakByTruncatingTail alignment:NSTextAlignmentLeft];
|
|
}
|
|
}
|
|
|
|
if (_showTyping)
|
|
{
|
|
CGContextSetFillColorWithColor(context, actionTextColor.CGColor);
|
|
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
static NSDictionary *attributes = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
|
style.lineBreakMode = NSLineBreakByClipping;
|
|
style.alignment = NSTextAlignmentLeft;
|
|
|
|
attributes = @{
|
|
NSParagraphStyleAttributeName: style,
|
|
NSFontAttributeName: _textFont,
|
|
NSForegroundColorAttributeName: _textColor
|
|
};
|
|
});
|
|
|
|
[_typingText drawWithRect:typingFrame options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
|
|
}
|
|
else
|
|
{
|
|
CGSize typingSize = [_typingText sizeWithFont:_textFont];
|
|
[_typingText drawInRect:CGRectMake(typingFrame.origin.x, typingFrame.origin.y, MIN(typingSize.width, typingFrame.size.width), typingFrame.size.height) withFont:_textFont lineBreakMode:NSLineBreakByClipping];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CGRectIntersectsRect(rect, textFrame))
|
|
{
|
|
CGContextSetFillColorWithColor(context, _textColor.CGColor);
|
|
|
|
if (_mediaIcon != nil)
|
|
{
|
|
[_mediaIcon drawAtPoint:CGPointMake(textFrame.origin.x, textFrame.origin.y + 1.5f) blendMode:kCGBlendModeNormal alpha:1.0f];
|
|
textFrame = CGRectMake(textFrame.origin.x + 19, textFrame.origin.y, textFrame.size.width - 19, textFrame.size.height);
|
|
}
|
|
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
static NSDictionary *attributes = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
|
style.lineSpacing = 1 + TGRetinaPixel;
|
|
style.lineBreakMode = NSLineBreakByWordWrapping;
|
|
style.alignment = NSTextAlignmentLeft;
|
|
|
|
attributes = @{
|
|
NSParagraphStyleAttributeName: style,
|
|
NSFontAttributeName: _textFont,
|
|
NSForegroundColorAttributeName: _textColor
|
|
};
|
|
});
|
|
|
|
[_text drawWithRect:textFrame options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine attributes:attributes context:nil];
|
|
}
|
|
else
|
|
{
|
|
[_text drawInRect:textFrame withFont:_textFont lineBreakMode:NSLineBreakByTruncatingTail alignment:NSTextAlignmentLeft];
|
|
}
|
|
|
|
//CGContextFillRect(context, textFrame);
|
|
}
|
|
|
|
if (_authorName != nil && _authorName.length != 0)
|
|
{
|
|
CGContextSetFillColorWithColor(context, authorNameColor);
|
|
if (CGRectIntersectsRect(rect, authorNameFrame))
|
|
{
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
static NSDictionary *attributes = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
|
style.lineBreakMode = NSLineBreakByTruncatingTail;
|
|
style.alignment = NSTextAlignmentLeft;
|
|
|
|
attributes = @{
|
|
NSParagraphStyleAttributeName: style,
|
|
NSFontAttributeName: _authorNameFont,
|
|
NSForegroundColorAttributeName: nAuthorNameColor
|
|
};
|
|
});
|
|
|
|
[_authorName drawWithRect:authorNameFrame options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
|
|
}
|
|
else
|
|
{
|
|
CGSize authorNameSize = [_authorName sizeWithFont:_authorNameFont];
|
|
[_authorName drawInRect:CGRectMake(authorNameFrame.origin.x, authorNameFrame.origin.y, MIN(authorNameSize.width, authorNameFrame.size.width), authorNameFrame.size.height) withFont:_authorNameFont lineBreakMode:NSLineBreakByTruncatingTail alignment:NSTextAlignmentRight];
|
|
}
|
|
|
|
//CGContextFillRect(context, authorNameFrame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark - Cell
|
|
|
|
@interface TGDialogListCell ()
|
|
{
|
|
CALayer *_separatorLayer;
|
|
UIImageView *_avatarIconView;
|
|
}
|
|
|
|
@property (nonatomic, strong) UIView *wrapView;
|
|
|
|
@property (nonatomic, strong) TGDialogListTextView *textView;
|
|
|
|
@property (nonatomic, strong) TGLetteredAvatarView *avatarView;
|
|
@property (nonatomic, strong) UIImageView *authorAvatarStrokeView;
|
|
|
|
@property (nonatomic, strong) TGDateLabel *dateLabel;
|
|
|
|
@property (nonatomic, strong) UIImageView *unreadCountBackgrond;
|
|
@property (nonatomic, strong) TGLabel *unreadCountLabel;
|
|
|
|
@property (nonatomic, strong) UIImageView *deliveryErrorBackgrond;
|
|
|
|
@property (nonatomic, strong) UIImageView *deliveredCheckmark;
|
|
@property (nonatomic, strong) UIImageView *readCheckmark;
|
|
@property (nonatomic, strong) UIImageView *pendingIndicator;
|
|
|
|
@property (nonatomic, strong) NSString *dateString;
|
|
|
|
@property (nonatomic) int validViews;
|
|
@property (nonatomic) CGSize validSize;
|
|
|
|
@property (nonatomic) bool hideAuthorName;
|
|
|
|
@property (nonatomic) bool editingIsActive;
|
|
|
|
@property (nonatomic, strong) UIImage *mediaIcon;
|
|
|
|
@property (nonatomic, strong) UIColor *messageTextColor;
|
|
|
|
//@property (nonatomic, strong) UIImageView *arrowView;
|
|
|
|
@property (nonatomic, strong) UIImageView *muteIcon;
|
|
@property (nonatomic, strong) UIImageView *verifiedIcon;
|
|
|
|
@property (nonatomic, strong) UIView *typingDotsContainer;
|
|
@property (nonatomic) bool animatingTyping;
|
|
@property (nonatomic, strong) NSTimer *typingDotsTimer;
|
|
@property (nonatomic) int typingDotsAnimationStep;
|
|
|
|
@end
|
|
|
|
@implementation TGDialogListCell
|
|
|
|
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier assetsSource:(id<TGDialogListCellAssetsSource>)assetsSource
|
|
{
|
|
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
|
if (self)
|
|
{
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
self.contentView.superview.clipsToBounds = false;
|
|
}
|
|
|
|
_separatorLayer = [[CALayer alloc] init];
|
|
_separatorLayer.backgroundColor = TGSeparatorColor().CGColor;
|
|
[self.layer addSublayer:_separatorLayer];
|
|
|
|
_wrapView = [[UIView alloc] init];
|
|
_wrapView.clipsToBounds = true;
|
|
[self addSubview:_wrapView];
|
|
|
|
UIView *selectedView = [[UIView alloc] init];
|
|
selectedView.backgroundColor = TGSelectionColor();
|
|
self.selectedBackgroundView = selectedView;
|
|
|
|
_assetsSource = assetsSource;
|
|
|
|
_textView = [[TGDialogListTextView alloc] initWithFrame:CGRectMake(73, 2, self.frame.size.width - 73, 46)];
|
|
_textView.contentMode = UIViewContentModeLeft;
|
|
_textView.titleFont = TGMediumSystemFontOfSize(16);
|
|
_textView.textFont = TGSystemFontOfSize(15);
|
|
_textView.authorNameFont = TGSystemFontOfSize(15);
|
|
_textView.opaque = true;
|
|
_textView.backgroundColor = [UIColor whiteColor];
|
|
|
|
[_wrapView addSubview:_textView];
|
|
|
|
_dateString = [[NSMutableString alloc] initWithCapacity:16];
|
|
|
|
CGFloat dateFontSize = 14.0f;
|
|
CGFloat amWidth = 24.0f;
|
|
if (TGIsPad())
|
|
{
|
|
dateFontSize = 15.0f;
|
|
amWidth = 25.0f;
|
|
}
|
|
|
|
_dateLabel = [[TGDateLabel alloc] init];
|
|
_dateLabel.amWidth = amWidth;
|
|
_dateLabel.pmWidth = amWidth;
|
|
_dateLabel.dstOffset = 0.0f;
|
|
_dateLabel.dateFont = TGSystemFontOfSize(dateFontSize);
|
|
_dateLabel.dateTextFont = TGSystemFontOfSize(dateFontSize);
|
|
_dateLabel.dateLabelFont = TGSystemFontOfSize(dateFontSize);
|
|
_dateLabel.textColor = UIColorRGB(0x969699);
|
|
_dateLabel.backgroundColor = [UIColor clearColor];
|
|
_dateLabel.opaque = false;
|
|
|
|
#if !TGTEST
|
|
[_wrapView addSubview:_dateLabel];
|
|
#endif
|
|
|
|
bool fadeTransition = cpuCoreCount() > 1;
|
|
|
|
_avatarView = [[TGLetteredAvatarView alloc] initWithFrame:CGRectMake(10, 7 - TGRetinaPixel, 62 + TGRetinaPixel, 62 + TGRetinaPixel)];
|
|
[_avatarView setSingleFontSize:35.0f doubleFontSize:21.0f useBoldFont:false];
|
|
_avatarView.fadeTransition = fadeTransition;
|
|
[_wrapView addSubview:_avatarView];
|
|
|
|
_avatarIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"BroadcastLargeAvatarIcon.png"]];
|
|
_avatarIconView.frame = (CGRect){{23.0f, 23.0f}, _avatarIconView.frame.size};
|
|
_avatarIconView.hidden = true;
|
|
[_wrapView addSubview:_avatarIconView];
|
|
|
|
static UIImage *unreadBackground = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(20.0f, 20.0f), false, 0.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGContextSetFillColorWithColor(context, UIColorRGB(0x0f94f3).CGColor);
|
|
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 20.0f, 20.0f));
|
|
|
|
unreadBackground = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:10.0f topCapHeight:0.0f];
|
|
UIGraphicsEndImageContext();
|
|
});
|
|
|
|
_unreadCountBackgrond = [[UIImageView alloc] initWithImage:unreadBackground];
|
|
|
|
[_wrapView addSubview:_unreadCountBackgrond];
|
|
|
|
_unreadCountLabel = [[TGLabel alloc] initWithFrame:CGRectMake(0, 0, 50, 20)];
|
|
_unreadCountLabel.textColor = [UIColor whiteColor];
|
|
_unreadCountLabel.font = TGSystemFontOfSize(14);
|
|
|
|
[_wrapView addSubview:_unreadCountLabel];
|
|
|
|
_unreadCountLabel.backgroundColor = [UIColor clearColor];
|
|
|
|
//_arrowView = [[UIImageView alloc] initWithImage:arrowImage()];
|
|
//[self addSubview:_arrowView];
|
|
|
|
_deliveredCheckmark = [[UIImageView alloc] initWithImage:deliveredCheckmark()];
|
|
|
|
_readCheckmark = [[UIImageView alloc] initWithImage:readCheckmark()];
|
|
|
|
[_wrapView addSubview:_readCheckmark];
|
|
[_wrapView addSubview:_deliveredCheckmark];
|
|
|
|
_validSize = CGSizeZero;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_avatarView cancelLoading];
|
|
|
|
if (_typingDotsTimer != nil)
|
|
{
|
|
[_typingDotsTimer invalidate];
|
|
_typingDotsTimer = nil;
|
|
}
|
|
}
|
|
|
|
- (void)prepareForReuse
|
|
{
|
|
[self stopTypingAnimation];
|
|
|
|
[super prepareForReuse];
|
|
}
|
|
|
|
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
|
|
{
|
|
bool wasSelected = self.selected;
|
|
|
|
[super setSelected:selected animated:animated];
|
|
|
|
if ((selected && !wasSelected))
|
|
{
|
|
[self adjustOrdering];
|
|
}
|
|
|
|
if ((selected && !wasSelected) || (!selected && wasSelected))
|
|
{
|
|
UIView *selectedView = self.selectedBackgroundView;
|
|
if (selectedView != nil && (self.selected || self.highlighted))
|
|
{
|
|
CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f;
|
|
selectedView.frame = CGRectMake(0, -separatorHeight, selectedView.frame.size.width, self.frame.size.height + separatorHeight);
|
|
}
|
|
|
|
if (TGIsPad())
|
|
{
|
|
bool hidden = (self.selected || self.highlighted);
|
|
if (_separatorLayer.hidden != hidden)
|
|
{
|
|
[CATransaction begin];
|
|
[CATransaction setDisableActions:true];
|
|
_separatorLayer.hidden = hidden;
|
|
[CATransaction commit];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
|
|
{
|
|
bool wasHighlighted = self.highlighted;
|
|
|
|
[super setHighlighted:highlighted animated:animated];
|
|
|
|
if ((highlighted && !wasHighlighted))
|
|
{
|
|
[self adjustOrdering];
|
|
}
|
|
|
|
if ((highlighted && !wasHighlighted) || (!highlighted && wasHighlighted))
|
|
{
|
|
UIView *selectedView = self.selectedBackgroundView;
|
|
if (selectedView != nil && (self.selected || self.highlighted))
|
|
{
|
|
CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f;
|
|
selectedView.frame = CGRectMake(0, -separatorHeight, selectedView.frame.size.width, self.frame.size.height + separatorHeight);
|
|
}
|
|
|
|
if (TGIsPad())
|
|
{
|
|
bool hidden = (self.selected || self.highlighted);
|
|
if (_separatorLayer.hidden != hidden)
|
|
{
|
|
[CATransaction begin];
|
|
[CATransaction setDisableActions:true];
|
|
_separatorLayer.hidden = hidden;
|
|
[CATransaction commit];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)adjustOrdering
|
|
{
|
|
UIView *selectedView = self.selectedBackgroundView;
|
|
if (selectedView != nil)
|
|
{
|
|
CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f;
|
|
selectedView.frame = CGRectMake(0, -separatorHeight, selectedView.frame.size.width, self.frame.size.height + separatorHeight);
|
|
}
|
|
|
|
if ([self.superview isKindOfClass:[UITableView class]])
|
|
{
|
|
Class UITableViewCellClass = [UITableViewCell class];
|
|
Class UISearchBarClass = [UISearchBar class];
|
|
int maxCellIndex = 0;
|
|
int index = -1;
|
|
int selfIndex = 0;
|
|
for (UIView *view in self.superview.subviews)
|
|
{
|
|
index++;
|
|
if ([view isKindOfClass:UITableViewCellClass] || [view isKindOfClass:UISearchBarClass])
|
|
{
|
|
maxCellIndex = index;
|
|
|
|
if (view == self)
|
|
selfIndex = index;
|
|
}
|
|
}
|
|
|
|
if (selfIndex < maxCellIndex)
|
|
{
|
|
[self.superview insertSubview:self atIndex:maxCellIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setTypingString:(NSString *)typingString
|
|
{
|
|
[self setTypingString:typingString animated:false];
|
|
}
|
|
|
|
- (void)setTypingString:(NSString *)typingString animated:(bool)__unused animated
|
|
{
|
|
_typingString = typingString;
|
|
|
|
if (((_textView.typingText == nil) != (typingString == nil)) || (typingString != nil) != _textView.showTyping || ![_textView.typingText isEqualToString:typingString])
|
|
{
|
|
_textView.showTyping = typingString != nil;
|
|
_textView.typingText = typingString;
|
|
|
|
if (typingString != nil)
|
|
[self startTypingAnimation:false];
|
|
else
|
|
[self stopTypingAnimation];
|
|
|
|
[_textView setNeedsDisplay];
|
|
_validSize = CGSizeZero;
|
|
[self setNeedsLayout];
|
|
}
|
|
}
|
|
|
|
- (void)collectCachedPhotos:(NSMutableDictionary *)dict
|
|
{
|
|
[_avatarView tryFillCache:dict];
|
|
}
|
|
|
|
- (UIView *)typingDotsContainer
|
|
{
|
|
if (_typingDotsContainer == nil)
|
|
{
|
|
_typingDotsContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
UILabel *typingDot = [[UILabel alloc] init];
|
|
typingDot.tag = 100 + i;
|
|
typingDot.text = @".";
|
|
typingDot.textColor = actionTextColor;
|
|
typingDot.font = _textView.textFont;
|
|
typingDot.backgroundColor = [UIColor clearColor];
|
|
typingDot.frame = CGRectMake(4 * i, 0, 4, 10);
|
|
typingDot.alpha = i == 0 ? 0.0f : 0.0f;
|
|
|
|
[_typingDotsContainer addSubview:typingDot];
|
|
}
|
|
}
|
|
|
|
return _typingDotsContainer;
|
|
}
|
|
|
|
- (void)restartAnimations:(bool)force
|
|
{
|
|
if (_animatingTyping)
|
|
{
|
|
_animatingTyping = false;
|
|
|
|
if (_typingDotsTimer != nil)
|
|
{
|
|
[_typingDotsTimer invalidate];
|
|
_typingDotsTimer = nil;
|
|
}
|
|
}
|
|
|
|
if (_textView.showTyping)
|
|
[self startTypingAnimation:force];
|
|
}
|
|
|
|
- (void)stopAnimations
|
|
{
|
|
[self stopTypingAnimation];
|
|
}
|
|
|
|
- (void)startTypingAnimation:(bool)force
|
|
{
|
|
if (!_animatingTyping)
|
|
{
|
|
UIView *typingDotsContainer = [self typingDotsContainer];
|
|
|
|
_animatingTyping = true;
|
|
|
|
if (typingDotsContainer.superview == nil)
|
|
{
|
|
[_wrapView addSubview:typingDotsContainer];
|
|
_validSize = CGSizeZero;
|
|
[self layoutSubviews];
|
|
}
|
|
|
|
if (self.window != nil)
|
|
{
|
|
UIApplicationState state = [UIApplication sharedApplication].applicationState;
|
|
if (state == UIApplicationStateActive || state == UIApplicationStateInactive || force)
|
|
[self _loopTypingAnimation];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)stopTypingAnimation
|
|
{
|
|
if (_animatingTyping)
|
|
{
|
|
_animatingTyping = false;
|
|
|
|
if (_typingDotsTimer != nil)
|
|
{
|
|
[_typingDotsTimer invalidate];
|
|
_typingDotsTimer = nil;
|
|
}
|
|
|
|
[_typingDotsContainer removeFromSuperview];
|
|
}
|
|
}
|
|
|
|
- (void)_loopTypingAnimation
|
|
{
|
|
if (_typingDotsTimer != nil)
|
|
{
|
|
[_typingDotsTimer invalidate];
|
|
_typingDotsTimer = nil;
|
|
}
|
|
|
|
_typingDotsAnimationStep = 0;
|
|
[self _typingAnimationStep];
|
|
}
|
|
|
|
- (void)_typingAnimationStep
|
|
{
|
|
if (_typingDotsTimer != nil)
|
|
{
|
|
[_typingDotsTimer invalidate];
|
|
_typingDotsTimer = nil;
|
|
}
|
|
|
|
_typingDotsAnimationStep++;
|
|
|
|
for (UIView *dotView in _typingDotsContainer.subviews)
|
|
{
|
|
if (dotView.tag >= 100)
|
|
{
|
|
int dotIndex = (int)(dotView.tag - 100);
|
|
if (TGIsRTL())
|
|
dotIndex = 2 - dotIndex;
|
|
|
|
dotView.alpha = dotIndex < (_typingDotsAnimationStep - 1) ? 1.0f : 0.0f;
|
|
}
|
|
}
|
|
|
|
if (_typingDotsAnimationStep > 3)
|
|
{
|
|
_typingDotsTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(_loopTypingAnimation) interval:0.22 repeat:false];
|
|
}
|
|
else
|
|
{
|
|
_typingDotsTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(_typingAnimationStep) interval:_typingDotsAnimationStep == 1 ? 0.22 : 0.12 repeat:false];
|
|
}
|
|
}
|
|
|
|
- (void)resetView:(bool)keepState
|
|
{
|
|
if (self.selectionStyle != UITableViewCellSelectionStyleBlue)
|
|
self.selectionStyle = UITableViewCellSelectionStyleBlue;
|
|
|
|
_avatarIconView.hidden = !_isBroadcast;
|
|
|
|
_dateString = _date == 0 ? nil : [TGDateUtils stringForMessageListDate:(int)_date];
|
|
|
|
_textView.title = _titleText;
|
|
_textView.isVerified = _isVerified;
|
|
|
|
if (normalTextColor == nil)
|
|
{
|
|
normalTextColor = UIColorRGB(0x8e8e93);
|
|
actionTextColor = UIColorRGB(0x8e8e93);
|
|
mediaTextColor = UIColorRGB(0x8e8e93);
|
|
}
|
|
|
|
bool attachmentFound = false;
|
|
_hideAuthorName = !_isGroupChat || _rawText || (_isChannel && !_isChannelGroup);
|
|
|
|
if (_messageAttachments != nil && _messageAttachments.count != 0)
|
|
{
|
|
for (TGMediaAttachment *attachment in _messageAttachments)
|
|
{
|
|
if (attachment.type == TGActionMediaAttachmentType)
|
|
{
|
|
_mediaIcon = nil;
|
|
TGActionMediaAttachment *actionAttachment = (TGActionMediaAttachment *)attachment;
|
|
switch (actionAttachment.actionType)
|
|
{
|
|
case TGMessageActionChatEditTitle:
|
|
{
|
|
if (TGPeerIdIsChannel(_conversationId)) {
|
|
_messageText = _isChannelGroup ? TGLocalized(@"Notification.RenamedGroup") : TGLocalized(@"Notification.RenamedChannel");
|
|
} else {
|
|
TGUser *user = [_users objectForKey:@"author"];
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.RenamedChat"), user.displayName];
|
|
}
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionChatEditPhoto:
|
|
{
|
|
if (TGPeerIdIsChannel(_conversationId)) {
|
|
if (_isChannelGroup) {
|
|
if ([(TGImageMediaAttachment *)[actionAttachment.actionData objectForKey:@"photo"] imageInfo] == nil) {
|
|
_messageText = TGLocalized(@"Group.MessagePhotoRemoved");
|
|
} else {
|
|
_messageText = TGLocalized(@"Group.MessagePhotoUpdated");
|
|
}
|
|
} else {
|
|
if ([(TGImageMediaAttachment *)[actionAttachment.actionData objectForKey:@"photo"] imageInfo] == nil) {
|
|
_messageText = TGLocalized(@"Channel.MessagePhotoRemoved");
|
|
} else {
|
|
_messageText = TGLocalized(@"Channel.MessagePhotoUpdated");
|
|
}
|
|
}
|
|
} else {
|
|
TGUser *user = [_users objectForKey:@"author"];
|
|
if ([(TGImageMediaAttachment *)[actionAttachment.actionData objectForKey:@"photo"] imageInfo] == nil)
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.RemovedGroupPhoto"), user.displayName];
|
|
else
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.ChangedGroupPhoto"), user.displayName];
|
|
}
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionUserChangedPhoto:
|
|
{
|
|
TGUser *user = [_users objectForKey:@"author"];
|
|
if ([(TGImageMediaAttachment *)[actionAttachment.actionData objectForKey:@"photo"] imageInfo] == nil)
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.RemovedUserPhoto"), user.displayFirstName];
|
|
else
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.ChangedUserPhoto"), user.displayFirstName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionChatAddMember:
|
|
{
|
|
NSArray *uids = actionAttachment.actionData[@"uids"];
|
|
if (uids != nil) {
|
|
TGUser *authorUser = [_users objectForKey:@"author"];
|
|
NSMutableArray *subjectUsers = [[NSMutableArray alloc] init];
|
|
for (NSNumber *nUid in uids) {
|
|
TGUser *user = [_users objectForKey:nUid];
|
|
if (user != nil) {
|
|
[subjectUsers addObject:user];
|
|
}
|
|
}
|
|
|
|
if (subjectUsers.count == 1 && authorUser.uid == ((TGUser *)subjectUsers[0]).uid) {
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.JoinedChat"), authorUser.displayName];
|
|
} else {
|
|
NSMutableString *subjectNames = [[NSMutableString alloc] init];
|
|
for (TGUser *user in subjectUsers) {
|
|
if (subjectNames.length != 0) {
|
|
[subjectNames appendString:@", "];
|
|
}
|
|
[subjectNames appendString:user.displayName];
|
|
}
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.Invited"), authorUser.displayName, subjectNames];
|
|
}
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
} else {
|
|
NSNumber *nUid = [actionAttachment.actionData objectForKey:@"uid"];
|
|
if (nUid != nil)
|
|
{
|
|
TGUser *authorUser = [_users objectForKey:@"author"];
|
|
TGUser *subjectUser = [_users objectForKey:nUid];
|
|
if (authorUser.uid == subjectUser.uid)
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.JoinedChat"), authorUser.displayName];
|
|
else
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.Invited"), authorUser.displayName, subjectUser.displayName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionJoinedByLink:
|
|
{
|
|
TGUser *authorUser = [_users objectForKey:@"author"];
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.JoinedGroupByLink"), authorUser.displayName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionChatDeleteMember:
|
|
{
|
|
NSNumber *nUid = [actionAttachment.actionData objectForKey:@"uid"];
|
|
if (nUid != nil)
|
|
{
|
|
TGUser *authorUser = [_users objectForKey:@"author"];
|
|
TGUser *subjectUser = [_users objectForKey:nUid];
|
|
if (authorUser.uid == subjectUser.uid)
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.LeftChat"), authorUser.displayName];
|
|
else
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.Kicked"), authorUser.displayName, subjectUser.displayName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionCreateChat:
|
|
{
|
|
TGUser *user = [_users objectForKey:@"author"];
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.CreatedChat"), user.displayName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionChannelCreated:
|
|
{
|
|
if (_isChannelGroup) {
|
|
_messageText = TGLocalized(@"Notification.CreatedGroup");
|
|
} else {
|
|
_messageText = TGLocalized(@"Notification.CreatedChannel");
|
|
}
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionGroupMigratedTo:
|
|
{
|
|
_messageText = TGLocalized(@"Notification.GroupMigratedToChannel");
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionGroupActivated:
|
|
{
|
|
_messageText = TGLocalized(@"Notification.GroupDeactivated");
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionGroupDeactivated:
|
|
{
|
|
_messageText = TGLocalized(@"Notification.GroupActivated");
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionChannelMigratedFrom:
|
|
{
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.ChannelMigratedFrom"), actionAttachment.actionData[@"title"]];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionChannelInviter:
|
|
{
|
|
TGUser *user = [_users objectForKey:@"author"];
|
|
if ([actionAttachment.actionData[@"uid"] intValue] == user.uid) {
|
|
if (_isChannelGroup) {
|
|
_messageText = TGLocalized(@"Notification.GroupInviterSelf");
|
|
} else {
|
|
_messageText = TGLocalized(@"Notification.ChannelInviterSelf");
|
|
}
|
|
} else {
|
|
int32_t inviterUid = [actionAttachment.actionData[@"uid"] intValue];
|
|
NSString *inviterName = nil;
|
|
TGUser *user = _users[@(inviterUid)];
|
|
if (user.uid == inviterUid) {
|
|
inviterName = user.displayName;
|
|
}
|
|
|
|
if (_isChannelGroup) {
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.GroupInviter"), inviterName];
|
|
} else {
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.ChannelInviter"), inviterName];
|
|
}
|
|
}
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionCreateBroadcastList:
|
|
{
|
|
_messageText = TGLocalized(@"Notification.CreatedBroadcastList");
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionContactRequest:
|
|
{
|
|
_messageText = [[NSString alloc] initWithFormat:@"%@ sent contact request", _authorName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionAcceptContactRequest:
|
|
{
|
|
_messageText = [[NSString alloc] initWithFormat:@"%@ accepted contact request", _authorName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionContactRegistered:
|
|
{
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.Joined"), _authorName];
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
_hideAuthorName = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionEncryptedChatRequest:
|
|
{
|
|
_messageText = TGLocalized(@"Notification.EncryptedChatRequested");
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionEncryptedChatAccept:
|
|
{
|
|
_messageText = TGLocalized(@"Notification.EncryptedChatAccepted");
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionEncryptedChatDecline:
|
|
{
|
|
_messageText = TGLocalized(@"Notification.EncryptedChatRejected");
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionEncryptedChatMessageLifetime:
|
|
{
|
|
int messageLifetime = [actionAttachment.actionData[@"messageLifetime"] intValue];
|
|
|
|
_messageTextColor = actionTextColor;
|
|
attachmentFound = true;
|
|
|
|
if (messageLifetime == 0)
|
|
{
|
|
if (_outgoing)
|
|
_messageText = TGLocalized(@"Notification.MessageLifetimeRemovedOutgoing");
|
|
else
|
|
{
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.MessageLifetimeRemoved"), _encryptionFirstName];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NSString *lifetimeString = [TGStringUtils stringForMessageTimerSeconds:messageLifetime];
|
|
|
|
if (_outgoing)
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.MessageLifetimeChangedOutgoing"), lifetimeString];
|
|
else
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.MessageLifetimeChanged"), _encryptionFirstName, lifetimeString];
|
|
}
|
|
|
|
break;
|
|
}
|
|
case TGMessageActionEncryptedChatScreenshot:
|
|
case TGMessageActionEncryptedChatMessageScreenshot:
|
|
{
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.SecretChatMessageScreenshot"), _encryptionFirstName];
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (attachment.type == TGImageMediaAttachmentType)
|
|
{
|
|
TGImageMediaAttachment *imageMediaAttachment = (TGImageMediaAttachment *)attachment;
|
|
if (imageMediaAttachment.caption.length > 0)
|
|
{
|
|
_messageText = imageMediaAttachment.caption;
|
|
_messageTextColor = normalTextColor;
|
|
}
|
|
else
|
|
{
|
|
_messageText = TGLocalized(@"Message.Photo");
|
|
_messageTextColor = mediaTextColor;
|
|
}
|
|
//_mediaIcon = [UIImage imageNamed:@"MediaPhoto"];
|
|
attachmentFound = true;
|
|
break;
|
|
}
|
|
else if (attachment.type == TGVideoMediaAttachmentType)
|
|
{
|
|
TGVideoMediaAttachment *videoMediaAttachment = (TGVideoMediaAttachment *)attachment;
|
|
if (videoMediaAttachment.caption.length > 0)
|
|
{
|
|
_messageText = videoMediaAttachment.caption;
|
|
_messageTextColor = normalTextColor;
|
|
}
|
|
else
|
|
{
|
|
_messageText = TGLocalized(@"Message.Video");
|
|
_messageTextColor = mediaTextColor;
|
|
}
|
|
//_mediaIcon = [UIImage imageNamed:@"MediaVideo"];
|
|
attachmentFound = true;
|
|
break;
|
|
}
|
|
else if (attachment.type == TGLocationMediaAttachmentType)
|
|
{
|
|
_messageText = TGLocalized(@"Message.Location");
|
|
_messageTextColor = mediaTextColor;
|
|
//_mediaIcon = [UIImage imageNamed:@"MediaLocation"];
|
|
attachmentFound = true;
|
|
break;
|
|
}
|
|
else if (attachment.type == TGContactMediaAttachmentType)
|
|
{
|
|
_messageText = TGLocalized(@"Message.Contact");
|
|
_messageTextColor = mediaTextColor;
|
|
//_mediaIcon = [UIImage imageNamed:@"MediaContact"];
|
|
attachmentFound = true;
|
|
break;
|
|
}
|
|
else if (attachment.type == TGDocumentMediaAttachmentType)
|
|
{
|
|
TGDocumentMediaAttachment *documentAttachment = (TGDocumentMediaAttachment *)attachment;
|
|
|
|
bool isAnimated = false;
|
|
CGSize imageSize = CGSizeZero;
|
|
bool isSticker = false;
|
|
bool isVoice = false;
|
|
NSString *stickerRepresentation = nil;
|
|
for (id attribute in documentAttachment.attributes)
|
|
{
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeAnimated class]])
|
|
{
|
|
isAnimated = true;
|
|
}
|
|
else if ([attribute isKindOfClass:[TGDocumentAttributeImageSize class]])
|
|
{
|
|
imageSize = ((TGDocumentAttributeImageSize *)attribute).size;
|
|
}
|
|
else if ([attribute isKindOfClass:[TGDocumentAttributeVideo class]]) {
|
|
imageSize = ((TGDocumentAttributeVideo *)attribute).size;
|
|
}
|
|
else if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]])
|
|
{
|
|
isSticker = true;
|
|
stickerRepresentation = ((TGDocumentAttributeSticker *)attribute).alt;
|
|
}
|
|
else if ([attribute isKindOfClass:[TGDocumentAttributeAudio class]]) {
|
|
isVoice = ((TGDocumentAttributeAudio *)attribute).isVoice;
|
|
}
|
|
}
|
|
|
|
if (TGPeerIdIsSecretChat(_conversationId) && [documentAttachment.mimeType isEqualToString:@"video/mp4"] && documentAttachment.size < 1024 * 1024) {
|
|
isAnimated = true;
|
|
}
|
|
|
|
if (isSticker)
|
|
{
|
|
if (stickerRepresentation.length == 0)
|
|
_messageText = TGLocalized(@"Message.Sticker");
|
|
else
|
|
_messageText = [[NSString alloc] initWithFormat:@"%@ %@", stickerRepresentation, TGLocalized(@"Message.Sticker")];
|
|
_mediaIcon = nil;
|
|
}
|
|
else if (isAnimated) {
|
|
_messageText = TGLocalized(@"Message.Animation");
|
|
_messageTextColor = mediaTextColor;
|
|
attachmentFound = true;
|
|
}
|
|
else if (isVoice) {
|
|
_messageText = TGLocalized(@"Message.Audio");
|
|
_messageTextColor = mediaTextColor;
|
|
attachmentFound = true;
|
|
}
|
|
else
|
|
{
|
|
NSString *fileName = ((TGDocumentMediaAttachment *)attachment).fileName;
|
|
if (fileName.length != 0)
|
|
_messageText = fileName;
|
|
else
|
|
_messageText = TGLocalized(@"Message.File");
|
|
|
|
_messageTextColor = mediaTextColor;
|
|
//_mediaIcon = [UIImage imageNamed:@"MediaFile"];
|
|
attachmentFound = true;
|
|
}
|
|
break;
|
|
}
|
|
else if (attachment.type == TGAudioMediaAttachmentType)
|
|
{
|
|
_messageText = TGLocalized(@"Message.Audio");
|
|
_messageTextColor = mediaTextColor;
|
|
//_mediaIcon = [UIImage imageNamed:@"MediaVoice"];
|
|
attachmentFound = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!attachmentFound)
|
|
{
|
|
_messageTextColor = normalTextColor;
|
|
_mediaIcon = nil;
|
|
}
|
|
|
|
if (_messageText.length == 0)
|
|
{
|
|
_messageTextColor = actionTextColor;
|
|
if (_isEncrypted)
|
|
{
|
|
if (_encryptionStatus == 1)
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"DialogList.AwaitingEncryption"), _encryptionFirstName];
|
|
else if (_encryptionStatus == 2)
|
|
_messageText = TGLocalized(@"DialogList.EncryptionProcessing");
|
|
else if (_encryptionStatus == 3)
|
|
_messageText = TGLocalized(@"DialogList.EncryptionRejected");
|
|
else if (_encryptionStatus == 4)
|
|
{
|
|
if (_encryptionOutgoing)
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"DialogList.EncryptedChatStartedOutgoing"), _encryptionFirstName];
|
|
else
|
|
_messageText = [[NSString alloc] initWithFormat:TGLocalized(@"DialogList.EncryptedChatStartedIncoming"), _encryptionFirstName];
|
|
}
|
|
}
|
|
}
|
|
|
|
_textView.text = _messageText;
|
|
_textView.textColor = _messageTextColor;
|
|
_textView.mediaIcon = _mediaIcon;
|
|
|
|
int totalUnreadCount = 0;
|
|
if (TGPeerIdIsChannel(_conversationId)) {
|
|
totalUnreadCount = _unreadCount + _serviceUnreadCount;
|
|
} else {
|
|
totalUnreadCount = _unreadCount + _serviceUnreadCount;
|
|
}
|
|
|
|
if (totalUnreadCount)
|
|
{
|
|
_unreadCountBackgrond.hidden = false;
|
|
_unreadCountLabel.hidden = false;
|
|
|
|
if (TGIsLocaleArabic())
|
|
{
|
|
_unreadCountLabel.text = [TGStringUtils stringWithLocalizedNumberCharacters:[[NSString alloc] initWithFormat:@"%d", totalUnreadCount]];
|
|
}
|
|
else
|
|
{
|
|
if (totalUnreadCount < 1000)
|
|
_unreadCountLabel.text = [[NSString alloc] initWithFormat:@"%d", totalUnreadCount];
|
|
else
|
|
_unreadCountLabel.text = [[NSString alloc] initWithFormat:@"%dK", totalUnreadCount / 1000];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_unreadCountBackgrond.hidden = true;
|
|
_unreadCountLabel.hidden = true;
|
|
}
|
|
|
|
if (_deliveryState == TGMessageDeliveryStateFailed)
|
|
{
|
|
_unreadCountBackgrond.hidden = true;
|
|
_unreadCountLabel.hidden = true;
|
|
|
|
if (_deliveryErrorBackgrond == nil)
|
|
{
|
|
_deliveryErrorBackgrond = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ModernConversationListBadgeUnsent.png"]];
|
|
[_wrapView addSubview:_deliveryErrorBackgrond];
|
|
}
|
|
else if (_deliveryErrorBackgrond.superview == nil)
|
|
[_wrapView addSubview:_deliveryErrorBackgrond];
|
|
}
|
|
else if (_deliveryErrorBackgrond != nil && _deliveryErrorBackgrond.superview != nil)
|
|
{
|
|
[_deliveryErrorBackgrond removeFromSuperview];
|
|
}
|
|
|
|
_textView.authorName = _hideAuthorName ? nil : _authorName;
|
|
|
|
_avatarView.hidden = false;
|
|
|
|
static UIImage *placeholder = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
//!placeholder
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(62.0f, 62.0f), false, 0.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
|
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 62.0f, 62.0f));
|
|
CGContextSetStrokeColorWithColor(context, UIColorRGB(0xd9d9d9).CGColor);
|
|
CGContextSetLineWidth(context, 1.0f);
|
|
CGContextStrokeEllipseInRect(context, CGRectMake(0.5f, 0.5f, 61.0f, 61.0f));
|
|
|
|
placeholder = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
});
|
|
|
|
if (_avatarUrl.length != 0)
|
|
{
|
|
_avatarView.fadeTransitionDuration = keepState ? 0.3 : 0.14;
|
|
|
|
if (![_avatarView.currentUrl isEqualToString:_avatarUrl])
|
|
{
|
|
if (keepState)
|
|
{
|
|
[_avatarView loadImage:_avatarUrl filter:@"circle:62x62" placeholder:(_avatarView.currentImage != nil ? _avatarView.currentImage : placeholder) forceFade:true];
|
|
}
|
|
else
|
|
{
|
|
[_avatarView loadImage:_avatarUrl filter:@"circle:62x62" placeholder:placeholder forceFade:false];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_avatarView.fadeTransitionDuration = 0.14;
|
|
|
|
if (_isEncrypted || _conversationId > 0)
|
|
{
|
|
NSString *firstName = nil;
|
|
NSString *lastName = nil;
|
|
if (_titleLetters.count >= 2)
|
|
{
|
|
firstName = _titleLetters[0];
|
|
lastName = _titleLetters[1];
|
|
}
|
|
else if (_titleLetters.count == 1)
|
|
firstName = _titleLetters[0];
|
|
|
|
[_avatarView loadUserPlaceholderWithSize:CGSizeMake(62.0f, 62.0f) uid:_isEncrypted ? _encryptedUserId : (int32_t)_conversationId firstName:firstName lastName:lastName placeholder:placeholder];
|
|
}
|
|
else
|
|
{
|
|
[_avatarView loadGroupPlaceholderWithSize:CGSizeMake(62.0f, 62.0f) conversationId:_conversationId title:_isBroadcast ? @"" : _titleText placeholder:placeholder];
|
|
}
|
|
//[_avatarView loadImage:[[NSString alloc] initWithFormat:@"dialogListPlaceholder:%lld", _conversationId] filter:nil placeholder:[_assetsSource groupAvatarPlaceholderGeneric] forceFade:false];
|
|
}
|
|
|
|
_textView.isMultichat = _isGroupChat;
|
|
_textView.isEncrypted = _isEncrypted;
|
|
|
|
_dateLabel.dateText = _dateString;
|
|
|
|
_validSize = CGSizeZero;
|
|
|
|
[_textView setNeedsDisplay];
|
|
|
|
if (_editingIsActive)
|
|
{
|
|
_editingIsActive = false;
|
|
}
|
|
|
|
if (_outgoing)
|
|
{
|
|
if (_deliveryState == TGMessageDeliveryStateDelivered && !_unread)
|
|
{
|
|
_deliveredCheckmark.hidden = true;
|
|
_readCheckmark.hidden = false;
|
|
}
|
|
else if (_deliveryState == TGMessageDeliveryStateDelivered && _unread)
|
|
{
|
|
_deliveredCheckmark.hidden = false;
|
|
_readCheckmark.hidden = true;
|
|
}
|
|
else
|
|
{
|
|
_deliveredCheckmark.hidden = true;
|
|
_readCheckmark.hidden = true;
|
|
}
|
|
|
|
if (_deliveryState == TGMessageDeliveryStatePending)
|
|
{
|
|
if (_pendingIndicator == nil)
|
|
{
|
|
static UIImage *pendingImage = nil;
|
|
if (pendingImage == nil)
|
|
{
|
|
pendingImage = [UIImage imageNamed:@"DialogListPending.png"];
|
|
}
|
|
|
|
_pendingIndicator = [[UIImageView alloc] initWithImage:pendingImage];
|
|
[_wrapView addSubview:_pendingIndicator];
|
|
}
|
|
|
|
_pendingIndicator.hidden = false;
|
|
}
|
|
else
|
|
{
|
|
_pendingIndicator.hidden = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_deliveredCheckmark.hidden = true;
|
|
_readCheckmark.hidden = true;
|
|
|
|
_pendingIndicator.hidden = true;
|
|
}
|
|
|
|
if (_isMuted)
|
|
{
|
|
if (_muteIcon == nil)
|
|
{
|
|
_muteIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"DialogList_Muted.png"]];
|
|
}
|
|
|
|
if (_muteIcon.superview == nil)
|
|
[_wrapView addSubview:_muteIcon];
|
|
}
|
|
else if (_muteIcon != nil)
|
|
{
|
|
[_muteIcon removeFromSuperview];
|
|
}
|
|
|
|
if (_isVerified) {
|
|
if (_verifiedIcon == nil) {
|
|
_verifiedIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ChannelVerifiedIconSmall.png"]];
|
|
}
|
|
if (_verifiedIcon.superview == nil) {
|
|
[_wrapView addSubview:_verifiedIcon];
|
|
}
|
|
} else if (_verifiedIcon != nil && _verifiedIcon.superview != nil) {
|
|
[_verifiedIcon removeFromSuperview];
|
|
}
|
|
|
|
[self setNeedsLayout];
|
|
}
|
|
|
|
- (void)dismissEditingControls:(bool)__unused animated
|
|
{
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
#undef TG_TIMESTAMP_DEFINE
|
|
#undef TG_TIMESTAMP_MEASURE
|
|
|
|
#define TG_TIMESTAMP_DEFINE(x)
|
|
#define TG_TIMESTAMP_MEASURE(x)
|
|
|
|
TG_TIMESTAMP_DEFINE(cellLayout);
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
|
|
[super layoutSubviews];
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
|
|
CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f;
|
|
|
|
CGSize rawSize = self.frame.size;
|
|
|
|
UIView *selectedView = self.selectedBackgroundView;
|
|
if (selectedView != nil)
|
|
selectedView.frame = CGRectMake(0, -separatorHeight, selectedView.frame.size.width, rawSize.height + separatorHeight);
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
|
|
self.backgroundView.frame = CGRectMake(0.0f, 0.0f, rawSize.width, rawSize.height);
|
|
|
|
static CGSize screenSize;
|
|
static CGFloat widescreenWidth;
|
|
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
screenSize = TGScreenSize();
|
|
widescreenWidth = MAX(screenSize.width, screenSize.height);
|
|
});
|
|
|
|
CGFloat contentOffset = self.contentView.frame.origin.x;
|
|
|
|
CGSize size = rawSize;
|
|
if (!TGIsPad())
|
|
{
|
|
if (rawSize.width >= widescreenWidth - FLT_EPSILON)
|
|
size.width = screenSize.height - contentOffset;
|
|
else
|
|
size.width = screenSize.width - contentOffset;
|
|
}
|
|
else
|
|
size.width = rawSize.width - contentOffset;
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
|
|
_separatorLayer.frame = CGRectMake(80.0f, size.height - separatorHeight, rawSize.width - 80.0f, separatorHeight);
|
|
|
|
_wrapView.frame = CGRectMake(contentOffset, 0.0f, size.width, size.height);
|
|
|
|
if (!CGSizeEqualToSize(_validSize, size))
|
|
{
|
|
if (_textView != nil)
|
|
{
|
|
if (!CGSizeEqualToSize(_textView.frame.size, CGRectMake(79.0f, 6, size.width - 79.0f, 62).size))
|
|
{
|
|
_textView.frame = CGRectMake(80.0f, 6, size.width - 80.0f, 62);
|
|
[_textView setNeedsDisplay];
|
|
}
|
|
}
|
|
|
|
/*CGRect arrowFrame = _arrowView.frame;
|
|
arrowFrame.origin.x = contentOffset > FLT_EPSILON ? (rawSize.width + 4.0f) : (size.width - 9.0f - _arrowView.frame.size.width);
|
|
arrowFrame.origin.y = 12.0f;
|
|
_arrowView.frame = arrowFrame;
|
|
_arrowView.alpha = contentOffset > FLT_EPSILON ? 0.0f : 1.0f;*/
|
|
|
|
int rightPadding = 0.0f;
|
|
|
|
CGFloat countTextWidth = [_unreadCountLabel.text sizeWithFont:_unreadCountLabel.font].width;
|
|
|
|
CGFloat backgroundWidth = MAX(20.0f, countTextWidth + 11.0f);
|
|
CGRect unreadCountBackgroundFrame = CGRectMake(size.width - 11.0f - backgroundWidth, 38.0f, backgroundWidth, 20.0f);
|
|
_unreadCountBackgrond.frame = unreadCountBackgroundFrame;
|
|
CGRect unreadCountLabelFrame = _unreadCountLabel.frame;
|
|
unreadCountLabelFrame.origin = CGPointMake(unreadCountBackgroundFrame.origin.x + TGRetinaFloor(((unreadCountBackgroundFrame.size.width - countTextWidth) / 2.0f)) - (TGIsRetina() ? 0.0f : 0.0f), unreadCountBackgroundFrame.origin.y + 1.0f -TGRetinaPixel);
|
|
_unreadCountLabel.frame = unreadCountLabelFrame;
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
|
|
if (!_unreadCountBackgrond.hidden)
|
|
rightPadding += unreadCountBackgroundFrame.size.width + 16;
|
|
|
|
if (_deliveryErrorBackgrond != nil && _deliveryErrorBackgrond.superview != nil)
|
|
{
|
|
CGRect deliveryErrorFrame = _deliveryErrorBackgrond.frame;
|
|
deliveryErrorFrame = CGRectMake(size.width - 14.0f - TGRetinaPixel - deliveryErrorFrame.size.width, 38.0f + TGRetinaPixel, deliveryErrorFrame.size.width, deliveryErrorFrame.size.height);
|
|
_deliveryErrorBackgrond.frame = deliveryErrorFrame;
|
|
|
|
rightPadding += 36;
|
|
}
|
|
|
|
CGSize dateTextSize = [_dateLabel measureTextSize];
|
|
|
|
CGFloat dateWidth = _date == 0 ? 0 : (int)(dateTextSize.width);
|
|
CGRect dateFrame = CGRectMake(size.width - dateWidth - 11.0f + (contentOffset > FLT_EPSILON ? 4.0f : 0.0f), 10.0f + TGRetinaPixel - (TGIsPad() ? 1.0f : 0.0f), 75, 20);
|
|
_dateLabel.frame = dateFrame;
|
|
CGFloat titleLabelWidth = (int)(dateFrame.origin.x - 4 - 80.0f - 18);
|
|
CGFloat groupChatIconWidth = 0.0f;
|
|
if (_isEncrypted)
|
|
{
|
|
groupChatIconWidth = 15;
|
|
titleLabelWidth -= groupChatIconWidth;
|
|
}
|
|
else if (false && _isGroupChat)
|
|
{
|
|
groupChatIconWidth = 22;
|
|
titleLabelWidth -= groupChatIconWidth;
|
|
}
|
|
|
|
if (_isMuted)
|
|
titleLabelWidth -= 12;
|
|
|
|
if (_isVerified) {
|
|
titleLabelWidth -= _verifiedIcon.frame.size.width + 10.0f;
|
|
}
|
|
|
|
titleLabelWidth = MIN(titleLabelWidth, [_titleText sizeWithFont:_textView.titleFont].width);
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
|
|
_deliveredCheckmark.frame = CGRectMake(dateFrame.origin.x - 15, 13.0f, 13, 11);
|
|
_readCheckmark.frame = CGRectMake(dateFrame.origin.x - 20, 13.0f, 18, 11);
|
|
|
|
if (_pendingIndicator != nil)
|
|
_pendingIndicator.frame = CGRectMake(dateFrame.origin.x - 16, 13, 12, 12);
|
|
|
|
CGRect titleRect = CGRectMake(80.0f + groupChatIconWidth, 8.0f, titleLabelWidth, 20);
|
|
|
|
CGRect messageRect = CGRectMake(80.0f, 30.0f - TGRetinaPixel, size.width - 80.0f - 7.0f - rightPadding, 40);
|
|
|
|
CGRect typingRect = messageRect;
|
|
typingRect.size.width -= 12;
|
|
if (TGIsRTL())
|
|
typingRect.origin.x += 12.0f;
|
|
_textView.typingFrame = typingRect;
|
|
|
|
if (_typingDotsContainer.superview != nil)
|
|
{
|
|
CGSize typingSize = [_textView.typingText sizeWithFont:_textView.textFont constrainedToSize:typingRect.size lineBreakMode:NSLineBreakByTruncatingTail];
|
|
|
|
CGRect typingDotsFrame = _typingDotsContainer.frame;
|
|
typingDotsFrame.origin.x = TGIsRTL() ? messageRect.origin.x : (typingRect.origin.x + typingSize.width);
|
|
typingDotsFrame.origin.y = typingRect.origin.y + 4;
|
|
_typingDotsContainer.frame = typingDotsFrame;
|
|
}
|
|
|
|
if (_authorName != nil && !_hideAuthorName)
|
|
{
|
|
_textView.authorNameFrame = CGRectMake(80.0f, 29.0f + TGRetinaPixel, size.width - 80.0f - 4.0f - rightPadding, 20);
|
|
|
|
messageRect.origin.y += iosMajorVersion() >= 7 ? (10 + TGRetinaPixel) : 17;
|
|
messageRect.size.height -= 12;
|
|
}
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
|
|
titleRect.size.width = titleLabelWidth;
|
|
|
|
if (_authorName != nil && !_hideAuthorName && [_messageText sizeWithFont:_textView.textFont constrainedToSize:messageRect.size].height < 20)
|
|
messageRect.origin.y += 9;
|
|
|
|
if (_isVerified) {
|
|
CGRect verifiedRect = _verifiedIcon.bounds;
|
|
verifiedRect.origin = CGPointMake(titleRect.origin.x + titleRect.size.width + 4.0f, titleRect.origin.y + 4 - TGRetinaPixel);
|
|
_verifiedIcon.frame = verifiedRect;
|
|
}
|
|
|
|
if (_isMuted)
|
|
{
|
|
CGRect muteRect = _muteIcon.frame;
|
|
muteRect.origin = CGPointMake(titleRect.origin.x + titleRect.size.width + 3, titleRect.origin.y + 6);
|
|
if (_isVerified) {
|
|
muteRect.origin.x += _verifiedIcon.bounds.size.width + 7.0f;
|
|
}
|
|
_muteIcon.frame = muteRect;
|
|
}
|
|
|
|
_textView.titleFrame = titleRect;
|
|
_textView.textFrame = messageRect;
|
|
|
|
_validSize = size;
|
|
|
|
TG_TIMESTAMP_MEASURE(cellLayout);
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
|
|
{
|
|
[super setEditing:editing animated:animated];
|
|
}
|
|
|
|
- (bool)showingDeleteConfirmationButton
|
|
{
|
|
return false;
|
|
}
|
|
|
|
- (void)resetLocalization
|
|
{
|
|
_dateLabel.dateText = @"";
|
|
}
|
|
|
|
@end
|