WFCUMessageCell.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. //
  2. // MessageCell.m
  3. // WFChat UIKit
  4. //
  5. // Created by WF Chat on 2017/9/1.
  6. // Copyright © 2017年 WildFireChat. All rights reserved.
  7. //
  8. #import "WFCUMessageCell.h"
  9. #import "WFCUUtilities.h"
  10. #import <WFChatClient/WFCChatClient.h>
  11. #import <SDWebImage/SDWebImage.h>
  12. #import "ZCCCircleProgressView.h"
  13. #import "WFCUConfigManager.h"
  14. #import "WFCUImage.h"
  15. #define Portrait_Size 40
  16. #define SelectView_Size 20
  17. #define Name_Label_Height 14
  18. #define Name_Label_Padding 6
  19. #define Name_Client_Padding 2
  20. #define Portrait_Padding_Left 16
  21. #define Portrait_Padding_Right 16
  22. #define Portrait_Padding_Buttom 4
  23. #define Client_Arad_Buttom_Padding 8
  24. #define Client_Bubble_Top_Padding 6
  25. #define Client_Bubble_Bottom_Padding 4
  26. #define Bubble_Padding_Arraw 16
  27. #define Bubble_Padding_Another_Side 8
  28. #define MESSAGE_BASE_CELL_QUOTE_SIZE 14
  29. @interface WFCUMessageCell ()
  30. @property (nonatomic, strong)UIActivityIndicatorView *activityIndicatorView;
  31. @property (nonatomic, strong)UIImageView *failureView;
  32. @property (nonatomic, strong)UIImageView *maskView;
  33. @property (nonatomic, strong)ZCCCircleProgressView *receiptView;
  34. @property (nonatomic, strong)UIImageView *selectView;
  35. @end
  36. @implementation WFCUMessageCell
  37. + (CGFloat)clientAreaWidth {
  38. return [WFCUMessageCell bubbleWidth] - Bubble_Padding_Arraw - Bubble_Padding_Another_Side;
  39. }
  40. + (CGFloat)bubbleWidth {
  41. return [UIScreen mainScreen].bounds.size.width - Portrait_Size - Portrait_Padding_Left - Portrait_Padding_Right - Portrait_Size - Portrait_Padding_Left;
  42. }
  43. + (CGSize)sizeForCell:(WFCUMessageModel *)msgModel withViewWidth:(CGFloat)width {
  44. CGFloat height = [super hightForHeaderArea:msgModel];
  45. CGFloat portraitSize = Portrait_Size;
  46. CGFloat nameLabelHeight = Name_Label_Height + Name_Client_Padding;
  47. CGFloat clientAreaWidth = [self clientAreaWidth];
  48. CGSize clientArea = [self sizeForClientArea:msgModel withViewWidth:clientAreaWidth];
  49. CGFloat nameAndClientHeight = clientArea.height;
  50. if (msgModel.showNameLabel) {
  51. nameAndClientHeight += nameLabelHeight;
  52. }
  53. nameAndClientHeight += Client_Bubble_Top_Padding;
  54. nameAndClientHeight += Client_Bubble_Bottom_Padding;
  55. if (portraitSize + Portrait_Padding_Buttom > nameAndClientHeight) {
  56. height += portraitSize + Portrait_Padding_Buttom;
  57. } else {
  58. height += nameAndClientHeight;
  59. }
  60. height += Client_Arad_Buttom_Padding; //buttom padding
  61. height += [self sizeForQuoteArea:msgModel withViewWidth:clientAreaWidth].height;
  62. return CGSizeMake(width, height);
  63. }
  64. + (CGSize)sizeForClientArea:(WFCUMessageModel *)msgModel withViewWidth:(CGFloat)width {
  65. return CGSizeZero;
  66. }
  67. + (CGSize)sizeForQuoteArea:(WFCUMessageModel *)msgModel withViewWidth:(CGFloat)width {
  68. if ([msgModel.message.content isKindOfClass:[WFCCTextMessageContent class]]) {
  69. WFCCTextMessageContent *txtContent = (WFCCTextMessageContent *)msgModel.message.content;
  70. if (txtContent.quoteInfo) {
  71. CGFloat quoteWidth = width;
  72. NSString *quoteTxt = [NSString stringWithFormat:@"%@:%@", txtContent.quoteInfo.userDisplayName, [WFCUMessageCell quoteMessageDigest:msgModel]];
  73. CGSize size = [WFCUUtilities getTextDrawingSize:quoteTxt font:[UIFont systemFontOfSize:MESSAGE_BASE_CELL_QUOTE_SIZE] constrainedSize:CGSizeMake(quoteWidth, 44)];
  74. size.height += 12;
  75. size.width += 8;
  76. return size;
  77. }
  78. }
  79. return CGSizeZero;
  80. }
  81. + (NSString *)quoteMessageDigest:(WFCUMessageModel *)model {
  82. NSString *messageDigest;
  83. if(model.quotedMessage) {
  84. if([model.quotedMessage.content isKindOfClass:[WFCCRecallMessageContent class]]) {
  85. messageDigest = @"消息已被撤回";
  86. } else {
  87. messageDigest = [model.quotedMessage.content digest:model.quotedMessage];
  88. }
  89. } else {
  90. messageDigest = @"消息不可用,可能被删除或者过期";
  91. }
  92. return messageDigest;
  93. }
  94. - (void)updateStatus {
  95. if (self.model.message.direction == MessageDirection_Send) {
  96. if (self.model.message.status == Message_Status_Sending) {
  97. CGRect frame = self.bubbleView.frame;
  98. frame.origin.x -= 24;
  99. frame.origin.y = frame.origin.y + frame.size.height - 24;
  100. frame.size.width = 20;
  101. frame.size.height = 20;
  102. self.activityIndicatorView.hidden = NO;
  103. self.activityIndicatorView.frame = frame;
  104. [self.activityIndicatorView startAnimating];
  105. } else {
  106. [_activityIndicatorView stopAnimating];
  107. _activityIndicatorView.hidden = YES;
  108. [self updateReceiptView];
  109. }
  110. if (self.model.message.status == Message_Status_Send_Failure) {
  111. CGRect frame = self.bubbleView.frame;
  112. frame.origin.x -= 24;
  113. frame.origin.y = frame.origin.y + frame.size.height - 24;
  114. frame.size.width = 20;
  115. frame.size.height = 20;
  116. self.failureView.frame = frame;
  117. self.failureView.hidden = NO;
  118. } else {
  119. _failureView.hidden = YES;
  120. }
  121. } else {
  122. [_activityIndicatorView stopAnimating];
  123. _activityIndicatorView.hidden = YES;
  124. _failureView.hidden = YES;
  125. }
  126. }
  127. -(void)onStatusChanged:(NSNotification *)notification {
  128. if(self.model.message.messageId == [notification.object longValue]) {
  129. WFCCMessageStatus newStatus = (WFCCMessageStatus)[[notification.userInfo objectForKey:@"status"] integerValue];
  130. self.model.message.status = newStatus;
  131. [self updateStatus];
  132. } else if(self.model.message.messageUid && self.model.message.messageUid == [[notification.userInfo objectForKey:@"messageUid"] longLongValue]) {
  133. self.model.message = [[WFCCIMService sharedWFCIMService] getMessageByUid:self.model.message.messageUid];
  134. [self updateStatus];
  135. }
  136. }
  137. - (void)onUserInfoUpdated:(NSNotification *)notification {
  138. if (self.model.message.conversation.type == Channel_Type && self.model.message.direction == MessageDirection_Receive) {
  139. return;
  140. }
  141. NSArray<WFCCUserInfo *> *userInfoList = notification.userInfo[@"userInfoList"];
  142. for (WFCCUserInfo *userInfo in userInfoList) {
  143. if([userInfo.userId isEqualToString:self.model.message.fromUser]) {
  144. if (self.model.message.conversation.type == Group_Type) {
  145. WFCCUserInfo *reloadUserInfo = [[WFCCIMService sharedWFCIMService] getUserInfo:userInfo.userId inGroup:self.model.message.conversation.target refresh:NO];
  146. [self updateUserInfo:reloadUserInfo];
  147. } else {
  148. [self updateUserInfo:userInfo];
  149. }
  150. break;
  151. }
  152. }
  153. }
  154. - (void)updateChannelInfo:(WFCCChannelInfo *)channelInfo {
  155. if(self.model.message.conversation.type == Channel_Type && self.model.message.direction == MessageDirection_Receive && [self.model.message.conversation.target isEqualToString:channelInfo.channelId]) {
  156. [self.portraitView sd_setImageWithURL:[NSURL URLWithString:[channelInfo.portrait stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] placeholderImage:[WFCUImage imageNamed:@"PersonalChat"]];
  157. if(self.model.showNameLabel) {
  158. self.nameLabel.text = channelInfo.name;
  159. [self updateExternalDomainInfo];
  160. }
  161. }
  162. }
  163. - (void)updateUserInfo:(WFCCUserInfo *)userInfo {
  164. if([userInfo.userId isEqualToString:self.model.message.fromUser]) {
  165. [self.portraitView sd_setImageWithURL:[NSURL URLWithString:[userInfo.portrait stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] placeholderImage:[WFCUImage imageNamed:@"PersonalChat"]];
  166. if(self.model.showNameLabel) {
  167. NSString *nameStr = nil;
  168. if (userInfo.friendAlias.length) {
  169. nameStr = userInfo.friendAlias;
  170. } else if(userInfo.groupAlias.length) {
  171. if(userInfo.displayName.length > 0) {
  172. nameStr = [userInfo.groupAlias stringByAppendingFormat:@"(%@)", userInfo.displayName];
  173. } else {
  174. nameStr = userInfo.groupAlias;
  175. }
  176. } else if(userInfo.displayName.length > 0) {
  177. nameStr = userInfo.displayName;
  178. } else {
  179. nameStr = [NSString stringWithFormat:@"%@<%@>", WFCString(@"User"), self.model.message.fromUser];
  180. }
  181. self.nameLabel.text = nameStr;
  182. [self updateExternalDomainInfo];
  183. }
  184. }
  185. }
  186. - (void)updateExternalDomainInfo {
  187. if([WFCCUtilities isExternalTarget:self.model.message.fromUser]) {
  188. NSString *domainId = [WFCCUtilities getExternalDomain:self.model.message.fromUser];
  189. self.nameLabel.attributedText = [WFCCUtilities getExternal:domainId withName:self.nameLabel.text withColor:[WFCUConfigManager globalManager].externalNameColor withSize:10];
  190. }
  191. }
  192. - (void)setModel:(WFCUMessageModel *)model {
  193. [[NSNotificationCenter defaultCenter] removeObserver:self];
  194. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onStatusChanged:) name:kSendingMessageStatusUpdated object:nil];
  195. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onUserInfoUpdated:) name:kUserInfoUpdated object:nil];
  196. [super setModel:model];
  197. CGFloat selectViewOffset = model.selecting ? SelectView_Size + Portrait_Padding_Right : 0;
  198. if (model.message.direction == MessageDirection_Send) {
  199. CGFloat top = [WFCUMessageCellBase hightForHeaderArea:model];
  200. CGRect frame = self.frame;
  201. self.portraitView.frame = CGRectMake(frame.size.width - Portrait_Size - Portrait_Padding_Right - selectViewOffset, top, Portrait_Size, Portrait_Size);
  202. if (model.showNameLabel) {
  203. self.nameLabel.frame = CGRectMake(frame.size.width - Portrait_Size - Portrait_Padding_Right - Name_Label_Padding - 200 - selectViewOffset, top, 200, Name_Label_Height);
  204. self.nameLabel.hidden = NO;
  205. self.nameLabel.textAlignment = NSTextAlignmentRight;
  206. } else {
  207. self.nameLabel.hidden = YES;
  208. }
  209. CGSize size = [self.class sizeForClientArea:model withViewWidth:[WFCUMessageCell clientAreaWidth]];
  210. self.bubbleView.image = [WFCUImage imageNamed:@"sent_msg_background"];
  211. self.bubbleView.frame = CGRectMake(frame.size.width - Portrait_Size - Portrait_Padding_Right - Name_Label_Padding - size.width - Bubble_Padding_Arraw - Bubble_Padding_Another_Side - selectViewOffset, top + Name_Client_Padding, size.width + Bubble_Padding_Arraw + Bubble_Padding_Another_Side, size.height + Client_Bubble_Top_Padding + Client_Bubble_Bottom_Padding);
  212. self.contentArea.frame = CGRectMake(Bubble_Padding_Another_Side, Client_Bubble_Top_Padding, size.width, size.height);
  213. UIImage *image = self.bubbleView.image;
  214. self.bubbleView.image = [self.bubbleView.image
  215. resizableImageWithCapInsets:UIEdgeInsetsMake(image.size.height * 0.95, image.size.width * 0.2,image.size.height * 0.1, image.size.width * 0.05)];
  216. [self updateReceiptView];
  217. } else {
  218. CGFloat top = [WFCUMessageCellBase hightForHeaderArea:model];
  219. self.portraitView.frame = CGRectMake(Portrait_Padding_Left, top, Portrait_Size, Portrait_Size);
  220. if (model.showNameLabel) {
  221. self.nameLabel.frame = CGRectMake(Portrait_Padding_Left + Portrait_Size + Name_Label_Padding, top, 200, Name_Label_Height);
  222. self.nameLabel.hidden = NO;
  223. self.nameLabel.textAlignment = NSTextAlignmentLeft;
  224. top += Name_Label_Height + Name_Client_Padding;
  225. } else {
  226. self.nameLabel.hidden = YES;
  227. }
  228. NSString *bubbleImageName = @"received_msg_background";
  229. if (@available(iOS 13.0, *)) {
  230. if(UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
  231. bubbleImageName = @"chat_from_bg_normal_dark";
  232. }
  233. }
  234. CGSize size = [self.class sizeForClientArea:model withViewWidth:[WFCUMessageCell clientAreaWidth]];
  235. self.bubbleView.image = [WFCUImage imageNamed:bubbleImageName];
  236. self.bubbleView.frame = CGRectMake(Portrait_Padding_Left + Portrait_Size + Name_Label_Padding, top, size.width + Bubble_Padding_Arraw + Bubble_Padding_Another_Side, size.height + Client_Bubble_Top_Padding + Client_Bubble_Bottom_Padding);
  237. self.contentArea.frame = CGRectMake(Bubble_Padding_Arraw, Client_Bubble_Top_Padding, size.width, size.height);
  238. UIImage *image = self.bubbleView.image;
  239. CGFloat leftProtection = image.size.width * 0.8;
  240. CGFloat rightProtection = image.size.width * 0.2;
  241. if (self.bubbleView.frame.size.width < image.size.width) {
  242. leftProtection = 17;
  243. rightProtection = 12;
  244. }
  245. self.bubbleView.image = [self.bubbleView.image
  246. resizableImageWithCapInsets:UIEdgeInsetsMake(image.size.height * 0.8, leftProtection,
  247. image.size.height * 0.2, rightProtection)];
  248. self.receiptView.hidden = YES;
  249. }
  250. if (model.selecting) {
  251. self.selectView.hidden = NO;
  252. if (model.selected) {
  253. self.selectView.image = [WFCUImage imageNamed:@"multi_selected"];
  254. } else {
  255. self.selectView.image = [WFCUImage imageNamed:@"multi_unselected"];
  256. }
  257. CGFloat top = [WFCUMessageCellBase hightForHeaderArea:model];
  258. CGRect frame = self.selectView.frame;
  259. frame.origin.y = top;
  260. self.selectView.frame = frame;
  261. } else {
  262. self.selectView.hidden = YES;
  263. }
  264. NSString *groupId = nil;
  265. if (self.model.message.conversation.type == Group_Type) {
  266. groupId = self.model.message.conversation.target;
  267. }
  268. WFCCUserInfo *userInfo = [[WFCCIMService sharedWFCIMService] getUserInfo:model.message.fromUser inGroup:groupId refresh:NO];
  269. if(userInfo.userId.length == 0) {
  270. userInfo = [[WFCCUserInfo alloc] init];
  271. userInfo.userId = model.message.fromUser;
  272. }
  273. if (self.model.message.conversation.type == Channel_Type && self.model.message.direction == MessageDirection_Receive) {
  274. WFCCChannelInfo *channelInfo = [[WFCCIMService sharedWFCIMService] getChannelInfo:self.model.message.conversation.target refresh:NO];
  275. [self updateChannelInfo:channelInfo];
  276. } else {
  277. [self updateUserInfo:userInfo];
  278. }
  279. [self setMaskImage:self.bubbleView.image];
  280. [self updateStatus];
  281. if (model.highlighted) {
  282. UIColor *bkColor = self.backgroundColor;
  283. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  284. self.backgroundColor = [UIColor grayColor];
  285. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  286. self.backgroundColor = bkColor;
  287. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  288. self.backgroundColor = [UIColor grayColor];
  289. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  290. self.backgroundColor = bkColor;
  291. });
  292. });
  293. });
  294. });
  295. model.highlighted = NO;
  296. }
  297. self.quoteContainer.hidden = YES;
  298. if ([model.message.content isKindOfClass:[WFCCTextMessageContent class]]) {
  299. WFCCTextMessageContent *txtContent = (WFCCTextMessageContent *)model.message.content;
  300. if (txtContent.quoteInfo) {
  301. if (!self.quoteLabel) {
  302. self.quoteLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  303. self.quoteLabel.font = [UIFont systemFontOfSize:MESSAGE_BASE_CELL_QUOTE_SIZE];
  304. self.quoteLabel.numberOfLines = 0;
  305. self.quoteLabel.lineBreakMode = NSLineBreakByTruncatingTail;
  306. self.quoteLabel.layer.cornerRadius = 3.f;
  307. self.quoteLabel.layer.masksToBounds = YES;
  308. self.quoteLabel.userInteractionEnabled = YES;
  309. self.quoteLabel.textColor = [UIColor grayColor];
  310. [self.quoteLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onQuoteLabelTaped:)]];
  311. self.quoteContainer = [[UIView alloc] initWithFrame:CGRectZero];
  312. self.quoteContainer.backgroundColor = [UIColor colorWithRed:0.85 green:0.85 blue:0.85 alpha:1.f];
  313. self.quoteContainer.layer.cornerRadius = 3.f;
  314. self.quoteContainer.layer.masksToBounds = YES;
  315. [self.quoteContainer addSubview:self.quoteLabel];
  316. [self.contentView addSubview:self.quoteContainer];
  317. }
  318. CGSize size = [self.class sizeForQuoteArea:model withViewWidth:[WFCUMessageCell clientAreaWidth]];
  319. CGRect frame;
  320. if (model.message.direction == MessageDirection_Send) {
  321. frame = CGRectMake(self.frame.size.width - Portrait_Size - Portrait_Padding_Right - Name_Label_Padding - size.width - Bubble_Padding_Another_Side - selectViewOffset, self.bubbleView.frame.origin.y + self.bubbleView.frame.size.height + 4, size.width, size.height-4);
  322. } else {
  323. frame = CGRectMake(Portrait_Padding_Left + Portrait_Size + Name_Label_Padding + Bubble_Padding_Arraw, self.bubbleView.frame.origin.y + self.bubbleView.frame.size.height + 4, size.width, size.height-4);
  324. }
  325. self.quoteContainer.frame = frame;
  326. frame = self.quoteContainer.bounds;
  327. frame.size.height -= 8;
  328. frame.size.width -= 8;
  329. frame.origin.x += 4;
  330. frame.origin.y += 4;
  331. self.quoteLabel.frame = frame;
  332. self.quoteContainer.hidden = NO;
  333. self.quoteLabel.text = [NSString stringWithFormat:@"%@:%@", txtContent.quoteInfo.userDisplayName, [WFCUMessageCell quoteMessageDigest:model]];
  334. }
  335. }
  336. }
  337. - (void)updateReceiptView {
  338. WFCUMessageModel *model = self.model;
  339. if (model.message.direction == MessageDirection_Send) {
  340. if([model.message.content.class getContentFlags] == WFCCPersistFlag_PERSIST_AND_COUNT && (model.message.status == Message_Status_Sent || model.message.status == Message_Status_Readed) && [[WFCCIMService sharedWFCIMService] isReceiptEnabled] && [[WFCCIMService sharedWFCIMService] isUserEnableReceipt] && ![model.message.content isKindOfClass:[WFCCCallStartMessageContent class]]) {
  341. if (model.message.conversation.type == Single_Type) {
  342. if (model.message.serverTime <= [[model.readDict objectForKey:model.message.conversation.target] longLongValue]) {
  343. [self.receiptView setProgress:1 subProgress:1];
  344. } else if (model.message.serverTime <= [[model.deliveryDict objectForKey:model.message.conversation.target] longLongValue]) {
  345. [self.receiptView setProgress:0 subProgress:1];
  346. } else {
  347. [self.receiptView setProgress:0 subProgress:0];
  348. }
  349. if([model.message.conversation.target isEqualToString:[WFCUConfigManager globalManager].fileTransferId]) {
  350. self.receiptView.hidden = YES;
  351. } else {
  352. self.receiptView.hidden = NO;
  353. }
  354. } else if(model.message.conversation.type == SecretChat_Type) {
  355. WFCCSecretChatInfo *secretChatInfo = [[WFCCIMService sharedWFCIMService] getSecretChatInfo:model.message.conversation.target];
  356. if(secretChatInfo.targetId.length) {
  357. if (model.message.serverTime <= [[model.readDict objectForKey:secretChatInfo.userId] longLongValue]) {
  358. [self.receiptView setProgress:1 subProgress:1];
  359. } else if (model.message.serverTime <= [[model.deliveryDict objectForKey:secretChatInfo.userId] longLongValue]) {
  360. [self.receiptView setProgress:0 subProgress:1];
  361. } else {
  362. [self.receiptView setProgress:0 subProgress:0];
  363. }
  364. self.receiptView.hidden = NO;
  365. } else {
  366. self.receiptView.hidden = YES;
  367. }
  368. } else if(model.message.conversation.type == Group_Type) {
  369. long long messageTS = model.message.serverTime;
  370. WFCCGroupInfo *groupInfo = nil;
  371. if (model.deliveryRate == -1) {
  372. __block int delieveriedCount = 0;
  373. [model.deliveryDict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
  374. if ([obj longLongValue] >= messageTS) {
  375. delieveriedCount++;
  376. }
  377. }];
  378. groupInfo = [[WFCCIMService sharedWFCIMService] getGroupInfo:model.message.conversation.target refresh:NO];
  379. model.deliveryRate = (float)delieveriedCount/(groupInfo.memberCount - 1);
  380. }
  381. if (model.readRate == -1) {
  382. __block int readedCount = 0;
  383. [model.readDict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
  384. if ([obj longLongValue] >= messageTS) {
  385. readedCount++;
  386. }
  387. }];
  388. if (!groupInfo) {
  389. groupInfo = [[WFCCIMService sharedWFCIMService] getGroupInfo:model.message.conversation.target refresh:NO];
  390. }
  391. model.readRate = (float)readedCount/(groupInfo.memberCount - 1);
  392. }
  393. if (model.deliveryRate < model.readRate) {
  394. model.deliveryRate = model.readRate;
  395. }
  396. [self.receiptView setProgress:model.readRate subProgress:model.deliveryRate];
  397. self.receiptView.hidden = NO;
  398. } else {
  399. self.receiptView.hidden = YES;
  400. }
  401. } else {
  402. self.receiptView.hidden = YES;
  403. }
  404. if (self.receiptView.hidden == NO) {
  405. self.receiptView.frame = CGRectMake(self.bubbleView.frame.origin.x - 16, self.frame.size.height - 24 , 14, 14);
  406. }
  407. }
  408. }
  409. - (void)onQuoteLabelTaped:(id)sender {
  410. if ([self.delegate respondsToSelector:@selector(didTapQuoteLabel:withModel:)]) {
  411. [self.delegate didTapQuoteLabel:self withModel:self.model];
  412. }
  413. }
  414. - (void)onTapReceiptView:(id)sender {
  415. if ([self.delegate respondsToSelector:@selector(didTapReceiptView:withModel:)] && self.model.message.conversation.type == Group_Type) {
  416. [self.delegate didTapReceiptView:self withModel:self.model];
  417. }
  418. }
  419. - (void)setMaskImage:(UIImage *)maskImage{
  420. if (_maskView == nil) {
  421. _maskView = [[UIImageView alloc] initWithImage:maskImage];
  422. _maskView.frame = self.bubbleView.bounds;
  423. self.bubbleView.layer.mask = _maskView.layer;
  424. self.bubbleView.layer.masksToBounds = YES;
  425. } else {
  426. _maskView.image = maskImage;
  427. _maskView.frame = self.bubbleView.bounds;
  428. }
  429. }
  430. - (ZCCCircleProgressView *)receiptView {
  431. if (!_receiptView) {
  432. _receiptView = [[ZCCCircleProgressView alloc] initWithFrame:CGRectMake(0, 0, 14, 14)];
  433. _receiptView.hidden = YES;
  434. _receiptView.userInteractionEnabled = YES;
  435. [_receiptView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapReceiptView:)]];
  436. [self.contentView addSubview:_receiptView];
  437. }
  438. return _receiptView;
  439. }
  440. - (UIImageView *)portraitView {
  441. if (!_portraitView) {
  442. _portraitView = [[UIImageView alloc] init];
  443. _portraitView.clipsToBounds = YES;
  444. _portraitView.layer.cornerRadius = 3.f;
  445. [_portraitView setImage:[WFCUImage imageNamed:@"PersonalChat"]];
  446. [_portraitView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapPortrait:)]];
  447. [_portraitView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPressPortrait:)]];
  448. _portraitView.userInteractionEnabled=YES;
  449. [self.contentView addSubview:_portraitView];
  450. }
  451. return _portraitView;
  452. }
  453. - (void)didTapPortrait:(id)sender {
  454. [self.delegate didTapMessagePortrait:self withModel:self.model];
  455. }
  456. - (void)didLongPressPortrait:(UILongPressGestureRecognizer *)recognizer {
  457. if (recognizer.state == UIGestureRecognizerStateBegan) {
  458. [self.delegate didLongPressMessagePortrait:self withModel:self.model];
  459. }
  460. }
  461. - (UILabel *)nameLabel {
  462. if (!_nameLabel) {
  463. _nameLabel = [[UILabel alloc] init];
  464. _nameLabel.font = [UIFont systemFontOfSize:Name_Label_Height-2];
  465. _nameLabel.textColor = [UIColor grayColor];
  466. [self.contentView addSubview:_nameLabel];
  467. }
  468. return _nameLabel;
  469. }
  470. - (UIView *)contentArea {
  471. if (!_contentArea) {
  472. _contentArea = [[UIView alloc] init];
  473. [self.bubbleView addSubview:_contentArea];
  474. }
  475. return _contentArea;
  476. }
  477. - (UIImageView *)bubbleView {
  478. if (!_bubbleView) {
  479. _bubbleView = [[UIImageView alloc] init];
  480. [self.contentView addSubview:_bubbleView];
  481. [_bubbleView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressed:)]];
  482. UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(onDoubleTaped:)];
  483. doubleTapGesture.numberOfTapsRequired = 2;
  484. doubleTapGesture.numberOfTouchesRequired = 1;
  485. [_bubbleView addGestureRecognizer:doubleTapGesture];
  486. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTaped:)];
  487. [_bubbleView addGestureRecognizer:tap];
  488. [tap requireGestureRecognizerToFail:doubleTapGesture];
  489. tap.cancelsTouchesInView = NO;
  490. [_bubbleView setUserInteractionEnabled:YES];
  491. }
  492. return _bubbleView;
  493. }
  494. - (UIActivityIndicatorView *)activityIndicatorView {
  495. if (!_activityIndicatorView) {
  496. _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
  497. [self.contentView addSubview:_activityIndicatorView];
  498. }
  499. return _activityIndicatorView;
  500. }
  501. - (UIImageView *)failureView {
  502. if (!_failureView) {
  503. _failureView = [[UIImageView alloc] init];
  504. _failureView.image = [WFCUImage imageNamed:@"failure"];
  505. [_failureView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onResend:)]];
  506. [_failureView setUserInteractionEnabled:YES];
  507. [self.contentView addSubview:_failureView];
  508. }
  509. return _failureView;
  510. }
  511. - (UIImageView *)selectView {
  512. if(!_selectView) {
  513. CGFloat top = [WFCUMessageCellBase hightForHeaderArea:self.model];
  514. CGRect frame = self.frame;
  515. frame = CGRectMake(frame.size.width - SelectView_Size - Portrait_Padding_Right, top, SelectView_Size, SelectView_Size);
  516. _selectView = [[UIImageView alloc] initWithFrame:frame];
  517. _selectView.image = [WFCUImage imageNamed:@"multi_unselected"];
  518. UIGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSelect:)];
  519. [_selectView addGestureRecognizer:tap];
  520. _selectView.userInteractionEnabled = YES;
  521. [self.contentView addSubview:_selectView];
  522. }
  523. return _selectView;
  524. }
  525. - (void)onSelect:(id)sender {
  526. self.model.selected = !self.model.selected;
  527. if (self.model.selected) {
  528. self.selectView.image = [WFCUImage imageNamed:@"multi_selected"];
  529. } else {
  530. self.selectView.image = [WFCUImage imageNamed:@"multi_unselected"];
  531. }
  532. }
  533. - (void)onResend:(id)sender {
  534. [self.delegate didTapResendBtn:self.model];
  535. }
  536. - (void)dealloc {
  537. [[NSNotificationCenter defaultCenter] removeObserver:self];
  538. }
  539. @end