This commit is contained in:
启星
2025-08-08 10:49:36 +08:00
parent 6400cf78bb
commit b5ce3d580a
8780 changed files with 978183 additions and 0 deletions

View File

@@ -0,0 +1,249 @@
// 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

View File

@@ -0,0 +1,680 @@
//
// TUIMessageCell.m
// UIKit
//
// Created by kennethmiao on 2018/9/17.
// Copyright © 2018 Tencent. All rights reserved.
//
#import "TUIMessageCell.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import <TUICore/TUITool.h>
#import "NSString+TUIEmoji.h"
#import "TUISystemMessageCellData.h"
#import <TUICore/TUICore.h>
@interface TUIMessageCell () <CAAnimationDelegate>
@property(nonatomic, strong) TUIMessageCellData *messageData;
@end
@implementation TUIMessageCell
#pragma mark - Life cycle
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setupSubViews];
[self setupRAC];
}
return self;
}
- (void)setupSubViews {
// head
_avatarView = [[UIImageView alloc] init];
_avatarView.contentMode = UIViewContentModeScaleAspectFill;
[self.contentView addSubview:_avatarView];
UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSelectMessageAvatar:)];
[_avatarView addGestureRecognizer:tap1];
UILongPressGestureRecognizer *tap2 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongSelectMessageAvatar:)];
[_avatarView addGestureRecognizer:tap2];
[_avatarView setUserInteractionEnabled:YES];
// nameLabel
_nameLabel = [[UILabel alloc] init];
_nameLabel.font = [self fontWithSize:13];
_nameLabel.textColor = [UIColor d_systemGrayColor];
[self.contentView addSubview:_nameLabel];
// container
_container = [[UIView alloc] init];
_container.backgroundColor = [UIColor clearColor];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSelectMessage:)];
tap.cancelsTouchesInView = NO;
[_container addGestureRecognizer:tap];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)];
[_container addGestureRecognizer:longPress];
[self.contentView addSubview:_container];
// indicator
_indicator = [[UIActivityIndicatorView alloc] init];
_indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[_indicator sizeToFit];
[self.contentView addSubview:_indicator];
// error
_retryView = [[UIImageView alloc] init];
_retryView.userInteractionEnabled = YES;
UITapGestureRecognizer *resendTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onRetryMessage:)];
[_retryView addGestureRecognizer:resendTap];
[self.contentView addSubview:_retryView];
// messageModifyRepliesLabel
_messageModifyRepliesButton = [[TUIFitButton alloc] initWithFrame:CGRectMake(0, 0, 12, 12)];
_messageModifyRepliesButton.imageSize = CGSizeMake(12, 12);
[_messageModifyRepliesButton addTarget:self action:@selector(onJumpToRepliesDetailPage:) forControlEvents:UIControlEventTouchUpInside];
[_messageModifyRepliesButton.titleLabel setFont:[self fontWithSize:12]];
[_messageModifyRepliesButton setTitleColor:TIMCommonDynamicColor(@"chat_message_read_name_date_text_color", @"#999999") forState:UIControlStateNormal];
[_messageModifyRepliesButton setImage:TIMCommonBundleThemeImage(@"chat_messageReplyIcon_img", @"messageReplyIcon") forState:UIControlStateNormal];
[self.contentView addSubview:_messageModifyRepliesButton];
_readReceiptLabel = [[UILabel alloc] init];
_readReceiptLabel.hidden = YES;
_readReceiptLabel.font = [self fontWithSize:12];
_readReceiptLabel.textColor = TIMCommonDynamicColor(@"chat_message_read_status_text_gray_color", @"#BBBBBB");
_readReceiptLabel.lineBreakMode = NSLineBreakByCharWrapping;
UITapGestureRecognizer *showReadReceiptTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSelectReadReceipt:)];
[_readReceiptLabel addGestureRecognizer:showReadReceiptTap];
_readReceiptLabel.userInteractionEnabled = YES;
[self.contentView addSubview:_readReceiptLabel];
// selectedIcon
_selectedIcon = [[UIImageView alloc] init];
[self.contentView addSubview:_selectedIcon];
// selectedView
_selectedView = [UIButton buttonWithType:UIButtonTypeCustom];
_selectedView.backgroundColor = [UIColor clearColor];
[_selectedView addTarget:self action:@selector(onSelectMessage:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:_selectedView];
// timeLabel
_timeLabel = [[UILabel alloc] init];
_timeLabel.textColor = [UIColor darkGrayColor];
_timeLabel.font = [self fontWithSize:11.0];
[self.contentView addSubview:_timeLabel];
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.backgroundColor = UIColor.clearColor;
self.contentView.backgroundColor = UIColor.clearColor;
[self makeConstraints];
}
- (void)makeConstraints {
[self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(_container.mas_leading).mas_offset(7);
make.top.mas_equalTo(self.avatarView.mas_top);
make.width.mas_equalTo(1);
make.height.mas_equalTo(20);
}];
[self.selectedIcon mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(3);
make.top.mas_equalTo(self.avatarView.mas_centerY).mas_offset(-10);
make.width.mas_equalTo(20);
make.height.mas_equalTo(20);
}];
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-10);
make.top.mas_equalTo(self.avatarView);
make.width.mas_greaterThanOrEqualTo(10);
make.height.mas_equalTo(10);
}];
[self.selectedView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.contentView);
}];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
TUIMessageCellLayout *cellLayout = self.messageData.cellLayout;
BOOL isInComing = (self.messageData.direction == MsgDirectionIncoming);
[self.nameLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
if (isInComing) {
make.leading.mas_equalTo(_container.mas_leading).mas_offset(7);
make.trailing.mas_equalTo(self.contentView).mas_offset(-7);
} else {
make.leading.mas_equalTo(self.contentView).mas_offset(7);
make.trailing.mas_equalTo(self.container.mas_trailing);
}
if (self.messageData.showName) {
make.width.mas_greaterThanOrEqualTo(20);
make.height.mas_greaterThanOrEqualTo(20);
} else {
make.height.mas_equalTo(0);
}
make.top.mas_equalTo(self.avatarView.mas_top);
}];
[self.selectedIcon mas_updateConstraints:^(MASConstraintMaker *make) {
if (self.messageData.showCheckBox) {
make.width.mas_equalTo(20);
make.height.mas_equalTo(20);
} else {
make.size.mas_equalTo(CGSizeZero);
}
}];
[self.timeLabel sizeToFit];
[self.timeLabel mas_updateConstraints:^(MASConstraintMaker *make) {
if (self.messageData.showMessageTime) {
make.width.mas_equalTo(self.timeLabel.frame.size.width);
make.height.mas_equalTo(self.timeLabel.frame.size.height);
} else {
make.width.mas_equalTo(0);
make.height.mas_equalTo(0);
}
}];
CGSize csize = [self.class getContentSize:self.messageData];
CGFloat contentWidth = csize.width;
CGFloat contentHeight = csize.height;
if (!CGSizeEqualToSize(self.messageData.messageContainerAppendSize, CGSizeZero)) {
/**
* Taking the maximum width between the "emoji reply message" and the text content
*/
contentWidth = MAX(self.messageData.messageContainerAppendSize.width, csize.width);
/**
* Limit the maximum width to Screen_Width *0.25 * 3
*/
contentWidth = MIN(contentWidth, Screen_Width * 0.25 * 3);
contentHeight = csize.height + self.messageData.messageContainerAppendSize.height;
}
if (self.messageData.direction == MsgDirectionIncoming) {
self.avatarView.hidden = !self.messageData.showAvatar;
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
if (self.messageData.showCheckBox) {
make.leading.mas_equalTo(self.selectedIcon.mas_trailing).mas_offset(cellLayout.avatarInsets.left);
} else {
make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(cellLayout.avatarInsets.left);
}
make.top.mas_equalTo(cellLayout.avatarInsets.top);
make.size.mas_equalTo(cellLayout.avatarSize);
}];
[self.container mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.avatarView.mas_trailing).mas_offset(cellLayout.messageInsets.left);
make.top.mas_equalTo(self.nameLabel.mas_bottom).mas_offset(cellLayout.messageInsets.top);
make.width.mas_equalTo(contentWidth);
make.height.mas_equalTo(contentHeight);
}];
CGRect indicatorFrame = self.indicator.frame;
[self.indicator mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.container.mas_trailing).mas_offset(8);
make.centerY.mas_equalTo(self.container.mas_centerY);
make.size.mas_equalTo(indicatorFrame.size);
}];
self.retryView.frame = self.indicator.frame;
self.readReceiptLabel.hidden = YES;
} else {
if (!self.messageData.showAvatar) {
cellLayout.avatarSize = CGSizeZero;
}
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-cellLayout.avatarInsets.right);
make.top.mas_equalTo(cellLayout.avatarInsets.top);
make.size.mas_equalTo(cellLayout.avatarSize);
}];
[self.container mas_remakeConstraints:^(MASConstraintMaker *make) {
make.trailing.mas_equalTo(self.avatarView.mas_leading).mas_offset(-cellLayout.messageInsets.right);
make.top.mas_equalTo(self.nameLabel.mas_bottom).mas_offset(cellLayout.messageInsets.top);
make.width.mas_equalTo(contentWidth);
make.height.mas_equalTo(contentHeight);
}];
CGRect indicatorFrame = self.indicator.frame;
[self.indicator mas_remakeConstraints:^(MASConstraintMaker *make) {
make.trailing.mas_equalTo(self.container.mas_leading).mas_offset(-8);
make.centerY.mas_equalTo(self.container.mas_centerY);
make.size.mas_equalTo(indicatorFrame.size);
}];
self.retryView.frame = self.indicator.frame;
[self.readReceiptLabel sizeToFit];
[self.readReceiptLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self.container.mas_bottom);
make.trailing.mas_equalTo(self.container.mas_leading).mas_offset(-8);
make.size.mas_equalTo(self.readReceiptLabel.frame.size);
}];
}
if (!self.messageModifyRepliesButton.isHidden) {
self.messageModifyRepliesButton.mm_sizeToFit();
CGFloat repliesBtnTextWidth = self.messageModifyRepliesButton.frame.size.width;
[self.messageModifyRepliesButton mas_remakeConstraints:^(MASConstraintMaker *make) {
if (isInComing) {
make.leading.mas_equalTo(self.container.mas_leading);
} else {
make.trailing.mas_equalTo(self.container.mas_trailing);
}
make.top.mas_equalTo(self.container.mas_bottom);
make.size.mas_equalTo(CGSizeMake(repliesBtnTextWidth + 10, 30));
}];
}
// according to apple super should be called at end of method
[super updateConstraints];
}
- (void)layoutSubviews {
[super layoutSubviews];
}
- (void)setupRAC {
@weakify(self);
[RACObserve(self, readReceiptLabel.text) subscribeNext:^(id _Nullable x) {
@strongify(self);
if ([self shouldHighlightReadReceiptLabel]) {
self.readReceiptLabel.textColor = TIMCommonDynamicColor(@"chat_message_read_status_text_color", @"#147AFF");
} else {
self.readReceiptLabel.textColor = TIMCommonDynamicColor(@"chat_message_read_status_text_gray_color", @"#BBBBBB");
}
}];
}
- (void)prepareForReuse {
[super prepareForReuse];
/**
* In the future, any UI problems caused by reuse can be solved by coding here.
*/
/**
* Once the message is reused, it means that a new message is about to appear, and the label content is changed to empty string.
*/
_readReceiptLabel.text = @"";
_readReceiptLabel.hidden = YES;
}
#pragma mark - Public
- (void)fillWithData:(TUIMessageCellData *)data {
[super fillWithData:data];
self.messageData = data;
[self loadAvatar:data];
if (self.messageData.showName) {
_nameLabel.hidden = NO;
} else {
_nameLabel.hidden = YES;
}
if (self.messageData.showCheckBox) {
_selectedIcon.hidden = NO;
_selectedView.hidden = NO;
} else {
_selectedIcon.hidden = YES;
_selectedView.hidden = YES;
}
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = data.cellLayout.avatarSize.height / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
self.nameLabel.text = data.senderName;
if (data.direction == MsgDirectionIncoming) {
self.nameLabel.textColor = self.class.incommingNameColor;
self.nameLabel.font = self.class.incommingNameFont;
} else {
self.nameLabel.textColor = self.class.outgoingNameColor;
self.nameLabel.font = self.class.outgoingNameFont;
}
self.retryView.image = [UIImage imageNamed:TUIChatImagePath(@"msg_error")];
if (data.status == Msg_Status_Fail) {
[_indicator stopAnimating];
_readReceiptLabel.hidden = YES;
self.retryView.hidden = NO;
} else {
if (data.status == Msg_Status_Sending_2) {
[_indicator startAnimating];
_readReceiptLabel.hidden = YES;
} else if (data.status == Msg_Status_Succ) {
[_indicator stopAnimating];
/**
* The message is sent successfully, indicating that the indicator and error are no longer displayed on the label, and the read receipt label can be
* displayed.
*/
if (self.messageData.showReadReceipt && self.messageData.direction == MsgDirectionOutgoing && self.messageData.innerMessage.needReadReceipt &&
(self.messageData.innerMessage.userID || self.messageData.innerMessage.groupID) &&
![self.messageData isKindOfClass:TUISystemMessageCellData.class]) {
[self updateReadLabelText];
_readReceiptLabel.hidden = NO;
}
} else if (data.status == Msg_Status_Sending) {
[_indicator startAnimating];
_readReceiptLabel.hidden = YES;
}
self.retryView.hidden = YES;
}
self.messageModifyRepliesButton.hidden = !data.showMessageModifyReplies;
if (data.showMessageModifyReplies) {
NSString *title = [NSString stringWithFormat:@"%ld%@", data.messageModifyReplies.count, TIMCommonLocalizableString(TUIKitRepliesNum)];
[self.messageModifyRepliesButton setTitle:title forState:UIControlStateNormal];
[self.messageModifyRepliesButton sizeToFit];
[self.messageModifyRepliesButton setNeedsUpdateConstraints];
[self.messageModifyRepliesButton updateConstraintsIfNeeded];
[self.messageModifyRepliesButton layoutIfNeeded];
}
NSString *imageName = (data.showCheckBox && data.selected) ? TIMCommonImagePath(@"icon_select_selected") : TIMCommonImagePath(@"icon_select_normal");
self.selectedIcon.image = [UIImage imageNamed:imageName];
_timeLabel.text = [TUITool convertDateToStr:data.innerMessage.timestamp];
[_timeLabel sizeToFit];
_timeLabel.hidden = !data.showMessageTime;
/**
* Text highlighting - asynchronous operations are here to keep the order of execution consistent with subclasses
*/
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf highlightWhenMatchKeyword:data.highlightKeyword];
});
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
- (void)loadAvatar:(TUIMessageCellData *)data {
[self.avatarView setImage:DefaultAvatarImage];
@weakify(self);
[[[RACObserve(data, avatarUrl) takeUntil:self.rac_prepareForReuseSignal] ignore:nil] subscribeNext:^(NSURL *url) {
@strongify(self);
[self.avatarView sd_setImageWithURL:url placeholderImage:DefaultAvatarImage];
}];
if (data.isUseMsgReceiverAvatar) {
NSString *userId = @"";
if ([data.innerMessage.sender isEqualToString:V2TIMManager.sharedInstance.getLoginUser]) {
userId = data.innerMessage.userID;
} else {
userId = V2TIMManager.sharedInstance.getLoginUser;
}
[V2TIMManager.sharedInstance getUsersInfo:@[ userId?:@"" ]
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
@strongify(self);
V2TIMUserFullInfo *info = infoList.firstObject;
if (info && [data isEqual:self.messageData]) {
data.avatarUrl = [NSURL URLWithString:info.faceURL];
[self.avatarView sd_setImageWithURL:data.avatarUrl placeholderImage:DefaultAvatarImage];
}
}
fail:^(int code, NSString *desc){
}];
}
}
- (void)highlightWhenMatchKeyword:(NSString *)keyword {
static NSString *const key = @"highlightAnimation";
if (keyword && keyword.length) {
if (self.highlightAnimating) {
return;
}
self.highlightAnimating = YES;
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"backgroundColor"];
animation.repeatCount = 3;
animation.values = @[
(id)[[UIColor orangeColor] colorWithAlphaComponent:0.2].CGColor,
(id)[[UIColor orangeColor] colorWithAlphaComponent:0.5].CGColor,
(id)[[UIColor orangeColor] colorWithAlphaComponent:0.2].CGColor,
];
animation.duration = 0.5;
animation.removedOnCompletion = YES;
animation.delegate = self;
[self.highlightAnimateView.layer addAnimation:animation forKey:key];
} else {
[self.highlightAnimateView.layer removeAnimationForKey:key];
}
}
- (void)updateReadLabelText {
if (self.messageData.innerMessage.groupID.length > 0) {
// group message
NSString *text = TIMCommonLocalizableString(Unread);
if (self.messageData.messageReceipt == nil) {
// haven't received the message receipt yet
return;
}
NSInteger readCount = self.messageData.messageReceipt.readCount;
NSInteger unreadCount = self.messageData.messageReceipt.unreadCount;
if (unreadCount == 0) {
// show "All read"
text = TIMCommonLocalizableString(TUIKitMessageReadAllRead);
} else if (readCount > 0) {
// show "x read"
text = [NSString stringWithFormat:@"%ld %@", (long)readCount, TIMCommonLocalizableString(TUIKitMessageReadPartRead)];
}
self.readReceiptLabel.text = text;
} else {
// c2c message
BOOL isPeerRead = self.messageData.messageReceipt.isPeerRead;
NSString *text = isPeerRead ? TIMCommonLocalizableString(TUIKitMessageReadC2CRead) : TIMCommonLocalizableString(TUIKitMessageReadC2CUnRead);
self.readReceiptLabel.text = text;
}
[self.readReceiptLabel sizeToFit];
[self.readReceiptLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self.container.mas_bottom);
make.trailing.mas_equalTo(self.container.mas_leading).mas_offset(-8);
make.size.mas_equalTo(self.readReceiptLabel.frame.size);
}];
self.readReceiptLabel.textColor = [self shouldHighlightReadReceiptLabel] ? TIMCommonDynamicColor(@"chat_message_read_status_text_color", @"#147AFF")
: TIMCommonDynamicColor(@"chat_message_read_status_text_gray_color", @"#BBBBBB");
}
- (UIView *)highlightAnimateView {
return self.container;
}
#pragma mark - TUIMessageCellProtocol
+ (CGFloat)getEstimatedHeight:(TUIMessageCellData *)data {
return 60.f;
}
+ (CGFloat)getHeight:(TUIMessageCellData *)data withWidth:(CGFloat)width {
CGFloat height = 0;
if (data.showName) height += kScale375(20);
if (data.showMessageModifyReplies) height += kScale375(22);
if (data.messageContainerAppendSize.height > 0) {
height += data.messageContainerAppendSize.height;
}
CGSize containerSize = [self getContentSize:data];
height += containerSize.height;
height += data.cellLayout.messageInsets.top;
height += data.cellLayout.messageInsets.bottom;
if (height < 55) height = 55;
return height;
}
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
return CGSizeZero;
}
#pragma mark - Private
- (void)animationDidStart:(CAAnimation *)anim {
self.highlightAnimating = YES;
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
self.highlightAnimating = NO;
}
#pragma mark-- Event
- (void)onLongPress:(UIGestureRecognizer *)recognizer {
if ([recognizer isKindOfClass:[UILongPressGestureRecognizer class]] && recognizer.state == UIGestureRecognizerStateBegan) {
if (_delegate && [_delegate respondsToSelector:@selector(onLongPressMessage:)]) {
[_delegate onLongPressMessage:self];
}
}
}
- (void)onRetryMessage:(UIGestureRecognizer *)recognizer {
if (_messageData.status == Msg_Status_Fail)
if (_delegate && [_delegate respondsToSelector:@selector(onRetryMessage:)]) {
[_delegate onRetryMessage:self];
}
}
- (void)onSelectMessage:(UIGestureRecognizer *)recognizer {
if (_delegate && [_delegate respondsToSelector:@selector(onSelectMessage:)]) {
[_delegate onSelectMessage:self];
}
}
- (void)onSelectMessageAvatar:(UIGestureRecognizer *)recognizer {
if (_delegate && [_delegate respondsToSelector:@selector(onSelectMessageAvatar:)]) {
[_delegate onSelectMessageAvatar:self];
}
}
- (void)onLongSelectMessageAvatar:(UIGestureRecognizer *)recognizer {
if (_delegate && [_delegate respondsToSelector:@selector(onLongSelectMessageAvatar:)]) {
[_delegate onLongSelectMessageAvatar:self];
}
}
- (void)onSelectReadReceipt:(UITapGestureRecognizer *)gesture {
if (![self shouldHighlightReadReceiptLabel]) {
return;
}
if (_delegate && [_delegate respondsToSelector:@selector(onSelectReadReceipt:)]) {
[_delegate onSelectReadReceipt:self.messageData];
}
}
- (void)onJumpToRepliesDetailPage:(UIButton *)btn {
NSLog(@"click onJumpToRepliesDetailPage");
NSLog(@"%@", self.messageData.messageModifyReplies);
if (_delegate && [_delegate respondsToSelector:@selector(onJumpToRepliesDetailPage:)]) {
[_delegate onJumpToRepliesDetailPage:self.messageData];
}
}
- (BOOL)shouldHighlightReadReceiptLabel {
if (self.messageData.innerMessage.groupID.length == 0) {
return ![self.readReceiptLabel.text isEqualToString:TIMCommonLocalizableString(TUIKitMessageReadC2CRead)];
} else {
return ![self.readReceiptLabel.text isEqualToString:TIMCommonLocalizableString(TUIKitMessageReadAllRead)];
}
}
- (UIFont *)fontWithSize:(CGFloat)size {
static NSCache *fontCache;
if (fontCache == nil) {
fontCache = [[NSCache alloc] init];
}
UIFont *font = [fontCache objectForKey:@(size)];
if (font == nil) {
font = [UIFont systemFontOfSize:size];
[fontCache setObject:font forKey:@(size)];
}
return font;
}
- (void)notifyBottomContainerReadyOfData:(TUIMessageCellData *)cellData {
// Override by subclass.
}
@end
@implementation TUIMessageCell (TUILayoutConfiguration)
static UIColor *gOutgoingNameColor;
+ (UIColor *)outgoingNameColor {
if (!gOutgoingNameColor) {
gOutgoingNameColor = [UIColor d_systemGrayColor];
}
return gOutgoingNameColor;
}
+ (void)setOutgoingNameColor:(UIColor *)outgoingNameColor {
gOutgoingNameColor = outgoingNameColor;
}
static UIFont *gOutgoingNameFont;
+ (UIFont *)outgoingNameFont {
if (!gOutgoingNameFont) {
gOutgoingNameFont = [UIFont systemFontOfSize:14];
}
return gOutgoingNameFont;
}
+ (void)setOutgoingNameFont:(UIFont *)outgoingNameFont {
gOutgoingNameFont = outgoingNameFont;
}
static UIColor *gIncommingNameColor;
+ (UIColor *)incommingNameColor {
if (!gIncommingNameColor) {
gIncommingNameColor = [UIColor d_systemGrayColor];
}
return gIncommingNameColor;
}
+ (void)setIncommingNameColor:(UIColor *)incommingNameColor {
gIncommingNameColor = incommingNameColor;
}
static UIFont *gIncommingNameFont;
+ (UIFont *)incommingNameFont {
if (!gIncommingNameFont) {
gIncommingNameFont = [UIFont systemFontOfSize:14];
}
return gIncommingNameFont;
}
+ (void)setIncommingNameFont:(UIFont *)incommingNameFont {
gIncommingNameFont = incommingNameFont;
}
@end

View File

@@ -0,0 +1,20 @@
//
// 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

View File

@@ -0,0 +1,73 @@
//
// 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

View File

@@ -0,0 +1,30 @@
// 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

View File

@@ -0,0 +1,107 @@
//
// 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

View File

@@ -0,0 +1,90 @@
//
// 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

View File

@@ -0,0 +1,637 @@
//
// NSString+TUIEmoji.m
// TUIChat
//
// Created by harvy on 2021/11/15.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "NSString+TUIEmoji.h"
#import "TIMConfig.h"
@implementation NSString (TUIEmoji)
+ (NSString *)getRegex_emoji {
NSString *regex_emoji = @"\\[[a-zA-Z0-9_\\u4e00-\\u9fa5]+\\]"; // match emoji
return regex_emoji;
}
- (NSString *)getLocalizableStringWithFaceContent {
NSString *content = self;
NSString *regex_emoji = [self.class getRegex_emoji]; // match emoji
NSError *error = nil;
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:regex_emoji options:NSRegularExpressionCaseInsensitive error:&error];
if (re) {
NSArray *resultArray = [re matchesInString:content options:0 range:NSMakeRange(0, content.length)];
TUIFaceGroup *group = [TIMConfig defaultConfig].faceGroups[0];
NSMutableArray *waitingReplaceM = [NSMutableArray array];
for (NSTextCheckingResult *match in resultArray) {
NSRange range = [match range];
NSString *subStr = [content substringWithRange:range];
for (TUIFaceCellData *face in group.faces) {
if ([face.name isEqualToString:subStr]) {
[waitingReplaceM
addObject:@{@"range" : NSStringFromRange(range), @"localizableStr" : face.localizableName.length ? face.localizableName : face.name}];
break;
}
}
}
if (waitingReplaceM.count) {
/**
* Replace from back to front, otherwise it will cause positional problems
*/
for (int i = (int)waitingReplaceM.count - 1; i >= 0; i--) {
NSRange range = NSRangeFromString(waitingReplaceM[i][@"range"]);
NSString *localizableStr = waitingReplaceM[i][@"localizableStr"];
content = [content stringByReplacingCharactersInRange:range withString:localizableStr];
}
}
}
return content;
}
- (NSString *)getInternationalStringWithfaceContent {
NSString *content = self;
NSString *regex_emoji = [self.class getRegex_emoji];
NSError *error = nil;
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:regex_emoji options:NSRegularExpressionCaseInsensitive error:&error];
if (re) {
NSMutableDictionary *faceDict = [NSMutableDictionary dictionary];
TUIFaceGroup *group = [TIMConfig defaultConfig].faceGroups[0];
for (TUIFaceCellData *face in group.faces) {
NSString *key = face.localizableName ?: face.name;
NSString *value = face.name ?: @"";
faceDict[key] = value;
}
NSArray *resultArray = [re matchesInString:content options:0 range:NSMakeRange(0, content.length)];
NSMutableArray *waitingReplaceM = [NSMutableArray array];
for (NSTextCheckingResult *match in resultArray) {
NSRange range = [match range];
NSString *subStr = [content substringWithRange:range];
[waitingReplaceM addObject:@{@"range" : NSStringFromRange(range), @"localizableStr" : faceDict[subStr] ?: subStr}];
}
if (waitingReplaceM.count != 0) {
/**
* Replace from back to front, otherwise it will cause positional problems
*/
for (int i = (int)waitingReplaceM.count - 1; i >= 0; i--) {
NSRange range = NSRangeFromString(waitingReplaceM[i][@"range"]);
NSString *localizableStr = waitingReplaceM[i][@"localizableStr"];
content = [content stringByReplacingCharactersInRange:range withString:localizableStr];
}
}
}
return content;
}
- (NSMutableAttributedString *)getFormatEmojiStringWithFont:(UIFont *)textFont
emojiLocations:(nullable NSMutableArray<NSDictionary<NSValue *, NSAttributedString *> *> *)emojiLocations {
/**
* First determine whether the text exists
*/
if (self.length == 0) {
NSLog(@"getFormatEmojiStringWithFont failed , current text is nil");
return [[NSMutableAttributedString alloc] initWithString:@""];
}
/**
* 1. Create a mutable attributed string
*/
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:self];
if ([TIMConfig defaultConfig].faceGroups.count == 0) {
[attributeString addAttribute:NSFontAttributeName value:textFont range:NSMakeRange(0, attributeString.length)];
return attributeString;
}
/**
* 2.Match strings with regular expressions
*/
NSError *error = nil;
static NSRegularExpression *re = nil;
if (re == nil) {
NSString *regex_emoji = [self.class getRegex_emoji];
re = [NSRegularExpression regularExpressionWithPattern:regex_emoji options:NSRegularExpressionCaseInsensitive error:&error];
}
if (!re) {
NSLog(@"%@", [error localizedDescription]);
return attributeString;
}
NSArray *resultArray = [re matchesInString:self options:0 range:NSMakeRange(0, self.length)];
TUIFaceGroup *group = [TIMConfig defaultConfig].faceGroups[0];
/**
* 3.Getting all emotes and locations
* - Used to store the dictionary, the dictionary stores the image and the corresponding location of the image
*/
NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:resultArray.count];
/**
* Replace the image with the corresponding image according to the matching range
*/
for (NSTextCheckingResult *match in resultArray) {
/**
* Get the range in the array element
*/
NSRange range = [match range];
/**
* Get the corresponding value in the original string
*/
NSString *subStr = [self substringWithRange:range];
for (TUIFaceCellData *face in group.faces) {
if ([face.name isEqualToString:subStr]) {
/**
* - Create a new NSTextAttachment to store our image
*/
TUIEmojiTextAttachment *emojiTextAttachment = [[TUIEmojiTextAttachment alloc] init];
emojiTextAttachment.faceCellData = face;
NSString *localizableFaceName = face.name;
// Set tag and image
emojiTextAttachment.emojiTag = localizableFaceName;
emojiTextAttachment.image = [[TUIImageCache sharedInstance] getFaceFromCache:face.path];
// Set emoji size
emojiTextAttachment.emojiSize = kTIMDefaultEmojiSize;
NSAttributedString *str = [NSAttributedString attributedStringWithAttachment:emojiTextAttachment];
/**
* - Convert attachments to mutable strings to replace emoji text in source strings
*/
NSAttributedString *imageStr = [NSAttributedString attributedStringWithAttachment:emojiTextAttachment];
/**
* - Save the picture and the corresponding position of the picture into the dictionary
*/
NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2];
[imageDic setObject:imageStr forKey:@"image"];
[imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"];
/**
* - Store dictionary in array
*/
[imageArray addObject:imageDic];
break;
}
}
}
/**
* 4.Replace from back to front, otherwise it will cause positional problems
*/
NSMutableArray *locations = [NSMutableArray array];
for (int i = (int)imageArray.count - 1; i >= 0; i--) {
NSRange originRange;
[imageArray[i][@"range"] getValue:&originRange];
/**
* Store location information
*/
NSAttributedString *originStr = [attributeString attributedSubstringFromRange:originRange];
NSAttributedString *currentStr = imageArray[i][@"image"];
[locations insertObject:@[ [NSValue valueWithRange:originRange], originStr, currentStr ] atIndex:0];
// Replace
[attributeString replaceCharactersInRange:originRange withAttributedString:currentStr];
}
/**
* 5.Getting the position information of the converted string of emoji
*/
NSInteger offsetLocation = 0;
for (NSArray *obj in locations) {
NSArray *location = (NSArray *)obj;
NSRange originRange = [(NSValue *)location[0] rangeValue];
NSAttributedString *originStr = location[1];
NSAttributedString *currentStr = location[2];
NSRange currentRange;
currentRange.location = originRange.location + offsetLocation;
currentRange.length = currentStr.length;
offsetLocation += currentStr.length - originStr.length;
[emojiLocations addObject:@{[NSValue valueWithRange:currentRange] : originStr}];
}
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
[attributeString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attributeString.length)];
[attributeString addAttribute:NSFontAttributeName value:textFont range:NSMakeRange(0, attributeString.length)];
return attributeString;
}
- (NSString *)getEmojiImagePath {
TUIFaceGroup *group = [TIMConfig defaultConfig].faceGroups[0];
NSString *loaclName = [self getLocalizableStringWithFaceContent];
for (TUIFaceCellData *face in group.faces) {
if ([face.localizableName isEqualToString:loaclName]) {
return face.path;
}
}
return nil;
}
- (UIImage *)getEmojiImage {
TUIFaceGroup *group = [TIMConfig defaultConfig].faceGroups[0];
for (TUIFaceCellData *face in group.faces) {
if ([face.name isEqualToString:self]) {
return [[TUIImageCache sharedInstance] getFaceFromCache:face.path];
}
}
return nil;
}
- (NSMutableAttributedString *)getAdvancedFormatEmojiStringWithFont:(UIFont *)textFont
textColor:(UIColor *)textColor
emojiLocations:(nullable NSMutableArray<NSDictionary<NSValue *, NSAttributedString *> *> *)emojiLocations {
if (self.length == 0) {
NSLog(@"getAdvancedFormatEmojiStringWithFont failed , current text is nil");
return [[NSMutableAttributedString alloc] initWithString:@""];
}
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:self];
if ([TIMConfig defaultConfig].faceGroups.count == 0) {
[attributeString addAttribute:NSFontAttributeName value:textFont range:NSMakeRange(0, attributeString.length)];
return attributeString;
}
NSString *regex_emoji = [self.class getRegex_emoji];
NSError *error = nil;
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:regex_emoji options:NSRegularExpressionCaseInsensitive error:&error];
if (error) {
NSLog(@"%@", [error localizedDescription]);
return attributeString;
}
NSArray *resultArray = [re matchesInString:self options:0 range:NSMakeRange(0, self.length)];
TUIFaceGroup *group = [TIMConfig defaultConfig].faceGroups[0];
NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:resultArray.count];
for (NSTextCheckingResult *match in resultArray) {
NSRange range = [match range];
NSString *subStr = [self substringWithRange:range];
for (TUIFaceCellData *face in group.faces) {
if ([face.name isEqualToString:subStr] || [face.localizableName isEqualToString:subStr]) {
TUIEmojiTextAttachment *emojiTextAttachment = [[TUIEmojiTextAttachment alloc] init];
emojiTextAttachment.faceCellData = face;
// Set tag and image
emojiTextAttachment.emojiTag = face.name;
emojiTextAttachment.image = [[TUIImageCache sharedInstance] getFaceFromCache:face.path];
// Set emoji size
emojiTextAttachment.emojiSize = kTIMDefaultEmojiSize;
NSAttributedString *imageStr = [NSAttributedString attributedStringWithAttachment:emojiTextAttachment];
NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2];
[imageDic setObject:imageStr forKey:@"image"];
[imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"];
[imageArray addObject:imageDic];
break;
}
}
}
NSMutableArray *locations = [NSMutableArray array];
for (int i = (int)imageArray.count - 1; i >= 0; i--) {
NSRange originRange;
[imageArray[i][@"range"] getValue:&originRange];
NSAttributedString *originStr = [attributeString attributedSubstringFromRange:originRange];
NSAttributedString *currentStr = imageArray[i][@"image"];
[locations insertObject:@[ [NSValue valueWithRange:originRange], originStr, currentStr ] atIndex:0];
[attributeString replaceCharactersInRange:originRange withAttributedString:currentStr];
}
NSInteger offsetLocation = 0;
for (NSArray *obj in locations) {
NSArray *location = (NSArray *)obj;
NSRange originRange = [(NSValue *)location[0] rangeValue];
NSAttributedString *originStr = location[1];
NSAttributedString *currentStr = location[2];
NSRange currentRange;
currentRange.location = originRange.location + offsetLocation;
currentRange.length = currentStr.length;
offsetLocation += currentStr.length - originStr.length;
[emojiLocations addObject:@{[NSValue valueWithRange:currentRange] : originStr}];
}
[attributeString addAttribute:NSFontAttributeName value:textFont range:NSMakeRange(0, attributeString.length)];
[attributeString addAttribute:NSForegroundColorAttributeName value:textColor range:NSMakeRange(0, attributeString.length)];
return attributeString;
}
/**
* Steps:
* 1. Match @user infos in string.
* 2. Split origin string into array(A) by @user info's ranges.
* 3. Iterate the array(A) to match emoji one by one.
* 4. Add all parsed elements(emoji, @user, pure text) into result.
* 5. Process the text and textIndex by the way.
* 6. Encapsulate all arrays in a dict and return it.
*/
- (NSDictionary *)splitTextByEmojiAndAtUsers:(NSArray *_Nullable)users {
if (self.length == 0) {
return nil;
}
NSMutableArray *result = [NSMutableArray new];
/// Find @user info's ranges in string.
NSMutableArray *atUsers = [NSMutableArray new];
for (NSString *user in users) {
/// Add an whitespace after the user's name due to the special format of @ content.
NSString *atUser = [NSString stringWithFormat:@"@%@ ", user];
[atUsers addObject:atUser];
}
NSArray *atUserRanges = [self rangeOfAtUsers:atUsers inString:self];
/// Split text using @user info's ranges.
NSArray *splitResult = [self splitArrayWithRanges:atUserRanges inString:self];
NSMutableArray *splitArrayByAtUser = splitResult.firstObject;
NSSet *atUserIndex = splitResult.lastObject;
/// Iterate the split array after finding @user, aimed to match emoji.
NSInteger k = -1;
NSMutableArray *textIndexArray = [NSMutableArray new];
for (int i = 0; i < splitArrayByAtUser.count; i++) {
NSString *str = splitArrayByAtUser[i];
if ([atUserIndex containsObject:@(i)]) {
/// str is @user info.
[result addObject:str];
k += 1;
} else {
/// str is not @user info, try to parse emoji in the same way as above.
NSArray *emojiRanges = [self matchTextByEmoji:str];
splitResult = [self splitArrayWithRanges:emojiRanges inString:str];
NSMutableArray *splitArrayByEmoji = splitResult.firstObject;
NSSet *emojiIndex = splitResult.lastObject;
for (int j = 0; j < splitArrayByEmoji.count; j++) {
NSString *tmp = splitArrayByEmoji[j];
[result addObject:tmp];
k += 1;
if (![emojiIndex containsObject:@(j)]) {
/// str is text.
[textIndexArray addObject:@(k)];
}
}
}
}
NSMutableArray *textArray = [NSMutableArray new];
for (NSNumber *n in textIndexArray) {
[textArray addObject:result[[n integerValue]]];
}
NSDictionary *dict = @{kSplitStringResultKey : result, kSplitStringTextKey : textArray, kSplitStringTextIndexKey : textIndexArray};
return dict;
}
/// Find all ranges of @user in string.
- (NSArray *)rangeOfAtUsers:(NSArray *)atUsers inString:(NSString *)string {
/// Find all positions of character "@".
NSString *tmp = nil;
NSMutableIndexSet *atIndex = [NSMutableIndexSet new];
for (int i = 0; i < [string length]; i++) {
tmp = [string substringWithRange:NSMakeRange(i, 1)];
if ([tmp isEqualToString:@"@"]) {
[atIndex addIndex:i];
}
}
/// Match @user with "@" position.
NSMutableArray *result = [NSMutableArray new];
for (NSString *user in atUsers) {
[atIndex enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
if (string.length >= user.length && idx <= string.length - user.length) {
NSRange range = NSMakeRange(idx, user.length);
if ([[string substringWithRange:range] isEqualToString:user]) {
[result addObject:[NSValue valueWithRange:range]];
[atIndex removeIndex:idx];
*stop = YES;
}
}
}];
}
return result;
}
/// Split string into multi substrings by given ranges.
/// Return value's structure is [result, indexes], in which indexs means position of content within ranges located in result after spliting.
- (NSArray *)splitArrayWithRanges:(NSArray *)ranges inString:(NSString *)string {
if (ranges.count == 0) {
return @[ @[ string ], @[] ];
}
if (string.length == 0) {
return nil;
}
/// Ascending sort.
NSArray *sortedRanges = [ranges sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
NSRange range1 = [obj1 rangeValue];
NSRange range2 = [obj2 rangeValue];
if (range1.location < range2.location) {
return (NSComparisonResult)NSOrderedAscending;
} else if (range1.location > range2.location) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}];
NSMutableArray *result = [NSMutableArray new];
NSMutableSet *indexes = [NSMutableSet new];
NSInteger prev = 0;
NSInteger i = 0;
NSInteger j = -1;
while (i < sortedRanges.count) {
NSRange cur = [sortedRanges[i] rangeValue];
NSString *str = nil;
if (cur.location > prev) {
/// Add the str in [prev, cur.location).
str = [string substringWithRange:NSMakeRange(prev, cur.location - prev)];
[result addObject:str];
j += 1;
}
/// Add the str in cur range.
str = [string substringWithRange:cur];
[result addObject:str];
j += 1;
[indexes addObject:@(j)];
/// Update prev to support calculation of next round.
prev = cur.location + cur.length;
/// Text exists after the last emoji.
if (i == sortedRanges.count - 1 && prev < string.length - 1) {
NSString *last = [string substringWithRange:NSMakeRange(prev, string.length - prev)];
[result addObject:last];
}
i++;
}
return @[ result, indexes ];
}
/// Match text by emoji, return the matched ranges
- (NSArray *)matchTextByEmoji:(NSString *)text {
NSMutableArray *result = [NSMutableArray new];
/// TUIKit qq emoji.
NSString *regexOfCustomEmoji = [self.class getRegex_emoji];
NSError *error = nil;
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:regexOfCustomEmoji options:NSRegularExpressionCaseInsensitive error:&error];
if (error) {
NSLog(@"re match custom emoji failed, error: %@", [error localizedDescription]);
return nil;
}
NSArray *matchResult = [re matchesInString:text options:0 range:NSMakeRange(0, text.length)];
for (NSTextCheckingResult *match in matchResult) {
NSString *substring = [text substringWithRange:match.range];
TUIFaceGroup *group = [TIMConfig defaultConfig].faceGroups[0];
for (TUIFaceCellData *face in group.faces) {
if ([face.name isEqualToString:substring] || [face.localizableName isEqualToString:substring]) {
[result addObject:[NSValue valueWithRange:match.range]];
break;
}
}
}
/// Unicode emoji.
NSString *regexOfUnicodeEmoji = [NSString unicodeEmojiReString];
re = [NSRegularExpression regularExpressionWithPattern:regexOfUnicodeEmoji options:NSRegularExpressionCaseInsensitive error:&error];
if (error) {
NSLog(@"re match universal emoji failed, error: %@", [error localizedDescription]);
return [result copy];
}
matchResult = [re matchesInString:text options:0 range:NSMakeRange(0, text.length)];
for (NSTextCheckingResult *match in matchResult) {
[result addObject:[NSValue valueWithRange:match.range]];
}
return [result copy];
}
+ (NSString *)replacedStringWithArray:(NSArray *)array index:(NSArray *)indexArray replaceDict:(NSDictionary *)replaceDict {
if (replaceDict == nil) {
return nil;
}
NSMutableArray *mutableArray = [array mutableCopy];
for (NSNumber *value in indexArray) {
NSInteger i = [value integerValue];
if (i < 0 || i > mutableArray.count - 1) {
continue;
}
if (replaceDict[mutableArray[i]]) {
mutableArray[i] = replaceDict[mutableArray[i]];
}
}
return [mutableArray componentsJoinedByString:@""];
}
/**
* Regex of unicode emoji, refer to https://unicode.org/reports/tr51/#EBNF_and_Regex
* Regex exression is like:
\p{ri} \p{ri}
| \p{Emoji}
( \p{EMod}
| \x{FE0F} \x{20E3}?
| [\x{E0020}-\x{E007E}]+ \x{E007F}
)?
(\x{200D}
( \p{ri} \p{ri}
| \p{Emoji}
( \p{EMod}
| \x{FE0F} \x{20E3}?
| [\x{E0020}-\x{E007E}]+ \x{E007F}
)?
)
)*
*/
+ (NSString *)unicodeEmojiReString {
NSString *ri = @"[\U0001F1E6-\U0001F1FF]";
/// \u0023(#), \u002A(*), \u0030(keycap 0), \u0039(keycap 9), \u00A9(©), \u00AE(®) couldn't be added to NSString directly, need to transform a little bit.
NSString *unsupport = [NSString stringWithFormat:@"%C|%C|[%C-%C]|", 0x0023, 0x002A, 0x0030, 0x0039];
NSString *support =
@"\U000000A9|\U000000AE|\u203C|\u2049|\u2122|\u2139|[\u2194-\u2199]|[\u21A9-\u21AA]|[\u231A-\u231B]|\u2328|\u23CF|[\u23E9-\u23EF]|[\u23F0-\u23F3]|["
@"\u23F8-\u23FA]|\u24C2|[\u25AA-\u25AB]|\u25B6|\u25C0|[\u25FB-\u25FE]|[\u2600-\u2604]|\u260E|\u2611|[\u2614-\u2615]|\u2618|\u261D|\u2620|[\u2622-"
@"\u2623]|\u2626|\u262A|[\u262E-\u262F]|[\u2638-\u263A]|\u2640|\u2642|[\u2648-\u264F]|[\u2650-\u2653]|\u265F|\u2660|\u2663|[\u2665-\u2666]|\u2668|"
@"\u267B|[\u267E-\u267F]|[\u2692-\u2697]|\u2699|[\u269B-\u269C]|[\u26A0-\u26A1]|\u26A7|[\u26AA-\u26AB]|[\u26B0-\u26B1]|[\u26BD-\u26BE]|[\u26C4-\u26C5]|"
@"\u26C8|[\u26CE-\u26CF]|\u26D1|[\u26D3-\u26D4]|[\u26E9-\u26EA]|[\u26F0-\u26F5]|[\u26F7-\u26FA]|\u26FD|\u2702|\u2705|[\u2708-\u270D]|\u270F|\u2712|"
@"\u2714|\u2716|\u271D|\u2721|\u2728|[\u2733-\u2734]|\u2744|\u2747|\u274C|\u274E|[\u2753-\u2755]|\u2757|[\u2763-\u2764]|[\u2795-\u2797]|\u27A1|\u27B0|"
@"\u27BF|[\u2934-\u2935]|[\u2B05-\u2B07]|[\u2B1B-\u2B1C]|\u2B50|\u2B55|\u3030|\u303D|\u3297|\u3299|\U0001F004|\U0001F0CF|[\U0001F170-\U0001F171]|["
@"\U0001F17E-\U0001F17F]|\U0001F18E|[\U0001F191-\U0001F19A]|[\U0001F1E6-\U0001F1FF]|[\U0001F201-\U0001F202]|\U0001F21A|\U0001F22F|[\U0001F232-"
@"\U0001F23A]|[\U0001F250-\U0001F251]|[\U0001F300-\U0001F30F]|[\U0001F310-\U0001F31F]|[\U0001F320-\U0001F321]|[\U0001F324-\U0001F32F]|[\U0001F330-"
@"\U0001F33F]|[\U0001F340-\U0001F34F]|[\U0001F350-\U0001F35F]|[\U0001F360-\U0001F36F]|[\U0001F370-\U0001F37F]|[\U0001F380-\U0001F38F]|[\U0001F390-"
@"\U0001F393]|[\U0001F396-\U0001F397]|[\U0001F399-\U0001F39B]|[\U0001F39E-\U0001F39F]|[\U0001F3A0-\U0001F3AF]|[\U0001F3B0-\U0001F3BF]|[\U0001F3C0-"
@"\U0001F3CF]|[\U0001F3D0-\U0001F3DF]|[\U0001F3E0-\U0001F3EF]|\U0001F3F0|[\U0001F3F3-\U0001F3F5]|[\U0001F3F7-\U0001F3FF]|[\U0001F400-\U0001F40F]|["
@"\U0001F410-\U0001F41F]|[\U0001F420-\U0001F42F]|[\U0001F430-\U0001F43F]|[\U0001F440-\U0001F44F]|[\U0001F450-\U0001F45F]|[\U0001F460-\U0001F46F]|["
@"\U0001F470-\U0001F47F]|[\U0001F480-\U0001F48F]|[\U0001F490-\U0001F49F]|[\U0001F4A0-\U0001F4AF]|[\U0001F4B0-\U0001F4BF]|[\U0001F4C0-\U0001F4CF]|["
@"\U0001F4D0-\U0001F4DF]|[\U0001F4E0-\U0001F4EF]|[\U0001F4F0-\U0001F4FF]|[\U0001F500-\U0001F50F]|[\U0001F510-\U0001F51F]|[\U0001F520-\U0001F52F]|["
@"\U0001F530-\U0001F53D]|[\U0001F549-\U0001F54E]|[\U0001F550-\U0001F55F]|[\U0001F560-\U0001F567]|\U0001F56F|\U0001F570|[\U0001F573-\U0001F57A]|"
@"\U0001F587|[\U0001F58A-\U0001F58D]|\U0001F590|[\U0001F595-\U0001F596]|[\U0001F5A4-\U0001F5A5]|\U0001F5A8|[\U0001F5B1-\U0001F5B2]|\U0001F5BC|["
@"\U0001F5C2-\U0001F5C4]|[\U0001F5D1-\U0001F5D3]|[\U0001F5DC-\U0001F5DE]|\U0001F5E1|\U0001F5E3|\U0001F5E8|\U0001F5EF|\U0001F5F3|[\U0001F5FA-\U0001F5FF]"
@"|[\U0001F600-\U0001F60F]|[\U0001F610-\U0001F61F]|[\U0001F620-\U0001F62F]|[\U0001F630-\U0001F63F]|[\U0001F640-\U0001F64F]|[\U0001F650-\U0001F65F]|["
@"\U0001F660-\U0001F66F]|[\U0001F670-\U0001F67F]|[\U0001F680-\U0001F68F]|[\U0001F690-\U0001F69F]|[\U0001F6A0-\U0001F6AF]|[\U0001F6B0-\U0001F6BF]|["
@"\U0001F6C0-\U0001F6C5]|[\U0001F6CB-\U0001F6CF]|[\U0001F6D0-\U0001F6D2]|[\U0001F6D5-\U0001F6D7]|[\U0001F6DD-\U0001F6DF]|[\U0001F6E0-\U0001F6E5]|"
@"\U0001F6E9|[\U0001F6EB-\U0001F6EC]|\U0001F6F0|[\U0001F6F3-\U0001F6FC]|[\U0001F7E0-\U0001F7EB]|\U0001F7F0|[\U0001F90C-\U0001F90F]|[\U0001F910-"
@"\U0001F91F]|[\U0001F920-\U0001F92F]|[\U0001F930-\U0001F93A]|[\U0001F93C-\U0001F93F]|[\U0001F940-\U0001F945]|[\U0001F947-\U0001F94C]|[\U0001F94D-"
@"\U0001F94F]|[\U0001F950-\U0001F95F]|[\U0001F960-\U0001F96F]|[\U0001F970-\U0001F97F]|[\U0001F980-\U0001F98F]|[\U0001F990-\U0001F99F]|[\U0001F9A0-"
@"\U0001F9AF]|[\U0001F9B0-\U0001F9BF]|[\U0001F9C0-\U0001F9CF]|[\U0001F9D0-\U0001F9DF]|[\U0001F9E0-\U0001F9EF]|[\U0001F9F0-\U0001F9FF]|[\U0001FA70-"
@"\U0001FA74]|[\U0001FA78-\U0001FA7C]|[\U0001FA80-\U0001FA86]|[\U0001FA90-\U0001FA9F]|[\U0001FAA0-\U0001FAAC]|[\U0001FAB0-\U0001FABA]|[\U0001FAC0-"
@"\U0001FAC5]|[\U0001FAD0-\U0001FAD9]|[\U0001FAE0-\U0001FAE7]|[\U0001FAF0-\U0001FAF6]";
NSString *emoji = [NSString stringWithFormat:@"[%@%@]", unsupport, support];
/// Construct regex of emoji by the rules above.
NSString *eMod = @"[\U0001F3FB-\U0001F3FF]";
NSString *variationSelector = @"\uFE0F";
NSString *keycap = @"\u20E3";
NSString *tags = @"[\U000E0020-\U000E007E]";
NSString *termTag = @"\U000E007F";
NSString *zwj = @"\u200D";
NSString *riSequence = [NSString stringWithFormat:@"[%@][%@]", ri, ri];
NSString *element = [NSString stringWithFormat:@"[%@]([%@]|%@%@?|[%@]+%@)?", emoji, eMod, variationSelector, keycap, tags, termTag];
NSString *regexEmoji = [NSString stringWithFormat:@"%@|%@(%@(%@|%@))*", riSequence, element, zwj, riSequence, element];
return regexEmoji;
}
@end
@implementation NSAttributedString (EmojiExtension)
- (NSString *)tui_getPlainString {
NSMutableString *plainString = [NSMutableString stringWithString:self.string];
__block NSUInteger base = 0;
[self enumerateAttribute:NSAttachmentAttributeName
inRange:NSMakeRange(0, self.length)
options:0
usingBlock:^(id value, NSRange range, BOOL *stop) {
if (value && [value isKindOfClass:[TUIEmojiTextAttachment class]]) {
[plainString replaceCharactersInRange:NSMakeRange(range.location + base, range.length)
withString:((TUIEmojiTextAttachment *)value).emojiTag];
base += ((TUIEmojiTextAttachment *)value).emojiTag.length - 1;
}
}];
return plainString;
}
@end

View File

@@ -0,0 +1,30 @@
// 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

View File

@@ -0,0 +1,15 @@
//
// 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

View File

@@ -0,0 +1,280 @@
// 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

View File

@@ -0,0 +1,151 @@
//
// 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

View File

@@ -0,0 +1,110 @@
// 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

View File

@@ -0,0 +1,160 @@
//
// 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

View File

@@ -0,0 +1,24 @@
//
// 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

View File

@@ -0,0 +1,26 @@
//
// 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

View File

@@ -0,0 +1,73 @@
// 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

View File

@@ -0,0 +1,97 @@
//
// 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

View File

@@ -0,0 +1,17 @@
//
// 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

View File

@@ -0,0 +1,21 @@
//
// 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

View File

@@ -0,0 +1,27 @@
//
// 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

View File

@@ -0,0 +1,41 @@
//
// 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

View File

@@ -0,0 +1,587 @@
//
// TIMCommonModel.h
// TIMCommon
//
// Created by cologne on 2023/3/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <TUICore/TUICommonModel.h>
#import "TIMDefine.h"
NS_ASSUME_NONNULL_BEGIN
////////////////////////////////////////////////////////////////////////////////
//
// TUIPopView
//
/////////////////////////////////////////////////////////////////////////////////
@class TUIPopView;
@protocol TUIPopViewDelegate <NSObject>
- (void)popView:(TUIPopView *)popView didSelectRowAtIndex:(NSInteger)index;
@end
@interface TUIPopView : UIView
@property(nonatomic, strong) UITableView *tableView;
@property(nonatomic, assign) CGPoint arrowPoint;
@property(nonatomic, weak) id<TUIPopViewDelegate> delegate;
- (void)setData:(NSMutableArray *)data;
- (void)showInWindow:(UIWindow *)window;
@end
@interface TUIPopCellData : NSObject
@property(nonatomic, strong) UIImage *image;
@property(nonatomic, strong) NSString *title;
@end
@interface TUIPopCell : UITableViewCell
@property(nonatomic, strong) UIImageView *image;
@property(nonatomic, strong) UILabel *title;
+ (CGFloat)getHeight;
- (void)setData:(TUIPopCellData *)data;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIModifyView
//
/////////////////////////////////////////////////////////////////////////////////
@class TUIModifyView;
@protocol TUIModifyViewDelegate <NSObject>
- (void)modifyView:(TUIModifyView *)modifyView didModiyContent:(NSString *)content;
@end
@interface TUIModifyViewData : NSObject
@property(nonatomic, strong) NSString *title;
@property(nonatomic, strong) NSString *content;
@property(nonatomic, strong) NSString *desc;
@property(nonatomic, assign) BOOL enableNull;
@end
@interface TUIModifyView : UIView
@property(nonatomic, strong) UIView *container;
@property(nonatomic, strong) UILabel *title;
@property(nonatomic, strong) UITextField *content;
@property(nonatomic, strong) UILabel *descLabel;
@property(nonatomic, strong) UIButton *confirm;
@property(nonatomic, strong) UIView *hLine;
@property(nonatomic, weak) id<TUIModifyViewDelegate> delegate;
- (void)setData:(TUIModifyViewData *)data;
- (void)showInWindow:(UIWindow *)window;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUINaviBarIndicatorView
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUINaviBarIndicatorView : UIView
@property(nonatomic, strong) UIActivityIndicatorView *indicator;
@property(nonatomic, strong) UILabel *label;
@property(nonatomic, assign) CGFloat maxLabelLength;
- (void)setTitle:(NSString *)title;
- (void)startAnimating;
- (void)stopAnimating;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUICommonCell & data
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUICommonCellData : NSObject
@property(strong) NSString *reuseId;
@property(nonatomic, assign) SEL cselector;
@property(nonatomic, strong) NSDictionary *ext;
- (CGFloat)heightOfWidth:(CGFloat)width;
- (CGFloat)estimatedHeight;
@end
@interface TUICommonTableViewCell : UITableViewCell
@property(readonly) TUICommonCellData *data;
@property UIColor *colorWhenTouched;
@property BOOL changeColorWhenTouched;
- (void)fillWithData:(TUICommonCellData *)data;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUICommonTextCell & data
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUICommonTextCellData : TUICommonCellData
@property NSString *key;
@property NSString *value;
@property BOOL showAccessory;
@property UIColor *keyColor;
@property UIColor *valueColor;
@property BOOL enableMultiLineValue;
@property(nonatomic, assign) UIEdgeInsets keyEdgeInsets;
@end
@interface TUICommonTextCell : TUICommonTableViewCell
@property UILabel *keyLabel;
@property UILabel *valueLabel;
@property(readonly) TUICommonTextCellData *textData;
- (void)fillWithData:(TUICommonTextCellData *)data;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUICommonSwitchCell & data
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUICommonSwitchCellData : TUICommonCellData
@property NSString *title;
@property NSString *desc;
@property(getter=isOn) BOOL on;
@property CGFloat margin;
@property SEL cswitchSelector;
@property(nonatomic, assign) BOOL displaySeparatorLine;
@property(nonatomic, assign) BOOL disableChecked;
@end
@interface TUICommonSwitchCell : 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) TUICommonSwitchCellData *switchData;
- (void)fillWithData:(TUICommonSwitchCellData *)data;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIButtonCell & data
//
/////////////////////////////////////////////////////////////////////////////////
typedef enum : NSUInteger {
ButtonGreen,
ButtonWhite,
ButtonRedText,
ButtonBule,
} TUIButtonStyle;
@interface TUIButtonCellData : TUICommonCellData
@property(nonatomic, strong) NSString *title;
@property SEL cbuttonSelector;
@property TUIButtonStyle style;
@property(nonatomic, strong) UIColor *textColor;
@property(nonatomic, assign) BOOL hideSeparatorLine;
@end
@interface TUIButtonCell : TUICommonTableViewCell
@property(nonatomic, strong) UIButton *button;
@property TUIButtonCellData *buttonData;
- (void)fillWithData:(TUIButtonCellData *)data;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIGroupPendencyCell & data
//
/////////////////////////////////////////////////////////////////////////////////
#define TUIGroupPendencyCellData_onPendencyChanged @"TUIGroupPendencyCellData_onPendencyChanged"
@interface TUIGroupPendencyCellData : TUICommonCellData
@property(nonatomic, strong) NSString *groupId;
@property(nonatomic, strong) NSString *fromUser;
@property(nonatomic, strong) NSString *toUser;
@property(readonly) V2TIMGroupApplication *pendencyItem;
@property NSURL *avatarUrl;
@property NSString *title;
/**
* The joining group introduction of the requester. Such as "Xiao Ming applied to join the group".
*/
@property NSString *requestMsg;
/**
* Agree or Not
* YES: Agree; NO: Indicates that the current request was not granted, but does not mean that the request has been denied.
*/
@property BOOL isAccepted;
/**
*
* Refuse or Not
* YES: Refuse; NO: Indicates that the current request is not denied, but does not mean that the request has been granted.
*/
@property BOOL isRejectd;
@property SEL cbuttonSelector;
- (instancetype)initWithPendency:(V2TIMGroupApplication *)args;
typedef void (^TUIGroupPendencyCellDataSuccessCallback)(void);
typedef void (^TUIGroupPendencyCellDataFailureCallback)(int code, NSString *msg);
- (void)agreeWithSuccess:(TUIGroupPendencyCellDataSuccessCallback)success
failure:(TUIGroupPendencyCellDataFailureCallback)failure;
- (void)rejectWithSuccess:(TUIGroupPendencyCellDataSuccessCallback)success
failure:(TUIGroupPendencyCellDataFailureCallback)failure;
- (void)accept;
- (void)reject;
@end
@interface TUIGroupPendencyCell : TUICommonTableViewCell
@property UIImageView *avatarView;
@property UILabel *titleLabel;
@property UILabel *addWordingLabel;
@property UIButton *agreeButton;
@property TUIGroupPendencyCellData *pendencyData;
- (void)fillWithData:(TUIGroupPendencyCellData *)pendencyData;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIFaceCell & data
//
/////////////////////////////////////////////////////////////////////////////////
/**
* 【Module name】 TUIFaceCellData
* 【Function description]】The name and local storage path of the stored emoticon.
*/
@interface TUIFaceCellData : NSObject
/**
* The name of emoticon
*/
@property(nonatomic, strong) NSString *name;
/**
* The localized name of the emoticon (the attribute used for internationalization, if it is empty or the length is 0, the name is displayed by default)
*/
@property(nonatomic, copy) NSString *localizableName;
/**
* The storage path of the emoticon cached locally.
*/
@property(nonatomic, strong) NSString *path;
@end
/**
* 【Module name】TUIFaceCell
* 【Function description】 Store the image of the emoticon, and initialize the Cell according to TUIFaceCellData.
* In the emoticon view, TUIFaceCell is the unit displayed on the interface.
*/
@interface TUIFaceCell : UICollectionViewCell
/**
* The image view for displaying emoticon
*/
@property(nonatomic, strong) UIImageView *face;
@property(nonatomic, strong) UIImage *staicImage;
@property(nonatomic, strong) UIImage *gifImage;
@property(nonatomic, copy) void(^longPressCallback)(UILongPressGestureRecognizer *recognizer);
- (void)setData:(TUIFaceCellData *)data;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIFaceGroup
//
/////////////////////////////////////////////////////////////////////////////////
/**
* 【Module name】 TUIFaceGroup
* 【Function description】 It is used to realize the grouping of emoticon, which is convenient for users to browse and select under different emoticon themes.
* This class stores the index of each emoticon group, so that FaceView can locate each emoticon group.
* At the same time, this class stores the path of all emoticon pictures in an emoticon group, and provides data such as the number of lines, the number of
* emoticons in each line, etc., to locate specific emoticons
*/
@interface TUIFaceGroup : NSObject
/**
* Index of emoticons group, begining with zero.
*/
@property(nonatomic, assign) int groupIndex;
/**
* The resource path of the entire expression group
*/
@property(nonatomic, strong) NSString *groupPath;
/**
* The number of lines of emoticons in the emoticon group
*/
@property(nonatomic, assign) int rowCount;
/**
* The number of emoticons contained in each line
*/
@property(nonatomic, assign) int itemCountPerRow;
@property(nonatomic, strong) NSMutableArray *faces;
@property(nonatomic, strong) NSDictionary *facesMap;
/**
* The flag of indicating whether to display the delete button
* When set to YES, FaceView will display a "delete" icon in the lower right corner of the emoticon view. Clicking the icon can delete the entered emoticon
* directly without evoking the keyboard.
*/
@property(nonatomic, assign) BOOL needBackDelete;
/**
* The path to the cover image of the emoticon group
*/
@property(nonatomic, strong) NSString *menuPath;
@property(nonatomic, strong) TUIFaceGroup *recentGroup;
@property(nonatomic, assign) BOOL isNeedAddInInputBar;
@property(nonatomic, copy) NSString *groupName;
@end
@interface TUIEmojiTextAttachment : NSTextAttachment
@property(nonatomic, strong) TUIFaceCellData *faceCellData;
@property(nonatomic, copy) NSString *emojiTag;
@property(nonatomic, assign) CGSize emojiSize; // For emoji image size
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIUnReadView
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUIUnReadView : UIView
/**
* The label of displaying unread message count
*/
@property(nonatomic, strong) UILabel *unReadLabel;
/**
* Set the unread message count
*/
- (void)setNum:(NSInteger)num;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIConversationPin
//
/////////////////////////////////////////////////////////////////////////////////
extern NSString *kTopConversationListChangedNotification;
@interface TUIConversationPin : NSObject
+ (instancetype)sharedInstance;
/**
*
* Getting the list of pinned conversations
*/
- (NSArray *)topConversationList;
/**
*
* Pin the conversation
*/
- (void)addTopConversation:(NSString *)conv callback:(void (^__nullable)(BOOL success, NSString *__nullable errorMessage))callback;
/**
*
* Remove pinned conversations
*/
- (void)removeTopConversation:(NSString *)conv callback:(void (^__nullable)(BOOL success, NSString *__nullable errorMessage))callback;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUICommonContactSelectCellData
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUICommonContactSelectCellData : TUICommonCellData
@property(nonatomic, strong) NSString *identifier;
@property(nonatomic, strong) NSString *title;
@property(nonatomic, strong) NSURL *avatarUrl;
@property(nonatomic, strong) UIImage *avatarImage;
@property(nonatomic, getter=isSelected) BOOL selected;
@property(nonatomic, getter=isEnabled) BOOL enabled;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUICommonContactListPickerCell
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUICommonContactListPickerCell : UICollectionViewCell
@property UIImageView *avatar;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIContactListPickerOnCancel
//
/////////////////////////////////////////////////////////////////////////////////
typedef void (^TUIContactListPickerOnCancel)(TUICommonContactSelectCellData *data);
@interface TUIContactListPicker : UIControl
@property(nonatomic, strong, readonly) UIButton *accessoryBtn;
@property(nonatomic, strong) NSArray<TUICommonContactSelectCellData *> *selectArray;
@property(nonatomic, copy) TUIContactListPickerOnCancel onCancel;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIProfileCardCell & vc
//
/////////////////////////////////////////////////////////////////////////////////
@class TUIProfileCardCell;
@protocol TUIProfileCardDelegate <NSObject>
- (void)didTapOnAvatar:(TUIProfileCardCell *)cell;
@end
@interface TUIProfileCardCellData : 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 TUIProfileCardCell : 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) TUIProfileCardCellData *cardData;
@property(nonatomic, weak) id<TUIProfileCardDelegate> delegate;
- (void)fillWithData:(TUIProfileCardCellData *)data;
@end
@interface TUIAvatarViewController : UIViewController
@property(nonatomic, strong) TUIProfileCardCellData *avatarData;
@end
typedef NS_ENUM(NSUInteger, TUISelectAvatarType) {
TUISelectAvatarTypeUserAvatar,
TUISelectAvatarTypeGroupAvatar,
TUISelectAvatarTypeCover,
TUISelectAvatarTypeConversationBackGroundCover,
};
@interface TUISelectAvatarCardItem : NSObject
@property(nonatomic, strong) NSString *posterUrlStr;
@property(nonatomic, assign) BOOL isSelect;
@property(nonatomic, copy) NSString *fullUrlStr;
@property(nonatomic, assign) BOOL isDefaultBackgroundItem;
@property(nonatomic, assign) BOOL isGroupGridAvatar;
@property(nonatomic, copy) NSString *createGroupType;
@property(nonatomic, strong) UIImage *cacheGroupGridAvatarImage;
@end
@interface TUISelectAvatarController : UIViewController
@property(nonatomic, copy) void (^selectCallBack)(NSString *urlStr);
@property(nonatomic, assign) TUISelectAvatarType selectAvatarType;
@property(nonatomic, copy) NSString *profilFaceURL;
@property(nonatomic, strong) UIImage *cacheGroupGridAvatarImage;
@property(nonatomic, copy) NSString *createGroupType;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUICommonAvatarCell & Data
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUICommonAvatarCellData : TUICommonCellData
;
@property(nonatomic, strong) NSString *key;
@property(nonatomic, strong) NSString *value;
@property BOOL showAccessory;
@property(nonatomic, strong) UIImage *avatarImage;
@property(nonatomic, strong) NSURL *avatarUrl;
@end
@interface TUICommonAvatarCell : TUICommonTableViewCell
@property UILabel *keyLabel;
@property UILabel *valueLabel;
@property UIImageView *avatar;
@property(readonly) TUICommonAvatarCellData *avatarData;
- (void)fillWithData:(TUICommonAvatarCellData *)avatarData;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIConversationGroupItem
//
/////////////////////////////////////////////////////////////////////////////////
extern NSUInteger kConversationMarkStarType;
@interface TUIConversationGroupItem : NSObject
@property(nonatomic, strong) NSString *groupName;
@property(nonatomic, assign) NSInteger unreadCount;
@property(nonatomic, assign) NSInteger groupIndex;
@property(nonatomic, assign) BOOL isShow;
@property(nonatomic, strong) UIButton *groupBtn;
@end
@interface TUISendMessageAppendParams : NSObject
@property (nonatomic, assign) BOOL isSendPushInfo;
@property (nonatomic, assign) BOOL isOnlineUserOnly;
@property (nonatomic, assign) V2TIMMessagePriority priority;
+ (instancetype)defaultConfig;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
//
// 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 urlhttps://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 urlhttps://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

View File

@@ -0,0 +1,80 @@
//
// 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

View File

@@ -0,0 +1,26 @@
//
// 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 */

View File

@@ -0,0 +1,20 @@
// 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

View File

@@ -0,0 +1,46 @@
// 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

View File

@@ -0,0 +1,19 @@
//
// 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

View File

@@ -0,0 +1,33 @@
//
// 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

View File

@@ -0,0 +1,51 @@
//
// 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

View File

@@ -0,0 +1,291 @@
//
// TIMRTLUtil.m
// TIMCommon
//
// Created by cologne on 2023/7/21.
// Copyright © 2023 Tencent. All rights reserved
//
#import "TIMRTLUtil.h"
#import <objc/runtime.h>
#import <TUICore/TUIGlobalization.h>
@implementation TIMRTLUtil
@end
@interface UIView (TUIRTL)
@end
@implementation UIView (TUIRTL)
- (void)setRTLFrame:(CGRect)frame width:(CGFloat)width {
if (isRTL()) {
if (self.superview == nil) {
NSAssert(0, @"must invoke after have superView");
}
CGFloat x = width - frame.origin.x - frame.size.width;
frame.origin.x = x;
}
self.frame = frame;
}
- (void)setRTLFrame:(CGRect)frame {
[self setRTLFrame:frame width:self.superview.frame.size.width];
}
- (void)resetFrameToFitRTL {
[self setRTLFrame:self.frame];
}
@end
@interface UIImage (TUIRTL)
@end
@implementation UIImage (TUIRTL)
- (UIImage *_Nonnull)checkOverturn{
if (isRTL()) {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, self.size.width / 2, self.size.height / 2);
CGContextScaleCTM(bitmap, -1.0, -1.0);
CGContextTranslateCTM(bitmap, -self.size.width / 2, -self.size.height / 2);
CGContextDrawImage(bitmap, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
return image;
}
return self;
}
- (UIImage *)_imageFlippedForRightToLeftLayoutDirection {
if (isRTL()) {
return [UIImage imageWithCGImage:self.CGImage
scale:self.scale
orientation:UIImageOrientationUpMirrored];
}
return self;
}
- (UIImage *)rtl_imageFlippedForRightToLeftLayoutDirection {
if (isRTL()) {
if (@available(iOS 13.0, *)) {
UITraitCollection *const scaleTraitCollection = [UITraitCollection currentTraitCollection];
UITraitCollection *const darkUnscaledTraitCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
UITraitCollection *const darkScaledTraitCollection =
[UITraitCollection traitCollectionWithTraitsFromCollections:@[ scaleTraitCollection, darkUnscaledTraitCollection ]];
UIImage *lightImg = [[self.imageAsset imageWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]
_imageFlippedForRightToLeftLayoutDirection];
UIImage *darkImage = [[self.imageAsset imageWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]]
_imageFlippedForRightToLeftLayoutDirection];
UIImage *image =
[lightImg imageWithConfiguration:[self.configuration
configurationWithTraitCollection:[UITraitCollection
traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]];
[image.imageAsset registerImage:darkImage withTraitCollection:darkScaledTraitCollection];
return image;
} else {
return [UIImage imageWithCGImage:self.CGImage scale:self.scale orientation:UIImageOrientationUpMirrored];
}
}
return self;
}
@end
@interface UINavigationController (TUIRTL)
@end
@implementation UINavigationController (TUIRTL)
+ (void)load {
Method oldMethod = class_getInstanceMethod(self, @selector(initWithRootViewController:));
Method newMethod = class_getInstanceMethod(self, @selector(rtl_initWithRootViewController:));
method_exchangeImplementations(oldMethod, newMethod);
}
- (instancetype)rtl_initWithRootViewController:(UIViewController *)rootViewController {
if ([self rtl_initWithRootViewController:rootViewController]) {
if (@available(iOS 9.0, *)) {
if (isRTL()) {
self.navigationBar.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
self.view.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
}
}
}
return self;
}
@end
UIEdgeInsets rtlEdgeInsetsWithInsets(UIEdgeInsets insets) {
if (insets.left != insets.right && isRTL()) {
CGFloat temp = insets.left;
insets.left = insets.right;
insets.right = temp;
}
return insets;
}
@implementation UIButton (TUIRTL)
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){
if (!cls) {
return;
}
/* if current class not exist selector, then get super*/
Method originalMethod = class_getInstanceMethod(cls, originSelector);
Method swizzledMethod = class_getInstanceMethod(cls, swizzleSelector);
/* add selector if not exist, implement append with method */
if (class_addMethod(cls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)) ) {
/* replace class instance method, added if selector not exist */
/* for class cluster , it always add new selector here */
class_replaceMethod(cls,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
/* swizzleMethod maybe belong to super */
class_replaceMethod(cls,
swizzleSelector,
class_replaceMethod(cls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)),
method_getTypeEncoding(originalMethod));
}
}
+ (void)load
{
swizzleInstanceMethod(self, @selector(setContentEdgeInsets:), @selector(rtl_setContentEdgeInsets:));
swizzleInstanceMethod(self, @selector(setImageEdgeInsets:), @selector(rtl_setImageEdgeInsets:));
swizzleInstanceMethod(self, @selector(setTitleEdgeInsets:), @selector(rtl_setTitleEdgeInsets:));
}
- (void)rtl_setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets {
[self rtl_setContentEdgeInsets:rtlEdgeInsetsWithInsets(contentEdgeInsets)];
}
- (void)rtl_setImageEdgeInsets:(UIEdgeInsets)imageEdgeInsets {
[self rtl_setImageEdgeInsets:rtlEdgeInsetsWithInsets(imageEdgeInsets)];
}
- (void)rtl_setTitleEdgeInsets:(UIEdgeInsets)titleEdgeInsets {
[self rtl_setTitleEdgeInsets:rtlEdgeInsetsWithInsets(titleEdgeInsets)];
}
@end
@implementation UILabel (TUIRTL)
- (void)setRtlAlignment:(TUITextRTLAlignment)rtlAlignment {
objc_setAssociatedObject(self, @selector(rtlAlignment), @(rtlAlignment), OBJC_ASSOCIATION_ASSIGN);
switch (rtlAlignment) {
case TUITextRTLAlignmentLeading:
self.textAlignment = (isRTL() ? NSTextAlignmentRight : NSTextAlignmentLeft);
break;
case TUITextRTLAlignmentTrailing:
self.textAlignment = (isRTL() ? NSTextAlignmentLeft : NSTextAlignmentRight);
break;
case TUITextRTLAlignmentCenter:
self.textAlignment = NSTextAlignmentCenter;
case TUITextRTLAlignmentUndefine:
break;
default:
break;
}
}
- (TUITextRTLAlignment)rtlAlignment {
NSNumber *identifier = objc_getAssociatedObject(self, @selector(rtlAlignment));
if (identifier) {
return identifier.integerValue;
}
return TUITextRTLAlignmentUndefine;
}
@end
@implementation NSMutableAttributedString (TUIRTL)
- (void)setRtlAlignment:(TUITextRTLAlignment)rtlAlignment {
switch (rtlAlignment) {
case TUITextRTLAlignmentLeading:
self.rtlAlignment = (isRTL() ? NSTextAlignmentRight : NSTextAlignmentLeft);
break;
case TUITextRTLAlignmentTrailing:
self.rtlAlignment = (isRTL() ? NSTextAlignmentLeft : NSTextAlignmentRight);
break;
case TUITextRTLAlignmentCenter:
self.rtlAlignment = NSTextAlignmentCenter;
case TUITextRTLAlignmentUndefine:
break;
default:
break;
}
}
@end
BOOL isRTLString(NSString *string) {
if ([string hasPrefix:@"\u202B"] || [string hasPrefix:@"\u202A"]) {
return YES;
}
return NO;
}
NSString * rtlString(NSString *string) {
if (string.length == 0 || isRTLString(string)) {
return string;
}
if (isRTL()) {
string = [@"\u202B" stringByAppendingString:string];
} else {
string = [@"\u202A" stringByAppendingString:string];
}
return string;
}
NSAttributedString *rtlAttributeString(NSAttributedString *attributeString ,NSTextAlignment textAlignment ){
if (attributeString.length == 0) {
return attributeString;
}
NSRange range;
NSDictionary *originAttributes = [attributeString attributesAtIndex:0 effectiveRange:&range];
NSParagraphStyle *style = [originAttributes objectForKey:NSParagraphStyleAttributeName];
if (style && isRTLString(attributeString.string)) {
return attributeString;
}
NSMutableDictionary *attributes = originAttributes ? [originAttributes mutableCopy] : [NSMutableDictionary new];
if (!style) {
NSMutableParagraphStyle *mutableParagraphStyle = [[NSMutableParagraphStyle alloc] init];
UILabel *test = [UILabel new];
test.textAlignment = textAlignment;
mutableParagraphStyle.alignment = test.textAlignment;
style = mutableParagraphStyle;
[attributes setValue:mutableParagraphStyle forKey:NSParagraphStyleAttributeName];
}
NSString *string = rtlString(attributeString.string);
return [[NSAttributedString alloc] initWithString:string attributes:attributes];
}
@implementation TUICollectionRTLFitFlowLayout
- (UIUserInterfaceLayoutDirection)effectiveUserInterfaceLayoutDirection {
if (isRTL()) {
return UIUserInterfaceLayoutDirectionRightToLeft;
}
return UIUserInterfaceLayoutDirectionLeftToRight;
}
- (BOOL)flipsHorizontallyInOppositeLayoutDirection{
return isRTL()? YES:NO;
}
@end

View File

@@ -0,0 +1,661 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
// TUIAttributedLabel.h
#import <CoreText/CoreText.h>
#import <UIKit/UIKit.h>
//! Project version number for TUIAttributedLabel.
FOUNDATION_EXPORT double TUIAttributedLabelVersionNumber;
//! Project version string for TUIAttributedLabel.
FOUNDATION_EXPORT const unsigned char TUIAttributedLabelVersionString[];
@class TUIAttributedLabelLink;
/**
Vertical alignment for text in a label whose bounds are larger than its text bounds
*/
typedef NS_ENUM(NSInteger, TUIAttributedLabelVerticalAlignment) {
TUIAttributedLabelVerticalAlignmentCenter = 0,
TUIAttributedLabelVerticalAlignmentTop = 1,
TUIAttributedLabelVerticalAlignmentBottom = 2,
};
/**
Determines whether the text to which this attribute applies has a strikeout drawn through itself.
*/
extern NSString *const kTUIStrikeOutAttributeName;
/**
The background fill color. Value must be a `CGColorRef`. Default value is `nil` (no fill).
*/
extern NSString *const kTUIBackgroundFillColorAttributeName;
/**
The padding for the background fill. Value must be a `UIEdgeInsets`. Default value is `UIEdgeInsetsZero` (no padding).
*/
extern NSString *const kTUIBackgroundFillPaddingAttributeName;
/**
The background stroke color. Value must be a `CGColorRef`. Default value is `nil` (no stroke).
*/
extern NSString *const kTUIBackgroundStrokeColorAttributeName;
/**
The background stroke line width. Value must be an `NSNumber`. Default value is `1.0f`.
*/
extern NSString *const kTUIBackgroundLineWidthAttributeName;
/**
The background corner radius. Value must be an `NSNumber`. Default value is `5.0f`.
*/
extern NSString *const kTUIBackgroundCornerRadiusAttributeName;
@protocol TUIAttributedLabelDelegate;
// Override UILabel @property to accept both NSString and NSAttributedString
@protocol TUIAttributedLabel <NSObject>
@property(nonatomic, copy) IBInspectable id text;
@end
IB_DESIGNABLE
/**
`TUIAttributedLabel` is a drop-in replacement for `UILabel` that supports `NSAttributedString`, as well as automatically-detected and manually-added links to
URLs, addresses, phone numbers, and dates.
## Differences Between `TUIAttributedLabel` and `UILabel`
For the most part, `TUIAttributedLabel` behaves just like `UILabel`. The following are notable exceptions, in which `TUIAttributedLabel` may act differently:
- `text` - This property now takes an `id` type argument, which can either be a kind of `NSString` or `NSAttributedString` (mutable or immutable in both cases)
- `attributedText` - Do not set this property directly. Instead, pass an `NSAttributedString` to `text`.
- `lineBreakMode` - This property displays only the first line when the value is `UILineBreakModeHeadTruncation`, `UILineBreakModeTailTruncation`, or
`UILineBreakModeMiddleTruncation`
- `adjustsFontsizeToFitWidth` - Supported in iOS 5 and greater, this property is effective for any value of `numberOfLines` greater than zero. In iOS 4,
setting `numberOfLines` to a value greater than 1 with `adjustsFontSizeToFitWidth` set to `YES` may cause `sizeToFit` to execute indefinitely.
- `baselineAdjustment` - This property has no affect.
- `textAlignment` - This property does not support justified alignment.
- `NSTextAttachment` - This string attribute is not supported.
Any properties affecting text or paragraph styling, such as `firstLineIndent` will only apply when text is set with an `NSString`. If the text is set with an
`NSAttributedString`, these properties will not apply.
### NSCoding
`TUIAttributedLabel`, like `UILabel`, conforms to `NSCoding`. However, if the build target is set to less than iOS 6.0, `linkAttributes` and
`activeLinkAttributes` will not be encoded or decoded. This is due to an runtime exception thrown when attempting to copy non-object CoreText values in
dictionaries.
@warning Any properties changed on the label after setting the text will not be reflected until a subsequent call to `setText:` or
`setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`. This is to say, order of operations matters in this case. For example, if the label text
color is originally black when the text is set, changing the text color to red will have no effect on the display of the label until the text is set once
again.
@bug Setting `attributedText` directly is not recommended, as it may cause a crash when attempting to access any links previously set. Instead, call
`setText:`, passing an `NSAttributedString`.
*/
@interface TUIAttributedLabel : UILabel <TUIAttributedLabel, UIGestureRecognizerDelegate>
/**
* The designated initializers are @c initWithFrame: and @c initWithCoder:.
* init will not properly initialize many required properties and other configuration.
*/
- (instancetype)init NS_UNAVAILABLE;
///-----------------------------
/// @name Accessing the Delegate
///-----------------------------
/**
The receiver's delegate.
@discussion A `TUIAttributedLabel` delegate responds to messages sent by tapping on links in the label. You can use the delegate to respond to links
referencing a URL, address, phone number, date, or date with a specified time zone and duration.
*/
@property(nonatomic, unsafe_unretained) IBOutlet id<TUIAttributedLabelDelegate> delegate;
///--------------------------------------------
/// @name Detecting, Accessing, & Styling Links
///--------------------------------------------
/**
A bitmask of `NSTextCheckingType` which are used to automatically detect links in the label text.
@warning You must specify `enabledTextCheckingTypes` before setting the `text`, with either `setText:` or
`setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`.
*/
@property(nonatomic, assign) NSTextCheckingTypes enabledTextCheckingTypes;
/**
An array of `NSTextCheckingResult` objects for links detected or manually added to the label text.
*/
@property(readonly, nonatomic, strong) NSArray *links;
/**
A dictionary containing the default `NSAttributedString` attributes to be applied to links detected or manually added to the label text. The default link style
is blue and underlined.
@warning You must specify `linkAttributes` before setting autodecting or manually-adding links for these attributes to be applied.
*/
@property(nonatomic, strong) NSDictionary *linkAttributes;
/**
A dictionary containing the default `NSAttributedString` attributes to be applied to links when they are in the active state. If `nil` or an empty
`NSDictionary`, active links will not be styled. The default active link style is red and underlined.
*/
@property(nonatomic, strong) NSDictionary *activeLinkAttributes;
/**
A dictionary containing the default `NSAttributedString` attributes to be applied to links when they are in the inactive state, which is triggered by a change
in `tintColor` in iOS 7 and later. If `nil` or an empty `NSDictionary`, inactive links will not be styled. The default inactive link style is gray and
unadorned.
*/
@property(nonatomic, strong) NSDictionary *inactiveLinkAttributes;
/**
The edge inset for the background of a link. The default value is `{0, -1, 0, -1}`.
*/
@property(nonatomic, assign) UIEdgeInsets linkBackgroundEdgeInset;
/**
Indicates if links will be detected within an extended area around the touch
to emulate the link detection behaviour of WKWebView.
Default value is NO. Enabling this may adversely impact performance.
*/
@property(nonatomic, assign) BOOL extendsLinkTouchArea;
///---------------------------------------
/// @name Acccessing Text Style Attributes
///---------------------------------------
/**
The shadow blur radius for the label. A value of 0 indicates no blur, while larger values produce correspondingly larger blurring. This value must not be
negative. The default value is 0.
*/
@property(nonatomic, assign) IBInspectable CGFloat shadowRadius;
/**
The shadow blur radius for the label when the label's `highlighted` property is `YES`. A value of 0 indicates no blur, while larger values produce
correspondingly larger blurring. This value must not be negative. The default value is 0.
*/
@property(nonatomic, assign) IBInspectable CGFloat highlightedShadowRadius;
/**
The shadow offset for the label when the label's `highlighted` property is `YES`. A size of {0, 0} indicates no offset, with positive values extending down and
to the right. The default size is {0, 0}.
*/
@property(nonatomic, assign) IBInspectable CGSize highlightedShadowOffset;
/**
The shadow color for the label when the label's `highlighted` property is `YES`. The default value is `nil` (no shadow color).
*/
@property(nonatomic, strong) IBInspectable UIColor *highlightedShadowColor;
/**
The amount to kern the next character. Default is standard kerning. If this attribute is set to 0.0, no kerning is done at all.
*/
@property(nonatomic, assign) IBInspectable CGFloat kern;
///--------------------------------------------
/// @name Acccessing Paragraph Style Attributes
///--------------------------------------------
/**
The distance, in points, from the leading margin of a frame to the beginning of the
paragraph's first line. This value is always nonnegative, and is 0.0 by default.
This applies to the full text, rather than any specific paragraph metrics.
*/
@property(nonatomic, assign) IBInspectable CGFloat firstLineIndent;
/**
The space in points added between lines within the paragraph. This value is always nonnegative and is 0.0 by default.
*/
@property(nonatomic, assign) IBInspectable CGFloat lineSpacing;
/**
The minimum line height within the paragraph. If the value is 0.0, the minimum line height is set to the line height of the `font`. 0.0 by default.
*/
@property(nonatomic, assign) IBInspectable CGFloat minimumLineHeight;
/**
The maximum line height within the paragraph. If the value is 0.0, the maximum line height is set to the line height of the `font`. 0.0 by default.
*/
@property(nonatomic, assign) IBInspectable CGFloat maximumLineHeight;
/**
The line height multiple. This value is 1.0 by default.
*/
@property(nonatomic, assign) IBInspectable CGFloat lineHeightMultiple;
/**
The distance, in points, from the margin to the text container. This value is `UIEdgeInsetsZero` by default.
sizeThatFits: will have its returned size increased by these margins.
drawTextInRect: will inset all drawn text by these margins.
*/
@property(nonatomic, assign) IBInspectable UIEdgeInsets textInsets;
/**
The vertical text alignment for the label, for when the frame size is greater than the text rect size. The vertical alignment is
`TUIAttributedLabelVerticalAlignmentCenter` by default.
*/
@property(nonatomic, assign) TUIAttributedLabelVerticalAlignment verticalAlignment;
///--------------------------------------------
/// @name Accessing Truncation Token Appearance
///--------------------------------------------
/**
The attributed string to apply to the truncation token at the end of a truncated line.
*/
@property(nonatomic, strong) IBInspectable NSAttributedString *attributedTruncationToken;
///--------------------------
/// @name Long press gestures
///--------------------------
/**
* The long-press gesture recognizer used internally by the label.
*/
@property(nonatomic, strong, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;
///--------------------------------------------
/// @name Calculating Size of Attributed String
///--------------------------------------------
/**
Calculate and return the size that best fits an attributed string, given the specified constraints on size and number of lines.
@param attributedString The attributed string.
@param size The maximum dimensions used to calculate size.
@param numberOfLines The maximum number of lines in the text to draw, if the constraining size cannot accomodate the full attributed string.
@return The size that fits the attributed string within the specified constraints.
*/
+ (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString withConstraints:(CGSize)size limitedToNumberOfLines:(NSUInteger)numberOfLines;
///----------------------------------
/// @name Setting the Text Attributes
///----------------------------------
/**
Sets the text displayed by the label.
@param text An `NSString` or `NSAttributedString` object to be displayed by the label. If the specified text is an `NSString`, the label will display the text
like a `UILabel`, inheriting the text styles of the label. If the specified text is an `NSAttributedString`, the label text styles will be overridden by the
styles specified in the attributed string.
@discussion This method overrides `UILabel -setText:` to accept both `NSString` and `NSAttributedString` objects. This string is `nil` by default.
*/
- (void)setText:(id)text;
/**
Sets the text displayed by the label, after configuring an attributed string containing the text attributes inherited from the label in a block.
@param text An `NSString` or `NSAttributedString` object to be displayed by the label.
@param block A block object that returns an `NSMutableAttributedString` object and takes a single argument, which is an `NSMutableAttributedString` object with
the text from the first parameter, and the text attributes inherited from the label text styles. For example, if you specified the `font` of the label to be
`[UIFont boldSystemFontOfSize:14]` and `textColor` to be `[UIColor redColor]`, the `NSAttributedString` argument of the block would be contain the
`NSAttributedString` attribute equivalents of those properties. In this block, you can set further attributes on particular ranges.
@discussion This string is `nil` by default.
*/
- (void)setText:(id)text
afterInheritingLabelAttributesAndConfiguringWithBlock:(NSMutableAttributedString * (^)(NSMutableAttributedString *mutableAttributedString))block;
///------------------------------------
/// @name Accessing the Text Attributes
///------------------------------------
/**
A copy of the label's current attributedText. This returns `nil` if an attributed string has never been set on the label.
@warning Do not set this property directly. Instead, set @c text to an @c NSAttributedString.
*/
@property(readwrite, nonatomic, copy) NSAttributedString *attributedText;
///-------------------
/// @name Adding Links
///-------------------
/**
Adds a link. You can customize an individual link's appearance and accessibility value by creating your own @c TUIAttributedLabelLink and passing it to this
method. The other methods for adding links will use the label's default attributes.
@warning Modifying the link's attribute dictionaries must be done before calling this method.
@param link A @c TUIAttributedLabelLink object.
*/
- (void)addLink:(TUIAttributedLabelLink *)link;
/**
Adds a link to an @c NSTextCheckingResult.
@param result An @c NSTextCheckingResult representing the link's location and type.
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result;
/**
Adds a link to an @c NSTextCheckingResult.
@param result An @c NSTextCheckingResult representing the link's location and type.
@param attributes The attributes to be added to the text in the range of the specified link. If set, the label's @c activeAttributes and @c inactiveAttributes
will be applied to the link. If `nil`, no attributes are added to the link.
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result attributes:(NSDictionary *)attributes;
/**
Adds a link to a URL for a specified range in the label text.
@param url The url to be linked to
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkToURL:(NSURL *)url withRange:(NSRange)range;
/**
Adds a link to an address for a specified range in the label text.
@param addressComponents A dictionary of address components for the address to be linked to
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
@discussion The address component dictionary keys are described in `NSTextCheckingResult`'s "Keys for Address Components."
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkToAddress:(NSDictionary *)addressComponents withRange:(NSRange)range;
/**
Adds a link to a phone number for a specified range in the label text.
@param phoneNumber The phone number to be linked to.
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkToPhoneNumber:(NSString *)phoneNumber withRange:(NSRange)range;
/**
Adds a link to a date for a specified range in the label text.
@param date The date to be linked to.
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkToDate:(NSDate *)date withRange:(NSRange)range;
/**
Adds a link to a date with a particular time zone and duration for a specified range in the label text.
@param date The date to be linked to.
@param timeZone The time zone of the specified date.
@param duration The duration, in seconds from the specified date.
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkToDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone duration:(NSTimeInterval)duration withRange:(NSRange)range;
/**
Adds a link to transit information for a specified range in the label text.
@param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`.
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
@return The newly added link object.
*/
- (TUIAttributedLabelLink *)addLinkToTransitInformation:(NSDictionary *)components withRange:(NSRange)range;
/**
Returns whether an @c NSTextCheckingResult is found at the give point.
@discussion This can be used together with @c UITapGestureRecognizer to tap interactions with overlapping views.
@param point The point inside the label.
*/
- (BOOL)containslinkAtPoint:(CGPoint)point;
/**
Returns the @c TUIAttributedLabelLink at the give point if it exists.
@discussion This can be used together with @c UIViewControllerPreviewingDelegate to peek into links.
@param point The point inside the label.
*/
- (TUIAttributedLabelLink *)linkAtPoint:(CGPoint)point;
@end
/**
The `TUIAttributedLabelDelegate` protocol defines the messages sent to an attributed label delegate when links are tapped. All of the methods of this protocol
are optional.
*/
@protocol TUIAttributedLabelDelegate <NSObject>
///-----------------------------------
/// @name Responding to Link Selection
///-----------------------------------
@optional
/**
Tells the delegate that the user did select a link to a URL.
@param label The label whose link was selected.
@param url The URL for the selected link.
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url;
/**
Tells the delegate that the user did select a link to an address.
@param label The label whose link was selected.
@param addressComponents The components of the address for the selected link.
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithAddress:(NSDictionary *)addressComponents;
/**
Tells the delegate that the user did select a link to a phone number.
@param label The label whose link was selected.
@param phoneNumber The phone number for the selected link.
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber;
/**
Tells the delegate that the user did select a link to a date.
@param label The label whose link was selected.
@param date The datefor the selected link.
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithDate:(NSDate *)date;
/**
Tells the delegate that the user did select a link to a date with a time zone and duration.
@param label The label whose link was selected.
@param date The date for the selected link.
@param timeZone The time zone of the date for the selected link.
@param duration The duration, in seconds from the date for the selected link.
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone duration:(NSTimeInterval)duration;
/**
Tells the delegate that the user did select a link to transit information
@param label The label whose link was selected.
@param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`.
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithTransitInformation:(NSDictionary *)components;
/**
Tells the delegate that the user did select a link to a text checking result.
@discussion This method is called if no other delegate method was called, which can occur by either now implementing the method in `TUIAttributedLabelDelegate`
corresponding to a particular link, or the link was added by passing an instance of a custom `NSTextCheckingResult` subclass into
`-addLinkWithTextCheckingResult:`.
@param label The label whose link was selected.
@param result The custom text checking result.
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithTextCheckingResult:(NSTextCheckingResult *)result;
///---------------------------------
/// @name Responding to Long Presses
///---------------------------------
/**
* Long-press delegate methods include the CGPoint tapped within the label's coordinate space.
* This may be useful on iPad to present a popover from a specific origin point.
*/
/**
Tells the delegate that the user long-pressed a link to a URL.
@param label The label whose link was long pressed.
@param url The URL for the link.
@param point the point pressed, in the label's coordinate space
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithURL:(NSURL *)url atPoint:(CGPoint)point;
/**
Tells the delegate that the user long-pressed a link to an address.
@param label The label whose link was long pressed.
@param addressComponents The components of the address for the link.
@param point the point pressed, in the label's coordinate space
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithAddress:(NSDictionary *)addressComponents atPoint:(CGPoint)point;
/**
Tells the delegate that the user long-pressed a link to a phone number.
@param label The label whose link was long pressed.
@param phoneNumber The phone number for the link.
@param point the point pressed, in the label's coordinate space
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithPhoneNumber:(NSString *)phoneNumber atPoint:(CGPoint)point;
/**
Tells the delegate that the user long-pressed a link to a date.
@param label The label whose link was long pressed.
@param date The date for the selected link.
@param point the point pressed, in the label's coordinate space
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithDate:(NSDate *)date atPoint:(CGPoint)point;
/**
Tells the delegate that the user long-pressed a link to a date with a time zone and duration.
@param label The label whose link was long pressed.
@param date The date for the link.
@param timeZone The time zone of the date for the link.
@param duration The duration, in seconds from the date for the link.
@param point the point pressed, in the label's coordinate space
*/
- (void)attributedLabel:(TUIAttributedLabel *)label
didLongPressLinkWithDate:(NSDate *)date
timeZone:(NSTimeZone *)timeZone
duration:(NSTimeInterval)duration
atPoint:(CGPoint)point;
/**
Tells the delegate that the user long-pressed a link to transit information.
@param label The label whose link was long pressed.
@param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`.
@param point the point pressed, in the label's coordinate space
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithTransitInformation:(NSDictionary *)components atPoint:(CGPoint)point;
/**
Tells the delegate that the user long-pressed a link to a text checking result.
@discussion Similar to `-attributedLabel:didSelectLinkWithTextCheckingResult:`, this method is called if a link is long pressed and the delegate does not
implement the method corresponding to this type of link.
@param label The label whose link was long pressed.
@param result The custom text checking result.
@param point the point pressed, in the label's coordinate space
*/
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithTextCheckingResult:(NSTextCheckingResult *)result atPoint:(CGPoint)point;
@end
@interface TUIAttributedLabelLink : NSObject <NSCoding>
typedef void (^TUIAttributedLabelLinkBlock)(TUIAttributedLabel *, TUIAttributedLabelLink *);
/**
An `NSTextCheckingResult` representing the link's location and type.
*/
@property(readonly, nonatomic, strong) NSTextCheckingResult *result;
/**
A dictionary containing the @c NSAttributedString attributes to be applied to the link.
*/
@property(readonly, nonatomic, copy) NSDictionary *attributes;
/**
A dictionary containing the @c NSAttributedString attributes to be applied to the link when it is in the active state.
*/
@property(readonly, nonatomic, copy) NSDictionary *activeAttributes;
/**
A dictionary containing the @c NSAttributedString attributes to be applied to the link when it is in the inactive state, which is triggered by a change in
`tintColor` in iOS 7 and later.
*/
@property(readonly, nonatomic, copy) NSDictionary *inactiveAttributes;
/**
Additional information about a link for VoiceOver users. Has default values if the link's @c result is @c NSTextCheckingTypeLink, @c
NSTextCheckingTypePhoneNumber, or @c NSTextCheckingTypeDate.
*/
@property(nonatomic, copy) NSString *accessibilityValue;
/**
A block called when this link is tapped.
If non-nil, tapping on this link will call this block instead of the
@c TUIAttributedLabelDelegate tap methods, which will not be called for this link.
*/
@property(nonatomic, copy) TUIAttributedLabelLinkBlock linkTapBlock;
/**
A block called when this link is long-pressed.
If non-nil, long pressing on this link will call this block instead of the
@c TUIAttributedLabelDelegate long press methods, which will not be called for this link.
*/
@property(nonatomic, copy) TUIAttributedLabelLinkBlock linkLongPressBlock;
/**
Initializes a link using the attribute dictionaries specified.
@param attributes The @c attributes property for the link.
@param activeAttributes The @c activeAttributes property for the link.
@param inactiveAttributes The @c inactiveAttributes property for the link.
@param result An @c NSTextCheckingResult representing the link's location and type.
@return The initialized link object.
*/
- (instancetype)initWithAttributes:(NSDictionary *)attributes
activeAttributes:(NSDictionary *)activeAttributes
inactiveAttributes:(NSDictionary *)inactiveAttributes
textCheckingResult:(NSTextCheckingResult *)result;
/**
Initializes a link using the attribute dictionaries set on a specified label.
@param label The attributed label from which to inherit attribute dictionaries.
@param result An @c NSTextCheckingResult representing the link's location and type.
@return The initialized link object.
*/
- (instancetype)initWithAttributesFromLabel:(TUIAttributedLabel *)label textCheckingResult:(NSTextCheckingResult *)result;
@end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
//
// 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

View File

@@ -0,0 +1,29 @@
//
// 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

View File

@@ -0,0 +1,66 @@
//
// TUIFitButton.m
// TUICore
//
// Created by wyl on 2022/5/24.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIFitButton.h"
@implementation TUIFitButton
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
if (!CGRectEqualToRect(self.titleRect, CGRectZero)) {
return self.titleRect;
} else if (!CGSizeEqualToSize(self.titleSize, CGSizeZero)) {
CGRect oldrect = [super titleRectForContentRect:contentRect];
CGRect newrect = CGRectZero;
newrect.origin.x = oldrect.origin.x + (oldrect.size.width - self.titleSize.width) / 2;
newrect.origin.y = oldrect.origin.y + (oldrect.size.height - self.titleSize.height) / 2;
newrect.size.width = self.titleSize.width;
newrect.size.height = self.titleSize.height;
return newrect;
}
return [super titleRectForContentRect:contentRect];
}
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
if (!CGRectEqualToRect(self.imageRect, CGRectZero)) {
return self.imageRect;
} else if (!CGSizeEqualToSize(self.imageSize, CGSizeZero)) {
CGRect oldrect = [super imageRectForContentRect:contentRect];
CGRect newrect = CGRectZero;
newrect.origin.x = oldrect.origin.x + (oldrect.size.width - self.imageSize.width) / 2;
newrect.origin.y = oldrect.origin.y + (oldrect.size.height - self.imageSize.height) / 2;
newrect.size.width = self.imageSize.width;
newrect.size.height = self.imageSize.height;
return newrect;
}
return [super imageRectForContentRect:contentRect];
}
- (void)setNormalImage:(UIImage *)normalImage {
_normalImage = normalImage;
[self setImage:normalImage forState:UIControlStateNormal];
}
@end
@implementation TUIBlockButton
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addTarget:self action:@selector(buttonTap:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)buttonTap:(id)button {
if (self.clickCallBack) {
self.clickCallBack(self);
}
}
- (void)dealloc {
}
@end

View File

@@ -0,0 +1,54 @@
//
// 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

View File

@@ -0,0 +1,283 @@
//
// TUIFloatTitleView.m
// TUI
//
// Created by wyl on 2023/1/16.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIFloatViewController.h"
#import <TIMCommon/TIMDefine.h>
typedef enum : NSUInteger {
FLEX_TOP,
FLEX_Bottom,
} FLEX_Location;
@interface TUIFloatTitleView ()
@end
@implementation TUIFloatTitleView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupView];
}
return self;
}
- (void)setupView {
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"";
self.titleLabel.font = [UIFont boldSystemFontOfSize:kScale390(20)];
[self addSubview:self.titleLabel];
self.subTitleLabel = [[UILabel alloc] init];
self.subTitleLabel.text = @"";
self.subTitleLabel.font = [UIFont systemFontOfSize:kScale390(12)];
self.subTitleLabel.tintColor = [UIColor grayColor];
[self addSubview:self.subTitleLabel];
self.leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self addSubview:self.leftButton];
[self.leftButton setTitle:TIMCommonLocalizableString(TUIKitCreateCancel) forState:UIControlStateNormal];
self.leftButton.titleLabel.font = [UIFont systemFontOfSize:kScale390(16)];
[self.leftButton addTarget:self action:@selector(leftButtonClick) forControlEvents:UIControlEventTouchUpInside];
[self.leftButton setTitleColor:[UIColor tui_colorWithHex:@"#0365F9"] forState:UIControlStateNormal];
self.rightButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self addSubview:self.rightButton];
[self.rightButton setTitle:TIMCommonLocalizableString(TUIKitCreateNext) forState:UIControlStateNormal];
self.rightButton.titleLabel.font = [UIFont systemFontOfSize:kScale390(16)];
[self.rightButton addTarget:self action:@selector(rightButtonClick) forControlEvents:UIControlEventTouchUpInside];
[self.rightButton setTitleColor:[UIColor tui_colorWithHex:@"#0365F9"] forState:UIControlStateNormal];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self.titleLabel sizeToFit];
[self.subTitleLabel sizeToFit];
if (self.subTitleLabel.isHidden || self.subTitleLabel.text.length == 0) {
self.titleLabel.frame = CGRectMake((self.frame.size.width - self.titleLabel.frame.size.width) * 0.5, kScale390(23.5), self.titleLabel.frame.size.width,
self.titleLabel.frame.size.height);
} else {
self.titleLabel.frame = CGRectMake((self.frame.size.width - self.titleLabel.frame.size.width) * 0.5, kScale390(17.5), self.titleLabel.frame.size.width,
self.titleLabel.frame.size.height);
self.subTitleLabel.frame = CGRectMake((self.frame.size.width - self.subTitleLabel.frame.size.width) * 0.5,
self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height + kScale390(1),
self.subTitleLabel.frame.size.width, self.subTitleLabel.frame.size.height);
}
[self.leftButton sizeToFit];
self.leftButton.frame = CGRectMake(kScale390(15), kScale390(23.5), self.leftButton.frame.size.width, self.leftButton.frame.size.height);
[self.rightButton sizeToFit];
self.rightButton.frame = CGRectMake(self.frame.size.width - self.rightButton.frame.size.width - kScale390(14), kScale390(23.5),
self.rightButton.frame.size.width, self.rightButton.frame.size.height);
if (isRTL()){
[self.leftButton resetFrameToFitRTL];
[self.rightButton resetFrameToFitRTL];
}
}
- (void)leftButtonClick {
if (self.leftButtonClickCallback) {
self.leftButtonClickCallback();
}
}
- (void)rightButtonClick {
if (self.rightButtonClickCallback) {
self.rightButtonClickCallback();
}
}
- (void)setTitleText:(NSString *)mainText subTitleText:(NSString *)secondText leftBtnText:(NSString *)leftBtnText rightBtnText:(NSString *)rightBtnText {
self.titleLabel.text = mainText;
self.subTitleLabel.text = secondText;
[self.leftButton setTitle:leftBtnText forState:UIControlStateNormal];
[self.rightButton setTitle:rightBtnText forState:UIControlStateNormal];
}
@end
@interface TUIFloatViewController ()
@property(nonatomic, assign) CGFloat topMargin;
@property(nonatomic, assign) FLEX_Location currentLoaction;
@property(nonatomic, strong) UIPanGestureRecognizer *panCover;
@property(nonatomic, strong) UITapGestureRecognizer *singleTap;
@end
@implementation TUIFloatViewController
- (void)appendChildViewController:(UIViewController<TUIFloatSubViewControllerProtocol> *)vc topMargin:(CGFloat)topMargin {
self.childVC = vc;
self.topMargin = topMargin;
[self addChildViewController:vc];
[self.containerView addSubview:vc.view];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor tui_colorWithHex:@"#000000" alpha:0.6];
self.modalPresentationStyle = UIModalPresentationCustom;
self.containerView.backgroundColor = [UIColor whiteColor];
self.topImgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:TIMCommonImagePath(@"icon_flex_arrow")]];
[self.topGestureView addSubview:self.topImgView];
self.topImgView.hidden = YES;
@weakify(self);
self.topGestureView.leftButtonClickCallback = ^{
@strongify(self);
if ([self.childVC respondsToSelector:@selector(floatControllerLeftButtonClick)]) {
[self.childVC performSelector:@selector(floatControllerLeftButtonClick)];
}
};
self.topGestureView.rightButtonClickCallback = ^{
@strongify(self);
if ([self.childVC respondsToSelector:@selector(floatControllerRightButtonClick)]) {
[self.childVC performSelector:@selector(floatControllerRightButtonClick)];
}
};
[self addSingleTapGesture];
if (!_currentLoaction) {
self.currentLoaction = FLEX_TOP;
}
[self updateSubContainerView];
}
- (void)addSingleTapGesture {
// When clicking on the shadow, the page disappears
self.view.userInteractionEnabled = YES;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
singleTap.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:singleTap];
}
- (void)singleTap:(UITapGestureRecognizer *)tap {
CGPoint translation = [tap locationInView:self.containerView];
if (translation.x < 0 || translation.y < 0) {
[self dismissViewControllerAnimated:YES completion:nil];
} else if (translation.x > self.containerView.frame.size.width || translation.y > self.containerView.frame.size.height) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
- (void)setnormalTop {
self.currentLoaction = FLEX_TOP;
}
- (void)setNormalBottom {
self.currentLoaction = FLEX_Bottom;
}
- (void)setCurrentLoaction:(FLEX_Location)currentLoaction {
_currentLoaction = currentLoaction;
if (currentLoaction == FLEX_TOP) {
self.containerView.frame = CGRectMake(0, self.topMargin, self.view.frame.size.width, self.view.frame.size.height - self.topMargin);
} else if (currentLoaction == FLEX_Bottom) {
self.containerView.frame = CGRectMake(0, self.view.frame.size.height - kScale390(393), self.view.frame.size.width, kScale390(393));
}
}
- (void)floatDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
[self dismissViewControllerAnimated:flag completion:completion];
}
#pragma mark - lazy
- (UIView *)containerView {
if (_containerView == nil) {
_containerView = [[UIView alloc] init];
_containerView.layer.cornerRadius = kScale390(12);
[self.view addSubview:_containerView];
}
return _containerView;
}
- (UIView *)topGestureView {
if (_topGestureView == nil) {
_topGestureView = [[TUIFloatTitleView alloc] init];
// [_topGestureView addGestureRecognizer:self.panCover];
[self.containerView addSubview:_topGestureView];
}
return _topGestureView;
}
- (UIPanGestureRecognizer *)panCover {
if (_panCover == nil) {
_panCover = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPanCover:)];
}
return _panCover;
}
- (void)onPanCover:(UIPanGestureRecognizer *)pan {
CGPoint translation = [pan translationInView:self.topGestureView];
CGFloat absX = fabs(translation.x);
CGFloat absY = fabs(translation.y);
if (MAX(absX, absY) < 2) return;
if (absX > absY) {
if (translation.x < 0) {
// scroll left
} else {
// scroll right
}
} else if (absY > absX) {
if (translation.y < 0) {
// scroll up
[self.topGestureView removeGestureRecognizer:self.panCover];
[UIView animateWithDuration:0.3
animations:^{
self.currentLoaction = FLEX_TOP;
[self.topGestureView addGestureRecognizer:self.panCover];
}
completion:^(BOOL finished) {
if (finished) {
[self updateSubContainerView];
}
}];
} else {
// scroll down
if (self.currentLoaction == FLEX_Bottom) {
[self dismissViewControllerAnimated:YES completion:nil];
}
[self.topGestureView removeGestureRecognizer:self.panCover];
[UIView animateWithDuration:0.3
animations:^{
self.currentLoaction = FLEX_Bottom;
[self.topGestureView addGestureRecognizer:self.panCover];
}
completion:^(BOOL finished) {
if (finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self updateSubContainerView];
});
}
}];
}
}
}
- (void)updateSubContainerView {
self.topGestureView.frame = CGRectMake(0, 0, self.containerView.frame.size.width, kScale390(68.5));
self.topImgView.frame = CGRectMake((self.topGestureView.frame.size.width - kScale390(24)) * 0.5, kScale390(22), kScale390(24), kScale390(6));
self.childVC.view.frame = CGRectMake(0, self.topGestureView.frame.origin.y + self.topGestureView.frame.size.height, self.containerView.frame.size.width,
self.containerView.frame.size.height - self.topGestureView.frame.size.height);
}
@end

View File

@@ -0,0 +1,21 @@
//
// TUIGroupAvatar+Helper.h
// TIMCommon
//
// Created by wyl on 2023/4/27.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TUICore/TUIDefine.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUIGroupAvatar (Helper)
+ (UIImage *)getNormalGroupCacheAvatar:(NSString *)groupID groupType:(NSString *)groupType;
+ (void)configAvatarByParam:(NSDictionary *)param targetView:(UIImageView *)targetView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,125 @@
//
// TUIGroupAvatar+Helper.m
// TIMCommon
//
// Created by wyl on 2023/4/27.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TUICore/TUIConfig.h>
#import "TUIGroupAvatar+Helper.h"
@implementation TUIGroupAvatar (Helper)
+ (UIImage *)getNormalGroupCacheAvatar:(NSString *)groupID groupType:(NSString *)groupType {
/**
*
* Setup default avatar
*/
UIImage *avatarImage = nil;
if (groupID.length > 0) {
/**
* If it is a group, change the group default avatar to the last used avatar
*/
UIImage *avatar = nil;
if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", groupID];
NSInteger member = [NSUserDefaults.standardUserDefaults integerForKey:key];
avatar = [TUIGroupAvatar getCacheAvatarForGroup:groupID number:(UInt32)member];
}
avatarImage = avatar ? avatar : DefaultGroupAvatarImageByGroupType(groupType);
;
return avatarImage;
}
return avatarImage;
}
+ (void)configAvatarByParam:(NSDictionary *)param targetView:(UIImageView *)targetView {
NSString *groupID = param[@"groupID"];
NSString *faceUrl = param[@"faceUrl"];
NSString *groupType = param[@"groupType"];
UIImage *originAvatarImage = param[@"originAvatarImage"];
if (groupID.length > 0) {
/**
*
* Group avatar
*/
if (IS_NOT_EMPTY_NSSTRING(faceUrl)) {
/**
*
* The group avatar has been manually set externally
*/
[targetView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:originAvatarImage];
} else {
/**
* The group avatar has not been set externally. If the synthetic avatar is allowed, the synthetic avatar will be used; otherwise, the default
* avatar will be used.
*/
if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
/**
*
* If the synthetic avatar is allowed, the synthetic avatar will be used
* 1. Asynchronously obtain the cached synthetic avatar according to the number of group members
* 2. If the cache is hit, use the cached synthetic avatar directly
* 3. If the cache is not hit, recompose a new avatar
*
* Note:
* 1. Since "asynchronously obtaining cached avatars" and "synthesizing avatars" take a long time, it is easy to cause cell reuse problems, so
* it is necessary to confirm whether to assign values directly according to groupID.
* 2. Use SDWebImage to implement placeholder, because SDWebImage has already dealt with the problem of cell reuse
*/
// 1. Obtain group avatar from cache
// fix: The getCacheGroupAvatar needs to request the
// network. When the network is disconnected, since the headImageView is not set, the current conversation sends a message, the conversation is
// moved up, and the avatar of the first conversation is reused, resulting in confusion of the avatar.
[targetView sd_setImageWithURL:nil placeholderImage:originAvatarImage];
[TUIGroupAvatar getCacheGroupAvatar:groupID
callback:^(UIImage *avatar, NSString *groupID) {
if ([groupID isEqualToString:groupID]) {
// 1.1 When the callback is invoked, the cell is not reused
if (avatar != nil) {
// 2. Hit the cache and assign directly
[targetView sd_setImageWithURL:nil placeholderImage:avatar];
} else {
// 3. Synthesize new avatars asynchronously without hitting cache
[targetView sd_setImageWithURL:nil placeholderImage:originAvatarImage];
[TUIGroupAvatar
fetchGroupAvatars:groupID
placeholder:originAvatarImage
callback:^(BOOL success, UIImage *image, NSString *groupID) {
if ([groupID isEqualToString:groupID]) {
// When the callback is invoked, the cell is not reused
[targetView
sd_setImageWithURL:nil
placeholderImage:success ? image : DefaultGroupAvatarImageByGroupType(groupType)];
} else {
// callback When the callback is invoked, the cell has
// been reused to other groupIDs. Since a new callback will be triggered when the new
// groupID synthesizes new avatar, it is ignored here
}
}];
}
} else {
// 1.2 callback cell groupID groupID
// callback 1.2 When the callback is invoked, the cell has been reused to other groupIDs. Since a new
// callback will be triggered when the new groupID gets the cache, it is ignored here
}
}];
} else {
/**
* Synthetic avatars are not allowed, use the default avatar directly
*/
[targetView sd_setImageWithURL:nil placeholderImage:originAvatarImage];
}
}
} else {
/**
* Personal avatar
*/
[targetView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:originAvatarImage];
}
}
@end

View File

@@ -0,0 +1,22 @@
//
// TUISecondConfirm.h
// TIMCommon
//
// Created by xiangzhang on 2023/5/15.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^TUISecondConfirmBtnClickCallback)(void);
@interface TUISecondConfirmBtnInfo : NSObject
@property(nonatomic, strong) NSString *tile;
@property(nonatomic, copy) TUISecondConfirmBtnClickCallback click;
@end
@interface TUISecondConfirm : NSObject
+ (void)show:(NSString *)title cancelBtnInfo:(TUISecondConfirmBtnInfo *)cancelBtnInfo confirmBtnInfo:(TUISecondConfirmBtnInfo *)confirmBtnInfo;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,105 @@
//
// TUISecondConfirm.m
// TIMCommon
//
// Created by xiangzhang on 2023/5/15.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUISecondConfirm.h"
#import <TUICore/TUIDefine.h>
#import <TUICore/UIView+TUILayout.h>
@implementation TUISecondConfirmBtnInfo
@end
static UIWindow *gSecondWindow = nil;
static TUISecondConfirmBtnInfo *gCancelBtnInfo = nil;
static TUISecondConfirmBtnInfo *gConfirmBtnInfo = nil;
@implementation TUISecondConfirm
+ (void)show:(NSString *)title cancelBtnInfo:(TUISecondConfirmBtnInfo *)cancelBtnInfo confirmBtnInfo:(TUISecondConfirmBtnInfo *)confirmBtnInfo {
gCancelBtnInfo = cancelBtnInfo;
gConfirmBtnInfo = confirmBtnInfo;
gSecondWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
gSecondWindow.windowLevel = UIWindowLevelAlert - 1;
gSecondWindow.backgroundColor = [UIColor clearColor];
gSecondWindow.hidden = NO;
if (@available(iOS 13.0, *)) {
for (UIWindowScene *windowScene in [UIApplication sharedApplication].connectedScenes) {
if (windowScene.activationState == UISceneActivationStateForegroundActive) {
gSecondWindow.windowScene = windowScene;
break;
}
}
}
UIView *backgroupView = [[UIView alloc] initWithFrame:gSecondWindow.bounds];
backgroupView.backgroundColor = RGBA(0, 0, 0, 0.4);
[gSecondWindow addSubview:backgroupView];
UIView *confimView = [[UIView alloc] init];
confimView.backgroundColor = TIMCommonDynamicColor(@"second_confirm_bg_color", @"#FFFFFF");
confimView.layer.cornerRadius = 13;
confimView.layer.masksToBounds = YES;
[gSecondWindow addSubview:confimView];
confimView.mm_width(gSecondWindow.mm_w - kScale375(32) * 2).mm_height(183).mm__centerX(gSecondWindow.mm_centerX).mm__centerY(gSecondWindow.mm_h / 2);
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, confimView.mm_w, 123)];
titleLabel.text = title;
titleLabel.textColor = TIMCommonDynamicColor(@"second_confirm_title_color", @"#000000");
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.font = [UIFont systemFontOfSize:16];
titleLabel.numberOfLines = 0;
[confimView addSubview:titleLabel];
UIView *line1 = [[UIView alloc] initWithFrame:CGRectMake(0, titleLabel.mm_maxY, titleLabel.mm_w, 0.5)];
line1.backgroundColor = TIMCommonDynamicColor(@"second_confirm_line_color", @"#DDDDDD");
[confimView addSubview:line1];
UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[cancelBtn setFrame:CGRectMake(0, line1.mm_maxY, line1.mm_w / 2, confimView.mm_h - line1.mm_maxY)];
[cancelBtn setTitle:gCancelBtnInfo.tile forState:UIControlStateNormal];
[cancelBtn.titleLabel setFont:[UIFont systemFontOfSize:16]];
[cancelBtn setTitleColor:TIMCommonDynamicColor(@"second_confirm_cancel_btn_title_color", @"#000000") forState:UIControlStateNormal];
[cancelBtn addTarget:self action:@selector(onCancelBtnClick) forControlEvents:UIControlEventTouchUpInside];
[confimView addSubview:cancelBtn];
UIView *line2 = [[UIView alloc] initWithFrame:CGRectMake(cancelBtn.mm_maxX, cancelBtn.mm_y, 0.5, cancelBtn.mm_h)];
line2.backgroundColor = TIMCommonDynamicColor(@"second_confirm_line_color", @"#DDDDDD");
[confimView addSubview:line2];
UIButton *confirmBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[confirmBtn setFrame:CGRectMake(line2.mm_maxX, cancelBtn.mm_y, cancelBtn.mm_w, cancelBtn.mm_h)];
[confirmBtn setTitle:gConfirmBtnInfo.tile forState:UIControlStateNormal];
[confirmBtn.titleLabel setFont:[UIFont systemFontOfSize:16]];
[confirmBtn setTitleColor:TIMCommonDynamicColor(@"second_confirm_confirm_btn_title_color", @"#FF584C") forState:UIControlStateNormal];
[confirmBtn addTarget:self action:@selector(onConfirmBtnBtnClick) forControlEvents:UIControlEventTouchUpInside];
[confimView addSubview:confirmBtn];
}
+ (void)onCancelBtnClick {
if (gCancelBtnInfo && gCancelBtnInfo.click) {
gCancelBtnInfo.click();
}
[self dismiss];
}
+ (void)onConfirmBtnBtnClick {
if (gConfirmBtnInfo && gConfirmBtnInfo.click) {
gConfirmBtnInfo.click();
}
[self dismiss];
}
+ (void)dismiss {
gSecondWindow = nil;
gCancelBtnInfo = nil;
gConfirmBtnInfo = nil;
}
@end

View File

@@ -0,0 +1,25 @@
//
// TUITextView.h
// Masonry
//
// Created by xiangzhang on 2022/10/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol TUITextViewDelegate <NSObject>
- (void)onLongPressTextViewMessage:(UITextView *)textView;
@end
@interface TUITextView : UITextView
@property (nonatomic, strong) UILongPressGestureRecognizer *longPressGesture;
@property (nonatomic, weak) id<TUITextViewDelegate> tuiTextViewDelegate;
- (void)disableHighlightLink;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,65 @@
//
// TUITextView.m
// Masonry
//
// Created by xiangzhang on 2022/10/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUITextView.h"
#import <TUICore/TUIThemeManager.h>
@implementation TUITextView
- (instancetype)init {
self = [super init];
if (self) {
self.dataDetectorTypes = UIDataDetectorTypeLink | UIDataDetectorTypePhoneNumber;
[self setupLongPressGesture];
self.tintColor = TIMCommonDynamicColor(@"chat_highlight_link_color", @"#6495ED");
}
return self;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
return NO;
}
- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder API_AVAILABLE(ios(13.0)) {
if (@available(iOS 16.0, *)) {
[builder removeMenuForIdentifier:UIMenuLookup];
}
[super buildMenuWithBuilder:builder];
}
- (void)disableHighlightLink {
self.dataDetectorTypes = UIDataDetectorTypeNone;
}
- (void)setupLongPressGesture {
self.longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
[self addGestureRecognizer:self.longPressGesture];
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
if ([gesture isKindOfClass:[UILongPressGestureRecognizer class]] && gesture.state == UIGestureRecognizerStateBegan) {
if (self.tuiTextViewDelegate && [self.tuiTextViewDelegate respondsToSelector:@selector(onLongPressTextViewMessage:)]) {
[self.tuiTextViewDelegate onLongPressTextViewMessage:self];
}
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] &&
gestureRecognizer != self.longPressGesture) {
return NO;
}
return YES;
}
@end

View File

@@ -0,0 +1,25 @@
//
// TUIUserAuthorizationCenter.h
// TUIChat
//
// Created by wyl on 2022/2/16.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_OPTIONS(NSUInteger, TUIChatAuthControlType) {
TUIChatAuthControlTypeMicro = 1 << 0,
TUIChatAuthControlTypeCamera = 1 << 1,
TUIChatAuthControlTypePhoto = 1 << 2,
};
@interface TUIUserAuthorizationCenter : NSObject
@property(nonatomic, assign, class, readonly) BOOL isEnableCameraAuthorization;
@property(nonatomic, assign, class, readonly) BOOL isEnableMicroAuthorization;
+ (void)cameraStateActionWithPopCompletion:(void (^)(void))completion API_AVAILABLE(ios(8.0));
+ (void)microStateActionWithPopCompletion:(void (^)(void))completion API_AVAILABLE(ios(8.0));
+ (void)openSettingPage;
+ (void)showAlert:(TUIChatAuthControlType)type;
@end

View File

@@ -0,0 +1,136 @@
//
// TUIUserAuthorizationCenter.m
// TUIChat
//
// Created by wyl on 2022/2/16.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIUserAuthorizationCenter.h"
#import <AVFoundation/AVCaptureDevice.h>
#import <CoreLocation/CLLocationManager.h>
#import <CoreMotion/CoreMotion.h>
#import <EventKit/EventKit.h>
#import <Photos/Photos.h>
#import <Speech/Speech.h>
#import <TIMCommon/TIMDefine.h>
#import <UserNotifications/UserNotifications.h>
#import <TUICore/TUIGlobalization.h>
@implementation TUIUserAuthorizationCenter
+ (BOOL)isEnableCameraAuthorization {
if (@available(iOS 7.0, *)) {
return [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusAuthorized;
} else {
return YES;
}
}
+ (void)cameraStateActionWithPopCompletion:(void (^)(void))completion {
if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusNotDetermined) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
completionHandler:^(BOOL granted) {
if (granted && completion) {
completion();
}
}];
} else {
[self showAlert:TUIChatAuthControlTypeCamera];
}
}
+ (void)openSettingPage {
if (@available(iOS 8.0, *)) {
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
if (@available(iOS 10.0, *)) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
} else {
[[UIApplication sharedApplication] openURL:url];
}
}
} else {
// Fallback on earlier versions
}
}
+ (BOOL)isEnableMicroAuthorization {
if (@available(iOS 7.0, *)) {
return [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusAuthorized;
} else {
return YES;
}
}
+ (void)microStateActionWithPopCompletion:(void (^)(void))completion {
#if !TARGET_OS_MACCATALYST
if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusNotDetermined) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
if (granted && completion) {
completion();
}
}];
} else {
[self showAlert:TUIChatAuthControlTypeMicro];
}
#endif
}
+ (BOOL)isEnableLocationAuthorization {
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (@available(iOS 8.0, *)) {
return status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse;
} else {
// Fallback on earlier versions
return YES;
}
}
+ (void)showAlert:(TUIChatAuthControlType)type {
NSString *title = @"";
NSString *message = @"";
NSString *laterMessage = @"";
NSString *openSettingMessage = @"";
if (TUIChatAuthControlTypeMicro == type) {
title = TIMCommonLocalizableString(TUIKitInputNoMicTitle);
message = TIMCommonLocalizableString(TUIKitInputNoMicTips);
laterMessage = TIMCommonLocalizableString(TUIKitInputNoMicOperateLater);
openSettingMessage = TIMCommonLocalizableString(TUIKitInputNoMicOperateEnable);
} else if (TUIChatAuthControlTypeCamera == type) {
title = TIMCommonLocalizableString(TUIKitInputNoCameraTitle);
message = TIMCommonLocalizableString(TUIKitInputNoCameraTips);
laterMessage = TIMCommonLocalizableString(TUIKitInputNoCameraOperateLater);
openSettingMessage = TIMCommonLocalizableString(TUIKitInputNoCameraOperateEnable);
} else if (TUIChatAuthControlTypePhoto == type) {
title = TIMCommonLocalizableString(TUIKitInputNoPhotoTitle);
message = TIMCommonLocalizableString(TUIKitInputNoPhotoTips);
laterMessage = TIMCommonLocalizableString(TUIKitInputNoPhotoOperateLater);
openSettingMessage = TIMCommonLocalizableString(TUIKitInputNoPhotoerateEnable);
} else {
return;
}
if (@available(iOS 8.0, *)) {
UIAlertController *ac = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:laterMessage style:UIAlertActionStyleCancel handler:nil]];
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:openSettingMessage
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
UIApplication *app = [UIApplication sharedApplication];
NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([app canOpenURL:settingsURL]) {
[app openURL:settingsURL];
}
}]];
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:ac animated:YES completion:nil];
// [self presentViewController:ac animated:YES completion:nil];
});
} else {
// Fallback on earlier versions
}
}
@end

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,801 @@
/*
Localizable.strings
Chinese Simplized
Created by harvy on 2020/10/9.
*/
/***************************** 日期格式化 Start *************************/
"YesterdayDateFormat" = "aHH:mm";
/***************************** 日期格式化 End *************************/
"TUIKitTipsMostSelectTextFormat" = "يمكن اختيار حد أقصى %ld";
"Confirm" = "تأكيد";
"Cancel" = "إلغاء";
"Send" = "إرسال";
"Save" = "حفظ";
"You" = "أنت";
"you" = "أنت";
"Male" = "ذكر";
"Female" = "أنثى";
"File" = "ملف";
"Download" = "تحميل";
"Unsetted" = "غير محدد";
"Show" = "عرض";
"Monday" = "الاثنين";
"Tuesday" = "الثلاثاء";
"Wednesday" = "الأربعاء";
"Thursday" = "الخميس";
"Friday" = "الجمعة";
"Saturday" = "السبت";
"Sunday" = "الأحد";
"Yesterday" = "أمس";
"am" = "صباحًا";
"pm" = "مساءً";
"Read" = "تمت القراءة";
"Unread" = "غير مقروء";
"Copy" = "نسخ";
"Delete" = "حذف";
"Revoke" = "إلغاء";
"Retry" = "إعادة المحاولة";
"Re_send" = "إعادة الإرسال";
"Make_a_call" = "إجراء مكالمة";
"Done" = "تم";
"All" = "الكل";
"Agreed" = "تم الموافقة";
"Disclined" = "تم الرفض";
"Agree" = "موافقة";
"Have_been_sent" = "تم الإرسال";
"OK" = "موافق";
"None" = "لا شيء";
"Send" = "إرسال";
"Cancel" = "إلغاء";
"Alia" = "ملاحظة";
"Group" = "مجموعة";
"Accept" = "قبول";
"Decline" = "رفض";
"send_success" = "تم الإرسال بنجاح";
"add_success" = "تمت الإضافة بنجاح";
"delete_success" = "تم الحذف بنجاح";
"my_friend" = "أصدقائي";
"no_personal_signature" = "لا توجد توقيع شخصي";
"SignatureFormat" = "التوقيع الشخصي: %@";
"choose_avatar_for_you" = "تم تعيين صورة رمزية لك تلقائيًا من التطبيق";
"SearchGroupPlaceholder" = "معرف المستخدم";
"please_fill_in_verification_information" = "يرجى ملء معلومات التحقق";
"please_fill_in_remarks_group_info" = "يرجى ملء معلومات الملاحظات والمجموعة";
"no_set" = "غير محدد";
"no_personal_signature" = "لا توجد توقيع شخصي";
"logout" = "تسجيل الخروج";
"login" = "تسجيل الدخول";
"ReadAll" = "قراءة الكل";
"Hide" = "إخفاء";
"Delete" = "حذف";
"ContactsJoinGroup" = "إضافة محادثة جماعية";
"ContactsAddFriends" = "إضافة صديق";
"ChatsNewChatText" = "بدء محادثة";
"ChatsNewGroupText" = "إنشاء محادثة جماعية";
"ChatsSelectContact" = "اختيار جهة الاتصال";
"GroupJoin" = "إضافة مجموعة";
"GroupRequestJoinGroupFormat" = "%@ يطلب الانضمام إلى المحادثة الجماعية";
"GroupDeleteFriend" = "حذف جهة الاتصال";
"GroupAddFirend" = "إضافة جهة الاتصال";
"GroupMember" = "أعضاء المجموعة";
"ProfileAlia" = "اسم الملاحظة";
"ProfileMessageDoNotDisturb" = "عدم الإزعاج";
"ProfileStickyonTop" = "تثبيت المحادثة في الأعلى";
"ProfileBlocked" = "إضافة إلى القائمة السوداء";
"ProfileSendMessages" = "إرسال رسالة";
"ProfileDeleteFirend" = "حذف الصديق";
"ProfileEditAlia" = "تعديل الملاحظة";
"ProfileDetails" = "تفاصيل الملف الشخصي";
"ProfileSetBackgroundImage" = "تعيين صورة خلفية للمحادثة الحالية";
"ProfilePhoto" = "الصورة الرمزية";
"ProfileName" = "الاسم";
"ProfileAccount" = "الحساب";
"ProfileSignature" = "التوقيع الشخصي";
"ProfileGender" = "الجنس";
"ProfileEditName" = "تعديل الاسم";
"ProfileEditNameDesc" = "الصينية فقط والحروف والأرقام والشرطات السفلية";
"ProfileEditSignture" = "تعديل التوقيع الشخصي";
"ProfileEditGender" = "تعديل الجنس";
"ProfileEditAlia" = "تعديل الملاحظة";
"ProfileEdit" = "تعديل";
"ProfileAlia" = "اسم الملاحظة";
"ProfileBlocked" = "إضافة إلى القائمة السوداء";
"ProfileMessageDoNotDisturb" = "عدم الإزعاج";
"ProfileStickyonTop" = "تثبيت المحادثة في الأعلى";
"ProfileSendMessages" = "إرسال رسالة";
"ProfileDeleteFirend" = "حذف الصديق";
"ProfileBirthday" = "تاريخ الميلاد";
"SignatureFormat" = "التوقيع الشخصي: %@";
"FriendRequestFormat" = "أنا %@";
"FriendRequestFillInfo" = "إضافة صديق";
"FriendOneWay" = "صديق في اتجاه واحد";
"FriendAddTitle" = "إضافة صديق";
"FriendAddVerificationMessage" = "رسالة التحقق";
"FriendAddResultSuccessWait" = "تم الإرسال بنجاح، في انتظار الموافقة";
"FriendAddResultForbid" = "الطرف الآخر ممنوع إضافته";
"FriendAddResultSuccess" = "تمت الإضافة إلى قائمة الأصدقاء";
"FriendAddResultExists" = "الطرف الآخر هو بالفعل صديق لك";
"MeFriendRequest" = "عندما يرغب شخص ما في إضافتي كصديق";
"MeFriendRequestMethodAgreeAll" = "الموافقة على أي شخص يرغب في إضافتي كصديق";
"MeFriendRequestMethodNeedConfirm" = "يحتاج التحقق";
"MeFriendRequestMethodDenyAll" = "رفض أي شخص يرغب في إضافتي كصديق";
"MeMessageReadStatus" = "حالة قراءة الرسائل";
"MeMessageReadStatusOpenDesc" = "عند الإغلاق، لن تظهر حالة قراءة الرسائل في الرسائل التي تستلمها أو ترسلها، ولن تتمكن من معرفة ما إذا قرأ الطرف الآخر الرسالة أم لا، وبالمثل، الطرف الآخر لن يتمكن من معرفة ما إذا قرأت الرسالة أم لا.";
"MeMessageReadStatusCloseDesc" = "عند الفتح، ستظهر حالة قراءة الرسائل في الرسائل التي تستلمها أو ترسلها في المحادثات الجماعية، ويمكنك رؤية ما إذا قرأ الطرف الآخر الرسالة أم لا. إذا قام أصدقاؤك في المحادثات الفردية بتفعيل حالة قراءة الرسائل، ستظهر حالة قراءة الرسائل في المحادثات الفردية التي تتبادلها معهم.";
"ShowOnlineStatus" = "عرض حالة الاتصال";
"ShowOnlineStatusOpenDesc" = "عند الإغلاق، لن ترى حالة الاتصال (متصل/غير متصل) لأصدقائك في قائمة المحادثات وقائمة الأصدقاء.";
"ShowOnlineStatusCloseDesc" = "عند الفتح، سترى حالة الاتصال (متصل/غير متصل) لأصدقائك في قائمة المحادثات وقائمة الأصدقاء.";
"ShowPackageToast" = "هذه الميزة متاحة في الإصدار الفاخر";
"ShowCallsRecord" = "عرض سجل المكالمات";
"TUIKitClassic" = "التصميم الكلاسيكي";
"TUIKitMinimalist" = "التصميم البسيط";
"TUIKitThemeNameSystemFollowTitle" = "متابعة نظام الجهاز";
"TUIKitThemeNameSystemFollowSubTitle" = "عند الفتح، سيتم عرض التصميم الافتراضي (الخفيف/الداكن) فقط، ولا يمكن تغيير تصميم التطبيق إلى أي تصميم آخر.";
"TUIKitThemeNameSerious" = "جدي";
"TUIKitThemeNameLight" = "خفيف";
"TUIKitThemeNameLivey" = "حيوي";
"TUIKitThemeNameDark" = "داكن";
"TUIKitThemeNameSystem" = "متابعة نظام الجهاز";
"TUIKitDone" = "تم";
"TUIKitWelcome" = "مرحبًا بك في عائلة Tencent Cloud IM!";
"TUIKitMicCamerAuthTips" = "يرجى تمكين إذن الوصول إلى الميكروفون والكاميرا";
"TUIKitMicAuth" = "يرجى تمكين إذن الوصول إلى الميكروفون";
"TUIKitTipsConfirmResendMessage" = "هل ترغب في إعادة إرسال هذه الرسالة؟";
"TUIKitTipsSystemError" = "خطأ في النظام";
"TUIKitTipsEnterRoomErrorFormat" = "فشل الانضمام إلى الغرفة: %d";
"TUIKitWhoRequestForJoinGroupFormat" = "%@ يطلب الانضمام إلى المجموعة";
"TUIKitInviteJoinGroupFormat" = "دعوة %@ للانضمام إلى المجموعة";
"TUIKitAgreedByAdministor" = "تمت الموافقة من قبل المسؤول";
"TUIkitDiscliedByAdministor" = "تم الرفض من قبل المسؤول";
"TUIKitDownloadProgressFormat" = "جارٍ تنزيل %d٪";
"TUIKitOpenWithOtherApp" = "فتح باستخدام تطبيق آخر";
"TUIKitTipsContactListNil" = "قائمة جهات الاتصال فارغة، يرجى إضافة أصدقاء أولاً";
"TUIKitUserStatusUnknown" = "غير معروف";
"TUIKitUserStatusOnline" = "متصل";
"TUIKitUserStatusOffline" = "غير متصل";
"TUIKitUserStatusUnlogined" = "غير مسجل الدخول";
"TUIKitInputHoldToTalk" = "استمر في الضغط للتحدث";
"TUIKitInputReleaseToSend" = "رفع الإصبع لإرسال";
"TUIKitInputReleaseToCancel" = "رفع الإصبع للإلغاء";
"TUIKitInputBlankMessageTitle" = "لا يمكن إرسال رسالة فارغة";
"TUIKitInputWillFinishRecordInSeconds" = "سيتم إنهاء التسجيل في %ld ثانية";
"TUIKitInputRecordSlideToCancel" = "اسحب الإصبع للأعلى للإلغاء";
"TUIKitInputRecordReleaseToCancel" = "رفع الإصبع للإلغاء";
"TUIKitInputRecordTimeshort" = "مدة التسجيل قصيرة جدًا";
"TUIKitInputRecordTimeLong" = "مدة التسجيل طويلة جدًا";
"TUIKitGroupProfileDetails" = "تفاصيل الملف الشخصي";
"TUIKitGroupProfileMember" = "أعضاء المجموعة";
"TUIKitGroupProfileMemberCount" = "%d أشخاص";
"TUIKitGroupProfileMemberCountlu" = "%lu شخص";
"TUIKitGroupProfileType" = "نوع المجموعة";
"TUIKitGroupProfileJoinType" = "طريقة الانضمام النشطة";
"TUIKitGroupProfileInviteType" = "طريقة الدعوة للانضمام";
"TUIKitGroupProfileInviteJoin" = "دعوة للانضمام";
"TUIKitGroupProfileAutoApproval" = "الموافقة التلقائية";
"TUIKitGroupProfileAlias" = "اسم المجموعة الخاص بي";
"TUIKitGroupProfileMessageDoNotDisturb" = "عدم الإزعاج للرسائل";
"TUIKitGroupProfileStickyOnTop" = "تثبيت في الأعلى";
"TUIKitGroupProfileDeleteAndExit" = "حذف والخروج";
"TUIKitGroupProfileDissolve" = "حل المجموعة";
"TUIKitGroupProfileReport" = "تقرير";
"TUIKitGroupProfileJoinDisable" = "تعطيل الانضمام";
"TUIKitGroupProfileInviteDisable" = "تعطيل الدعوة";
"TUIKitGroupProfileAdminApprove" = "موافقة المشرف";
"TUIKitGroupProfileEditAlias" = "تعديل اسم المجموعة الخاص بي";
"TUIKitGroupProfileEditAliasDesc" = "مسموح فقط بالأحرف الصينية والأحرف اللاتينية والأرقام والشرطة السفلية";
"TUIKitGroupProfileEditGroupName" = "تعديل اسم المجموعة";
"TUIKitGroupProfileEditAnnouncement" = "تعديل إعلان المجموعة";
"TUIKitGroupProfileEditAvatar" = "تعديل صورة المجموعة";
"TUIKitGroupProfileDeleteGroupTips" = "بمجرد الخروج، لن تتلقى رسائل المجموعة";
"TUIKitGroupProfileGroupCountFormat" = "أعضاء المجموعة (%ld أشخاص)";
"TUIKitGroupProfileManage" = "الإدارة";
"TUIKitGroupProfileManageAdd" = "إضافة عضو";
"TUIKitGroupProfileManageDelete" = "حذف عضو";
"TUIKitGroupProfileAdmainAdd" = "تعيين كمشرف";
"TUIKitGroupProfileAdmainDelete" = "إلغاء تعيين كمشرف";
"TUIKitGroupTransferOwner" = "نقل ملكية المجموعة";
"TUIKitGroupTransferOwnerSuccess" = "تم نقل ملكية المجموعة بنجاح";
"TUIKitGroupApplicant" = "طلبات الانضمام للمجموعة";
"TUIKitGroupDismssTipsFormat" = "تم حل مجموعة %@";
"TUIKitGroupRecycledTipsFormat" = "تم إعادة تدوير مجموعة %@";
"TUIKitGroupKickOffTipsFormat" = "تم طردك من مجموعة %@";
"TUIKitGroupDropoutTipsFormat" = "لقد قمت بالخروج من مجموعة %@";
"TUIKitGroupMessagePinRemove" = "يزيل";
"TUIKitMessageTipsNormalRecallMessage" = "تم سحب رسالة";
"TUIKitMessageTipsYouRecallMessage" = "لقد قمت بسحب رسالة";
"TUIKitMessageTipsReEditMessage" = "إعادة التحرير";
"TUIKitMessageTipsRecallMessageFormat" = "\"%@\" قام بسحب رسالة";
"TUIKitMessageTipsOthersRecallMessage" = "قام الطرف الآخر بسحب رسالة";
"TUIKitMessageTipsJoinGroupFormat" = "\"%@\" انضم إلى المجموعة";
"TUIKitMessageTipsInviteJoinGroupFormat" = "\"%@\" يدعو \"%@\" للانضمام إلى المجموعة";
"TUIKitMessageTipsLeaveGroupFormat" = "\"%@\" غادر المجموعة";
"TUIKitMessageTipsKickoffGroupFormat" = "\"%@\" يطرد \"%@\" من المجموعة";
"TUIKitMessageTipsSettAdminFormat" = "\"%@\" تم تعيينه مديرًا للمجموعة";
"TUIKitMessageTipsCancelAdminFormat" = "\"%@\" تم إزالة صلاحياته كمدير للمجموعة";
"TUIkitMessageTipsEditGroupNameFormat" = "%@قام بتغيير اسم المجموعة إلى %@、";
"TUIKitMessageTipsEditGroupIntroFormat" = "\"%@\" قام بتغيير وصف المجموعة إلى \"%@\"、";
"TUIKitMessageTipsEditGroupAnnounceFormat" = "\"%@\" قام بتغيير إعلان المجموعة إلى \"%@\"、";
"TUIKitMessageTipsDeleteGroupAnnounceFormat" = "\"%@\" قام بحذف إعلان المجموعة、";
"TUIKitMessageTipsEditGroupAvatarFormat" = "\"%@\" قام بتغيير صورة المجموعة、";
"TUIKitMessageTipsEditGroupOwnerFormat" = "\"%@\" قام بنقل ملكية المجموعة إلى \"%@\"、";
"TUIKitMessageTipsCreateGroupFormat" = "\"%@\" قام بإنشاء مجموعة";
"TUIKitMessageTipsUnsupportCustomMessage" = "[رسالة مخصصة غير مدعومة]";
"TUIKitMessageTipsRecallRiskContent" = "تم سحب رسالة المخالفة";
"TUIKitMessageDisplayRiskContent" = "[رسالة المخالفة]";
"TUIKitMessageTipsMute" = "تم حظر الصوت";
"TUIKitMessageTipsUnmute" = "تم إلغاء حظر الصوت";
"TUIKitMessageTipsGroupPinMessage" = "\"%@\"ثبتت رسالة";
"TUIKitMessageTipsGroupUnPinMessage" = "\"%@\"تم إلغاء تثبيت رسالة";
"TUIKitSignalingFinishGroupChat" = "إنهاء المحادثة الجماعية";
"TUIKitSignalingFinishConversationAndTimeFormat" = "مدة المكالمة";
"TUIKitSignalingNewCall" = "بدء مكالمة";
"TUIKitSignalingNewGroupCallFormat" = "%@ يبدأ مكالمة جماعية";
"TUIkitSignalingCancelCall" = "إلغاء المكالمة";
"TUIkitSignalingCancelGroupCallFormat" = "%@ يلغي المكالمة الجماعية";
"TUIkitSignalingHangonCall" = "تم الرد";
"TUIKitSignalingHangonCallFormat" = "%@ تم الرد عليه";
"TUIKitSignalingBusyFormat" = "%@ مشغول";
"TUIKitSignalingDeclineFormat" = "%@ يرفض المكالمة";
"TUIKitSignalingCallBusy" = "الطرف الآخر مشغول";
"TUIkitSignalingDecline" = "رفض المكالمة";
"TUIKitSignalingNoResponse" = "لا يوجد رد";
"TUIkitSignalingUnrecognlize" = "أمر المكالمة غير معروف";
"TUIkitMessageTypeImage" = "[صورة]";
"TUIKitMessageTypeVoice" = "[تسجيل صوتي]";
"TUIkitMessageTypeVideo" = "[فيديو]";
"TUIkitMessageTypeFile" = "[ملف]";
"TUIKitMessageTypeAnimateEmoji" = "[رمز تعبيري متحرك]";
"TUIKitMessageTypeDraftFormat" = "[مسودة] ";
"TUIkitMessageTypeRiskImage" = "[صور غير قانونية]";
"TUIkitMessageTypeRiskVoice" = "[صوت غير قانوني]";
"TUIkitMessageTypeRiskVideo" = "[فيديو مخالف]";
"TUIKitMessageTypeLastMsgCountFormat" = "رسالة";
"TUIKitMessageTypeSecurityStrike" = "تم تضمين محتوى حساس، فشل الإرسال";
"TUIKitMessageTypeSecurityStrikeVoice" = "المحتوى الحساس المتضمن، لا يمكن الاستماع إليه";
"TUIKitMessageTypeSecurityStrikeImage" = "محتوى حساس متضمن ولا يمكن مشاهدته";
"TUIKitMessageTypeSecurityStrikeTranslate" = "يتضمن محتوى حساسًا، فشلت الترجمة";
"TUIKitMessageTypeOtherUseMic" = "الميكروفون يتم استخدامه بواسطة وظيفة أخرى، غير قادر على التسجيل.";
"TUIKitMessageReadPartRead" = "شخص قرأ";
"TUIKitMessageReadPartUnread" = "غير مقروء";
"TUIKitMessageReadPartDisable" = "بدون حالة";
"TUIKitMessageReadAllRead" = "تمت القراءة بالكامل";
"TUIKitMessageReadC2CRead" = "تمت القراءة";
"TUIKitMessageReadC2CUnRead" = "لم يتم القراءة";
"TUIKitMessageReadC2CUnReadDetail" = "لم يتم القراءة بعد";
"TUIKitMessageReadDetail" = "تفاصيل الرسالة";
"TUIKitMessageReadNoMoreData" = "لا توجد مزيد من البيانات";
"TUIKitMessageReadGetReadMembersFail" = "فشل في الحصول على قائمة المستخدمين الذين قرأوا الرسالة";
"TUIKitMessageReadGetUnreadMembersFail" = "فشل في الحصول على قائمة المستخدمين الذين لم يقروا الرسالة";
"TUIKitMoreCamera" = "التقاط صورة";
"TUIKitMorePhoto" = "صورة";
"TUIKitMoreVideo" = "تسجيل فيديو";
"TUIKitMoreVideoCaptureDurationTip" = "مدة التسجيل قصيرة جدًا";
"TUIKitMoreFile" = "ملف";
"TUIKitMoreVideoCall" = "مكالمة فيديو";
"TUIKitMoreVoiceCall" = "مكالمة صوتية";
"TUIKitMoreLink" = "مخصص";
"TUIKitMoreLinkDetails" = "عرض التفاصيل >>";
"TUIKitMorePoll" = "استطلاع";
"TUIKitMoreGroupNote" = "ملاحظة المجموعة";
"TUIKitFileSizeCheckLimited" = "تم تجاوز الحد الأقصى للحجم";
"TUIKitImageSizeCheckLimited" = "حجم الصورة يتجاوز الحد";
"TUIKitCallInviteYouVideoCall" = "يدعوك لمكالمة فيديو";
"TUIKitCallTurningOnMute" = "تشغيل الصوت";
"TUIKitCallTurningOffMute" = "إيقاف الصوت";
"TUIKitCallUsingSpeaker" = "استخدام مكبر الصوت";
"TUIKitCallUsingHeadphone" = "استخدام سماعة الأذن";
"TUIKitCallCancelCallingFormat" = "%@ قام بإلغاء المكالمة";
"TUIKitAtSelectMemberTitle" = "اختيار أعضاء المجموعة";
"TUIKitConversationTipsAtMe" = "[شخص ما @ لي]";
"TUIKitConversationTipsAtAll" = "[@ الجميع]";
"TUIKitConversationTipsAtMeAndAll" = "[شخص ما @ لي][@ الجميع]";
"TUIKitConversationTipsDelete" = "سيتم حذف هذه المحادثة وجميع سجلات الدردشة المرتبطة بها";
"TUIKitPublicGroup" = "مجموعة عامة";
"TUIKitWorkGroup" = "مجموعة عمل";
"TUIKitChatRoom" = "غرفة دردشة";
"TUIKitCommunity" = "مجتمع";
"TUIKitContactsNewFriends" = "جهات اتصال جديدة";
"TUIKitContactsGroupChats" = "الدردشات الجماعية";
"TUIKitContactsBlackList" = "القائمة السوداء";
"TUIKitAddFriendSourceFormat" = "المصدر: %@";
"TUIKitFriendApplicationApproved" = "تمت الموافقة على طلب الصداقة";
"TUIKitFirendRequestRejected" = "تم رفض طلب الصداقة";
"TUIKitOfflinePushCallTips" = "لديك طلب مكالمة";
"TUIKitChatTap2Remove" = "انقر للحذف";
"TUIKitChatPendencyTitle" = "انقر للمعالجة";
"TUIKitChatPendencyRequestToJoinGroupFormat" = "%@ طلبات الانضمام إلى المجموعة";
"TUIKitChatTipsAtMe" = "شخص ما @ لي";
"TUIKitChatBackToLatestLocation" = "العودة إلى الموقع الأحدث";
"TUIKitChatNewMessages" = "%@ رسالة جديدة";
"TUIKitAllowTypeAcceptOne" = "السماح لأي مستخدم بإضافة صديق";
"TUIKitAllowTypeNeedConfirm" = "يتطلب التحقق";
"TUIKitAllowTypeDeclineAll" = "رفض جميع طلبات الصداقة";
/***************************** 消息转发 & 消息搜索 *************************/
// 补充
"Multiple" = "متعدد";
"Forward" = "إعادة توجيه";
"Delete" = "حذف";
"Copy" = "نسخ";
"Revoke" = "إلغاء";
"Resend" = "إعادة إرسال";
"Search" = "بحث";
"Recall" = "إلغاء";
"MultiSelect" = "تحديد متعدد";
"Quote" = "اقتباس";
"TUIKitCallMicCamAuthTips" = "يرجى تمكين إذن الوصول إلى الميكروفون والكاميرا";
"TUIKitCallMicAuthTips" = "يرجى تمكين إذن الوصول إلى الميكروفون";
"TUIKitMessageTipsSureToResend" = "هل أنت متأكد من إعادة إرسال الرسالة؟";
// 消息转发
"TUIKitRelayNoMessageTips" = "يرجى تحديد الرسالة";
"TUIKitRelayRecentMessages" = "الرسائل الأخيرة";
"TUIKitRelayChatHistory" = "سجل الدردشة";
"TUIKitRelaySepcialForbid" = "لا يمكن إعادة توجيه رسائل الصوت وغيرها من الرسائل الخاصة المحددة.";
"TUIKitRelayConfirmForward" = "هل أنت متأكد من إعادة التوجيه؟";
"TUIKitRelayOneByOneForward" = "إعادة التوجيه بشكل فردي";
"TUIKitRelayCombineForwad" = "دمج إعادة التوجيه";
"TUIKitRelayGroupChatHistory" = "سجل الدردشة الجماعية";
"TUIKitRelayChatHistoryForSomebodyFormat" = "سجل الدردشة لـ %@ و %@";
"TUIKitRelayErrorForwardFormat" = "@فشل إعادة التوجيه، code:%d, desc:%@";
"TUIKitRelayTargetCreateNewChat" = "إنشاء دردشة جديدة";
"TUIKitRelayTargetCreateNewGroup" = "إنشاء مجموعة جديدة";
"TUIKitRelayTargetSelectFromContacts" = "اختيار من جهات الاتصال";
"TUIKitRelayTargetCrateGroupError" = "فشل إنشاء المجموعة";
"TUIKitRelayTargetNoneTips" = "يرجى تحديد جهة اتصال أو محادثة";
"TUIKitRelayLayerLimitTips" = "عذرًا، تجاوزت الحد المسموح به للتداخل الطبقي الحالي";
"TUIKitRelayCompatibleText" = "لا يدعم إعادة توجيه الرسائل المدمجة، يرجى الترقية إلى أحدث إصدار.";
"TUIKitRelayUnsupportForward" = "لا يدعم إعادة توجيه الرسائل التي فشلت في الإرسال";
"TUIKitRelayOneByOnyOverLimit" = "تم تجاوز الحد الأقصى لعدد رسائل إعادة التوجيه، لا يدعم حاليًا إعادة التوجيه بشكل فردي";
"TUIKitRelayPluginNotAllowed" = "لا يدعم إعادة توجيه رسائل التصويت/التسلسل الزمني";
// 消息搜索
"TUIKitSearchItemHeaderTitleContact" = "جهات الاتصال";
"TUIKitSearchItemFooterTitleContact" = "عرض المزيد من جهات الاتصال";
"TUIKitSearchItemHeaderTitleGroup" = "الدردشات الجماعية";
"TUIKitSearchItemFooterTitleGroup" = "عرض المزيد من الدردشات الجماعية";
"TUIkitSearchItemHeaderTitleChatHistory" = "سجل الدردشة";
"TUIKitSearchItemFooterTitleChatHistory" = "عرض المزيد من سجل الدردشة";
"TUIKitSearchResultMatchFormat" = "يحتوي على: %@";
"TUIKitSearchResultMatchGroupIDFormat" = "يحتوي على معرف المجموعة: %@";
"TUIKitSearchResultMatchGroupMember" = "يحتوي على أعضاء المجموعة:";
"TUIKitSearchResultDisplayChatHistoryCountFormat" = "%zd سجل دردشة";
/***************************** 消息转发 & 消息搜索 *************************/
/***************************** 消息回复 & 换肤 *************************/
"Reply" = "الرد";
"TUIKitReplyMessageNotFoundOriginMessage" = "تعذر تحديد الرسالة الأصلية";
"TUIKitClearAllChatHistory" = "مسح سجل الدردشة بالكامل";
"TUIKitClearAllChatHistoryTips" = "هل أنت متأكد من مسح سجل الدردشة بالكامل؟";
"Discline" = "رفض";
"Copied" = "تم النسخ";
"ConfirmDeleteMessage" = "هل أنت متأكد من حذف الرسائل المحددة؟";
"TUIKitAddFriend" = "إضافة صديق";
"TUIKitAddGroup" = "إضافة مجموعة";
"TUIKitSearchUserID" = "البحث عن معرف المستخدم";
"TUIKitSearchGroupID" = "البحث عن معرف المجموعة";
"TUIKitNoSelfSignature" = "لا يوجد توقيع شخصي حاليًا";
"TUIKitSelfSignatureFormat" = "توقيع شخصي: %@";//fix me
"TUIKitGroupProfileManage" = "إدارة المجموعة";
"TUIKitGroupManageShutupAllTips" = "عند تمكين الحظر الكلي للمستخدمين، سيتم السماح فقط للمالك والمشرفين بالتحدث.";
"TUIKitGroupManageAdminSetting" = "تعيين المشرفين";
"TUIKitGroupManageShutAll" = "حظر الكل";
"TUIKitGroupAddShutupMember" = "إضافة أعضاء المجموعة الذين يجب حظرهم";
"TUIKitGroupShutupOption" = "صامت";
"TUIKitGroupDisShutupOption" = "ضباب غائم";
"TUIKitGroupOwner" = "مالك المجموعة";
"TUIKitGroupManagerFormat" = "المشرفون (%zd/%zd)";
"TUIKitSetShutupAllFormat" = "%@ تم تمكين الحظر الكلي للمستخدمين،";
"TUIKitCancelShutupAllFormat" = "%@ تم إلغاء الحظر الكلي للمستخدمين،";
"TUIKitGroupNotice" = "إعلان المجموعة";
"TUIKitGroupNoticeNull" = "لا يوجد إعلان حاليًا";
"Edit" = "تحرير";
"Done" = "تم";
"TUIKitAddContactMyUserIDFormat" = "معرف المستخدم الخاص بي: %@";
"TUIKitAddUserNoDataTips" = "هذا المستخدم غير موجود";
"TUIKitAddGroupNoDataTips" = "هذه الدردشة الجماعية غير موجودة";
/***************************** 消息回复 & 换肤 *************************/
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitVideoTranscoding" = "جاري تحويل الفيديو...";
"TUIKitVideoDownloading" = "جاري تحميل الفيديو...";
"TUIKitVideoSavedSuccess" = "تم حفظ الفيديو بنجاح";
"TUIKitVideoSavedFailed" = "فشل حفظ الفيديو";
"TUIKitPictureSavedSuccess" = "تم حفظ الصورة بنجاح";
"TUIKitPictureSavedFailed" = "فشل حفظ الصورة";
"TUIKitPictureCheckRisk" = "لقد تم اكتشاف أن الصورة تحتوي على محتوى حساس ولا يمكن عرضها مؤقتًا.";
"TUIKitVideoCheckRisk" = "تم اكتشاف أن الفيديو يحتوي على محتوى حساس ولا يمكن مشاهدته مؤقتًا.";
"TUIKitVideoCheckRiskCancel" = "أرى";
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitGroupSetAdminsForbidden" = "لا يدعم الدردشة الجماعية الحالية إعداد المشرفين";
"TUIKitGroupAddAdmins" = "إضافة مشرفين";
"TUIKitGroupAddMemberOfBlockedForbidden" = "لا يدعم الدردشة الجماعية الحالية حظر أعضاء محددين";
"TUIKitNotDownload"= "لم يتم التنزيل بعد";
"TUIKitInputNoCameraTitle" = "لم يتم تمكين إذن الكاميرا";
"TUIKitInputNoCameraTips" = "لا يمكن استخدام ميزات التقاط الصور والفيديو والمكالمات الفيديو، انقر على (الذهاب للتمكين) لفتح إذن الكاميرا";
"TUIKitInputNoCameraOperateLater" = "لاحقًا";
"TUIKitInputNoCameraOperateEnable" = "الذهاب للتمكين";
"TUIKitInputNoMicTitle" = "لم يتم تمكين إذن الميكروفون";
"TUIKitInputNoMicTips" = "لا يمكن استخدام ميزات إرسال رسائل الصوت والتقاط الفيديو والمكالمات الصوتية والفيديو، انقر على (الذهاب للتمكين) لفتح إذن الميكروفون";
"TUIKitInputNoMicOperateLater" = "لاحقًا";
"TUIKitInputNoMicOperateEnable" = "الذهاب للتمكين";
"TUIKitInputNoPhotoTitle" = "لم يتم تمكين إذن الصور";
"TUIKitInputNoPhotoTips" = "لا يمكن استخدام ميزات عرض الصور واختيارها في ميزات الدردشة، انقر على (الذهاب للتمكين) لفتح إذن الصور";
"TUIKitInputNoPhotoOperateLater" = "لاحقًا";
"TUIKitInputNoPhotoerateEnable" = "الذهاب للتمكين";
"TUIKitInputRecordTipsTitle" = "قم بتحرير الإصدار لإرساله، اسحب لليسار للإلغاء";
"TUIKitInputRecordCancelTipsTitle" = "تم إلغاء الإصدار";
"TUIKitInputRecordRejectedInCall" = "جاري المكالمة الصوتية أو الفيديو، يرجى المحاولة لاحقًا";
"TUIKitInputRecordRejectedIsRecording" = "جاري التسجيل الصوتي، يرجى الانتظار حتى ينتهي";
"StickyonTop" = "تثبيت في الأعلى";
"CancelStickonTop" = "إلغاء التثبيت في الأعلى";
"ClearHistoryChatMessage" = "مسح الرسائل";
"MarkAsRead" = "وضع علامة كمقروءة";
"MarkAsUnRead" = "وضع علامة كغير مقروءة";
"MarkHide" = "إخفاء";
"MinimizeGroup" = "طي الدردشة";
"More" = "المزيد";
"Info" = "معلومات";
"TUIKitConversationMarkHide" = "إخفاء الدردشة";
"TUIKitConversationMarkHideDescriptionOn" = "عند التمكين، لن تظهر هذه الدردشة في قائمة الدردشات، وسيتم الاحتفاظ بسجل الدردشة.";
"TUIKitConversationMarkHideDescriptionOff" = "عند التعطيل، ستظهر هذه الدردشة في قائمة الدردشات، وسيتم الاحتفاظ بسجل الدردشة.";
"TUIKitConversationMarkFoldGroups" = "مجموعات الدردشة المطوية";
"TUIKitConversationMarkFold" = "طي هذه المجموعة";
"TUIKitRepliesDetailTitle" = "تفاصيل الرد";
"TUIKitRepliesNum" = "ردود";
"TUIKitReference" = "المرجع";
"TUIKitNotSupportThisMessage" = "[لا يتم دعم عرض هذه الرسالة مؤقتًا]";
"TUIKitReferenceOriginMessageRevoke" = "تم إبطال المحتوى المقتبس";
"TUIKitRepliesOriginMessageRevoke" = "تم إبطال محتوى الرد";
"TUIKitAudioPlaybackStyleLoudspeaker" = "مكبر الصوت";
"TUIKitAudioPlaybackStyleHandset" = "سماعة الهاتف";
"TUIKitAudioPlaybackStyleChange2Loudspeaker" = "التبديل إلى تشغيل الصوت عبر مكبر الصوت";
"TUIKitAudioPlaybackStyleChange2Handset" = "التبديل إلى تشغيل الصوت عبر سماعة الهاتف";
"TUIKitGroupMessagePin" = "دبوس الى أعلى الصفحة";
"TUIKitGroupMessageUnPin" = "إزالة التثبيت";
"TUIKitGroupMessagePinOverLimit" = "الرسائل المثبتة فوق الحد";
"TUIKitGroupMessagePinRepeatedly" = "لقد تم تثبيت هذه الرسالة";
"TUIKitGroupMessageUnPinRepeatedly" = "تم إلغاء تثبيت هذه الرسالة";
"TUIKitTranslate" = "ترجمة";
"TUIKitTranslateDefaultTips" = "تقدم خدمة الترجمة بواسطة Tencent Cloud IM";
"TUIKitTranslateFailed" = "فشل الترجمة";
"Hide" = "إخفاء";
"TranslateMessage" = "ترجمة الرسالة إلى";
"TranslateLanguageNotSupport" = "اللغة المستهدفة غير مدعومة، يرجى تغيير اللغة";
"TUIKitConvertToText" = "تحويل النص";
"TUIKitConvertToTextFailed" = "فشل تحويل";
"TUIKitSignalingSwitchToAudio" = "تحويل الفيديو إلى صوت";
"TUIKitSignalingUnsupportedCalling" = "دعوة غير معروفة";
"TUIKitSignalingComfirmSwitchToAudio" = "تأكيد التحويل إلى صوت";
"TUIKitErrorEnableUserStatusOnConsole" = "لم تقم بتمكين ميزة حالة المستخدم، يرجى تسجيل الدخول إلى لوحة التحكم وتمكينها";
"TUIKitChooseAvatar" = "اختيار الصورة الرمزية";
"TUIKitChooseCover" = "اختيار الغلاف";
"TUIKitChooseBackground" = "تغيير الخلفية";
"TUIKitDefaultBackground" = "الخلفية الافتراضية";
"TUIKitIdentity" = "المعرف";
"TUIKitChooseBackgroundSuccess" = "تم التعيين بنجاح!";
"TUIKitChooseBackgroundFailed" = "فشل التعيين!";
"TUIKitContactNoNewApplicationRequest" = "لا توجد طلبات صداقة جديدة حاليًا";
"TUIKitContactNoGroupChats" = "لا توجد محادثات جماعية حاليًا";
"TUIKitContactNoBlockList" = "لا توجد أشخاص في قائمة الحظر حاليًا";
"TUIKitSearchNoResultLists" = "لا توجد نتائج متعلقة بالمحادثات أو جهات الاتصال أو سجل الرسائل";
"TUIKitTyping" = "الشخص الآخر يكتب حاليًا";
"TUIKitMessageTipsEditGroupAddOptFormat" = "\"%@\" قام بتعديل طريقة الانضمام إلى المجموعة إلى \"%@\"、";
"TUIKitMessageTipsEditGroupInviteOptFormat" = "\"%@\" قام بتعديل طريقة الدعوة إلى المجموعة إلى \"%@\"、";
"TUIKitCreatGroupNamed" = "اسم المجموعة";
"TUIKitCreatGroupID" = "معرف المجموعة (اختياري)";
"TUIKitCreatGroupNamed_Placeholder" = "أدخل اسم المجموعة";
"TUIKitCreatGroupID_Placeholder" = "أدخل معرف المجموعة";
"TUIKitCreatGroupAvatar" = "صورة المجموعة";
"TUIKitCreatGroupType" = "نوع المجموعة";
"TUIKitCreatGroupType_Work" = "مجموعة عمل للأصدقاء (Work)";
"TUIKitCreatGroupType_Public" = "مجموعة تواصل مع غرباء (Public)";
"TUIKitCreatGroupType_Meeting" = "مجموعة اجتماع مؤقتة (Meeting)";
"TUIKitCreatGroupType_Community" = "مجموعة مجتمع (Community)";
"TUIKitCreatGroupType_Work_Desc" = "مجموعة عمل للأصدقاء (Work): تشبه مجموعات الدردشة العادية في تطبيق WeChat، بعد الإنشاء، يمكن للأصدقاء الموجودين في المجموعة دعوة أصدقاء آخرين للانضمام دون الحاجة لموافقة المدعو أو موافقة صاحب المجموعة.";
"TUIKitCreatGroupType_Public_Desc" = "مجموعة تواصل مع غرباء (Public): تشبه مجموعات QQ، بعد الإنشاء، يمكن لصاحب المجموعة تعيين مشرفين للمجموعة، عندما يقوم المستخدم بالبحث عن معرف المجموعة ويقدم طلب الانضمام، يحتاج إلى موافقة صاحب المجموعة أو المشرفين قبل الانضمام إلى المجموعة.";
"TUIKitCreatGroupType_Meeting_Desc" = "مجموعة اجتماع مؤقتة (Meeting): بعد الإنشاء، يمكن للمستخدمين الانضمام والخروج بحرية، ويدعم عرض الرسائل قبل الانضمام؛ مناسبة لسيناريوهات المؤتمرات الصوتية والمرئية وسيناريوهات التعليم عبر الإنترنت وغيرها من السيناريوهات التي تجمع بين منتجات الصوت والفيديو الفورية.";
"TUIKitCreatGroupType_Community_Desc" = "مجموعة مجتمع (Community): بعد الإنشاء، يمكن للمستخدمين الانضمام والخروج بحرية، وتدعم ما يصل إلى 100000 شخص، وتدعم تخزين الرسائل التاريخية، عندما يقوم المستخدم بالبحث عن معرف المجموعة ويقدم طلب الانضمام، لا يحتاج إلى موافقة المشرفين للانضمام إلى المجموعة.";
"TUIKitCreatGroupType_See_Doc" = "انظر الوثائق المنتج";
"TUIKitCreatGroupType_Desc_Highlight" = "وثائق المنتج";
"TUIKitCreatGroupType_See_Doc_Simple" = "عرض وثائق المنتج";
"TUIKitSearchItemCancel" = "إلغاء";
//----mini---
"Pin" = "تثبيت";
"UnPin" = "إلغاء التثبيت";
"TUIKitSelected" = "تم تحديدها";
"TUIKitThreadQuote" = "ردود";
"MessageInfo" = "تفاصيل الرسالة";
"C2CReadBy" = "تمت القراءة";
"C2CDeliveredTo" = "لم يتم القراءة";
"GroupReadBy" = "قائمة المقروءة";
"GroupDeliveredTo" = "قائمة الغير مقروءة";
"TUIKitMessage" = "إرسال رسالة";
"TUIKitAudio" = "مكالمة صوتية";
"TUIKitVideo" = "مكالمة فيديو";
"TUIKitAddMembers" = "+ إضافة أعضاء";
"TUIKitMembersRoleMember" = "عضو";
"TUIKitMembersRoleAdmin" = "مشرف";
"TUIKitMembersRoleSuper" = "مالك المجموعة";
"TUIKitCreateCancel" = "إلغاء";
"TUIKitCreateNext" = "التالي";
"TUIKitCreateFinish" = "إنشاء";
"TUIKitCreateMemebers" = "الأعضاء المحددين";
"TUIKitDateToday" = "اليوم";
//----mini---
"TUIPollCreateNew" = "إنشاء استطلاع";
"TUIPollCreateSucceed" = "تم الإنشاء بنجاح";
"TUIPollCreateFail" = "فشل الإنشاء";
"TUIPollEnterTitle" = "أدخل عنوان الاستطلاع";
"TUIPollEnterOption" = "أدخل الخيار";
"TUIPollAddOption" = "إضافة خيار";
"TUIPollEnableMultiSelect" = "السماح بالتحديد المتعدد";
"TUIPollSingleSelect" = "اختيار واحد";
"TUIPollMultiSelect" = "اختيار متعدد";
"TUIPollAnonymousVote" = "التصويت المجهول";
"TUIPollPublic" = "نتائج عامة";
"TUIPollNonPublic" = "نتائج غير عامة";
"TUIPollNonAnonymous" = "غير مجهول";
"TUIPollAnonymous" = "مجهول";
"TUIPollEnableNotification" = "إرسال إشعار عند تقديم جديد في المجموعة";
"TUIPolllPublish" = "نشر";
"TUIPollOneCount" = "صوت";
"TUIPollMoreCount" = "أصوات";
"TUIPollInvitedParticipants" = "عدد المشاركين المطلوبين: %d";
"TUIPollRespondedParticipants" = "عدد المشاركين الفعليين: %d";
"TUIPollResend" = "إعادة الإرسال";
"TUIPollResendFail" = "فشل إعادة الإرسال، رمز الخطأ: %d";
"TUIPollEnd" = "إنهاء الاستطلاع";
"TUIPollEndFail" = "فشل إنهاء الاستطلاع، رمز الخطأ: %d";
"TUIPollVoteNow" = "التصويت الآن";
"TUIPollConfirmVote" = "تأكيد";
"TUIPollCancelVote" = "إلغاء";
"TUIPollPercent" = "تم التصويت بـ %d صوت، يشكل %.0f";//fix me
"TUIPollEnded" = "انتهت الاستطلاع";
"TUIPollVoteSucceed" = "تم التصويت بنجاح";
"TUIPollVoteFail" = "فشل التصويت";
"TUIPollPublicResult" = "نتائج عامة";
"TUIPollViewAllOptions" = "عرض جميع الخيارات";
"TUIPollDisplayString" = "[استطلاع]";
"TUIPollTextFieldExceedLimit" = "تجاوز الحد الأقصى لعدد الأحرف (50)";
"TUIPollVoted" = "تم التصويت";
"TUIPoll" = "استطلاع";
"TUIGroupNote" = "التوالي";
"TUIGroupNoteTitle" = "التوالي الجماعي";
"TUIGroupNoteSettings" = "الإعدادات";
"TUIGroupNoteDeadline" = "الموعد النهائي %@";
"TUIGroupNoteTitleHint" = "إضافة عنوان التوالي";
"TUIGroupNoteDescriptionHint" = "إضافة وصف التوالي";
"TUIGroupNoteFormatExample" = "مثال";
"TUIGroupNoteFormatHint" = "يمكنك ملء تنسيق التوالي";
"TUIGroupNoteFillIn" = "يرجى ملء";
"TUIGroupNoteMultipleSettings" = "السماح بتقديم عدة نسخ لكل شخص";
"TUIGroupNoteDeadlineSettings" = "تعيين الموعد النهائي";
"TUIGroupNoteDeadlineNotSet" = "غير محدد";
"TUIGroupNoteNotifySettings" = "إرسال إشعار في المجموعة عند تقديم جديد";
"TUIGroupNoteSend" = "إرسال";
"TUIGroupNoteNow" = "التوالي الفوري";
"TUIGroupNoteMessageTitle" = "التوالي الجماعي";
"TUIGroupNoteMessageTitleInfo" = "تم إطلاقها بواسطة %@، وقد انضم %d شخصًا";
"TUIGroupNoteTipsMessagePrefix" = "انضم %@ إلى التوالي الجماعي";
"TUIGroupNoteHasStopped" = "تم إيقاف التوالي";
"TUIGroupNoteDeadlineMustBeLater" = "يجب أن يكون الموعد النهائي لاحقًا من الوقت الحالي";
"TUIGroupNoteDisplayString" = "[التوالي الجماعي]";
"TUIGroupNoteSendSucceed" = "تم الإرسال بنجاح";
"TUIGroupNoteSendFail" = "فشل الإرسال";
"TUIGroupNoteSubmitExceedLimit" = "تجاوز الحد الأقصى لعدد التواليات";
"TUIGroupNoteTextFieldExceedLimit" = "تجاوز الحد الأقصى لعدد الأحرف 50";
"TUIGroupNoteSubmitOnce" = "يُسمح بتقديم مرة واحدة فقط";
"TUIGroupNoteSubmitNotAllowed" = "غير مسموح بالتقديم";
"TUIGroupNoteExceedDeadline" = "تجاوز الموعد النهائي، تم إيقاف التوالي";
"TUIGroupNoteNotChanged" = "لم يتغير معلومات التوالي";
"TUIGroupNoteExpand" = "...عرض المزيد...";
"TUIGroupNoteNotDatePickerConfirm" = "تأكيد";
"TUIGroupNoteNotDatePickerCancel" = "إلغاء";
"TUIGroupNoteNotDatePickerDone" = "تم";
"TUIGroupNoteNotDatePickerYear" = " سنة";
"TUIGroupNoteNotDatePickerMonth" = " شهر";
"TUIGroupNoteNotDatePickerDay" = " يوم";
"TUIGroupNoteNotDatePickerHour" = " ساعة";
"TUIGroupNoteNotDatePickerMinute" = " دقيقة";
"TUIGroupNoteNotDatePickerSecond" = " ثانية";
"TUIGroupNoteNotDatePickerAM" = "صباحًا";
"TUIGroupNoteNotDatePickerPM" = "مساءً";
"TUIGroupNoteNotDatePickerNow" = " حتى الآن";
"TUIGroupNoteNotDatePickerToday" = " اليوم";
"TUIGroupNoteNotDatePickerMon" = " الاثنين";
"TUIGroupNoteNotDatePickerTue" = " الثلاثاء";
"TUIGroupNoteNotDatePickerWed" = " الأربعاء";
"TUIGroupNoteNotDatePickerTHR" = " الخميس";
"TUIGroupNoteNotDatePickerFRI" = " الجمعة";
"TUIGroupNoteNotDatePickerSAT" = " السبت";
"TUIGroupNoteNotDatePickerSUN" = " الأحد";
"TUICustomerServiceAccounts" = "رقم خدمة العملاء";
"TUICustomerServiceBranchMessage" = "[أخبار الفرع]";
"TUICustomerServiceCardMessage" = "[رسالة البطاقة]";
"TUICustomerServiceCollectInfomation" = "[جمع المعلومات]";
"TUICustomerServiceSatisfactionEvaluation" = "[تقييم الرضا]";
"TUICustomerServiceTimeout" = "[انتهت مهلة جلسة خدمة العملاء وانتهت تلقائيًا.]";
"TUICustomerServiceEnd" = "[انتهت جلسة خدمة العملاء]";
"TUICustomerServiceFillProductInfo" = "يرجى ملء معلومات المنتج";
"TUICustomerServiceSubmitProductInfo" = "إرسال معلومات المنتج";
"TUICustomerServiceClose" = "إنهاء";
"TUICustomerServiceName" = "اسم";
"TUICustomerServiceFillProductName" = "يرجى ملء اسم المنتج";
"TUICustomerServiceDesc" = "يصف";
"TUICustomerServiceFillProductDesc" = "يرجى ملء الوصف ذي الصلة";
"TUICustomerServicePic" = "صورة";
"TUICustomerServiceFillPicLink" = "يرجى ملء رابط الصورة";
"TUICustomerServiceJumpLink" = "القفز";
"TUICustomerServiceFillJumpLink" = "يرجى ملء رابط القفز للبطاقة";
"TUICustomerServiceSubmitEvaluation" = "إرسال المراجعة";
"TUICustomerService" = "البرنامج المساعد لخدمة العملاء";
"TUICustomerServiceSendProduct" = "إرسال البضائع";
"TUICustomerServiceCommonPhrase" = "التعبيرات شائعة الاستخدام";
"TUICustomerServiceCommonPhraseStock" = "هل هو في المخزون؟";
"TUICustomerServiceCommonPhraseCheaper" = "هل يمكن أن يكون أرخص؟";
"TUICustomerServiceCommonPhraseGift" = "أي هدايا مجانية؟";
"TUICustomerServiceCommonPhraseShipping" = "ما هو تاريخ الشحن؟";
"TUICustomerServiceCommonPhraseDelivery" = "أي نوع من التسليم السريع؟";
"TUICustomerServiceCommonPhraseArrive" = "متى ستصل؟";
"TUIChatBotAccounts" = "إنسان آلي";
"TUIChatBotChangeQuestion" = "غيره";
"TUIConversationGroupAll" = "الكل";
"TUIConversationGroupUnread" = "غير مقروءة";
"TUIConversationGroupAtMe" = "@ لي";
"TUIConversationGroupGroup" = "مجموعة";
"TUIConversationGroupC2C" = "دردشة فردية";
"TUIConversationGroupInputGroupName" = "يرجى إدخال اسم المجموعة";
"TUIConversationGroupCreateGroup" = "إنشاء مجموعة المحادثة";
"TUIConversationGroupNewGroup" = "مجموعة جديدة";
"TUIConversationGroupAddGroup" = "إضافة مجموعة";
"TUIConversationGroupMoveGroup" = "نقل المجموعة";
"TUIConversationGroupRemoveGroup" = "إزالة المجموعة";
"TUIConversationGroupShowGroup" = "عرض مجموعة المحادثة";
"TUIConversationGroupHideGroup" = "إخفاء مجموعة المحادثة";
"TUIConversationGroupEditGroup" = "إعدادات مجموعة المحادثة";
"TUIConversationGroupEdit" = "تعديل";
"TUIConversationGroupSave" = "حفظ";
"TUIConversationGroupSaveSucc" = "تم حفظ المجموعة بنجاح";
"TUIConversationGroupCreate" = "إنشاء";
"TUIConversationGroupNameIsNull" = "اسم المجموعة لا يمكن أن يكون فارغًا!";
"TUIConversationGroupNameIsInvalid" = "اسم المجموعة لا يمكن أن يكون اسمًا محددًا مسبقًا";
"TUIConversationGroupConversationIsNull" = "المحادثة لا يمكن أن تكون فارغة!";
"TUIConversationGroupCreateSucc" = "تم إنشاء المجموعة بنجاح";
"TUIConversationGroupModifySucc" = "تم تعديل المجموعة بنجاح";
"TUIConversationGroupName" = "اسم المجموعة";
"TUIConversationGroupIncludeConversation" = "تضمين المحادثة";
"TUIConversationGroupAddConversation" = "إضافة محادثة";
"TUIConversationGroup" = "مجموعة المحادثة";
"TUIConversationGroupManager" = "المدير";
"TUIConversationGroupSelectGroup" = "اختر مجموعة المحادثة";
"TUIConversationGroupUnlogined" = "غير مسجل الدخول، فشل الحفظ!";
"TUIConversationMark" = "وضع علامة";
"TUIConversationMarkCancel" = "إلغاء العلامة";
"TUIConversationNone" = "لا يوجد أي محادثات لـ %@";
//----timApp---
"TIMAppMainTitle" = "تينسنت كلاود · IM";
"TIMAppMainConnectingTitle" = "جاري الاتصال...";
"TIMAppMainDisconnectTitle" = "تينسنت كلاود · IM (غير متصل)";
"TIMAppTabBarItemMessageText" = "الرسائل";
"TIMAppTabBarItemContactText" = "جهات الاتصال";
"TIMAppTabBarItemMeText" = "الإعدادات";
"TIMAppTabBarItemMessageText_mini" = "الدردشة";
"TIMAppTabBarItemContactText_mini" = "جهات الاتصال";
"TIMAppTabBarItemSettingText_mini" = "الإعدادات";
"TIMAppTabBarItemCallsRecordText_mini" = "سجل المكالمات";
"TIMAppChat" = "الدردشة";
"TIMAppChatDisconnectTitle" = "الدردشة (غير متصل)";
"TIMAppChatSecurityWarning" = "لا تصدق بسهولة في التحويلات المصرفية والفوز بالجوائز وغيرها من المعلومات. كن حذرا عند التعامل مع مكالمات هاتفية غير معروفة لتجنب الوقوع في الخداع. يرجى الإبلاغ عن أي حالات مشبوهة في الوقت المناسب.";
"TIMAppChatSecurityWarningReport" = "النقر هنا للإبلاغ";
"TIMAppChatSecurityWarningGot" = "يستلم";
"TIMAppTencentCloudIM" = "الاتصال الفوري";
"TIMAppWelcomeToChat" = "مرحبًا بك في استخدام هذا التطبيق لتجربة خدمة الاتصال الفوري من تينسنت كلاود IM";
"TIMAppWelcomeToChatDetails" = "مرحبًا بك في استخدام هذا التطبيق لتجربة خدمة الاتصال الفوري من تينسنت كلاود IM. يمكنك استخدامه لإرسال الرسائل النصية والرموز التعبيرية والرسائل الغنية بالوسائط والرد على الرسائل والردود بالرموز التعبيرية وحالة قراءة الرسائل وعلاقات الأصدقاء وغيرها من الوظائف. في الوقت نفسه، قمنا بتوفير 3 أصدقاء آليين لك لا يمكنهم الرد، يمكنك بدء محادثة فردية أو محادثة جماعية معهم وتجربة إدارة المجموعات وغيرها من الوظائف.";
"TIMAppSelectStyle" = "اختيار النمط";
"TIMAppChatStyles" = "تبديل نمط IM الحصري";
"TIMAppChangeTheme" = "اختيار السمة";
"TIMAppChatThemes" = "تبديل سمة IM الحصرية";
"TIMAppEnterChat" = "دخول IM";
"TIMAppRunDemo" = "تشغيل العرض التوضيحي في دقيقة واحدة بتغيير سطرين من الشفرة";
"TIMAppCustomers" = "أكثر من 10000 عميل يخدمون شهريًا";
"TIMAppMessagingSuccess" = "معدل نجاح إرسال واستلام الرسائل وموثوقية الخدمة أعلى من 99.99٪";
"TIMAppActiveUsers" = "أكثر من مليار مستخدم نشط شهريًا";
"TIMAppOK" = "موافق";
"TIMApp1Billion+" = "أكثر من مليار";
"TIMApp10000+" = "أكثر من 10000";
"TIMApp1Minute" = "دقيقة واحدة";
"TIMAppMeAbout" = "حول تينسنت كلاود · IM";
"TIMAppConfirmLogout" = "هل أنت متأكد من تسجيل الخروج؟";
"TIMAppConfirm" = "تأكيد";
"TIMAppCancel" = "إلغاء";
"TIMChangeLanguage" = "تغيير اللغة";
//----timApp---
"TUIKitTickledMe" = "أنا";
"TUIKitTickledActionVaule" = "طبقة";
"TUIKitTickledMyself" = "نفسي";
"TUIKitImageViewOrigin" = "عرض الصورة الأصلية";
"TUIGroupCreateTipsMessage" = "إنشاء مجموعة";
"TUICommunityCreateTipsMessage" = "إنشاء مجتمع";
"TUICommunityCreateTipsMessageRuleError" = "معرف المجتمع، يجب أن يبدأ بـ @TGS#_";
"TUIGroupCreateTipsMessageRuleError" = "خارج المجتمع، لا يمكن أن يبدأ بـ @TGS#_";
"zh-Hans" = "الصينية المبسطة";
"en" = "إنجليزي";
"ar" = "عربي";
"zh-Hant" = "الصينية التقليدية";
"TUIChatFaceGroupAllEmojiName" = "جميع التعبيرات";
"TUIChatFaceGroupRecentEmojiName" = "مستخدم حديثا";

View File

@@ -0,0 +1,812 @@
/*
Localizable.strings
English
Created by harvy on 2020/10/9.
*/
/***************************** 日期格式化 Start *************************/
"YesterdayDateFormat" = "HH:mma"; // 英文是 HH:mma 不要动
/***************************** 日期格式化 End ***************************/
"TUIKitTipsMostSelectTextFormat" = "Select %ld at most";
"Confirm" = "OK";
"Cancel" = "Cancel";
"Send" = "Send";
"Save" = "Save";
"You" = "You";
"you" = "you";
"Male" = "Male";
"Female" = "Female";
"File" = "Files";
"Download" = "Download";
"Unsetted" = "Not set";
"Show" = "Show";
"Monday" = "Mon";
"Tuesday" = "Tue";
"Wednesday" = "Wed";
"Thursday" = "Thur";
"Friday" = "Fri";
"Saturday" = "Sat";
"Sunday" = "Sun";
"Yesterday" = "Yesterday";
"am" = "AM";
"pm" = "PM";
"Read" = "Read";
"Unread" = "Unread";
"Copy" = "Copy";
"Delete" = "Delete";
"Revoke" = "Recall";
"Retry" = "Retry";
"Re_send" = "Resend";
"Make_a_call" = "Start Call";
"Done" = "OK";
"All" = "All";
"Agreed" = "Agreed";
"Disclined" = "Declined";
"Agree" = "Agree";
"Have_been_sent" = "Sent";
"OK" = "OK";
"None" = "None";
"Send" = "Send";
"Cancel" = "Cancel";
"Alia" = "Remark";
"Group" = "List";
"Accept" = "Agree";
"Decline" = "Decline";
"send_success" = "Sent";
"add_success" = "Added";
"delete_success" = "Deleted";
"my_friend" = "My Friends";
"no_personal_signature" = "No status";
"SignatureFormat" = "Status:%@";
"choose_avatar_for_you" = "The App randomly assigns you a system avatar.";
"SearchGroupPlaceholder" = "User ID";
"please_fill_in_verification_information" = "Enter verification information";
"please_fill_in_remarks_group_info" = "Enter remark";
"no_set" = "Not set";
"no_personal_signature" = "No status";
"logout" = "Log Out";
"login" = "Log In";
"ReadAll" = "Read All";
"Hide" = "Hide";
"Delete" = "Delete";
"ChatsNewChatText" = "New Chat";
"ChatsNewGroupText" = "Create Group Chat";
"ChatsSelectContact" = "Select Contact";
"ContactsJoinGroup" = "Add Group";
"ContactsAddFriends" = "Add to Contacts";
"GroupJoin" = "Add Group";
"GroupRequestJoinGroupFormat" = "%@ requests to join group chat.";
"GroupDeleteFriend" = "Delete Contact";
"GroupAddFirend" = "Add Contact";
"GroupMember" = "Group Members";
"ProfileAlia" = "Alias";
"ProfileMessageDoNotDisturb" = "Mute Notifications";
"ProfileStickyonTop" = "Pin";
"ProfileBlocked" = "Block";
"ProfileSendMessages" = "Send Message";
"ProfileDeleteFirend" = "Delete Friend";
"ProfileEditAlia" = "Alias";
"ProfileDetails" = "Details";
"ProfileSetBackgroundImage" = "Background";
"ProfilePhoto" = "Profile Photo";
"ProfileName" = "Name";
"ProfileAccount" = "Account";
"ProfileSignature" = "Status";
"ProfileGender" = "Gender";
"ProfileEditName" = "Name";
"ProfileEditNameDesc" = "Only Chinese characters, letters, numbers and underscores are allowed";
"ProfileEditSignture" = "Status";
"ProfileEditGender" = "Gender";
"ProfileEditAlia" = "Alias";
"ProfileEdit" = "Edit";
"ProfileAlia" = "Alias";
"ProfileBlocked" = "Block";
"ProfileMessageDoNotDisturb" = "Mute Notifications";
"ProfileStickyonTop" = "Pin";
"ProfileSendMessages" = "Send Message";
"ProfileDeleteFirend" = "Delete Friend";
"ProfileBirthday" = "Birthday";
"SignatureFormat" = "Status:%@";
"FriendRequestFormat" = "I'm %@";
"FriendRequestFillInfo" = "Add Friend";
"FriendOneWay" = "One-way Friend";
"FriendAddTitle" = "Add Friend";
"FriendAddVerificationMessage" = "Verification Message";
"FriendAddResultSuccessWait" = "Sent successfully. Wait for approval.";
"FriendAddResultForbid" = "The other user disabled friend request.";
"FriendAddResultSuccess" = "Added to friend list";
"FriendAddResultExists" = "You're already friends.";
"MeFriendRequest" = "Friend Request";
"MeFriendRequestMethodAgreeAll" = "Allow any user to add you as friend";
"MeFriendRequestMethodNeedConfirm" = "Anyone upon Request";
"MeFriendRequestMethodDenyAll" = "Decline friend request from any user";
"MeMessageReadStatus" = "Message read status";
"MeMessageReadStatusOpenDesc" = "If disabled, the message read status is hidden for all your messages and for all the messages sent by members in a chat.";
"MeMessageReadStatusCloseDesc" = "If enabled, the message read status is displayed for all your messages and for all the messages sent by members in a chat.";
"ShowOnlineStatus" = "Display online status";
"ShowOnlineStatusOpenDesc" = "If disabled, the users' online status will not displayed in your session and contact list.";
"ShowOnlineStatusCloseDesc" = "If enabled, the users' online status will displayed in your session and contact list.";
"ShowPackageToast" = "This is the feature of ultimate edition";
"ShowCallsRecord" = "Display calls record";
"TUIKitClassic" = "Classic";
"TUIKitMinimalist" = "Minimalist";
"TUIKitThemeNameSystemFollowTitle" = "Automatic";
"TUIKitThemeNameSystemFollowSubTitle" = "After this function is enabled, only the default skin (light/dark) will be displayed. Other skin styles cannot be switched";
"TUIKitThemeNameSerious" = "Solemn";
"TUIKitThemeNameLight" = "Light";
"TUIKitThemeNameLivey" = "Lively";
"TUIKitThemeNameDark" = "Dark";
"TUIKitThemeNameSystem" = "System";
"TUIKitDone" = "Done";
"TUIKitWelcome" = "Welcome to Tencent Cloud Instant Messaging!";
"TUIKitMicCamerAuthTips" = "Enable mic and camera permissions";
"TUIKitMicAuth" = "Enable mic permission";
"TUIKitTipsConfirmResendMessage" = "Resend this message? ";
"TUIKitTipsSystemError" = "System error";
"TUIKitTipsEnterRoomErrorFormat" = "Failed to enter the room: %d";
"TUIKitWhoRequestForJoinGroupFormat" = "%@ requests to join group chat.";
"TUIKitInviteJoinGroupFormat" = "invite %@ to join group chat.";
"TUIKitAgreedByAdministor" = "Request approved by admin";
"TUIkitDiscliedByAdministor" = "Admin declined the request.";
"TUIKitDownloadProgressFormat" = "Downloading %d%%";
"TUIKitOpenWithOtherApp" = "Open with another app";
"TUIKitTipsContactListNil" = "Contact list is empty. Add friends first.";
"TUIKitInputHoldToTalk" = "Hold to Talk";
"TUIKitInputReleaseToSend" = "Release to End";
"TUIKitInputReleaseToCancel" = "Release to Cancel";
"TUIKitInputBlankMessageTitle" = "Unable to send blank message";
"TUIKitInputWillFinishRecordInSeconds" = "Recording will end in %ld seconds.";
"TUIKitInputRecordSlideToCancel" = "Slide up to cancel";
"TUIKitInputRecordReleaseToCancel" = "Release to Cancel";
"TUIKitInputRecordTimeshort" = "Message too short";
"TUIKitInputRecordTimeLong" = "Message too long";
"TUIKitGroupProfileDetails" = "Details";
"TUIKitGroupProfileMember" = "Group Members";
"TUIKitGroupProfileMemberCount" = "%d member(s)";
"TUIKitGroupProfileMemberCountlu" = "%lu member(s)";
"TUIKitGroupProfileType" = "Group Type";
"TUIKitGroupProfileJoinType" = "Group Joining Method";
"TUIKitGroupProfileInviteType" = "Group inviting method";
"TUIKitGroupProfileInviteJoin" = "Invite";
"TUIKitGroupProfileAutoApproval" = "Auto Approval";
"TUIKitGroupProfileAlias" = "My Alias in Group";
"TUIKitGroupProfileMessageDoNotDisturb" = "Mute Notifications";
"TUIKitGroupProfileStickyOnTop" = "Pin";
"TUIKitGroupProfileDeleteAndExit" = "Delete and Leave";
"TUIKitGroupProfileDissolve" = "Disband Group";
"TUIKitGroupProfileReport" = "Report";
"TUIKitGroupProfileJoinDisable" = "Prohibited from Joining";
"TUIKitGroupProfileInviteDisable" = "Prohibited from inviting";
"TUIKitGroupProfileAdminApprove" = "Admin Approval";
"TUIKitGroupProfileEditAlias" = "Edit My Alias in Group";
"TUIKitGroupProfileEditAliasDesc" = "Only letters, digits, underscores, and Chinese characters.";
"TUIKitGroupProfileEditGroupName" = "Edit Group Name";
"TUIKitGroupProfileEditAnnouncement" = "Edit Group Notice";
"TUIKitGroupProfileEditAvatar" = "Change Group Profile Photo";
"TUIKitGroupProfileDeleteGroupTips" = "After leaving, you will no longer receive messages from this group chat.";
"TUIKitGroupProfileGroupCountFormat" = "Group members (%ld in total)";
"TUIKitGroupProfileManage" = "Manage";
"TUIKitGroupProfileManageAdd" = "Add Member";
"TUIKitGroupProfileManageDelete" = "Delete Member";
"TUIKitGroupProfileAdmainAdd" = "Set Administrator";
"TUIKitGroupProfileAdmainDelete" = "Rmove Administrator";
"TUIKitGroupTransferOwner" = "Transfer group";
"TUIKitGroupTransferOwnerSuccess" = "Successful transfer";
"TUIKitGroupApplicant" = "Request to Join Group";
"TUIKitGroupDismssTipsFormat" = "Group %@ disbanded";
"TUIKitGroupRecycledTipsFormat" = "%@ group has been reclaimed.";
"TUIKitGroupKickOffTipsFormat" = "You were removed from the group %@.";
"TUIKitGroupDropoutTipsFormat" = "You have left the group %@.";
"TUIKitGroupMessagePinRemove" = "Remove";
"TUIKitMessageTipsNormalRecallMessage" = "A message is recalled";
"TUIKitMessageTipsYouRecallMessage" = "You recalled a message.";
"TUIKitMessageTipsReEditMessage" = "re-edit";
"TUIKitMessageTipsRecallMessageFormat" = "\"%@\" recalled a message.";
"TUIKitMessageTipsOthersRecallMessage" = "The other user recalled a message.";
"TUIKitMessageTipsJoinGroupFormat" = "\"%@\" joined the group.";
"TUIKitMessageTipsInviteJoinGroupFormat" = "\"%@\" invited \"%@\" to join the group.";
"TUIKitMessageTipsLeaveGroupFormat" = "\"%@\" left the group chat.";
"TUIKitMessageTipsKickoffGroupFormat" = "\"%@\" removed \"%@\" from the group.";
"TUIKitMessageTipsSettAdminFormat" = "\"%@\" is set as admin.";
"TUIKitMessageTipsCancelAdminFormat" = "The admin status of \"%@\" is terminated.";
"TUIkitMessageTipsEditGroupNameFormat" = "%@ changed the group name to\"%@\",";
"TUIKitMessageTipsEditGroupIntroFormat" = "%@ changed the group description to \"%@\".";
"TUIKitMessageTipsEditGroupAnnounceFormat" = "%@ changed the group notice to \"%@\".";
"TUIKitMessageTipsDeleteGroupAnnounceFormat" = "%@ deleted the group notice.";
"TUIKitMessageTipsEditGroupAvatarFormat" = "%@ changed the group profile photo.";
"TUIKitMessageTipsEditGroupOwnerFormat" = "%@ changed the group owner to \"%@\".";
"TUIKitMessageTipsCreateGroupFormat" = "\"%@\" created a group.";
"TUIKitMessageTipsUnsupportCustomMessage" = "[Custom Messages]";
"TUIKitMessageTipsMute" = "is blocked from posting";
"TUIKitMessageTipsUnmute" = "unblocked";
"TUIKitMessageTipsRecallRiskContent" = "The violation message was withdrawn";
"TUIKitMessageDisplayRiskContent" = "[Violation message]";
"TUIKitMessageTipsGroupPinMessage" = "\"%@\"pinned a message";
"TUIKitMessageTipsGroupUnPinMessage" = "\"%@\"unpinned a message";
"TUIKitSignalingFinishGroupChat" = "End Group Chat";
"TUIKitSignalingFinishConversationAndTimeFormat" = "Duration";
"TUIKitSignalingNewCall" = "Start Call";
"TUIKitSignalingNewGroupCallFormat" = "\"%@\" initiated a group call.";
"TUIkitSignalingCancelCall" = "Cancel Call";
"TUIkitSignalingCancelGroupCallFormat" = "\"%@\" canceled the group call.";
"TUIkitSignalingHangonCall" = "Answered";
"TUIKitSignalingHangonCallFormat" = "\"%@\" answered.";
"TUIKitSignalingBusyFormat" = "\"%@\" is busy.";
"TUIKitSignalingDeclineFormat" = "\"%@\" declined the call.";
"TUIKitSignalingCallBusy" = "Line busy";
"TUIkitSignalingDecline" = "Decline Call";
"TUIKitSignalingNoResponse" = "No answer";
"TUIkitSignalingUnrecognlize" = "Unrecognized call instruction";
"TUIKitUserStatusUnknown" = "unknown";
"TUIKitUserStatusOnline" = "online";
"TUIKitUserStatusOffline" = "offline";
"TUIKitUserStatusUnlogined" = "unlogined";
"TUIkitMessageTypeImage" = "[Image]";
"TUIKitMessageTypeVoice" = "[Voice]";
"TUIkitMessageTypeVideo" = "[Video]";
"TUIkitMessageTypeFile" = "[File]";
"TUIKitMessageTypeAnimateEmoji" = "[Animated Sticker]";
"TUIKitMessageTypeDraftFormat" = "[Drafts]";
"TUIkitMessageTypeRiskImage" = "[Illegal pictures]";
"TUIkitMessageTypeRiskVoice" = "[Illegal voice]";
"TUIkitMessageTypeRiskVideo" = "[Illegal video]";
"TUIKitMessageTypeLastMsgCountFormat" = "messages";
"TUIKitMessageTypeSecurityStrike" = "Sensitive content involved, sending failed";
"TUIKitMessageTypeSecurityStrikeVoice" = "Sensitive content involved, cannot be listened to";
"TUIKitMessageTypeSecurityStrikeImage" = "Sensitive content involved and cannot be viewed";
"TUIKitMessageTypeSecurityStrikeTranslate" = "Sensitive content involved, translation failed";
"TUIKitMessageTypeOtherUseMic" = "Microphone is being used by another function, unable to record.";
"TUIKitMessageReadPartRead" = "read";
"TUIKitMessageReadPartUnread" = "unread";
"TUIKitMessageReadPartDisable" = "disable read";
"TUIKitMessageReadAllRead" = "All read";
"TUIKitMessageReadC2CRead" = "Read";
"TUIKitMessageReadC2CUnRead" = "Unread";
"TUIKitMessageReadC2CUnReadDetail" = "Unread";
"TUIKitMessageReadDetail" = "Message Detail";
"TUIKitMessageReadNoMoreData" = "There is no more data";
"TUIKitMessageReadGetReadMembersFail" = "Get Read Members Failed.";
"TUIKitMessageReadGetUnreadMembersFail" = "Get Unread Members Failed.";
"TUIKitMoreCamera" = "Take Photo";
"TUIKitMorePhoto" = "Album";
"TUIKitMoreVideo" = "Record Video";
"TUIKitMoreVideoCaptureDurationTip" = "record time is too short";
"TUIKitMoreFile" = "File";
"TUIKitMoreVideoCall" = "Video Call";
"TUIKitMoreVoiceCall" = "Voice Call";
"TUIKitMoreLink" = "Custom";
"TUIKitMoreLinkDetails" = "View Details>>";
"TUIKitMorePoll" = "Poll";
"TUIKitMoreGroupNote" = "Group Note";
"TUIKitMoreEvaluation" = "Service Evaluation";
"TUIKitMoreCard" = "Card Message";
"TUIKitFileSizeCheckLimited" = "File size exceeds size limit";
"TUIKitImageSizeCheckLimited" = "Image size exceeds size limit";
"TUIKitCallInviteYouVideoCall" = "invited you to a video call.";
"TUIKitCallTurningOnMute" = "Mute On";
"TUIKitCallTurningOffMute" = "Mute Off";
"TUIKitCallUsingSpeaker" = "Turn On Speaker";
"TUIKitCallUsingHeadphone" = "Turn Off Speaker";
"TUIKitCallCancelCallingFormat" = "%@ canceled the call.";
"TUIKitAtSelectMemberTitle" = "Select Group Members";
"TUIKitConversationTipsAtMe" = "[You were mentioned]";
"TUIKitConversationTipsAtAll" = "[@All]";
"TUIKitConversationTipsAtMeAndAll" = "[I was mentioned][@All]";
"TUIKitConversationTipsDelete" = "This conversation and history message will all be deleted";
"TUIKitPublicGroup" = "Public Group";
"TUIKitWorkGroup" = "Discussion Group";
"TUIKitChatRoom" = "Chatroom";
"TUIKitCommunity" = "Community";
"TUIKitContactsNewFriends" = "New Contacts";
"TUIKitContactsGroupChats" = "Group Chats";
"TUIKitContactsBlackList" = "Blocked List";
"TUIKitAddFriendSourceFormat" = "Source: %@";
"TUIKitFriendApplicationApproved" = "Friend request accepted";
"TUIKitFirendRequestRejected" = "Friend request declined";
"TUIKitOfflinePushCallTips" = "You have a call request.";
"TUIKitChatTap2Remove" = "Tap to remove";
"TUIKitChatPendencyTitle" = "Tap to Process";
"TUIKitChatPendencyRequestToJoinGroupFormat" = "%@ group joining request(s)";
"TUIKitChatTipsAtMe" = "You were mentioned";
"TUIKitChatBackToLatestLocation" = "back to latest location";
"TUIKitChatNewMessages" = "%@new messages";
"TUIKitAllowTypeAcceptOne" = "Allow any user to add you as friend";
"TUIKitAllowTypeNeedConfirm" = "Anyone upon Request";
"TUIKitAllowTypeDeclineAll" = "Decline friend request from any user";
/***************************** 消息转发 & 消息搜索 *************************/
// 补充
"Multiple" = "Multiple";
"Forward" = "Forward";
"Delete" = "Delete";
"Copy" = "Copy";
"Revoke" = "Revoke";
"Resend" = "Resend";
"Search" = "Search";
"Recall" = "Recall";
"MultiSelect" = "Select";
"Quote" = "Quote";
"TUIKitCallMicCamAuthTips" = "Allow access to your microphone and camera";
"TUIKitCallMicAuthTips" = "Allow access to your microphone";
"TUIKitMessageTipsSureToResend" = "Are you sure you want to resend the message?";
// 消息转发
"TUIKitRelayNoMessageTips" = "Select messages";
"TUIKitRelayRecentMessages" = "Recent Chats";
"TUIKitRelayChatHistory" = "Chat History";
"TUIKitRelaySepcialForbid" = "Cannot forward some selected messages (e.g., voice messages).";
"TUIKitRelayConfirmForward" = "Are you sure you want to forward the messages?";
"TUIKitRelayOneByOneForward" = "One-by-One Forward";
"TUIKitRelayCombineForwad" = "Combine and Forward";
"TUIKitRelayGroupChatHistory" = "Group Chat History";
"TUIKitRelayChatHistoryForSomebodyFormat" = "Chat History of %@ and %@";
"TUIKitRelayErrorForwardFormat" = "@\"Failed to forward: code:%d, desc:%@\"";
"TUIKitRelayTargetCreateNewChat" = "New Chat";
"TUIKitRelayTargetCreateNewGroup" = "New Group";
"TUIKitRelayTargetSelectFromContacts" = "Select from contacts";
"TUIKitRelayTargetCrateGroupError" = "Failed to create the group.";
"TUIKitRelayTargetNoneTips" = "Select a contact or conversation";
"TUIKitRelayLayerLimitTips" = "The current nesting level exceeds the limit.";
"TUIKitRelayCompatibleText" = "Combine and Forward is not available. Upgrade to the latest version first.";
"TUIKitRelayUnsupportForward" = "Unable to forward failed messages.";
"TUIKitRelayOneByOnyOverLimit" = "Unable to forward the messages one by one because there are too many messages.";
"TUIKitRelayPluginNotAllowed" = "Unable to forward group note or poll messages.";
// 消息搜索
"TUIKitSearchItemHeaderTitleContact" = "Contacts";
"TUIKitSearchItemFooterTitleContact" = "More";
"TUIKitSearchItemHeaderTitleGroup" = "Group Chat";
"TUIKitSearchItemFooterTitleGroup" = "More";
"TUIkitSearchItemHeaderTitleChatHistory" = "Chat History";
"TUIKitSearchItemFooterTitleChatHistory" = "More";
"TUIKitSearchResultMatchFormat" = "Contains: %@";
"TUIKitSearchResultMatchGroupIDFormat" = "Contains group ID: %@";
"TUIKitSearchResultMatchGroupMember" = "Contains group members:";
"TUIKitSearchResultDisplayChatHistoryCountFormat" = "%zd message(s)";
/***************************** 消息转发 & 消息搜索 *************************/
/***************************** 消息回复 & 换肤 *************************/
"Reply" = "Reply";
"TUIKitReplyMessageNotFoundOriginMessage" = "Unable to locate the original message.";
"TUIKitClearAllChatHistory" = "Clear Chat History";
"TUIKitClearAllChatHistoryTips" = "Are you sure you want to clear the chat history?";
"Discline" = "Decline";
"Copied" = "Copied";
"ConfirmDeleteMessage" = "Delete now";
"TUIKitAddFriend" = "Add Contact";
"TUIKitAddGroup" = "Add Group";
"TUIKitSearchUserID" = "Search by user ID";
"TUIKitSearchGroupID" = "Search by group ID";
"TUIKitNoSelfSignature" = "No status";
"TUIKitSelfSignatureFormat" = "Status: %@";
"TUIKitGroupProfileManage" = "Manage";
"TUIKitGroupManageShutupAllTips" = "When Mute All is enabled, only the group owner and admins are allowed to send messages.";
"TUIKitGroupManageAdminSetting" = "Set as Admin";
"TUIKitGroupManageShutAll" = "Mute All";
"TUIKitGroupAddShutupMember" = "Add members to mute";
"TUIKitGroupShutupOption" = "Mute";
"TUIKitGroupDisShutupOption" = "UnMute";
"TUIKitGroupOwner" = "Group Owner";
"TUIKitGroupManagerFormat" = "Admin (%zd/%zd)";
"TUIKitSetShutupAllFormat" = "%@ enabled Mute All";
"TUIKitCancelShutupAllFormat" = "%@ disabled Mute All";
"TUIKitGroupNotice" = "Group Notice";
"TUIKitGroupNoticeNull" = "No group notice";
"Edit" = "Edit";
"Done" = "OK";
"TUIKitAddContactMyUserIDFormat" = "My User ID: %@";
"TUIKitAddUserNoDataTips" = "This user does not exist.";
"TUIKitAddGroupNoDataTips" = "This group chat does not exist.";
/***************************** 消息回复 & 换肤 *************************/
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitVideoTranscoding" = "Transcoding video...";
"TUIKitVideoDownloading" = "Downloading video...";
"TUIKitVideoSavedSuccess" = "Video saved successfully.";
"TUIKitVideoSavedFailed" = "Failed to save video.";
"TUIKitPictureSavedSuccess" = "Image saved successfully.";
"TUIKitPictureSavedFailed" = "Failed to save image.";
"TUIKitPictureCheckRisk" = "It has been detected that the image contains sensitive content and cannot be viewed temporarily.";
"TUIKitVideoCheckRisk" = "It has been detected that the video contains sensitive content and cannot be viewed temporarily.";
"TUIKitVideoCheckRiskCancel" = "OK";
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitGroupSetAdminsForbidden" = "Unable to set an admin for this type of group chat.";
"TUIKitGroupAddAdmins" = "Add Admin";
"TUIKitGroupAddMemberOfBlockedForbidden" = "Unable to mute specified members for this type of group chat.";
"TUIKitNotDownload"= "Not download";
"TUIKitInputNoCameraTitle" = "No access to camera";
"TUIKitInputNoCameraTips" = "Unable to take photos, record videos, or make video calls. Click Authorize Now to allow access to your camera.";
"TUIKitInputNoCameraOperateLater" = "Later";
"TUIKitInputNoCameraOperateEnable" = "Authorize Now";
"TUIKitInputNoMicTitle" = "No access to microphone";
"TUIKitInputNoMicTips" = "Unable to send voice messages, record videos, or make voice and video calls. Click Authorize Now to allow access to your microphone.";
"TUIKitInputNoMicOperateLater" = "Later";
"TUIKitInputNoMicOperateEnable" = "Authorize Now";
"TUIKitInputNoPhotoTitle" = "No access to images";
"TUIKitInputNoPhotoTips" = "Unable to view or select images in a chat. Click Authorize Now to allow access to your images.";
"TUIKitInputNoPhotoOperateLater" = "Later";
"TUIKitInputNoPhotoerateEnable" = "Authorize Now";
"TUIKitInputRecordTipsTitle" = "Swipe left to cancel or release to send";
"TUIKitInputRecordCancelTipsTitle" = "Release to cancel";
"TUIKitInputRecordRejectedInCall" = "Audio or video call is in progress, please try again later";
"TUIKitInputRecordRejectedIsRecording" = "The current recording is not finished";
"StickyonTop" = "Pin";
"CancelStickonTop" = "UnPin";
"ClearHistoryChatMessage" = "Clear messages";
"MarkAsRead" = "Mark As Read";
"MarkAsUnRead" = "Mark As UnRead";
"MarkHide" = "Hide";
"MinimizeGroup" = "Minimize Group";
"More" = "More";
"Info" = "Info";
"TUIKitConversationMarkHide" = "Conversation Hide";
"TUIKitConversationMarkHideDescriptionOn" = "After opening, this conversation will not appear in the conversation list, and the chat history will still be preserved";
"TUIKitConversationMarkHideDescriptionOff" = "After closing, this conversation will appear in the conversation list and the chat history will still be preserved.";
"TUIKitConversationMarkFoldGroups" = "Minimized Groups";
"TUIKitConversationMarkFold" = "Minimize Group";
"TUIKitRepliesDetailTitle" = "Reply Details";
"TUIKitRepliesNum" = " replies";
"TUIKitReference" = "quote";
"TUIKitNotSupportThisMessage" = "[This message display is not currently supported]";
"TUIKitReferenceOriginMessageRevoke" = "Quoted content has been revoked";
"TUIKitRepliesOriginMessageRevoke" = "Reply content has been revoked";
"TUIKitAudioPlaybackStyleLoudspeaker" = "Loudspeaker";
"TUIKitAudioPlaybackStyleHandset" = "Handset";
"TUIKitAudioPlaybackStyleChange2Loudspeaker" = "Switch To Loudspeaker";
"TUIKitAudioPlaybackStyleChange2Handset" = "Switch To Handset";
"TUIKitGroupMessagePin" = "Pin";
"TUIKitGroupMessageUnPin" = "UnPin";
"TUIKitGroupMessagePinOverLimit" = "pinned messages over limit";
"TUIKitGroupMessagePinRepeatedly" = "The message is already pinned.";
"TUIKitGroupMessageUnPinRepeatedly" = "This message has already been unpinned.";
"TUIKitTranslate" = "Translate";
"TUIKitTranslateDefaultTips" = "Translated by IM";
"TUIKitTranslateFailed" = "Unable to translate";
"Hide" = "Hide";
"TranslateMessage" = "Translate message to language";
"TranslateLanguageNotSupport" = "Translation is not supported between current languages, please switch to another language.";
"TUIKitConvertToText" = "Convert\nto Text";
"TUIKitConvertToTextFailed" = "Unable to convert";
"TUIKitSignalingSwitchToAudio" = "Switch to voice call";
"TUIKitSignalingUnsupportedCalling" = "Unknown invitation";
"TUIKitSignalingComfirmSwitchToAudio" = "Comfirm video to voice";
"TUIKitErrorEnableUserStatusOnConsole" = "The user status not supported. Please enable the ability in the console first.";
"TUIKitChooseAvatar" = "Choose Avatar";
"TUIKitChooseCover" = "Choose a cover";
"TUIKitChooseBackground" = "Choose a Wallpaper";
"TUIKitDefaultBackground" = "Default background";
"TUIKitIdentity" = "ID";
"TUIKitChooseBackgroundSuccess" = "Setup Success";
"TUIKitChooseBackgroundFailed" = "Setup failed";
"TUIKitContactNoNewApplicationRequest" = "Friend request is empty";
"TUIKitContactNoGroupChats" = "Group chats is empty";
"TUIKitContactNoBlockList" = "Block list is emply";
"TUIKitSearchNoResultLists" = "No conversations, contacts, or messages found";
"TUIKitTyping" = "Typing";
"TUIKitMessageTipsEditGroupAddOptFormat" = "%@ changed group add option to \"%@\"、";
"TUIKitMessageTipsEditGroupInviteOptFormat" = "%@ changed group inviting option to \"%@\"、";
"TUIKitCreatGroupNamed" = "Group Nickname";
"TUIKitCreatGroupID" = "Group IDoptional";
"TUIKitCreatGroupNamed_Placeholder" = "Enter group nickname";
"TUIKitCreatGroupID_Placeholder" = "Enter group ID";
"TUIKitCreatGroupAvatar" = "Group Avatar";
"TUIKitCreatGroupType" = "Group Type";
"TUIKitCreatGroupType_Work" = "Friends Working group(Work)";
"TUIKitCreatGroupType_Public" = "Stranger Social group(Public";
"TUIKitCreatGroupType_Meeting" = "Temporary Meeting group(Meeting";
"TUIKitCreatGroupType_Community" = "Community(Community)";
"TUIKitCreatGroupType_Work_Desc" = "Friends work group (Work): Similar to ordinary WeChat groups, after creation, only friends who are already in the group can be invited to join the group, and there is no need for the approval of the invitee or the approval of the group owner.";
"TUIKitCreatGroupType_Public_Desc" = "Stranger social group (Public): Similar to QQ group, the group owner can designate the group administrator after creation. After the user searches for the group ID and initiates a group application, the group owner or administrator must approve it before joining the group.";
"TUIKitCreatGroupType_Meeting_Desc" = "Temporary meeting group (Meeting): After creation, you can enter and leave at will, and support viewing of messages before joining the group; it is suitable for audio and video conference scenarios, online education scenarios, and other scenarios that are combined with real-time audio and video products.";
"TUIKitCreatGroupType_Community_Desc" = "Community(Community)After creation, you can enter and leave at will, support up to 100,000 people, support historical message storage, and after users search for group ID and initiate a group application, they can join the group without administrator approval.";
"TUIKitCreatGroupType_See_Doc" ="See product documentation for details";
"TUIKitCreatGroupType_Desc_Highlight" = "product documentation";
"TUIKitCreatGroupType_See_Doc_Simple" = "See product documentation";
"TUIKitSearchItemCancel" = "Cancel";
//----mini---
"Pin" = "Pin";
"UnPin" = "UnPin";
"TUIKitSelected" = "Selected";
"TUIKitThreadQuote" = "Thread Quote";
"MessageInfo" = "Message Info";
"C2CReadBy" = "Read";
"C2CDeliveredTo" = "Delivered";
"GroupReadBy" = "Read By";
"GroupDeliveredTo" = "Delivered To";
"TUIKitMessage" = "Message";
"TUIKitAudio" = "Audio";
"TUIKitVideo" = "Video";
"TUIKitAddMembers" = "+ Add members";
"TUIKitMembersRoleMember" = "Member";
"TUIKitMembersRoleAdmin" = "Admin";
"TUIKitMembersRoleSuper" = "Super";
"TUIKitCreateCancel" = "Cancel";
"TUIKitCreateNext" = "Next";
"TUIKitCreateFinish" = "Done";
"TUIKitCreateMemebers" = "Participants";
"TUIKitDateToday" = "Today";
//----mini---
"TUIPollCreateNew" = "Create Poll";
"TUIPollCreateSucceed" = "Create Succeeded";
"TUIPollCreateFail" = "Create Failed";
"TUIPollEnterTitle" = "Enter a poll question";
"TUIPollEnterOption" = "Enter an option";
"TUIPollAddOption" = "Add Option";
"TUIPollEnableMultiSelect" = "Allow selecting multiple options";
"TUIPollSingleSelect" = "Single";
"TUIPollMultiSelect" = "Multiple";
"TUIPollAnonymousVote" = "Anonymous votes";
"TUIPollPublic" = "Public";
"TUIPollNonPublic" = "Non-Public";
"TUIPollNonAnonymous" = "Non-Anonymous";
"TUIPollAnonymous" = "Anonymous";
"TUIPollEnableNotification" = "Allow notification for new submit";
"TUIPolllPublish" = "Publish";
"TUIPollOneCount" = "vote";
"TUIPollMoreCount" = "votes";
"TUIPollInvitedParticipants" = "Invited: %d";
"TUIPollRespondedParticipants" = "Responded: %d";
"TUIPollResend" = "Resend";
"TUIPollResendFail" = "Resend poll failed, error code: %d";
"TUIPollEnd" = "Close Poll";
"TUIPollEndFail" = "Close poll failed, error code: %d";
"TUIPollVoteNow" = "Vote now";
"TUIPollConfirmVote" = "Confirm";
"TUIPollCancelVote" = "Cancel";
"TUIPollPercent" = "votes %d, percent %.0f%%";
"TUIPollEnded" = "Poll is closed";
"TUIPollVoteSucceed" = "Vote succeeded";
"TUIPollVoteFail" = "Vote failed";
"TUIPollPublicResult" = "Public results";
"TUIPollViewAllOptions" = "View all options";
"TUIPollDisplayString" = "[Poll]";
"TUIPollTextFieldExceedLimit" = "Maximum 50 characters";
"TUIPollVoted" = "Voted";
"TUIPoll" = "Poll";
"TUIGroupNote" = "Group note";
"TUIGroupNoteTitle" = "Group note";
"TUIGroupNoteSettings" = "Settings";
"TUIGroupNoteDeadline" = "Deadline %@";
"TUIGroupNoteTitleHint" = "Enter group note title";
"TUIGroupNoteDescriptionHint" = "Enter group note description";
"TUIGroupNoteFormatExample" = "eg.";
"TUIGroupNoteFormatHint" = "Fill in the group note form";
"TUIGroupNoteFillIn" = "Please fill in";
"TUIGroupNoteMultipleSettings" = "Allow multiple submission";
"TUIGroupNoteDeadlineSettings" = "Set deadline";
"TUIGroupNoteDeadlineNotSet" = "Not set";
"TUIGroupNoteNotifySettings" = "Submission Result Notification";
"TUIGroupNoteSend" = "Send";
"TUIGroupNoteNow" = "Join group note now";
"TUIGroupNoteMessageTitle" = "Group note";
"TUIGroupNoteMessageTitleInfo" = "Started by %@, %d participated";
"TUIGroupNoteTipsMessagePrefix" = "%@ participated in group note";
"TUIGroupNoteHasStopped" = "Group note has stopped";
"TUIGroupNoteDeadlineMustBeLater" = "The deadline must be later than the current time";
"TUIGroupNoteDisplayString" = "[Group Note]";
"TUIGroupNoteSendSucceed" = "Send succeeded";
"TUIGroupNoteSendFail" = "Send failed";
"TUIGroupNoteSubmitExceedLimit" = "The count of group notes exceeds the upper limit";
"TUIGroupNoteTextFieldExceedLimit" = "Maximum 50 characters";
"TUIGroupNoteSubmitOnce" = "Only one submission is allowed";
"TUIGroupNoteSubmitNotAllowed" = "You are not allow to submit";
"TUIGroupNoteExceedDeadline" = "Exceed deadline, collection stopped";
"TUIGroupNoteNotChanged" = "Group note's content is not changed";
"TUIGroupNoteExpand" = "...Expand...";
"TUIGroupNoteNotDatePickerConfirm" = "OK";
"TUIGroupNoteNotDatePickerCancel" = "Cancel";
"TUIGroupNoteNotDatePickerDone" = "Done";
"TUIGroupNoteNotDatePickerYear" = " ";
"TUIGroupNoteNotDatePickerMonth" = " ";
"TUIGroupNoteNotDatePickerDay" = " ";
"TUIGroupNoteNotDatePickerHour" = " ";
"TUIGroupNoteNotDatePickerMinute" = " ";
"TUIGroupNoteNotDatePickerSecond" = " ";
"TUIGroupNoteNotDatePickerAM" = "AM";
"TUIGroupNoteNotDatePickerPM" = "PM";
"TUIGroupNoteNotDatePickerNow" = " Now";
"TUIGroupNoteNotDatePickerToday" = " Today";
"TUIGroupNoteNotDatePickerMon" = " Mon";
"TUIGroupNoteNotDatePickerTue" = " Tue";
"TUIGroupNoteNotDatePickerWed" = " Wed";
"TUIGroupNoteNotDatePickerTHR" = " Thu";
"TUIGroupNoteNotDatePickerFRI" = " Fri";
"TUIGroupNoteNotDatePickerSAT" = " Sat";
"TUIGroupNoteNotDatePickerSUN" = " Sun";
"TUICustomerServiceAccounts" = "Customer Service List";
"TUICustomerServiceBranchMessage" = "[Branch Message]";
"TUICustomerServiceCardMessage" = "[Card Message]";
"TUICustomerServiceCollectInfomation" = "[Collect infomation]";
"TUICustomerServiceSatisfactionEvaluation" = "[Satisfaction Evaluation]";
"TUICustomerServiceTimeout" = "[Session Timeout]";
"TUICustomerServiceEnd" = "[Session End]";
"TUICustomerServiceFillProductInfo" = "Please fill in product information";
"TUICustomerServiceSubmitProductInfo" = "Submit";
"TUICustomerServiceClose" = "Close";
"TUICustomerServiceName" = "Name";
"TUICustomerServiceFillProductName" = "Please fill in the product name";
"TUICustomerServiceDesc" = "Desc";
"TUICustomerServiceFillProductDesc" = "Please fill in the product description";
"TUICustomerServicePic" = "Pic";
"TUICustomerServiceFillPicLink" = "Please fill in the picture link";
"TUICustomerServiceJumpLink" = "Jump";
"TUICustomerServiceFillJumpLink" = "Please fill in the card jump link";
"TUICustomerServiceSubmitEvaluation" = "Submit";
"TUICustomerService" = "Customer Service plugin";
"TUICustomerServiceSendProduct" = "Send Product Info";
"TUICustomerServiceCommonPhrase" = "Common Phrases";
"TUICustomerServiceCommonPhraseStock" = "Is it in stock?";
"TUICustomerServiceCommonPhraseCheaper" = "Can it be cheaper?";
"TUICustomerServiceCommonPhraseGift" = "Is there a giveaway?";
"TUICustomerServiceCommonPhraseShipping" = "What is the shipping date?";
"TUICustomerServiceCommonPhraseDelivery" = "Which express company is it?";
"TUICustomerServiceCommonPhraseArrive" = "When will it arrive?";
"TUIChatBotAccounts" = "Chat Bot";
"TUIChatBotChangeQuestion" = "Change it";
"TUIConversationGroupAll" = "All";
"TUIConversationGroupUnread" = "Unread";
"TUIConversationGroupAtMe" = "@ Me";
"TUIConversationGroupGroup" = "Group";
"TUIConversationGroupC2C" = "C2C";
"TUIConversationGroupInputGroupName" = "Please input a group name";
"TUIConversationGroupCreateGroup" = "Create new conversation group";
"TUIConversationGroupNewGroup" = "Create new group";
"TUIConversationGroupAddGroup" = "Add to group";
"TUIConversationGroupMoveGroup" = "Move group";
"TUIConversationGroupRemoveGroup" = "Remove group";
"TUIConversationGroupShowGroup" = "Show conversation group";
"TUIConversationGroupHideGroup" = "Hide conversation group";
"TUIConversationGroupEditGroup" = "Group settings";
"TUIConversationGroupEdit" = "Edit";
"TUIConversationGroupSave" = "Save";
"TUIConversationGroupSaveSucc" = "Group save success";
"TUIConversationGroupCreate" = "Create";
"TUIConversationGroupNameIsNull" = "Group name cannot be empty";
"TUIConversationGroupNameIsInvalid" = "Group name cannot be the preset default group name";
"TUIConversationGroupConversationIsNull" = "Conversation name cannot be empty";
"TUIConversationGroupCreateSucc" = "Group created successfully";
"TUIConversationGroupModifySucc" = "Group modified successfully";
"TUIConversationGroupName" = "Group name";
"TUIConversationGroupIncludeConversation" = "Contains conversation";
"TUIConversationGroupAddConversation" = "Add conversation";
"TUIConversationGroup" = "Conversation group";
"TUIConversationGroupManager" = "Manager";
"TUIConversationGroupSelectGroup" = "Select conversation group";
"TUIConversationGroupUnlogined" = "Unloginedsave failed ";
"TUIConversationMark" = "Mark";
"TUIConversationMarkCancel" = "Unmark";
"TUIConversationNone" = "No %@ conversation yet";
//----timApp---
"TIMAppMainTitle" = "TencentCloud·IM";
"TIMAppMainConnectingTitle" = "Connecting…";
"TIMAppMainDisconnectTitle" = "Disconnected";
"TIMAppTabBarItemMessageText" = "Messages";
"TIMAppTabBarItemContactText" = "Contacts";
"TIMAppTabBarItemMeText" = "Settings";
"TIMAppTabBarItemMessageText_mini" = "Messages";
"TIMAppTabBarItemContactText_mini" = "Contacts";
"TIMAppTabBarItemSettingText_mini" = "Settings";
"TIMAppTabBarItemCallsRecordText_mini" = "Calls";
"TIMAppChat" = "Chat";
"TIMAppChatDisconnectTitle" = "Disconnected";
"TIMAppChatSecurityWarning" = "Do not easily believe in remittance, winning prizes, and other information. Be cautious when dealing with unknown phone calls to avoid being deceived. Please report any suspicious situations in a timely manner.";
"TIMTencentRTCAppChatSecurityWarning" = "[Security Reminder]Please be alert to all kinds of fraud such as impersonating police, loan institutions, customer service, and avoid leaking personal privacy.";
"TIMAppChatSecurityWarningReport" = "Report";
"TIMAppChatSecurityWarningGot" = "Got it";
"TIMAppTencentCloudIM" = "TencentCloudIM";
"TIMAppWelcomeToChat" = " Welcome to Chat demo!";
"TIMAppWelcomeToChatDetails" = "Welcome to Chat demo! You can try out various features such as sending text/emoji/rich media messages, checking message read status and friend relationship. You can also start one-to-one chats or group chats and manage groups with three no-replying bot friends equipped for you.";
"TIMAppSelectStyle" = "Select Style";
"TIMAppChatStyles" = "Chat styles";
"TIMAppChangeTheme" = "Change Theme";
"TIMAppChatThemes" = "Chat themes";
"TIMAppEnterChat" = "Enter Chat";
"TIMAppRunDemo" = "Run a demo in 1 minute with a few lines of code changes";
"TIMAppCustomers" = "Over 10,000 customers per month";
"TIMAppMessagingSuccess" = "Over 99.99% messaging success rate and service reliability";
"TIMAppActiveUsers" = "Over 1 billion active users per month";
"TIMAppOK" = "OK";
"TIMApp1Billion+" = "1 billion+";
"TIMApp10000+" = "10,000+";
"TIMApp1Minute" = "1 minute";
"TIMAppMeAbout" = "About TencentCloud Chat";
"TIMAppConfirmLogout" = "Leave now?";
"TIMAppConfirm" = "OK";
"TIMAppCancel" = "Cancel";
"TIMAppMarkAllMessageAsReadSucc" = "Success to mark all as read";
"TIMAppMarkAllMessageAsReadErrFormat" = "Error, %d, %@";
"TIMChangeLanguage" = "Language";
//----timApp---
"TUIKitTickledMe" = "I";
"TUIKitTickledActionVaule" = " tickled ";
"TUIKitTickledMyself" = "myself";
"TUIKitImageViewOrigin" = "Original image";
"TUIGroupCreateTipsMessage" = "Create Group";
"TUICommunityCreateTipsMessage" = "Create Community";
"TUICommunityCreateTipsMessageRuleError" = "Custom group ID of \"Community\" must be prefixed with @TGS#_.";
"TUIGroupCreateTipsMessageRuleError" = "Non-community, cannot start with @TGS#_";
"zh-Hans" = "Simple Chinese";
"en" = "English";
"ar" = "Arabic";
"zh-Hant" = "Traditional Chinese";
"TUIChatFaceGroupAllEmojiName" = "All";
"TUIChatFaceGroupRecentEmojiName" = "Recently";

View File

@@ -0,0 +1,813 @@
/*
Localizable.strings
Chinese Simplized
Created by harvy on 2020/10/9.
*/
/***************************** 日期格式化 Start *************************/
"YesterdayDateFormat" = "aHH:mm";
/***************************** 日期格式化 End *************************/
"TUIKitTipsMostSelectTextFormat" = "最多选择%ld个";
"Confirm" = "确定";
"Cancel" = "取消";
"Send" = "发送";
"Save" = "保存";
"You" = "您";
"you" = "您";
"Male" = "男";
"Female" = "女";
"File" = "文件";
"Download" = "下载";
"Unsetted" = "未设置";
"Show" = "展示";
"Monday" = "星期一";
"Tuesday" = "星期二";
"Wednesday" = "星期三";
"Thursday" = "星期四";
"Friday" = "星期五";
"Saturday" = "星期六";
"Sunday" = "星期日";
"Yesterday" = "昨天";
"am" = "上午";
"pm" = "下午";
"Read" = "已读";
"Unread" = "未读";
"Copy" = "复制";
"Delete" = "删除";
"Revoke" = "撤回";
"Retry" = "重试";
"Re_send" = "重发";
"Make_a_call" = "发起呼叫";
"Done" = "完成";
"All" = "所有人";
"Agreed" = "已同意";
"Disclined" = "已拒绝";
"Agree" = "同意";
"Have_been_sent" = "已发送";
"OK" = "好";
"None" = "无";
"Send" = "发送";
"Cancel" = "取消";
"Alia" = "备注";
"Group" = "分组";
"Accept" = "同意";
"Decline" = "拒绝";
"send_success" = "发送成功";
"add_success" = "添加成功";
"delete_success" = "删除成功";
"my_friend" = "我的朋友";
"no_personal_signature" = "暂无个性签名";
"SignatureFormat" = "个性签名:%@";
"choose_avatar_for_you" = "App随机为您分配一个系统头像";
"SearchGroupPlaceholder" = "用户ID";
"please_fill_in_verification_information" = "请填写验证信息";
"please_fill_in_remarks_group_info" = "请填写备注";
"no_set" = "未设置";
"no_personal_signature" = "暂无个性签名";
"logout" = "退出登录";
"login" = "登录";
"ReadAll" = "全部已读";
"Hide" = "隐藏";
"Delete" = "删除";
"ContactsJoinGroup" = "添加群聊";
"ContactsAddFriends" = "添加好友";
"ChatsNewChatText" = "发起会话";
"ChatsNewGroupText" = "创建群聊";
"ChatsSelectContact" = "选择联系人";
"GroupJoin" = "添加群组";
"GroupRequestJoinGroupFormat" = "%@ 申请加入群聊";
"GroupDeleteFriend" = "删除联系人";
"GroupAddFirend" = "添加联系人";
"GroupMember" = "群成员";
"ProfileAlia" = "备注名";
"ProfileMessageDoNotDisturb" = "消息免打扰";
"ProfileStickyonTop" = "置顶聊天";
"ProfileBlocked" = "加入黑名单";
"ProfileSendMessages" = "发送消息";
"ProfileDeleteFirend" = "删除好友";
"ProfileEditAlia" = "修改备注";
"ProfileDetails" = "详细资料";
"ProfileSetBackgroundImage" = "设置当前聊天背景";
"ProfilePhoto" = "头像";
"ProfileName" = "昵称";
"ProfileAccount" = "账号";
"ProfileSignature" = "个性签名";
"ProfileGender" = "性别";
"ProfileEditName" = "修改昵称";
"ProfileEditNameDesc" = "仅限中文、字母、数字和下划线";
"ProfileEditSignture" = "修改个性签名";
"ProfileEditGender" = "修改性别";
"ProfileEditAlia" = "修改备注";
"ProfileEdit" = "编辑";
"ProfileAlia" = "备注名";
"ProfileBlocked" = "加入黑名单";
"ProfileMessageDoNotDisturb" = "消息免打扰";
"ProfileStickyonTop" = "置顶聊天";
"ProfileSendMessages" = "发送消息";
"ProfileDeleteFirend" = "删除好友";
"ProfileBirthday" = "生日";
"SignatureFormat" = "个性签名:%@";
"FriendRequestFormat" = "我是%@";
"FriendRequestFillInfo" = "添加好友";
"FriendOneWay" = "单向好友";
"FriendAddTitle" = "加好友";
"FriendAddVerificationMessage" = "验证消息";
"FriendAddResultSuccessWait" = "发送成功,等待审核同意";
"FriendAddResultForbid" = "对方禁止添加";
"FriendAddResultSuccess" = "已添加到好友列表";
"FriendAddResultExists" = "对方已是您的好友";
"MeFriendRequest" = "加我为好友时";
"MeFriendRequestMethodAgreeAll" = "同意任何用户加好友";
"MeFriendRequestMethodNeedConfirm" = "需要验证";
"MeFriendRequestMethodDenyAll" = "拒绝任何人加好友";
"MeMessageReadStatus" = "消息阅读状态";
"MeMessageReadStatusOpenDesc" = "关闭后,您收发的消息均不带消息阅读状态,您将无法看到对方是否已读,同时对方也无法看到您是否已读。";
"MeMessageReadStatusCloseDesc" = "开启后,您在群聊中收发的消息均带有消息阅读状态,并且可以看到对方是否已读。与您单聊的好友若也开启了消息阅读状态,您与好友在单聊中收发的消息也将带有消息阅读状态。";
"ShowOnlineStatus" = "显示在线状态";
"ShowOnlineStatusOpenDesc" = "关闭后,您将不可以在会话列表和通讯录中看到好友在线或离线的状态提示。";
"ShowOnlineStatusCloseDesc" = "开启后,您将可以在会话列表和通讯录中看到好友在线或离线的状态提示。";
"ShowPackageToast" = "该功能为旗舰版功能";
"ShowCallsRecord" = "显示通话记录";
"TUIKitClassic" = "经典版";
"TUIKitMinimalist" = "简约版";
"TUIKitThemeNameSystemFollowTitle" = "跟随系统自动切换";
"TUIKitThemeNameSystemFollowSubTitle" = "开启后仅展示默认皮肤 (轻快/深色),不可切换其他皮肤样式";
"TUIKitThemeNameSerious" = "深沉";
"TUIKitThemeNameLight" = "轻量";
"TUIKitThemeNameLivey" = "活泼";
"TUIKitThemeNameDark" = "深色";
"TUIKitThemeNameSystem" = "跟随系统";
"TUIKitDone" = "完成";
"TUIKitWelcome" = "欢迎加入腾讯云 IM 大家庭!";
"TUIKitMicCamerAuthTips" = "请开启麦克风和摄像头权限";
"TUIKitMicAuth" = "请开启麦克风权限";
"TUIKitTipsConfirmResendMessage" = "确定重发此消息吗? ";
"TUIKitTipsSystemError" = "系统错误";
"TUIKitTipsEnterRoomErrorFormat" = "进房失败: %d";
"TUIKitWhoRequestForJoinGroupFormat" = "%@申请加入群聊";
"TUIKitInviteJoinGroupFormat" = "邀请%@加入群聊";
"TUIKitAgreedByAdministor" = "管理员同意申请";
"TUIkitDiscliedByAdministor" = "管理员拒绝申请";
"TUIKitDownloadProgressFormat" = "正在下载%d%%";
"TUIKitOpenWithOtherApp" = "用其他应用程序打开";
"TUIKitTipsContactListNil" = "联系人列表为空,请先添加好友";
"TUIKitUserStatusUnknown" = "未知";
"TUIKitUserStatusOnline" = "在线";
"TUIKitUserStatusOffline" = "离线";
"TUIKitUserStatusUnlogined" = "未登录";
"TUIKitInputHoldToTalk" = "按住 说话";
"TUIKitInputReleaseToSend" = "松开 结束";
"TUIKitInputReleaseToCancel" = "松开 取消";
"TUIKitInputBlankMessageTitle" = "不能发送空白消息";
"TUIKitInputWillFinishRecordInSeconds" = "将在 %ld 秒后结束录制";
"TUIKitInputRecordSlideToCancel" = "手指上滑,取消发送";
"TUIKitInputRecordReleaseToCancel" = "松开手指,取消发送";
"TUIKitInputRecordTimeshort" = "说话时间太短";
"TUIKitInputRecordTimeLong" = "说话时间太长";
"TUIKitGroupProfileDetails" = "详细资料";
"TUIKitGroupProfileMember" = "群成员";
"TUIKitGroupProfileMemberCount" = "%d 人";
"TUIKitGroupProfileMemberCountlu" = "%lu人";
"TUIKitGroupProfileType" = "群类型";
"TUIKitGroupProfileJoinType" = "主动加群方式";
"TUIKitGroupProfileInviteType" = "邀请进群方式";
"TUIKitGroupProfileInviteJoin" = "邀请加入";
"TUIKitGroupProfileAutoApproval" = "自动审批";
"TUIKitGroupProfileAlias" = "我的群昵称";
"TUIKitGroupProfileMessageDoNotDisturb" = "消息免打扰";
"TUIKitGroupProfileStickyOnTop" = "置顶聊天";
"TUIKitGroupProfileDeleteAndExit" = "删除并退出";
"TUIKitGroupProfileReport" = "举报";
"TUIKitGroupProfileDissolve" = "解散该群";
"TUIKitGroupProfileJoinDisable" = "禁止加群";
"TUIKitGroupProfileInviteDisable" = "禁止邀请";
"TUIKitGroupProfileAdminApprove" = "管理员审批";
"TUIKitGroupProfileEditAlias" = "修改我的群昵称";
"TUIKitGroupProfileEditAliasDesc" = "仅限中文、字母、数字和下划线";
"TUIKitGroupProfileEditGroupName" = "修改群名称";
"TUIKitGroupProfileEditAnnouncement" = "修改群公告";
"TUIKitGroupProfileEditAvatar" = "修改群头像";
"TUIKitGroupProfileDeleteGroupTips" = "退出后不会再接收到此群聊消息";
"TUIKitGroupProfileGroupCountFormat" = "群成员(%ld人)";
"TUIKitGroupProfileManage" = "管理";
"TUIKitGroupProfileManageAdd" = "添加成员";
"TUIKitGroupProfileManageDelete" = "删除成员";
"TUIKitGroupProfileAdmainAdd" = "设置为管理员";
"TUIKitGroupProfileAdmainDelete" = "取消管理员";
"TUIKitGroupTransferOwner" = "转让群主";
"TUIKitGroupTransferOwnerSuccess" = "转让群主成功";
"TUIKitGroupApplicant" = "群申请";
"TUIKitGroupDismssTipsFormat" = "%@ 群已解散";
"TUIKitGroupRecycledTipsFormat" = "%@ 群已回收";
"TUIKitGroupKickOffTipsFormat" = "您已被踢出 %@ 群";
"TUIKitGroupDropoutTipsFormat" = "您已退出 %@ 群";
"TUIKitGroupMessagePinRemove" = "移除";
"TUIKitMessageTipsNormalRecallMessage" = "有一条消息被撤回";
"TUIKitMessageTipsYouRecallMessage" = "您撤回了一条消息";
"TUIKitMessageTipsReEditMessage" = "重新编辑";
"TUIKitMessageTipsRecallMessageFormat" = "\"%@\"撤回了一条消息";
"TUIKitMessageTipsOthersRecallMessage" = "对方撤回了一条消息";
"TUIKitMessageTipsJoinGroupFormat" = "\"%@\"加入群组";
"TUIKitMessageTipsInviteJoinGroupFormat" = "\"%@\"邀请\"%@\"加入群组";
"TUIKitMessageTipsLeaveGroupFormat" = "\"%@\"退出了群聊";
"TUIKitMessageTipsKickoffGroupFormat" = "\"%@\"将\"%@\"踢出群组";
"TUIKitMessageTipsSettAdminFormat" = "\"%@\"被设置管理员";
"TUIKitMessageTipsCancelAdminFormat" = "\"%@\"被取消管理员";
"TUIkitMessageTipsEditGroupNameFormat" = "%@修改群名为\"%@\"、";
"TUIKitMessageTipsEditGroupIntroFormat" = "%@修改群简介为\"%@\"、";
"TUIKitMessageTipsEditGroupAnnounceFormat" = "%@修改群公告为\"%@\"、";
"TUIKitMessageTipsDeleteGroupAnnounceFormat" = "%@删除了群公告、";
"TUIKitMessageTipsEditGroupAvatarFormat" = "%@修改群头像、";
"TUIKitMessageTipsEditGroupOwnerFormat" = "%@修改群主为\"%@\"、";
"TUIKitMessageTipsCreateGroupFormat" = "\"%@\"创建群组";
"TUIKitMessageTipsUnsupportCustomMessage" = "[自定义消息]";
"TUIKitMessageTipsMute" = "被禁言";
"TUIKitMessageTipsUnmute" = "被解除禁言";
"TUIKitMessageTipsRecallRiskContent" = "违规消息被撤回";
"TUIKitMessageDisplayRiskContent" = "[违规消息]";
"TUIKitMessageTipsGroupPinMessage" = "\"%@\"置顶了一条消息";
"TUIKitMessageTipsGroupUnPinMessage" = "\"%@\"移除了一条置顶消息";
"TUIKitSignalingFinishGroupChat" = "结束群聊";
"TUIKitSignalingFinishConversationAndTimeFormat" = "通话时长";
"TUIKitSignalingNewCall" = "发起通话";
"TUIKitSignalingNewGroupCallFormat" = "\"%@\" 发起群通话";
"TUIkitSignalingCancelCall" = "取消通话";
"TUIkitSignalingCancelGroupCallFormat" = "\"%@\" 取消群通话";
"TUIkitSignalingHangonCall" = "已接听";
"TUIKitSignalingHangonCallFormat" = "\"%@\" 已接听";
"TUIKitSignalingBusyFormat" = "\"%@\" 忙线";
"TUIKitSignalingDeclineFormat" = "\"%@\" 拒绝通话";
"TUIKitSignalingCallBusy" = "对方忙线";
"TUIkitSignalingDecline" = "拒绝通话";
"TUIKitSignalingNoResponse" = "无应答";
"TUIkitSignalingUnrecognlize" = "不能识别的通话指令";
"TUIkitMessageTypeImage" = "[图片]";
"TUIKitMessageTypeVoice" = "[语音]";
"TUIkitMessageTypeVideo" = "[视频]";
"TUIkitMessageTypeFile" = "[文件]";
"TUIKitMessageTypeAnimateEmoji" = "[动画表情]";
"TUIKitMessageTypeDraftFormat" = "[草稿] ";
"TUIkitMessageTypeRiskImage" = "[违规图片]";
"TUIkitMessageTypeRiskVoice" = "[违规语音]";
"TUIkitMessageTypeRiskVideo" = "[违规视频]";
"TUIKitMessageTypeLastMsgCountFormat" = "条";
"TUIKitMessageTypeSecurityStrike" = "涉及敏感内容,发送失败";
"TUIKitMessageTypeSecurityStrikeVoice" = "涉及敏感内容,无法收听";
"TUIKitMessageTypeSecurityStrikeImage" = "涉及敏感内容,无法查看";
"TUIKitMessageTypeSecurityStrikeTranslate" = "涉及敏感内容,翻译失败";
"TUIKitMessageTypeOtherUseMic" = "其他功能正在使用麦克风,无法录制";
"TUIKitMessageReadPartRead" = "人已读";
"TUIKitMessageReadPartUnread" = "人未读";
"TUIKitMessageReadPartDisable" = "人无状态";
"TUIKitMessageReadAllRead" = "全部已读";
"TUIKitMessageReadC2CRead" = "已读";
"TUIKitMessageReadC2CUnRead" = "未读";
"TUIKitMessageReadC2CUnReadDetail" = "尚未阅读";
"TUIKitMessageReadDetail" = "消息详情";
"TUIKitMessageReadNoMoreData" = "没有更多数据了";
"TUIKitMessageReadGetReadMembersFail" = "获取已读成员失败";
"TUIKitMessageReadGetUnreadMembersFail" = "获取未读成员失败";
"TUIKitMoreCamera" = "拍照";
"TUIKitMorePhoto" = "图片";
"TUIKitMoreVideo" = "录像";
"TUIKitMoreVideoCaptureDurationTip" = "录制时间过短";
"TUIKitMoreFile" = "文件";
"TUIKitMoreVideoCall" = "视频通话";
"TUIKitMoreVoiceCall" = "语音通话";
"TUIKitMoreLink" = "自定义";
"TUIKitMoreLinkDetails" = "查看详情>>";
"TUIKitMorePoll" = "投票";
"TUIKitMoreGroupNote" = "群接龙";
"TUIKitMoreEvaluation" = "服务评价";
"TUIKitMoreCard" = "卡片消息";
"TUIKitFileSizeCheckLimited" = "文件大小超出限制";
"TUIKitImageSizeCheckLimited" = "图片大小超出限制";
"TUIKitCallInviteYouVideoCall" = "邀请你视频通话";
"TUIKitCallTurningOnMute" = "开启静音";
"TUIKitCallTurningOffMute" = "关闭静音";
"TUIKitCallUsingSpeaker" = "使用扬声器";
"TUIKitCallUsingHeadphone" = "使用听筒";
"TUIKitCallCancelCallingFormat" = "%@ 取消了通话";
"TUIKitAtSelectMemberTitle" = "选择群成员";
"TUIKitConversationTipsAtMe" = "[有人@我]";
"TUIKitConversationTipsAtAll" = "[@所有人]";
"TUIKitConversationTipsAtMeAndAll" = "[有人@我][@所有人]";
"TUIKitConversationTipsDelete" = "此会话及聊天记录将全部被删除";
"TUIKitPublicGroup" = "公开群";
"TUIKitWorkGroup" = "讨论组";
"TUIKitChatRoom" = "聊天室";
"TUIKitCommunity" = "社群";
"TUIKitContactsNewFriends" = "新的联系人";
"TUIKitContactsGroupChats" = "群聊";
"TUIKitContactsBlackList" = "黑名单";
"TUIKitAddFriendSourceFormat" = "来源: %@";
"TUIKitFriendApplicationApproved" = "已同意好友申请";
"TUIKitFirendRequestRejected" = "已拒绝好友申请";
"TUIKitOfflinePushCallTips" = "您有一个通话请求";
"TUIKitChatTap2Remove" = "点击删除";
"TUIKitChatPendencyTitle" = "点击处理";
"TUIKitChatPendencyRequestToJoinGroupFormat" = "%@条入群请求";
"TUIKitChatTipsAtMe" = "有人@我";
"TUIKitChatBackToLatestLocation" = "回到最新位置";
"TUIKitChatNewMessages" = "%@条新消息";
"TUIKitAllowTypeAcceptOne" = "同意任何用户加好友";
"TUIKitAllowTypeNeedConfirm" = "需要验证";
"TUIKitAllowTypeDeclineAll" = "拒绝任何人加好友";
/***************************** 消息转发 & 消息搜索 *************************/
// 补充
"Multiple" = "多选";
"Forward" = "转发";
"Delete" = "删除";
"Copy" = "复制";
"Revoke" = "撤回";
"Resend" = "重发";
"Search" = "搜索";
"Recall" = "撤回";
"MultiSelect" = "多选";
"Quote" = "引用";
"TUIKitCallMicCamAuthTips" = "请开启麦克风和摄像头权限";
"TUIKitCallMicAuthTips" = "请开启麦克风权限";
"TUIKitMessageTipsSureToResend" = "确定重发消息?";
// 消息转发
"TUIKitRelayNoMessageTips" = "请选择消息";
"TUIKitRelayRecentMessages" = "最近聊天";
"TUIKitRelayChatHistory" = "聊天记录";
"TUIKitRelaySepcialForbid" = "选中的消息中,语音消息等其他消息不能被转发.";
"TUIKitRelayConfirmForward" = "确定转发?";
"TUIKitRelayOneByOneForward" = "逐条转发";
"TUIKitRelayCombineForwad" = "合并转发";
"TUIKitRelayGroupChatHistory" = "群聊的聊天记录";
"TUIKitRelayChatHistoryForSomebodyFormat" = "%@和%@的聊天记录";
"TUIKitRelayErrorForwardFormat" = "@\"转发失败, code:%d, desc:%@\"";
"TUIKitRelayTargetCreateNewChat" = "创建新聊天";
"TUIKitRelayTargetCreateNewGroup" = "创建新群聊";
"TUIKitRelayTargetSelectFromContacts" = "从联系人中选择";
"TUIKitRelayTargetCrateGroupError" = "创建群失败";
"TUIKitRelayTargetNoneTips" = "请选择联系人或会话";
"TUIKitRelayLayerLimitTips" = "抱歉,当前嵌套层级超过了限制";
"TUIKitRelayCompatibleText" = "不支持合并转发消息,请升级到最新版本。";
"TUIKitRelayUnsupportForward" = "发送失败的消息不支持转发";
"TUIKitRelayOneByOnyOverLimit" = "转发消息过多,暂不支持逐条转发";
"TUIKitRelayPluginNotAllowed" = "投票/接龙消息不支持转发";
// 消息搜索
"TUIKitSearchItemHeaderTitleContact" = "联系人";
"TUIKitSearchItemFooterTitleContact" = "查看更多联系人";
"TUIKitSearchItemHeaderTitleGroup" = "群聊";
"TUIKitSearchItemFooterTitleGroup" = "查看更多群聊";
"TUIkitSearchItemHeaderTitleChatHistory" = "聊天记录";
"TUIKitSearchItemFooterTitleChatHistory" = "查看更多聊天记录";
"TUIKitSearchResultMatchFormat" = "包含:%@";
"TUIKitSearchResultMatchGroupIDFormat" = "包含群ID:%@";
"TUIKitSearchResultMatchGroupMember" = "包含群成员:";
"TUIKitSearchResultDisplayChatHistoryCountFormat" = "%zd条聊天记录";
/***************************** 消息转发 & 消息搜索 *************************/
/***************************** 消息回复 & 换肤 *************************/
"Reply" = "回复";
"TUIKitReplyMessageNotFoundOriginMessage" = "无法定位到原消息";
"TUIKitClearAllChatHistory" = "清空聊天记录";
"TUIKitClearAllChatHistoryTips" = "确定清空聊天记录?";
"Discline" = "拒绝";
"Copied" = "已复制";
"ConfirmDeleteMessage" = "确定删除已选消息";
"TUIKitAddFriend" = "添加好友";
"TUIKitAddGroup" = "添加群组";
"TUIKitSearchUserID" = "搜索用户ID";
"TUIKitSearchGroupID" = "搜索群ID";
"TUIKitNoSelfSignature" = "暂无个性签名";
"TUIKitSelfSignatureFormat" = "个性签名:%@";
"TUIKitGroupProfileManage" = "群管理";
"TUIKitGroupManageShutupAllTips" = "全员禁言开启后,只允许群主和管理员发言。";
"TUIKitGroupManageAdminSetting" = "设置管理员";
"TUIKitGroupManageShutAll" = "全员禁言";
"TUIKitGroupAddShutupMember" = "添加需要禁言的群成员";
"TUIKitGroupShutupOption" = "禁言";
"TUIKitGroupDisShutupOption" = "解除禁言";
"TUIKitGroupOwner" = "群主";
"TUIKitGroupManagerFormat" = "管理员(%zd/%zd)";
"TUIKitSetShutupAllFormat" = "%@ 开启全员禁言、";
"TUIKitCancelShutupAllFormat" = "%@ 取消全员禁言、";
"TUIKitGroupNotice" = "群公告";
"TUIKitGroupNoticeNull" = "暂未设置群公告";
"Edit" = "编辑";
"Done" = "完成";
"TUIKitAddContactMyUserIDFormat" = "我的用户ID: %@";
"TUIKitAddUserNoDataTips" = "该用户不存在";
"TUIKitAddGroupNoDataTips" = "该群聊不存在";
/***************************** 消息回复 & 换肤 *************************/
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitVideoTranscoding" = "视频转码中...";
"TUIKitVideoDownloading" = "视频下载中...";
"TUIKitVideoSavedSuccess" = "视频保存成功";
"TUIKitVideoSavedFailed" = "视频保存失败";
"TUIKitPictureSavedSuccess" = "图片保存成功";
"TUIKitPictureSavedFailed" = "图片保存失败";
"TUIKitPictureCheckRisk" = "检测到图片涉及敏感内容,暂时无法查看";
"TUIKitVideoCheckRisk" = "检测到视频涉及敏感内容,暂时无法查看";
"TUIKitVideoCheckRiskCancel" = "我知道了";
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitGroupSetAdminsForbidden" = "当前类型群聊暂不支持设置管理员";
"TUIKitGroupAddAdmins" = "添加管理员";
"TUIKitGroupAddMemberOfBlockedForbidden" = "当前类型群聊暂不支持禁言指定群成员";
"TUIKitNotDownload"= "未下载";
"TUIKitInputNoCameraTitle" = "相机权限未开启";
"TUIKitInputNoCameraTips" = "无法使用拍摄图片视频、视频通话等功能,点击\"去开启\"打开相机权限";
"TUIKitInputNoCameraOperateLater" = "以后再说";
"TUIKitInputNoCameraOperateEnable" = "去开启";
"TUIKitInputNoMicTitle" = "麦克风权限未开启";
"TUIKitInputNoMicTips" = "无法使用发送语音消息、拍摄视频、音视频通话等功能,点击\"去开启\"打开麦克风权限";
"TUIKitInputNoMicOperateLater" = "以后再说";
"TUIKitInputNoMicOperateEnable" = "去开启";
"TUIKitInputNoPhotoTitle" = "图片权限未开启";
"TUIKitInputNoPhotoTips" = "无法使用聊天功能中的图片查看、选择等功能,点击\"去开启\"打开图片权限";
"TUIKitInputNoPhotoOperateLater" = "以后再说";
"TUIKitInputNoPhotoerateEnable" = "去开启";
"TUIKitInputRecordTipsTitle" = "松开手指发送,左滑取消发送";
"TUIKitInputRecordCancelTipsTitle" = "松开手指,取消发送";
"TUIKitInputRecordRejectedInCall" = "正在音视频通话,请稍后再试";
"TUIKitInputRecordRejectedIsRecording" = "当前录音未结束";
"StickyonTop" = "置顶";
"CancelStickonTop" = "取消置顶";
"ClearHistoryChatMessage" = "清空消息";
"MarkAsRead" = "标为已读";
"MarkAsUnRead" = "标为未读";
"MarkHide" = "不显示";
"MinimizeGroup" = "会话折叠";
"More" = "更多";
"Info" = "详情";
"TUIKitConversationMarkHide" = "会话隐藏";
"TUIKitConversationMarkHideDescriptionOn" = "开启后,此会话将不会出现在会话列表中,聊天记录仍会被保留。";
"TUIKitConversationMarkHideDescriptionOff" = "关闭后,此会话将出现在会话列表中,聊天记录仍会被保留。";
"TUIKitConversationMarkFoldGroups" = "折叠的群聊";
"TUIKitConversationMarkFold" = "折叠该群聊";
"TUIKitRepliesDetailTitle" = "回复详情";
"TUIKitRepliesNum" = "条回复";
"TUIKitReference" = "引用";
"TUIKitNotSupportThisMessage" = "[暂时不支持该消息展示]";
"TUIKitRepliesOriginMessageRevoke" = "回复内容已撤回";
"TUIKitReferenceOriginMessageRevoke" = "引用内容已撤回";
"TUIKitAudioPlaybackStyleLoudspeaker" = "扬声器";
"TUIKitAudioPlaybackStyleHandset" = "听筒";
"TUIKitAudioPlaybackStyleChange2Loudspeaker" = "切换至扬声器播放";
"TUIKitAudioPlaybackStyleChange2Handset" = "切换至听筒播放";
"TUIKitGroupMessagePin" = "置顶";
"TUIKitGroupMessageUnPin" = "取消置顶";
"TUIKitGroupMessagePinOverLimit" = "置顶消息超过数量上限";
"TUIKitGroupMessagePinRepeatedly" = "这条消息已经被置顶了";
"TUIKitGroupMessageUnPinRepeatedly" = "这条消息已经被取消置顶了";
"TUIKitTranslate" = "翻译";
"TUIKitTranslateDefaultTips" = "由腾讯云 IM 提供翻译支持";
"TUIKitTranslateFailed" = "翻译失败";
"Hide" = "隐藏";
"TranslateMessage" = "将消息翻译为";
"TranslateLanguageNotSupport" = "当前翻译语言不支持,请切换至其他语言";
"TUIKitConvertToText" = "转文字";
"TUIKitConvertToTextFailed" = "转换失败";
"TUIKitSignalingSwitchToAudio" = "视频转语音";
"TUIKitSignalingUnsupportedCalling" = "未知邀请";
"TUIKitSignalingComfirmSwitchToAudio" = "确认转语音";
"TUIKitErrorEnableUserStatusOnConsole" = "您未开启用户状态功能,请先登录控制台开启";
"TUIKitChooseAvatar" = "选择头像";
"TUIKitChooseCover" = "选择封面";
"TUIKitChooseBackground" = "更换背景";
"TUIKitDefaultBackground" = "默认背景";
"TUIKitIdentity" = "ID";
"TUIKitChooseBackgroundSuccess" = "设置成功!";
"TUIKitChooseBackgroundFailed" = "设置失败!";
"TUIKitContactNoNewApplicationRequest" = "暂无新的联系人好友";
"TUIKitContactNoGroupChats" = "暂无群聊";
"TUIKitContactNoBlockList" = "暂无黑名单好友";
"TUIKitSearchNoResultLists" = "无相关会话、联系人或消息记录";
"TUIKitTyping" = "对方正在输入";
"TUIKitMessageTipsEditGroupAddOptFormat" = "%@修改加群方式为\"%@\"、";
"TUIKitMessageTipsEditGroupInviteOptFormat" = "%@修改邀请进群方式为\"%@\"、";
"TUIKitCreatGroupNamed" = "群名称";
"TUIKitCreatGroupID" = "群ID选填";
"TUIKitCreatGroupNamed_Placeholder" = "输入群名称";
"TUIKitCreatGroupID_Placeholder" = "输入群ID";
"TUIKitCreatGroupAvatar" = "群头像";
"TUIKitCreatGroupType" = "群类型";
"TUIKitCreatGroupType_Work" = "好友工作群(Work)";
"TUIKitCreatGroupType_Public" = "陌生人社交群(Public";
"TUIKitCreatGroupType_Meeting" = "临时会议群(Meeting";
"TUIKitCreatGroupType_Community" = "社群(Community)";
"TUIKitCreatGroupType_Work_Desc" = "好友工作群(Work类似普通微信群创建后仅支持已在群内的好友邀请加群且无需被邀请方同意或群主审批。";
"TUIKitCreatGroupType_Public_Desc" = "陌生人社交群(Public类似 QQ 群,创建后群主可以指定群管理员,用户搜索群 ID 发起加群申请后,需要群主或管理员审批通过才能入群。";
"TUIKitCreatGroupType_Meeting_Desc" = "临时会议群(Meeting创建后可以随意进出且支持查看入群前消息适用于音视频会议场景、在线教育场景等与实时音视频产品结合的场景。";
"TUIKitCreatGroupType_Community_Desc" = "社群(Community)创建后可以随意进出最多支持100000人支持历史消息存储用户搜索群 ID 发起加群申请后,无需管理员审批即可进群。";
"TUIKitCreatGroupType_See_Doc" ="详见产品文档";
"TUIKitCreatGroupType_Desc_Highlight" = "产品文档";
"TUIKitCreatGroupType_See_Doc_Simple" = "查看产品文档";
"TUIKitSearchItemCancel" = "取消";
//----mini---
"Pin" = "置顶";
"UnPin" = "取消置顶";
"TUIKitSelected" = "条选中";
"TUIKitThreadQuote" = "条回复";
"MessageInfo" = "消息详情";
"C2CReadBy" = "已读";
"C2CDeliveredTo" = "未读";
"GroupReadBy" = "已读列表";
"GroupDeliveredTo" = "未读列表";
"TUIKitMessage" = "发送消息";
"TUIKitAudio" = "语音通话";
"TUIKitVideo" = "视频通话";
"TUIKitAddMembers" = "+ 添加成员";
"TUIKitMembersRoleMember" = "成员";
"TUIKitMembersRoleAdmin" = "管理员";
"TUIKitMembersRoleSuper" = "群主";
"TUIKitCreateCancel" = "取消";
"TUIKitCreateNext" = "下一步";
"TUIKitCreateFinish" = "创建";
"TUIKitCreateMemebers" = "已选成员";
"TUIKitDateToday" = "今天";
//----mini---
"TUIPollCreateNew" = "发起投票";
"TUIPollCreateSucceed" = "创建成功";
"TUIPollCreateFail" = "创建失败";
"TUIPollEnterTitle" = "请输入投票主题";
"TUIPollEnterOption" = "请输入选项";
"TUIPollAddOption" = "添加选项";
"TUIPollEnableMultiSelect" = "允许多选";
"TUIPollSingleSelect" = "单选";
"TUIPollMultiSelect" = "多选";
"TUIPollAnonymousVote" = "匿名投票";
"TUIPollPublic" = "公开结果";
"TUIPollNonPublic" = "不公开结果";
"TUIPollNonAnonymous" = "不匿名";
"TUIPollAnonymous" = "匿名";
"TUIPollEnableNotification" = "有新提交时在群里发通知";
"TUIPolllPublish" = "发布";
"TUIPollOneCount" = "票";
"TUIPollMoreCount" = "票";
"TUIPollInvitedParticipants" = "应参与人数:%d";
"TUIPollRespondedParticipants" = "实际参与人数:%d";
"TUIPollResend" = "再次发送";
"TUIPollResendFail" = "再次发送失败,错误码:%d";
"TUIPollEnd" = "结束投票";
"TUIPollEndFail" = "结束投票失败,错误码:%d";
"TUIPollVoteNow" = "立即投票";
"TUIPollConfirmVote" = "确定";
"TUIPollCancelVote" = "取消";
"TUIPollPercent" = "已投%d票占比%.0f%%";
"TUIPollEnded" = "投票已结束";
"TUIPollVoteSucceed" = "投票成功";
"TUIPollVoteFail" = "投票失败";
"TUIPollPublicResult" = "公开结果";
"TUIPollViewAllOptions" = "查看全部选项";
"TUIPollDisplayString" = "[投票]";
"TUIPollTextFieldExceedLimit" = "超过最大字符数 50";
"TUIPollVoted" = "已投票";
"TUIPoll" = "投票";
"TUIGroupNote" = "接龙";
"TUIGroupNoteTitle" = "群接龙";
"TUIGroupNoteSettings" = "设置";
"TUIGroupNoteDeadline" = "截止时间 %@";
"TUIGroupNoteTitleHint" = "添加接龙主题";
"TUIGroupNoteDescriptionHint" = "添加接龙说明";
"TUIGroupNoteFormatExample" = "例";
"TUIGroupNoteFormatHint" = "可填写接龙格式";
"TUIGroupNoteFillIn" = "请填写";
"TUIGroupNoteMultipleSettings" = "允许每人提交多份";
"TUIGroupNoteDeadlineSettings" = "设置截止时间";
"TUIGroupNoteDeadlineNotSet" = "未设置";
"TUIGroupNoteNotifySettings" = "有新提交时在群里发通知";
"TUIGroupNoteSend" = "发送";
"TUIGroupNoteNow" = "立即接龙";
"TUIGroupNoteMessageTitle" = "群接龙";
"TUIGroupNoteMessageTitleInfo" = "由%@发起,已有%d人参加";
"TUIGroupNoteTipsMessagePrefix" = "%@已参加群接龙";
"TUIGroupNoteHasStopped" = "已停止接龙";
"TUIGroupNoteDeadlineMustBeLater" = "截止时间不能早于当前时间";
"TUIGroupNoteDisplayString" = "[群接龙]";
"TUIGroupNoteSendSucceed" = "发送成功";
"TUIGroupNoteSendFail" = "发送失败";
"TUIGroupNoteSubmitExceedLimit" = "接龙数量超过上限";
"TUIGroupNoteTextFieldExceedLimit" = "超过最大字符数 50";
"TUIGroupNoteSubmitOnce" = "只允许提交一次";
"TUIGroupNoteSubmitNotAllowed" = "不允许提交";
"TUIGroupNoteExceedDeadline" = "超过截止时间,已停止接龙";
"TUIGroupNoteNotChanged" = "接龙信息没有变化";
"TUIGroupNoteExpand" = "...展开...";
"TUIGroupNoteNotDatePickerConfirm" = "确定";
"TUIGroupNoteNotDatePickerCancel" = "取消";
"TUIGroupNoteNotDatePickerDone" = "完成";
"TUIGroupNoteNotDatePickerYear" = " 年";
"TUIGroupNoteNotDatePickerMonth" = " 月";
"TUIGroupNoteNotDatePickerDay" = " 日";
"TUIGroupNoteNotDatePickerHour" = " 时";
"TUIGroupNoteNotDatePickerMinute" = " 分";
"TUIGroupNoteNotDatePickerSecond" = " 秒";
"TUIGroupNoteNotDatePickerAM" = "上午";
"TUIGroupNoteNotDatePickerPM" = "下午";
"TUIGroupNoteNotDatePickerNow" = " 至今";
"TUIGroupNoteNotDatePickerToday" = " 今天";
"TUIGroupNoteNotDatePickerMon" = " 周一";
"TUIGroupNoteNotDatePickerTue" = " 周二";
"TUIGroupNoteNotDatePickerWed" = " 周三";
"TUIGroupNoteNotDatePickerTHR" = " 周四";
"TUIGroupNoteNotDatePickerFRI" = " 周五";
"TUIGroupNoteNotDatePickerSAT" = " 周六";
"TUIGroupNoteNotDatePickerSUN" = " 周日";
"TUICustomerServiceAccounts" = "客服号";
"TUICustomerServiceBranchMessage" = "[分支消息]";
"TUICustomerServiceCardMessage" = "[卡片消息]";
"TUICustomerServiceCollectInfomation" = "[收集信息]";
"TUICustomerServiceSatisfactionEvaluation" = "[满意度评价]";
"TUICustomerServiceTimeout" = "[客服会话已超时,自动结束]";
"TUICustomerServiceEnd" = "[客服会话已结束]";
"TUICustomerServiceFillProductInfo" = "请填写商品信息";
"TUICustomerServiceSubmitProductInfo" = "提交商品信息";
"TUICustomerServiceClose" = "关闭";
"TUICustomerServiceName" = "名称";
"TUICustomerServiceFillProductName" = "请填写产品名称";
"TUICustomerServiceDesc" = "描述";
"TUICustomerServiceFillProductDesc" = "请填写相关描述";
"TUICustomerServicePic" = "图片";
"TUICustomerServiceFillPicLink" = "请填写图片链接";
"TUICustomerServiceJumpLink" = "跳转";
"TUICustomerServiceFillJumpLink" = "请填写卡片跳转链接";
"TUICustomerServiceSubmitEvaluation" = "提交评价";
"TUICustomerService" = "客服插件";
"TUICustomerServiceSendProduct" = "发送商品";
"TUICustomerServiceCommonPhrase" = "常用语";
"TUICustomerServiceCommonPhraseStock" = "是否有货?";
"TUICustomerServiceCommonPhraseCheaper" = "可以便宜一点吗?";
"TUICustomerServiceCommonPhraseGift" = "有什么赠品?";
"TUICustomerServiceCommonPhraseShipping" = "什么时候发货?";
"TUICustomerServiceCommonPhraseDelivery" = "发什么快递?";
"TUICustomerServiceCommonPhraseArrive" = "什么时候到货?";
"TUIChatBotAccounts" = "机器人";
"TUIChatBotChangeQuestion" = "换一换";
"TUIConversationGroupAll" = "全部";
"TUIConversationGroupUnread" = "未读";
"TUIConversationGroupAtMe" = "@ 我";
"TUIConversationGroupGroup" = "群聊";
"TUIConversationGroupC2C" = "单聊";
"TUIConversationGroupInputGroupName" = "请输入分组名";
"TUIConversationGroupCreateGroup" = "创建会话分组";
"TUIConversationGroupNewGroup" = "新增分组";
"TUIConversationGroupAddGroup" = "添加分组";
"TUIConversationGroupMoveGroup" = "移动分组";
"TUIConversationGroupRemoveGroup" = "移除分组";
"TUIConversationGroupShowGroup" = "展示会话分组";
"TUIConversationGroupHideGroup" = "隐藏会话分组";
"TUIConversationGroupEditGroup" = "会话分组设置";
"TUIConversationGroupEdit" = "编辑";
"TUIConversationGroupSave" = "保存";
"TUIConversationGroupSaveSucc" = "分组保存成功";
"TUIConversationGroupCreate" = "创建";
"TUIConversationGroupNameIsNull" = "分组名不能为空!";
"TUIConversationGroupNameIsInvalid" = "分组名不能是预置的默认分组名";
"TUIConversationGroupConversationIsNull" = "会话不能为空!";
"TUIConversationGroupCreateSucc" = "分组创建成功";
"TUIConversationGroupModifySucc" = "分组修改成功";
"TUIConversationGroupName" = "分组名称";
"TUIConversationGroupIncludeConversation" = "包含会话";
"TUIConversationGroupAddConversation" = "添加会话";
"TUIConversationGroup" = "会话分组";
"TUIConversationGroupManager" = "管理";
"TUIConversationGroupSelectGroup" = "选择会话分组";
"TUIConversationGroupUnlogined" = "未登录,保存失败!";
"TUIConversationMark" = "标记";
"TUIConversationMarkCancel" = "取消标记";
"TUIConversationNone" = "暂无%@的会话";
//----timApp---
"TIMAppMainTitle" = "腾讯云·IM";
"TIMAppMainConnectingTitle" = "连接中...";
"TIMAppMainDisconnectTitle" = "腾讯云·IM(未连接)";
"TIMAppTabBarItemMessageText" = "消息";
"TIMAppTabBarItemContactText" = "通讯录";
"TIMAppTabBarItemMeText" = "设置";
"TIMAppTabBarItemMessageText_mini" = "消息";
"TIMAppTabBarItemContactText_mini" = "联系人";
"TIMAppTabBarItemSettingText_mini" = "设置";
"TIMAppTabBarItemCallsRecordText_mini" = "通话记录";
"TIMAppChat" = "聊天";
"TIMAppChatDisconnectTitle" = "聊天(未连接)";
"TIMAppChatSecurityWarning" = "请勿轻信汇款、中奖等信息,谨慎对待陌生电话,避免上当受骗。遇到可疑情况请及时反馈。";
"TIMAppChatSecurityWarningReport" = "点此举报";
"TIMAppChatSecurityWarningGot" = "我知道了";
"TIMAppTencentCloudIM" = "即时通信";
"TIMAppWelcomeToChat" = "欢迎使用本 APP 体验即时通信 IM 产品服务";
"TIMAppWelcomeToChatDetails" = "欢迎使用本 APP 体验即时通信 IM 产品服务,如发送文字/表情/富媒体消息、消息回复、表情回应、消息阅读状态、好友关系链等功能。同时我们为您默认提供了3个不会回复的机器人好友您可直接发起单聊、群聊会话体验群管理等功能。";
"TIMAppSelectStyle" = "选择风格";
"TIMAppChatStyles" = "IM 专属风格切换";
"TIMAppChangeTheme" = "选择皮肤";
"TIMAppChatThemes" = "IM 专属皮肤切换";
"TIMAppEnterChat" = "进入 IM";
"TIMAppRunDemo" = "改2行代码1分钟跑通 Demo";
"TIMAppCustomers" = "每月服务客户数超过10000家";
"TIMAppMessagingSuccess" = "消息收发成功率、服务可靠性高于99.99%";
"TIMAppActiveUsers" = "每月活跃用户数超过10亿";
"TIMAppOK" = "已了解";
"TIMApp1Billion+" = "10亿+";
"TIMApp10000+" = "10000+";
"TIMApp1Minute" = "1分钟";
"TIMAppMeAbout" = "关于腾讯云·IM";
"TIMAppConfirmLogout" = "确定退出吗";
"TIMAppConfirm" = "确定";
"TIMAppCancel" = "取消";
"TIMAppMarkAllMessageAsReadSucc" = "清空未读成功";
"TIMAppMarkAllMessageAsReadErrFormat" = "清空未读失败, %d, %@";
"TIMChangeLanguage" = "切换语言";
//----timApp---
"TUIKitTickledMe" = "我";
"TUIKitTickledActionVaule" = "拍了拍";
"TUIKitTickledMyself" = "自己";
"TUIKitImageViewOrigin" = "查看原图";
"TUIGroupCreateTipsMessage" = "创建群组";
"TUICommunityCreateTipsMessage" = "创建社群";
"TUICommunityCreateTipsMessageRuleError" = "社群 ID必须以 @TGS#_ 开头";
"TUIGroupCreateTipsMessageRuleError" = "非社群,不能以 @TGS#_ 开头";
//languageID
"zh-Hans" = "简体中文";
"en" = "英语";
"ar" = "阿拉伯语";
"zh-Hant" = "繁体中文";
"TUIChatFaceGroupAllEmojiName" = "所有表情";
"TUIChatFaceGroupRecentEmojiName" = "最近使用";

View File

@@ -0,0 +1,811 @@
/*
Localizable.strings
Chinese Simplized
Created by harvy on 2024/10/9.
*/
/***************************** 日期格式化 Start *************************/
"YesterdayDateFormat" = "aHH:mm";
/***************************** 日期格式化 End *************************/
"TUIKitTipsMostSelectTextFormat" = "最多選擇%ld個";
"Confirm" = "確定";
"Cancel" = "取消";
"Send" = "發送";
"Save" = "保存";
"You" = "您";
"you" = "您";
"Male" = "男";
"Female" = "女";
"File" = "文件";
"Download" = "下載";
"Unsetted" = "未設置";
"Show" = "展示";
"Monday" = "星期一";
"Tuesday" = "星期二";
"Wednesday" = "星期三";
"Thursday" = "星期四";
"Friday" = "星期五";
"Saturday" = "星期六";
"Sunday" = "星期日";
"Yesterday" = "昨天";
"am" = "上午";
"pm" = "下午";
"Read" = "已讀";
"Unread" = "未讀";
"Copy" = "複製";
"Delete" = "刪除";
"Revoke" = "撤回";
"Retry" = "重試";
"Re_send" = "重發";
"Make_a_call" = "發起呼叫";
"Done" = "完成";
"All" = "所有人";
"Agreed" = "已同意";
"Disclined" = "已拒絕";
"Agree" = "同意";
"Have_been_sent" = "已發送";
"OK" = "好";
"None" = "無";
"Send" = "發送";
"Cancel" = "取消";
"Alia" = "備註";
"Group" = "分組";
"Accept" = "同意";
"Decline" = "拒絕";
"send_success" = "發送成功";
"add_success" = "添加成功";
"delete_success" = "刪除成功";
"my_friend" = "我的朋友";
"no_personal_signature" = "暫無個性簽名";
"SignatureFormat" = "個性簽名:%@";
"choose_avatar_for_you" = "App隨機為您分配一個系統頭像";
"SearchGroupPlaceholder" = "用戶ID";
"please_fill_in_verification_information" = "請填寫驗證信息";
"please_fill_in_remarks_group_info" = "請填寫備註";
"no_set" = "未設置";
"no_personal_signature" = "暫無個性簽名";
"logout" = "退出登錄";
"login" = "登錄";
"ReadAll" = "全部已讀";
"Hide" = "隱藏";
"Delete" = "刪除";
"ContactsJoinGroup" = "添加群聊";
"ContactsAddFriends" = "添加好友";
"ChatsNewChatText" = "發起會話";
"ChatsNewGroupText" = "創建群聊";
"ChatsSelectContact" = "選擇聯繫人";
"GroupJoin" = "添加群組";
"GroupRequestJoinGroupFormat" = "%@ 申請加入群聊";
"GroupDeleteFriend" = "刪除聯繫人";
"GroupAddFirend" = "添加聯繫人";
"GroupMember" = "群成員";
"ProfileAlia" = "備註名";
"ProfileMessageDoNotDisturb" = "消息免打擾";
"ProfileStickyonTop" = "置頂聊天";
"ProfileBlocked" = "加入黑名單";
"ProfileSendMessages" = "發送消息";
"ProfileDeleteFirend" = "刪除好友";
"ProfileEditAlia" = "修改備註";
"ProfileDetails" = "詳細資料";
"ProfileSetBackgroundImage" = "設置當前聊天背景";
"ProfilePhoto" = "頭像";
"ProfileName" = "昵稱";
"ProfileAccount" = "帳號";
"ProfileSignature" = "個性簽名";
"ProfileGender" = "性別";
"ProfileEditName" = "修改昵稱";
"ProfileEditNameDesc" = "僅限中文、字母、數字和下劃線";
"ProfileEditSignture" = "修改個性簽名";
"ProfileEditGender" = "修改性別";
"ProfileEditAlia" = "修改備註";
"ProfileEdit" = "編輯";
"ProfileAlia" = "備註名";
"ProfileBlocked" = "加入黑名單";
"ProfileMessageDoNotDisturb" = "消息免打擾";
"ProfileStickyonTop" = "置頂聊天";
"ProfileSendMessages" = "發送消息";
"ProfileDeleteFirend" = "刪除好友";
"ProfileBirthday" = "生日";
"SignatureFormat" = "個性簽名:%@";
"FriendRequestFormat" = "我是%@";
"FriendRequestFillInfo" = "添加好友";
"FriendOneWay" = "單向好友";
"FriendAddTitle" = "加好友";
"FriendAddVerificationMessage" = "驗證消息";
"FriendAddResultSuccessWait" = "發送成功,等待審核同意";
"FriendAddResultForbid" = "對方禁止添加";
"FriendAddResultSuccess" = "已添加到好友列表";
"FriendAddResultExists" = "對方已是您的好友";
"MeFriendRequest" = "加我為好友時";
"MeFriendRequestMethodAgreeAll" = "同意任何用戶加好友";
"MeFriendRequestMethodNeedConfirm" = "需要驗證";
"MeFriendRequestMethodDenyAll" = "拒絕任何人加好友";
"MeMessageReadStatus" = "消息閱讀狀態";
"MeMessageReadStatusOpenDesc" = "關閉後,您收發的消息均不帶消息閱讀狀態,您將無法看到對方是否已讀,同時對方也無法看到您是否已讀。";
"MeMessageReadStatusCloseDesc" = "開啟後,您在群聊中收發的消息均帶有消息閱讀狀態,並且可以看到對方是否已讀。與您單聊的好友若也開啟了消息閱讀狀態,您與好友在單聊中收發的消息也將帶有消息閱讀狀態。";
"ShowOnlineStatus" = "顯示在線狀態";
"ShowOnlineStatusOpenDesc" = "關閉後,您將不可以在會話列表和通訊錄中看到好友在線或離線的狀態提示。";
"ShowOnlineStatusCloseDesc" = "開啟後,您將可以在會話列表和通訊錄中看到好友在線或離線的狀態提示。";
"ShowPackageToast" = "該功能為旗艦版功能";
"ShowCallsRecord" = "顯示通話記錄";
"TUIKitClassic" = "經典版";
"TUIKitMinimalist" = "簡約版";
"TUIKitThemeNameSystemFollowTitle" = "跟隨系統自動切換";
"TUIKitThemeNameSystemFollowSubTitle" = "開啟後僅展示默認皮膚 (輕快/深色),不可切換其他皮膚樣式";
"TUIKitThemeNameSerious" = "深沉";
"TUIKitThemeNameLight" = "輕量";
"TUIKitThemeNameLivey" = "活潑";
"TUIKitThemeNameDark" = "深色";
"TUIKitThemeNameSystem" = "跟隨系統";
"TUIKitDone" = "完成";
"TUIKitWelcome" = "歡迎加入騰訊雲 IM 大家庭!";
"TUIKitMicCamerAuthTips" = "請開啟麥克風和攝像頭權限";
"TUIKitMicAuth" = "請開啟麥克風權限";
"TUIKitTipsConfirmResendMessage" = "確定重發此消息嗎? ";
"TUIKitTipsSystemError" = "系統錯誤";
"TUIKitTipsEnterRoomErrorFormat" = "進房失敗: %d";
"TUIKitWhoRequestForJoinGroupFormat" = "%@申請加入群聊";
"TUIKitInviteJoinGroupFormat" = "邀請%@加入群聊";
"TUIKitAgreedByAdministor" = "管理員同意申請";
"TUIkitDiscliedByAdministor" = "管理員拒絕申請";
"TUIKitDownloadProgressFormat" = "正在下載%d%%";
"TUIKitOpenWithOtherApp" = "用其他應用程序打開";
"TUIKitTipsContactListNil" = "聯繫人列表為空,請先添加好友";
"TUIKitUserStatusUnknown" = "未知";
"TUIKitUserStatusOnline" = "在線";
"TUIKitUserStatusOffline" = "離線";
"TUIKitUserStatusUnlogined" = "未登錄";
"TUIKitInputHoldToTalk" = "按住 說話";
"TUIKitInputReleaseToSend" = "鬆開 結束";
"TUIKitInputReleaseToCancel" = "鬆開 取消";
"TUIKitInputBlankMessageTitle" = "不能發送空白消息";
"TUIKitInputWillFinishRecordInSeconds" = "將在 %ld 秒後結束錄製";
"TUIKitInputRecordSlideToCancel" = "手指上滑,取消發送";
"TUIKitInputRecordReleaseToCancel" = "鬆開手指,取消發送";
"TUIKitInputRecordTimeshort" = "說話時間太短";
"TUIKitInputRecordTimeLong" = "說話時間太長";
"TUIKitGroupProfileDetails" = "詳細資料";
"TUIKitGroupProfileMember" = "群成員";
"TUIKitGroupProfileMemberCount" = "%d 人";
"TUIKitGroupProfileMemberCountlu" = "%lu人";
"TUIKitGroupProfileType" = "群類型";
"TUIKitGroupProfileJoinType" = "主動加群方式";
"TUIKitGroupProfileInviteType" = "邀請進群方式";
"TUIKitGroupProfileInviteJoin" = "邀請加入";
"TUIKitGroupProfileAutoApproval" = "自動審批";
"TUIKitGroupProfileAlias" = "我的群昵稱";
"TUIKitGroupProfileMessageDoNotDisturb" = "消息免打擾";
"TUIKitGroupProfileStickyOnTop" = "置頂聊天";
"TUIKitGroupProfileDeleteAndExit" = "刪除並退出";
"TUIKitGroupProfileReport" = "舉報";
"TUIKitGroupProfileDissolve" = "解散該群";
"TUIKitGroupProfileJoinDisable" = "禁止加群";
"TUIKitGroupProfileInviteDisable" = "禁止邀請";
"TUIKitGroupProfileAdminApprove" = "管理員審批";
"TUIKitGroupProfileEditAlias" = "修改我的群昵稱";
"TUIKitGroupProfileEditAliasDesc" = "僅限中文、字母、數字和下劃線";
"TUIKitGroupProfileEditGroupName" = "修改群名稱";
"TUIKitGroupProfileEditAnnouncement" = "修改群公告";
"TUIKitGroupProfileEditAvatar" = "修改群頭像";
"TUIKitGroupProfileDeleteGroupTips" = "退出後不會再接收到此群聊消息";
"TUIKitGroupProfileGroupCountFormat" = "群成員(%ld人)";
"TUIKitGroupProfileManage" = "管理";
"TUIKitGroupProfileManageAdd" = "添加成員";
"TUIKitGroupProfileManageDelete" = "刪除成員";
"TUIKitGroupProfileAdmainAdd" = "設置為管理員";
"TUIKitGroupProfileAdmainDelete" = "取消管理員";
"TUIKitGroupTransferOwner" = "轉讓群主";
"TUIKitGroupTransferOwnerSuccess" = "轉讓群主成功";
"TUIKitGroupApplicant" = "群申請";
"TUIKitGroupDismssTipsFormat" = "%@ 群已解散";
"TUIKitGroupRecycledTipsFormat" = "%@ 群已回收";
"TUIKitGroupKickOffTipsFormat" = "您已被踢出 %@ 群";
"TUIKitGroupDropoutTipsFormat" = "您已退出 %@ 群";
"TUIKitGroupMessagePinRemove" = "移除";
"TUIKitMessageTipsNormalRecallMessage" = "有一條消息被撤回";
"TUIKitMessageTipsYouRecallMessage" = "您撤回了一條消息";
"TUIKitMessageTipsReEditMessage" = "重新編輯";
"TUIKitMessageTipsRecallMessageFormat" = "\"%@\"撤回了一條消息";
"TUIKitMessageTipsOthersRecallMessage" = "對方撤回了一條消息";
"TUIKitMessageTipsJoinGroupFormat" = "\"%@\"加入群組";
"TUIKitMessageTipsInviteJoinGroupFormat" = "\"%@\"邀請\"%@\"加入群組";
"TUIKitMessageTipsLeaveGroupFormat" = "\"%@\"退出了群聊";
"TUIKitMessageTipsKickoffGroupFormat" = "\"%@\"將\"%@\"踢出群組";
"TUIKitMessageTipsSettAdminFormat" = "\"%@\"被設置管理員";
"TUIKitMessageTipsCancelAdminFormat" = "\"%@\"被取消管理員";
"TUIkitMessageTipsEditGroupNameFormat" = "%@修改群名為\"%@\"、";
"TUIKitMessageTipsEditGroupIntroFormat" = "%@修改群簡介為\"%@\"、";
"TUIKitMessageTipsEditGroupAnnounceFormat" = "%@修改群公告為\"%@\"、";
"TUIKitMessageTipsDeleteGroupAnnounceFormat" = "%@刪除了群公告、";
"TUIKitMessageTipsEditGroupAvatarFormat" = "%@修改群頭像、";
"TUIKitMessageTipsEditGroupOwnerFormat" = "%@修改群主為\"%@\"、";
"TUIKitMessageTipsCreateGroupFormat" = "\"%@\"創建群組";
"TUIKitMessageTipsUnsupportCustomMessage" = "[自定義消息]";
"TUIKitMessageTipsMute" = "被禁言";
"TUIKitMessageTipsUnmute" = "被解除禁言";
"TUIKitMessageTipsRecallRiskContent" = "違規消息被撤回";
"TUIKitMessageDisplayRiskContent" = "[違規消息]";
"TUIKitMessageTipsGroupPinMessage" = "\"%@\"置頂了一條消息";
"TUIKitMessageTipsGroupUnPinMessage" = "\"%@\"移除了一條置頂消息";
"TUIKitSignalingFinishGroupChat" = "結束群聊";
"TUIKitSignalingFinishConversationAndTimeFormat" = "通話時長";
"TUIKitSignalingNewCall" = "發起通話";
"TUIKitSignalingNewGroupCallFormat" = "\"%@\" 發起群通話";
"TUIkitSignalingCancelCall" = "取消通話";
"TUIkitSignalingCancelGroupCallFormat" = "\"%@\" 取消群通話";
"TUIkitSignalingHangonCall" = "已接聽";
"TUIKitSignalingHangonCallFormat" = "\"%@\" 已接聽";
"TUIKitSignalingBusyFormat" = "\"%@\" 忙線";
"TUIKitSignalingDeclineFormat" = "\"%@\" 拒絕通話";
"TUIKitSignalingCallBusy" = "對方忙線";
"TUIkitSignalingDecline" = "拒絕通話";
"TUIKitSignalingNoResponse" = "無應答";
"TUIkitSignalingUnrecognlize" = "不能識別的通話指令";
"TUIkitMessageTypeImage" = "[圖片]";
"TUIKitMessageTypeVoice" = "[語音]";
"TUIkitMessageTypeVideo" = "[視頻]";
"TUIkitMessageTypeFile" = "[文件]";
"TUIKitMessageTypeAnimateEmoji" = "[動畫表情]";
"TUIKitMessageTypeDraftFormat" = "[草稿] ";
"TUIkitMessageTypeRiskImage" = "[違規圖片]";
"TUIkitMessageTypeRiskVoice" = "[違規語音]";
"TUIkitMessageTypeRiskVideo" = "[違規視頻]";
"TUIKitMessageTypeLastMsgCountFormat" = "條";
"TUIKitMessageTypeSecurityStrike" = "涉及敏感內容,發送失敗";
"TUIKitMessageTypeSecurityStrikeVoice" = "涉及敏感內容,無法收聽";
"TUIKitMessageTypeSecurityStrikeImage" = "涉及敏感內容,無法查看";
"TUIKitMessageTypeSecurityStrikeTranslate" = "涉及敏感內容,翻譯失敗";
"TUIKitMessageTypeOtherUseMic" = "其他功能正在使用麥克風,無法錄製";
"TUIKitMessageReadPartRead" = "人已讀";
"TUIKitMessageReadPartUnread" = "人未讀";
"TUIKitMessageReadPartDisable" = "人無狀態";
"TUIKitMessageReadAllRead" = "全部已讀";
"TUIKitMessageReadC2CRead" = "已讀";
"TUIKitMessageReadC2CUnRead" = "未讀";
"TUIKitMessageReadC2CUnReadDetail" = "尚未閱讀";
"TUIKitMessageReadDetail" = "訊息詳情";
"TUIKitMessageReadNoMoreData" = "沒有更多數據了";
"TUIKitMessageReadGetReadMembersFail" = "獲取已讀成員失敗";
"TUIKitMessageReadGetUnreadMembersFail" = "獲取未讀成員失敗";
"TUIKitMoreCamera" = "拍照";
"TUIKitMorePhoto" = "圖片";
"TUIKitMoreVideo" = "錄像";
"TUIKitMoreVideoCaptureDurationTip" = "錄製時間過短";
"TUIKitMoreFile" = "文件";
"TUIKitMoreVideoCall" = "視頻通話";
"TUIKitMoreVoiceCall" = "語音通話";
"TUIKitMoreLink" = "自定義";
"TUIKitMoreLinkDetails" = "查看詳情>>";
"TUIKitMorePoll" = "投票";
"TUIKitMoreGroupNote" = "群接龍";
"TUIKitMoreEvaluation" = "服務評價";
"TUIKitMoreCard" = "卡片訊息";
"TUIKitFileSizeCheckLimited" = "文件大小超出限制";
"TUIKitImageSizeCheckLimited" = "圖片大小超出限制";
"TUIKitCallInviteYouVideoCall" = "邀請你視頻通話";
"TUIKitCallTurningOnMute" = "開啟靜音";
"TUIKitCallTurningOffMute" = "關閉靜音";
"TUIKitCallUsingSpeaker" = "使用揚聲器";
"TUIKitCallUsingHeadphone" = "使用聽筒";
"TUIKitCallCancelCallingFormat" = "%@ 取消了通話";
"TUIKitAtSelectMemberTitle" = "選擇群成員";
"TUIKitConversationTipsAtMe" = "[有人@我]";
"TUIKitConversationTipsAtAll" = "[@所有人]";
"TUIKitConversationTipsAtMeAndAll" = "[有人@我][@所有人]";
"TUIKitConversationTipsDelete" = "此會話及聊天記錄將全部被刪除";
"TUIKitPublicGroup" = "公開群";
"TUIKitWorkGroup" = "討論組";
"TUIKitChatRoom" = "聊天室";
"TUIKitCommunity" = "社群";
"TUIKitContactsNewFriends" = "新的聯繫人";
"TUIKitContactsGroupChats" = "群聊";
"TUIKitContactsBlackList" = "黑名單";
"TUIKitAddFriendSourceFormat" = "來源: %@";
"TUIKitFriendApplicationApproved" = "已同意好友申請";
"TUIKitFirendRequestRejected" = "已拒絕好友申請";
"TUIKitOfflinePushCallTips" = "您有一個通話請求";
"TUIKitChatTap2Remove" = "點擊刪除";
"TUIKitChatPendencyTitle" = "點擊處理";
"TUIKitChatPendencyRequestToJoinGroupFormat" = "%@條入群請求";
"TUIKitChatTipsAtMe" = "有人@我";
"TUIKitChatBackToLatestLocation" = "回到最新位置";
"TUIKitChatNewMessages" = "%@條新訊息";
"TUIKitAllowTypeAcceptOne" = "同意任何用戶加好友";
"TUIKitAllowTypeNeedConfirm" = "需要驗證";
"TUIKitAllowTypeDeclineAll" = "拒絕任何人加好友";
/***************************** 消息转发 & 消息搜索 *************************/
// 补充
"Multiple" = "多選";
"Forward" = "轉發";
"Delete" = "刪除";
"Copy" = "複製";
"Revoke" = "撤回";
"Resend" = "重發";
"Search" = "搜尋";
"Recall" = "撤回";
"MultiSelect" = "多選";
"Quote" = "引用";
"TUIKitCallMicCamAuthTips" = "請開啟麥克風和攝像頭權限";
"TUIKitCallMicAuthTips" = "請開啟麥克風權限";
"TUIKitMessageTipsSureToResend" = "確定重發訊息?";
// 訊息轉發
"TUIKitRelayNoMessageTips" = "請選擇訊息";
"TUIKitRelayRecentMessages" = "最近聊天";
"TUIKitRelayChatHistory" = "聊天記錄";
"TUIKitRelaySepcialForbid" = "選中的訊息中,語音訊息等其他訊息不能被轉發.";
"TUIKitRelayConfirmForward" = "確定轉發?";
"TUIKitRelayOneByOneForward" = "逐條轉發";
"TUIKitRelayCombineForwad" = "合併轉發";
"TUIKitRelayGroupChatHistory" = "群聊的聊天記錄";
"TUIKitRelayChatHistoryForSomebodyFormat" = "%@和%@的聊天記錄";
"TUIKitRelayErrorForwardFormat" = "@\"轉發失敗, code:%d, desc:%@\"";
"TUIKitRelayTargetCreateNewChat" = "創建新聊天";
"TUIKitRelayTargetCreateNewGroup" = "創建新群聊";
"TUIKitRelayTargetSelectFromContacts" = "從聯繫人中選擇";
"TUIKitRelayTargetCrateGroupError" = "創建群失敗";
"TUIKitRelayTargetNoneTips" = "請選擇聯繫人或會話";
"TUIKitRelayLayerLimitTips" = "抱歉,當前嵌套層級超過了限制";
"TUIKitRelayCompatibleText" = "不支持合併轉發訊息,請升級到最新版本。";
"TUIKitRelayUnsupportForward" = "發送失敗的訊息不支持轉發";
"TUIKitRelayOneByOnyOverLimit" = "轉發訊息過多,暫不支持逐條轉發";
"TUIKitRelayPluginNotAllowed" = "投票/接龍訊息不支持轉發";
// 訊息搜尋
"TUIKitSearchItemHeaderTitleContact" = "聯繫人";
"TUIKitSearchItemFooterTitleContact" = "查看更多聯繫人";
"TUIKitSearchItemHeaderTitleGroup" = "群聊";
"TUIKitSearchItemFooterTitleGroup" = "查看更多群聊";
"TUIkitSearchItemHeaderTitleChatHistory" = "聊天記錄";
"TUIKitSearchItemFooterTitleChatHistory" = "查看更多聊天記錄";
"TUIKitSearchResultMatchFormat" = "包含:%@";
"TUIKitSearchResultMatchGroupIDFormat" = "包含群ID:%@";
"TUIKitSearchResultMatchGroupMember" = "包含群成員:";
"TUIKitSearchResultDisplayChatHistoryCountFormat" = "%zd條聊天記錄";
/***************************** 消息转发 & 消息搜索 *************************/
/***************************** 消息回复 & 换肤 *************************/
"Reply" = "回覆";
"TUIKitReplyMessageNotFoundOriginMessage" = "無法定位到原訊息";
"TUIKitClearAllChatHistory" = "清空聊天記錄";
"TUIKitClearAllChatHistoryTips" = "確定清空聊天記錄?";
"Discline" = "拒絕";
"Copied" = "已複製";
"ConfirmDeleteMessage" = "確定刪除已選訊息";
"TUIKitAddFriend" = "添加好友";
"TUIKitAddGroup" = "添加群組";
"TUIKitSearchUserID" = "搜尋用戶ID";
"TUIKitSearchGroupID" = "搜尋群ID";
"TUIKitNoSelfSignature" = "暫無個性簽名";
"TUIKitSelfSignatureFormat" = "個性簽名:%@";
"TUIKitGroupProfileManage" = "群管理";
"TUIKitGroupManageShutupAllTips" = "全員禁言開啟後,只允許群主和管理员發言。";
"TUIKitGroupManageAdminSetting" = "設置管理员";
"TUIKitGroupManageShutAll" = "全員禁言";
"TUIKitGroupAddShutupMember" = "添加需要禁言的群成員";
"TUIKitGroupShutupOption" = "禁言";
"TUIKitGroupDisShutupOption" = "解除禁言";
"TUIKitGroupOwner" = "群主";
"TUIKitGroupManagerFormat" = "管理员(%zd/%zd)";
"TUIKitSetShutupAllFormat" = "%@ 開啟全員禁言、";
"TUIKitCancelShutupAllFormat" = "%@ 取消全員禁言、";
"TUIKitGroupNotice" = "群公告";
"TUIKitGroupNoticeNull" = "暫未設置群公告";
"Edit" = "編輯";
"Done" = "完成";
"TUIKitAddContactMyUserIDFormat" = "我的用戶ID: %@";
"TUIKitAddUserNoDataTips" = "該用戶不存在";
"TUIKitAddGroupNoDataTips" = "該群聊不存在";
/***************************** 消息回复 & 换肤 *************************/
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitVideoTranscoding" = "視頻轉碼中...";
"TUIKitVideoDownloading" = "視頻下載中...";
"TUIKitVideoSavedSuccess" = "視頻保存成功";
"TUIKitVideoSavedFailed" = "視頻保存失敗";
"TUIKitPictureSavedSuccess" = "圖片保存成功";
"TUIKitPictureSavedFailed" = "圖片保存失敗";
"TUIKitPictureCheckRisk" = "檢測到圖片涉及敏感內容,暫時無法查看";
"TUIKitVideoCheckRisk" = "檢測到視頻涉及敏感內容,暫時無法查看";
"TUIKitVideoCheckRiskCancel" = "我知道了";
/***************************** 视频、图片加载 & 保存 *************************/
"TUIKitGroupSetAdminsForbidden" = "當前類型群聊暫不支持設置管理员";
"TUIKitGroupAddAdmins" = "添加管理员";
"TUIKitGroupAddMemberOfBlockedForbidden" = "當前類型群聊暫不支持禁言指定群成員";
"TUIKitNotDownload"= "未下載";
"TUIKitInputNoCameraTitle" = "相機權限未開啟";
"TUIKitInputNoCameraTips" = "無法使用拍攝圖片視頻、視頻通話等功能,點擊\"去開啟\"打開相機權限";
"TUIKitInputNoCameraOperateLater" = "以後再說";
"TUIKitInputNoCameraOperateEnable" = "去開啟";
"TUIKitInputNoMicTitle" = "麥克風權限未開啟";
"TUIKitInputNoMicTips" = "無法使用發送語音消息、拍攝視頻、音視頻通話等功能,點擊\"去開啟\"打開麥克風權限";
"TUIKitInputNoMicOperateLater" = "以後再說";
"TUIKitInputNoMicOperateEnable" = "去開啟";
"TUIKitInputNoPhotoTitle" = "圖片權限未開啟";
"TUIKitInputNoPhotoTips" = "無法使用聊天功能中的圖片查看、選擇等功能,點擊\"去開啟\"打開圖片權限";
"TUIKitInputNoPhotoOperateLater" = "以後再說";
"TUIKitInputNoPhotoerateEnable" = "去開啟";
"TUIKitInputRecordTipsTitle" = "鬆開手指發送,左滑取消發送";
"TUIKitInputRecordCancelTipsTitle" = "鬆開手指,取消發送";
"TUIKitInputRecordRejectedInCall" = "正在音視頻通話,請稍後再試";
"TUIKitInputRecordRejectedIsRecording" = "當前錄音未結束";
"StickyonTop" = "置頂";
"CancelStickonTop" = "取消置頂";
"ClearHistoryChatMessage" = "清空消息";
"MarkAsRead" = "標為已讀";
"MarkAsUnRead" = "標為未讀";
"MarkHide" = "不顯示";
"MinimizeGroup" = "會話折疊";
"More" = "更多";
"Info" = "詳情";
"TUIKitConversationMarkHide" = "會話隱藏";
"TUIKitConversationMarkHideDescriptionOn" = "開啟後,此會話將不會出現在會話列表中,聊天記錄仍會被保留。";
"TUIKitConversationMarkHideDescriptionOff" = "關閉後,此會話將出現在會話列表中,聊天記錄仍會被保留。";
"TUIKitConversationMarkFoldGroups" = "折疊的群聊";
"TUIKitConversationMarkFold" = "折疊該群聊";
"TUIKitRepliesDetailTitle" = "回覆詳情";
"TUIKitRepliesNum" = "條回覆";
"TUIKitReference" = "引用";
"TUIKitNotSupportThisMessage" = "[暫時不支持該消息展示]";
"TUIKitRepliesOriginMessageRevoke" = "回覆內容已撤回";
"TUIKitReferenceOriginMessageRevoke" = "引用內容已撤回";
"TUIKitAudioPlaybackStyleLoudspeaker" = "揚聲器";
"TUIKitAudioPlaybackStyleHandset" = "聽筒";
"TUIKitAudioPlaybackStyleChange2Loudspeaker" = "切換至揚聲器播放";
"TUIKitAudioPlaybackStyleChange2Handset" = "切換至聽筒播放";
"TUIKitGroupMessagePin" = "置頂";
"TUIKitGroupMessageUnPin" = "取消置頂";
"TUIKitGroupMessagePinOverLimit" = "置頂消息超過數量上限";
"TUIKitGroupMessagePinRepeatedly" = "這條消息已經被置頂了";
"TUIKitGroupMessageUnPinRepeatedly" = "這條消息已經被取消置頂了";
"TUIKitTranslate" = "翻譯";
"TUIKitTranslateDefaultTips" = "由騰訊雲 IM 提供翻譯支持";
"TUIKitTranslateFailed" = "翻譯失敗";
"Hide" = "隱藏";
"TranslateMessage" = "將消息翻譯為";
"TranslateLanguageNotSupport" = "當前翻譯語言不支持,請切換至其他語言";
"TUIKitConvertToText" = "轉文字";
"TUIKitConvertToTextFailed" = "轉換失敗";
"TUIKitSignalingSwitchToAudio" = "視頻轉語音";
"TUIKitSignalingUnsupportedCalling" = "未知邀請";
"TUIKitSignalingComfirmSwitchToAudio" = "確認轉語音";
"TUIKitErrorEnableUserStatusOnConsole" = "您未開啟用戶狀態功能,請先登錄控制台開啟";
"TUIKitChooseAvatar" = "選擇頭像";
"TUIKitChooseCover" = "選擇封面";
"TUIKitChooseBackground" = "更換背景";
"TUIKitDefaultBackground" = "默認背景";
"TUIKitIdentity" = "ID";
"TUIKitChooseBackgroundSuccess" = "設置成功!";
"TUIKitChooseBackgroundFailed" = "設置失敗!";
"TUIKitContactNoNewApplicationRequest" = "暫無新的聯繫人好友";
"TUIKitContactNoGroupChats" = "暫無群聊";
"TUIKitContactNoBlockList" = "暫無黑名單好友";
"TUIKitSearchNoResultLists" = "無相關會話、聯繫人或消息記錄";
"TUIKitTyping" = "對方正在輸入";
"TUIKitMessageTipsEditGroupAddOptFormat" = "%@修改加群方式為\"%@\"、";
"TUIKitMessageTipsEditGroupInviteOptFormat" = "%@修改邀請進群方式為\"%@\"、";
"TUIKitCreatGroupNamed" = "群名稱";
"TUIKitCreatGroupID" = "群ID選填";
"TUIKitCreatGroupNamed_Placeholder" = "輸入群名稱";
"TUIKitCreatGroupID_Placeholder" = "輸入群ID";
"TUIKitCreatGroupAvatar" = "群頭像";
"TUIKitCreatGroupType" = "群類型";
"TUIKitCreatGroupType_Work" = "好友工作群(Work)";
"TUIKitCreatGroupType_Public" = "陌生人社交群(Public";
"TUIKitCreatGroupType_Meeting" = "臨時會議群(Meeting";
"TUIKitCreatGroupType_Community" = "社群(Community)";
"TUIKitCreatGroupType_Work_Desc" = "好友工作群(Work類似普通微信群創建後僅支持已在群內的好友邀請加群且無需被邀請方同意或群主審批。";
"TUIKitCreatGroupType_Public_Desc" = "陌生人社交群(Public類似 QQ 群,創建後群主可以指定群管理員,用戶搜索群 ID 發起加群申請後,需要群主或管理員審批通過才能入群。";
"TUIKitCreatGroupType_Meeting_Desc" = "臨時會議群(Meeting創建後可以隨意進出且支持查看入群前消息適用於音視頻會議場景、在線教育場景等與實時音視頻產品結合的場景。";
"TUIKitCreatGroupType_Community_Desc" = "社群(Community)創建後可以隨意進出最多支持100000人支持歷史消息存儲用戶搜索群 ID 發起加群申請後,無需管理員審批即可進群。";
"TUIKitCreatGroupType_See_Doc" ="詳見產品文檔";
"TUIKitCreatGroupType_Desc_Highlight" = "產品文檔";
"TUIKitCreatGroupType_See_Doc_Simple" = "查看產品文檔";
"TUIKitSearchItemCancel" = "取消";
//----mini---
"Pin" = "置頂";
"UnPin" = "取消置頂";
"TUIKitSelected" = "條選中";
"TUIKitThreadQuote" = "條回覆";
"MessageInfo" = "消息詳情";
"C2CReadBy" = "已讀";
"C2CDeliveredTo" = "未讀";
"GroupReadBy" = "已讀列表";
"GroupDeliveredTo" = "未讀列表";
"TUIKitMessage" = "發送消息";
"TUIKitAudio" = "語音通話";
"TUIKitVideo" = "視頻通話";
"TUIKitAddMembers" = "+ 添加成員";
"TUIKitMembersRoleMember" = "成員";
"TUIKitMembersRoleAdmin" = "管理員";
"TUIKitMembersRoleSuper" = "群主";
"TUIKitCreateCancel" = "取消";
"TUIKitCreateNext" = "下一步";
"TUIKitCreateFinish" = "創建";
"TUIKitCreateMemebers" = "已選成員";
"TUIKitDateToday" = "今天";
//----mini---
"TUIPollCreateNew" = "發起投票";
"TUIPollCreateSucceed" = "創建成功";
"TUIPollCreateFail" = "創建失敗";
"TUIPollEnterTitle" = "請輸入投票主題";
"TUIPollEnterOption" = "請輸入選項";
"TUIPollAddOption" = "添加選項";
"TUIPollEnableMultiSelect" = "允許多選";
"TUIPollSingleSelect" = "單選";
"TUIPollMultiSelect" = "多選";
"TUIPollAnonymousVote" = "匿名投票";
"TUIPollPublic" = "公開結果";
"TUIPollNonPublic" = "不公開結果";
"TUIPollNonAnonymous" = "不匿名";
"TUIPollAnonymous" = "匿名";
"TUIPollEnableNotification" = "有新提交時在群里發通知";
"TUIPolllPublish" = "發布";
"TUIPollOneCount" = "票";
"TUIPollMoreCount" = "票";
"TUIPollInvitedParticipants" = "應參與人數:%d";
"TUIPollRespondedParticipants" = "實際參與人數:%d";
"TUIPollResend" = "再次發送";
"TUIPollResendFail" = "再次發送失敗,錯誤碼:%d";
"TUIPollEnd" = "結束投票";
"TUIPollEndFail" = "結束投票失敗,錯誤碼:%d";
"TUIPollVoteNow" = "立即投票";
"TUIPollConfirmVote" = "確定";
"TUIPollCancelVote" = "取消";
"TUIPollPercent" = "已投%d票占比%.0f%%";
"TUIPollEnded" = "投票已結束";
"TUIPollVoteSucceed" = "投票成功";
"TUIPollVoteFail" = "投票失敗";
"TUIPollPublicResult" = "公開結果";
"TUIPollViewAllOptions" = "查看全部選項";
"TUIPollDisplayString" = "[投票]";
"TUIPollTextFieldExceedLimit" = "超過最大字符數 50";
"TUIPollVoted" = "已投票";
"TUIPoll" = "投票";
"TUIGroupNote" = "接龍";
"TUIGroupNoteTitle" = "群接龍";
"TUIGroupNoteSettings" = "設置";
"TUIGroupNoteDeadline" = "截止時間 %@";
"TUIGroupNoteTitleHint" = "添加接龍主題";
"TUIGroupNoteDescriptionHint" = "添加接龍說明";
"TUIGroupNoteFormatExample" = "例";
"TUIGroupNoteFormatHint" = "可填寫接龍格式";
"TUIGroupNoteFillIn" = "請填寫";
"TUIGroupNoteMultipleSettings" = "允許每人提交多份";
"TUIGroupNoteDeadlineSettings" = "設置截止時間";
"TUIGroupNoteDeadlineNotSet" = "未設置";
"TUIGroupNoteNotifySettings" = "有新提交時在群里發通知";
"TUIGroupNoteSend" = "發送";
"TUIGroupNoteNow" = "立即接龍";
"TUIGroupNoteMessageTitle" = "群接龍";
"TUIGroupNoteMessageTitleInfo" = "由%@發起,已有%d人參加";
"TUIGroupNoteTipsMessagePrefix" = "%@已參加群接龍";
"TUIGroupNoteHasStopped" = "已停止接龍";
"TUIGroupNoteDeadlineMustBeLater" = "截止時間不能早於當前時間";
"TUIGroupNoteDisplayString" = "[群接龍]";
"TUIGroupNoteSendSucceed" = "發送成功";
"TUIGroupNoteSendFail" = "發送失敗";
"TUIGroupNoteSubmitExceedLimit" = "接龍數量超過上限";
"TUIGroupNoteTextFieldExceedLimit" = "超過最大字符數 50";
"TUIGroupNoteSubmitOnce" = "只允許提交一次";
"TUIGroupNoteSubmitNotAllowed" = "不允許提交";
"TUIGroupNoteExceedDeadline" = "超過截止時間,已停止接龍";
"TUIGroupNoteNotChanged" = "接龍信息沒有變化";
"TUIGroupNoteExpand" = "...展開...";
"TUIGroupNoteNotDatePickerConfirm" = "確定";
"TUIGroupNoteNotDatePickerCancel" = "取消";
"TUIGroupNoteNotDatePickerDone" = "完成";
"TUIGroupNoteNotDatePickerYear" = " 年";
"TUIGroupNoteNotDatePickerMonth" = " 月";
"TUIGroupNoteNotDatePickerDay" = " 日";
"TUIGroupNoteNotDatePickerHour" = " 時";
"TUIGroupNoteNotDatePickerMinute" = " 分";
"TUIGroupNoteNotDatePickerSecond" = " 秒";
"TUIGroupNoteNotDatePickerAM" = "上午";
"TUIGroupNoteNotDatePickerPM" = "下午";
"TUIGroupNoteNotDatePickerNow" = " 至今";
"TUIGroupNoteNotDatePickerToday" = " 今天";
"TUIGroupNoteNotDatePickerMon" = " 周一";
"TUIGroupNoteNotDatePickerTue" = " 周二";
"TUIGroupNoteNotDatePickerWed" = " 周三";
"TUIGroupNoteNotDatePickerTHR" = " 周四";
"TUIGroupNoteNotDatePickerFRI" = " 周五";
"TUIGroupNoteNotDatePickerSAT" = " 周六";
"TUIGroupNoteNotDatePickerSUN" = " 周日";
"TUICustomerServiceAccounts" = "客服號";
"TUICustomerServiceBranchMessage" = "[分支消息]";
"TUICustomerServiceCardMessage" = "[卡片消息]";
"TUICustomerServiceCollectInfomation" = "[收集信息]";
"TUICustomerServiceSatisfactionEvaluation" = "[滿意度評價]";
"TUICustomerServiceTimeout" = "[客服會話已超時,自動結束]";
"TUICustomerServiceEnd" = "[客服會話已結束]";
"TUICustomerServiceFillProductInfo" = "請填寫商品信息";
"TUICustomerServiceSubmitProductInfo" = "提交商品信息";
"TUICustomerServiceClose" = "關閉";
"TUICustomerServiceName" = "名稱";
"TUICustomerServiceFillProductName" = "請填寫產品名稱";
"TUICustomerServiceDesc" = "描述";
"TUICustomerServiceFillProductDesc" = "請填寫相關描述";
"TUICustomerServicePic" = "圖片";
"TUICustomerServiceFillPicLink" = "請填寫圖片鏈接";
"TUICustomerServiceJumpLink" = "跳轉";
"TUICustomerServiceFillJumpLink" = "請填寫卡片跳轉鏈接";
"TUICustomerServiceSubmitEvaluation" = "提交評價";
"TUICustomerService" = "客服插件";
"TUICustomerServiceSendProduct" = "發送商品";
"TUICustomerServiceCommonPhrase" = "常用語";
"TUICustomerServiceCommonPhraseStock" = "是否有貨?";
"TUICustomerServiceCommonPhraseCheaper" = "可以便宜一點嗎?";
"TUICustomerServiceCommonPhraseGift" = "有什麼贈品?";
"TUICustomerServiceCommonPhraseShipping" = "什麼時候發貨?";
"TUICustomerServiceCommonPhraseDelivery" = "發什麼快遞?";
"TUICustomerServiceCommonPhraseArrive" = "什麼時候到貨?";
"TUIChatBotAccounts" = "機器人";
"TUIChatBotChangeQuestion" = "換一換";
"TUIConversationGroupAll" = "全部";
"TUIConversationGroupUnread" = "未讀";
"TUIConversationGroupAtMe" = "@ 我";
"TUIConversationGroupGroup" = "群聊";
"TUIConversationGroupC2C" = "單聊";
"TUIConversationGroupInputGroupName" = "請輸入分組名";
"TUIConversationGroupCreateGroup" = "創建會話分組";
"TUIConversationGroupNewGroup" = "新增分組";
"TUIConversationGroupAddGroup" = "添加分組";
"TUIConversationGroupMoveGroup" = "移動分組";
"TUIConversationGroupRemoveGroup" = "移除分組";
"TUIConversationGroupShowGroup" = "展示會話分組";
"TUIConversationGroupHideGroup" = "隱藏會話分組";
"TUIConversationGroupEditGroup" = "會話分組設置";
"TUIConversationGroupEdit" = "編輯";
"TUIConversationGroupSave" = "保存";
"TUIConversationGroupSaveSucc" = "分組保存成功";
"TUIConversationGroupCreate" = "創建";
"TUIConversationGroupNameIsNull" = "分組名不能為空!";
"TUIConversationGroupNameIsInvalid" = "分組名不能是預置的默認分組名";
"TUIConversationGroupConversationIsNull" = "會話不能為空!";
"TUIConversationGroupCreateSucc" = "分組創建成功";
"TUIConversationGroupModifySucc" = "分組修改成功";
"TUIConversationGroupName" = "分組名稱";
"TUIConversationGroupIncludeConversation" = "包含會話";
"TUIConversationGroupAddConversation" = "添加會話";
"TUIConversationGroup" = "會話分組";
"TUIConversationGroupManager" = "管理";
"TUIConversationGroupSelectGroup" = "選擇會話分組";
"TUIConversationGroupUnlogined" = "未登錄,保存失敗!";
"TUIConversationMark" = "標記";
"TUIConversationMarkCancel" = "取消標記";
"TUIConversationNone" = "暫無%@的會話";
//----timApp---
"TIMAppMainTitle" = "騰訊雲·IM";
"TIMAppMainConnectingTitle" = "連接中...";
"TIMAppMainDisconnectTitle" = "騰訊雲·IM(未連接)";
"TIMAppTabBarItemMessageText" = "消息";
"TIMAppTabBarItemContactText" = "通訊錄";
"TIMAppTabBarItemMeText" = "設置";
"TIMAppTabBarItemMessageText_mini" = "聊天";
"TIMAppTabBarItemContactText_mini" = "聯繫人";
"TIMAppTabBarItemSettingText_mini" = "設置";
"TIMAppTabBarItemCallsRecordText_mini" = "通話記錄";
"TIMAppChat" = "聊天";
"TIMAppChatDisconnectTitle" = "聊天(未連接)";
"TIMAppChatSecurityWarning" = "請勿輕信匯款、中獎等信息,謹慎對待陌生電話,避免上當受騙。遇到可疑情況請及時反饋。";
"TIMAppChatSecurityWarningReport" = "點此舉報";
"TIMAppChatSecurityWarningGot" = "我知道了";
"TIMAppTencentCloudIM" = "即時通信";
"TIMAppWelcomeToChat" = "歡迎使用本 APP 體驗即時通信 IM 產品服務";
"TIMAppWelcomeToChatDetails" = "歡迎使用本 APP 體驗即時通信 IM 產品服務,如發送文字/表情/富媒體消息、消息回復、表情回應、消息閱讀狀態、好友關係鏈等功能。同時我們為您默認提供了3個不會回復的機器人好友您可直接發起單聊、群聊會話體驗群管理等功能。";
"TIMAppSelectStyle" = "選擇風格";
"TIMAppChatStyles" = "IM 專屬風格切換";
"TIMAppChangeTheme" = "選擇皮膚";
"TIMAppChatThemes" = "IM 專屬皮膚切換";
"TIMAppEnterChat" = "進入 IM";
"TIMAppRunDemo" = "改2行代碼1分鐘跑通 Demo";
"TIMAppCustomers" = "每月服務客戶數超過10000家";
"TIMAppMessagingSuccess" = "消息收發成功率、服務可靠性高於99.99%";
"TIMAppActiveUsers" = "每月活躍用戶數超過10億";
"TIMAppOK" = "已了解";
"TIMApp1Billion+" = "10億+";
"TIMApp10000+" = "10000+";
"TIMApp1Minute" = "1分鐘";
"TIMAppMeAbout" = "關於騰訊雲·IM";
"TIMAppConfirmLogout" = "確定退出嗎";
"TIMAppConfirm" = "確定";
"TIMAppCancel" = "取消";
"TIMAppMarkAllMessageAsReadSucc" = "清空未讀成功";
"TIMAppMarkAllMessageAsReadErrFormat" = "清空未讀失敗, %d, %@";
"TIMChangeLanguage" = "切換語言";
//----timApp---
"TUIKitTickledMe" = "我";
"TUIKitTickledActionVaule" = "拍了拍";
"TUIKitTickledMyself" = "自己";
"TUIKitImageViewOrigin" = "查看原圖";
"TUIGroupCreateTipsMessage" = "創建群組";
"TUICommunityCreateTipsMessage" = "創建社群";
"TUICommunityCreateTipsMessageRuleError" = "社群 ID必須以 @TGS#_ 開頭";
"TUIGroupCreateTipsMessageRuleError" = "非社群,不能以 @TGS#_ 閅頭";
//languageID
"zh-Hans" = "簡體中文";
"en" = "英語";
"ar" = "阿拉伯語";
"zh-Hant" = "繁體中文";
"TUIChatFaceGroupAllEmojiName" = "所有表情";
"TUIChatFaceGroupRecentEmojiName" = "最近使用";

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