Commit a89c0026 authored by Alex朱枝文's avatar Alex朱枝文

提交imSDK

parent 4aa3256e

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

English | [简体中文](./README_ZH.md)
# TUIKit (iOS)
## Download Links
[Latest TUIChat download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIChat.zip)
[Latest TUIConversation download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIConversation.zip)
[Latest TUIContact download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIContact.zip)
[Latest TUISearch download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUISearch.zip)
[Latest TUITranslationPlugin download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUITranslationPlugin.zip)
[Latest TUIVoiceToTextPlugin download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIVoiceToTextPlugin.zip)
[Latest TUICustomerServicePlugin download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUICustomerServicePlugin.zip)
[Latest TUIEmojiPlugin download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIEmojiPlugin.zip)
[Latest TUIMultimediaPlugin download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIMultimediaPlugin.zip)
[Latest TUICore download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUICore.zip)
[Latest TIMCommon download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TIMCommon.zip)
[Latest TUICallKit download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUICallKit.zip)
[Latest TUIRoomKit download](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIRoomKit.zip)
If you encounter a TUIKit bug, please submit a pull request. After successful merging, we will update the TUIKit library for pod integration in time.
\ No newline at end of file
[English](./README.md) | 简体中文
# TUIKit(iOS)
## 下载地址
[最新 TUIChat 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIChat.zip)
[最新 TUIConversation 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIConversation.zip)
[最新 TUIContact 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIContact.zip)
[最新 TUISearch 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUISearch.zip)
[最新 TUITranslationPlugin 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUITranslationPlugin.zip)
[最新 TUIVoiceToTextPlugin 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIVoiceToTextPlugin.zip)
[最新 TUICustomerServicePlugin 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUICustomerServicePlugin.zip)
[最新 TUIEmojiPlugin 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIEmojiPlugin.zip)
[最新 TUIMultimediaPlugin 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIMultimediaPlugin.zip)
[最新 TUICore 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUICore.zip)
[最新 TIMCommon 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TIMCommon.zip)
[最新 TUICallKit 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUICallKit.zip)
[最新 TUIRoomKit 下载](https://im.sdk.cloud.tencent.cn/download/tuikit/8.5.6864/ios/TUIRoomKit.zip)
## 交流&反馈
如果您遇到 TUIKit 的 Bug,欢迎提交 Pull Request,Merge 成功后我们会及时更新 pod 集成的 TUIKit 库 。
欢迎加入 QQ 群进行技术交流和反馈问题。
![img]( https://im.sdk.qcloud.com/tools/resource/officialwebsite/pictures/doc_tuikit_qq_group.jpg)
\ No newline at end of file
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
*
* This document declares the modules and components used to implement the message unit.
* The message unit (TUIMessageCell) is a general term for the bubble message/picture message/emoticon message/video message displayed in the chat view.
* The above messages are implemented by inheriting from this class or a subclass of this class. If you want to customize the message, you also need to
* implement it by inheriting from this class or a subclass of this class. The XXXX message unit (TUIXXXXMessageCell) is mainly responsible for displaying on
* the page and responding to user interaction events. For data processing and acquisition in the message unit, please refer to
* TUIChat\CellData\TUIXXXXMessageCellData.h according to the specific message unit
*
* The interaction callbacks provided by the TUIMessageCellDelegate protocol include: long press, resend, click on the message, click on the avatar, etc.
* The TUIMessageCell class stores message-related information, such as the sender's avatar, sender's nickname, and message content (supports various formats
* such as text, pictures, and videos). At the same time, TUIMessageeCell, as a parent class, provides basic properties and behavior templates for subclass
* messages.
*/
#import <UIKit/UIKit.h>
#import "TUIFitButton.h"
#import "TUIMessageCellData.h"
#import "TUISecurityStrikeView.h"
@class TUIMessageCell;
@protocol TUIMessageCellProtocol <NSObject>
@required
+ (CGFloat)getHeight:(TUIMessageCellData *)data withWidth:(CGFloat)width;
+ (CGFloat)getEstimatedHeight:(TUIMessageCellData *)data;
+ (CGSize)getContentSize:(TUIMessageCellData *)data;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIMessageCellDelegate
//
/////////////////////////////////////////////////////////////////////////////////
@protocol TUIMessageCellDelegate <NSObject>
/**
* Callback for long press message
* You can use this callback to implement secondary operations such as delete and recall (when the sender of the message long-presses his own message) on top
* of the long-pressed message.
*/
- (void)onLongPressMessage:(TUIMessageCell *)cell;
/**
* Callback for clicking retryView
* You can use this callback to implement: resend the message.
*/
- (void)onRetryMessage:(TUIMessageCell *)cell;
/**
* Callback for clicking message cell
* Usually:
* - Clicking on the sound message means playing voice
* - Clicking on the file message means opening the file
* - Clicking on the picture message means showing the larger image
* - Clicking on the video message means playing the video.
* Usually, it only provides a reference for the function implementation, and you can implement the delegate function according to your needs.
*/
- (void)onSelectMessage:(TUIMessageCell *)cell;
/**
* Callback for clicking avatar view of the messageCell
* You can use this callback to implement: in response to the user's click, jump to the detailed information interface of the corresponding user.
*/
- (void)onSelectMessageAvatar:(TUIMessageCell *)cell;
/**
* Callback for long pressing avatar view of messageCell
*/
- (void)onLongSelectMessageAvatar:(TUIMessageCell *)cell;
/**
* Callback for clicking read receipt label
*/
- (void)onSelectReadReceipt:(TUIMessageCellData *)cell;
/**
* Clicking the x-person reply button to jump to the multi-person reply details page
*/
- (void)onJumpToRepliesDetailPage:(TUIMessageCellData *)data;
- (void)onJumpToMessageInfoPage:(TUIMessageCellData *)data selectCell:(TUIMessageCell *)cell;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIMessageCell
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUIMessageCell : TUICommonTableViewCell <TUIMessageCellProtocol>
/**
* Icon that identifies the message selected
* In the multi-selection scenario, it is used to identify whether the message is selected
*/
@property(nonatomic, strong) UIImageView *selectedIcon;
/**
* Message selection view
* When multiple selection is activated, the view will be overlaid on this cell, and clicking on the view will trigger the check/uncheck of the message
*/
@property(nonatomic, strong) UIButton *selectedView;
/**
*
* The icon view of displays user's avatar
*/
@property(nonatomic, strong) UIImageView *avatarView;
/**
*
* The label of displays user's displayname
*/
@property(nonatomic, strong) UILabel *nameLabel;
/**
* Container view
* It wraps various views of MesageCell as the "bottom" of MessageCell, which is convenient for view management and layout.
*/
@property(nonatomic, strong) UIView *container;
/**
* Activity indicator
* A circling icon is provided while the message is being sent to indicate that the message is being sent.
*/
@property(nonatomic, strong) UIActivityIndicatorView *indicator;
/**
* Retry view, displayed after sending failed, click on this view to trigger onRetryMessage: callback.
*/
@property(nonatomic, strong) UIImageView *retryView;
/**
* security Strike View
*/
@property (nonatomic, strong) TUISecurityStrikeView * securityStrikeView;
/**
* Message reply details button
*/
@property(nonatomic, strong) TUIFitButton *messageModifyRepliesButton;
/**
* The message data class which stores the information required in the messageCell, including sender ID, sender avatar, message sending status, message bubble
* icon, etc. For details of messageData, please refer to: TUIChat\Cell\CellData\TUIMessageCellData.h
*/
@property(readonly) TUIMessageCellData *messageData;
/**
* A control that identifies whether a message has been read
*/
@property(nonatomic, strong) UILabel *readReceiptLabel;
/**
* The message time label control, which is not displayed by default, is located at the far right of the message cell
* In the message forwarding scenario, open the forwarded message list, and the time of the current message will be displayed on the far right of the message.
*/
@property(nonatomic, strong) UILabel *timeLabel;
/**
* Whether to disable the default selection behavior encapsulated in TUIKit, such as group live broadcast by default to create live room and other behaviors,
* default: NO
*/
@property(nonatomic, assign) BOOL disableDefaultSelectAction;
@property(nonatomic, weak) id<TUIMessageCellDelegate> delegate;
/**
*
* Whether the highlight flashing animation is in progress
*/
@property(nonatomic, assign) BOOL highlightAnimating;
- (void)fillWithData:(TUICommonCellData *)data;
/**
* Set the highlighting effect after matching the keyword, mainly used for jumping after message search, subclass rewriting
* The base class provides the default highlighting effect, and the subclass can implement it freely
*
* @param keyword Highlight keywords
*/
- (void)highlightWhenMatchKeyword:(NSString *)keyword;
/**
* Returns the view for highlighting
*/
- (UIView *)highlightAnimateView;
/**
* Update the content of the read label
*/
- (void)updateReadLabelText;
/// Preset bottom container in cell, which can be added custom view/viewControllers.
@property(nonatomic, strong) UIView *bottomContainer;
/// When bottom container is layout ready, notify it to add custom extensions.
- (void)notifyBottomContainerReadyOfData:(TUIMessageCellData *)cellData;
/// Callback of SelectCell
@property(nonatomic, copy) TUIValueCallbck pluginMsgSelectCallback;
@end
@interface TUIMessageCell (TUILayoutConfiguration)
/**
* The color of the label that displays the recipient's nickname
* Used when the nickname needs to be displayed and the message direction is MsgDirectionIncoming
*/
@property(nonatomic, class) UIColor *incommingNameColor;
/**
*
* The font of the label that displays the recipient's nickname
* Used when the nickname needs to be displayed and the message direction is MsgDirectionIncoming
*
*/
@property(nonatomic, class) UIFont *incommingNameFont;
/**
* The color of the label showing the sender's nickname
* Used when the nickname needs to be displayed and the message direction is MsgDirectionOutgoing.
*/
@property(nonatomic, class) UIColor *outgoingNameColor;
/**
*
* The font of the label that displays the sender's nickname
* Used when the nickname needs to be displayed and the message direction is MsgDirectionOutgoing.
*/
@property(nonatomic, class) UIFont *outgoingNameFont;
@end
This diff is collapsed.
//
// TUISecurityStrikeView.h
// TIMCommon
//
// Created by wyl on 2023/10/11.
// Copyright © 2023 Tencent. All rights reserved.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#define kTUISecurityStrikeViewTopLineMargin 14.5
#define kTUISecurityStrikeViewTopLineToBottom 28
@interface TUISecurityStrikeView : UIView
@property(nonatomic, strong) UIView * topLine;
@property(nonatomic, strong) UILabel * textLabel;
+ (UIImage *)changeImageColorWith:(UIColor *)color image:(UIImage *)image alpha:(CGFloat)alpha;
@end
NS_ASSUME_NONNULL_END
//
// TUISecurityStrikeView.m
// TIMCommon
//
// Created by wyl on 2023/10/11.
// Copyright © 2023 Tencent. All rights reserved.
#import "TUISecurityStrikeView.h"
#import <TIMCommon/TIMDefine.h>
@implementation TUISecurityStrikeView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self){
[self setupView];
}
return self;
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
- (void)setupView {
self.topLine = [[UIView alloc] initWithFrame:CGRectZero];
self.topLine.backgroundColor = TUIDynamicColor(@"", TUIThemeModuleTIMCommon, @"#E5C7C7");
[self addSubview:self.topLine];
self.textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:self.textLabel];
self.textLabel.font = [UIFont systemFontOfSize:14];
self.textLabel.text = TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrike);
self.textLabel.textColor = TUIDynamicColor(@"", TUIThemeModuleTIMCommon, @"#DA2222");
self.textLabel.numberOfLines = 0;
self.textLabel.textAlignment = isRTL()?NSTextAlignmentRight:NSTextAlignmentLeft;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
[self.topLine mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(kTUISecurityStrikeViewTopLineMargin);
make.leading.mas_equalTo(10);
make.trailing.mas_equalTo(-10);
make.height.mas_equalTo(0.5);
}];
[self.textLabel sizeToFit];
[self.textLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(10);
make.bottom.mas_equalTo(-11);
make.width.mas_equalTo(self);
}];
}
+ (UIImage *)changeImageColorWith:(UIColor *)color image:(UIImage *)image alpha:(CGFloat)alpha {
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetAlpha(context, alpha);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextClipToMask(context, rect, image.CGImage);
[color setFill];
CGContextFillRect(context, rect);
UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
* This file declares the TUISystemMessageCell class, which is responsible for displaying system messages.
* The system message unit is responsible for displaying special messages from the system. Such messages are usually white on a gray background and centered.
*/
#import "TUIMessageCell.h"
#import "TUISystemMessageCellData.h"
/**
* 【Module name】 TUISystemMessageCell
* 【Function description】System message unit
* - It is used to display the system messages. Common system messages include: recall-message, group-member-change-message, group-created and
* group-diss-message, etc.
* - System messages are typically used to display notifications from apps that are sent by the system, not from any user.
*/
@interface TUISystemMessageCell : TUIMessageCell
/**
*
* The label of display system message content, such as "You recalled a message.".
*/
@property(readonly) UILabel *messageLabel;
@property(readonly) TUISystemMessageCellData *systemData;
- (void)fillWithData:(TUISystemMessageCellData *)data;
@end
//
// TUISystemMessageCell.m
// UIKit
//
// Created by annidyfeng on 2019/5/30.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUISystemMessageCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/NSString+TUIUtil.h>
@interface TUISystemMessageCell ()
@property(nonatomic, strong) UILabel *messageLabel;
@property TUISystemMessageCellData *systemData;
@end
@implementation TUISystemMessageCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_messageLabel = [[UILabel alloc] init];
_messageLabel.textAlignment = NSTextAlignmentCenter;
_messageLabel.numberOfLines = 0;
_messageLabel.backgroundColor = [UIColor clearColor];
_messageLabel.layer.cornerRadius = 3;
[_messageLabel.layer setMasksToBounds:YES];
[self.container addSubview:_messageLabel];
self.backgroundColor = [UIColor clearColor];
self.contentView.backgroundColor = [UIColor clearColor];
}
return self;
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
[self.container mas_remakeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.contentView);
make.size.mas_equalTo(self.contentView);
}];
[self.messageLabel sizeToFit];
if(self.messageLabel.superview) {
[self.messageLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.container);
make.leading.trailing.mas_equalTo(self.container);
}];
}
}
- (void)fillWithData:(TUISystemMessageCellData *)data;
{
[super fillWithData:data];
self.systemData = data;
self.messageLabel.textColor = TUISystemMessageCellData.textColor ? : data.contentColor;
self.messageLabel.font = TUISystemMessageCellData.textFont ? : data.contentFont;
self.messageLabel.backgroundColor = TUISystemMessageCellData.textBackgroundColor ? : [UIColor clearColor];
self.messageLabel.attributedText = data.attributedString;
self.nameLabel.hidden = YES;
self.avatarView.hidden = YES;
self.retryView.hidden = YES;
[self.indicator stopAnimating];
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
- (void)layoutSubviews {
[super layoutSubviews];
}
#pragma mark - TUIMessageCellProtocol
+ (CGFloat)getEstimatedHeight:(TUIMessageCellData *)data {
return 42.f;
}
+ (CGFloat)getHeight:(TUIMessageCellData *)data withWidth:(CGFloat)width {
return [self getContentSize:data].height + kScale375(16);
}
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
NSAssert([data isKindOfClass:TUISystemMessageCellData.class], @"data must be kind of TUISystemMessageCellData");
TUISystemMessageCellData *systemCellData = (TUISystemMessageCellData *)data;
static CGSize maxSystemSize;
if (CGSizeEqualToSize(maxSystemSize, CGSizeZero)) {
maxSystemSize = CGSizeMake(Screen_Width, MAXFLOAT);
}
CGSize size = [systemCellData.attributedString.string textSizeIn:maxSystemSize font:systemCellData.contentFont];
size.height += 10;
size.width += 16;
return size;
}
@end
//
// NSString+TUIEmoji.h
// TUIChat
//
// Created by harvy on 2021/11/15.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "TIMDefine.h"
NS_ASSUME_NONNULL_BEGIN
#define kSplitStringResultKey @"result"
#define kSplitStringTextKey @"text"
#define kSplitStringTextIndexKey @"textIndex"
@interface NSString (TUIEmoji)
/**
* Localize the emoji text in the current text and get the localized text
* eg: The original text was @"你好, [大哭]"
* - If it is currently in English, this method converts the text to @"Hello,[Cry]"
* - If the current is Chinese, this method converts the text to @"你好,[大哭]"
*/
- (NSString *)getLocalizableStringWithFaceContent;
/**
* Internationalize the emoji text in the current text and get the internationalized text. The internationalized text of the emoji is Chinese
*/
- (NSString *)getInternationalStringWithfaceContent;
/**
*
* Get the formatted emoticon text (after the image and text are mixed) The emoticon is stored in the NSTextAttachment object and cannot carry parameters
*/
- (NSMutableAttributedString *)getFormatEmojiStringWithFont:(UIFont *)textFont
emojiLocations:(nullable NSMutableArray<NSDictionary<NSValue *, NSAttributedString *> *> *)emojiLocations;
/**
*
* Get the formatted emoji (after the image and text are mixed together) The emoji is stored in the TUIEmojiTextAttachment object, which can carry parameters.
* For example: the original text is @"Hello,[cry]", then this method turns the text into @"Hello,😭"
*/
- (NSMutableAttributedString *)getAdvancedFormatEmojiStringWithFont:(UIFont *)textFont
textColor:(UIColor *)textColor
emojiLocations:(nullable NSMutableArray<NSDictionary<NSValue *, NSAttributedString *> *> *)emojiLocations;
- (NSString *)getEmojiImagePath;
- (UIImage *)getEmojiImage;
/**
* Split string using both emoji and @user. For instance,
* Origin string is @"hello[Grin]world, @user1 see you!", and users is @[@"user1"];
* Return value is:
* @{
* kSplitStringResultKey: @[@"hello", @"[Grin]", @"world, ", @"user1 ", @"see you!"],
* kSplitStringTextKey: @[@"hello", @"world, ", @"see you!"],
* kSplitStringTextIndexKey: @[@0, @2, @4]
* }
* kSplitStringResultKey's value contains all elements after spliting.
* kSplitStringTextKey'value contains all text elements in the split result, excluding emojis and @user infos.
* kSplitStringTextIndexKey'value contains the location of text in split result.
*/
- (NSDictionary *)splitTextByEmojiAndAtUsers:(NSArray *_Nullable)users;
/**
* Replace the element in array, whose index is in index with the corresponding value in replaceDict.
* For instance,
* array is @[@"hello", @"[Grin]", @"world, ", @"user1 ", @"see you!"]
* index is @[@0, @2, @4]
* replaceDict is @{@"hello":@"你好", @"world":@"世界", @"see you!":@"再见!"}
* Return value is @"你好[Grin]世界, @user1 再见!"
*/
+ (NSString *)replacedStringWithArray:(NSArray *)array index:(NSArray *)index replaceDict:(NSDictionary *)replaceDict;
@end
@interface NSAttributedString (EmojiExtension)
/**
* @"你好,😭"" -> @"你好,[大哭]"
* @"Hello,😭" -> @"Hello,[Cry]"
*/
- (NSString *)tui_getPlainString;
@end
NS_ASSUME_NONNULL_END
This diff is collapsed.
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
* - This file declares the TUIBubbleMessageCellData class.
* - This class inherits from TUIMessageCellData and is used to store a series of data and information required by the bubble message unit.
* - This class is used as the base class for the data source of the bubble message. When you want to implement a custom bubble message,
* you also need to make the data source of the corresponding message inherit from this class.
*
*/
#import "TUIMessageCellData.h"
NS_ASSUME_NONNULL_BEGIN
/**
*
* 【Module name】TUIBubbleMessageCellData
* 【Function description】Bubble message data source.
* - Bubble messages, the most common type of messages that contain text and emoji characters, will be your most common type of message in most cases.
* - The Bubble Message data source (hereinafter referred to as the data source) is responsible for storing various information required to render the Bubble
* Message UI.
* - The data source implements a series of business logic that can provide the required information to the Bubble Message UI.
* - Both TUIFileMessageCellData and TUIVoiceMessageCellData inherit from this class and implement the UI of bubble messages.
*/
@interface TUIBubbleMessageCellData : TUIMessageCellData
@end
NS_ASSUME_NONNULL_END
//
// TUIBubbleMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/30.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIBubbleMessageCellData.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
@implementation TUIBubbleMessageCellData
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
* This file declares the TUIMessageCellData class.
* - The "message unit" data source, as the parent class of various detailed data sources, provides basic templates for the properties and behaviors of various
* "message unit" data sources.
* - The "data source class" in this document is the base class for all message data, and each type of data source inherits from this class or its subclasses.
* - When you want to customize the message, you need to inherit the data source of the customized message from this class or a subclass of this class.
*/
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import "TUIMessageCellLayout.h"
@class TUIRelationUserModel;
NS_ASSUME_NONNULL_BEGIN
typedef void (^TDownloadProgress)(NSInteger curSize, NSInteger totalSize);
typedef void (^TDownloadResponse)(int code, NSString *desc, NSString *path);
/**
* The definition of message status
*/
typedef NS_ENUM(NSUInteger, TMsgStatus) {
Msg_Status_Init, // message initial
Msg_Status_Sending, // message sending
Msg_Status_Sending_2, // message sending, recommended
Msg_Status_Succ, // message sent successfully
Msg_Status_Fail, // Failed to send message
};
/**
*
* The definition of message direction
* Message direction affects UI styles such as bubble icons, bubble positions, etc.
*/
typedef NS_ENUM(NSUInteger, TMsgDirection) {
MsgDirectionIncoming,
MsgDirectionOutgoing,
};
/**
*
* The source of message
* Different display logic can be done according to the source of the message.
*/
typedef NS_ENUM(NSUInteger, TMsgSource) {
Msg_Source_Unkown = 0, // 未知
Msg_Source_OnlinePush, // Messages actively pushed in the background
Msg_Source_GetHistory, // SDK actively requests historical messages pulled from the background
};
/**
* 【Module name】TUIMessageCellData
* 【Function description】The data source of the chat message unit cooperates with the message controller to realize the business logic of message sending and
* receiving.
* - It is used to store various data and information required for message management and logic implementation. Including a series of data such as message
* status, message sender ID and avatar.
* - The chat information data unit integrates and calls the IM SDK, and can implement the business logic of the message through the interface provided by the
* SDK.
*/
@interface TUIMessageCellData : TUICommonCellData
/**
* Getting cellData according to message
*/
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message;
/**
* Getting the display string according to the message
*/
+ (NSString *)getDisplayString:(V2TIMMessage *)message;
/**
* Class to get the layout of the message reply custom reference and its data
*/
- (Class)getReplyQuoteViewDataClass;
- (Class)getReplyQuoteViewClass;
/**
* Message unique id
*/
@property(nonatomic, strong) NSString *msgID;
/**
* Message sender ID
*/
@property(nonatomic, strong) NSString *identifier;
/**
* Message display sender name
*/
@property(nonatomic, strong, readonly) NSString *senderName;
/**
* Sender's avatar url
*/
@property(nonatomic, strong) NSURL *__nullable avatarUrl;
/**
* Sender's avatar
*/
@property(nonatomic, strong) UIImage *__nullable avatarImage __attribute__((deprecated("not supported")));
/**
* Whether to use the receiver's avatar, default is NO
*/
@property(nonatomic, assign) BOOL isUseMsgReceiverAvatar;
/**
*
* The flag of showing name
* - In 1 vs 1 chat, the nickname is not displayed in the message by default.
* - In group chat, the nickname is displayed for messages sent by other users in the group.
* - YES: showing nickname; NO: hidden nickname
*/
@property(nonatomic, assign) BOOL showName;
/**
* Display user avatar
*/
@property(nonatomic, assign) BOOL showAvatar;
/**
* Whether the current message is the same as the sender of the next message
*/
@property(nonatomic, assign) BOOL sameToNextMsgSender;
/**
*
* The flag of showing message multiple selection
* - In the message list, the selection button is not displayed by default. When you long press the message to pop up the multi-select button and click it, the
* message list becomes multi-selectable.
* - YES: Enable multiple selection, multiple selection views are displayed; NO: Disable multiple selection, the default view is displayed.
*/
@property(nonatomic, assign) BOOL showCheckBox;
/**
* The flag of selected
*/
@property(nonatomic, assign) BOOL selected;
/**
* The user list in at message
*/
@property(nonatomic, strong) NSMutableArray<NSString *> *atUserList;
/**
* Message direction
* - Message direction affects UI styles such as bubble icons, bubble positions, etc.
*/
@property(nonatomic, assign) TMsgDirection direction;
/**
* Message status
*/
@property(nonatomic, assign) TMsgStatus status;
/**
* Message source
*/
@property(nonatomic, assign) TMsgSource source;
/**
* IMSDK message
* The Message object provided by IM SDK. Contains various member functions for obtaining message information, including obtaining priority, obtaining element
* index, obtaining offline message configuration information, etc. For details, please refer to
* TXIMSDK__Plus_iOS\Frameworks\ImSDK_Plus.framework\Headers\V2TIMMessage.h
*/
@property(nonatomic, strong) V2TIMMessage *innerMessage;
/**
* Message unit layout
* It includes UI information such as message margins, bubble padding, avatar margins, and avatar size.
* For details, please refer to Section\Chat\CellLayout\TUIMessageCellLayout.h
*/
@property(nonatomic, strong) TUIMessageCellLayout *cellLayout;
/**
* The flag of whether showing read receipts.
*/
@property(nonatomic, assign) BOOL showReadReceipt;
/**
* The flag of whether showing message time.
*/
@property(nonatomic, assign) BOOL showMessageTime;
/**
* The flag of whether showing the button which indicated how many people modiffied.
*/
@property(nonatomic, assign) BOOL showMessageModifyReplies;
/**
* Highlight keywords, when the keyword is not empty, it will be highlighted briefly, mainly used in message search scenarios.
*/
@property(nonatomic, copy) NSString *__nullable highlightKeyword;
/**
* Message read receipt
*/
@property(nonatomic, strong) V2TIMMessageReceipt *messageReceipt;
/**
* List of Reply Messages for the current message
*/
@property(nonatomic, strong) NSArray *messageModifyReplies;
@property(nonatomic, assign) CGSize messageContainerAppendSize;
/// Size for bottom container.
@property(nonatomic, assign) CGSize bottomContainerSize;
/// Placeholder data, to be replaced after data preparation is completed.
@property(nonatomic, strong) TUIMessageCellData* _Nullable placeHolderCellData;
/// Video transcoding progress
@property(nonatomic, assign) CGFloat videoTranscodingProgress;
/// If cell content can be forwarded.
- (BOOL)canForward;
- (BOOL)canLongPress;
- (BOOL)shouldHide;
/// Custom cell refresh when message modified
- (BOOL)customReloadCellWithNewMsg:(V2TIMMessage *)newMessage;
/**
* Initialize the message unit according to the message direction (receive/sent)
* - In addition to the initialization of basic messages, it also includes setting direction variables, nickname fonts, etc. according to the direction.
* - Also provides inheritable behavior for subclasses.
*/
- (instancetype)initWithDirection:(TMsgDirection)direction NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@property(nonatomic, assign) CGSize msgStatusSize;
/**
* TUIChat supports batch retrieval of user information except for the message sender's nickname.
* You can override the requestForAdditionalUserInfo method in your custom TUIMessageCellData to return the user IDs which you want to retrieve, and directly use the additionalUserInfoResult property in your custom TUIMessageCell to render the UI as needed.
* After TUIChat retrieves the information, it will assign it to the additionalUserInfoResult property and asynchronously refresh your cell.
*/
- (NSArray<NSString *> *)requestForAdditionalUserInfo;
@property(nonatomic, strong) NSDictionary<NSString *, TUIRelationUserModel *> *additionalUserInfoResult;
@end
NS_ASSUME_NONNULL_END
/**
* 【Module name】TUIMessageCellDataFileUploadProtocol
* 【Function description】File type message, unified upload (send) progress field
*/
@protocol TUIMessageCellDataFileUploadProtocol <NSObject>
@required
/**
* The progress of uploading (sending)
*/
@property(nonatomic, assign) NSUInteger uploadProgress;
@end
@protocol TUIMessageCellDataFileDownloadProtocol <NSObject>
@required
/**
* The progress of downloading (receving)
*/
@property(nonatomic, assign) NSUInteger downladProgress;
/**
* The flag of whether is downloading
* YES: downloading; NO: not download
*/
@property(nonatomic, assign) BOOL isDownloading;
@end
//
// TUIMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMessageCellData.h"
#import <TIMCommon/TIMDefine.h>
@interface TUIMessageCellData ()
@end
@implementation TUIMessageCellData
{
NSString *_msgID;
NSString *_identifier;
NSURL *_avatarUrl;
}
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
return nil;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return nil;
}
- (Class)getReplyQuoteViewDataClass {
return nil;
}
- (Class)getReplyQuoteViewClass {
return nil;
}
- (instancetype)initWithDirection:(TMsgDirection)direction {
self = [super init];
if (self) {
_direction = direction;
_status = Msg_Status_Init;
_source = Msg_Source_Unkown;
_showReadReceipt = YES;
_sameToNextMsgSender = NO;
_showAvatar = YES;
_cellLayout = [self cellLayout:direction];
_additionalUserInfoResult = @{};
}
return self;
}
- (TUIMessageCellLayout *)cellLayout:(TMsgDirection)direction {
if (direction == MsgDirectionIncoming) {
return [TUIMessageCellLayout incommingMessageLayout];
} else {
return [TUIMessageCellLayout outgoingMessageLayout];
}
}
- (void)setMsgID:(NSString *)msgID {
_msgID = msgID;
}
- (NSString *)msgID {
if (_msgID) {
return _msgID;
}
if (self.innerMessage) {
return self.innerMessage.msgID;
}
return nil;
}
- (void)setIdentifier:(NSString *)identifier {
_identifier = identifier;
}
- (NSString *)identifier {
if (_identifier) {
return _identifier;
}
if (self.innerMessage) {
return self.innerMessage.sender;
}
return nil;
}
- (NSString *)senderName {
if (self.innerMessage) {
return self.innerMessage.nameCard ? : (self.innerMessage.friendRemark ? : (self.innerMessage.nickName ? : self.innerMessage.sender));
}
return nil;
}
- (void)setAvatarUrl:(NSURL *)avatarUrl {
_avatarUrl = avatarUrl;
}
- (NSURL *)avatarUrl {
if (_avatarUrl) {
return _avatarUrl;
}
if (self.innerMessage) {
return [NSURL URLWithString:self.innerMessage.faceURL];;
}
return nil;
}
- (BOOL)canForward {
return YES;
}
- (BOOL)canLongPress {
return YES;
}
- (BOOL)shouldHide {
return NO;
}
- (BOOL)customReloadCellWithNewMsg:(V2TIMMessage *)newMessage {
return NO;
}
- (CGSize)msgStatusSize {
if (self.showReadReceipt && self.innerMessage.needReadReceipt &&
(self.innerMessage.userID || self.innerMessage.groupID)) {
if (self.direction == MsgDirectionOutgoing) {
return CGSizeMake(54, 14);
} else {
return CGSizeMake(38, 14);
}
}
else {
//The community type does not require read receipt markers, only the time is needed.
return CGSizeMake(26, 14);
}
}
- (NSDictionary *)messageModifyUserInfos {
return self.additionalUserInfoResult;
}
- (NSArray<NSString *> *)requestForAdditionalUserInfo {
return @[];
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
@import UIKit;
NS_ASSUME_NONNULL_BEGIN
/**
*【Module Name】TUIMessageCellLayout
*【Function description】The layout of message unit
* - UI layouts for implementing various message units (text, voice, video, images, emoticons, etc.).
* - When you want to adjust the interface layout in TUIKit, you can modify the corresponding properties in this layout.
*/
@interface TUIMessageCellLayout : NSObject
/**
* The insets of message
*/
@property(nonatomic, assign) UIEdgeInsets messageInsets;
/**
* The insets of bubble content.
*/
@property(nonatomic, assign) UIEdgeInsets bubbleInsets;
/**
* The insets of avatar
*/
@property(nonatomic, assign) UIEdgeInsets avatarInsets;
/**
* The size of avatar
*/
@property(nonatomic, assign) CGSize avatarSize;
/////////////////////////////////////////////////////////////////////////////////
// Text Message Layout
/////////////////////////////////////////////////////////////////////////////////
/**
* Getting text message (receive) layout
*/
+ (TUIMessageCellLayout *)incommingTextMessageLayout;
/**
* Getting text message (send) layout
*/
+ (TUIMessageCellLayout *)outgoingTextMessageLayout;
/////////////////////////////////////////////////////////////////////////////////
// Voice Message Layout
/////////////////////////////////////////////////////////////////////////////////
/**
* Getting voice message (receive) layout
*/
+ (TUIMessageCellLayout *)incommingVoiceMessageLayout;
/**
* Getting voice message (send) layout
*/
+ (TUIMessageCellLayout *)outgoingVoiceMessageLayout;
/////////////////////////////////////////////////////////////////////////////////
// System Message Layout
/////////////////////////////////////////////////////////////////////////////////
/**
* Getting system message layout
*/
+ (TUIMessageCellLayout *)systemMessageLayout;
/////////////////////////////////////////////////////////////////////////////////
// Image Message Layout
/////////////////////////////////////////////////////////////////////////////////
/**
* Getting Image message layout
*/
+ (TUIMessageCellLayout *)incommingImageMessageLayout;
+ (TUIMessageCellLayout *)outgoingImageMessageLayout;
/////////////////////////////////////////////////////////////////////////////////
// Video Message Layout
/////////////////////////////////////////////////////////////////////////////////
/**
* Getting video message layout
*/
+ (TUIMessageCellLayout *)incommingVideoMessageLayout;
+ (TUIMessageCellLayout *)outgoingVideoMessageLayout;
/////////////////////////////////////////////////////////////////////////////////
// Other Message Layout
/////////////////////////////////////////////////////////////////////////////////
/**
* Getting receive message layout
*/
+ (TUIMessageCellLayout *)incommingMessageLayout;
/**
* Getting send message layout
*/
+ (TUIMessageCellLayout *)outgoingMessageLayout;
@end
NS_ASSUME_NONNULL_END
//
// TUIMessageCellLayout.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMessageCellLayout.h"
#import <TIMCommon/TIMDefine.h>
@implementation TUIMessageCellLayout
- (instancetype)init:(BOOL)isIncomming {
self = [super init];
if (self) {
self.avatarSize = CGSizeMake(40, 40);
if (isIncomming) {
self.avatarInsets = (UIEdgeInsets){
.left = 8,
.top = 3,
.bottom = 1,
};
self.messageInsets = (UIEdgeInsets){
.top = 3,
.bottom = 17,
.left = 8,
};
} else {
self.avatarInsets = (UIEdgeInsets){
.right = 8,
.top = 3,
.bottom = 1,
};
self.messageInsets = (UIEdgeInsets){
.top = 3,
.bottom = 17,
.right = 8,
};
}
}
return self;
}
static TUIMessageCellLayout *gIncommingMessageLayout;
+ (TUIMessageCellLayout *)incommingMessageLayout {
if (!gIncommingMessageLayout) {
gIncommingMessageLayout = [[TUIMessageCellLayout alloc] init:YES];
}
return gIncommingMessageLayout;
}
static TUIMessageCellLayout *gOutgoingMessageLayout;
+ (TUIMessageCellLayout *)outgoingMessageLayout {
if (!gOutgoingMessageLayout) {
gOutgoingMessageLayout = [[TUIMessageCellLayout alloc] init:NO];
}
return gOutgoingMessageLayout;
}
#pragma Text CellLayout
static TUIMessageCellLayout *gIncommingTextMessageLayout;
+ (TUIMessageCellLayout *)incommingTextMessageLayout {
if (!gIncommingTextMessageLayout) {
gIncommingTextMessageLayout = [[TUIMessageCellLayout alloc] init:YES];
gIncommingTextMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 10.5, .bottom = 10.5, .left = 16, .right = 16};
}
return gIncommingTextMessageLayout;
}
static TUIMessageCellLayout *gOutgingTextMessageLayout;
+ (TUIMessageCellLayout *)outgoingTextMessageLayout {
if (!gOutgingTextMessageLayout) {
gOutgingTextMessageLayout = [[TUIMessageCellLayout alloc] init:NO];
gOutgingTextMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 10.5, .bottom = 10.5, .left = 16, .right = 16};
}
return gOutgingTextMessageLayout;
}
#pragma Voice CellLayout
static TUIMessageCellLayout *gIncommingVoiceMessageLayout;
+ (TUIMessageCellLayout *)incommingVoiceMessageLayout {
if (!gIncommingVoiceMessageLayout) {
gIncommingVoiceMessageLayout = [[TUIMessageCellLayout alloc] init:YES];
gIncommingVoiceMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 12, .bottom = 12, .left = 16, .right = 16};
}
return gIncommingVoiceMessageLayout;
}
static TUIMessageCellLayout *gOutgingVoiceMessageLayout;
+ (TUIMessageCellLayout *)outgoingVoiceMessageLayout {
if (!gOutgingVoiceMessageLayout) {
gOutgingVoiceMessageLayout = [[TUIMessageCellLayout alloc] init:NO];
gOutgingVoiceMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 14, .bottom = 20, .left = 22, .right = 20};
}
return gOutgingVoiceMessageLayout;
}
#pragma System CellLayout
static TUIMessageCellLayout *gSystemMessageLayout;
+ (TUIMessageCellLayout *)systemMessageLayout {
if (!gSystemMessageLayout) {
gSystemMessageLayout = [[TUIMessageCellLayout alloc] init:YES];
gSystemMessageLayout.messageInsets = (UIEdgeInsets){.top = 5, .bottom = 5};
}
return gSystemMessageLayout;
}
#pragma Image CellLayout
static TUIMessageCellLayout *gIncommingImageMessageLayout;
+ (TUIMessageCellLayout *)incommingImageMessageLayout {
if (!gIncommingImageMessageLayout) {
gIncommingImageMessageLayout = [[TUIMessageCellLayout alloc] init:YES];
gIncommingImageMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 0, .bottom = 0, .left = 0 ,.right = 0};
}
return gIncommingImageMessageLayout;
}
static TUIMessageCellLayout *gOutgoingImageMessageLayout;
+ (TUIMessageCellLayout *)outgoingImageMessageLayout {
if (!gOutgoingImageMessageLayout) {
gOutgoingImageMessageLayout = [[TUIMessageCellLayout alloc] init:NO];
gOutgoingImageMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 0, .bottom = 0, .left = 0 ,.right = 0};
}
return gOutgoingImageMessageLayout;
}
#pragma Video CellLayout
static TUIMessageCellLayout *gIncommingVideoMessageLayout;
+ (TUIMessageCellLayout *)incommingVideoMessageLayout {
if (!gIncommingVideoMessageLayout) {
gIncommingVideoMessageLayout = [[TUIMessageCellLayout alloc] init:YES];
gIncommingVideoMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 0, .bottom = 0, .left = 0 ,.right = 0};
}
return gIncommingVideoMessageLayout;
}
static TUIMessageCellLayout *gOutgoingVideoMessageLayout;
+ (TUIMessageCellLayout *)outgoingVideoMessageLayout {
if (!gOutgoingVideoMessageLayout) {
gOutgoingVideoMessageLayout = [[TUIMessageCellLayout alloc] init:NO];
gOutgoingVideoMessageLayout.bubbleInsets = (UIEdgeInsets){.top = 0, .bottom = 0, .left = 0 ,.right = 0};
}
return gOutgoingVideoMessageLayout;
}
@end
//
// TUIRelationUserModel.h
// TIMCommon
//
// Created by wyl on 2023/12/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUIRelationUserModel : NSObject
@property(nonatomic, copy) NSString *userID;
@property(nonatomic, copy) NSString *nickName;
@property(nonatomic, copy) NSString *faceURL;
@property(nonatomic, copy) NSString *friendRemark;
@property(nonatomic, copy) NSString *nameCard;
- (NSString *)getDisplayName;
@end
NS_ASSUME_NONNULL_END
//
// TUIRelationUserModel.m
// TIMCommon
//
// Created by wyl on 2023/12/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIRelationUserModel.h"
#import <TIMCommon/TIMDefine.h>
@implementation TUIRelationUserModel
- (NSString *)getDisplayName {
if (IS_NOT_EMPTY_NSSTRING(self.nameCard)) {
return self.nameCard;
} else if (IS_NOT_EMPTY_NSSTRING(self.friendRemark)) {
return self.friendRemark;
} else if (IS_NOT_EMPTY_NSSTRING(self.nickName)) {
return self.nickName;
} else {
return self.userID;
}
return @"";
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This file declares the TUISystemMessageCellData class.
* This class inherits from TUIMessageCellData and is used to store a series of data and information required by the system message unit.
*/
#import "TUIMessageCellData.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, TUISystemMessageType) {
TUISystemMessageTypeUnknown = 0,
TUISystemMessageTypeDate = 1,
};
/**
* 【Module name】TUISystemMessageCellData
* 【Function description】The datasource of system message unit.
*/
@interface TUISystemMessageCellData : TUIMessageCellData
/**
* The content of system message, such as "You recalled a message.".
*/
@property(nonatomic, strong) NSString *content;
/**
* The flag of whether supporting re-edit.
*/
@property(nonatomic, assign) BOOL supportReEdit;
/**
* Mutable string
* The recalled message can be re-edited within 2 minutes, which is displayed here based on attributedString.
*/
@property(nonatomic, strong, nullable) NSMutableAttributedString *attributedString;
/**
* The font of label which displays the system message content.
*/
@property(nonatomic, strong, nullable) UIFont *contentFont;
/**
* The color of label which displays the system message content.
*/
@property(nonatomic, strong, nullable) UIColor *contentColor;
/**
* The type of system message type, default is TUISystemMessageTypeUnknown
*/
@property(nonatomic, assign) TUISystemMessageType type;
@property(nonatomic, strong) NSArray<NSString *> *replacedUserIDList;
/**
* The font of label which displays the system message content.
*/
@property(nonatomic, class) UIFont *textFont;
/**
* The color of label which displays the system message content.
*/
@property(nonatomic, class) UIColor *textColor;
/**
* The background color of label which displays the system message content.
*/
@property(nonatomic, class) UIColor *textBackgroundColor;
@end
NS_ASSUME_NONNULL_END
//
// TUISystemMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUISystemMessageCellData.h"
#import <TIMCommon/TIMDefine.h>
#import "TUIRelationUserModel.h"
@implementation TUISystemMessageCellData
- (instancetype)initWithDirection:(TMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
self.showAvatar = NO;
_contentFont = [UIFont systemFontOfSize:13];
_contentColor = [UIColor d_systemGrayColor];
self.cellLayout = [TUIMessageCellLayout systemMessageLayout];
}
return self;
}
- (NSMutableAttributedString *)attributedString {
__block BOOL forceRefresh = NO;
[self.additionalUserInfoResult enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, TUIRelationUserModel * _Nonnull obj, BOOL * _Nonnull stop) {
NSString *str = [NSString stringWithFormat:@"{%@}", key];
NSString *showName = obj.userID;
if (obj.nameCard.length > 0) {
showName = obj.nameCard;
} else if (obj.friendRemark.length > 0) {
showName = obj.friendRemark;
} else if (obj.nickName.length > 0) {
showName = obj.nickName;
}
if ([self.content containsString:str]) {
self.content = [self.content stringByReplacingOccurrencesOfString:str withString:showName];
forceRefresh = YES;
}
}];
if (forceRefresh || (_attributedString == nil && self.content.length > 0)) {
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:self.content];
NSDictionary *attributeDict = @{NSForegroundColorAttributeName : [UIColor d_systemGrayColor]};
[attributeString setAttributes:attributeDict range:NSMakeRange(0, attributeString.length)];
if (self.supportReEdit) {
NSString *reEditStr = TIMCommonLocalizableString(TUIKitMessageTipsReEditMessage);
[attributeString appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@", reEditStr]]];
NSDictionary *attributeDict = @{NSForegroundColorAttributeName : [UIColor d_systemBlueColor]};
[attributeString setAttributes:attributeDict range:NSMakeRange(self.content.length + 1, reEditStr.length)];
[attributeString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInteger:NSUnderlineStyleNone]
range:NSMakeRange(self.content.length + 1, reEditStr.length)];
}
_attributedString = attributeString;
}
return _attributedString;
}
- (NSArray<NSString *> *)requestForAdditionalUserInfo {
NSMutableArray *result = [NSMutableArray arrayWithArray:[super requestForAdditionalUserInfo]];
if (self.replacedUserIDList) {
[result addObjectsFromArray:self.replacedUserIDList];
}
return result;
}
static UIFont *gTextFont;
+ (void)setTextFont:(UIFont *)textFont {
gTextFont = textFont;
}
+ (UIFont *)textFont {
return gTextFont;
}
static UIColor *gTextColor;
+ (void)setTextColor:(UIColor *)textColor {
gTextColor = textColor;
}
+ (UIColor *)textColor {
return gTextColor;
}
static UIColor *gTextBackgroundColor;
+ (void)setTextBackgroundColor:(UIColor *)textBackgroundColor {
gTextBackgroundColor = textBackgroundColor;
}
+ (UIColor *)textBackgroundColor {
return gTextBackgroundColor;
}
@end
//
// NSTimer+TUISafe.h
// TUICore
//
// Created by wyl on 2022/7/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSTimer (TUISafe)
+ (NSTimer *)tui_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
@end
NS_ASSUME_NONNULL_END
//
// NSTimer+TUISafe.m
// TUICore
//
// Created by wyl on 2022/7/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "NSTimer+TUISafe.h"
@implementation NSTimer (TUISafe)
+ (NSTimer *)tui_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block {
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(tui_callBlock:) userInfo:[block copy] repeats:repeats];
}
+ (void)tui_callBlock:(NSTimer *)timer {
void (^block)(NSTimer *timer) = timer.userInfo;
!block ?: block(timer);
}
@end
//
// TIMCommonMediator.h
// TUIEmojiPlugin
//
// Created by cologne on 2023/11/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface TIMCommonMediator : NSObject
+ (instancetype)share;
/// Protocol : Class
/// Register Protocol : Class
- (void)registerService:(Protocol *)service class:(Class)cls;
/// Protocol [Class new]
/// get [class new] by Protocol
- (id)getObject:(Protocol *)service;
@end
NS_ASSUME_NONNULL_END
//
// TIMCommonMediator.m
// TUIEmojiPlugin
//
// Created by cologne on 2023/11/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TIMCommonMediator.h"
@interface TIMCommonMediator()
@property (nonatomic, strong) NSMutableDictionary *map;
@end
@implementation TIMCommonMediator
+ (instancetype)share {
static TIMCommonMediator *mediator = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mediator = [TIMCommonMediator new];
mediator.map = [NSMutableDictionary new];
});
return mediator;
}
- (void)registerService:(Protocol *)service class:(Class)cls {
if (!service || !cls) return;
self.map[NSStringFromProtocol(service)] = cls;
}
- (id)getObject:(Protocol *)service {
if (!service) return nil;
Class cls = self.map[NSStringFromProtocol(service)];
id obj = [cls new];
if ([obj conformsToProtocol:service]) {
return obj;
}
return nil;
}
@end
This diff is collapsed.
This diff is collapsed.
//
// TIMConfig.h
// Pods
//
// Created by cologne on 2023/3/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <TUICore/TUIConfig.h>
#import "TIMCommonModel.h"
#import "TIMDefine.h"
@class TUIFaceCellData;
@class TUIFaceGroup;
NS_ASSUME_NONNULL_BEGIN
@interface TIMConfig : NSObject
+ (TIMConfig *)defaultConfig;
/**
* In respect for the copyright of the emoji design, the Chat Demo/TUIKit project does not include the cutouts of large emoji elements. Please replace them
* with your own designed or copyrighted emoji packs before the official launch for commercial use. The default small yellow face emoji pack is copyrighted by
* Tencent Cloud and can be authorized for a fee. If you wish to obtain authorization, please submit a ticket to contact us.
*
* submit a ticket url:https://console.cloud.tencent.com/workorder/category?level1_id=29&level2_id=40&source=14&data_title=%E5%8D%B3%E6%97%B6%E9%80%9A%E4%BF%A1%20IM&step=1 (China mainland)
* submit a ticket url:https://console.tencentcloud.com/workorder/category?level1_id=29&level2_id=40&source=14&data_title=Chat&step=1 (Other regions)
*/
@property(nonatomic, strong) NSArray<TUIFaceGroup *> *faceGroups;
/**
*
* The list of emoticons displayed after long-pressing the message on the chat interface
*/
@property(nonatomic, strong) NSArray<TUIFaceGroup *> *chatPopDetailGroups;
@property(nonatomic, assign) BOOL enableMessageBubble;
+ (BOOL)isClassicEntrance;
@end
NS_ASSUME_NONNULL_END
//
// TIMConfig.m
// Pods
//
// Created by cologne on 2023/3/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TIMConfig.h"
#import "TIMCommonMediator.h"
#import "TUIEmojiMeditorProtocol.h"
#define kTUIKitFirstInitAppStyleID @"Classic"; // Classic / Minimalist
typedef NS_OPTIONS(NSInteger, emojiFaceType) {
emojiFaceTypeKeyBoard = 1 << 0,
emojiFaceTypePopDetail = 1 << 1,
};
@interface TIMConfig ()
@end
@implementation TIMConfig
+ (void)load {
TUIRegisterThemeResourcePath(TIMCommonThemePath, TUIThemeModuleTIMCommon);
}
- (id)init {
self = [super init];
if (self) {
self.enableMessageBubble = YES;
}
return self;
}
+ (id)defaultConfig {
static dispatch_once_t onceToken;
static TIMConfig *config;
dispatch_once(&onceToken, ^{
config = [[TIMConfig alloc] init];
});
return config;
}
- (NSArray<TUIFaceGroup *> *)faceGroups {
id<TUIEmojiMeditorProtocol> service = [[TIMCommonMediator share] getObject:@protocol(TUIEmojiMeditorProtocol)];
return [service getFaceGroup];
}
- (NSArray<TUIFaceGroup *> *)chatPopDetailGroups {
id<TUIEmojiMeditorProtocol> service = [[TIMCommonMediator share] getObject:@protocol(TUIEmojiMeditorProtocol)];
return [service getChatPopDetailGroups];
}
+ (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
//
// TIMDefine.h
// Pods
//
// Created by cologne on 2023/3/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#ifndef TIMDefine_h
#define TIMDefine_h
#import <ReactiveObjC/ReactiveObjC.h>
#import <TUICore/TUIDefine.h>
#import <Masonry/Masonry.h>
#import "TIMConfig.h"
#import "TIMCommonModel.h"
#import "TIMRTLUtil.h"
#define kEnableAllRotationOrientationNotification @"kEnableAllRotationOrientationNotification"
#define kDisableAllRotationOrientationNotification @"kDisableAllRotationOrientationNotification"
#define TUIMessageMediaViewDeviceOrientationChangeNotification @"TUIMessageMediaViewDeviceOrientationChangeNotification"
//Provide customers with the ability to modify the default emoji expression size in various input behaviors
#define kTIMDefaultEmojiSize CGSizeMake(23, 23)
#endif /* TIMDefine_h */
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
@import ImSDK_Plus;
NS_ASSUME_NONNULL_BEGIN
@interface V2TIMGroupInfo (TUIDataProvider)
- (BOOL)isMeOwner;
- (BOOL)isPrivate;
- (BOOL)canInviteMember;
- (BOOL)canRemoveMember;
- (BOOL)canDismissGroup;
- (BOOL)canSupportSetAdmain;
@end
NS_ASSUME_NONNULL_END
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TUICore/TUIGlobalization.h>
#import "TIMGroupInfo+TUIDataProvider.h"
@implementation V2TIMGroupInfo (TUIDataProvider)
- (BOOL)isMeOwner {
return [self.owner isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]] || (self.role == V2TIM_GROUP_MEMBER_ROLE_ADMIN);
}
- (BOOL)isPrivate {
return [self.groupType isEqualToString:@"Work"];
}
- (BOOL)canInviteMember {
return self.groupApproveOpt != V2TIM_GROUP_ADD_FORBID;
}
- (BOOL)canRemoveMember {
return [self isMeOwner] && (self.memberCount > 1);
}
- (BOOL)canDismissGroup {
if ([self isPrivate]) {
return NO;
} else {
if ([self.owner isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]] || (self.role == V2TIM_GROUP_MEMBER_ROLE_SUPER)) {
return YES;
} else {
return NO;
}
}
}
- (BOOL)canSupportSetAdmain {
BOOL isMeSuper = [self.owner isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]] || (self.role == V2TIM_GROUP_MEMBER_ROLE_SUPER);
BOOL isCurrentGroupTypeSupportSetAdmain = ([self.groupType isEqualToString:@"Public"] || [self.groupType isEqualToString:@"Meeting"] ||
[self.groupType isEqualToString:@"Community"] || [self.groupType isEqualToString:@"Private"]);
return isMeSuper && isCurrentGroupTypeSupportSetAdmain && (self.memberCount > 1);
}
@end
//
// TIMInputViewMoreActionProtocol.h
// TIMCommon
//
// Created by wyl on 2023/5/5.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol TIMInputViewMoreActionProtocol <NSObject>
- (void)sendMessage:(V2TIMMessage *)message;
@end
NS_ASSUME_NONNULL_END
//
// TIMPopActionProtocol.h
// TIMCommon
//
// Created by wyl on 2023/4/3.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol TIMPopActionProtocol <NSObject>
- (void)onDelete:(id)sender;
- (void)onCopyMsg:(id)sender;
- (void)onRevoke:(id)sender;
- (void)onReSend:(id)sender;
- (void)onMulitSelect:(id)sender;
- (void)onForward:(id)sender;
- (void)onReply:(id)sender;
- (void)onReference:(id)sender;
@end
NS_ASSUME_NONNULL_END
//
// TIMRTLUtil.h
// TIMCommon
//
// Created by cologne on 2023/7/21.
// Copyright © 2023 Tencent. All rights reserved
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface TIMRTLUtil : NSObject
@end
@interface UIView (TUIRTL)
- (void)resetFrameToFitRTL;
@end
@interface UIImage (TUIRTL)
- (UIImage *_Nonnull)checkOverturn;
- (UIImage *)rtl_imageFlippedForRightToLeftLayoutDirection;
@end
typedef NS_ENUM(NSUInteger, TUITextRTLAlignment) {
TUITextRTLAlignmentUndefine,
TUITextRTLAlignmentLeading,
TUITextRTLAlignmentTrailing,
TUITextRTLAlignmentCenter,
};
@interface UILabel (TUIRTL)
@property (nonatomic, assign) TUITextRTLAlignment rtlAlignment;
@end
@interface NSMutableAttributedString (TUIRTL)
@property (nonatomic, assign) TUITextRTLAlignment rtlAlignment;
@end
BOOL isRTLString(NSString *string);
NSString * rtlString(NSString *string);
NSAttributedString *rtlAttributeString(NSAttributedString *attributeString ,NSTextAlignment textAlignment );
UIEdgeInsets rtlEdgeInsetsWithInsets(UIEdgeInsets insets);
@interface TUICollectionRTLFitFlowLayout : UICollectionViewFlowLayout
@end
NS_ASSUME_NONNULL_END
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
//
// TUIEmojiMeditorProtocol.h
// TUIEmojiPlugin
//
// Created by wyl on 2023/11/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TIMDefine.h"
#import "TIMCommonModel.h"
@class V2TIMMessage;
@class TUIFaceGroup;
NS_ASSUME_NONNULL_BEGIN
@protocol TUIEmojiMeditorProtocol <NSObject>
- (void)updateEmojiGroups;
- (id)getFaceGroup;
- (void)appendFaceGroup:(TUIFaceGroup *)faceGroup;
- (id)getChatPopDetailGroups;
- (id)getChatContextEmojiDetailGroups;
- (id)getChatPopMenuRecentQueue;
- (void)updateRecentMenuQueue:(NSString *)faceName;
@end
NS_ASSUME_NONNULL_END
//
// TUIFitButton.h
// TUICore
//
// Created by wyl on 2022/5/24.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUIFitButton : UIButton
@property(nonatomic, assign) CGRect titleRect;
@property(nonatomic, assign) CGRect imageRect;
@property(nonatomic, assign) CGSize imageSize;
@property(nonatomic, assign) CGSize titleSize;
@property(nonatomic, strong) UIImage* hoverImage;
@property(nonatomic, strong) UIImage* normalImage;
@end
@interface TUIBlockButton : TUIFitButton
@property(nonatomic, copy) void (^clickCallBack)(id button);
@end
NS_ASSUME_NONNULL_END
This diff is collapsed.
//
// TUIFloatViewController.h
// TIMCommon
//
// Created by wyl on 2023/1/16.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol TUIFloatSubViewControllerProtocol <NSObject>
@optional
@property(nonatomic, copy) void (^floatDataSourceChanged)(NSArray *arr);
- (void)floatControllerLeftButtonClick;
- (void)floatControllerRightButtonClick;
@end
@interface TUIFloatTitleView : UIView
@property(nonatomic, strong) UIButton *leftButton;
@property(nonatomic, strong) UIButton *rightButton;
@property(nonatomic, copy) void (^leftButtonClickCallback)(void);
@property(nonatomic, copy) void (^rightButtonClickCallback)(void);
@property(nonatomic, strong) UILabel *titleLabel;
@property(nonatomic, strong) UILabel *subTitleLabel;
- (void)setTitleText:(NSString *)mainText subTitleText:(NSString *)secondText leftBtnText:(NSString *)leftBtnText rightBtnText:(NSString *)rightBtnText;
@end
@interface TUIFloatViewController : UIViewController
@property(nonatomic, strong) TUIFloatTitleView *topGestureView;
@property(nonatomic, strong) UIImageView *topImgView;
@property(nonatomic, strong) UIView *containerView;
@property(nonatomic, strong) UIViewController<TUIFloatSubViewControllerProtocol> *childVC;
- (void)updateSubContainerView;
- (void)setnormalTop;
- (void)setNormalBottom;
- (void)appendChildViewController:(UIViewController<TUIFloatSubViewControllerProtocol> *)vc topMargin:(CGFloat)topMargin;
- (void)floatDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion;
@end
NS_ASSUME_NONNULL_END
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment