1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-04 02:17:51 +01:00
Telegram/Watch/Extension/TGMessageViewModel.m
2016-02-25 01:03:51 +01:00

519 lines
21 KiB
Objective-C

#import "TGMessageViewModel.h"
#import "TGStringUtils.h"
#import "TGGeometry.h"
#import "WKInterfaceImage+Signals.h"
#import "WKInterfaceGroup+Signals.h"
#import "TGBridgeUser.h"
#import "TGBridgeChat.h"
#import "TGBridgeMessage.h"
#import "TGBridgeUserCache.h"
#import "TGBridgeContext.h"
#import "TGBridgeMediaSignals.h"
@implementation TGMessageViewModel
+ (void)imageBubbleSizeForImageSize:(CGSize)imageSize minSize:(CGSize)minSize maxSize:(CGSize)maxSize thumbnailSize:(out CGSize *)thumbnailSize renderSize:(out CGSize *)renderSize
{
CGSize imageTargetMaxSize = maxSize;
CGSize imageScalingMaxSize = CGSizeMake(imageTargetMaxSize.width - 18.0f, imageTargetMaxSize.height - 18.0f);
CGSize imageTargetMinSize = minSize;
CGFloat imageAspect = 1.0f;
if (imageSize.width > 1.0f - FLT_EPSILON && imageSize.height > 1.0f - FLT_EPSILON)
imageAspect = imageSize.width / imageSize.height;
if (imageSize.width < imageScalingMaxSize.width || imageSize.height < imageScalingMaxSize.height)
{
if (imageSize.width <= FLT_EPSILON || imageSize.height <= FLT_EPSILON)
imageSize = imageTargetMinSize;
}
else
{
if (imageSize.width > imageTargetMaxSize.width)
{
imageSize.width = imageTargetMaxSize.width;
imageSize.height = floorf(imageTargetMaxSize.width / imageAspect);
}
if (imageSize.height > imageTargetMaxSize.height)
{
imageSize.width = floorf(imageTargetMaxSize.height * imageAspect);
imageSize.height = imageTargetMaxSize.height;
}
}
if (renderSize != NULL)
*renderSize = imageSize;
imageSize.width = MIN(imageTargetMaxSize.width, imageSize.width);
imageSize.height = MIN(imageTargetMaxSize.height, imageSize.height);
imageSize.width = MAX(imageTargetMinSize.width, imageSize.width);
imageSize.height = MAX(imageTargetMinSize.height, imageSize.height);
if (thumbnailSize != NULL)
*thumbnailSize = imageSize;
}
+ (void)updateAuthorLabel:(WKInterfaceLabel *)authorLabel isOutgoing:(bool)isOutgoing isGroup:(bool)isGroup user:(TGBridgeUser *)user ownUserId:(int32_t)ownUserId
{
if (isGroup && !isOutgoing)
{
authorLabel.hidden = false;
authorLabel.text = user.displayName;
authorLabel.textColor = [TGColor colorForUserId:user.identifier myUserId:ownUserId];
}
else
{
authorLabel.hidden = true;
}
}
+ (void)updateMediaGroup:(WKInterfaceGroup *)mediaGroup activityIndicator:(WKInterfaceImage *)activityIndicator mediaAttachment:(TGBridgeMediaAttachment *)mediaAttachment currentPhoto:(int64_t *)currentPhoto standalone:(bool)standalone margin:(CGFloat)margin imageSize:(CGSize *)imageSize isVisible:(bool (^)(void))isVisible completion:(void (^)(void))completion
{
CGSize targetImageSize = CGSizeZero;
if ([mediaAttachment isKindOfClass:[TGBridgeImageMediaAttachment class]])
[((TGBridgeImageMediaAttachment *)mediaAttachment).imageInfo imageUrlForLargestSize:&targetImageSize];
else if ([mediaAttachment isKindOfClass:[TGBridgeVideoMediaAttachment class]])
targetImageSize = ((TGBridgeVideoMediaAttachment *)mediaAttachment).dimensions;
CGSize screenSize = TGWatchScreenSize();
CGSize mediaGroupSize = CGSizeZero;
if (standalone)
{
mediaGroupSize = TGFitSize(targetImageSize, CGSizeMake(screenSize.width - margin * 2, FLT_MAX));
}
else
{
CGSize maxSize = CGSizeMake(screenSize.width - 15, screenSize.width);
CGSize minSize = CGSizeMake(screenSize.width / 1.25f, screenSize.width / 2);
[self imageBubbleSizeForImageSize:targetImageSize minSize:minSize maxSize:maxSize thumbnailSize:&mediaGroupSize renderSize:NULL];
}
mediaGroupSize = CGSizeMake(ceilf(mediaGroupSize.width), ceilf(mediaGroupSize.height));
if (imageSize != NULL)
*imageSize = CGSizeMake(mediaGroupSize.width, mediaGroupSize.height);
if ([mediaAttachment isKindOfClass:[TGBridgeImageMediaAttachment class]])
{
TGBridgeImageMediaAttachment *imageAttachment = (TGBridgeImageMediaAttachment *)mediaAttachment;
if (currentPhoto == NULL || imageAttachment.imageId != *currentPhoto)
{
if (currentPhoto != NULL)
*currentPhoto = imageAttachment.imageId;
[mediaGroup setBackgroundImageSignal:[[[TGBridgeMediaSignals previewWithImageAttachment:imageAttachment size:mediaGroupSize] onNext:^(id next)
{
if (next != nil)
activityIndicator.hidden = true;
if (completion != nil)
completion();
}] onError:^(id error)
{
if (currentPhoto != NULL)
*currentPhoto = 0;
if (completion != nil)
completion();
}] isVisible:isVisible];
}
}
else if ([mediaAttachment isKindOfClass:[TGBridgeVideoMediaAttachment class]])
{
activityIndicator.hidden = true;
TGBridgeVideoMediaAttachment *videoAttachment = (TGBridgeVideoMediaAttachment *)mediaAttachment;
if (currentPhoto == NULL || videoAttachment.videoId != *currentPhoto)
{
if (currentPhoto != NULL)
*currentPhoto = videoAttachment.videoId;
[mediaGroup setBackgroundImageSignal:[[[TGBridgeMediaSignals previewWithVideoAttachment:videoAttachment size:mediaGroupSize] onNext:^(id next)
{
if (next != nil)
activityIndicator.hidden = true;
if (completion != nil)
completion();
}] onError:^(id error)
{
if (currentPhoto != NULL)
*currentPhoto = 0;
if (completion != nil)
completion();
}] isVisible:isVisible];
}
}
}
+ (void)updateForwardHeaderGroup:(WKInterfaceGroup *)forwardHeaderGroup titleLabel:(WKInterfaceLabel *)titleLabel fromLabel:(WKInterfaceLabel *)fromLabel forwardAttachment:(TGBridgeForwardedMessageMediaAttachment *)forwardAttachment forwardPeer:(id)forwardPeer textColor:(UIColor *)textColor
{
forwardHeaderGroup.hidden = (forwardAttachment == nil);
if (forwardHeaderGroup.hidden)
return;
titleLabel.text = TGLocalized(@"Watch.Message.ForwardedFrom");
NSString *authorName = nil;
if ([forwardPeer isKindOfClass:[TGBridgeUser class]])
authorName = ((TGBridgeUser *)forwardPeer).displayName;
else if ([forwardPeer isKindOfClass:[TGBridgeChat class]])
authorName = ((TGBridgeChat *)forwardPeer).groupTitle;
NSMutableAttributedString *forwardAttributedText = [[NSMutableAttributedString alloc] initWithString:authorName attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:12], NSForegroundColorAttributeName:textColor }];
NSRange formatNameRange = NSMakeRange(0, authorName.length);
if (formatNameRange.location != NSNotFound)
{
[forwardAttributedText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:12 weight:UIFontWeightMedium] range:NSMakeRange(formatNameRange.location, authorName.length)];
}
fromLabel.attributedText = forwardAttributedText;
}
+ (void)updateReplyHeaderGroup:(WKInterfaceGroup *)replyHeaderGroup authorLabel:(WKInterfaceLabel *)authorLabel imageGroup:(WKInterfaceGroup *)imageGroup textLabel:(WKInterfaceLabel *)textLabel titleColor:(UIColor *)titleColor subtitleColor:(UIColor *)subtitleColor replyAttachment:(TGBridgeReplyMessageMediaAttachment *)replyAttachment currentReplyPhoto:(int64_t *)currentReplyPhoto isVisible:(bool (^)(void))isVisible completion:(void (^)(void))completion
{
TGBridgeMessage *message = replyAttachment.message;
replyHeaderGroup.hidden = (message == nil);
if (replyHeaderGroup.hidden)
return;
bool hasAttachment = false;
bool hasImagePreview = false;
NSString *messageText = nil;
UIColor *textColor = nil;
TGBridgeImageMediaAttachment *imageAttachment = nil;
TGBridgeVideoMediaAttachment *videoAttachment = nil;
for (TGBridgeMediaAttachment *attachment in message.media)
{
if ([attachment isKindOfClass:[TGBridgeImageMediaAttachment class]])
{
hasAttachment = true;
hasImagePreview = true;
messageText = TGLocalized(@"Message.Photo");
imageAttachment = (TGBridgeImageMediaAttachment *)attachment;
}
else if ([attachment isKindOfClass:[TGBridgeVideoMediaAttachment class]])
{
hasAttachment = true;
hasImagePreview = true;
messageText = TGLocalized(@"Message.Video");
videoAttachment = (TGBridgeVideoMediaAttachment *)attachment;
}
else if ([attachment isKindOfClass:[TGBridgeAudioMediaAttachment class]])
{
hasAttachment = true;
messageText = TGLocalized(@"Message.Audio");
}
else if ([attachment isKindOfClass:[TGBridgeDocumentMediaAttachment class]])
{
hasAttachment = true;
TGBridgeDocumentMediaAttachment *documentAttachment = (TGBridgeDocumentMediaAttachment *)attachment;
if (documentAttachment.isSticker)
{
if (documentAttachment.stickerAlt.length > 0)
messageText = [NSString stringWithFormat:@"%@ %@", documentAttachment.stickerAlt, TGLocalized(@"Message.Sticker")];
else
messageText = TGLocalized(@"Message.Sticker");
}
else
{
if (documentAttachment.fileName.length > 0)
messageText = documentAttachment.fileName;
else
messageText = TGLocalized(@"Message.File");
}
}
else if ([attachment isKindOfClass:[TGBridgeLocationMediaAttachment class]])
{
hasAttachment = true;
messageText = TGLocalized(@"Message.Location");
}
else if ([attachment isKindOfClass:[TGBridgeContactMediaAttachment class]])
{
hasAttachment = true;
messageText = TGLocalized(@"Message.Contact");
}
else if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]])
{
hasAttachment = true;
TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment;
[self stringForActionAttachment:actionAttachment message:message users:nil forChannel:false];
}
}
if (!hasAttachment)
{
messageText = message.text;
textColor = titleColor;
}
else
{
textColor = subtitleColor;
}
authorLabel.text = [[[TGBridgeUserCache instance] userWithId:(int32_t)message.fromUid] displayName];
imageGroup.hidden = !hasImagePreview;
textLabel.text = messageText;
textLabel.textColor = textColor;
if (imageAttachment != nil)
{
if (currentReplyPhoto == NULL || imageAttachment.imageId != *currentReplyPhoto)
{
if (currentReplyPhoto != NULL)
*currentReplyPhoto = imageAttachment.imageId;
[imageGroup setBackgroundImageSignal:[[[TGBridgeMediaSignals previewWithImageAttachment:imageAttachment size:CGSizeMake(26, 26)] onNext:^(id next)
{
if (completion != nil)
completion();
}] onError:^(id error)
{
if (currentReplyPhoto != NULL)
*currentReplyPhoto = 0;
if (completion != nil)
completion();
}] isVisible:isVisible];
}
}
else if (videoAttachment != nil)
{
if (currentReplyPhoto == NULL || videoAttachment.videoId != *currentReplyPhoto)
{
if (currentReplyPhoto != NULL)
*currentReplyPhoto = videoAttachment.videoId;
[imageGroup setBackgroundImageSignal:[[[TGBridgeMediaSignals previewWithVideoAttachment:videoAttachment size:CGSizeMake(26, 26)] onNext:^(id next)
{
if (completion != nil)
completion();
}] onError:^(id error)
{
if (currentReplyPhoto != NULL)
*currentReplyPhoto = 0;
if (completion != nil)
completion();
}] isVisible:isVisible];
}
}
else
{
if (completion != nil)
completion();
}
}
+ (NSString *)stringForActionAttachment:(TGBridgeActionMediaAttachment *)actionAttachment message:(TGBridgeMessage *)message users:(NSDictionary *)users forChannel:(bool)forChannel
{
NSString *messageText = nil;
TGBridgeUser *author = (users != nil) ? users[@(message.fromUid)] : [[TGBridgeUserCache instance] userWithId:(int32_t)message.fromUid];
switch (actionAttachment.actionType)
{
case TGBridgeMessageActionChatEditTitle:
{
if (forChannel)
{
messageText = TGLocalized(@"Notification.RenamedChannel");
}
else
{
NSString *authorName = [TGStringUtils initialsForFirstName:author.firstName lastName:author.lastName single:false];
NSString *formatString = TGLocalized(@"Notification.RenamedChat");
messageText = [NSString stringWithFormat:formatString, authorName];
}
}
break;
case TGBridgeMessageActionChatEditPhoto:
{
NSString *authorName = [TGStringUtils initialsForFirstName:author.firstName lastName:author.lastName single:false];
bool changed = actionAttachment.actionData[@"photo"];
if (forChannel)
{
messageText = changed ? TGLocalized(@"Channel.MessagePhotoUpdated") : TGLocalized(@"Channel.MessagePhotoRemoved");
}
else
{
NSString *formatString = changed ? TGLocalized(@"Notification.ChangedGroupPhoto") : TGLocalized(@"Notification.RemovedGroupPhoto");
messageText = [NSString stringWithFormat:formatString, authorName];
}
}
break;
case TGBridgeMessageActionUserChangedPhoto:
{
}
break;
case TGBridgeMessageActionChatAddMember:
case TGBridgeMessageActionChatDeleteMember:
{
NSString *authorName = [TGStringUtils initialsForFirstName:author.firstName lastName:author.lastName single:false];
TGBridgeUser *user = (users != nil) ? users[actionAttachment.actionData[@"uid"]] : [[TGBridgeUserCache instance] userWithId:[actionAttachment.actionData[@"uid"] int32Value]];
if (user.identifier == author.identifier)
{
NSString *formatString = (actionAttachment.actionType == TGBridgeMessageActionChatAddMember) ? TGLocalized(@"Notification.JoinedChat") : TGLocalized(@"Notification.LeftChat");
messageText = [[NSString alloc] initWithFormat:formatString, authorName];
}
else
{
NSString *userName = [TGStringUtils initialsForFirstName:user.firstName lastName:user.lastName single:false];
NSString *formatString = (actionAttachment.actionType == TGBridgeMessageActionChatAddMember) ? TGLocalized(@"Notification.Invited") : TGLocalized(@"Notification.Kicked");
messageText = [[NSString alloc] initWithFormat:formatString, authorName, userName];
}
}
break;
case TGBridgeMessageActionJoinedByLink:
{
NSString *authorName = [TGStringUtils initialsForFirstName:author.firstName lastName:author.lastName single:false];
NSString *formatString = TGLocalizedStatic(@"Notification.JoinedGroupByLink");
messageText = [[NSString alloc] initWithFormat:formatString, authorName, actionAttachment.actionData[@"title"]];
}
break;
case TGBridgeMessageActionCreateChat:
{
NSString *authorName = [TGStringUtils initialsForFirstName:author.firstName lastName:author.lastName single:false];
NSString *formatString = TGLocalizedStatic(@"Notification.CreatedChatWithTitle");
messageText = [[NSString alloc] initWithFormat:formatString, authorName, actionAttachment.actionData[@"title"]];
}
break;
case TGBridgeMessageActionContactRegistered:
{
messageText = TGLocalized(@"Watch.Notification.Joined");
}
break;
case TGBridgeMessageActionChannelCreated:
{
messageText = TGLocalized(@"Notification.CreatedChannel");
}
break;
case TGBridgeMessageActionChannelInviter:
{
TGBridgeUser *user = (users != nil) ? users[actionAttachment.actionData[@"uid"]] : [[TGBridgeUserCache instance] userWithId:[actionAttachment.actionData[@"uid"] int32Value]];
NSString *authorName = [TGStringUtils initialsForFirstName:user.firstName lastName:user.lastName single:false];
NSString *formatString = TGLocalizedStatic(@"Notification.ChannelInviter");
messageText = [[NSString alloc] initWithFormat:formatString, authorName];
}
case TGBridgeMessageActionGroupMigratedTo:
{
messageText = TGLocalized(@"Notification.GroupMigratedToChannel");
}
break;
case TGBridgeMessageActionGroupActivated:
{
messageText = TGLocalized(@"Notification.GroupActivated");
}
break;
case TGBridgeMessageActionGroupDeactivated:
{
messageText = TGLocalized(@"Notification.GroupDeactivated");
}
break;
case TGBridgeMessageActionChannelMigratedFrom:
{
messageText = TGLocalized(@"Notification.ChannelMigratedFrom");
}
break;
default:
break;
}
return messageText;
}
+ (NSAttributedString *)attributedTextForMessage:(TGBridgeMessage *)message fontSize:(CGFloat)fontSize textColor:(UIColor *)textColor
{
NSArray *textCheckingResults = [message textCheckingResults];
NSString *messageText = message.text ?: @"";
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:messageText attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:fontSize], NSForegroundColorAttributeName: textColor }];
for (TGBridgeTextCheckingResult *result in textCheckingResults)
{
if (result.type == TGBridgeTextCheckingResultTypeBold)
[string addAttributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:fontSize weight:UIFontWeightMedium] } range:result.range];
else if (result.type == TGBridgeTextCheckingResultTypeItalic)
[string addAttributes:@{ NSFontAttributeName: [UIFont italicSystemFontOfSize:fontSize] } range:result.range];
else if (result.type == TGBridgeTextCheckingResultTypeCode || result.type == TGBridgeTextCheckingResultTypePre)
[string addAttributes:@{ NSFontAttributeName: [UIFont fontWithName:@"Courier" size:fontSize] } range:result.range];
}
return string;
}
@end
@implementation TGStickerViewModel
+ (void)updateWithMessage:(TGBridgeMessage *)message isGroup:(bool)isGroup context:(TGBridgeContext *)context currentDocumentId:(int64_t *)currentDocumentId authorLabel:(WKInterfaceLabel *)authorLabel imageGroup:(WKInterfaceGroup *)imageGroup isVisible:(bool (^)(void))isVisible completion:(void (^)(void))completion
{
[TGMessageViewModel updateAuthorLabel:authorLabel isOutgoing:message.outgoing isGroup:isGroup user:[[TGBridgeUserCache instance] userWithId:(int32_t)message.fromUid] ownUserId:context.userId];
for (TGBridgeMediaAttachment *attachment in message.media)
{
if ([attachment isKindOfClass:[TGBridgeDocumentMediaAttachment class]])
{
TGBridgeDocumentMediaAttachment *documentAttachment = (TGBridgeDocumentMediaAttachment *)attachment;
if (currentDocumentId == NULL || *currentDocumentId != documentAttachment.documentId)
{
if (currentDocumentId != NULL)
*currentDocumentId = documentAttachment.documentId;
[imageGroup setBackgroundImageSignal:[[[TGBridgeMediaSignals stickerWithDocumentAttachment:documentAttachment type:TGMediaStickerImageTypeNormal] onNext:^(id next)
{
if (completion != nil)
completion();
}] onError:^(id error)
{
if (currentDocumentId != NULL)
*currentDocumentId = 0;
if (completion != nil)
completion();
}] isVisible:isVisible];
}
break;
}
}
}
@end