增加换肤功能

This commit is contained in:
启星
2025-08-14 10:07:49 +08:00
parent f6964c1e89
commit 4f9318d98e
8789 changed files with 978530 additions and 2 deletions

View File

@@ -0,0 +1,35 @@
//
// TCommonFriendCellData.h
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/7.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <TIMCommon/TIMCommonModel.h>
NS_ASSUME_NONNULL_BEGIN
@class V2TIMFriendInfo;
@class V2TIMGroupInfo;
typedef NS_ENUM(NSInteger, TUIContactOnlineStatus) { TUIContactOnlineStatusUnknown = 0, TUIContactOnlineStatusOnline = 1, TUIContactOnlineStatusOffline = 2 };
@interface TUICommonContactCellData : TUICommonCellData
- (instancetype)initWithFriend:(V2TIMFriendInfo *)args;
- (instancetype)initWithGroupInfo:(V2TIMGroupInfo *)args;
@property V2TIMFriendInfo *friendProfile;
@property NSString *identifier;
@property NSURL *avatarUrl;
@property NSString *title;
@property UIImage *avatarImage;
// The flag of indicating the user's online status
@property(nonatomic, assign) TUIContactOnlineStatus onlineStatus;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,51 @@
//
// TCommonFriendCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/7.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUICommonContactCellData.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
@implementation TUICommonContactCellData {
V2TIMFriendInfo *_friendProfile;
}
- (instancetype)initWithFriend:(V2TIMFriendInfo *)args {
self = [super init];
if (args.friendRemark.length) {
_title = args.friendRemark;
} else {
_title = [args.userFullInfo showName];
}
_identifier = args.userID;
_avatarUrl = [NSURL URLWithString:args.userFullInfo.faceURL];
_friendProfile = args;
return self;
}
- (instancetype)initWithGroupInfo:(V2TIMGroupInfo *)args {
self = [super init];
_title = args.groupName;
_avatarImage = DefaultGroupAvatarImageByGroupType(args.groupType);
_avatarUrl = [NSURL URLWithString:args.faceURL];
_identifier = args.groupID;
return self;
}
- (NSComparisonResult)compare:(TUICommonContactCellData *)data {
return [self.title localizedCompare:data.title];
}
- (CGFloat)heightOfWidth:(CGFloat)width {
return 56;
}
@end

View File

@@ -0,0 +1,45 @@
//
// TCommonPendencyCellData.h
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/7.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
@class V2TIMFriendApplication;
NS_ASSUME_NONNULL_BEGIN
@interface TUICommonPendencyCellData : TUICommonCellData
@property V2TIMFriendApplication *application;
@property NSString *identifier;
@property NSURL *avatarUrl;
@property NSString *title;
@property NSString *addSource;
@property NSString *addWording;
@property BOOL isAccepted;
@property BOOL isRejected;
@property SEL cbuttonSelector;
@property SEL cRejectButtonSelector;
@property BOOL hideSource;
- (instancetype)initWithPendency:(V2TIMFriendApplication *)application;
typedef void (^TUICommonPendencyCellDataSuccessCallback)(void);
typedef void (^TUICommonPendencyCellDataFailureCallback)(int code, NSString *msg);
- (void)agreeWithSuccess:(TUICommonPendencyCellDataSuccessCallback)success
failure:(TUICommonPendencyCellDataFailureCallback)failure;
- (void)rejectWithSuccess:(TUICommonPendencyCellDataSuccessCallback)success
failure:(TUICommonPendencyCellDataFailureCallback)failure;
- (void)agree;
- (void)reject;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,91 @@
//
// TCommonPendencyCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/7.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUICommonPendencyCellData.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TUICore/TUIGlobalization.h>
#import <TUICore/TUITool.h>
#import <TUICore/UIView+TUIToast.h>
@implementation TUICommonPendencyCellData
- (instancetype)initWithPendency:(V2TIMFriendApplication *)application {
self = [super init];
_identifier = application.userID;
if (application.nickName.length > 0) {
_title = application.nickName;
} else {
_title = _identifier;
}
if (application.addSource) {
_addSource = [NSString
stringWithFormat:TIMCommonLocalizableString(TUIKitAddFriendSourceFormat), [application.addSource substringFromIndex:@"AddSource_Type_".length]];
}
_addWording = application.addWording;
_avatarUrl = [NSURL URLWithString:application.faceUrl];
_isAccepted = NO;
_application = application;
_hideSource = NO;
return self;
}
- (BOOL)isEqual:(TUICommonPendencyCellData *)object {
return [self.identifier isEqual:object.identifier];
}
- (void)agree {
[self agreeWithSuccess:^{
//Success
} failure:^(int code, NSString * _Nonnull msg) {
//failure
}];
}
- (void)reject {
[self rejectWithSuccess:^{
//Success
} failure:^(int code, NSString * _Nonnull msg) {
//failure
}];
}
- (void)agreeWithSuccess:(TUICommonPendencyCellDataSuccessCallback)success failure:(TUICommonPendencyCellDataFailureCallback)failure {
[[V2TIMManager sharedInstance] acceptFriendApplication:_application
type:V2TIM_FRIEND_ACCEPT_AGREE_AND_ADD
succ:^(V2TIMFriendOperationResult *result) {
if (success) {
success();
}
[TUITool makeToast:TIMCommonLocalizableString(TUIKitFriendApplicationApproved)];
}
fail:^(int code, NSString *msg) {
[TUITool makeToastError:code msg:msg];
if (failure) {
failure(code,msg);
}
}];
}
- (void)rejectWithSuccess:(TUICommonPendencyCellDataSuccessCallback)success failure:(TUICommonPendencyCellDataFailureCallback)failure {
[[V2TIMManager sharedInstance] refuseFriendApplication:_application
succ:^(V2TIMFriendOperationResult *result) {
if (success) {
success();
}
[TUITool makeToast:TIMCommonLocalizableString(TUIKitFirendRequestRejected)];
}
fail:^(int code, NSString *msg) {
if (failure) {
failure(code,msg);
}
[TUITool makeToastError:code msg:msg];
}];
}
@end

View File

@@ -0,0 +1,22 @@
//
// TUIContactActionCellData.h
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/6/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUIContactActionCellData : TUICommonCellData
@property NSString *title;
@property UIImage *icon;
@property NSInteger readNum;
@property (nonatomic, copy, nullable) void (^onClicked)(NSDictionary * _Nullable param);
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,13 @@
//
// TUIContactActionCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/6/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIContactActionCellData.h"
@implementation TUIContactActionCellData
@end

View File

@@ -0,0 +1,87 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This document 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
@interface TUIContactConversationCellData : 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;
/**
* Conversation Messages Overview (subtitle)
* 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 the name of 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;
/**
* seq list of group@ messages
*/
@property(nonatomic, strong) NSMutableArray<NSNumber *> *atMsgSeqs;
/**
* Latest message time
* Save the receive/send time of the latest message in the conversation.
*/
@property(nonatomic, strong) NSDate *time;
/**
* The flag that whether the conversation is pinned to the top
*/
@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 selected, the default is NO
*/
@property(nonatomic, assign) BOOL selected;
/**
* 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;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,30 @@
//
// TUIContactConversationCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/16.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIContactConversationCellData.h"
@implementation TUIContactConversationCellData
@synthesize title;
@synthesize userID;
@synthesize groupID;
@synthesize groupType;
@synthesize avatarImage;
@synthesize conversationID;
@synthesize draftText;
@synthesize faceUrl;
- (CGFloat)heightOfWidth:(CGFloat)width {
return TConversationCell_Height;
}
- (BOOL)isEqual:(TUIContactConversationCellData *)object {
return [self.conversationID isEqual:object.conversationID];
}
@end

View File

@@ -0,0 +1,44 @@
//
// TUIFindContactCellModel.h
// TUIContact
//
// Created by harvy on 2021/12/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
@class V2TIMUserFullInfo;
@class V2TIMGroupInfo;
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, TUIFindContactType) {
TUIFindContactTypeC2C = 1,
TUIFindContactTypeGroup = 2,
};
@class TUIFindContactCellModel;
typedef void (^TUIFindContactOnCallback)(TUIFindContactCellModel *);
@interface TUIFindContactCellModel : NSObject
@property(nonatomic, assign) TUIFindContactType type;
@property(nonatomic, strong) UIImage *avatar;
@property(nonatomic, strong) NSURL *avatarUrl;
@property(nonatomic, copy) NSString *mainTitle;
@property(nonatomic, copy) NSString *subTitle;
@property(nonatomic, copy) NSString *desc;
/**
* c2c-> userID, group -> groupID
* If the conversation type is c2c, contactID represents userid; if the conversation type is group, contactID represents groupID
*/
@property(nonatomic, copy) NSString *contactID;
@property(nonatomic, strong) V2TIMUserFullInfo *userInfo;
@property(nonatomic, strong) V2TIMGroupInfo *groupInfo;
@property(nonatomic, copy) TUIFindContactOnCallback onClick;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,13 @@
//
// TUIFindContactCellModel.m
// TUIContact
//
// Created by harvy on 2021/12/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIFindContactCellModel.h"
@implementation TUIFindContactCellModel
@end

View File

@@ -0,0 +1,27 @@
//
// TUIMemberInfoCellData.h
// TUIGroup
//
// Created by harvy on 2021/12/27.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, TUIMemberInfoCellStyle) { TUIMemberInfoCellStyleNormal = 0, TUIMemberInfoCellStyleAdd = 1 };
@interface TUIMemberInfoCellData : NSObject
@property(nonatomic, copy) NSString *identifier;
@property(nonatomic, strong) UIImage *avatar;
@property(nonatomic, copy) NSString *avatarUrl;
@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) TUIMemberInfoCellStyle style;
@property(nonatomic, assign) NSInteger role;
@property(nonatomic, assign) BOOL showAccessory;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,13 @@
//
// TUIMemberInfoCellData.m
// TUIGroup
//
// Created by harvy on 2021/12/27.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMemberInfoCellData.h"
@implementation TUIMemberInfoCellData
@end

View File

@@ -0,0 +1,29 @@
//
// TUICommonContactCell.h
//
//
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <UIKit/UIKit.h>
#import "TUICommonContactCellData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUICommonContactCell : TUICommonTableViewCell
@property UIImageView *avatarView;
@property UILabel *titleLabel;
// The icon of indicating the user's online status
@property(nonatomic, strong) UIImageView *onlineStatusIcon;
@property(readonly) TUICommonContactCellData *contactData;
- (void)fillWithData:(TUICommonContactCellData *)contactData;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,114 @@
//
// TCommonContactCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUICommonContactCell.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import "TUICommonContactCellData.h"
#define kScale UIScreen.mainScreen.bounds.size.width / 375.0
@interface TUICommonContactCell ()
@property TUICommonContactCellData *contactData;
@end
@implementation TUICommonContactCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.contentView.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
self.avatarView = [[UIImageView alloc] initWithImage:DefaultAvatarImage];
[self.contentView addSubview:self.avatarView];
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.titleLabel];
self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
self.onlineStatusIcon = [[UIImageView alloc] init];
[self.contentView addSubview:self.onlineStatusIcon];
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
self.changeColorWhenTouched = YES;
}
return self;
}
- (void)fillWithData:(TUICommonContactCellData *)contactData {
[super fillWithData:contactData];
self.contactData = contactData;
self.titleLabel.text = contactData.title;
[self.avatarView sd_setImageWithURL:contactData.avatarUrl placeholderImage:contactData.avatarImage ?: DefaultAvatarImage];
@weakify(self);
[[RACObserve(TUIConfig.defaultConfig, displayOnlineStatusIcon) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(id _Nullable x) {
@strongify(self);
if (contactData.onlineStatus == TUIContactOnlineStatusOnline && TUIConfig.defaultConfig.displayOnlineStatusIcon) {
self.onlineStatusIcon.hidden = NO;
self.onlineStatusIcon.image = TIMCommonDynamicImage(@"icon_online_status", [UIImage imageNamed:TIMCommonImagePath(@"icon_online_status")]);
} else if (contactData.onlineStatus == TUIContactOnlineStatusOffline && 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;
}
}];
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGFloat imgWidth = kScale390(34);
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(imgWidth);
make.centerY.mas_equalTo(self.contentView.mas_centerY);
make.leading.mas_equalTo(kScale390(12));
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = imgWidth / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.avatarView.mas_centerY);
make.leading.mas_equalTo(self.avatarView.mas_trailing).mas_offset(12);
make.height.mas_equalTo(20);
make.trailing.mas_greaterThanOrEqualTo(self.contentView.mas_trailing);
}];
[self.onlineStatusIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(kScale375(15));
make.trailing.mas_equalTo(self.avatarView.mas_trailing).mas_offset(kScale375(5));
make.bottom.mas_equalTo(self.avatarView.mas_bottom);
}];
self.onlineStatusIcon.layer.cornerRadius = 0.5 * kScale375(15);
}
@end

View File

@@ -0,0 +1,46 @@
//
// TUICommonContactProfileCardCell.m
//
//
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <UIKit/UIKit.h>
@class TUICommonContactProfileCardCell;
@protocol TUIContactProfileCardDelegate <NSObject>
- (void)didTapOnAvatar:(TUICommonContactProfileCardCell *)cell;
@end
@interface TUICommonContactProfileCardCellData : TUICommonCellData
@property(nonatomic, strong) UIImage *avatarImage;
@property(nonatomic, strong) NSURL *avatarUrl;
@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSString *identifier;
@property(nonatomic, strong) NSString *signature;
@property(nonatomic, strong) UIImage *genderIconImage;
@property(nonatomic, strong) NSString *genderString;
@property BOOL showAccessory;
@property BOOL showSignature;
@end
@interface TUICommonContactProfileCardCell : TUICommonTableViewCell
@property(nonatomic, strong) UIImageView *avatar;
@property(nonatomic, strong) UILabel *name;
@property(nonatomic, strong) UILabel *identifier;
@property(nonatomic, strong) UILabel *signature;
@property(nonatomic, strong) UIImageView *genderIcon;
@property(nonatomic, strong) TUICommonContactProfileCardCellData *cardData;
@property(nonatomic, weak) id<TUIContactProfileCardDelegate> delegate;
- (void)fillWithData:(TUICommonContactProfileCardCellData *)data;
@end

View File

@@ -0,0 +1,213 @@
//
// TUIContactProfileCardCell.m
// UIKit
//
// Created by annidy on 2019/5/27.
// Copyright © 2019 Tencent. All rights reserved.
//
#import "TUICommonContactProfileCardCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
@implementation TUICommonContactProfileCardCellData
- (instancetype)init {
self = [super init];
if (self) {
_avatarImage = DefaultAvatarImage;
if ([_genderString isEqualToString:TIMCommonLocalizableString(Male)]) {
_genderIconImage = TUIContactCommonBundleImage(@"male");
} else if ([_genderString isEqualToString:TIMCommonLocalizableString(Female)]) {
_genderIconImage = TUIContactCommonBundleImage(@"female");
} else {
_genderIconImage = nil;
}
}
return self;
}
- (CGFloat)heightOfWidth:(CGFloat)width {
return TPersonalCommonCell_Image_Size.height + 2 * TPersonalCommonCell_Margin + (self.showSignature ? 24 : 0);
}
@end
@implementation TUICommonContactProfileCardCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setupViews];
}
return self;
}
- (void)setupViews {
CGSize headSize = TPersonalCommonCell_Image_Size;
_avatar = [[UIImageView alloc] initWithFrame:CGRectMake(TPersonalCommonCell_Margin, TPersonalCommonCell_Margin, headSize.width, headSize.height)];
_avatar.contentMode = UIViewContentModeScaleAspectFit;
_avatar.layer.cornerRadius = 4;
_avatar.layer.masksToBounds = YES;
UITapGestureRecognizer *tapAvatar = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapAvatar)];
[_avatar addGestureRecognizer:tapAvatar];
_avatar.userInteractionEnabled = YES;
[self.contentView addSubview:_avatar];
// CGSize genderIconSize = CGSizeMake(20, 20);
_genderIcon = [[UIImageView alloc] init];
_genderIcon.contentMode = UIViewContentModeScaleAspectFit;
_genderIcon.image = self.cardData.genderIconImage;
[self.contentView addSubview:_genderIcon];
_name = [[UILabel alloc] init];
[_name setFont:[UIFont boldSystemFontOfSize:18]];
[_name setTextColor:TIMCommonDynamicColor(@"form_title_color", @"#000000")];
[self.contentView addSubview:_name];
_identifier = [[UILabel alloc] init];
[_identifier setFont:[UIFont systemFontOfSize:13]];
[_identifier setTextColor:TIMCommonDynamicColor(@"form_subtitle_color", @"#888888")];
[self.contentView addSubview:_identifier];
_signature = [[UILabel alloc] init];
[_signature setFont:[UIFont systemFontOfSize:14]];
[_signature setTextColor:TIMCommonDynamicColor(@"form_subtitle_color", @"#888888")];
[self.contentView addSubview:_signature];
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
- (void)fillWithData:(TUICommonContactProfileCardCellData *)data {
[super fillWithData:data];
self.cardData = data;
_signature.hidden = !data.showSignature;
// set data
@weakify(self);
RAC(_signature, text) = [RACObserve(data, signature) takeUntil:self.rac_prepareForReuseSignal];
[[[RACObserve(data, identifier) takeUntil:self.rac_prepareForReuseSignal] distinctUntilChanged] subscribeNext:^(NSString *x) {
@strongify(self);
self.identifier.text = [@"ID: " stringByAppendingString:data.identifier];
}];
[[[RACObserve(data, name) takeUntil:self.rac_prepareForReuseSignal] distinctUntilChanged] subscribeNext:^(NSString *x) {
@strongify(self);
self.name.text = x;
}];
[[RACObserve(data, avatarUrl) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSURL *x) {
@strongify(self);
[self.avatar sd_setImageWithURL:x placeholderImage:self.cardData.avatarImage];
}];
[[RACObserve(data, genderString) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSString *x) {
@strongify(self);
if ([x isEqualToString:TIMCommonLocalizableString(Male)]) {
self.genderIcon.image = TUIContactCommonBundleImage(@"male");
} else if ([x isEqualToString:TIMCommonLocalizableString(Female)]) {
self.genderIcon.image = TUIContactCommonBundleImage(@"female");
} else {
self.genderIcon.image = nil;
}
}];
if (data.showAccessory) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
self.accessoryType = UITableViewCellAccessoryNone;
}
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGSize headSize = TPersonalCommonCell_Image_Size;
[self.avatar mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(headSize);
make.top.mas_equalTo(TPersonalCommonCell_Margin);
make.leading.mas_equalTo(TPersonalCommonCell_Margin);
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatar.layer.masksToBounds = YES;
self.avatar.layer.cornerRadius = headSize.height / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatar.layer.masksToBounds = YES;
self.avatar.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.name sizeToFit];
[self.name mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(TPersonalCommonCell_Margin);
make.leading.mas_equalTo(self.avatar.mas_trailing).mas_offset(15);
make.width.mas_lessThanOrEqualTo(self.name.frame.size.width);
make.height.mas_greaterThanOrEqualTo(self.name.frame.size.height);
make.trailing.mas_lessThanOrEqualTo(self.genderIcon.mas_leading).mas_offset(- 1);
}];
[self.genderIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(self.name.font.pointSize *0.9);
make.centerY.mas_equalTo(self.name);
make.leading.mas_equalTo(self.name.mas_trailing).mas_offset(1);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(- 10);
}];
[self.identifier sizeToFit];
[self.identifier mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.name);
make.top.mas_equalTo(self.name.mas_bottom).mas_offset(5);
if(self.identifier.frame.size.width > 80) {
make.width.mas_greaterThanOrEqualTo(self.identifier.frame.size.width);
}
else {
make.width.mas_greaterThanOrEqualTo(@80);
}
make.height.mas_greaterThanOrEqualTo(self.identifier.frame.size.height);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(-1);
}];
if (self.cardData.showSignature) {
[self.signature sizeToFit];
[self.signature mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.name);
make.top.mas_equalTo(self.identifier.mas_bottom).mas_offset(5);
if(self.signature.frame.size.width > 80) {
make.width.mas_greaterThanOrEqualTo(self.signature.frame.size.width);
}
else {
make.width.mas_greaterThanOrEqualTo(@80);
}
make.height.mas_greaterThanOrEqualTo(self.signature.frame.size.height);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(-1);
}];
} else {
self.signature.frame = CGRectZero;
}
}
- (void)layoutSubviews {
[super layoutSubviews];
}
- (void)onTapAvatar {
if (_delegate && [_delegate respondsToSelector:@selector(didTapOnAvatar:)]) [_delegate didTapOnAvatar:self];
}
@end

View File

@@ -0,0 +1,25 @@
//
// TUICommonContactSelectCell.h
//
//
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUICommonContactSelectCell : TUICommonTableViewCell
@property UIButton *selectButton;
@property UIImageView *avatarView;
@property UILabel *titleLabel;
@property(readonly) TUICommonContactSelectCellData *selectData;
- (void)fillWithData:(TUICommonContactSelectCellData *)selectData;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,120 @@
//
// TUICommonContactSelectCell.m
//
//
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import "TUICommonContactSelectCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
@interface TUICommonContactSelectCell ()
@property TUICommonContactSelectCellData *selectData;
@end
@implementation TUICommonContactSelectCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.selectButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.contentView addSubview:self.selectButton];
[self.selectButton setImage:[UIImage imageNamed:TIMCommonImagePath(@"icon_select_normal")] forState:UIControlStateNormal];
[self.selectButton setImage:[UIImage imageNamed:TIMCommonImagePath(@"icon_select_pressed")] forState:UIControlStateHighlighted];
[self.selectButton setImage:[UIImage imageNamed:TIMCommonImagePath(@"icon_select_selected")] forState:UIControlStateSelected];
[self.selectButton setImage:[UIImage imageNamed:TIMCommonImagePath(@"icon_select_selected_disable")] forState:UIControlStateDisabled];
self.selectButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
self.avatarView = [[UIImageView alloc] initWithImage:DefaultAvatarImage];
[self.contentView addSubview:self.avatarView];
self.avatarView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.titleLabel];
self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
self.titleLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
return self;
}
- (void)fillWithData:(TUICommonContactSelectCellData *)selectData {
[super fillWithData:selectData];
self.selectData = selectData;
self.titleLabel.text = selectData.title;
if (selectData.avatarUrl) {
[self.avatarView sd_setImageWithURL:selectData.avatarUrl placeholderImage:DefaultAvatarImage];
} else if (selectData.avatarImage) {
[self.avatarView setImage:selectData.avatarImage];
} else {
[self.avatarView setImage:DefaultAvatarImage];
}
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = self.avatarView.frame.size.height / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.selectButton setSelected:selectData.isSelected];
self.selectButton.enabled = selectData.enabled;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGFloat imgWidth = kScale390(34);
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(imgWidth);
make.centerY.mas_equalTo(self.contentView.mas_centerY);
make.leading.mas_equalTo(kScale390(12));
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = imgWidth / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.avatarView.mas_centerY);
make.leading.mas_equalTo(self.avatarView.mas_trailing).mas_offset(12);
make.height.mas_equalTo(20);
make.trailing.mas_greaterThanOrEqualTo(self.contentView.mas_trailing);
}];
[self.selectButton mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.contentView.mas_centerY);
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-kScale390(20));
make.width.height.mas_equalTo(20);
}];
}
@end

View File

@@ -0,0 +1,37 @@
//
// TUIContactCommonSwitchCell.h
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/10.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUICommonContactSwitchCellData : TUICommonCellData
@property NSString *title;
@property NSString *desc;
@property(getter=isOn) BOOL on;
@property CGFloat margin;
@property SEL cswitchSelector;
@end
@interface TUICommonContactSwitchCell : TUICommonTableViewCell
@property UILabel *titleLabel; // main title label
@property UILabel *descLabel; // detail title label below the main title label, used for explaining details
@property UISwitch *switcher;
@property(readonly) TUICommonContactSwitchCellData *switchData;
- (void)fillWithData:(TUICommonContactSwitchCellData *)data;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,141 @@
//
// TUIContactCommonSwitchCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/10.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUICommonContactSwitchCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
@implementation TUICommonContactSwitchCellData
- (instancetype)init {
self = [super init];
_margin = 20;
return self;
}
- (CGFloat)heightOfWidth:(CGFloat)width {
CGFloat height = [super heightOfWidth:width];
if (self.desc.length > 0) {
NSString *str = self.desc;
NSDictionary *attribute = @{NSFontAttributeName : [UIFont systemFontOfSize:12]};
CGSize size = [str boundingRectWithSize:CGSizeMake(264, 999)
options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:attribute
context:nil]
.size;
height += size.height + 10;
}
return height;
}
@end
@interface TUICommonContactSwitchCell ()
@property TUICommonContactSwitchCellData *switchData;
@end
@implementation TUICommonContactSwitchCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = TIMCommonDynamicColor(@"form_key_text_color", @"#444444");
_titleLabel.font = [UIFont systemFontOfSize:16];
_titleLabel.rtlAlignment = TUITextRTLAlignmentLeading;
[self.contentView addSubview:_titleLabel];
_descLabel = [[UILabel alloc] init];
_descLabel.textColor = TIMCommonDynamicColor(@"group_modify_desc_color", @"#888888");
_descLabel.font = [UIFont systemFontOfSize:12];
_descLabel.numberOfLines = 0;
_descLabel.rtlAlignment = TUITextRTLAlignmentLeading;
_descLabel.hidden = YES;
[self.contentView addSubview:_descLabel];
_switcher = [[UISwitch alloc] init];
// Change the color when the switch is on to blue
_switcher.onTintColor = TIMCommonDynamicColor(@"common_switch_on_color", @"#147AFF");
self.accessoryView = _switcher;
[self.contentView addSubview:_switcher];
[_switcher addTarget:self action:@selector(switchClick) forControlEvents:UIControlEventValueChanged];
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
return self;
}
- (void)fillWithData:(TUICommonContactSwitchCellData *)switchData {
[super fillWithData:switchData];
self.switchData = switchData;
_titleLabel.text = switchData.title;
[_switcher setOn:switchData.isOn];
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
if (self.switchData.desc.length > 0) {
_descLabel.text = self.switchData.desc;
_descLabel.hidden = NO;
NSString *str = self.switchData.desc;
NSDictionary *attribute = @{NSFontAttributeName : [UIFont systemFontOfSize:12]};
CGSize size = [str boundingRectWithSize:CGSizeMake(264, 999)
options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:attribute
context:nil]
.size;
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(size.width);
make.height.mas_equalTo(24);
make.leading.mas_equalTo(self.switchData.margin);
make.top.mas_equalTo(12);
}];
[self.descLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(size.width);
make.height.mas_equalTo(size.height);
make.leading.mas_equalTo(self.titleLabel.mas_leading);
make.top.mas_equalTo(self.titleLabel.mas_bottom).mas_offset(2);
}];
} else {
[self.titleLabel sizeToFit];
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(self.titleLabel.frame.size);
make.leading.mas_equalTo(self.switchData.margin);
make.centerY.mas_equalTo(self.contentView);
}];
}
}
- (void)switchClick {
if (self.switchData.cswitchSelector) {
UIViewController *vc = self.mm_viewController;
if ([vc respondsToSelector:self.switchData.cswitchSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[vc performSelector:self.switchData.cswitchSelector withObject:self];
#pragma clang diagnostic pop
}
}
}
@end

View File

@@ -0,0 +1,42 @@
//
// TUIContactCommonTextCell.h
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUICommonContactTextCellData : TUICommonCellData
@property NSString *key;
@property NSString *value;
@property BOOL showAccessory;
@property UIColor *keyColor;
@property UIColor *valueColor;
/**
* valueLabel
* Allow valueLabel to be displayed on multiple lines
*/
@property BOOL enableMultiLineValue;
@property(nonatomic, assign) UIEdgeInsets keyEdgeInsets;
@end
@interface TUICommonContactTextCell : TUICommonTableViewCell
@property UILabel *keyLabel;
@property UILabel *valueLabel;
@property(readonly) TUICommonContactTextCellData *textData;
- (void)fillWithData:(TUICommonContactTextCellData *)data;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,132 @@
//
// TUIContactCommonTextCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUICommonContactTextCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
@implementation TUICommonContactTextCellData
- (instancetype)init {
self = [super init];
self.keyEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0);
return self;
}
- (CGFloat)heightOfWidth:(CGFloat)width {
CGFloat height = [super heightOfWidth:width];
if (self.enableMultiLineValue) {
NSString *str = self.value;
NSDictionary *attribute = @{NSFontAttributeName : [UIFont systemFontOfSize:16]};
CGSize size = [str boundingRectWithSize:CGSizeMake(280, 999)
options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:attribute
context:nil]
.size;
height = size.height + 30;
}
return height;
}
@end
@interface TUICommonContactTextCell ()
@property TUICommonContactTextCellData *textData;
@end
@implementation TUICommonContactTextCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]) {
self.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
self.contentView.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
_keyLabel = [[UILabel alloc] init];
_keyLabel.textColor = TIMCommonDynamicColor(@"form_key_text_color", @"#444444");
_keyLabel.font = [UIFont systemFontOfSize:16.0];
[self.contentView addSubview:_keyLabel];
[_keyLabel setRtlAlignment:TUITextRTLAlignmentTrailing];
_valueLabel = [[UILabel alloc] init];
[self.contentView addSubview:_valueLabel];
_valueLabel.textColor = TIMCommonDynamicColor(@"form_value_text_color", @"#000000");
_valueLabel.font = [UIFont systemFontOfSize:16.0];
[_valueLabel setRtlAlignment:TUITextRTLAlignmentTrailing];
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
return self;
}
- (void)fillWithData:(TUICommonContactTextCellData *)textData {
[super fillWithData:textData];
self.textData = textData;
RAC(_keyLabel, text) = [RACObserve(textData, key) takeUntil:self.rac_prepareForReuseSignal];
RAC(_valueLabel, text) = [RACObserve(textData, value) takeUntil:self.rac_prepareForReuseSignal];
if (textData.showAccessory) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
self.accessoryType = UITableViewCellAccessoryNone;
}
if (self.textData.keyColor) {
self.keyLabel.textColor = self.textData.keyColor;
}
if (self.textData.valueColor) {
self.valueLabel.textColor = self.textData.valueColor;
}
if (self.textData.enableMultiLineValue) {
self.valueLabel.numberOfLines = 0;
} else {
self.valueLabel.numberOfLines = 1;
}
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
[self.keyLabel sizeToFit];
[self.keyLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(self.keyLabel.frame.size);
make.leading.mas_equalTo(self.contentView).mas_offset(self.textData.keyEdgeInsets.left);
make.centerY.mas_equalTo(self.contentView);
}];
[self.valueLabel sizeToFit];
[self.valueLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.keyLabel.mas_trailing).mas_offset(10);
if (self.textData.showAccessory) {
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-10);
}
else {
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-20);
}
make.centerY.mas_equalTo(self.contentView);
}];
}
@end

View File

@@ -0,0 +1,31 @@
//
// TCommonPendencyCell.h
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/7.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import "TUICommonPendencyCellData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUICommonPendencyCell : TUICommonTableViewCell
@property UIImageView *avatarView;
@property UILabel *titleLabel;
@property UILabel *addSourceLabel;
@property UILabel *addWordingLabel;
@property UIButton *agreeButton;
@property UIButton *rejectButton;
@property UIStackView *stackView;
@property(nonatomic) TUICommonPendencyCellData *pendencyData;
- (void)fillWithData:(TUICommonPendencyCellData *)pendencyData;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,216 @@
//
// TCommonPendencyCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/7.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUICommonPendencyCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
@implementation TUICommonPendencyCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
self.avatarView = [[UIImageView alloc] initWithImage:DefaultAvatarImage];
[self.contentView addSubview:self.avatarView];
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.titleLabel];
self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
self.addSourceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.addSourceLabel];
self.addSourceLabel.textColor = [UIColor d_systemGrayColor];
self.addSourceLabel.font = [UIFont systemFontOfSize:15];
self.addWordingLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.addWordingLabel];
self.addWordingLabel.textColor = [UIColor d_systemGrayColor];
self.addWordingLabel.font = [UIFont systemFontOfSize:15];
self.agreeButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.agreeButton setTitleColor:TIMCommonDynamicColor(@"form_title_color", @"#000000") forState:UIControlStateNormal];
[self.agreeButton addTarget:self action:@selector(agreeClick) forControlEvents:UIControlEventTouchUpInside];
self.rejectButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.rejectButton setTitleColor:TIMCommonDynamicColor(@"form_title_color", @"#000000") forState:UIControlStateNormal];
[self.rejectButton addTarget:self action:@selector(rejectClick) forControlEvents:UIControlEventTouchUpInside];
UIStackView *stackView = [[UIStackView alloc] init];
[stackView addSubview:self.agreeButton];
[stackView addSubview:self.rejectButton];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.alignment = UIStackViewAlignmentCenter;
[stackView sizeToFit];
self.stackView = stackView;
self.accessoryView = stackView;
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)fillWithData:(TUICommonPendencyCellData *)pendencyData {
[super fillWithData:pendencyData];
self.pendencyData = pendencyData;
self.titleLabel.text = pendencyData.title;
self.addSourceLabel.text = pendencyData.addSource;
self.addWordingLabel.text = pendencyData.addWording;
self.avatarView.image = DefaultAvatarImage;
if (pendencyData.avatarUrl) {
[self.avatarView sd_setImageWithURL:pendencyData.avatarUrl];
}
if (pendencyData.isAccepted) {
[self.agreeButton setTitle:TIMCommonLocalizableString(Agreed) forState:UIControlStateNormal];
self.agreeButton.enabled = NO;
self.agreeButton.layer.borderColor = [UIColor clearColor].CGColor;
[self.agreeButton setTitleColor:TIMCommonDynamicColor(@"form_title_color", @"#000000") forState:UIControlStateNormal];
self.agreeButton.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
} else {
[self.agreeButton setTitle:TIMCommonLocalizableString(Agree) forState:UIControlStateNormal];
self.agreeButton.enabled = YES;
self.agreeButton.layer.borderColor = [UIColor clearColor].CGColor;
self.agreeButton.layer.borderWidth = 1;
[self.agreeButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.agreeButton.backgroundColor = TIMCommonDynamicColor(@"primary_theme_color", @"#147AFF");
}
if (pendencyData.isRejected) {
[self.rejectButton setTitle:TIMCommonLocalizableString(Disclined) forState:UIControlStateNormal];
self.rejectButton.enabled = NO;
self.rejectButton.layer.borderColor = [UIColor clearColor].CGColor;
[self.rejectButton setTitleColor:TIMCommonDynamicColor(@"form_title_color", @"#000000") forState:UIControlStateNormal];
} else {
[self.rejectButton setTitle:TIMCommonLocalizableString(Discline) forState:UIControlStateNormal];
self.rejectButton.enabled = YES;
self.rejectButton.layer.borderColor = TUIDemoDynamicColor(@"separator_color", @"#DBDBDB").CGColor;
self.rejectButton.layer.borderWidth = 0.2;
[self.rejectButton setTitleColor:TIMCommonDynamicColor(@"primary_theme_color", @"#147AFF") forState:UIControlStateNormal];
}
if (self.pendencyData.isRejected && !self.pendencyData.isAccepted) {
self.agreeButton.hidden = YES;
self.rejectButton.hidden = NO;
} else if (self.pendencyData.isAccepted && !self.pendencyData.isRejected) {
self.agreeButton.hidden = NO;
self.rejectButton.hidden = YES;
} else {
self.agreeButton.hidden = NO;
self.rejectButton.hidden = NO;
}
self.addSourceLabel.hidden = self.pendencyData.hideSource;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGSize headSize = CGSizeMake(70, 70);
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(headSize);
make.leading.mas_equalTo(12);
make.centerY.mas_equalTo(self.contentView);
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = headSize.height / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.contentView.mas_top).mas_offset(14);
make.leading.mas_equalTo(self.avatarView.mas_trailing).mas_offset(12);
make.height.mas_equalTo(20);
make.width.mas_equalTo(120);
}];
[self.addSourceLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.titleLabel.mas_bottom).mas_offset(6);
make.leading.mas_equalTo(self.titleLabel.mas_leading);
make.height.mas_equalTo(15);
make.width.mas_equalTo(120);
}];
[self.addWordingLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.addSourceLabel.mas_bottom).mas_offset(6);
make.leading.mas_equalTo(self.titleLabel.mas_leading);
make.height.mas_equalTo(15);
make.width.mas_equalTo(120);
}];
[self.agreeButton sizeToFit];
[self.agreeButton mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.stackView.mas_leading);
make.centerY.mas_equalTo(self.stackView);
make.height.mas_equalTo(self.agreeButton.frame.size.height);
make.width.mas_equalTo(self.agreeButton.frame.size.width + 20);
}];
[self.rejectButton sizeToFit];
[self.rejectButton mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.agreeButton.mas_trailing).mas_offset(10);
make.height.mas_equalTo(self.rejectButton.frame.size.height);
make.width.mas_equalTo(self.rejectButton.frame.size.width + 20);
}];
self.stackView.bounds = CGRectMake(0, 0, 3 * self.agreeButton.mm_w + 10, self.agreeButton.mm_h);
}
- (void)agreeClick {
if (self.pendencyData.cbuttonSelector) {
UIViewController *vc = self.mm_viewController;
if ([vc respondsToSelector:self.pendencyData.cbuttonSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[vc performSelector:self.pendencyData.cbuttonSelector withObject:self];
#pragma clang diagnostic pop
}
}
}
- (void)rejectClick {
if (self.pendencyData.cRejectButtonSelector) {
UIViewController *vc = self.mm_viewController;
if ([vc respondsToSelector:self.pendencyData.cRejectButtonSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[vc performSelector:self.pendencyData.cRejectButtonSelector withObject:self];
#pragma clang diagnostic pop
}
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ((touch.view == self.agreeButton)) {
return NO;
} else if (touch.view == self.rejectButton) {
return NO;
}
return YES;
}
@end

View File

@@ -0,0 +1,28 @@
//
// TUIContactActionCell.h
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/6/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <UIKit/UIKit.h>
#import "TUIContactActionCellData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUIContactActionCell : TUICommonTableViewCell
@property UIImageView *avatarView;
@property UILabel *titleLabel;
@property TUIUnReadView *unRead;
@property(readonly) TUIContactActionCellData *actionData;
- (void)fillWithData:(TUIContactActionCellData *)contactData;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,107 @@
//
// TUIContactActionCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/6/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIContactActionCell.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import "TUICommonContactCellData.h"
@interface TUIContactActionCell ()
@property TUIContactActionCellData *actionData;
@end
@implementation TUIContactActionCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.avatarView = [[UIImageView alloc] initWithImage:DefaultAvatarImage];
[self.contentView addSubview:self.avatarView];
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.titleLabel];
self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
self.unRead = [[TUIUnReadView alloc] init];
[self.contentView addSubview:self.unRead];
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
//[self setSelectionStyle:UITableViewCellSelectionStyleDefault];
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return self;
}
- (void)fillWithData:(TUIContactActionCellData *)actionData {
[super fillWithData:actionData];
self.actionData = actionData;
self.titleLabel.text = actionData.title;
if (actionData.icon) {
[self.avatarView setImage:actionData.icon];
}
@weakify(self);
[[RACObserve(self.actionData, readNum) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSNumber *x) {
@strongify(self);
[self.unRead setNum:[x integerValue]];
}];
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGFloat imgWidth = kScale390(34);
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(imgWidth);
make.centerY.mas_equalTo(self.contentView.mas_centerY);
make.leading.mas_equalTo(kScale390(12));
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = imgWidth / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.avatarView.mas_centerY);
make.leading.mas_equalTo(self.avatarView.mas_trailing).mas_offset(12);
make.height.mas_equalTo(20);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing);
}];
[self.unRead.unReadLabel sizeToFit];
[self.unRead mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.avatarView.mas_centerY);
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-5);
make.width.mas_equalTo(kScale375(20));
make.height.mas_equalTo(kScale375(20));
}];
[self.unRead.unReadLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.unRead);
make.size.mas_equalTo(self.unRead.unReadLabel);
}];
self.unRead.layer.cornerRadius = kScale375(10);
[self.unRead.layer masksToBounds];
}
@end

View File

@@ -0,0 +1,25 @@
//
// TUIFindContactCell.h
// TUIContact
//
// Created by harvy on 2021/12/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TUIFindContactCellModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUIFindContactCell : UITableViewCell
@property(nonatomic, strong) UIImageView *avatarView;
@property(nonatomic, strong) UILabel *mainTitleLabel;
@property(nonatomic, strong) UILabel *subTitleLabel;
@property(nonatomic, strong) UILabel *descLabel;
@property(nonatomic, strong) TUIFindContactCellModel *data;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,138 @@
//
// TUIFindContactCell.m
// TUIContact
//
// Created by harvy on 2021/12/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIFindContactCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import <TUICore/UIView+TUILayout.h>
#define kScale UIScreen.mainScreen.bounds.size.width / 375.0
@implementation TUIFindContactCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setupView];
}
return self;
}
- (void)setupView {
[self.contentView addSubview:self.avatarView];
[self.contentView addSubview:self.mainTitleLabel];
[self.contentView addSubview:self.subTitleLabel];
[self.contentView addSubview:self.descLabel];
}
- (void)setData:(TUIFindContactCellModel *)data {
_data = data;
self.mainTitleLabel.text = data.mainTitle;
self.subTitleLabel.text = data.subTitle;
self.descLabel.text = data.desc;
UIImage *placeHolder = (data.type == TUIFindContactTypeC2C) ? DefaultAvatarImage : DefaultGroupAvatarImageByGroupType(data.groupInfo.groupType);
[self.avatarView sd_setImageWithURL:data.avatarUrl placeholderImage:data.avatar ?: placeHolder];
self.descLabel.hidden = (data.type == TUIFindContactTypeC2C);
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGFloat imgWidth = kScale390(48);
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(imgWidth);
make.top.mas_equalTo(kScale390(10));
make.leading.mas_equalTo(kScale390(16));
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = imgWidth / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.mainTitleLabel sizeToFit];
[self.mainTitleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.avatarView.mas_top);
make.leading.mas_equalTo(self.avatarView.mas_trailing).mas_offset(12);
make.height.mas_equalTo(20);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(- kScale390(12));
}];
[self.subTitleLabel sizeToFit];
[self.subTitleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.mainTitleLabel.mas_bottom).mas_offset(2);
make.leading.mas_equalTo(self.mainTitleLabel.mas_leading);
make.height.mas_equalTo(self.subTitleLabel.frame.size.height);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(- kScale390(12));
}];
[self.descLabel sizeToFit];
[self.descLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.subTitleLabel.mas_bottom).mas_offset(2);
make.leading.mas_equalTo(self.mainTitleLabel.mas_leading);
make.height.mas_equalTo(self.subTitleLabel.frame.size.height);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(- kScale390(12));
}];
}
- (void)layoutSubviews {
[super layoutSubviews];
}
- (UIImageView *)avatarView {
if (_avatarView == nil) {
_avatarView = [[UIImageView alloc] init];
_avatarView.layer.cornerRadius = 3 * kScale;
_avatarView.layer.masksToBounds = YES;
}
return _avatarView;
}
- (UILabel *)mainTitleLabel {
if (_mainTitleLabel == nil) {
_mainTitleLabel = [[UILabel alloc] init];
_mainTitleLabel.text = @"mainTitle";
_mainTitleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
_mainTitleLabel.font = [UIFont systemFontOfSize:18.0 * kScale];
}
return _mainTitleLabel;
}
- (UILabel *)subTitleLabel {
if (_subTitleLabel == nil) {
_subTitleLabel = [[UILabel alloc] init];
_subTitleLabel.text = @"subTitle";
_subTitleLabel.textColor = TIMCommonDynamicColor(@"form_subtitle_color", @"#888888");
_subTitleLabel.font = [UIFont systemFontOfSize:13.0 * kScale];
}
return _subTitleLabel;
}
- (UILabel *)descLabel {
if (_descLabel == nil) {
_descLabel = [[UILabel alloc] init];
_descLabel.text = @"descLabel";
_descLabel.textColor = TIMCommonDynamicColor(@"form_desc_color", @"#888888");
_descLabel.font = [UIFont systemFontOfSize:13.0 * kScale];
}
return _descLabel;
}
@end

View File

@@ -0,0 +1,18 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <UIKit/UIKit.h>
#import <TIMCommon/TUICommonGroupInfoCellData.h>
@interface TUIGroupMemberCell : UICollectionViewCell
@property(nonatomic, strong) UIImageView *head;
@property(nonatomic, strong) UILabel *name;
+ (CGSize)getSize;
@property(nonatomic, strong) TUIGroupMemberCellData *data;
@end

View File

@@ -0,0 +1,97 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import "TUIGroupMemberCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/UIView+TUILayout.h>
#import "ReactiveObjC/ReactiveObjC.h"
#import "SDWebImage/UIImageView+WebCache.h"
@implementation TUIGroupMemberCell
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupViews];
}
return self;
}
- (void)setupViews {
_head = [[UIImageView alloc] init];
_head.layer.cornerRadius = 5;
[_head.layer setMasksToBounds:YES];
[self.contentView addSubview:_head];
_name = [[UILabel alloc] init];
[_name setFont:[UIFont systemFontOfSize:13]];
[_name setTextColor:[UIColor grayColor]];
_name.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:_name];
}
- (void)setData:(TUIGroupMemberCellData *)data {
_data = data;
if (data.avatarUrl) {
[self.head sd_setImageWithURL:[NSURL URLWithString:data.avatarUrl] placeholderImage:data.avatarImage ?: DefaultAvatarImage];
} else {
if (data.avatarImage) {
self.head.image = data.avatarImage;
} else {
self.head.image = DefaultAvatarImage;
}
}
if (data.name.length) {
self.name.text = data.name;
} else {
self.name.text = data.identifier;
}
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGSize headSize = [[self class] getSize];
[_head mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.top.mas_equalTo(self.contentView);
make.width.mas_equalTo(headSize.width);
make.height.mas_equalTo(headSize.width);
}];
[_name mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.head);
make.top.mas_equalTo(self.head.mas_bottom).mas_offset(TGroupMemberCell_Margin);
make.width.mas_equalTo(headSize.width);
make.height.mas_equalTo(TGroupMemberCell_Name_Height);
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
_head.layer.masksToBounds = YES;
_head.layer.cornerRadius = _head.frame.size.height / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
_head.layer.masksToBounds = YES;
_head.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
}
+ (CGSize)getSize {
CGSize headSize = TGroupMemberCell_Head_Size;
if (headSize.width * TGroupMembersCell_Column_Count + TGroupMembersCell_Margin * (TGroupMembersCell_Column_Count + 1) > Screen_Width) {
CGFloat wd = (Screen_Width - (TGroupMembersCell_Margin * (TGroupMembersCell_Column_Count + 1))) / TGroupMembersCell_Column_Count;
headSize = CGSizeMake(wd, wd);
}
return CGSizeMake(headSize.width, headSize.height + TGroupMemberCell_Name_Height + TGroupMemberCell_Margin);
}
@end

View File

@@ -0,0 +1,38 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <UIKit/UIKit.h>
#import <TIMCommon/TUICommonGroupInfoCellData.h>
@class TUIGroupMembersCell;
/////////////////////////////////////////////////////////////////////////////////
//
// TUIGroupMembersCellDelegate
//
/////////////////////////////////////////////////////////////////////////////////
@protocol TUIGroupMembersCellDelegate <NSObject>
- (void)groupMembersCell:(TUIGroupMembersCell *)cell didSelectItemAtIndex:(NSInteger)index;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIGroupMembersCell
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUIGroupMembersCell : UITableViewCell
@property(nonatomic, strong) UICollectionView *memberCollectionView;
@property(nonatomic, strong) UICollectionViewFlowLayout *memberFlowLayout;
@property(nonatomic, weak) id<TUIGroupMembersCellDelegate> delegate;
@property(nonatomic) TUIGroupMembersCellData *data;
+ (CGFloat)getHeight:(TUIGroupMembersCellData *)data;
@end

View File

@@ -0,0 +1,97 @@
//
// TUIGroupMembersCell.m
// UIKit
//
// Created by kennethmiao on 2018/9/25.
// Copyright © 2018 Tencent. All rights reserved.
//
#import "TUIGroupMembersCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import "TUIGroupMemberCell.h"
@interface TUIGroupMembersCell () <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@end
@implementation TUIGroupMembersCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setupViews];
}
return self;
}
- (void)setupViews {
_memberFlowLayout = [[UICollectionViewFlowLayout alloc] init];
CGSize cellSize = [TUIGroupMemberCell getSize];
_memberFlowLayout.itemSize = cellSize;
_memberFlowLayout.minimumInteritemSpacing =
(Screen_Width - cellSize.width * TGroupMembersCell_Column_Count - 2 * 20) / (TGroupMembersCell_Column_Count - 1);
_memberFlowLayout.minimumLineSpacing = TGroupMembersCell_Margin;
_memberFlowLayout.sectionInset = UIEdgeInsetsMake(TGroupMembersCell_Margin, 20, TGroupMembersCell_Margin, 20);
_memberCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_memberFlowLayout];
[_memberCollectionView registerClass:[TUIGroupMemberCell class] forCellWithReuseIdentifier:TGroupMemberCell_ReuseId];
_memberCollectionView.collectionViewLayout = _memberFlowLayout;
_memberCollectionView.delegate = self;
_memberCollectionView.dataSource = self;
_memberCollectionView.showsHorizontalScrollIndicator = NO;
_memberCollectionView.showsVerticalScrollIndicator = NO;
_memberCollectionView.backgroundColor = self.backgroundColor;
[self.contentView addSubview:_memberCollectionView];
[self setSeparatorInset:UIEdgeInsetsMake(0, TGroupMembersCell_Margin, 0, 0)];
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
}
- (void)updateLayout {
CGFloat height = [TUIGroupMembersCell getHeight:_data];
_memberCollectionView.frame = CGRectMake(0, 0, Screen_Width, height);
}
- (void)setData:(TUIGroupMembersCellData *)data {
_data = data;
[self updateLayout];
[_memberCollectionView reloadData];
}
+ (CGFloat)getHeight:(TUIGroupMembersCellData *)data {
NSInteger row = ceil(data.members.count * 1.0 / TGroupMembersCell_Column_Count);
if (row > TGroupMembersCell_Row_Count) {
row = TGroupMembersCell_Row_Count;
}
CGFloat height = row * [TUIGroupMemberCell getSize].height + (row + 1) * TGroupMembersCell_Margin;
return height;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _data.members.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
TUIGroupMemberCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:TGroupMemberCell_ReuseId forIndexPath:indexPath];
TUIGroupMemberCellData *data = nil;
data = _data.members[indexPath.item];
[cell setData:data];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (_delegate && [_delegate respondsToSelector:@selector(groupMembersCell:didSelectItemAtIndex:)]) {
[_delegate groupMembersCell:self didSelectItemAtIndex:indexPath.section * TGroupMembersCell_Column_Count + indexPath.row];
}
}
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return [TUIGroupMemberCell getSize];
}
@end

View File

@@ -0,0 +1,26 @@
//
// TUIMemberInfoCell.h
// TUIGroup
//
// Created by harvy on 2021/12/27.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
@class TUIMemberInfoCellData;
NS_ASSUME_NONNULL_BEGIN
@interface TUIMemberTagView : UIView
@property(nonatomic, strong) UILabel *tagname;
@end
@interface TUIMemberInfoCell : UITableViewCell
@property(nonatomic, strong) UIImageView *avatarImageView;
@property(nonatomic, strong) UILabel *nameLabel;
@property(nonatomic, strong) TUIMemberInfoCellData *data;
@property(nonatomic, strong) TUIMemberTagView *tagView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,180 @@
//
// TUIMemberInfoCell.m
// TUIGroup
//
// Created by harvy on 2021/12/27.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMemberInfoCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import <TUICore/UIView+TUILayout.h>
#import "TUIMemberInfoCellData.h"
#import "UIImageView+WebCache.h"
#define kScale UIScreen.mainScreen.bounds.size.width / 375.0
@implementation TUIMemberTagView : UIView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor tui_colorWithHex:@"#E7F3FC"];
self.layer.borderWidth = kScale390(1);
self.layer.cornerRadius = kScale390(3);
self.layer.borderColor = [UIColor tui_colorWithHex:@"#1890FF"].CGColor;
[self addSubview:self.tagname];
}
return self;
}
- (UILabel *)tagname {
if (!_tagname) {
_tagname = [[UILabel alloc] init];
_tagname.text = @"";
_tagname.textColor = [UIColor tui_colorWithHex:@"#1890FF"];
_tagname.font = [UIFont systemFontOfSize:kScale390(10)];
}
return _tagname;
}
- (void)layoutSubviews {
[super layoutSubviews];
[_tagname sizeToFit];
_tagname.frame = CGRectMake(kScale390(8), 0, _tagname.frame.size.width, self.frame.size.height);
}
@end
@implementation TUIMemberInfoCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setupViews];
}
return self;
}
- (void)setupViews {
[self.contentView addSubview:self.avatarImageView];
[self.contentView addSubview:self.nameLabel];
[self.contentView addSubview:self.tagView];
}
- (void)layoutSubviews {
[super layoutSubviews];
}
- (void)setData:(TUIMemberInfoCellData *)data {
_data = data;
UIImage *defaultImage = DefaultAvatarImage;
[self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:data.avatarUrl] placeholderImage:data.avatar ?: defaultImage];
self.nameLabel.text = data.name;
self.tagView.hidden = NO;
if (data.role == V2TIM_GROUP_MEMBER_ROLE_SUPER) {
self.tagView.tagname.text = TIMCommonLocalizableString(TUIKitMembersRoleSuper);
} else if (data.role == V2TIM_GROUP_MEMBER_ROLE_ADMIN) {
self.tagView.tagname.text = TIMCommonLocalizableString(TUIKitMembersRoleAdmin);
} else {
self.tagView.tagname.text = @"";
self.tagView.hidden = YES;
}
if (data.showAccessory) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
self.accessoryType = UITableViewCellAccessoryNone;
}
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
if (self.data.style == TUIMemberInfoCellStyleAdd) {
[self.avatarImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(18.0 * kScale);
make.centerY.mas_equalTo(self.contentView);
make.width.height.mas_equalTo(20.0 * kScale);
}];
self.nameLabel.font = [UIFont systemFontOfSize:16.0 * kScale];
self.nameLabel.textColor = TIMCommonDynamicColor(@"form_value_text_color", @"#000000");
} else {
[self.avatarImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(16.0 * kScale);
make.centerY.mas_equalTo(self.contentView);
make.width.height.mas_equalTo(34.0 * kScale);
}];
self.nameLabel.font = [UIFont systemFontOfSize:16.0 * kScale];
self.nameLabel.textColor = TIMCommonDynamicColor(@"form_value_text_color", @"#000000");
}
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarImageView.layer.masksToBounds = YES;
self.avatarImageView.layer.cornerRadius = self.avatarImageView.frame.size.height / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarImageView.layer.masksToBounds = YES;
self.avatarImageView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.nameLabel sizeToFit];
[self.nameLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.avatarImageView.mas_trailing).mas_offset(14);
make.centerY.mas_equalTo(self.contentView);
make.size.mas_equalTo(self.nameLabel.frame.size);
if (self.tagView.tagname.text.length > 0) {
make.trailing.mas_lessThanOrEqualTo(self.tagView.mas_trailing).mas_offset(- 2.0 * kScale);
}
else {
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(- 2.0 * kScale);
}
}];
[self.tagView.tagname sizeToFit];
[self.tagView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.nameLabel.mas_trailing).mas_offset(kScale390(10));
make.width.mas_equalTo(self.tagView.tagname.frame.size.width + kScale390(16));
make.height.mas_equalTo(kScale390(15));
make.centerY.mas_equalTo(self.contentView.mas_centerY);
make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(- 2.0 * kScale);
}];
}
- (UIImageView *)avatarImageView {
if (_avatarImageView == nil) {
_avatarImageView = [[UIImageView alloc] init];
}
return _avatarImageView;
}
- (UILabel *)nameLabel {
if (_nameLabel == nil) {
_nameLabel = [[UILabel alloc] init];
_nameLabel.font = [UIFont systemFontOfSize:18.0 * kScale];
_nameLabel.textColor = [UIColor colorWithRed:17 / 255.0 green:17 / 255.0 blue:17 / 255.0 alpha:1 / 1.0];
}
return _nameLabel;
}
- (TUIMemberTagView *)tagView {
if (_tagView == nil) {
_tagView = [[TUIMemberTagView alloc] init];
}
return _tagView;
}
@end

View File

@@ -0,0 +1,15 @@
//
// TUISelectedUserCollectionViewCell.h
// TXIMSDK_TUIKit_iOS
//
// Created by xiangzhang on 2020/7/6.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
#import <TUICore/UIView+TUILayout.h>
#import <UIKit/UIKit.h>
@interface TUIMemberPanelCell : UICollectionViewCell
- (void)fillWithData:(TUIUserModel *)model;
@end

View File

@@ -0,0 +1,37 @@
//
// TUISelectedUserCollectionViewCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by xiangzhang on 2020/7/6.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMemberPanelCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIDarkModel.h>
#import <TUICore/TUIThemeManager.h>
#import "SDWebImage/UIImageView+WebCache.h"
@implementation TUIMemberPanelCell {
UIImageView *_imageView;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = TUIContactDynamicColor(@"group_controller_bg_color", @"#F2F3F5");
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
_imageView.backgroundColor = [UIColor clearColor];
_imageView.contentMode = UIViewContentModeScaleToFill;
[self addSubview:_imageView];
}
return self;
}
- (void)fillWithData:(TUIUserModel *)model {
[_imageView sd_setImageWithURL:[NSURL URLWithString:model.avatar]
placeholderImage:[UIImage imageNamed:TIMCommonImagePath(@"default_c2c_head")]
options:SDWebImageHighPriority];
}
@end

View File

@@ -0,0 +1,18 @@
//
// TUISelectMemberCell.h
// TXIMSDK_TUIKit_iOS
//
// Created by xiangzhang on 2021/8/26.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <TIMCommon/TIMCommonModel.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUISelectGroupMemberCell : UITableViewCell
- (void)fillWithData:(TUIUserModel *)model isSelect:(BOOL)isSelect;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,83 @@
//
// TUISelectMemberCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by xiangzhang on 2021/8/26.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUISelectGroupMemberCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIDarkModel.h>
#import <TUICore/TUIThemeManager.h>
@implementation TUISelectGroupMemberCell {
UIImageView *_selectedMark;
UIImageView *_userImg;
UILabel *_nameLabel;
TUIUserModel *_userModel;
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#F2F3F5");
_selectedMark = [[UIImageView alloc] initWithFrame:CGRectZero];
[self addSubview:_selectedMark];
_userImg = [[UIImageView alloc] initWithFrame:CGRectZero];
[self addSubview:_userImg];
_nameLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_nameLabel.textAlignment = isRTL()?NSTextAlignmentRight:NSTextAlignmentLeft;
[self addSubview:_nameLabel];
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
return self;
}
- (void)fillWithData:(TUIUserModel *)model isSelect:(BOOL)isSelect {
_userModel = model;
_selectedMark.image = isSelect ? [UIImage imageNamed:TUIContactImagePath(@"ic_selected")] : [UIImage imageNamed:TUIContactImagePath(@"ic_unselect")];
[_userImg sd_setImageWithURL:[NSURL URLWithString:model.avatar] placeholderImage:DefaultAvatarImage];
_nameLabel.text = model.name;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
[_selectedMark mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(20);
make.leading.mas_equalTo(self.contentView).mas_offset(12);
make.centerY.mas_equalTo(self.contentView);
}];
[_userImg mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(32);
make.leading.mas_equalTo(_selectedMark.mas_trailing).mas_offset(12);
make.centerY.mas_equalTo(self.contentView);
}];
[_nameLabel sizeToFit];
[_nameLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(_userImg.mas_trailing).mas_offset(12);
make.trailing.mas_equalTo(self.contentView.mas_trailing);
make.height.mas_equalTo(self.contentView);
make.centerY.mas_equalTo(self.contentView);
}];
}
- (void)layoutSubviews {
[super layoutSubviews];
}
@end

View File

@@ -0,0 +1,36 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This file declares the view model used to implement the blocklist page.
* The view model is responsible for some data processing and business logic in the interface, such as pulling blacklist information and loading blocklist
* data.
*/
#import <Foundation/Foundation.h>
#import "TUICommonContactCell.h"
NS_ASSUME_NONNULL_BEGIN
/**
* 【Module name】 TUIBlackListViewModel
* 【Function description】It is responsible for pulling the user's blocklist information and displaying it on the page.
* The view model is also responsible for loading the pulled information to facilitate data processing in the client.
*/
@interface TUIBlackListViewDataProvider : NSObject
/**
* Bocklist data
* The blocklist stores the detailed information of the blocked users.
* Include details such as user avatar (URL and image), user ID, user nickname, etc. Used to display detailed information when you click to a detailed meeting.
*/
@property(readonly) NSArray<TUICommonContactCellData *> *blackListData;
@property(readonly) BOOL isLoadFinished;
- (void)loadBlackList;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,42 @@
//
// TUIBlackListViewModel.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIBlackListViewDataProvider.h"
#import <TIMCommon/TIMDefine.h>
@interface TUIBlackListViewDataProvider ()
@property NSArray<TUICommonContactCellData *> *blackListData;
@property BOOL isLoadFinished;
@property BOOL isLoading;
@end
@implementation TUIBlackListViewDataProvider
- (void)loadBlackList {
if (self.isLoading) return;
self.isLoading = YES;
self.isLoadFinished = NO;
@weakify(self);
[[V2TIMManager sharedInstance]
getBlackList:^(NSArray<V2TIMFriendInfo *> *infoList) {
@strongify(self);
NSMutableArray *list = @[].mutableCopy;
for (V2TIMFriendInfo *fd in infoList) {
TUICommonContactCellData *data = [[TUICommonContactCellData alloc] initWithFriend:fd];
[list addObject:data];
}
self.blackListData = list;
self.isLoadFinished = YES;
self.isLoading = NO;
}
fail:^(int code, NSString *msg) {
self.isLoading = NO;
}];
}
@end

View File

@@ -0,0 +1,64 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This file declares the view model for the friend selection interface
* The view model is responsible for pulling friend data through the IM SDK interface and loading the data.
*/
#import <Foundation/Foundation.h>
@class TUICommonContactSelectCellData;
NS_ASSUME_NONNULL_BEGIN
typedef BOOL (^ContactSelectFilterBlock)(TUICommonContactSelectCellData *data);
/**
* 【Module name】Friend selection interface view model (TContactSelectViewModel)
* 【Function description】Implement the friend selection interface view model.
* This view model is responsible for pulling friend lists, friend requests and loading related data from the server.
* At the same time, this view model will also group friends according to the initials of their nicknames, which helps the view maintain an "alphabet" on the
* right side of the interface to quickly retrieve friends.
*/
@interface TUIContactSelectViewDataProvider : NSObject
/**
* Data dictionary, responsible for classifying friend information (TCommonContactCellData) by initials.
* For example, Jack and James are stored in "J".
*/
@property(readonly) NSDictionary<NSString *, NSArray<TUICommonContactSelectCellData *> *> *dataDict;
/**
* The group list, that is, the group information of the current friend.
* For example, if the current user has only one friend "Jack", there is only one element "J" in this list.
* The grouping information is up to 26 letters from A - Z and "#".
*/
@property(readonly) NSArray *groupList;
/**
* An identifier indicating whether the current loading process is complete
* YES: Loading is done; NO: Loading
* With this identifier, we can avoid reloading the data.
*/
@property(readonly) BOOL isLoadFinished;
/**
*
* Filter to disable contacts
*/
@property(copy) ContactSelectFilterBlock disableFilter;
/**
*
* Filter to display contacts
*/
@property(copy) ContactSelectFilterBlock avaliableFilter;
- (void)loadContacts;
- (void)setSourceIds:(NSArray<NSString *> *)ids;
- (void)setSourceIds:(NSArray<NSString *> *)ids displayNames:(NSDictionary *__nullable)displayNames;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,105 @@
//
// TContactSelectViewModel.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/8.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIContactSelectViewDataProvider.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <TUICore/NSString+TUIUtil.h>
@interface TUIContactSelectViewDataProvider ()
@property NSDictionary<NSString *, NSArray<TUICommonContactSelectCellData *> *> *dataDict;
@property NSArray *groupList;
@property BOOL isLoadFinished;
@end
@implementation TUIContactSelectViewDataProvider
- (void)loadContacts {
self.isLoadFinished = NO;
@weakify(self);
[[V2TIMManager sharedInstance]
getFriendList:^(NSArray<V2TIMFriendInfo *> *infoList) {
@strongify(self);
NSMutableArray *arr = [NSMutableArray new];
for (V2TIMFriendInfo *fr in infoList) {
[arr addObject:fr.userFullInfo];
}
[self fillList:arr displayNames:nil];
}
fail:nil];
}
- (void)setSourceIds:(NSArray<NSString *> *)ids {
[self setSourceIds:ids displayNames:nil];
}
- (void)setSourceIds:(NSArray<NSString *> *)ids displayNames:(NSDictionary *__nullable)displayNames {
[[V2TIMManager sharedInstance] getUsersInfo:ids
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
[self fillList:infoList displayNames:displayNames];
}
fail:nil];
}
- (void)fillList:(NSArray<V2TIMUserFullInfo *> *)profiles displayNames:(NSDictionary *__nullable)displayNames {
NSMutableDictionary *dataDict = @{}.mutableCopy;
NSMutableArray *groupList = @[].mutableCopy;
NSMutableArray *nonameList = @[].mutableCopy;
for (V2TIMUserFullInfo *profile in profiles) {
TUICommonContactSelectCellData *data = [[TUICommonContactSelectCellData alloc] init];
NSString *showName = @"";
if (displayNames && [displayNames.allKeys containsObject:profile.userID]) {
showName = [displayNames objectForKey:profile.userID];
}
if (showName.length == 0) {
showName = profile.showName;
}
data.title = showName;
if (profile.faceURL.length) {
data.avatarUrl = [NSURL URLWithString:profile.faceURL];
}
data.identifier = profile.userID;
if (self.avaliableFilter && !self.avaliableFilter(data)) {
continue;
}
if (self.disableFilter) {
data.enabled = !self.disableFilter(data);
}
NSString *group = [[data.title firstPinYin] uppercaseString];
if (group.length == 0 || !isalpha([group characterAtIndex:0])) {
[nonameList addObject:data];
continue;
}
NSMutableArray *list = [dataDict objectForKey:group];
if (!list) {
list = @[].mutableCopy;
dataDict[group] = list;
[groupList addObject:group];
}
[list addObject:data];
}
[groupList sortUsingSelector:@selector(localizedStandardCompare:)];
if (nonameList.count) {
[groupList addObject:@"#"];
dataDict[@"#"] = nonameList;
}
for (NSMutableArray *list in [self.dataDict allValues]) {
[list sortUsingSelector:@selector(compare:)];
}
self.groupList = groupList;
self.dataDict = dataDict;
self.isLoadFinished = YES;
}
@end

View File

@@ -0,0 +1,59 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This file declares the view model for the Contacts interface.
* The view model is responsible for pulling friend lists, friend requests from the server and loading related data.
*/
#import <Foundation/Foundation.h>
#import "TUICommonContactCell.h"
NS_ASSUME_NONNULL_BEGIN
/**
* 【Module name】Message List View Model (TContactViewModel)
* 【Function description】A view model that implements a message list.
* 1. This view model is responsible for pulling friend lists, friend requests and loading related data from the server.
* 2. At the same time, this view model will also group friends by the first latter of their nicknames, which helps the view maintain an "alphabet" on the
* right side of the interface to facilitate quick retrieval of friends.
*/
@interface TUIContactViewDataProvider : NSObject
/**
* Data dictionary, responsible for classifying friend information (TCommonContactCellData) by initials.
* For example, Jack and James are stored in "J".
*/
@property(readonly) NSDictionary<NSString *, NSArray<TUICommonContactCellData *> *> *dataDict;
/**
* The group list, that is, the group information of the current friend.
* For example, if the current user has only one friend "Jack", there is only one element "J" in this list.
* The grouping information is up to 26 letters from A - Z and "#".
*/
@property(readonly) NSArray *groupList;
/**
* An identifier indicating whether the current loading process is complete
* YES: Loading is done; NO: Loading
* With this identifier, we can avoid reloading the data.
*/
@property(readonly) BOOL isLoadFinished;
/**
* Count of pending friend requests
*/
@property(readonly) NSUInteger pendencyCnt;
@property(readonly) NSDictionary *contactMap;
- (void)loadContacts;
- (void)loadFriendApplication;
- (void)clearApplicationCnt;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,254 @@
//
// TContactViewModel.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIContactViewDataProvider.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/NSString+TUIUtil.h>
#define kGetUserStatusPageCount 500
@interface TUIContactViewDataProvider () <V2TIMFriendshipListener, V2TIMSDKListener>
@property NSDictionary<NSString *, NSArray<TUICommonContactCellData *> *> *dataDict;
@property NSArray *groupList;
@property BOOL isLoadFinished;
@property NSUInteger pendencyCnt;
@property(nonatomic, strong) NSDictionary *contactMap;
@end
@implementation TUIContactViewDataProvider
- (instancetype)init {
if (self = [super init]) {
[[V2TIMManager sharedInstance] addFriendListener:self];
[[V2TIMManager sharedInstance] addIMSDKListener:self];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)loadContacts {
self.isLoadFinished = NO;
@weakify(self);
[[V2TIMManager sharedInstance]
getFriendList:^(NSArray<V2TIMFriendInfo *> *infoList) {
@strongify(self);
NSMutableDictionary *dataDict = @{}.mutableCopy;
NSMutableArray *groupList = @[].mutableCopy;
NSMutableArray *nonameList = @[].mutableCopy;
NSMutableDictionary *contactMap = [NSMutableDictionary dictionary];
NSMutableArray *userIDList = [NSMutableArray array];
for (V2TIMFriendInfo *friend in infoList) {
TUICommonContactCellData *data = [[TUICommonContactCellData alloc] initWithFriend:friend];
// for online status
data.onlineStatus = TUIContactOnlineStatusUnknown;
if (data.identifier) {
[contactMap setObject:data forKey:data.identifier];
[userIDList addObject:data.identifier];
}
NSString *group = [[data.title firstPinYin] uppercaseString];
if (group.length == 0 || !isalpha([group characterAtIndex:0])) {
[nonameList addObject:data];
continue;
}
NSMutableArray *list = [dataDict objectForKey:group];
if (!list) {
list = @[].mutableCopy;
dataDict[group] = list;
[groupList addObject:group];
}
[list addObject:data];
}
[groupList sortUsingSelector:@selector(localizedStandardCompare:)];
if (nonameList.count) {
[groupList addObject:@"#"];
dataDict[@"#"] = nonameList;
}
for (NSMutableArray *list in [dataDict allValues]) {
[list sortUsingSelector:@selector(compare:)];
}
self.groupList = groupList;
self.dataDict = dataDict;
self.contactMap = [NSDictionary dictionaryWithDictionary:contactMap];
self.isLoadFinished = YES;
// refresh online status async
[self asyncGetOnlineStatus:userIDList];
}
fail:^(int code, NSString *desc) {
NSLog(@"getFriendList failed, code:%d desc:%@", code, desc);
}];
[self loadFriendApplication];
}
- (void)loadFriendApplication {
@weakify(self);
[[V2TIMManager sharedInstance]
getFriendApplicationList:^(V2TIMFriendApplicationResult *result) {
@strongify(self);
self.pendencyCnt = result.unreadCount;
}
fail:nil];
}
- (void)asyncGetOnlineStatus:(NSArray *)userIDList {
if (NSThread.isMainThread) {
@weakify(self);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@strongify(self);
[self asyncGetOnlineStatus:userIDList];
});
return;
}
if (userIDList.count == 0) {
return;
}
@weakify(self);
void (^getUserStatus)(NSArray *userIDList) = ^(NSArray *userIDList) {
@strongify(self);
@weakify(self);
[V2TIMManager.sharedInstance getUserStatus:userIDList
succ:^(NSArray<V2TIMUserStatus *> *result) {
@strongify(self);
[self handleOnlineStatus:result];
}
fail:^(int code, NSString *desc) {
#if DEBUG
if (code == ERR_SDK_INTERFACE_NOT_SUPPORT && TUIConfig.defaultConfig.displayOnlineStatusIcon) {
[TUITool makeToast:desc];
}
#endif
}];
};
NSInteger count = kGetUserStatusPageCount;
if (userIDList.count > count) {
NSArray *subUserIDList = [userIDList subarrayWithRange:NSMakeRange(0, count)];
NSArray *pendingUserIDList = [userIDList subarrayWithRange:NSMakeRange(count, userIDList.count - count)];
getUserStatus(subUserIDList);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@strongify(self);
[self asyncGetOnlineStatus:pendingUserIDList];
});
} else {
getUserStatus(userIDList);
}
}
- (void)asyncUpdateOnlineStatus {
if (NSThread.isMainThread) {
@weakify(self);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@strongify(self);
[self asyncUpdateOnlineStatus];
});
return;
}
// reset
NSMutableArray *userIDList = [NSMutableArray array];
for (TUICommonContactCellData *contact in self.contactMap.allValues) {
contact.onlineStatus = TUIContactOnlineStatusOffline;
if (contact.identifier) {
[userIDList addObject:contact.identifier];
}
}
// refresh table view on the main thread
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
self.isLoadFinished = YES;
// fetch
[self asyncGetOnlineStatus:userIDList];
});
}
- (void)handleOnlineStatus:(NSArray<V2TIMUserStatus *> *)userStatusList {
NSInteger changed = 0;
for (V2TIMUserStatus *userStatus in userStatusList) {
if ([self.contactMap.allKeys containsObject:userStatus.userID]) {
changed++;
TUICommonContactCellData *contact = [self.contactMap objectForKey:userStatus.userID];
contact.onlineStatus = (userStatus.statusType == V2TIM_USER_STATUS_ONLINE) ? TUIContactOnlineStatusOnline : TUIContactOnlineStatusOffline;
}
}
if (changed == 0) {
return;
}
// refresh table view on the main thread
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
self.isLoadFinished = YES;
});
}
- (void)clearApplicationCnt {
@weakify(self);
[[V2TIMManager sharedInstance]
setFriendApplicationRead:^{
@strongify(self);
(self).pendencyCnt = 0;
}
fail:nil];
}
#pragma mark - V2TIMSDKListener
- (void)onUserStatusChanged:(NSArray<V2TIMUserStatus *> *)userStatusList {
[self handleOnlineStatus:userStatusList];
}
- (void)onConnectFailed:(int)code err:(NSString *)err {
NSLog(@"%s", __func__);
}
- (void)onConnectSuccess {
NSLog(@"%s", __func__);
[self asyncUpdateOnlineStatus];
}
#pragma mark - V2TIMFriendshipListener
- (void)onFriendApplicationListAdded:(NSArray<V2TIMFriendApplication *> *)applicationList {
[self loadFriendApplication];
}
- (void)onFriendApplicationListDeleted:(NSArray *)userIDList {
[self loadFriendApplication];
}
- (void)onFriendApplicationListRead {
[self loadFriendApplication];
}
- (void)onFriendListAdded:(NSArray<V2TIMFriendInfo *> *)infoList {
[self loadContacts];
}
- (void)onFriendListDeleted:(NSArray *)userIDList {
[self loadContacts];
}
- (void)onFriendProfileChanged:(NSArray<V2TIMFriendInfo *> *)infoList {
[self loadContacts];
}
@end

View File

@@ -0,0 +1,27 @@
//
// TUIFindContactViewDataProvider.h
// TUIContact
//
// Created by harvy on 2021/12/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TUIFindContactCellModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUIFindContactViewDataProvider : NSObject
@property(nonatomic, strong, readonly) NSArray<TUIFindContactCellModel *> *users;
@property(nonatomic, strong, readonly) NSArray<TUIFindContactCellModel *> *groups;
- (void)findUser:(NSString *)userID completion:(dispatch_block_t)completion;
- (void)findGroup:(NSString *)groupID completion:(dispatch_block_t)completion;
- (NSString *)getMyUserIDDescription;
- (void)clear;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,100 @@
//
// TUIFindContactViewDataProvider.m
// TUIContact
//
// Created by harvy on 2021/12/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIFindContactViewDataProvider.h"
#import <ImSDK_Plus/ImSDK_Plus.h>
#import <TUICore/TUIGlobalization.h>
#import "TUIFindContactCellModel.h"
@interface TUIFindContactViewDataProvider ()
@property(nonatomic, strong) NSArray<TUIFindContactCellModel *> *users;
@property(nonatomic, strong) NSArray<TUIFindContactCellModel *> *groups;
@end
@implementation TUIFindContactViewDataProvider
- (void)findUser:(NSString *)userID completion:(dispatch_block_t)completion {
if (!completion) {
return;
}
if (userID == nil) {
self.users = @[];
completion();
return;
}
__weak typeof(self) weakSelf = self;
[[V2TIMManager sharedInstance] getUsersInfo:@[ userID ]
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
V2TIMUserFullInfo *userInfo = infoList.firstObject;
if (userInfo) {
TUIFindContactCellModel *cellModel = [[TUIFindContactCellModel alloc] init];
cellModel.avatarUrl = [NSURL URLWithString:userInfo.faceURL];
cellModel.mainTitle = userInfo.nickName ?: userInfo.userID;
cellModel.subTitle = [NSString stringWithFormat:@"ID: %@", userInfo.userID];
cellModel.desc = @"";
cellModel.type = TUIFindContactTypeC2C;
cellModel.contactID = userInfo.userID;
cellModel.userInfo = userInfo;
weakSelf.users = @[ cellModel ];
}
completion();
}
fail:^(int code, NSString *msg) {
weakSelf.users = @[];
completion();
}];
}
- (void)findGroup:(NSString *)groupID completion:(dispatch_block_t)completion {
if (!completion) {
return;
}
if (groupID == nil) {
self.groups = @[];
completion();
return;
}
__weak typeof(self) weakSelf = self;
[[V2TIMManager sharedInstance] getGroupsInfo:@[ groupID ]
succ:^(NSArray<V2TIMGroupInfoResult *> *groupResultList) {
V2TIMGroupInfoResult *result = groupResultList.firstObject;
if (result && result.resultCode == 0) {
V2TIMGroupInfo *info = result.info;
TUIFindContactCellModel *cellModel = [[TUIFindContactCellModel alloc] init];
cellModel.avatarUrl = [NSURL URLWithString:info.faceURL];
cellModel.mainTitle = info.groupName;
cellModel.subTitle = [NSString stringWithFormat:@"ID: %@", info.groupID];
cellModel.desc = [NSString stringWithFormat:@"%@: %@",TIMCommonLocalizableString(TUIKitGroupProfileType),info.groupType];
cellModel.type = TUIFindContactTypeGroup;
cellModel.contactID = info.groupID;
cellModel.groupInfo = info;
weakSelf.groups = @[ cellModel ];
} else {
weakSelf.groups = @[];
}
completion();
}
fail:^(int code, NSString *desc) {
weakSelf.groups = @[];
completion();
}];
}
- (NSString *)getMyUserIDDescription {
NSString *loginUser = V2TIMManager.sharedInstance.getLoginUser;
return [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitAddContactMyUserIDFormat), loginUser];
}
- (void)clear {
self.users = @[];
self.groups = @[];
}
@end

View File

@@ -0,0 +1,36 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This file declares the view model for the group list interface.
* The view model is responsible for pulling and loading the group list data through the interface provided by the IM SDK, which is convenient for the page to
* display the group list.
*/
#import <Foundation/Foundation.h>
#import "TUICommonContactCell.h"
NS_ASSUME_NONNULL_BEGIN
/**
* 【Module name】Group List View Model (TUIGroupConversationListViewModel)
* 【Function description】It is responsible for pulling the group information of the user and loading the obtained data.
* The view model pulls the group information of the user through the interface provided by the IM SDK. The group information is classified and stored
* according to the first latter of the name.
*/
@interface TUIGroupConversationListViewDataProvider : NSObject
@property(readonly) NSDictionary<NSString *, NSArray<TUICommonContactCellData *> *> *dataDict;
@property(readonly) NSArray *groupList;
@property(readonly) BOOL isLoadFinished;
- (void)loadConversation;
- (void)removeData:(TUICommonContactCellData *)data;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,81 @@
//
// TUIGroupConversationListModel.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/6/11.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIGroupConversationListViewDataProvider.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/NSString+TUIUtil.h>
@interface TUIGroupConversationListViewDataProvider ()
@property BOOL isLoadFinished;
@property BOOL isLoading;
@property NSDictionary<NSString *, NSArray<TUICommonContactCellData *> *> *dataDict;
@property NSArray *groupList;
@end
@implementation TUIGroupConversationListViewDataProvider
- (void)loadConversation {
if (self.isLoading) return;
self.isLoading = NO;
self.isLoadFinished = NO;
NSMutableDictionary *dataDict = @{}.mutableCopy;
NSMutableArray *groupList = @[].mutableCopy;
NSMutableArray *nonameList = @[].mutableCopy;
@weakify(self);
[[V2TIMManager sharedInstance]
getJoinedGroupList:^(NSArray<V2TIMGroupInfo *> *infoList) {
@strongify(self);
for (V2TIMGroupInfo *group in infoList) {
TUICommonContactCellData *data = [[TUICommonContactCellData alloc] initWithGroupInfo:group];
NSString *group = [[data.title firstPinYin] uppercaseString];
if (group.length == 0 || !isalpha([group characterAtIndex:0])) {
[nonameList addObject:data];
continue;
}
NSMutableArray *list = [dataDict objectForKey:group];
if (!list) {
list = @[].mutableCopy;
dataDict[group] = list;
[groupList addObject:group];
}
[list addObject:data];
}
[groupList sortUsingSelector:@selector(localizedStandardCompare:)];
if (nonameList.count) {
[groupList addObject:@"#"];
dataDict[@"#"] = nonameList;
}
for (NSMutableArray *list in [self.dataDict allValues]) {
[list sortUsingSelector:@selector(compare:)];
}
self.groupList = groupList;
self.dataDict = dataDict;
self.isLoadFinished = YES;
}
fail:nil];
}
- (void)removeData:(TUICommonContactCellData *)data {
NSMutableDictionary *dictDict = [NSMutableDictionary dictionaryWithDictionary:self.dataDict];
for (NSString *key in self.dataDict) {
NSMutableArray *list = [NSMutableArray arrayWithArray:self.dataDict[key]];
if ([list containsObject:data]) {
[list removeObject:data];
dictDict[key] = list;
break;
}
}
self.dataDict = dictDict;
}
@end

View File

@@ -0,0 +1,44 @@
//
// TUIGroupManageDataProvider.h
// TUIGroup
//
// Created by harvy on 2021/12/24.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
@class TUIUserModel;
NS_ASSUME_NONNULL_BEGIN
@protocol TUIGroupManageDataProviderDelegate <NSObject>
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadData;
- (void)showCoverViewWhenMuteAll:(BOOL)show;
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)onError:(int)code desc:(NSString *)desc operate:(NSString *)operate;
@end
@interface TUIGroupManageDataProvider : NSObject
@property(nonatomic, assign) BOOL muteAll;
@property(nonatomic, copy) NSString *groupID;
@property(nonatomic, assign) BOOL currentGroupTypeSupportSettingAdmin;
@property(nonatomic, assign) BOOL currentGroupTypeSupportAddMemberOfBlocked;
@property(nonatomic, weak) id<TUIGroupManageDataProviderDelegate> delegate;
@property(nonatomic, strong, readonly) NSMutableArray *datas;
- (void)loadData;
- (void)mutedAll:(BOOL)mute completion:(void (^)(int, NSString *))completion;
- (void)mute:(BOOL)mute user:(TUIUserModel *)user;
- (void)updateMuteMembersFilterAdmins;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,285 @@
//
// TUIGroupManageDataProvider.m
// TUIGroup
//
// Created by harvy on 2021/12/24.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIGroupManageDataProvider.h"
#import <ImSDK_Plus/ImSDK_Plus.h>
#import <TUICore/TUIGlobalization.h>
#import "TUIMemberInfoCellData.h"
#import "TUISelectGroupMemberCell.h"
@interface TUIGroupManageDataProvider ()
@property(nonatomic, strong) NSMutableArray *datas;
@property(nonatomic, strong) NSMutableArray *groupInfoDatasArray;
@property(nonatomic, strong) NSMutableArray *muteMembersDataArray;
@property(nonatomic, strong) V2TIMGroupInfo *groupInfo;
@end
@implementation TUIGroupManageDataProvider
- (void)mutedAll:(BOOL)mute completion:(void (^)(int, NSString *))completion {
__weak typeof(self) weakSelf = self;
V2TIMGroupInfo *groupInfo = [[V2TIMGroupInfo alloc] init];
groupInfo.groupID = self.groupID;
groupInfo.allMuted = mute;
[V2TIMManager.sharedInstance setGroupInfo:groupInfo
succ:^{
weakSelf.muteAll = mute;
weakSelf.groupInfo.allMuted = mute;
[weakSelf setupGroupInfo:weakSelf.groupInfo];
if (completion) {
completion(0, nil);
}
}
fail:^(int code, NSString *desc) {
weakSelf.muteAll = !mute;
if (completion) {
completion(code, desc);
}
}];
}
- (void)mute:(BOOL)mute user:(TUIUserModel *)user {
if (!NSThread.isMainThread) {
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
[self mute:mute user:user];
});
return;
}
__weak typeof(self) weakSelf = self;
void (^callback)(int, NSString *, BOOL) = ^(int code, NSString *desc, BOOL mute) {
dispatch_async(dispatch_get_main_queue(), ^{
TUIMemberInfoCellData *existData = nil;
for (TUIMemberInfoCellData *data in weakSelf.muteMembersDataArray) {
if ([data.identifier isEqualToString:user.userId]) {
existData = data;
break;
}
}
if (code == 0 && mute) {
// mute succ
if (!existData) {
TUIMemberInfoCellData *cellData = [[TUIMemberInfoCellData alloc] init];
cellData.identifier = user.userId;
cellData.name = user.name ?: user.userId;
cellData.avatarUrl = user.avatar;
[weakSelf.muteMembersDataArray addObject:cellData];
}
} else if (code == 0 && !mute) {
// unmute succ
if (existData) {
[weakSelf.muteMembersDataArray removeObject:existData];
}
} else {
// fail
if ([weakSelf.delegate respondsToSelector:@selector(onError:desc:operate:)]) {
[weakSelf.delegate onError:code
desc:desc
operate:mute ?
TIMCommonLocalizableString(TUIKitGroupShutupOption) :
TIMCommonLocalizableString(TUIKitGroupDisShutupOption)
];
}
}
if ([weakSelf.delegate respondsToSelector:@selector(reloadData)]) {
[weakSelf.delegate reloadData];
}
});
};
[V2TIMManager.sharedInstance muteGroupMember:self.groupID
member:user.userId
muteTime:mute ? 365 * 24 * 3600 : 0
succ:^{
callback(0, nil, mute);
}
fail:^(int code, NSString *desc) {
callback(code, desc, mute);
}];
}
- (void)loadData {
self.groupInfoDatasArray = [NSMutableArray array];
self.muteMembersDataArray = [NSMutableArray array];
self.datas = [NSMutableArray arrayWithArray:@[ self.groupInfoDatasArray, self.muteMembersDataArray ]];
@weakify(self);
[V2TIMManager.sharedInstance getGroupsInfo:@[ self.groupID ?: @"" ]
succ:^(NSArray<V2TIMGroupInfoResult *> *groupResultList) {
@strongify(self);
V2TIMGroupInfoResult *result = groupResultList.firstObject;
if (result == nil || result.resultCode != 0) {
return;
}
V2TIMGroupInfo *groupInfo = result.info;
self.groupInfo = groupInfo;
[self setupGroupInfo:groupInfo];
self.muteAll = groupInfo.allMuted;
self.currentGroupTypeSupportSettingAdmin = [self canSupportSettingAdminAtThisGroupType:groupInfo.groupType];
self.currentGroupTypeSupportAddMemberOfBlocked = [self canSupportAddMemberOfBlockedAtThisGroupType:groupInfo.groupType];
}
fail:^(int code, NSString *desc) {
@strongify(self);
[self setupGroupInfo:nil];
}];
[self loadMuteMembers];
}
- (void)loadMuteMembers {
if (!NSThread.isMainThread) {
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
[self loadMuteMembers];
});
}
[self.muteMembersDataArray removeAllObjects];
if ([self.delegate respondsToSelector:@selector(reloadData)]) {
[self.delegate reloadData];
}
TUIMemberInfoCellData *add = [[TUIMemberInfoCellData alloc] init];
add.avatar = TUIContactCommonBundleImage(@"icon_add");
add.name = TIMCommonLocalizableString(TUIKitGroupAddShutupMember);
add.style = TUIMemberInfoCellStyleAdd;
[self.muteMembersDataArray addObject:add];
if ([self.delegate respondsToSelector:@selector(insertRowsAtIndexPaths:withRowAnimation:)]) {
[self.delegate insertRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:0 inSection:1] ] withRowAnimation:UITableViewRowAnimationNone];
}
[self setupGroupMembers:0 first:YES];
}
- (void)setupGroupMembers:(uint64_t)seq first:(uint64_t)first {
if (seq == 0 && !first) {
return;
}
__weak typeof(self) weakSelf = self;
[V2TIMManager.sharedInstance
getGroupMemberList:self.groupID
filter:V2TIM_GROUP_MEMBER_FILTER_ALL
nextSeq:0
succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
NSMutableArray *indexPaths = [NSMutableArray array];
for (V2TIMGroupMemberFullInfo *info in memberList) {
if (info.muteUntil && info.muteUntil > [NSDate.new timeIntervalSince1970]) {
TUIMemberInfoCellData *member = [[TUIMemberInfoCellData alloc] init];
member.avatarUrl = info.faceURL;
member.name = (info.nameCard ?: info.nickName) ?: info.userID;
member.identifier = info.userID;
BOOL exist = NO;
for (TUIMemberInfoCellData *data in weakSelf.muteMembersDataArray) {
if ([data.identifier isEqualToString:info.userID]) {
exist = YES;
break;
}
}
BOOL isSuper = (info.role == V2TIM_GROUP_MEMBER_ROLE_SUPER);
BOOL isAdMin = (info.role == V2TIM_GROUP_MEMBER_ROLE_ADMIN);
BOOL allowShowInMuteList = YES;
if (isSuper || isAdMin) {
allowShowInMuteList = NO;
}
if (!exist && allowShowInMuteList) {
[weakSelf.muteMembersDataArray addObject:member];
[indexPaths addObject:[NSIndexPath indexPathForRow:[weakSelf.muteMembersDataArray indexOfObject:member] inSection:1]];
}
}
}
if (indexPaths.count) {
if ([weakSelf.delegate respondsToSelector:@selector(insertRowsAtIndexPaths:withRowAnimation:)]) {
[weakSelf.delegate insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
}
}
[weakSelf setupGroupMembers:nextSeq first:NO];
}
fail:^(int code, NSString *desc){
}];
}
- (void)setupGroupInfo:(V2TIMGroupInfo *)groupInfo {
if (!NSThread.isMainThread) {
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
[self setupGroupInfo:groupInfo];
});
return;
}
if (groupInfo == nil) {
return;
}
[self.groupInfoDatasArray removeAllObjects];
TUICommonTextCellData *adminSetting = [[TUICommonTextCellData alloc] init];
adminSetting.key = TIMCommonLocalizableString(TUIKitGroupManageAdminSetting);
adminSetting.value = @"";
adminSetting.showAccessory = YES;
adminSetting.cselector = @selector(onSettingAdmin:);
[self.groupInfoDatasArray addObject:adminSetting];
TUICommonSwitchCellData *shutupAll = [[TUICommonSwitchCellData alloc] init];
shutupAll.title = TIMCommonLocalizableString(TUIKitGroupManageShutAll);
shutupAll.on = groupInfo.allMuted;
shutupAll.cswitchSelector = @selector(onMutedAll:);
[self.groupInfoDatasArray addObject:shutupAll];
if ([self.delegate respondsToSelector:@selector(reloadData)]) {
[self.delegate reloadData];
}
}
//- (void)onMutedAll:(TUICommonSwitchCellData *)switchData
//{
// if ([self.delegate respondsToSelector:@selector(onMutedAll:)]) {
// [self.delegate onMutedAll:switchData.isOn];
// }
//}
- (void)updateMuteMembersFilterAdmins {
[self loadMuteMembers];
}
- (NSMutableArray *)datas {
if (_datas == nil) {
_datas = [NSMutableArray array];
}
return _datas;
}
- (BOOL)canSupportSettingAdminAtThisGroupType:(NSString *)grouptype {
if ([grouptype isEqualToString:@"Work"] || [grouptype isEqualToString:@"AVChatRoom"]) {
return NO;
}
return YES;
}
- (BOOL)canSupportAddMemberOfBlockedAtThisGroupType:(NSString *)grouptype {
if ([grouptype isEqualToString:@"Work"]) {
return NO;
}
return YES;
}
@end

View File

@@ -0,0 +1,24 @@
//
// TUIGroupMemberDataProvider.h
// TXIMSDK_TUIKit_iOS
//
// Created by xiangzhang on 2021/7/2.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
@import ImSDK_Plus;
@class TUIGroupMemberCellData;
NS_ASSUME_NONNULL_BEGIN
@interface TUIGroupMemberDataProvider : NSObject
@property(nonatomic, strong) V2TIMGroupInfo *groupInfo;
@property(nonatomic, assign) BOOL isNoMoreData;
- (instancetype)initWithGroupID:(NSString *)groupID;
- (void)loadDatas:(void (^)(BOOL success, NSString *err, NSArray *datas))completion;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,86 @@
//
// TUIGroupMemberDataProvider.m
// TXIMSDK_TUIKit_iOS
//
// Created by xiangzhang on 2021/7/2.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIGroupMemberDataProvider.h"
#import <TIMCommon/TIMDefine.h>
#import "TUIMemberInfoCellData.h"
@interface TUIGroupMemberDataProvider ()
@property(nonatomic, strong) NSString *groupID;
@property(nonatomic, assign) NSUInteger index;
@end
@implementation TUIGroupMemberDataProvider
- (instancetype)initWithGroupID:(NSString *)groupID {
self = [super init];
if (self) {
self.groupID = groupID;
}
return self;
}
- (void)loadDatas:(void (^)(BOOL success, NSString *err, NSArray *datas))completion {
@weakify(self);
[[V2TIMManager sharedInstance] getGroupMemberList:self.groupID
filter:V2TIM_GROUP_MEMBER_FILTER_ALL
nextSeq:self.index
succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
@strongify(self);
self.index = nextSeq;
self.isNoMoreData = (nextSeq == 0);
NSMutableArray *arrayM = [NSMutableArray array];
NSMutableArray *ids = [NSMutableArray array];
NSMutableDictionary *map = [NSMutableDictionary dictionary];
for (V2TIMGroupMemberFullInfo *member in memberList) {
TUIMemberInfoCellData *user = [[TUIMemberInfoCellData alloc] init];
user.identifier = member.userID;
user.role = member.role;
if (member.nameCard.length > 0) {
user.name = member.nameCard;
} else if (member.friendRemark.length > 0) {
user.name = member.friendRemark;
} else if (member.nickName.length > 0) {
user.name = member.nickName;
} else {
user.name = member.userID;
}
[arrayM addObject:user];
[ids addObject:user.identifier];
if (user.identifier && user) {
map[user.identifier] = user;
}
}
[[V2TIMManager sharedInstance] getUsersInfo:ids
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
NSArray *userIDs = map.allKeys;
for (V2TIMUserFullInfo *info in infoList) {
if (![userIDs containsObject:info.userID]) {
continue;
}
TUIMemberInfoCellData *user = map[info.userID];
user.avatarUrl = info.faceURL;
}
if (completion) {
completion(YES, @"", arrayM);
}
}
fail:^(int code, NSString *desc) {
if (completion) {
completion(NO, desc, @[]);
}
}];
}
fail:^(int code, NSString *msg) {
if (completion) {
completion(NO, msg, @[]);
}
}];
}
@end

View File

@@ -0,0 +1,34 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This file declares the view model for the friend request interface.
* The view model can pull the friend application information through the interface provided by the IM SDK, and load the pulled information to facilitate the
* further display of the friend application interface.
*/
#import <Foundation/Foundation.h>
#import "TUICommonPendencyCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUINewFriendViewDataProvider : NSObject
@property(readonly) NSArray *dataList;
/**
* Has data not shown.
* YESThere are unshown requestsNOAll requests are loaded.
*/
@property BOOL hasNextData;
@property BOOL isLoading;
- (void)loadData;
- (void)removeData:(TUICommonPendencyCellData *)data;
- (void)agreeData:(TUICommonPendencyCellData *)data;
- (void)rejectData:(TUICommonPendencyCellData *)data;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,83 @@
//
// TUINewFriendViewModel.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/7.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUINewFriendViewDataProvider.h"
#import <TIMCommon/TIMDefine.h>
@interface TUINewFriendViewDataProvider ()
@property NSArray *dataList;
@property(nonatomic, assign) uint64_t origSeq;
@property(nonatomic, assign) uint64_t seq;
@property(nonatomic, assign) uint64_t timestamp;
@property(nonatomic, assign) uint64_t numPerPage;
@end
@implementation TUINewFriendViewDataProvider
- (instancetype)init {
self = [super init];
_numPerPage = 5;
_dataList = @[];
return self;
}
- (void)loadData {
if (self.isLoading) return;
self.isLoading = YES;
@weakify(self);
[[V2TIMManager sharedInstance]
getFriendApplicationList:^(V2TIMFriendApplicationResult *result) {
@strongify(self);
NSMutableArray *list = @[].mutableCopy;
for (V2TIMFriendApplication *item in result.applicationList) {
if (item.type == V2TIM_FRIEND_APPLICATION_COME_IN) {
TUICommonPendencyCellData *data = [[TUICommonPendencyCellData alloc] initWithPendency:item];
data.hideSource = YES;
[list addObject:data];
}
}
self.dataList = list;
self.isLoading = NO;
self.hasNextData = YES;
}
fail:nil];
}
- (void)removeData:(TUICommonPendencyCellData *)data {
NSMutableArray *dataList = [NSMutableArray arrayWithArray:self.dataList];
[dataList removeObject:data];
self.dataList = dataList;
[[V2TIMManager sharedInstance] deleteFriendApplication:data.application succ:nil fail:nil];
}
- (void)agreeData:(TUICommonPendencyCellData *)data {
[[V2TIMManager sharedInstance] acceptFriendApplication:data.application type:V2TIM_FRIEND_ACCEPT_AGREE_AND_ADD succ:nil fail:nil];
data.isAccepted = YES;
}
- (void)rejectData:(TUICommonPendencyCellData *)data {
[V2TIMManager.sharedInstance refuseFriendApplication:data.application
succ:^(V2TIMFriendOperationResult *result) {
}
fail:^(int code, NSString *desc){
}];
data.isRejected = YES;
}
@end

View File

@@ -0,0 +1,29 @@
//
// TUISettingAdminDataProvider.h
// TUIGroup
//
// Created by harvy on 2021/12/28.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
@class TUIUserModel;
NS_ASSUME_NONNULL_BEGIN
@interface TUISettingAdminDataProvider : NSObject
@property(nonatomic, copy) NSString *groupID;
@property(nonatomic, strong, readonly) NSMutableArray *datas;
@property(nonatomic, strong, readonly) NSMutableArray *owners;
@property(nonatomic, strong, readonly) NSMutableArray *admins;
- (void)loadData:(void (^)(int, NSString *))callback;
- (void)removeAdmin:(NSString *)userID callback:(void (^)(int, NSString *))callback;
- (void)settingAdmins:(NSArray<TUIUserModel *> *)userModels callback:(void (^)(int, NSString *))callback;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,212 @@
//
// TUISettingAdminDataProvider.m
// TUIGroup
//
// Created by harvy on 2021/12/28.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUISettingAdminDataProvider.h"
#import <ImSDK_Plus/ImSDK_Plus.h>
#import <TIMCommon/TIMDefine.h>
#import "TUIMemberInfoCellData.h"
@interface TUISettingAdminDataProvider ()
@property(nonatomic, strong) NSMutableArray *datas;
@property(nonatomic, strong) NSMutableArray *owners;
@property(nonatomic, strong) NSMutableArray *admins;
@end
@implementation TUISettingAdminDataProvider
- (void)removeAdmin:(NSString *)userID callback:(void (^)(int, NSString *))callback {
__weak typeof(self) weakSelf = self;
[V2TIMManager.sharedInstance setGroupMemberRole:self.groupID
member:userID
newRole:V2TIM_GROUP_MEMBER_ROLE_MEMBER
succ:^{
TUIMemberInfoCellData *exist = [self existAdmin:userID];
if (exist) {
[weakSelf.admins removeObject:exist];
}
if (callback) {
callback(0, nil);
}
}
fail:^(int code, NSString *desc) {
if (callback) {
callback(code, desc);
}
}];
}
- (void)settingAdmins:(NSArray<TUIUserModel *> *)userModels callback:(void (^)(int, NSString *))callback;
{
NSMutableArray *validUsers = [NSMutableArray array];
for (TUIUserModel *user in userModels) {
TUIMemberInfoCellData *exist = [self existAdmin:user.userId];
if (!exist) {
TUIMemberInfoCellData *data = [[TUIMemberInfoCellData alloc] init];
data.identifier = user.userId;
data.name = user.name;
data.avatarUrl = user.avatar;
[validUsers addObject:data];
}
}
if (validUsers.count == 0) {
if (callback) {
callback(0, nil);
}
return;
}
if (self.admins.count + validUsers.count > 11) {
if (callback) {
callback(-1, @"The number of administrator must be less than ten");
}
return;
}
__block int errorCode = 0;
__block NSString *errorMsg = nil;
NSMutableArray *results = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
for (TUIMemberInfoCellData *data in validUsers) {
dispatch_group_enter(group);
[V2TIMManager.sharedInstance setGroupMemberRole:self.groupID
member:data.identifier
newRole:V2TIM_GROUP_MEMBER_ROLE_ADMIN
succ:^{
[results addObject:data];
dispatch_group_leave(group);
}
fail:^(int code, NSString *desc) {
if (errorCode == 0) {
errorCode = code;
errorMsg = desc;
}
dispatch_group_leave(group);
}];
}
__weak typeof(self) weakSelf = self;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[weakSelf.admins addObjectsFromArray:results];
if (callback) {
callback(errorCode, errorMsg);
}
});
}
- (void)loadData:(void (^)(int, NSString *))callback {
{
TUIMemberInfoCellData *add = [[TUIMemberInfoCellData alloc] init];
add.style = TUIMemberInfoCellStyleAdd;
add.name = TIMCommonLocalizableString(TUIKitGroupAddAdmins);
add.avatar = TUIContactCommonBundleImage(@"icon_add");
[self.admins addObject:add];
}
__weak typeof(self) weakSelf = self;
__block int errorCode = 0;
__block NSString *errorMsg = nil;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[V2TIMManager.sharedInstance getGroupMemberList:self.groupID
filter:V2TIM_GROUP_MEMBER_FILTER_OWNER
nextSeq:0
succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
for (V2TIMGroupMemberFullInfo *info in memberList) {
TUIMemberInfoCellData *cellData = [[TUIMemberInfoCellData alloc] init];
cellData.identifier = info.userID;
cellData.name = (info.nameCard ?: info.nickName) ?: info.userID;
cellData.avatarUrl = info.faceURL;
if (info.role == V2TIM_GROUP_MEMBER_ROLE_SUPER) {
[weakSelf.owners addObject:cellData];
}
}
dispatch_group_leave(group);
}
fail:^(int code, NSString *desc) {
if (errorCode == 0) {
errorCode = code;
errorMsg = desc;
}
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[V2TIMManager.sharedInstance getGroupMemberList:self.groupID
filter:V2TIM_GROUP_MEMBER_FILTER_ADMIN
nextSeq:0
succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
for (V2TIMGroupMemberFullInfo *info in memberList) {
TUIMemberInfoCellData *cellData = [[TUIMemberInfoCellData alloc] init];
cellData.identifier = info.userID;
cellData.name = (info.nameCard ?: info.nickName) ?: info.userID;
cellData.avatarUrl = info.faceURL;
if (info.role == V2TIM_GROUP_MEMBER_ROLE_ADMIN) {
[weakSelf.admins addObject:cellData];
}
}
dispatch_group_leave(group);
}
fail:^(int code, NSString *desc) {
if (errorCode == 0) {
errorCode = code;
errorMsg = desc;
}
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
weakSelf.datas = [NSMutableArray arrayWithArray:@[ weakSelf.owners, weakSelf.admins ]];
if (callback) {
callback(errorCode, errorMsg);
}
});
}
- (TUIMemberInfoCellData *)existAdmin:(NSString *)userID {
TUIMemberInfoCellData *exist = nil;
for (TUIMemberInfoCellData *data in self.admins) {
if ([data.identifier isEqual:userID]) {
exist = data;
break;
}
}
return exist;
}
- (NSMutableArray *)datas {
if (_datas == nil) {
_datas = [NSMutableArray array];
}
return _datas;
}
- (NSMutableArray *)owners {
if (_owners == nil) {
_owners = [NSMutableArray array];
}
return _owners;
}
- (NSMutableArray *)admins {
if (_admins == nil) {
_admins = [NSMutableArray array];
}
return _admins;
}
@end

View File

@@ -0,0 +1,23 @@
//
// TUIContactDefine..h
// Pods
//
// Created by xiangzhang on 2022/10/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TIMCommonModel.h>
#ifndef TUIContactDefine_h
#define TUIContactDefine_h
typedef NS_ENUM(NSInteger, TUISelectMemberOptionalStyle) {
TUISelectMemberOptionalStyleNone = 0,
TUISelectMemberOptionalStyleAtAll = 1 << 0,
TUISelectMemberOptionalStyleTransferOwner = 1 << 1,
TUISelectMemberOptionalStylePublicMan = 1 << 2
};
typedef void (^SelectedFinished)(NSMutableArray<TUIUserModel *> *modelList);
#endif /* TUIContactDefine_h */

View File

@@ -0,0 +1,38 @@
//
// TUIContactConfig.h
// TUIContact
//
// Created by Tencent on 2024/9/23.
// Copyright © 2024 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_OPTIONS(NSInteger, TUIContactConfigItem) {
TUIContactConfigItem_None = 0,
TUIContactConfigItem_Alias = 1 << 0,
TUIContactConfigItem_MuteAndPin = 1 << 1,
TUIContactConfigItem_Background = 1 << 2,
TUIContactConfigItem_Block = 1 << 3,
TUIContactConfigItem_ClearChatHistory = 1 << 4,
TUIContactConfigItem_Delete = 1 << 5,
TUIContactConfigItem_AddFriend = 1 << 6,
};
@interface TUIContactConfig : NSObject
+ (TUIContactConfig *)sharedConfig;
/**
* Hide items in contact config interface.
*/
- (void)hideItemsInContactConfig:(TUIContactConfigItem)items;
/**
* Get the hidden status of specified item.
*/
- (BOOL)isItemHiddenInContactConfig:(TUIContactConfigItem)item;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,79 @@
//
// TUIContactConfig.m
// TUIContact
//
// Created by Tencent on 2024/9/23.
// Copyright © 2024 Tencent. All rights reserved.
//
#import "TUIContactConfig.h"
@interface TUIContactConfig()
@property (nonatomic, assign) BOOL hideContactAlias;
@property (nonatomic, assign) BOOL hideContactMuteAndPinItems;
@property (nonatomic, assign) BOOL hideContactBackgroundItem;
@property (nonatomic, assign) BOOL hideContactBlock;
@property (nonatomic, assign) BOOL hideContactClearChatHistory;
@property (nonatomic, assign) BOOL hideContactDelete;
@property (nonatomic, assign) BOOL hideContactAddFriend;
@end
@implementation TUIContactConfig
+ (TUIContactConfig *)sharedConfig {
static dispatch_once_t onceToken;
static TUIContactConfig *config;
dispatch_once(&onceToken, ^{
config = [[TUIContactConfig alloc] init];
});
return config;
}
- (instancetype)init {
self = [super init];
if (self) {
self.hideContactAlias = NO;
self.hideContactMuteAndPinItems = NO;
self.hideContactBackgroundItem = NO;
self.hideContactBlock = NO;
self.hideContactClearChatHistory = NO;
self.hideContactDelete = NO;
self.hideContactAddFriend = NO;
}
return self;
}
- (void)hideItemsInContactConfig:(TUIContactConfigItem)items {
self.hideContactAlias = items & TUIContactConfigItem_Alias;
self.hideContactMuteAndPinItems = items & TUIContactConfigItem_MuteAndPin;
self.hideContactBackgroundItem = items & TUIContactConfigItem_Background;
self.hideContactBlock = items & TUIContactConfigItem_Block;
self.hideContactClearChatHistory = items & TUIContactConfigItem_ClearChatHistory;
self.hideContactDelete = items & TUIContactConfigItem_Delete;
self.hideContactAddFriend = items & TUIContactConfigItem_AddFriend;
}
- (BOOL)isItemHiddenInContactConfig:(TUIContactConfigItem)item {
if (item & TUIContactConfigItem_Alias) {
return self.hideContactAlias;
} else if (item & TUIContactConfigItem_MuteAndPin) {
return self.hideContactMuteAndPinItems;
} else if (item & TUIContactConfigItem_Background) {
return self.hideContactBackgroundItem;
} else if (item & TUIContactConfigItem_Block) {
return self.hideContactBlock;
} else if (item & TUIContactConfigItem_ClearChatHistory) {
return self.hideContactClearChatHistory;
} else if (item & TUIContactConfigItem_Delete) {
return self.hideContactDelete;
} else if (item & TUIContactConfigItem_AddFriend) {
return self.hideContactAddFriend;
} else {
return NO;
}
}
@end

View File

@@ -0,0 +1,39 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <UIKit/UIKit.h>
#import "TUIGroupMemberCell.h"
@class TUIGroupMembersView;
/////////////////////////////////////////////////////////////////////////////////
//
// TUIGroupMembersViewDelegate
//
/////////////////////////////////////////////////////////////////////////////////
@protocol TUIGroupMembersViewDelegate <NSObject>
- (void)groupMembersView:(TUIGroupMembersView *)groupMembersView didSelectGroupMember:(TUIGroupMemberCellData *)groupMember;
- (void)groupMembersView:(TUIGroupMembersView *)groupMembersView didLoadMoreData:(void (^)(NSArray<TUIGroupMemberCellData *> *))completion;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIGroupMembersView
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUIGroupMembersView : UIView
@property(nonatomic, strong) UISearchBar *searchBar;
@property(nonatomic, strong) UICollectionView *collectionView;
@property(nonatomic, strong) UICollectionViewFlowLayout *flowLayout;
@property(nonatomic, weak) id<TUIGroupMembersViewDelegate> delegate;
@property(nonatomic, strong) UIActivityIndicatorView *indicatorView;
- (void)setData:(NSMutableArray<TUIGroupMemberCellData *> *)data;
@end

View File

@@ -0,0 +1,142 @@
//
// TUIGroupMembersView.m
// TUIKit
//
// Created by kennethmiao on 2018/10/11.
// Copyright © 2018 Tencent. All rights reserved.
//
#import "TUIGroupMembersView.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIGlobalization.h>
@interface TUIGroupMembersView () <UISearchBarDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property(nonatomic, strong) NSMutableArray<TUIGroupMemberCellData *> *data;
@end
@implementation TUIGroupMembersView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupViews];
}
return self;
}
- (void)setupViews {
self.backgroundColor = [UIColor whiteColor];
_flowLayout = [[UICollectionViewFlowLayout alloc] init];
_flowLayout.headerReferenceSize = CGSizeMake(self.frame.size.width, TGroupMembersController_Margin);
CGSize cellSize = [TUIGroupMemberCell getSize];
CGFloat y = _searchBar.frame.origin.y + _searchBar.frame.size.height;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(TGroupMembersController_Margin, y,
self.frame.size.width - 2 * TGroupMembersController_Margin, self.frame.size.height - y)
collectionViewLayout:_flowLayout];
[_collectionView registerClass:[TUIGroupMemberCell class] forCellWithReuseIdentifier:TGroupMemberCell_ReuseId];
_collectionView.collectionViewLayout = _flowLayout;
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.showsHorizontalScrollIndicator = NO;
_collectionView.showsVerticalScrollIndicator = NO;
_collectionView.backgroundColor = [UIColor clearColor];
_collectionView.contentInset = UIEdgeInsetsMake(0, 0, TMessageController_Header_Height, 0);
[self addSubview:_collectionView];
_indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
_indicatorView.hidesWhenStopped = YES;
[self.collectionView addSubview:_indicatorView];
_flowLayout.minimumLineSpacing =
(_collectionView.frame.size.width - cellSize.width * TGroupMembersController_Row_Count) / (TGroupMembersController_Row_Count - 1);
;
_flowLayout.minimumInteritemSpacing = _flowLayout.minimumLineSpacing;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _data.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
TUIGroupMemberCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:TGroupMemberCell_ReuseId forIndexPath:indexPath];
TUIGroupMemberCellData *data = _data[indexPath.row];
[cell setData:data];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
}
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return [TUIGroupMemberCell getSize];
}
#pragma mark - Load
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y > 0 && (scrollView.contentOffset.y >= scrollView.bounds.origin.y)) {
[self loadMoreData];
}
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
searchBar.showsCancelButton = YES;
UIButton *cancleBtn = [searchBar valueForKey:@"cancelButton"];
[cancleBtn setTitle:TIMCommonLocalizableString(Cancel) forState:UIControlStateNormal];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
searchBar.showsCancelButton = NO;
searchBar.text = @"";
[searchBar resignFirstResponder];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
searchBar.showsCancelButton = NO;
[searchBar resignFirstResponder];
[self reloadData];
}
- (void)setData:(NSMutableArray<TUIGroupMemberCellData *> *)data {
_data = data;
[self reloadData];
}
- (void)reloadData {
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
self.indicatorView.frame = CGRectMake(0, self.collectionView.contentSize.height, self.collectionView.bounds.size.width, TMessageController_Header_Height);
if (self.collectionView.contentSize.height > self.collectionView.frame.size.height) {
[self.indicatorView startAnimating];
} else {
[self.indicatorView stopAnimating];
}
}
- (void)loadMoreData {
if (![self.delegate respondsToSelector:@selector(groupMembersView:didLoadMoreData:)]) {
CGPoint point = self.collectionView.contentOffset;
point.y -= TMessageController_Header_Height;
[self.collectionView setContentOffset:point animated:YES];
return;
}
static BOOL isLoading = NO;
if (isLoading) {
return;
}
isLoading = YES;
__weak typeof(self) weakSelf = self;
[self.delegate groupMembersView:self
didLoadMoreData:^(NSArray<TUIGroupMemberCellData *> *moreData) {
isLoading = NO;
[weakSelf.data addObjectsFromArray:moreData];
CGPoint point = self.collectionView.contentOffset;
point.y -= TMessageController_Header_Height;
[weakSelf.collectionView setContentOffset:point animated:YES];
[weakSelf reloadData];
}];
}
@end

View File

@@ -0,0 +1,49 @@
//
// TUIStyleSelectViewController.h
// TUIKitDemo
//
// Created by wyl on 2022/11/7.
// Copyright © 2022 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#define kTUIKitFirstInitAppStyleID @"Classic"; // Classic / Minimalist
@class TUIStyleSelectCellModel;
typedef void (^StyleSelectCallback)(TUIStyleSelectCellModel *);
@protocol TUIStyleSelectControllerDelegate <NSObject>
- (void)onSelectStyle:(TUIStyleSelectCellModel *)cellModel;
@end
@interface TUIStyleSelectCell : UITableViewCell
@property(nonatomic, strong) UILabel *nameLabel;
@property(nonatomic, strong) UIImageView *chooseIconView;
@property(nonatomic, strong) TUIStyleSelectCellModel *cellModel;
@end
@interface TUIStyleSelectCellModel : NSObject
@property(nonatomic, copy) NSString *styleID;
@property(nonatomic, strong) NSString *styleName;
@property(nonatomic, assign) BOOL selected;
@end
@interface TUIStyleSelectViewController : UIViewController
@property(nonatomic, weak) id<TUIStyleSelectControllerDelegate> delegate;
+ (BOOL)isClassicEntrance;
- (void)setBackGroundColor:(UIColor *)color;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,298 @@
//
// TUIStyleSelectViewController.m
// TUIKitDemo
//
// Created by wyl on 2022/11/7.
// Copyright © 2022 Tencent. All rights reserved.
//
#import "TUIStyleSelectViewController.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIDarkModel.h>
#import <TUICore/TUIGlobalization.h>
#import <TUICore/TUIThemeManager.h>
@implementation TUIStyleSelectCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setupViews];
}
return self;
}
- (void)setCellModel:(TUIStyleSelectCellModel *)cellModel {
_cellModel = cellModel;
self.nameLabel.text = cellModel.styleName;
self.nameLabel.textColor = cellModel.selected ? RGBA(0, 110, 255, 1) : TIMCommonDynamicColor(@"form_title_color", @"#000000");
self.chooseIconView.hidden = !cellModel.selected;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
- (void)setupViews {
[self.contentView addSubview:self.nameLabel];
[self.contentView addSubview:self.chooseIconView];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
[self.chooseIconView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.contentView);
make.width.mas_equalTo(20);
make.height.mas_equalTo(20);
make.trailing.mas_equalTo(-16);
}];
[self.nameLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(16);
make.trailing.mas_equalTo(self.chooseIconView.mas_leading).mas_offset(-2);
make.height.mas_equalTo(self.nameLabel.font.lineHeight);
make.centerY.mas_equalTo(self.contentView);
}];
}
- (UILabel *)nameLabel {
if (_nameLabel == nil) {
_nameLabel = [[UILabel alloc] init];
_nameLabel.font = [UIFont systemFontOfSize:16.0];
_nameLabel.text = @"1233";
_nameLabel.textAlignment = isRTL()?NSTextAlignmentRight:NSTextAlignmentLeft;
_nameLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
}
return _nameLabel;
}
- (UIImageView *)chooseIconView {
if (_chooseIconView == nil) {
_chooseIconView = [[UIImageView alloc] init];
_chooseIconView.image = TIMCommonBundleImage(@"default_choose");
}
return _chooseIconView;
}
@end
@implementation TUIStyleSelectCellModel
@end
@interface TUIStyleSelectViewController () <UITableViewDelegate, UITableViewDataSource>
@property(nonatomic, strong) TUINaviBarIndicatorView *titleView;
@property(nonatomic, strong) UITableView *tableView;
@property(nonatomic, strong) NSMutableArray *datas;
@property(nonatomic, strong) TUIStyleSelectCellModel *selectModel;
@end
@implementation TUIStyleSelectViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupViews];
[self prepareData];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (@available(iOS 15.0, *)) {
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
[appearance configureWithDefaultBackground];
appearance.shadowColor = nil;
appearance.backgroundEffect = nil;
appearance.backgroundColor = self.tintColor;
self.navigationController.navigationBar.backgroundColor = self.tintColor;
self.navigationController.navigationBar.barTintColor = self.tintColor;
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.navigationController.navigationBar.standardAppearance = appearance;
/**
* iOS15
* New feature in iOS15: sliding border style
*/
self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
} else {
self.navigationController.navigationBar.backgroundColor = self.tintColor;
self.navigationController.navigationBar.barTintColor = self.tintColor;
self.navigationController.navigationBar.shadowImage = [UIImage new];
}
self.navigationController.view.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#F2F3F5");
self.navigationController.navigationBarHidden = NO;
}
- (UIColor *)tintColor {
return TIMCommonDynamicColor(@"head_bg_gradient_start_color", @"#EBF0F6");
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navigationController.navigationBarHidden = YES;
}
- (void)setupViews {
self.edgesForExtendedLayout = UIRectEdgeNone;
self.automaticallyAdjustsScrollViewInsets = NO;
/**
* Not setting it will cause some problems such as confusion in position, no animation, etc.
*/
self.definesPresentationContext = YES;
self.navigationController.navigationBarHidden = NO;
_titleView = [[TUINaviBarIndicatorView alloc] init];
[_titleView setTitle:TIMCommonLocalizableString(TIMAppSelectStyle)];
self.navigationItem.titleView = _titleView;
self.navigationItem.title = @"";
UIImage *image = TIMCommonDynamicImage(@"nav_back_img", [UIImage imageNamed:@"ic_back_white"]);
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
image = [image rtl_imageFlippedForRightToLeftLayoutDirection];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:image forState:UIControlStateNormal];
[backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.hidesBackButton = YES;
[self.view addSubview:self.tableView];
}
- (void)back {
[self.navigationController popViewControllerAnimated:YES];
}
- (void)prepareData {
TUIStyleSelectCellModel *classic = [[TUIStyleSelectCellModel alloc] init];
classic.styleID = @"Classic";
classic.styleName = TIMCommonLocalizableString(TUIKitClassic);
classic.selected = NO;
TUIStyleSelectCellModel *mini = [[TUIStyleSelectCellModel alloc] init];
mini.styleID = @"Minimalist";
mini.styleName = TIMCommonLocalizableString(TUIKitMinimalist);
mini.selected = NO;
self.datas = [NSMutableArray arrayWithArray:@[ classic, mini ]];
NSString *styleID = [[NSUserDefaults standardUserDefaults] objectForKey:@"StyleSelectkey"];
for (TUIStyleSelectCellModel *cellModel in self.datas) {
if ([cellModel.styleID isEqual:styleID]) {
cellModel.selected = YES;
self.selectModel = cellModel;
break;
}
}
}
#pragma mark - UITableViewDelegate, UITableViewDataSource
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.0;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return [UIView new];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.datas.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TUIStyleSelectCellModel *cellModel = self.datas[indexPath.row];
TUIStyleSelectCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
cell.cellModel = cellModel;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
TUIStyleSelectCellModel *cellModel = self.datas[indexPath.row];
[[NSUserDefaults standardUserDefaults] setValue:cellModel.styleID forKey:@"StyleSelectkey"];
[NSUserDefaults.standardUserDefaults synchronize];
/**
* Handling UI selection
*/
self.selectModel.selected = NO;
cellModel.selected = YES;
self.selectModel = cellModel;
[tableView reloadData];
/**
* Notify page dynamic refresh
*/
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
if ([weakSelf.delegate respondsToSelector:@selector(onSelectStyle:)]) {
[weakSelf.delegate onSelectStyle:cellModel];
}
});
}
- (UITableView *)tableView {
if (_tableView == nil) {
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#FFFFFF");
[_tableView registerClass:TUIStyleSelectCell.class forCellReuseIdentifier:@"cell"];
}
return _tableView;
}
- (void)setBackGroundColor:(UIColor *)color {
self.view.backgroundColor = color;
self.tableView.backgroundColor = color;
}
- (NSMutableArray *)datas {
if (_datas == nil) {
_datas = [NSMutableArray array];
}
return _datas;
}
+ (NSString *)getCurrentStyleSelectID {
NSString *styleID = [[NSUserDefaults standardUserDefaults] objectForKey:@"StyleSelectkey"];
if (IS_NOT_EMPTY_NSSTRING(styleID)) {
return styleID;
} else {
// First Init
NSString *initStyleID = kTUIKitFirstInitAppStyleID;
[[NSUserDefaults standardUserDefaults] setValue:initStyleID forKey:@"StyleSelectkey"];
[NSUserDefaults.standardUserDefaults synchronize];
return initStyleID;
}
}
+ (BOOL)isClassicEntrance {
NSString *styleID = [self.class getCurrentStyleSelectID];
if ([styleID isKindOfClass:NSString.class]) {
if (styleID.length > 0) {
if ([styleID isEqualToString:@"Classic"]) {
return YES;
}
}
}
return NO;
}
@end

View File

@@ -0,0 +1,79 @@
//
// ThemeSelectController.h
// TUIKitDemo
//
// Created by harvy on 2022/1/5.
// Copyright © 2022 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class TUIThemeSelectCollectionViewCellModel;
typedef void (^ThemeSelectCallback)(TUIThemeSelectCollectionViewCellModel *cellModel);
@protocol TUIThemeSelectControllerDelegate <NSObject>
- (void)onSelectTheme:(TUIThemeSelectCollectionViewCellModel *)cellModel;
@end
@interface TUIThemeSelectCollectionViewCellModel : NSObject
@property(nonatomic, strong) UIImage *backImage;
@property(nonatomic, strong) UIColor *startColor;
@property(nonatomic, strong) UIColor *endColor;
@property(nonatomic, assign) BOOL selected;
@property(nonatomic, copy) NSString *themeName;
@property(nonatomic, copy) NSString *themeID;
@end
@interface TUIThemeSelectCollectionViewCell : UICollectionViewCell
@property(nonatomic, strong) UIImageView *backView;
@property(nonatomic, strong) UIButton *chooseButton;
@property(nonatomic, strong) UILabel *descLabel;
@property(nonatomic, strong) TUIThemeSelectCollectionViewCellModel *cellModel;
@property(nonatomic, copy) ThemeSelectCallback onSelect;
@end
@interface TUIThemeHeaderCollectionViewCell : UICollectionViewCell
@property(nonatomic, strong) TUIThemeSelectCollectionViewCellModel *cellModel;
@property(nonatomic, copy) ThemeSelectCallback onSelect;
@end
@interface TUIThemeSelectController : UIViewController
@property(nonatomic, weak) id<TUIThemeSelectControllerDelegate> delegate;
@property(nonatomic, assign) BOOL disable;
/**
* Disable follow system style.
*/
+ (void)disableFollowSystemStyle;
/**
* Applying the theme, if the id is empty, use the last setting.
*/
+ (void)applyTheme:(NSString *__nullable)themeID;
/**
* Applying the last theme
*/
+ (void)applyLastTheme;
/**
* Get the last used theme name
*/
+ (NSString *)getLastThemeName;
- (void)setBackGroundColor:(UIColor *)color;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,636 @@
//
// ThemeSelectController.m
// TUIKitDemo
//
// Created by harvy on 2022/1/5.
// Copyright © 2022 Tencent. All rights reserved.
//
#import "TUIThemeSelectController.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIDarkModel.h>
#import <TUICore/TUIThemeManager.h>
#import <TUICore/TUITool.h>
#import <TIMCommon/TIMDefine.h>
@implementation TUIThemeSelectCollectionViewCellModel
@end
@implementation TUIThemeSelectCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupViews];
}
return self;
}
- (void)setCellModel:(TUIThemeSelectCollectionViewCellModel *)cellModel {
_cellModel = cellModel;
self.chooseButton.selected = cellModel.selected;
self.descLabel.text = cellModel.themeName;
self.backView.image = cellModel.backImage;
}
- (void)setupViews {
self.contentView.layer.cornerRadius = 5.0;
self.contentView.layer.masksToBounds = YES;
[self.contentView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap)]];
[self.contentView addSubview:self.backView];
[self.contentView addSubview:self.chooseButton];
[self.contentView addSubview:self.descLabel];
}
- (void)onTap {
if (self.onSelect) {
self.onSelect(self.cellModel);
}
}
- (void)layoutSubviews {
[super layoutSubviews];
self.backView.frame = self.contentView.bounds;
self.chooseButton.frame = CGRectMake(self.contentView.mm_w - 6 - 20, 6, 20, 20);
self.descLabel.frame = CGRectMake(0, self.contentView.mm_h - 28, self.contentView.mm_w, 28);
}
- (UIImageView *)backView {
if (_backView == nil) {
_backView = [[UIImageView alloc] init];
}
return _backView;
}
- (UIButton *)chooseButton {
if (_chooseButton == nil) {
_chooseButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_chooseButton setImage:TUIContactDynamicImage(@"", [UIImage imageNamed:TUIContactImagePath(@"add_unselect")]) forState:UIControlStateNormal];
[_chooseButton setImage:TUIContactDynamicImage(@"", [UIImage imageNamed:TUIContactImagePath(@"add_selected")]) forState:UIControlStateSelected];
_chooseButton.userInteractionEnabled = NO;
}
return _chooseButton;
}
- (UILabel *)descLabel {
if (_descLabel == nil) {
_descLabel = [[UILabel alloc] init];
_descLabel.textColor = [UIColor colorWithRed:255 / 255.0 green:255 / 255.0 blue:255 / 255.0 alpha:1 / 1.0];
_descLabel.font = [UIFont systemFontOfSize:13.0];
_descLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.1];
_descLabel.textAlignment = NSTextAlignmentCenter;
}
return _descLabel;
}
@end
@interface TUIThemeHeaderCollectionViewCell ()
@property(nonatomic, strong) UISwitch *switcher;
@property(nonatomic, strong) UILabel *titleLabel;
@property(nonatomic, strong) UILabel *subTitleLabel;
@end
@implementation TUIThemeHeaderCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupBaseViews];
}
return self;
}
- (void)setCellModel:(TUIThemeSelectCollectionViewCellModel *)cellModel {
_cellModel = cellModel;
self.titleLabel.text = TIMCommonLocalizableString(TUIKitThemeNameSystemFollowTitle);
self.subTitleLabel.text = TIMCommonLocalizableString(TUIKitThemeNameSystemFollowSubTitle);
self.switcher.on = cellModel.selected;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(kScale375(24));
make.top.mas_equalTo(12);
make.trailing.mas_equalTo(self.switcher.mas_leading).mas_offset(- 3);
make.height.mas_equalTo(20);
}];
[self.subTitleLabel sizeToFit];
[self.subTitleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.titleLabel);
make.trailing.mas_equalTo(self.switcher.mas_leading);
make.top.mas_equalTo(self.titleLabel.mas_bottom).mas_offset(3);
make.bottom.mas_equalTo(self.contentView.mas_bottom);
}];
[self.switcher mas_remakeConstraints:^(MASConstraintMaker *make) {
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-kScale375(24));
make.top.mas_equalTo(self.titleLabel);
make.width.mas_equalTo(35);
make.height.mas_equalTo(20);
}];
}
- (void)setupBaseViews {
self.contentView.layer.cornerRadius = 5.0;
self.contentView.layer.masksToBounds = YES;
self.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
self.titleLabel.font = [UIFont systemFontOfSize:16.0];
self.titleLabel.rtlAlignment = TUITextRTLAlignmentLeading;
self.titleLabel.numberOfLines = 0;
[self.contentView addSubview:self.titleLabel];
self.subTitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.subTitleLabel.textColor = TIMCommonDynamicColor(@"form_desc_color", @"#888888");
self.subTitleLabel.rtlAlignment = TUITextRTLAlignmentLeading;
self.subTitleLabel.font = [UIFont systemFontOfSize:12.0];
self.subTitleLabel.backgroundColor = [UIColor clearColor];
self.subTitleLabel.numberOfLines = 0;
[self.contentView addSubview:self.subTitleLabel];
self.switcher = [[UISwitch alloc] initWithFrame:CGRectZero];
_switcher.onTintColor = TIMCommonDynamicColor(@"common_switch_on_color", @"#147AFF");
[_switcher addTarget:self action:@selector(switchClick:) forControlEvents:UIControlEventValueChanged];
[self.contentView addSubview:self.switcher];
}
- (void)switchClick:(id)sw {
if (self.onSelect) {
self.onSelect(self.cellModel);
}
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
self.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
}
@end
@interface TUIThemeSelectController () <UICollectionViewDelegate, UICollectionViewDataSource>
@property(nonatomic, strong) TUINaviBarIndicatorView *titleView;
@property(nonatomic, strong) UICollectionView *collectionView;
@property(nonatomic, strong) NSMutableArray *datas;
@property(nonatomic, strong) TUIThemeSelectCollectionViewCellModel *selectModel;
@property(nonatomic, strong) TUIThemeSelectCollectionViewCellModel *systemModel;
@end
@implementation TUIThemeSelectController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupViews];
[self prepareData];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThemeChanged) name:TUIDidApplyingThemeChangedNotfication object:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (@available(iOS 15.0, *)) {
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
[appearance configureWithDefaultBackground];
appearance.shadowColor = nil;
appearance.backgroundEffect = nil;
appearance.backgroundColor = self.tintColor;
self.navigationController.navigationBar.backgroundColor = self.tintColor;
self.navigationController.navigationBar.barTintColor = self.tintColor;
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.navigationController.navigationBar.standardAppearance = appearance;
self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
} else {
self.navigationController.navigationBar.backgroundColor = self.tintColor;
self.navigationController.navigationBar.barTintColor = self.tintColor;
self.navigationController.navigationBar.shadowImage = [UIImage new];
}
self.navigationController.view.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#F2F3F5");
self.navigationController.navigationBarHidden = NO;
}
- (UIColor *)tintColor {
return TIMCommonDynamicColor(@"head_bg_gradient_start_color", @"#EBF0F6");
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navigationController.navigationBarHidden = YES;
}
- (void)setupViews {
self.edgesForExtendedLayout = UIRectEdgeNone;
self.automaticallyAdjustsScrollViewInsets = NO;
self.definesPresentationContext = YES;
self.navigationController.navigationBarHidden = NO;
_titleView = [[TUINaviBarIndicatorView alloc] init];
[_titleView setTitle:TIMCommonLocalizableString(TIMAppChangeTheme)];
self.navigationItem.titleView = _titleView;
self.navigationItem.title = @"";
UIImage *image = TIMCommonDynamicImage(@"nav_back_img", [UIImage imageNamed:TIMCommonImagePath(@"nav_back")]);
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
image = [image rtl_imageFlippedForRightToLeftLayoutDirection];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:image forState:UIControlStateNormal];
[backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.hidesBackButton = YES;
[self.view addSubview:self.collectionView];
}
- (void)prepareData {
NSString *lastThemeID = [self.class getCacheThemeID];
BOOL isSystemDark = NO;
BOOL isSystemLight = NO;
if (@available(iOS 13.0, *)) {
if ([lastThemeID isEqualToString:@"system"]) {
if ((self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)) {
isSystemDark = YES;
} else {
isSystemLight = YES;
}
}
}
TUIThemeSelectCollectionViewCellModel *system = [[TUIThemeSelectCollectionViewCellModel alloc] init];
system.backImage = [self imageWithColors:@[ @"#FEFEFE", @"#FEFEFE" ]];
system.themeID = @"system";
system.themeName = TIMCommonLocalizableString(TUIKitThemeNameSystem);
system.selected = [lastThemeID isEqual:system.themeID];
self.systemModel = system;
TUIThemeSelectCollectionViewCellModel *serious = [[TUIThemeSelectCollectionViewCellModel alloc] init];
serious.backImage = TUIContactDynamicImage(@"", [UIImage imageNamed:TUIContactImagePath(@"theme_cover_serious")]);
serious.themeID = @"serious";
serious.themeName = TIMCommonLocalizableString(TUIKitThemeNameSerious);
serious.selected = [lastThemeID isEqual:serious.themeID];
TUIThemeSelectCollectionViewCellModel *light = [[TUIThemeSelectCollectionViewCellModel alloc] init];
light.backImage = TUIContactDynamicImage(@"", [UIImage imageNamed:TUIContactImagePath(@"theme_cover_light")]);
light.themeID = @"light";
light.themeName = TIMCommonLocalizableString(TUIKitThemeNameLight);
light.selected = ([lastThemeID isEqual:light.themeID] || isSystemLight);
TUIThemeSelectCollectionViewCellModel *mingmei = [[TUIThemeSelectCollectionViewCellModel alloc] init];
mingmei.backImage = TUIContactDynamicImage(@"", [UIImage imageNamed:TUIContactImagePath(@"theme_cover_lively")]);
mingmei.themeID = @"lively";
mingmei.themeName = TIMCommonLocalizableString(TUIKitThemeNameLivey);
mingmei.selected = [lastThemeID isEqual:mingmei.themeID];
TUIThemeSelectCollectionViewCellModel *dark = [[TUIThemeSelectCollectionViewCellModel alloc] init];
dark.backImage = TUIContactDynamicImage(@"", [UIImage imageNamed:TUIContactImagePath(@"theme_cover_dark")]);
dark.themeID = @"dark";
dark.themeName = TIMCommonLocalizableString(TUIKitThemeNameDark);
dark.selected = ([lastThemeID isEqual:dark.themeID] || isSystemDark);
self.datas = [NSMutableArray arrayWithArray:@[ light, serious, mingmei, dark ]];
for (TUIThemeSelectCollectionViewCellModel *cellModel in self.datas) {
if (cellModel.selected) {
self.selectModel = cellModel;
break;
}
}
if (gDisableFollowSystemStyle) {
return;
}
if (self.selectModel == nil || [lastThemeID isEqualToString:@"system"]) {
self.selectModel = system;
}
}
- (void)back {
if (self.disable) {
return;
}
[self.navigationController popViewControllerAnimated:YES];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.collectionView.frame = self.view.bounds;
}
+ (void)cacheThemeID:(NSString *)themeID {
[NSUserDefaults.standardUserDefaults setObject:themeID forKey:@"current_theme_id"];
[NSUserDefaults.standardUserDefaults synchronize];
}
+ (NSString *)getCacheThemeID {
NSString *lastThemeID = [NSUserDefaults.standardUserDefaults objectForKey:@"current_theme_id"];
if (lastThemeID == nil || lastThemeID.length == 0) {
lastThemeID = @"system";
}
return lastThemeID;
}
+ (void)changeFollowSystemChangeThemeSwitch:(BOOL)flag {
if (flag) {
[NSUserDefaults.standardUserDefaults setObject:@"0" forKey:@"followSystemChangeThemeSwitch"];
} else {
[NSUserDefaults.standardUserDefaults setObject:@"1" forKey:@"followSystemChangeThemeSwitch"];
}
[NSUserDefaults.standardUserDefaults synchronize];
}
+ (BOOL)followSystemChangeThemeSwitch {
/**
* The first time to start or not setting, follow the system settings in default
*/
if ([[self.class getCacheThemeID] isEqualToString:@"system"]) {
return YES;
}
NSString *followSystemChangeThemeSwitch = [NSUserDefaults.standardUserDefaults objectForKey:@"followSystemChangeThemeSwitch"];
if (followSystemChangeThemeSwitch && followSystemChangeThemeSwitch.length > 0) {
if ([followSystemChangeThemeSwitch isEqualToString:@"1"]) {
return YES;
}
}
return NO;
}
#pragma mark - UICollectionViewDelegate, UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.datas.count;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
TUIThemeSelectCollectionViewCell *reusableView = nil;
if (!gDisableFollowSystemStyle && kind == UICollectionElementKindSectionHeader) {
BOOL changeThemeswitch = [self.class followSystemChangeThemeSwitch];
TUIThemeSelectCollectionViewCellModel *system = [[TUIThemeSelectCollectionViewCellModel alloc] init];
system.selected = changeThemeswitch;
TUIThemeSelectCollectionViewCell *headerview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:@"HeaderView"
forIndexPath:indexPath];
headerview.cellModel = system;
__weak typeof(self) weakSelf = self;
headerview.onSelect = ^(TUIThemeSelectCollectionViewCellModel *_Nonnull cellModel) {
[weakSelf onSelectFollowSystem:cellModel];
};
reusableView = headerview;
}
return reusableView;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
TUIThemeSelectCollectionViewCellModel *cellModel = self.datas[indexPath.item];
TUIThemeSelectCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
cell.cellModel = cellModel;
__weak typeof(self) weakSelf = self;
cell.onSelect = ^(TUIThemeSelectCollectionViewCellModel *_Nonnull cellModel) {
[weakSelf onSelectTheme:cellModel];
};
return cell;
}
- (void)onSelectFollowSystem:(TUIThemeSelectCollectionViewCellModel *)cellModel {
[self.class changeFollowSystemChangeThemeSwitch:cellModel.selected];
if (cellModel.selected) {
for (TUIThemeSelectCollectionViewCellModel *cellModel in self.datas) {
if (cellModel.selected) {
self.selectModel = cellModel;
break;
}
}
[self onSelectTheme:self.selectModel];
} else {
[self onSelectTheme:self.systemModel];
}
}
- (void)onSelectTheme:(TUIThemeSelectCollectionViewCellModel *)cellModel {
if (self.disable) {
return;
}
if (cellModel && ![cellModel.themeID isEqualToString:@"system"]) {
/**
* As long as the theme is selected, turn off the switch
*/
[self.class changeFollowSystemChangeThemeSwitch:YES];
}
/**
* Change the theme
*/
self.selectModel.selected = NO;
cellModel.selected = YES;
self.selectModel = cellModel;
[self.collectionView reloadData];
/**
* Cache the currently selected theme
*/
[self.class cacheThemeID:self.selectModel.themeID];
// Applying theme
[self.class applyTheme:self.selectModel.themeID];
// Notify
if ([self.delegate respondsToSelector:@selector(onSelectTheme:)]) {
[self.delegate onSelectTheme:self.selectModel];
}
}
static BOOL gDisableFollowSystemStyle = NO;
+ (void)disableFollowSystemStyle {
gDisableFollowSystemStyle = YES;
}
+ (void)applyLastTheme {
[self applyTheme:nil];
}
+ (void)applyTheme:(NSString *__nullable)themeID {
NSString *lastThemeID = [self getCacheThemeID];
if (themeID.length) {
lastThemeID = themeID;
}
if (lastThemeID == nil || lastThemeID.length == 0 || [lastThemeID isEqual:@"system"]) {
/**
* Uninstall the theme and let it follow system changes
*/
[TUIShareThemeManager unApplyThemeForModule:TUIThemeModuleAll];
} else {
[TUIShareThemeManager applyTheme:lastThemeID forModule:TUIThemeModuleAll];
}
if (gDisableFollowSystemStyle) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if (@available(iOS 13.0, *)) {
if (lastThemeID == nil || lastThemeID.length == 0 || [lastThemeID isEqual:@"system"]) {
/**
* Following system settings
*/
UIApplication.sharedApplication.keyWindow.overrideUserInterfaceStyle = 0;
} else if ([lastThemeID isEqual:@"dark"]) {
/**
* Mandatory switch to dark mode
*/
UIApplication.sharedApplication.keyWindow.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
} else {
/**
* Ignoring the system settings, mandatory swtich to light mode, and apply the current theme
*/
UIApplication.sharedApplication.keyWindow.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
}
});
}
+ (NSString *)getLastThemeName {
NSString *themeID = [self getCacheThemeID];
if ([themeID isEqualToString:@"system"]) {
return TIMCommonLocalizableString(TUIKitThemeNameSystem);
} else if ([themeID isEqualToString:@"serious"]) {
return TIMCommonLocalizableString(TUIKitThemeNameSerious);
} else if ([themeID isEqualToString:@"light"]) {
return TIMCommonLocalizableString(TUIKitThemeNameLight);
} else if ([themeID isEqualToString:@"lively"]) {
return TIMCommonLocalizableString(TUIKitThemeNameLivey);
} else if ([themeID isEqualToString:@"dark"]) {
return TIMCommonLocalizableString(TUIKitThemeNameDark);
} else {
return @"";
}
}
- (UICollectionView *)collectionView {
if (_collectionView == nil) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
CGFloat itemWidth = (UIScreen.mainScreen.bounds.size.width - 12.0 - 32.0) * 0.5;
CGFloat itemHeight = itemWidth * 232.0 / 331.0;
layout.itemSize = CGSizeMake(itemWidth, itemHeight);
layout.minimumLineSpacing = 12;
layout.minimumInteritemSpacing = 0;
layout.sectionInset = UIEdgeInsetsMake(12, 16, 12, 16);
if (!gDisableFollowSystemStyle) {
layout.headerReferenceSize = CGSizeMake((UIScreen.mainScreen.bounds.size.width), 120);
}
_collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
_collectionView.delegate = self;
_collectionView.dataSource = self;
[_collectionView registerClass:TUIThemeSelectCollectionViewCell.class forCellWithReuseIdentifier:@"cell"];
[_collectionView registerClass:[TUIThemeHeaderCollectionViewCell class]
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:@"HeaderView"];
_collectionView.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
}
return _collectionView;
}
- (NSMutableArray *)datas {
if (_datas == nil) {
_datas = [NSMutableArray array];
}
return _datas;
}
- (UIImage *)imageWithColors:(NSArray<NSString *> *)hexColors {
CGSize imageSize = CGSizeMake(165, 116);
NSMutableArray *array = [NSMutableArray array];
for (NSString *hex in hexColors) {
UIColor *color = [UIColor tui_colorWithHex:hex];
[array addObject:(__bridge id)color.CGColor];
}
CGFloat locations[] = {0.5, 1.0};
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 1);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGColorSpaceRef colorSpace = CGColorGetColorSpace([UIColor tui_colorWithHex:hexColors.lastObject].CGColor);
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)array, locations);
CGPoint start;
CGPoint end;
start = CGPointMake(0.0, 0.0);
end = CGPointMake(imageSize.width, imageSize.height);
// switch (gradientType) {
// case GradientFromTopToBottom:
// start = CGPointMake(imageSize.width/2, 0.0);
// end = CGPointMake(imageSize.width/2, imageSize.height);
// break;
// case GradientFromLeftToRight:
// start = CGPointMake(0.0, imageSize.height/2);
// end = CGPointMake(imageSize.width, imageSize.height/2);
// break;
// case GradientFromLeftTopToRightBottom:
// start = CGPointMake(0.0, 0.0);
// end = CGPointMake(imageSize.width, imageSize.height);
// break;
// case GradientFromLeftBottomToRightTop:
// start = CGPointMake(0.0, imageSize.height);
// end = CGPointMake(imageSize.width, 0.0);
// break;
// default:
// break;
// }
CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradient);
CGContextRestoreGState(context);
CGColorSpaceRelease(colorSpace);
UIGraphicsEndImageContext();
return image;
}
- (void)setBackGroundColor:(UIColor *)color {
self.view.backgroundColor = color;
self.collectionView.backgroundColor = color;
}
// MARK: ThemeChanged
- (void)onThemeChanged {
dispatch_async(dispatch_get_main_queue(), ^{
[self prepareData];
[self.collectionView reloadData];
});
}
@end

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeUserID</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
</array>
</dict>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More