#import "TGNeoServiceMessageViewModel.h" #import "TGNeoLabelViewModel.h" #import "TGBridgeMessage.h" #import "TGBridgeUser.h" #import "TGChatInfo.h" #import "TGPeerIdAdapter.h" const UIEdgeInsets TGNeoServiceMessageInsets = { 2, 0, 6, 0 }; const UIEdgeInsets TGNeoChatInfoInsets = { 12, 0, 12, 0 }; @interface TGNeoServiceMessageViewModel () { TGNeoLabelViewModel *_titleModel; TGNeoLabelViewModel *_textModel; bool _chatInfo; } @end @implementation TGNeoServiceMessageViewModel - (instancetype)initWithMessage:(TGBridgeMessage *)message type:(TGNeoMessageType)type users:(NSDictionary *)users context:(TGBridgeContext *)context { self = [super initWithMessage:message type:type users:users context:context]; if (self != nil) { NSString *actionText = nil; NSArray *additionalAttributes = nil; bool isChannel = type == TGNeoMessageTypeChannel; TGBridgeUser *author = users[@(message.fromUid)]; for (TGBridgeMediaAttachment *attachment in message.media) { if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]]) { TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment; switch (actionAttachment.actionType) { case TGBridgeMessageActionChatEditTitle: { if (isChannel) { NSString *formatString = TGLocalized(@"Notification.ChannelFullTitleUpdated"); actionText = [NSString stringWithFormat:formatString, actionAttachment.actionData[@"title"]]; } else { NSString *authorName = author.displayName; NSString *formatString = TGLocalized(@"Notification.ChangedGroupName"); actionText = [NSString stringWithFormat:formatString, authorName, actionAttachment.actionData[@"title"]]; NSRange formatNameRange = [formatString rangeOfString:@"%@"]; if (formatNameRange.location != NSNotFound) { additionalAttributes = [TGNeoServiceMessageViewModel _mediumFontAttributeForRange:NSMakeRange(formatNameRange.location, authorName.length)]; } } } break; case TGBridgeMessageActionChatEditPhoto: { NSString *authorName = author.displayName; bool changed = actionAttachment.actionData[@"photo"]; if (isChannel) { actionText = changed ? TGLocalized(@"Notification.ChannelPhotoUpdated") : TGLocalized(@"Notification.ChannelPhotoRemoved"); } else { NSString *formatString = changed ? TGLocalized(@"Notification.ChangedGroupPhoto") : TGLocalized(@"Notification.RemovedGroupPhoto"); actionText = [NSString stringWithFormat:formatString, authorName]; NSRange formatNameRange = [formatString rangeOfString:@"%@"]; if (formatNameRange.location != NSNotFound) { additionalAttributes = [TGNeoServiceMessageViewModel _mediumFontAttributeForRange:NSMakeRange(formatNameRange.location, authorName.length)]; } } } break; case TGBridgeMessageActionUserChangedPhoto: break; case TGBridgeMessageActionChatAddMember: case TGBridgeMessageActionChatDeleteMember: { NSString *authorName = author.displayName; TGBridgeUser *user = users[@([actionAttachment.actionData[@"uid"] int32Value])]; if (user.identifier == author.identifier) { NSString *formatString = (actionAttachment.actionType == TGBridgeMessageActionChatAddMember) ? TGLocalized(@"Notification.JoinedChat") : TGLocalized(@"Notification.LeftChat"); actionText = [[NSString alloc] initWithFormat:formatString, authorName]; NSRange formatNameRange = [formatString rangeOfString:@"%@"]; if (formatNameRange.location != NSNotFound) { additionalAttributes = [TGNeoServiceMessageViewModel _mediumFontAttributeForRange:NSMakeRange(formatNameRange.location, authorName.length)]; } } else { NSString *userName = user.displayName; NSString *formatString = (actionAttachment.actionType == TGBridgeMessageActionChatAddMember) ? TGLocalized(@"Notification.Invited") : TGLocalized(@"Notification.Kicked"); actionText = [[NSString alloc] initWithFormat:formatString, authorName, userName]; NSRange formatNameRangeFirst = [formatString rangeOfString:@"%@"]; NSRange formatNameRangeSecond = formatNameRangeFirst.location != NSNotFound ? [formatString rangeOfString:@"%@" options:0 range:NSMakeRange(formatNameRangeFirst.location + formatNameRangeFirst.length, formatString.length - (formatNameRangeFirst.location + formatNameRangeFirst.length))] : NSMakeRange(NSNotFound, 0); if (formatNameRangeFirst.location != NSNotFound && formatNameRangeSecond.location != NSNotFound) { NSMutableArray *array = [[NSMutableArray alloc] init]; NSRange rangeFirst = NSMakeRange(formatNameRangeFirst.location, authorName.length); [array addObjectsFromArray:[TGNeoServiceMessageViewModel _mediumFontAttributeForRange:rangeFirst]]; [array addObjectsFromArray:[TGNeoServiceMessageViewModel _mediumFontAttributeForRange:NSMakeRange(rangeFirst.length - formatNameRangeFirst.length + formatNameRangeSecond.location, userName.length)]]; additionalAttributes = array; } } } break; case TGBridgeMessageActionJoinedByLink: { NSString *authorName = author.displayName; NSString *formatString = TGLocalized(@"Notification.JoinedGroupByLink"); actionText = [[NSString alloc] initWithFormat:formatString, authorName, actionAttachment.actionData[@"title"]]; NSRange formatNameRange = [formatString rangeOfString:@"%@"]; if (formatNameRange.location != NSNotFound) { additionalAttributes = [TGNeoServiceMessageViewModel _mediumFontAttributeForRange:NSMakeRange(formatNameRange.location, authorName.length)]; } } break; case TGBridgeMessageActionCreateChat: { NSString *authorName = author.displayName; NSString *formatString = TGLocalized(@"Notification.CreatedChatWithTitle"); actionText = [[NSString alloc] initWithFormat:formatString, authorName, actionAttachment.actionData[@"title"]]; NSRange formatNameRange = [formatString rangeOfString:@"%@"]; if (formatNameRange.location != NSNotFound) { additionalAttributes = [TGNeoServiceMessageViewModel _mediumFontAttributeForRange:NSMakeRange(formatNameRange.location, authorName.length)]; } } break; case TGBridgeMessageActionContactRegistered: { actionText = TGLocalized(@"Notification.Joined"); } break; case TGBridgeMessageActionChannelCreated: { actionText = TGLocalized(@"Notification.ChannelCreated"); } case TGBridgeMessageActionChannelInviter: { TGBridgeUser *user = users[@([actionAttachment.actionData[@"uid"] int32Value])]; NSString *authorName = user.displayName; NSString *formatString = TGLocalized(@"Notification.ChannelInviter"); actionText = [[NSString alloc] initWithFormat:formatString, authorName]; NSRange formatNameRange = [formatString rangeOfString:@"%@"]; if (formatNameRange.location != NSNotFound) { additionalAttributes = [TGNeoServiceMessageViewModel _mediumFontAttributeForRange:NSMakeRange(formatNameRange.location, authorName.length)]; } } break; case TGBridgeMessageActionGroupMigratedTo: { actionText = TGLocalized(@"Notification.GroupMigratedToChannel"); } break; case TGBridgeMessageActionGroupActivated: { actionText = TGLocalized(@"Notification.GroupActivated"); } break; case TGBridgeMessageActionGroupDeactivated: { actionText = TGLocalized(@"Notification.GroupDeactivated"); } break; case TGBridgeMessageActionChannelMigratedFrom: { actionText = TGLocalized(@"Notification.ChannelMigratedFrom"); } break; default: break; } } } if (actionText == nil) actionText = @""; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; style.alignment = NSTextAlignmentCenter; NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:actionText attributes:@ { NSFontAttributeName: [UIFont systemFontOfSize:12], NSForegroundColorAttributeName: [UIColor whiteColor], NSParagraphStyleAttributeName: style }]; if (additionalAttributes != nil) { NSUInteger count = additionalAttributes.count; for (NSUInteger i = 0; i < count; i += 2) { NSRange range = NSMakeRange(0, 0); [(NSValue *)[additionalAttributes objectAtIndex:i] getValue:&range]; NSDictionary *attributes = [additionalAttributes objectAtIndex:i + 1]; if (range.location + range.length <= attributedText.length) [attributedText addAttributes:attributes range:range]; } } _textModel = [[TGNeoLabelViewModel alloc] initWithAttributedText:attributedText]; _textModel.multiline = true; [self addSubmodel:_textModel]; } return self; } - (instancetype)initWithChatInfo:(TGChatInfo *)chatInfo { self = [super init]; if (self != nil) { _chatInfo = true; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; style.alignment = NSTextAlignmentCenter; NSDictionary *attributes = @{ NSParagraphStyleAttributeName: style }; _titleModel = [[TGNeoLabelViewModel alloc] initWithText:chatInfo.title font:[UIFont systemFontOfSize:12 weight:UIFontWeightSemibold] color:[UIColor whiteColor] attributes:attributes]; [self addSubmodel:_titleModel]; _textModel = [[TGNeoLabelViewModel alloc] initWithText:chatInfo.text font:[UIFont systemFontOfSize:12 weight:UIFontWeightMedium] color:[UIColor whiteColor] attributes:attributes]; [self addSubmodel:_textModel]; } return self; } - (CGSize)layoutWithContainerSize:(CGSize)containerSize { CGSize titleSize = CGSizeZero; UIEdgeInsets inset = _chatInfo ? TGNeoChatInfoInsets : TGNeoServiceMessageInsets; CGFloat textTopOffset = inset.top; if (_titleModel != nil) { titleSize = [_titleModel contentSizeWithContainerSize:CGSizeMake(containerSize.width, FLT_MAX)]; _titleModel.frame = CGRectMake((containerSize.width - titleSize.width) / 2, textTopOffset, titleSize.width, titleSize.height); textTopOffset = CGRectGetMaxY(_titleModel.frame) + 1; } CGSize textSize = [_textModel contentSizeWithContainerSize:CGSizeMake(containerSize.width, FLT_MAX)]; _textModel.frame = CGRectMake((containerSize.width - textSize.width) / 2, textTopOffset, textSize.width, textSize.height); CGSize contentSize = CGSizeMake(containerSize.width, CGRectGetMaxY(_textModel.frame) + inset.bottom); self.contentSize = contentSize; return contentSize; } + (NSArray *)_mediumFontAttributeForRange:(NSRange)range { NSDictionary *fontAttributes = @{ NSFontAttributeName: [UIFont systemFontOfSize:12.0f weight:UIFontWeightMedium], NSForegroundColorAttributeName: [UIColor whiteColor] }; return [[NSArray alloc] initWithObjects:[[NSValue alloc] initWithBytes:&range objCType:@encode(NSRange)], fontAttributes, nil]; } @end