增加换肤功能
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
* This file declares the modules used to implement the conversation unit data source.
|
||||
* The conversation unit data source (hereinafter referred to as the "data source") contains a series of information and data required for the display of the
|
||||
* conversation unit, which will be described further below. The data source also contains some business logic, such as getting and generating message overview
|
||||
* (subTitle), updating conversation information (group message or user message update) and other logic.
|
||||
*/
|
||||
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, TUIConversationOnlineStatus) {
|
||||
TUIConversationOnlineStatusUnknown = 0,
|
||||
TUIConversationOnlineStatusOnline = 1,
|
||||
TUIConversationOnlineStatusOffline = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* 【Module name】Conversation unit data source (TUIConversationCellData)
|
||||
* 【Function description】Store a series of information and data required by the conversation unit.
|
||||
* The conversation unit data source contains the following information and data:
|
||||
* 1. Conversation ID.
|
||||
* 2. Conversation type.
|
||||
* 3. Avatar URL and avatar image.
|
||||
* 4. Conversation title and information overview (subtitle).
|
||||
* 5. Conversation time (receive/send time of the latest message).
|
||||
* 6. Conversation unread count.
|
||||
* 7. Conversation top logo.
|
||||
* The data source also contains some business logic, such as getting and generating message overview (subTitle), updating conversation information (group
|
||||
* message or user message update) and other logic.
|
||||
*/
|
||||
@interface TUIConversationCellData : TUICommonCellData
|
||||
|
||||
@property(nonatomic, strong) NSString *conversationID;
|
||||
|
||||
@property(nonatomic, strong) NSString *groupID;
|
||||
|
||||
@property(nonatomic, strong) NSString *groupType;
|
||||
|
||||
@property(nonatomic, strong) NSString *userID;
|
||||
|
||||
@property(nonatomic, strong) NSString *title;
|
||||
|
||||
@property(nonatomic, strong) NSString *faceUrl;
|
||||
|
||||
@property(nonatomic, strong) UIImage *avatarImage;
|
||||
|
||||
@property(nonatomic, strong) NSString *draftText;
|
||||
|
||||
@property(nonatomic, assign) int unreadCount;
|
||||
|
||||
/**
|
||||
* Overview of conversation messages (sub title)
|
||||
* The overview is responsible for displaying the content/type of the latest message for the corresponding conversation.
|
||||
* When the latest message is a text message/system message, the content of the overview is the text content of the message
|
||||
* When the latest message is a multimedia message, the content of the overview is in the corresponding multimedia form, such as: "[Animation Expression]" /
|
||||
* "[File]" / "[Voice]" / "[Picture]" / "[Video]", etc. If there is a draft in the current conversation, the overview content is: "[Draft]XXXXX", where XXXXX is
|
||||
* the draft content.
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableAttributedString *subTitle;
|
||||
|
||||
/**
|
||||
* Group@ message tips string
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *atTipsStr;
|
||||
|
||||
/**
|
||||
* Sequence list of group-at message
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableArray<NSNumber *> *atMsgSeqs;
|
||||
|
||||
/**
|
||||
*
|
||||
* The time of the latest message
|
||||
* Recording the receive/send time of the latest message in the conversation.
|
||||
*/
|
||||
@property(nonatomic, strong) NSDate *time;
|
||||
|
||||
/**
|
||||
*
|
||||
* The flag indicating whether the session is pinned
|
||||
* YES: Conversation is pinned; NO: Conversation not pinned
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isOnTop;
|
||||
|
||||
/**
|
||||
*
|
||||
* Indicates whether to display the message checkbox
|
||||
* In the conversation list, the message checkbox is not displayed by default.
|
||||
* In the message forwarding scenario, the list cell is multiplexed to the select conversation page. When the "Multiple Choice" button is clicked, the
|
||||
* conversation list becomes multi-selectable. YES: Multiple selection is enable, multiple selection views are displayed; NO: Multiple selection is disable, the
|
||||
* default view is displayed
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL showCheckBox;
|
||||
|
||||
/**
|
||||
* Indicates whether the current message is disable selected, the default is NO
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL disableSelected;
|
||||
|
||||
/**
|
||||
* Indicates whether the current message is selected, the default is NO
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL selected;
|
||||
|
||||
/**
|
||||
* Indicates whether the cell is displayed in lite mode, the default is NO
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isLiteMode;
|
||||
|
||||
/**
|
||||
* Whether the current conversation is marked as do-not-disturb for new messages
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isNotDisturb;
|
||||
|
||||
/**
|
||||
* key by which to sort the conversation list
|
||||
*/
|
||||
@property(nonatomic, assign) NSUInteger orderKey;
|
||||
|
||||
/**
|
||||
* conversation group list
|
||||
*/
|
||||
@property(nonatomic, strong) NSArray *conversationGroupList;
|
||||
|
||||
/**
|
||||
* conversation mark list
|
||||
*/
|
||||
@property(nonatomic, strong) NSArray *conversationMarkList;
|
||||
|
||||
/**
|
||||
* The user's online status
|
||||
*/
|
||||
@property(nonatomic, assign) TUIConversationOnlineStatus onlineStatus;
|
||||
|
||||
/**
|
||||
* Conversation Mark - The current conversation is marked as unread
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isMarkAsUnread;
|
||||
|
||||
/**
|
||||
* Conversation Mark - The current conversation is marked as hidden
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isMarkAsHide;
|
||||
|
||||
/**
|
||||
* Conversation Mark - The current conversation is marked as folded
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isMarkAsFolded;
|
||||
|
||||
/**
|
||||
* Conversation Mark - Conversation folded, when there are folded conversations, a folded group will be generated locally to accommodate them, this tag is the
|
||||
* folded group tag
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isLocalConversationFoldList;
|
||||
|
||||
/**
|
||||
* Conversation collapsed subtitle: in the format "group name: last message"
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableAttributedString *foldSubTitle;
|
||||
|
||||
@property(nonatomic, strong) V2TIMMessage *lastMessage;
|
||||
@property(nonatomic, strong) V2TIMConversation *innerConversation;
|
||||
|
||||
+ (BOOL)isMarkedByHideType:(NSArray *)markList;
|
||||
|
||||
+ (BOOL)isMarkedByUnReadType:(NSArray *)markList;
|
||||
|
||||
+ (BOOL)isMarkedByFoldType:(NSArray *)markList;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// TUIConversationCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/16.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationCellData.h"
|
||||
|
||||
@implementation TUIConversationCellData
|
||||
|
||||
@synthesize title;
|
||||
@synthesize userID;
|
||||
@synthesize groupID;
|
||||
@synthesize groupType;
|
||||
@synthesize avatarImage;
|
||||
@synthesize conversationID;
|
||||
@synthesize draftText;
|
||||
@synthesize faceUrl;
|
||||
|
||||
- (CGFloat)heightOfWidth:(CGFloat)width {
|
||||
if (self.isLiteMode) {
|
||||
return TConversationCell_Height_LiteMode;
|
||||
} else {
|
||||
return TConversationCell_Height;
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)isMarkedByHideType:(NSArray *)markList {
|
||||
for (NSNumber *num in markList) {
|
||||
if (num.unsignedLongValue == V2TIM_CONVERSATION_MARK_TYPE_HIDE) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)isMarkedByUnReadType:(NSArray *)markList {
|
||||
for (NSNumber *num in markList) {
|
||||
if (num.unsignedLongValue == V2TIM_CONVERSATION_MARK_TYPE_UNREAD) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)isMarkedByFoldType:(NSArray *)markList {
|
||||
for (NSNumber *num in markList) {
|
||||
if (num.unsignedLongValue == V2TIM_CONVERSATION_MARK_TYPE_FOLD) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
87
TUIKit/TUIConversation/BaseCell/CellUI/TUIConversationCell.h
Normal file
87
TUIKit/TUIConversation/BaseCell/CellUI/TUIConversationCell.h
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
#import "TUIConversationCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
*
|
||||
* [Function description] In the message list interface, display the preview information of a single conversation.
|
||||
* The session information displayed in the session unit includes:
|
||||
* 1. Avatar information (user avatar/group avatar)
|
||||
* 2. Session title (user nickname/group name)
|
||||
* 3. Conversation message overview (displays the latest message content)
|
||||
* 4. Number of unread messages (if there are unread messages)
|
||||
* 5. Session time (receipt/sent time of the latest message)
|
||||
*/
|
||||
@interface TUIConversationCell : UITableViewCell
|
||||
|
||||
/**
|
||||
* Image view for displaying avatar
|
||||
* When conversation type is one-to-one, the image view will display user's avatar
|
||||
* When conversation type is group chat, the image view will display group's avatar
|
||||
*/
|
||||
@property(nonatomic, strong) UIImageView *headImageView;
|
||||
|
||||
/**
|
||||
* Title of conversation
|
||||
* When conversation type is one-to-one, the title is the friend's remarks, and if the corresponding friend has no remarks, the friend's ID is displayed.
|
||||
* When the conversation is a group chat, the title is the group name.
|
||||
*/
|
||||
@property(nonatomic, strong) UILabel *titleLabel;
|
||||
|
||||
/**
|
||||
* Overview of conversation messages (sub title)
|
||||
* The overview is responsible for displaying the content/type of the latest message for the corresponding conversation.
|
||||
* When the latest message is a text message/system message, the content of the overview is the text content of the message
|
||||
* When the latest message is a multimedia message, the content of the overview is in the corresponding multimedia form, such as: "[Animation Expression]" /
|
||||
* "[File]" / "[Voice]" / "[Picture]" / "[Video]", etc. If there is a draft in the current conversation, the overview content is: "[Draft]XXXXX", where XXXXX is
|
||||
* the draft content.
|
||||
*/
|
||||
@property(nonatomic, strong) UILabel *subTitleLabel;
|
||||
|
||||
/**
|
||||
* The label for displaying time
|
||||
* Responsible for displaying the received/sent time of the latest message in the conversation unit.
|
||||
*/
|
||||
@property(nonatomic, strong) UILabel *timeLabel;
|
||||
|
||||
/**
|
||||
* The red dot view that identifies whether the current conversation is set to do not disturb
|
||||
*/
|
||||
@property(nonatomic, strong) UIView *notDisturbRedDot;
|
||||
|
||||
/**
|
||||
* The image view that identifies whether the current conversation is set to do not disturb
|
||||
*/
|
||||
@property(nonatomic, strong) UIImageView *notDisturbView;
|
||||
|
||||
/**
|
||||
* A view indicating whether the current conversation has unread messages
|
||||
* If the current conversation has unread messages, the unread count is displayed to the right of the conversation unit.
|
||||
*/
|
||||
@property(nonatomic, strong) TUIUnReadView *unReadView;
|
||||
|
||||
/**
|
||||
* Icon indicating whether the conversation is selected
|
||||
* In the multi-selection scenario, it is used to identify whether the conversation is selected
|
||||
*/
|
||||
@property(nonatomic, strong) UIImageView *selectedIcon;
|
||||
|
||||
/**
|
||||
* The icon of the user's online status
|
||||
*/
|
||||
@property(nonatomic, strong) UIImageView *onlineStatusIcon;
|
||||
|
||||
@property(nonatomic, strong) UIImageView *lastMessageStatusImageView;
|
||||
|
||||
@property(atomic, strong) TUIConversationCellData *convData;
|
||||
|
||||
- (void)fillWithData:(TUIConversationCellData *)convData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
479
TUIKit/TUIConversation/BaseCell/CellUI/TUIConversationCell.m
Normal file
479
TUIKit/TUIConversation/BaseCell/CellUI/TUIConversationCell.m
Normal file
@@ -0,0 +1,479 @@
|
||||
//
|
||||
// TUIConversationCell.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/16.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationCell.h"
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import <TUICore/TUITool.h>
|
||||
#import "TUIConversationConfig.h"
|
||||
|
||||
#define kScale UIScreen.mainScreen.bounds.size.width / 375.0
|
||||
|
||||
@implementation TUIConversationCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
// self.contentView.backgroundColor = TUIConversationDynamicColor(@"conversation_cell_bg_color", @"#FFFFFF");
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.contentView.backgroundColor = [UIColor clearColor];
|
||||
_headImageView = [[UIImageView alloc] init];
|
||||
[self.contentView addSubview:_headImageView];
|
||||
|
||||
_timeLabel = [[UILabel alloc] init];
|
||||
_timeLabel.font = [UIFont systemFontOfSize:12];
|
||||
_timeLabel.textColor = TIMCommonDynamicColor(@"form_desc_color", @"#999999");
|
||||
_timeLabel.layer.masksToBounds = YES;
|
||||
[_timeLabel setRtlAlignment:TUITextRTLAlignmentLeading];
|
||||
[self.contentView addSubview:_timeLabel];
|
||||
|
||||
_titleLabel = [[UILabel alloc] init];
|
||||
_titleLabel.font = [UIFont boldSystemFontOfSize:16];
|
||||
_titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#333333");
|
||||
_titleLabel.layer.masksToBounds = YES;
|
||||
[_titleLabel setRtlAlignment:TUITextRTLAlignmentLeading];
|
||||
[self.contentView addSubview:_titleLabel];
|
||||
|
||||
_unReadView = [[TUIUnReadView alloc] init];
|
||||
[self.contentView addSubview:_unReadView];
|
||||
|
||||
_subTitleLabel = [[UILabel alloc] init];
|
||||
_subTitleLabel.layer.masksToBounds = YES;
|
||||
_subTitleLabel.font = [UIFont systemFontOfSize:12];
|
||||
_subTitleLabel.textColor = TIMCommonDynamicColor(@"form_subtitle_color", @"#666666");
|
||||
[_subTitleLabel setRtlAlignment:TUITextRTLAlignmentLeading];
|
||||
[self.contentView addSubview:_subTitleLabel];
|
||||
|
||||
_notDisturbRedDot = [[UIView alloc] init];
|
||||
_notDisturbRedDot.backgroundColor = [UIColor redColor];
|
||||
_notDisturbRedDot.layer.cornerRadius = TConversationCell_Margin_Disturb_Dot / 2.0;
|
||||
_notDisturbRedDot.layer.masksToBounds = YES;
|
||||
[self.contentView addSubview:_notDisturbRedDot];
|
||||
|
||||
_notDisturbView = [[UIImageView alloc] init];
|
||||
[self.contentView addSubview:_notDisturbView];
|
||||
|
||||
[self setSeparatorInset:UIEdgeInsetsMake(0, TConversationCell_Margin, 0, 0)];
|
||||
|
||||
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
|
||||
//[self setSelectionStyle:UITableViewCellSelectionStyleDefault];
|
||||
|
||||
// selectedIcon
|
||||
_selectedIcon = [[UIImageView alloc] init];
|
||||
[self.contentView addSubview:_selectedIcon];
|
||||
|
||||
_onlineStatusIcon = [[UIImageView alloc] init];
|
||||
[self.contentView addSubview:_onlineStatusIcon];
|
||||
|
||||
_lastMessageStatusImageView = [[UIImageView alloc] init];
|
||||
[self.contentView addSubview:_lastMessageStatusImageView];
|
||||
_lastMessageStatusImageView.hidden = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)fillWithData:(TUIConversationCellData *)convData {
|
||||
self.convData = convData;
|
||||
|
||||
|
||||
self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
|
||||
if ([TUIConversationConfig sharedConfig].cellTitleLabelFont) {
|
||||
self.titleLabel.font = [TUIConversationConfig sharedConfig].cellTitleLabelFont;
|
||||
}
|
||||
|
||||
self.subTitleLabel.attributedText = convData.subTitle;
|
||||
if ([TUIConversationConfig sharedConfig].cellSubtitleLabelFont) {
|
||||
self.subTitleLabel.font = [TUIConversationConfig sharedConfig].cellSubtitleLabelFont;
|
||||
}
|
||||
|
||||
self.timeLabel.text = [TUITool convertDateToStr:convData.time];
|
||||
if ([TUIConversationConfig sharedConfig].cellTimeLabelFont) {
|
||||
self.timeLabel.font = [TUIConversationConfig sharedConfig].cellTimeLabelFont;
|
||||
}
|
||||
|
||||
if (self.convData.showCheckBox) {
|
||||
_selectedIcon.hidden = NO;
|
||||
} else {
|
||||
_selectedIcon.hidden = YES;
|
||||
}
|
||||
[self configRedPoint:convData];
|
||||
|
||||
if (convData.isOnTop) {
|
||||
self.contentView.backgroundColor = TUIConversationDynamicColor(@"conversation_cell_top_bg_color", @"#F4F4F4");
|
||||
} else {
|
||||
self.contentView.backgroundColor = TUIConversationDynamicColor(@"conversation_cell_bg_color", @"#FFFFFF");
|
||||
;
|
||||
}
|
||||
|
||||
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
|
||||
self.headImageView.layer.masksToBounds = YES;
|
||||
self.headImageView.layer.cornerRadius = self.headImageView.frame.size.height / 2;
|
||||
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
|
||||
self.headImageView.layer.masksToBounds = YES;
|
||||
self.headImageView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
|
||||
}
|
||||
|
||||
@weakify(self);
|
||||
[[[RACObserve(convData, title) takeUntil:self.rac_prepareForReuseSignal] distinctUntilChanged] subscribeNext:^(NSString *x) {
|
||||
@strongify(self);
|
||||
self.titleLabel.text = x;
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}];
|
||||
|
||||
/**
|
||||
*
|
||||
* Setup default avatar
|
||||
*/
|
||||
if (convData.groupID.length > 0) {
|
||||
/**
|
||||
* ,
|
||||
* If it is a group, change the group default avatar to the last used avatar
|
||||
*/
|
||||
UIImage *avatar = nil;
|
||||
if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
|
||||
NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", convData.groupID];
|
||||
NSInteger member = [NSUserDefaults.standardUserDefaults integerForKey:key];
|
||||
avatar = [TUIGroupAvatar getCacheAvatarForGroup:convData.groupID number:(UInt32)member];
|
||||
}
|
||||
convData.avatarImage = avatar ? avatar : DefaultGroupAvatarImageByGroupType(convData.groupType);
|
||||
}
|
||||
|
||||
[[RACObserve(convData, faceUrl) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSString *faceUrl) {
|
||||
@strongify(self);
|
||||
if (self.convData.groupID.length > 0) {
|
||||
/**
|
||||
*
|
||||
* Group avatar
|
||||
*/
|
||||
if (IS_NOT_EMPTY_NSSTRING(faceUrl)) {
|
||||
/**
|
||||
* The group avatar has been manually set externally
|
||||
*/
|
||||
[self.headImageView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:self.convData.avatarImage];
|
||||
} else {
|
||||
/**
|
||||
* The group avatar has not been set externally. If the synthetic avatar is allowed, the synthetic avatar will be used; otherwise, the default
|
||||
* avatar will be used.
|
||||
*/
|
||||
if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
|
||||
/**
|
||||
* If the synthetic avatar is allowed, the synthetic avatar will be used
|
||||
* 1. Asynchronously obtain the cached synthetic avatar according to the number of group members
|
||||
* 2. If the cache is hit, use the cached synthetic avatar directly
|
||||
* 3. If the cache is not hit, recompose a new avatar
|
||||
*
|
||||
* Note:
|
||||
* 1. Since "asynchronously obtaining cached avatars" and "synthesizing avatars" take a long time, it is easy to cause cell reuse problems, so
|
||||
* it is necessary to confirm whether to assign values directly according to groupID.
|
||||
* 2. Use SDWebImage to implement placeholder, because SDWebImage has already dealt with the problem of cell reuse
|
||||
*/
|
||||
|
||||
// 1. Obtain group avatar from cache
|
||||
|
||||
// fix: The getCacheGroupAvatar needs to request the
|
||||
// network. When the network is disconnected, since the headImageView is not set, the current conversation sends a message, the conversation
|
||||
// is moved up, and the avatar of the first conversation is reused, resulting in confusion of the avatar.
|
||||
[self.headImageView sd_setImageWithURL:nil placeholderImage:convData.avatarImage];
|
||||
[TUIGroupAvatar
|
||||
getCacheGroupAvatar:convData.groupID
|
||||
callback:^(UIImage *avatar, NSString *groupID) {
|
||||
@strongify(self);
|
||||
if ([groupID isEqualToString:self.convData.groupID]) {
|
||||
// 1.1 When the callback is invoked, the cell is not reused
|
||||
|
||||
if (avatar != nil) {
|
||||
// 2. Hit the cache and assign directly
|
||||
[self.headImageView sd_setImageWithURL:nil placeholderImage:avatar];
|
||||
} else {
|
||||
// 3. Synthesize new avatars asynchronously without hitting cache
|
||||
|
||||
[self.headImageView sd_setImageWithURL:nil placeholderImage:convData.avatarImage];
|
||||
[TUIGroupAvatar
|
||||
fetchGroupAvatars:convData.groupID
|
||||
placeholder:convData.avatarImage
|
||||
callback:^(BOOL success, UIImage *image, NSString *groupID) {
|
||||
@strongify(self);
|
||||
if ([groupID isEqualToString:self.convData.groupID]) {
|
||||
// callback ,cell
|
||||
// When the callback is invoked, the cell is not reused
|
||||
[self.headImageView
|
||||
sd_setImageWithURL:nil
|
||||
placeholderImage:success ? image
|
||||
: DefaultGroupAvatarImageByGroupType(self.convData.groupType)];
|
||||
} else {
|
||||
//When the callback is invoked, the cell has been reused to other groupIDs.
|
||||
// Since a new callback will be triggered when the new groupID synthesizes new avatar, it is
|
||||
// ignored here
|
||||
}
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
// 1.2 When the callback is invoked, the cell has been reused to other groupIDs. Since a new callback will be triggered
|
||||
// when the new groupID gets the cache, it is ignored here
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
/**
|
||||
* Synthetic avatars are not allowed, use the default avatar directly
|
||||
*/
|
||||
[self.headImageView sd_setImageWithURL:nil placeholderImage:convData.avatarImage];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Personal avatar
|
||||
*/
|
||||
[self.headImageView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:self.convData.avatarImage];
|
||||
}
|
||||
}];
|
||||
|
||||
if (convData.showCheckBox) {
|
||||
NSString *imageName = nil;
|
||||
if (convData.disableSelected) {
|
||||
imageName = TIMCommonImagePath(@"icon_select_selected_disable");
|
||||
} else if (convData.selected) {
|
||||
imageName = TIMCommonImagePath(@"icon_select_selected");
|
||||
} else {
|
||||
imageName = TIMCommonImagePath(@"icon_select_normal");
|
||||
}
|
||||
self.selectedIcon.image = [UIImage imageNamed:imageName];
|
||||
}
|
||||
|
||||
[self configOnlineStatusIcon:convData];
|
||||
|
||||
[self configDisplayLastMessageStatusImage:convData];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
|
||||
}
|
||||
|
||||
- (void)configDisplayLastMessageStatusImage:(TUIConversationCellData *)convData {
|
||||
UIImage *image = [self getDisplayLastMessageStatusImage:convData];
|
||||
self.lastMessageStatusImageView.image = image;
|
||||
}
|
||||
|
||||
- (UIImage *)getDisplayLastMessageStatusImage:(TUIConversationCellData *)convData {
|
||||
UIImage *image = nil;
|
||||
if (!convData.draftText && (V2TIM_MSG_STATUS_SENDING == convData.lastMessage.status || V2TIM_MSG_STATUS_SEND_FAIL == convData.lastMessage.status)) {
|
||||
if (V2TIM_MSG_STATUS_SENDING == convData.lastMessage.status) {
|
||||
image = [UIImage imageNamed:TUIConversationImagePath(@"msg_sending_for_conv")];
|
||||
} else {
|
||||
image = [UIImage imageNamed:TUIConversationImagePath(@"msg_error_for_conv")];
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
- (void)configOnlineStatusIcon:(TUIConversationCellData *)convData {
|
||||
@weakify(self);
|
||||
[[RACObserve(TUIConfig.defaultConfig, displayOnlineStatusIcon) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(id _Nullable x) {
|
||||
@strongify(self);
|
||||
if (convData.onlineStatus == TUIConversationOnlineStatusOnline && TUIConfig.defaultConfig.displayOnlineStatusIcon) {
|
||||
self.onlineStatusIcon.hidden = NO;
|
||||
self.onlineStatusIcon.image = TIMCommonDynamicImage(@"icon_online_status", [UIImage imageNamed:TIMCommonImagePath(@"icon_online_status")]);
|
||||
} else if (convData.onlineStatus == TUIConversationOnlineStatusOffline && TUIConfig.defaultConfig.displayOnlineStatusIcon) {
|
||||
self.onlineStatusIcon.hidden = NO;
|
||||
self.onlineStatusIcon.image = TIMCommonDynamicImage(@"icon_offline_status", [UIImage imageNamed:TIMCommonImagePath(@"icon_offline_status")]);
|
||||
} else {
|
||||
self.onlineStatusIcon.hidden = YES;
|
||||
self.onlineStatusIcon.image = nil;
|
||||
}
|
||||
}];
|
||||
}
|
||||
- (void)configRedPoint:(TUIConversationCellData *)convData {
|
||||
if (convData.isNotDisturb) {
|
||||
if (0 == convData.unreadCount) {
|
||||
self.notDisturbRedDot.hidden = YES;
|
||||
} else {
|
||||
self.notDisturbRedDot.hidden = NO;
|
||||
}
|
||||
self.notDisturbView.hidden = NO;
|
||||
self.unReadView.hidden = YES;
|
||||
UIImage *image = TUIConversationBundleThemeImage(@"conversation_message_not_disturb_img", @"message_not_disturb");
|
||||
[self.notDisturbView setImage:image];
|
||||
} else {
|
||||
self.notDisturbRedDot.hidden = YES;
|
||||
self.notDisturbView.hidden = YES;
|
||||
[self.unReadView setNum:convData.unreadCount];
|
||||
self.unReadView.hidden = convData.unreadCount == 0 ? YES : ![TUIConversationConfig sharedConfig].showCellUnreadCount;
|
||||
}
|
||||
|
||||
// Mark As Unread
|
||||
if (convData.isMarkAsUnread) {
|
||||
// When marked as unread, don't care about 'unreadCount', you need to display red dot/number 1 according to whether do not disturb or not
|
||||
if (convData.isNotDisturb) {
|
||||
// Displays a red dot when marked as unread and do not disturb
|
||||
self.notDisturbRedDot.hidden = NO;
|
||||
} else {
|
||||
// Marked unread Show number 1
|
||||
[self.unReadView setNum:1];
|
||||
self.unReadView.hidden = ![TUIConversationConfig sharedConfig].showCellUnreadCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Collapsed group chat No need for Do Not Disturb icon
|
||||
if (convData.isLocalConversationFoldList) {
|
||||
self.notDisturbView.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)requiresConstraintBasedLayout {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// this is Apple's recommended place for adding/updating constraints
|
||||
- (void)updateConstraints {
|
||||
|
||||
[super updateConstraints];
|
||||
CGFloat height = [self.convData heightOfWidth:self.mm_w];
|
||||
self.mm_h = height;
|
||||
|
||||
if (self.convData.isOnTop) {
|
||||
self.contentView.backgroundColor = [TUIConversationConfig sharedConfig].pinnedCellBackgroundColor ? : TUIConversationDynamicColor(@"conversation_cell_top_bg_color", @"#F4F4F4");
|
||||
} else {
|
||||
self.contentView.backgroundColor = [TUIConversationConfig sharedConfig].cellBackgroundColor ? : TUIConversationDynamicColor(@"conversation_cell_bg_color", @"#FFFFFF");
|
||||
}
|
||||
|
||||
CGFloat selectedIconSize = 20;
|
||||
[self.selectedIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
if (self.convData.showCheckBox) {
|
||||
make.width.height.mas_equalTo(selectedIconSize);
|
||||
make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(10);
|
||||
make.centerY.mas_equalTo(self.contentView.mas_centerY);
|
||||
}
|
||||
}];
|
||||
MASAttachKeys(self.selectedIcon);
|
||||
|
||||
CGFloat imgHeight = 50;
|
||||
[self.headImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.size.mas_equalTo(imgHeight);
|
||||
make.centerY.mas_equalTo(self.contentView.mas_centerY);
|
||||
if (self.convData.showCheckBox) {
|
||||
make.leading.mas_equalTo(self.selectedIcon.mas_trailing).mas_offset(TConversationCell_Margin + 3);
|
||||
}
|
||||
else {
|
||||
make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(TConversationCell_Margin + 3);
|
||||
}
|
||||
}];
|
||||
MASAttachKeys(self.headImageView);
|
||||
|
||||
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
|
||||
self.headImageView.layer.masksToBounds = YES;
|
||||
self.headImageView.layer.cornerRadius = imgHeight / 2;
|
||||
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
|
||||
self.headImageView.layer.masksToBounds = YES;
|
||||
self.headImageView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
|
||||
}
|
||||
|
||||
CGFloat titleLabelHeight = 24;
|
||||
if (self.convData.isLiteMode) {
|
||||
[self.titleLabel sizeToFit];
|
||||
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_greaterThanOrEqualTo(120);
|
||||
make.height.mas_greaterThanOrEqualTo(titleLabelHeight);
|
||||
make.top.mas_equalTo((height - titleLabelHeight) / 2);
|
||||
make.leading.mas_equalTo(self.headImageView.mas_trailing).mas_offset(TConversationCell_Margin);
|
||||
make.trailing.mas_equalTo(self.contentView).mas_offset(- 2*TConversationCell_Margin_Text);
|
||||
}];
|
||||
self.timeLabel.hidden = YES;
|
||||
self.lastMessageStatusImageView.hidden = YES;
|
||||
self.subTitleLabel.hidden = YES;
|
||||
self.unReadView.hidden = YES;
|
||||
self.notDisturbRedDot.hidden = YES;
|
||||
self.notDisturbView.hidden = YES;
|
||||
self.onlineStatusIcon.hidden = YES;
|
||||
} else {
|
||||
[self.timeLabel sizeToFit];
|
||||
[self.timeLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo(self.timeLabel);
|
||||
make.height.mas_greaterThanOrEqualTo(self.timeLabel.font.lineHeight);
|
||||
make.top.mas_equalTo(self.contentView.mas_top).mas_offset(TConversationCell_Margin_Disturb);
|
||||
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(- TConversationCell_Margin_Disturb);
|
||||
}];
|
||||
MASAttachKeys(self.timeLabel);
|
||||
|
||||
[self.lastMessageStatusImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo(kScale390(14));
|
||||
make.height.mas_equalTo(14);
|
||||
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(- (kScale390(1) + TConversationCell_Margin_Disturb + kScale390(8)));
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom).mas_offset(kScale390(16));
|
||||
}];
|
||||
MASAttachKeys(self.lastMessageStatusImageView);
|
||||
|
||||
[self.titleLabel sizeToFit];
|
||||
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.mas_greaterThanOrEqualTo(titleLabelHeight);
|
||||
make.top.mas_equalTo(self.contentView.mas_top).mas_offset(12);
|
||||
make.leading.mas_equalTo(self.headImageView.mas_trailing).mas_offset(10);
|
||||
make.trailing.mas_lessThanOrEqualTo(self.timeLabel.mas_trailing).mas_offset(- 2*TConversationCell_Margin_Text);
|
||||
}];
|
||||
MASAttachKeys(self.titleLabel);
|
||||
[self.subTitleLabel sizeToFit];
|
||||
[self.subTitleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.mas_greaterThanOrEqualTo(self.subTitleLabel);
|
||||
make.bottom.mas_equalTo(self.contentView).mas_offset(- 16);
|
||||
make.leading.mas_equalTo(self.titleLabel);
|
||||
make.trailing.mas_equalTo(self.contentView).mas_offset(- 2*TConversationCell_Margin_Text);
|
||||
}];
|
||||
MASAttachKeys(self.subTitleLabel);
|
||||
|
||||
[self.unReadView.unReadLabel sizeToFit];
|
||||
[self.unReadView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.trailing.mas_equalTo(self.timeLabel.mas_trailing);
|
||||
make.centerY.mas_equalTo(self.subTitleLabel);
|
||||
make.width.mas_equalTo(kScale375(20));
|
||||
make.height.mas_equalTo(kScale375(20));
|
||||
}];
|
||||
MASAttachKeys(self.unReadView);
|
||||
|
||||
[self.unReadView.unReadLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.center.mas_equalTo(self.unReadView);
|
||||
make.size.mas_equalTo(self.unReadView.unReadLabel);
|
||||
}];
|
||||
self.unReadView.layer.cornerRadius = kScale375(10);
|
||||
[self.unReadView.layer masksToBounds];
|
||||
|
||||
[self.notDisturbRedDot mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.trailing.mas_equalTo(self.headImageView.mas_trailing).mas_offset(3);
|
||||
make.top.mas_equalTo(self.headImageView.mas_top).mas_offset(1);
|
||||
make.width.height.mas_equalTo(TConversationCell_Margin_Disturb_Dot);
|
||||
}];
|
||||
MASAttachKeys(self.notDisturbRedDot);
|
||||
|
||||
[self.notDisturbView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.height.mas_equalTo(TConversationCell_Margin_Disturb);
|
||||
make.trailing.mas_equalTo(self.timeLabel.mas_trailing);
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom).mas_offset(-15);
|
||||
}];
|
||||
|
||||
[self.onlineStatusIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.height.mas_equalTo(kScale375(15));
|
||||
make.leading.mas_equalTo(self.headImageView.mas_trailing).mas_offset(-kScale375(15));
|
||||
make.bottom.mas_equalTo(self.headImageView.mas_bottom).mas_offset(-kScale375(1));
|
||||
}];
|
||||
self.onlineStatusIcon.layer.cornerRadius = 0.5 * kScale375(15);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user