增加换肤功能
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
27
TUIKit/TUIContact/BaseCell/CellData/TUIMemberInfoCellData.h
Normal 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
|
||||
13
TUIKit/TUIContact/BaseCell/CellData/TUIMemberInfoCellData.m
Normal 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
|
||||
29
TUIKit/TUIContact/BaseCell/CellUI/TUICommonContactCell.h
Normal 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
|
||||
114
TUIKit/TUIContact/BaseCell/CellUI/TUICommonContactCell.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
120
TUIKit/TUIContact/BaseCell/CellUI/TUICommonContactSelectCell.m
Normal 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
|
||||
@@ -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
|
||||
141
TUIKit/TUIContact/BaseCell/CellUI/TUICommonContactSwitchCell.m
Normal 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
|
||||
42
TUIKit/TUIContact/BaseCell/CellUI/TUICommonContactTextCell.h
Normal 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
|
||||
132
TUIKit/TUIContact/BaseCell/CellUI/TUICommonContactTextCell.m
Normal 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
|
||||
31
TUIKit/TUIContact/BaseCell/CellUI/TUICommonPendencyCell.h
Normal 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
|
||||
216
TUIKit/TUIContact/BaseCell/CellUI/TUICommonPendencyCell.m
Normal 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
|
||||
28
TUIKit/TUIContact/BaseCell/CellUI/TUIContactActionCell.h
Normal 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
|
||||
107
TUIKit/TUIContact/BaseCell/CellUI/TUIContactActionCell.m
Normal 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
|
||||
25
TUIKit/TUIContact/BaseCell/CellUI/TUIFindContactCell.h
Normal 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
|
||||
138
TUIKit/TUIContact/BaseCell/CellUI/TUIFindContactCell.m
Normal 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
|
||||
18
TUIKit/TUIContact/BaseCell/CellUI/TUIGroupMemberCell.h
Normal 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
|
||||
97
TUIKit/TUIContact/BaseCell/CellUI/TUIGroupMemberCell.m
Normal 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
|
||||
38
TUIKit/TUIContact/BaseCell/CellUI/TUIGroupMembersCell.h
Normal 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
|
||||
97
TUIKit/TUIContact/BaseCell/CellUI/TUIGroupMembersCell.m
Normal 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
|
||||
26
TUIKit/TUIContact/BaseCell/CellUI/TUIMemberInfoCell.h
Normal 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
|
||||
180
TUIKit/TUIContact/BaseCell/CellUI/TUIMemberInfoCell.m
Normal 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
|
||||
15
TUIKit/TUIContact/BaseCell/CellUI/TUIMemberPanelCell.h
Normal 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
|
||||
37
TUIKit/TUIContact/BaseCell/CellUI/TUIMemberPanelCell.m
Normal 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
|
||||
18
TUIKit/TUIContact/BaseCell/CellUI/TUISelectGroupMemberCell.h
Normal 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
|
||||
83
TUIKit/TUIContact/BaseCell/CellUI/TUISelectGroupMemberCell.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
254
TUIKit/TUIContact/BaseDataProvider/TUIContactViewDataProvider.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
285
TUIKit/TUIContact/BaseDataProvider/TUIGroupManageDataProvider.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
* YES:There are unshown requests;NO:All 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
212
TUIKit/TUIContact/BaseDataProvider/TUISettingAdminDataProvider.m
Normal 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
|
||||
23
TUIKit/TUIContact/CommonModel/TUIContactDefine.h
Normal 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 */
|
||||
38
TUIKit/TUIContact/CommonUI/TUIContactConfig.h
Normal 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
|
||||
79
TUIKit/TUIContact/CommonUI/TUIContactConfig.m
Normal 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
|
||||
39
TUIKit/TUIContact/CommonUI/TUIGroupMembersView.h
Normal 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
|
||||
142
TUIKit/TUIContact/CommonUI/TUIGroupMembersView.m
Normal 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
|
||||
49
TUIKit/TUIContact/CommonUI/TUIStyleSelectViewController.h
Normal 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
|
||||
298
TUIKit/TUIContact/CommonUI/TUIStyleSelectViewController.m
Normal 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
|
||||
79
TUIKit/TUIContact/CommonUI/TUIThemeSelectController.h
Normal 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
|
||||
636
TUIKit/TUIContact/CommonUI/TUIThemeSelectController.m
Normal 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
|
||||
36
TUIKit/TUIContact/Resources/PrivacyInfo.xcprivacy
Normal 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>
|
||||
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/add@2x.png
Normal file
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/add_friend@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/add_group@3x.png
Normal file
|
After Width: | Height: | Size: 456 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/add_solid@2x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/add_solid@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/back@3x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/blacklist@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/blacklist@3x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 612 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/delete@2x.png
Normal file
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/female.png
Executable file
|
After Width: | Height: | Size: 253 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/female@2x.png
Executable file
|
After Width: | Height: | Size: 414 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/female@3x.png
Executable file
|
After Width: | Height: | Size: 554 B |
|
After Width: | Height: | Size: 382 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/ic_selected@2x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/ic_selected@3x.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/ic_unselect@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/ic_unselect@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/icon_add@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/icon_add@3x.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 294 B |
|
After Width: | Height: | Size: 448 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/male.png
Normal file
|
After Width: | Height: | Size: 227 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/male@2x.png
Normal file
|
After Width: | Height: | Size: 391 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/male@3x.png
Normal file
|
After Width: | Height: | Size: 512 B |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/new_friend@2x.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
TUIKit/TUIContact/Resources/TUIContact.bundle/new_friend@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 12 KiB |