Commit 89e1c017 authored by Alex朱枝文's avatar Alex朱枝文

移除desk

parent 28c0707f

Too many changes to show.

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

//
// TDeskChatPopActionsView.h
// TUIChat
//
// Created by wyl on 2022/6/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskChatPopActionsView : UIView
@end
NS_ASSUME_NONNULL_END
//
// TDeskChatPopActionsView.m
// TUIChat
//
// Created by wyl on 2022/6/13.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIChatPopActionsView.h"
@implementation TDeskChatPopActionsView
- (void)layoutSubviews {
[super layoutSubviews];
[self updateCorner];
}
- (void)updateCorner {
UIRectCorner corner = UIRectCornerBottomLeft | UIRectCornerBottomRight;
CGRect containerBounds = self.bounds;
CGRect bounds = CGRectMake(containerBounds.origin.x, containerBounds.origin.y - 1, containerBounds.size.width, containerBounds.size.height);
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:corner cornerRadii:CGSizeMake(5, 5)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
@end
//
// TDeskChatPopMenu.h
// TUIChat
//
// Created by harvy on 2021/11/30.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TDesk_TUIChatConfig.h"
#import "TDesk_TUIChatPopMenuDefine.h"
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^TDeskChatPopMenuActionCallback)(void);
@interface TDeskChatPopMenuAction : NSObject
@property(nonatomic, copy) NSString *title;
@property(nonatomic, strong) UIImage *image;
@property(nonatomic, copy) TDeskChatPopMenuActionCallback callback;
/**
* The higher the weight, the more prominent it is: audioPlayback 11000 Copy 10000, Forward 9000, Multiple Choice 8000, Quote 7000, Reply 5000, Withdraw 4000, Delete 3000.
*/
@property(nonatomic, assign) NSInteger weight;
- (instancetype)initWithTitle:(NSString *)title image:(UIImage *)image weight:(NSInteger)weight callback:(TDeskChatPopMenuActionCallback)callback;
@end
typedef void (^TDeskChatPopMenuHideCallback)(void);
@interface TDeskChatPopMenu : UIView
@property(nonatomic, copy) TDeskChatPopMenuHideCallback hideCallback;
@property(nonatomic, copy) void (^reactClickCallback)(NSString *faceName);
@property(nonatomic, weak) TDeskMessageCellData *targetCellData;
/**
* TDeskChatPopMenu has no emojiView by default. If you need a chatPopMenu with emojiView, use this initializer.
*/
- (instancetype)initWithEmojiView:(BOOL)hasEmojiView frame:(CGRect)frame;
@property(nonatomic, strong, readonly) UIView *emojiContainerView;
@property(nonatomic, strong, readonly) UIView *containerView;
- (void)addAction:(TDeskChatPopMenuAction *)action;
- (void)removeAllAction;
- (void)setArrawPosition:(CGPoint)point adjustHeight:(CGFloat)adjustHeight;
- (void)showInView:(UIView *__nullable)window;
- (void)layoutSubview;
- (void)hideWithAnimation;
@end
NS_ASSUME_NONNULL_END
This diff is collapsed.
//
// TUIChatPopMenuDefine.h
// TUIChat
//
// Created by cologne on 2023/11/22.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#define TChatEmojiView_Margin 10
#define TChatEmojiView_MarginTopBottom 17
#define TChatEmojiView_Padding 20
#define TChatEmojiView_Page_Height 30
#define TChatEmojiView_CollectionOffsetY 8
#define TChatEmojiView_CollectionHeight 107
//
// TDeskFaceSegementScrollView.h
// TUIEmojiPlugin
//
// Created by wyl on 2023/11/15.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TDesk_TUIFaceView.h"
#import "TDesk_TUIFaceVerticalView.h"
NS_ASSUME_NONNULL_BEGIN
@class TDeskFaceGroup;
@interface TDeskFaceSegementScrollView : UIView
@property(nonatomic, copy) void(^onScrollCallback)(NSInteger indexPage);
@property(strong, nonatomic) UIScrollView *pageScrollView;
- (void)setItems:(NSArray<TDeskFaceGroup *> *)items delegate:(id <TDeskFaceVerticalViewDelegate>) delegate;
- (void)updateContainerView;
- (void)setPageIndex:(NSInteger)index;
- (void)setAllFloatCtrlViewAllowSendSwitch:(BOOL)isAllow;
- (void)updateRecentView;
@end
NS_ASSUME_NONNULL_END
//
// TDeskFaceSegementScrollView.m
// TUIEmojiPlugin
//
// Created by wyl on 2023/11/15.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIFaceSegementScrollView.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
#import <TDeskCommon/TDesk_TUIFitButton.h>
#import <TDeskCore/TDesk_TUIThemeManager.h>
#import <TDeskCommon/TDesk_TIMCommonMediator.h>
#import <TDeskCommon/TDesk_TUIEmojiMeditorProtocol.h>
@interface TDeskFaceSegementScrollView () <UIScrollViewDelegate>
@property(nonatomic, strong) NSArray<TDeskFaceGroup *> *items;
@property(nonatomic, strong) NSMutableArray<TDeskFaceVerticalView*> * viewArray;
@end
@implementation TDeskFaceSegementScrollView
- (void)setItems:(NSArray<TDeskFaceGroup *> *)items delegate:(id <TDeskFaceViewDelegate>) delegate {
_items = items;
for (UIView *view in self.pageScrollView.subviews) {
if (view) {
[view removeFromSuperview];
}
}
[self.viewArray removeAllObjects];
for (int i = 0; i < items.count; i++) {
TDeskFaceVerticalView* faceView = [[TDeskFaceVerticalView alloc] initWithFrame:CGRectMake(0,
0, self.frame.size.width, self.pageScrollView.frame.size.height)];
TDeskFaceGroup *indexGroup = items[i];
if (indexGroup.recentGroup) {
[faceView setData:[NSMutableArray arrayWithArray:@[indexGroup.recentGroup,indexGroup]]];
}
else {
[faceView setData:[NSMutableArray arrayWithArray:@[indexGroup]]];
}
faceView.frame = CGRectMake(i * self.frame.size.width, 0, self.frame.size.width, self.pageScrollView.frame.size.height);
faceView.delegate = delegate;
[self.pageScrollView addSubview:faceView];
[self.viewArray addObject:faceView];
}
self.pageScrollView.contentSize = CGSizeMake(self.viewArray.count * self.frame.size.width, self.pageScrollView.frame.size.height);
if (isRTL()) {
_pageScrollView.transform = CGAffineTransformMakeRotation(M_PI);
NSArray *subViews = _pageScrollView.subviews;
for (UIView *subView in subViews) {
subView.transform = CGAffineTransformMakeRotation(M_PI);
}
}
}
- (void)updateContainerView {
self.pageScrollView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
for (int i = 0; i < self.viewArray.count; i++) {
TDeskFaceVerticalView* view = self.viewArray[i];
view.frame = CGRectMake(i * self.pageScrollView.frame.size.width, 0, self.pageScrollView.frame.size.width, self.pageScrollView.frame.size.height);
}
self.pageScrollView.contentSize = CGSizeMake(self.viewArray.count * self.frame.size.width, self.pageScrollView.frame.size.height);
}
- (UIScrollView *)pageScrollView {
if (_pageScrollView == nil) {
_pageScrollView = [[UIScrollView alloc]
initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
_pageScrollView.backgroundColor = [UIColor clearColor];
_pageScrollView.delegate = self;
_pageScrollView.showsVerticalScrollIndicator = NO;
_pageScrollView.showsHorizontalScrollIndicator = NO;
_pageScrollView.bounces = NO;
_pageScrollView.scrollsToTop = NO;
_pageScrollView.pagingEnabled = YES;
[self addSubview:_pageScrollView];
}
return _pageScrollView;
}
- (NSMutableArray<TDeskFaceVerticalView *> *)viewArray {
if(!_viewArray) {
_viewArray = [NSMutableArray arrayWithCapacity:3];
}
return _viewArray;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == _pageScrollView) {
int p = _pageScrollView.contentOffset.x / self.frame.size.width;
if (self.onScrollCallback){
self.onScrollCallback(p);
}
}
}
- (void)setPageIndex:(NSInteger)index {
CGPoint p = CGPointMake(_pageScrollView.frame.size.width * index,0);
[_pageScrollView setContentOffset:p animated:NO];
}
- (void)setAllFloatCtrlViewAllowSendSwitch:(BOOL)isAllow {
for (TDeskFaceVerticalView *view in self.viewArray) {
[view setFloatCtrlViewAllowSendSwitch:isAllow];
}
}
- (void)updateRecentView {
id<TDeskEmojiMeditorProtocol> service = [[TDeskCommonMediator share] getObject:@protocol(TDeskEmojiMeditorProtocol)];
TDeskFaceVerticalView* faceView = self.viewArray[0];
TDeskFaceGroup *indexGroup = self.items[0];
indexGroup.recentGroup = [service getChatPopMenuRecentQueue];
indexGroup.recentGroup.rowCount = 1;
indexGroup.recentGroup.itemCountPerRow = 8;
indexGroup.recentGroup.groupName = TDeskIMCommonLocalizableString(TUIChatFaceGroupRecentEmojiName);
if (indexGroup.isNeedAddInInputBar && indexGroup.recentGroup) {
[faceView setData:[NSMutableArray arrayWithArray:@[indexGroup.recentGroup,indexGroup]]];
}
else {
[faceView setData:[NSMutableArray arrayWithArray:@[indexGroup]]];
}
}
@end
//
// TDeskFaceVerticalView.h
// TUIChat
//
// Created by wyl on 2023/11/16.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TDesk_TUIFaceView.h"
@class TDeskFaceVerticalView;
@protocol TDeskFaceVerticalViewDelegate <TDeskFaceViewDelegate>
- (void)faceViewClickSendMessageBtn;
@end
@interface TDeskFaceVerticalView : UIView
/**
* Line view
* The separtor which distinguish emoticons from other views
*/
@property(nonatomic, strong) UIView *lineView;
/**
* The collectionView of emoticon view
* Contains multiple lines of expressions, and cooperates with faceFlowLayout for flexible and unified view layout.
*/
@property(nonatomic, strong) UICollectionView *faceCollectionView;
/**
* The flow layout of @faceCollectionView
* Cooperating with faceCollectionView to make the expression view more beautiful. Supported setting layout direction, line spacing, cell spacing, etc.
*/
@property(nonatomic, strong) UICollectionViewFlowLayout *faceFlowLayout;
/**
*
* The data of @faceView
* The object stored in this NSMutableArray is TDeskFaceGroup, that is, the expression group.
*/
@property(nonatomic, strong, readonly) NSMutableArray *faceGroups;
@property(nonatomic, strong, readonly) NSMutableArray *sectionIndexInGroup;
@property(nonatomic, strong, readonly) NSMutableArray *groupIndexInSection;
@property(nonatomic, strong, readonly) NSMutableDictionary *itemIndexs;
@property(nonatomic, strong, readonly) UIView *floatCtrlView;
/**
*
* Delegate variable, delegated
* Need to implement the functionality required in the @TDeskFaceVerticalViewDelegate protocol.
*/
@property(nonatomic, weak) id<TDeskFaceVerticalViewDelegate> delegate;
/**
* Swipe to the specified expression group.
* Switch the emoticon group according to the subscript of the emoticon group clicked by the user.
*
* @param index The index of the destination group, starting from 0.
*/
- (void)scrollToFaceGroupIndex:(NSInteger)index;
/**
* Setting data
* Used to initialize TDeskFaceView or update data in faceView when needed.
*
* @param data The data that needs to be set (TDeskFaceGroup). The object stored in this NSMutableArray is TDeskFaceGroup, that is, the emoticon group.
*/
- (void)setData:(NSMutableArray *)data;
- (void)setFloatCtrlViewAllowSendSwitch:(BOOL)isAllow;
@end
This diff is collapsed.
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
* 【Module Description】
* - This file declares the TDeskFaceViewDelegate protocol and two classes, TDeskFaceGroup and TDeskFaceView.
* - This file is used to implement the emoticon browsing view in the chat window, that is, the interface opened by clicking the smiley face icon in the
* default state.
* - Through this view, you can view and use all your emoticons, browse between different emoji groups, and further select and send emoticon message.
* - This view has integrated the editing function of string-type expressions (such as [Smile]).
*
* 【Function description】
* - TDeskFaceView: Emoji view, which displays the emoticons of each group, and provides functions for selecting and deleting emoticons.
* - TDeskFaceGroup: Emoticons group. Including the initialization of the emoticons group, the positioning of a single emoticon, etc.
*/
#import <TDeskCommon/TDesk_TIMCommonModel.h>
#import <UIKit/UIKit.h>
@class TDeskFaceView;
/////////////////////////////////////////////////////////////////////////////////
//
// TDeskFaceViewDelegate
//
/////////////////////////////////////////////////////////////////////////////////
@protocol TDeskFaceViewDelegate <NSObject>
/**
* The callback after sliding to the specified emoticons group.
* - You can use this callback to respond to the swipe operation, and then update the information of the emoticon view to display the emoticons in the new
* emoticon group.
*
* @param faceView Delegator, emoticon view. Usually, the expression view has one and only one.
* @param index The index of the emoji group to which slide.
*/
- (void)faceView:(TDeskFaceView *)faceView scrollToFaceGroupIndex:(NSInteger)index;
/**
* The Callback after selecting a specific emoticon (index positioning).
* You can use this callback to achieve:
* - When a string type emoticon (such as [smile]) is clicked, add the emoticon to the input bar.
* - When clicking on another type of emoji, send that emoji directly.
*
* @param faceView Delegator, emoticon view. Usually, the expression view has one and only one.
* @param indexPath Index path, used to locate expressions. index.section: the group where the expression is located; index.row: the row where the expression
* is located.
*/
- (void)faceView:(TDeskFaceView *)faceView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
/**
* The action callback after clicking the delete button in the emoji view.
* You can use this callback to delete the entire emoji string in the inputBar, for example, for "[smile]", delete the square brackets and the content between
* the brackets directly, instead of only deleting the rightmost "]".
*
* @param faceView Delegator, emoticon view. Usually, the expression view has one and only one.
*/
- (void)faceViewDidBackDelete:(TDeskFaceView *)faceView;
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TDeskFaceView
//
/////////////////////////////////////////////////////////////////////////////////
/**
* 【Module name】TDeskFaceView
* 【Function description】It is used to realize the emoticon browsing view in the chat window, that is, the interface opened by clicking the smiley face icon
* in the default state.
* - Through this view, you can view all available emoticons, support emoticon grouping, select and send emoticons.
* - This view has integrated the editing functions of string-type emoticons (such as [smile]), and the selection and sending functions of image-type
* emoticons.
*/
@interface TDeskFaceView : UIView
/**
* Line view
* The separtor which distinguish emoticons from other views
*/
@property(nonatomic, strong) UIView *lineView;
/**
* The collectionView of emoticon view
* Contains multiple lines of expressions, and cooperates with faceFlowLayout for flexible and unified view layout.
*/
@property(nonatomic, strong) UICollectionView *faceCollectionView;
/**
* The flow layout of @faceCollectionView
* Cooperating with faceCollectionView to make the expression view more beautiful. Supported setting layout direction, line spacing, cell spacing, etc.
*/
@property(nonatomic, strong) UICollectionViewFlowLayout *faceFlowLayout;
/**
* Page control
* It is used to realize multi-page browsing of emoticons, can slide to switch emoticon pages, and display the total number of pages and the current number of
* pages in the form of small dots below the emoticon page.
*/
@property(nonatomic, strong) UIPageControl *pageControl;
/**
* The data of @faceView
* The object stored in this NSMutableArray is TDeskFaceGroup, that is, the expression group.
*/
@property(nonatomic, strong, readonly) NSMutableArray *faceGroups;
@property(nonatomic, strong, readonly) NSMutableArray *sectionIndexInGroup;
@property(nonatomic, strong, readonly) NSMutableArray *pageCountInGroup;
@property(nonatomic, strong, readonly) NSMutableArray *groupIndexInSection;
@property(nonatomic, strong, readonly) NSMutableDictionary *itemIndexs;
/**
* Delegate variable, delegated
* Need to implement the functionality required in the @TDeskFaceViewDelegate protocol.
*/
@property(nonatomic, weak) id<TDeskFaceViewDelegate> delegate;
/**
* Swipe to the specified expression group.
* Switch the emoticon group according to the subscript of the emoticon group clicked by the user.
*
* @param index The index of the destination group, starting from 0.
*/
- (void)scrollToFaceGroupIndex:(NSInteger)index;
/**
* Setting data
* Used to initialize TDeskFaceView or update data in faceView when needed.
*
* @param data The data that needs to be set (TDeskFaceGroup). The object stored in this NSMutableArray is TDeskFaceGroup, that is, the emoticon group.
*/
- (void)setData:(NSMutableArray *)data;
@end
This diff is collapsed.
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
* This file declares the data source for TDeskGroupCreatedCell
*/
#import <TDeskCommon/TDesk_TUISystemMessageCellData.h>
@interface TDeskGroupCreatedCellData : TDeskSystemMessageCellData
@property(nonatomic, copy) NSString *opUser;
@property(nonatomic, strong) NSNumber *cmd;
@end
//
// TDeskGroupCreatedCellData.m
// TUIKitDemo
//
// Created by annidyfeng on 2019/6/10.
// Copyright © 2019 Tencent. All rights reserved.
//
#import "TDesk_TUIGroupCreatedCellData.h"
#import <TDeskCore/TDesk_NSString+TUIUtil.h>
@implementation TDeskGroupCreatedCellData
+ (TDeskGroupCreatedCellData *)getCellData:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
TDeskGroupCreatedCellData *cellData = [[TDeskGroupCreatedCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
cellData.innerMessage = message;
cellData.msgID = message.msgID;
cellData.content = param[@"content"];
cellData.opUser = [self.class getOpUserName:message]?:param[@"opUser"];
cellData.cmd = param[@"cmd"];
return cellData;
}
+ (NSString *)getOpUserName:(V2TIMMessage *)info {
NSString *opUser;
if (info.nameCard.length > 0) {
opUser = info.nameCard;
} else if (info.nickName.length > 0) {
opUser = info.nickName;
} else {
opUser = info.userID;
}
return opUser;
}
- (NSMutableAttributedString *)attributedString {
NSString *localizableContent = self.content;
if (self.cmd && [self.cmd isKindOfClass:NSNumber.class]) {
NSInteger command = [self.cmd integerValue];
if (command == 1) {
localizableContent = TDeskIMCommonLocalizableString(TUICommunityCreateTipsMessage);
} else {
localizableContent = TDeskIMCommonLocalizableString(TUIGroupCreateTipsMessage);
}
}
NSString *str = [NSString stringWithFormat:@"\"%@\" %@", self.opUser, localizableContent];
str = rtlString(str);
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:str];
NSDictionary *attributeDict = @{NSForegroundColorAttributeName : [UIColor d_systemGrayColor]};
[attributeString setAttributes:attributeDict range:NSMakeRange(0, attributeString.length)];
return attributeString;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)msg {
if (msg.customElem == nil || msg.customElem.data == nil) {
return nil;
}
NSDictionary *param = [TDeskTool jsonData2Dictionary:msg.customElem.data];
if (param == nil || ![param isKindOfClass:[NSDictionary class]]) {
return nil;
}
NSString *businessID = param[@"businessID"];
if (![businessID isKindOfClass:[NSString class]]) {
return nil;
}
if (![businessID isEqualToString:BussinessID_GroupCreate] && ![param.allKeys containsObject:BussinessID_GroupCreate]) {
return nil;
}
NSString *localizableContent = param[@"content"];
NSNumber *cmd = param[@"cmd"];
if (cmd && [cmd isKindOfClass:NSNumber.class]) {
NSInteger command = [cmd integerValue];
if (command == 1) {
localizableContent = TDeskIMCommonLocalizableString(TUICommunityCreateTipsMessage);
} else {
localizableContent = TDeskIMCommonLocalizableString(TUIGroupCreateTipsMessage);
}
}
NSString * opUser = [self.class getOpUserName:msg]?:param[@"opUser"];
NSString *str = [NSString stringWithFormat:@"\"%@\" %@", opUser, localizableContent];
return rtlString(str);
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This document declares modules for implementing "more" units.
* - "More units", that is, several units that appear after clicking the "+" in the lower right corner of the chat interface.
* - At present, "More Units" provides four multimedia sending functions of shooting, video, picture and file, and you can also customize it.
* - TDeskInputMoreCellData is responsible for storing the information needed for a series of "more" cells.
*/
#import <Foundation/Foundation.h>
@import UIKit;
NS_ASSUME_NONNULL_BEGIN
typedef void (^TUIInputMoreCallback)(NSDictionary *param);
/////////////////////////////////////////////////////////////////////////////////
//
// TDeskInputMoreCellData
//
/////////////////////////////////////////////////////////////////////////////////
/**
* 【Module name】TDeskInputMoreCellData
* 【Function description】"More units" data source
* - "More Units" is responsible for displaying in "More Views", showing the user the functionality contained in "More Views". At the same time, it serves as
* the entrance of each function and responds to user interaction events.
* - The data source is responsible for storing the information needed for a series of "more units".
*/
@interface TDeskInputMoreCellData : NSObject
/**
* Image for single unit
* The icons of each unit are different, which are used to visually represent the function corresponding to the unit
*/
@property(nonatomic, strong) UIImage *image;
/**
* Name for single unit
* The names of each unit are different (such as Photo, Video, File, Album, etc.), which are used to display the corresponding functions of the unit in text
* form below the icon.
*/
@property(nonatomic, strong) NSString *title;
/**
* Callback for clicked
*/
@property(nonatomic, copy) TUIInputMoreCallback onClicked;
/**
* Prioriy for displaying in more menu list
* The larger the value, the higher the front in list
*/
@property(nonatomic, assign) NSInteger priority;
@end
NS_ASSUME_NONNULL_END
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import "TDesk_TUIInputMoreCellData.h"
@implementation TDeskInputMoreCellData
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/******************************************************************************
*
* This file declares the TDeskMemberCellData class.
* It provides a data source for the TDeskMemberCell class, which is mainly used in the message read member list interface, etc.
*
******************************************************************************/
#import <TDeskCommon/TDesk_TIMCommonModel.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskMemberDescribeCellData : TDeskCommonCellData
@property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) UIImage *icon;
@end
@interface TDeskMemberCellData : TDeskCommonCellData
@property(nonatomic, copy) NSString *title; // member's display name
@property(nonatomic, copy) NSURL *avatarUrL; // member's avatar image url
@property(nonatomic, copy) NSString *detail; // optional, used to display more info
@property(nonatomic, copy) NSString *userID;
- (instancetype)initWithUserID:(nonnull NSString *)userID
nickName:(nullable NSString *)nickName
friendRemark:(nullable NSString *)friendRemark
nameCard:(nullable NSString *)nameCard
avatarUrl:(nonnull NSString *)avatarUrl
detail:(nullable NSString *)detail;
@end
NS_ASSUME_NONNULL_END
//
// TDeskMemberCellData.m
// TUIChat
//
// Created by xia on 2022/3/14.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIMemberCellData.h"
@implementation TDeskMemberDescribeCellData
@end
@implementation TDeskMemberCellData
- (instancetype)initWithUserID:(nonnull NSString *)userID
nickName:(nullable NSString *)nickName
friendRemark:(nullable NSString *)friendRemark
nameCard:(nullable NSString *)nameCard
avatarUrl:(nonnull NSString *)avatarUrl
detail:(nullable NSString *)detail {
self = [super init];
if (userID.length > 0) {
_userID = userID;
}
if (avatarUrl.length > 0) {
_avatarUrL = [NSURL URLWithString:avatarUrl];
}
_detail = detail;
if (nameCard.length > 0) {
_title = nameCard;
} else if (friendRemark.length > 0) {
_title = friendRemark;
} else if (nickName.length > 0) {
_title = nickName;
} else {
_title = userID;
}
return self;
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This file declares the TDeskTextMessageCellData class.
* This class inherits from TDeskBubbleMessageCellData and is used to store a series of data and information required by the text message unit.
*/
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
/**
* 【Module name】 TDeskTextMessageCellData
* 【Function description】The datasource of text message unit.
* - Text message unit, which is the most common message unit in most message sending and receiving situations.
* - The text message unit data source provides a series of required data and information for the text message unit.
*/
@interface TDeskTextMessageCellData : TDeskBubbleMessageCellData
/**
* Content of text message
*/
@property(nonatomic, strong) NSString *content;
@property(nonatomic, assign) BOOL isAudioCall;
@property(nonatomic, assign) BOOL isVideoCall;
@property(nonatomic, assign) BOOL isCaller;
@property(nonatomic, assign) BOOL showUnreadPoint;
/**
*
* Mutable strings.
* After the text message receives the content string, it is necessary to convert the string expression (such as [smile]) that may exist in the string into a
* picture expression. This string is responsible for storing the converted result of the above process.
*
*/
- (NSAttributedString *)getContentAttributedString:(UIFont *)textFont;
/**
*
* Get the display size of content string
*/
- (CGSize)getContentAttributedStringSize:(NSAttributedString *)attributeString maxTextSize:(CGSize)maxTextSize;
/**
* NSValue (NSRange) stores the converted string of emoji at the position of attributedString.
* NSAttributedString stores the string before emoji conversion, such as "[呲牙]".
* When the text is selected and copied, it is necessary to find the original string of emoji.
*/
@property(nonatomic, strong) NSMutableArray<NSDictionary<NSValue *, NSAttributedString *> *> *emojiLocations;
/**
* The size of the label which displays the text message content.
* Position the text message with the @textOrigin.
*/
@property(nonatomic, assign) CGSize textSize;
/**
* The origin of label which displays the text message content.
*/
@property(nonatomic, assign) CGPoint textOrigin;
@end
NS_ASSUME_NONNULL_END
//
// TDeskTextMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUITextMessageCellData.h"
#import <TDeskCommon/TDesk_NSString+TUIEmoji.h>
#import <TDeskCommon/TDesk_TIMCommonModel.h>
#import <TDeskCommon/TDesk_TIMDefine.h>
#import <TDeskCore/TDesk_TUIThemeManager.h>
#ifndef CGFLOAT_CEIL
#ifdef CGFLOAT_IS_DOUBLE
#define CGFLOAT_CEIL(value) ceil(value)
#else
#define CGFLOAT_CEIL(value) ceilf(value)
#endif
#endif
@interface TDeskTextMessageCellData ()
@property(nonatomic, assign) CGSize size;
@property(nonatomic, assign) CGFloat containerWidth;
@property(nonatomic, strong) NSMutableAttributedString *attributedString;
@end
@implementation TDeskTextMessageCellData
{
NSString *_content;
}
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
TDeskTextMessageCellData *textData = [[TDeskTextMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
textData.content = message.textElem.text;
textData.reuseId = TTextMessageCell_ReuseId;
textData.status = Msg_Status_Init;
return textData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
NSString *content = message.textElem.text;
return content.getLocalizableStringWithFaceContent;
}
- (Class)getReplyQuoteViewDataClass {
return NSClassFromString(@"TDeskTextReplyQuoteViewData");
}
- (Class)getReplyQuoteViewClass {
return NSClassFromString(@"TDeskTextReplyQuoteView");
}
- (instancetype)initWithDirection:(TDeskMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
if (direction == MsgDirectionIncoming) {
self.cellLayout = [TDeskMessageCellLayout incommingTextMessageLayout];
} else {
self.cellLayout = [TDeskMessageCellLayout outgoingTextMessageLayout];
}
}
return self;
}
- (void)setContent:(NSString *)content {
if (![_content isEqualToString:content]) {
_content = content;
_attributedString = nil;
}
}
- (NSString *)content {
return _content;
}
- (NSAttributedString *)getContentAttributedString:(UIFont *)textFont {
if (!_attributedString) {
_emojiLocations = [NSMutableArray array];
_attributedString = [self.content getFormatEmojiStringWithFont:textFont emojiLocations:_emojiLocations];
if (self.isAudioCall || self.isVideoCall) {
NSTextAttachment *attchment = [[NSTextAttachment alloc] init];
UIImage *image = nil;
if (self.isAudioCall) {
image = TUIChatCommonBundleImage(@"audio_call");
}
if (self.isVideoCall) {
if (self.isCaller) {
image = TUIChatCommonBundleImage(@"video_call_self");
} else {
image = TUIChatCommonBundleImage(@"video_call");
}
}
attchment.image = image;
attchment.bounds = CGRectMake(0, -(textFont.lineHeight - textFont.pointSize) / 2, 16, 16);
NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:(NSTextAttachment *)(attchment)];
NSAttributedString *spaceString = [[NSAttributedString alloc] initWithString:@" " attributes:@{NSFontAttributeName : textFont}];
if (self.isCaller) {
[_attributedString appendAttributedString:spaceString];
[_attributedString appendAttributedString:imageString];
} else {
[_attributedString insertAttributedString:spaceString atIndex:0];
[_attributedString insertAttributedString:imageString atIndex:0];
}
}
}
return _attributedString;
}
- (CGSize)getContentAttributedStringSize:(NSAttributedString *)attributeString maxTextSize:(CGSize)maxTextSize {
CGRect rect = [attributeString boundingRectWithSize:maxTextSize
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
context:nil];
CGFloat width = CGFLOAT_CEIL(rect.size.width);
CGFloat height = CGFLOAT_CEIL(rect.size.height);
return CGSizeMake(width, height);
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
*
* This file declares the TDeskFaceMessageCellData class.
* This class inherits from TDeskMessageCellData and is used to store a series of data and information required by the emoticon message unit.
*/
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
/**
* 【Module name】TDeskFaceMessageCellData
* 【Function description】Emoticon message unit data source.
* - The emoticon message unit is the message unit used and displayed when displaying animated emoticons.
* - The emoticon message unit data source is a class that provides a series of required data for the display of the emoticon message unit.
*/
@interface TDeskFaceMessageCellData : TDeskBubbleMessageCellData
/**
*
* The index of emoticon groups
* - The subscript of the group where the emoticon is located, which is used to locate the emoticon group where the emoticon is located.
*/
@property(nonatomic, assign) NSInteger groupIndex;
/**
* The path of the emoticon file
*/
@property(nonatomic, strong) NSString *path;
/**
* The name of emoticon.
*/
@property(nonatomic, strong) NSString *faceName;
@end
NS_ASSUME_NONNULL_END
//
// TFaceMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIFaceMessageCellData.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
@implementation TDeskFaceMessageCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
V2TIMFaceElem *elem = message.faceElem;
TDeskFaceMessageCellData *faceData = [[TDeskFaceMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
faceData.groupIndex = elem.index;
faceData.faceName = [[NSString alloc] initWithData:elem.data encoding:NSUTF8StringEncoding];
for (TDeskFaceGroup *group in [TDeskIMConfig defaultConfig].faceGroups) {
if (group.groupIndex == faceData.groupIndex) {
NSString *path = [group.groupPath stringByAppendingPathComponent:faceData.faceName];
faceData.path = path;
break;
}
}
faceData.reuseId = TFaceMessageCell_ReuseId;
return faceData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return TDeskIMCommonLocalizableString(TUIKitMessageTypeAnimateEmoji);
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskFileMessageCellData : TDeskBubbleMessageCellData <TDeskMessageCellDataFileUploadProtocol, TDeskMessageCellDataFileDownloadProtocol>
/**
* File path
*/
@property(nonatomic, strong) NSString *path;
/**
* File name, including suffix.
*/
@property(nonatomic, strong) NSString *fileName;
/**
* Inner ID for file
*/
@property(nonatomic, strong) NSString *uuid;
/**
* File size, used to display the data volume information of the file.
*/
@property(nonatomic, assign) int length;
/**
* The progress of file uploading, which maintained by the cellData.
*/
@property(nonatomic, assign) NSUInteger uploadProgress;
/**
* The progress of file downloading, which maintained by the cellData.
*/
@property(nonatomic, assign) NSUInteger downladProgress;
/**
* The flag of indicating whether the file is downloading
* YES: dowloading; NO: not download
*/
@property(nonatomic, assign) BOOL isDownloading;
/**
* Downloading the file
* This method integrates and calls the IM SDK, and obtains the file through the interface provided by the SDK.
* 1. Before downloading the file from server, it will try to read file from local when the file exists in the local.
* 2. When the file not exists in the local, it will download from server through the api provided by IMSDK. But if there is downloading task, it will wait for
* the task finished.
* - The download progress (percentage value) is updated through the callback of the IMSDK.
* - There are two parameters which is @curSize and @totalSize in the callback of IMSDK. The progress value equals to curSize * 100 / totalSize.
* 3. When finished download, the file will be storaged to the @path.
*/
- (void)downloadFile;
/**
* Determine if the file is already downloaded to local
* This method will first try to get the file path from the local, if the acquisition is successful, record the path and return YES. Otherwise return NO.
*/
- (BOOL)isLocalExist;
/**
* Getting the file path and it will return the flag of whether the file exists through @isExist.
*/
- (NSString *)getFilePath:(BOOL *)isExist;
@end
NS_ASSUME_NONNULL_END
//
// TDeskFileMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIFileMessageCellData.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
#import <TDeskCore/TDesk_NSString+TUIUtil.h>
#import "TDesk_TUIMessageProgressManager.h"
@interface TDeskFileMessageCellData ()
@property(nonatomic, strong) NSMutableArray *progressBlocks;
@property(nonatomic, strong) NSMutableArray *responseBlocks;
@end
@implementation TDeskFileMessageCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
V2TIMFileElem *elem = message.fileElem;
TDeskFileMessageCellData *fileData = [[TDeskFileMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
fileData.path = [elem.path safePathString];
fileData.fileName = elem.filename;
fileData.length = elem.fileSize;
fileData.uuid = elem.uuid;
fileData.reuseId = TFileMessageCell_ReuseId;
return fileData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return TDeskIMCommonLocalizableString(TUIkitMessageTypeFile); // @"[File]";
}
- (Class)getReplyQuoteViewDataClass {
return NSClassFromString(@"TDeskFileReplyQuoteViewData");
}
- (Class)getReplyQuoteViewClass {
return NSClassFromString(@"TDeskFileReplyQuoteView");
}
- (int)length {
if (self.innerMessage) {
_length = self.innerMessage.fileElem.fileSize;
}
return _length;
}
- (instancetype)initWithDirection:(TDeskMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
_uploadProgress = 100;
_downladProgress = 100;
_isDownloading = NO;
_progressBlocks = [NSMutableArray array];
_responseBlocks = [NSMutableArray array];
}
return self;
}
- (void)downloadFile {
BOOL isExist = NO;
NSString *path = [self getFilePath:&isExist];
if (isExist) {
return;
}
NSInteger progress = [TDeskMessageProgressManager.shareManager downloadProgressForMessage:self.msgID];
if (progress != 0) {
return;
}
if (self.isDownloading) return;
self.isDownloading = YES;
@weakify(self);
if (self.innerMessage.elemType == V2TIM_ELEM_TYPE_FILE) {
NSString *msgID = self.msgID;
[self.innerMessage.fileElem downloadFile:path
progress:^(NSInteger curSize, NSInteger totalSize) {
@strongify(self);
NSInteger progress = curSize * 100 / totalSize;
[self updateDownalodProgress:MIN(progress, 99)];
[TDeskMessageProgressManager.shareManager appendDownloadProgress:msgID progress:MIN(progress, 99)];
}
succ:^{
@strongify(self);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.isDownloading = NO;
[self updateDownalodProgress:100];
[TDeskMessageProgressManager.shareManager appendDownloadProgress:msgID progress:100];
dispatch_async(dispatch_get_main_queue(), ^{
self.path = path;
});
});
}
fail:^(int code, NSString *msg) {
@strongify(self);
self.isDownloading = NO;
}];
}
}
- (void)updateDownalodProgress:(NSUInteger)progress {
dispatch_async(dispatch_get_main_queue(), ^{
self.downladProgress = progress;
});
}
- (BOOL)isLocalExist {
BOOL isExist;
[self getFilePath:&isExist];
return isExist;
}
- (NSString *)getFilePath:(BOOL *)isExist {
NSString *path = nil;
BOOL isDir = NO;
*isExist = NO;
if (self.direction == MsgDirectionOutgoing) {
// The origin file path is valid when uploading
path = [NSString stringWithFormat:@"%@%@", TUIKit_File_Path, _path.lastPathComponent];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
if (!*isExist) {
path = [NSString stringWithFormat:@"%@%@%@", TUIKit_File_Path,self.uuid, _fileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
if (*isExist) {
_path = path;
}
// TODO: uuid
return path;
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import "TDesk_TUIChatDefine.h"
#import "TDesk_TUIMessageItem.h"
NS_ASSUME_NONNULL_BEGIN
/////////////////////////////////////////////////////////////////////////////////
//
// TDeskImageMessageCellData
//
/////////////////////////////////////////////////////////////////////////////////
/**
*
* 【Module name】 TDeskImageMessageCellData
* 【Function description】It is used to realize the picture bubble in the chat window, including the display of picture message sending progress.
* At the same time, this module already supports three different types of "thumbnail", "large image" and "original image", and
* has handled the business logic of displaying the corresponding image type under appropriate circumstances:
* 1. Thumbnail - By default, you see thumbnails in the chat window, which is smaller and saves traffic.
* 2. Large image - If the user clicks on the thumbnail, they see a larger image with a better resolution.
* 3. Original image - If the sender chooses to send the original image, the recipient will see the "original image" button which can click to download the
* image with the original size.
*/
@interface TDeskImageMessageCellData : TDeskBubbleMessageCellData <TDeskMessageCellDataFileUploadProtocol>
@property(nonatomic, strong) UIImage *thumbImage;
@property(nonatomic, strong) UIImage *originImage;
@property(nonatomic, strong) UIImage *largeImage;
/**
*
* The file storage path
*
* @note
* @path is maintained by the program by default, you can directly obtain the demo storage path by importing TIMDefine.h and referencing TUIKit_Image_Path
* Other routes are also available if you have further individual needs
*/
@property(nonatomic, strong) NSString *path;
@property(nonatomic, assign) NSInteger length;
/**
*
* The set of image items
*
* @note
* There are usually three imageItems stored in @items, namely thumb (thumb image), origin (original image), and large (large image), which is convenient to
* obtain images flexibly according to needs.
*
*/
@property(nonatomic, strong) NSMutableArray *items;
/**
* The progress of loading thumbnail
*/
@property(nonatomic, assign) NSUInteger thumbProgress;
/**
* The progress of loading origin image
*/
@property(nonatomic, assign) NSUInteger originProgress;
/**
* The progress of loading large image
*/
@property(nonatomic, assign) NSUInteger largeProgress;
/**
* The progress of uploading (sending)
*/
@property(nonatomic, assign) NSUInteger uploadProgress;
@property(nonatomic, assign) BOOL isSuperLongImage;
/**
* Downloading image.
* This method integrates and calls the IM SDK, and obtains images from sever through the interface provided by the SDK.
* 1. Before downloading the file from server, it will try to read file from local when the file exists in the local.
* 2. If the file is not exist in the local, it will download from server through the api named @getImage which provided by the class of TIMImage in the IMSDK.
* - The download progress (percentage value) is updated through the callback of the IMSDK.
* - There are two parameters which is @curSize and @totalSize in the callback of IMSDK. The progress value equals to curSize * 100 / totalSize.
* - The type of items in the image message is TIMElem. You can obtain image list from the paramter named imageList provided by TIMElem, which including
* original image、large image and thumbnail and you can obtain the image from it with the @imageType.
* 3. The image obtained through the SDK interface is a binary file, which needs to be decoded first, converted to CGIamge for decoding, and then packaged as a
* UIImage before it can be used.
* 4. When finished download, the image will be storaged to the @path.
*/
- (void)downloadImage:(TUIImageType)type;
- (void)downloadImage:(TUIImageType)type finish:(TUIImageMessageDownloadCallback)finish;
/**
*
* Decode the image and assign the image to a variable of the corresponding type (@thumbImage, @largeImage or @originImage).
*/
- (void)decodeImage:(TUIImageType)type;
/**
*
* Getting image file path
*/
- (NSString *)getImagePath:(TUIImageType)type isExist:(BOOL *)isExist;
@end
NS_ASSUME_NONNULL_END
//
// TDeskImageMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIImageMessageCellData.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
#import <TDeskCore/TDesk_NSString+TUIUtil.h>
@interface TDeskImageMessageCellData ()
@property(nonatomic, assign) BOOL isDownloading;
@property(nonatomic, copy) TUIImageMessageDownloadCallback onFinish;
@end
@implementation TDeskImageMessageCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
V2TIMImageElem *elem = message.imageElem;
TDeskImageMessageCellData *imageData = [[TDeskImageMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
imageData.path = [elem.path safePathString];
imageData.items = [NSMutableArray array];
for (V2TIMImage *item in elem.imageList) {
TDeskImageItem *itemData = [[TDeskImageItem alloc] init];
itemData.uuid = item.uuid;
itemData.size = CGSizeMake(item.width, item.height);
// itemData.url = item.url;
if (item.type == V2TIM_IMAGE_TYPE_THUMB) {
itemData.type = TImage_Type_Thumb;
} else if (item.type == V2TIM_IMAGE_TYPE_LARGE) {
itemData.type = TImage_Type_Large;
} else if (item.type == V2TIM_IMAGE_TYPE_ORIGIN) {
itemData.type = TImage_Type_Origin;
}
[imageData.items addObject:itemData];
}
imageData.reuseId = TImageMessageCell_ReuseId;
return imageData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return TDeskIMCommonLocalizableString(TUIkitMessageTypeImage); // @"[Image]";
}
- (Class)getReplyQuoteViewDataClass {
return NSClassFromString(@"TDeskImageReplyQuoteViewData");
}
- (Class)getReplyQuoteViewClass {
return NSClassFromString(@"TDeskImageReplyQuoteView");
}
- (instancetype)initWithDirection:(TDeskMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
_uploadProgress = 100;
if (direction == MsgDirectionIncoming) {
self.cellLayout = [TDeskMessageCellLayout incommingImageMessageLayout];
} else {
self.cellLayout = [TDeskMessageCellLayout outgoingImageMessageLayout];
}
}
return self;
}
- (NSString *)getImagePath:(TUIImageType)type isExist:(BOOL *)isExist {
NSString *path = nil;
BOOL isDir = NO;
*isExist = NO;
if (self.direction == MsgDirectionOutgoing) {
path = [NSString stringWithFormat:@"%@%@", TUIKit_Image_Path, _path.lastPathComponent];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
if (!*isExist) {
TDeskImageItem *tImageItem = [self getTImageItem:type];
path = [NSString stringWithFormat:@"%@%@_%ld", TUIKit_Image_Path, tImageItem.uuid, (long)tImageItem.type];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
return path;
}
- (void)downloadImage:(TUIImageType)type finish:(TUIImageMessageDownloadCallback)finish {
self.onFinish = finish;
[self downloadImage:type];
}
- (void)downloadImage:(TUIImageType)type {
BOOL isExist = NO;
NSString *path = [self getImagePath:type isExist:&isExist];
if (isExist) {
[self decodeImage:type];
return;
}
if (self.isDownloading) {
return;
}
self.isDownloading = YES;
V2TIMImage *imImage = [self getIMImage:type];
@weakify(self);
[imImage downloadImage:path
progress:^(NSInteger curSize, NSInteger totalSize) {
@strongify(self);
NSInteger progress = curSize * 100 / totalSize;
[self updateProgress:MIN(progress, 99) withType:type];
}
succ:^{
@strongify(self);
self.isDownloading = NO;
[self updateProgress:100 withType:type];
[self decodeImage:type];
}
fail:^(int code, NSString *msg) {
@strongify(self);
self.isDownloading = NO;
/**
* If the uuid of the picture is the same (the same user sends
* the same picture continuously), the same path may trigger multiple download operations. Except for the first time, subsequent downloads will report
* an error. At this time, it is necessary to judge whether the local file exists.
*/
[self decodeImage:type];
}];
}
- (void)updateProgress:(NSUInteger)progress withType:(TUIImageType)type {
dispatch_async(dispatch_get_main_queue(), ^{
if (type == TImage_Type_Thumb) self.thumbProgress = progress;
if (type == TImage_Type_Large) self.largeProgress = progress;
if (type == TImage_Type_Origin) self.originProgress = progress;
});
}
- (void)decodeImage:(TUIImageType)type {
BOOL isExist = NO;
NSString *path = [self getImagePath:type isExist:&isExist];
if (!isExist) {
return;
}
__weak typeof(self) weakSelf = self;
void (^finishBlock)(UIImage *) = ^(UIImage *image) {
if (type == TImage_Type_Thumb) {
weakSelf.thumbImage = image;
weakSelf.thumbProgress = 100;
weakSelf.uploadProgress = 100;
}
if (type == TImage_Type_Large) {
weakSelf.largeImage = image;
weakSelf.largeProgress = 100;
}
if (type == TImage_Type_Origin) {
weakSelf.originImage = image;
weakSelf.originProgress = 100;
}
if (weakSelf.onFinish) {
weakSelf.onFinish();
}
};
NSString *cacheKey = [path substringFromIndex:TUIKit_Image_Path.length];
UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:cacheKey];
if (cacheImage) {
finishBlock(cacheImage);
} else {
[TDeskTool asyncDecodeImage:path
complete:^(NSString *path, UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (![path tdesk_containsString:@".gif"] || (image.sd_imageFormat != SDImageFormatGIF)) {
/**
* The gif image is too large to be cached in memory
*/
[[SDImageCache sharedImageCache] storeImageToMemory:image forKey:cacheKey];
}
finishBlock(image);
});
}];
}
}
- (TDeskImageItem *)getTImageItem:(TUIImageType)type {
for (TDeskImageItem *item in self.items) {
if (item.type == type) {
return item;
}
}
return nil;
}
- (V2TIMImage *)getIMImage:(TUIImageType)type {
V2TIMMessage *imMsg = self.innerMessage;
if (imMsg.elemType == V2TIM_ELEM_TYPE_IMAGE) {
for (V2TIMImage *imImage in imMsg.imageElem.imageList) {
if (type == TImage_Type_Thumb && imImage.type == V2TIM_IMAGE_TYPE_THUMB) {
return imImage;
} else if (type == TImage_Type_Origin && imImage.type == V2TIM_IMAGE_TYPE_ORIGIN) {
return imImage;
} else if (type == TImage_Type_Large && imImage.type == V2TIM_IMAGE_TYPE_LARGE) {
return imImage;
}
}
}
return nil;
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
*
* This file declares the TDeskJoinGroupMessageCellData class.
* This document is responsible for realizing the function of the small gray bar for entering the group, and can also be further extended to a group message
* unit with a single operator. That is, this file can highlight the operator's nickname in blue and provide a response interface for the highlighted part in
* blue.
*/
#import <TDeskCommon/TDesk_TUISystemMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskJoinGroupMessageCellData : TDeskSystemMessageCellData
/**
*
* Operator nickname. For example, "Tom joined the group", the variable content is "Tom"
*/
@property(nonatomic, strong) NSString *opUserName;
/**
* The nickname of the operator.
*/
@property(nonatomic, strong) NSMutableArray<NSString *> *userNameList;
/**
* Operator Id.
*/
@property(nonatomic, strong) NSString *opUserID;
/**
* List of the operator IDs.
*/
@property(nonatomic, strong) NSMutableArray<NSString *> *userIDList;
@end
NS_ASSUME_NONNULL_END
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import "TDesk_TUIJoinGroupMessageCellData.h"
@implementation TDeskJoinGroupMessageCellData
- (instancetype)initWithDirection:(TDeskMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
self.userNameList = [NSMutableArray array];
self.userIDList = [NSMutableArray array];
}
return self;
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/////////////////////////////////////////////////////////////////////////////////
//
// TDeskMenuCellData
//
/////////////////////////////////////////////////////////////////////////////////
@interface TDeskMenuCellData : NSObject
/**
* Access path for grouped thumbnails in grouping units
*/
@property(nonatomic, strong) NSString *path;
@property(nonatomic, assign) BOOL isSelected;
@end
NS_ASSUME_NONNULL_END
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import "TDesk_TUIMenuCellData.h"
@implementation TDeskMenuCellData
@end
//
// TDeskMergeMessageCellData.h
// Pods
//
// Created by harvy on 2020/12/9.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TDeskCommon/TDesk_TIMDefine.h>
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskMergeMessageCellData : TDeskMessageCellData
@property(nonatomic, copy) NSString *title;
@property(nonatomic, strong) NSArray<NSString *> *abstractList;
@property(nonatomic, strong) V2TIMMergerElem *mergerElem;
@property(nonatomic, assign) CGSize abstractSize;
@property(nonatomic, assign) CGSize abstractRow1Size;
@property(nonatomic, assign) CGSize abstractRow2Size;
@property(nonatomic, assign) CGSize abstractRow3Size;
@property(nonatomic, strong) NSArray<NSDictionary *> *abstractSendDetailList;
- (NSAttributedString *)abstractAttributedString;
@end
NS_ASSUME_NONNULL_END
//
// TDeskMergeMessageCellData.m
// Pods
//
// Created by harvy on 2020/12/9.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIMergeMessageCellData.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
#import "TDesk_TUITextMessageCellData.h"
#import <TDeskCommon/TDesk_NSString+TUIEmoji.h>
@implementation TDeskMergeMessageCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
V2TIMMergerElem *elem = message.mergerElem;
if (elem.layersOverLimit) {
TDeskTextMessageCellData *limitCell = [[TDeskTextMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
limitCell.content = TDeskIMCommonLocalizableString(TUIKitRelayLayerLimitTips);
return limitCell;
}
TDeskMergeMessageCellData *mergeData = [[TDeskMergeMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
mergeData.title = elem.title;
mergeData.abstractList = [NSArray arrayWithArray:elem.abstractList];
mergeData.abstractSendDetailList = [self.class formatAbstractSendDetailList:elem.abstractList];
mergeData.mergerElem = elem;
mergeData.reuseId = TMergeMessageCell_ReuserId;
return mergeData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return [NSString stringWithFormat:@"[%@]", TDeskIMCommonLocalizableString(TUIKitRelayChatHistory)];
}
- (Class)getReplyQuoteViewDataClass {
return NSClassFromString(@"TDeskMergeReplyQuoteViewData");
}
- (Class)getReplyQuoteViewClass {
return NSClassFromString(@"TDeskMergeReplyQuoteView");
}
- (NSAttributedString *)abstractAttributedString {
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineSpacing = 4;
style.alignment = isRTL()? NSTextAlignmentRight:NSTextAlignmentLeft;
NSDictionary *attribute = @{
NSForegroundColorAttributeName : [UIColor colorWithRed:187 / 255.0 green:187 / 255.0 blue:187 / 255.0 alpha:1 / 1.0],
NSFontAttributeName : [UIFont systemFontOfSize:12.0],
NSParagraphStyleAttributeName : style
};
NSMutableAttributedString *abstr = [[NSMutableAttributedString alloc] initWithString:@""];
int i = 0;
for (NSString *ab in self.abstractList) {
if (i >= 4) {
break;
}
NSString *resultStr = [NSString stringWithFormat:@"%@\n", ab];
NSString *str = resultStr;
[abstr appendAttributedString:[[NSAttributedString alloc] initWithString:str attributes:attribute]];
i++;
}
return abstr;
}
+ (NSMutableArray *)formatAbstractSendDetailList:(NSArray *)originAbstractList {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:3];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.alignment = isRTL()? NSTextAlignmentRight:NSTextAlignmentLeft;
style.lineBreakMode = NSLineBreakByTruncatingTail;
NSDictionary *attribute = @{
NSForegroundColorAttributeName : [UIColor colorWithRed:187 / 255.0 green:187 / 255.0 blue:187 / 255.0 alpha:1 / 1.0],
NSFontAttributeName : [UIFont systemFontOfSize:12.0],
NSParagraphStyleAttributeName : style
};
int i = 0;
for (NSString *ab in originAbstractList) {
if (i >= 4) {
break;
}
NSString *str = ab;
NSString * splitStr = @":";
if ([str tdesk_containsString:@"\u202C:"]) {
splitStr = @"\u202C:";
}
NSArray<NSString *> *result = [str componentsSeparatedByString:splitStr];
NSString *sender = result[0];
NSString *detail = result[1];
sender = [NSString stringWithFormat:@"%@",sender];
detail = [NSString stringWithFormat:@"%@",detail.getLocalizableStringWithFaceContent];
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:3];
if(sender.length>0 ){
NSMutableAttributedString *abstr = [[NSMutableAttributedString alloc] initWithString:@""];
[abstr appendAttributedString:[[NSAttributedString alloc] initWithString:sender attributes:attribute]];
[dic setObject:abstr forKey:@"sender"];
}
if(detail.length>0 ){
NSMutableAttributedString *abstr = [[NSMutableAttributedString alloc] initWithString:@""];
[abstr appendAttributedString:[[NSAttributedString alloc] initWithString:detail attributes:attribute]];
[dic setObject:abstr forKey:@"detail"];
}
[array addObject:dic];
i++;
}
return array;
}
- (BOOL)isArString:(NSString *)text {
NSString *isoLangCode = (__bridge_transfer NSString *)CFStringTokenizerCopyBestStringLanguage((__bridge CFStringRef)text, CFRangeMake(0, text.length));
if ([isoLangCode isEqualToString:@"ar"]) {
return YES;
}
return NO;
}
@end
//
// TDeskTypingStatusCellData.h
// TUIChat
//
// Created by wyl on 2022/7/4.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskTypingStatusCellData : TDeskMessageCellData
@property(nonatomic, assign) NSInteger typingStatus;
@end
NS_ASSUME_NONNULL_END
//
// TDeskTypingStatusCellData.m
// TUIChat
//
// Created by wyl on 2022/7/4.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUITypingStatusCellData.h"
@implementation TDeskTypingStatusCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
TDeskTypingStatusCellData *cellData = [[TDeskTypingStatusCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
cellData.msgID = message.msgID;
if ([param.allKeys containsObject:@"typingStatus"]) {
cellData.typingStatus = [param[@"typingStatus"] intValue];
}
return cellData;
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
* 1. This file declares the TDeskVideoItem class, TDeskSnapshotItem class, and TDeskVideoMessageCellData class.
* - TDeskVideoItem corresponds to V2TIMVideoElem in the IM SDK. We convert the classes in the SDK to TDeskVideoItem, which is convenient for us to further
* process and operate the data.
* - TDeskSnapshotItem corresponds to the video cover class in the IM SDK. It is still an image in essence, but is bound to the corresponding Video.
* - TDeskVideoMessageCellData inherits from the TDeskMessageCellData class and is used to store a series of data and information required by the image message
* unit.
* 2. The business logic for obtaining video information and cover information has been implemented in this document. When you need to get video and cover
* data, you can directly call the relevant member functions declared in this file
*/
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import "TDesk_TUIChatDefine.h"
#import "TDesk_TUIMessageItem.h"
NS_ASSUME_NONNULL_BEGIN
/////////////////////////////////////////////////////////////////////////////////
//
// TDeskVideoMessageCellData
//
/////////////////////////////////////////////////////////////////////////////////
@interface TDeskVideoMessageCellData : TDeskBubbleMessageCellData <TDeskMessageCellDataFileUploadProtocol>
@property(nonatomic, strong) UIImage *thumbImage;
@property(nonatomic, strong) NSString *videoPath;
@property(nonatomic, strong) NSString *snapshotPath;
@property(nonatomic, strong) TDeskVideoItem *videoItem;
@property(nonatomic, strong) TDeskSnapshotItem *snapshotItem;
@property(nonatomic, assign) NSUInteger uploadProgress;
@property(nonatomic, assign) NSUInteger thumbProgress;
@property(nonatomic, assign) NSUInteger videoProgress;
/// Is the current message a custom message
@property(nonatomic, assign) BOOL isPlaceHolderCellData;
+ (TDeskMessageCellData *)placeholderCellDataWithSnapshotUrl:(NSString *)snapshotUrl thubImage:(UIImage *)thubImage;
- (void)getVideoUrl:(void (^)(NSString *url))urlCallBack;
/**
* Downloading the cover image of the video. It will download from server if the image not exist in local.
*/
- (void)downloadThumb;
- (void)downloadThumb:(TUIVideoMessageDownloadCallback)finish;
/**
* Downloading the video file. It will download from server if the video not exist in local.
*/
- (void)downloadVideo;
- (BOOL)isVideoExist;
@end
NS_ASSUME_NONNULL_END
//
// TDeskVideoMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIVideoMessageCellData.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
#import <TDeskCore/TDesk_NSString+TUIUtil.h>
#import <TDeskCore/TDesk_TUILogin.h>
#define TVideo_Block_Progress @"TVideo_Block_Progress";
#define TVideo_Block_Response @"TVideo_Block_Response";
@interface TDeskVideoMessageCellData ()
@property(nonatomic, strong) NSString *videoUrl;
@property(nonatomic, assign) BOOL isDownloadingSnapshot;
@property(nonatomic, assign) BOOL isDownloadingVideo;
@property(nonatomic, copy) TUIVideoMessageDownloadCallback onFinish;
@end
@implementation TDeskVideoMessageCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
V2TIMVideoElem *elem = message.videoElem;
TDeskVideoMessageCellData *videoData = [[TDeskVideoMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
videoData.videoPath = [elem.videoPath safePathString];
videoData.snapshotPath = [elem.snapshotPath safePathString];
videoData.videoItem = [[TDeskVideoItem alloc] init];
videoData.videoItem.uuid = elem.videoUUID;
videoData.videoItem.type = elem.videoType;
videoData.videoItem.length = elem.videoSize;
videoData.videoItem.duration = elem.duration;
videoData.snapshotItem = [[TDeskSnapshotItem alloc] init];
videoData.snapshotItem.uuid = elem.snapshotUUID;
// videoData.snapshotItem.type = elem.snaps;
videoData.snapshotItem.length = elem.snapshotSize;
videoData.snapshotItem.size = CGSizeMake(elem.snapshotWidth, elem.snapshotHeight);
videoData.reuseId = TVideoMessageCell_ReuseId;
return videoData;
}
+ (TDeskMessageCellData *)placeholderCellDataWithSnapshotUrl:(NSString *)snapshotUrl thubImage:(UIImage *)thubImage {
TDeskVideoMessageCellData *videoData = [[TDeskVideoMessageCellData alloc] initWithDirection:(MsgDirectionOutgoing)];
videoData.thumbImage = thubImage;
videoData.snapshotPath = [snapshotUrl safePathString];
videoData.videoItem = [[TDeskVideoItem alloc] init];
videoData.snapshotItem = [[TDeskSnapshotItem alloc] init];
videoData.snapshotItem.size = CGSizeEqualToSize(thubImage.size, CGSizeZero) ? CGSizeMake(kScale375(100), kScale375(100)) : thubImage.size;
videoData.reuseId = TVideoMessageCell_ReuseId;
videoData.avatarUrl = [NSURL URLWithString:[TDeskLogin getFaceUrl]];
videoData.isPlaceHolderCellData = YES;
return videoData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return TDeskIMCommonLocalizableString(TUIkitMessageTypeVideo);
}
- (Class)getReplyQuoteViewDataClass {
return NSClassFromString(@"TDeskVideoReplyQuoteViewData");
}
- (Class)getReplyQuoteViewClass {
return NSClassFromString(@"TDeskVideoReplyQuoteView");
}
- (instancetype)initWithDirection:(TDeskMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
_uploadProgress = 100;
_isDownloadingVideo = NO;
_isDownloadingSnapshot = NO;
if (direction == MsgDirectionIncoming) {
self.cellLayout = [TDeskMessageCellLayout incommingVideoMessageLayout];
} else {
self.cellLayout = [TDeskMessageCellLayout outgoingVideoMessageLayout];
}
}
return self;
}
- (void)downloadThumb:(TUIVideoMessageDownloadCallback)finish {
self.onFinish = finish;
[self downloadThumb];
}
- (void)downloadThumb {
BOOL isExist = NO;
NSString *path = [self getSnapshotPath:&isExist];
if (isExist) {
[self decodeThumb];
return;
}
if (self.isDownloadingSnapshot) {
return;
}
self.isDownloadingSnapshot = YES;
@weakify(self);
V2TIMMessage *imMsg = self.innerMessage;
if (imMsg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
// Avoid large files that slow down callback progress.
[self updateThumbProgress:1];
[imMsg.videoElem downloadSnapshot:path
progress:^(NSInteger curSize, NSInteger totalSize) {
[self updateThumbProgress:MAX(1, curSize * 100 / totalSize)];
}
succ:^{
@strongify(self);
self.isDownloadingSnapshot = NO;
[self updateThumbProgress:100];
[self decodeThumb];
}
fail:^(int code, NSString *msg) {
@strongify(self);
self.isDownloadingSnapshot = NO;
}];
}
}
- (void)updateThumbProgress:(NSUInteger)progress {
dispatch_async(dispatch_get_main_queue(), ^{
self.thumbProgress = progress;
});
}
- (void)decodeThumb {
BOOL isExist = NO;
NSString *path = [self getSnapshotPath:&isExist];
if (!isExist) {
return;
}
@weakify(self);
[TDeskTool asyncDecodeImage:path
complete:^(NSString *path, UIImage *image) {
@strongify(self);
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
self.thumbImage = image;
self.thumbProgress = 100;
if (self.onFinish) {
self.onFinish();
}
});
}];
}
- (void)downloadVideo {
BOOL isExist = NO;
NSString *path = [self getVideoPath:&isExist];
if (isExist) {
return;
}
if (self.isDownloadingVideo) {
return;
}
self.isDownloadingVideo = YES;
@weakify(self);
V2TIMMessage *imMsg = self.innerMessage;
if (imMsg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
[imMsg.videoElem downloadVideo:path
progress:^(NSInteger curSize, NSInteger totalSize) {
@strongify(self);
[self updateVideoProgress:curSize * 100 / totalSize];
}
succ:^{
@strongify(self);
self.isDownloadingVideo = NO;
[self updateVideoProgress:100];
dispatch_async(dispatch_get_main_queue(), ^{
self.videoPath = path;
});
}
fail:^(int code, NSString *msg) {
@strongify(self);
self.isDownloadingVideo = NO;
}];
}
}
- (void)updateVideoProgress:(NSUInteger)progress {
dispatch_async(dispatch_get_main_queue(), ^{
self.videoProgress = progress;
});
}
- (void)getVideoUrl:(void (^)(NSString *url))urlCallBack {
if (!urlCallBack) {
return;
}
if (self.videoUrl) {
urlCallBack(self.videoUrl);
}
@weakify(self);
V2TIMMessage *imMsg = self.innerMessage;
if (imMsg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
[imMsg.videoElem getVideoUrl:^(NSString *url) {
@strongify(self);
self.videoUrl = url;
urlCallBack(self.videoUrl);
}];
}
}
- (BOOL)isVideoExist {
BOOL isExist;
[self getVideoPath:&isExist];
return isExist;
}
- (NSString *)getVideoPath:(BOOL *)isExist {
NSString *path = nil;
BOOL isDir = NO;
*isExist = NO;
if (_videoPath && _videoPath.lastPathComponent.length) {
path = [NSString stringWithFormat:@"%@%@", TUIKit_Video_Path, _videoPath.lastPathComponent];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
if (!*isExist) {
if (_videoItem) {
if (_videoItem.uuid && _videoItem.uuid.length && _videoItem.type && _videoItem.type.length) {
path = [NSString stringWithFormat:@"%@%@.%@", TUIKit_Video_Path, _videoItem.uuid, _videoItem.type];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
}
}
if (*isExist) {
_videoPath = path;
}
return path;
}
- (NSString *)getSnapshotPath:(BOOL *)isExist {
NSString *path = nil;
BOOL isDir = NO;
*isExist = NO;
if (_snapshotPath && _snapshotPath.length) {
path = [NSString stringWithFormat:@"%@%@", TUIKit_Video_Path, _snapshotPath.lastPathComponent];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
if (!*isExist) {
if (_snapshotItem) {
if (_snapshotItem.uuid && _snapshotItem.uuid.length) {
path = [NSString stringWithFormat:@"%@%@", TUIKit_Video_Path, _snapshotItem.uuid];
path = [TUIKit_Video_Path stringByAppendingString:_snapshotItem.uuid];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
}
}
return path;
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, TUIVoiceAudioPlaybackStyle) {
TUIVoiceAudioPlaybackStyleLoudspeaker = 1,
TUIVoiceAudioPlaybackStyleHandset = 2,
};
@interface TDeskVoiceMessageCellData : TDeskBubbleMessageCellData
@property(nonatomic, strong) NSString *path;
@property(nonatomic, strong) NSString *uuid;
@property(nonatomic, assign) int duration;
@property(nonatomic, assign) int length;
@property(nonatomic, assign) BOOL isDownloading;
@property(nonatomic, assign) BOOL isPlaying;
@property(nonatomic, assign) CGFloat voiceHeight;
@property(nonatomic, assign) NSTimeInterval currentTime;
/**
*
* Play animation image
* An animation used to implement the "sonic image" fade of the speech as it plays.
* If you want to customize the implementation of other kinds of animation icons, you can refer to the implementation of voiceAnimationIamges here.
*/
@property NSArray<UIImage *> *voiceAnimationImages;
/**
*
* voice icon
* Animated icon to show when the speech is not playing.
*/
@property UIImage *voiceImage;
@property(nonatomic, assign) CGFloat voiceTop;
/**
*
* Top margin of voice message
* This value is used to determine the position of the bubble, which is convenient for UI layout of the content in the bubble.
* If the value is abnormal or set arbitrarily, UI errors such as message position dislocation will occur.
*/
@property(nonatomic, class) CGFloat incommingVoiceTop;
@property(nonatomic, class) CGFloat outgoingVoiceTop;
- (void)stopVoiceMessage;
/**
* Begin to play voice. It will download the voice file from server if it not exists in local.
*/
- (void)playVoiceMessage;
@property(nonatomic, copy) void (^audioPlayerDidFinishPlayingBlock)(void);
//The style of audio playback.
+ (TUIVoiceAudioPlaybackStyle)getAudioplaybackStyle;
+ (void)changeAudioPlaybackStyle;
@end
NS_ASSUME_NONNULL_END
//
// TDeskVoiceMessageCellData.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIVoiceMessageCellData.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
#import <TDeskCore/TDesk_TUIThemeManager.h>
@import AVFoundation;
@interface TDeskVoiceMessageCellData () <AVAudioPlayerDelegate>
@property AVAudioPlayer *audioPlayer;
@property NSString *wavPath;
@property(nonatomic, strong) NSTimer *timer;
@end
@implementation TDeskVoiceMessageCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
V2TIMSoundElem *elem = message.soundElem;
TDeskVoiceMessageCellData *soundData = [[TDeskVoiceMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
soundData.duration = elem.duration;
soundData.length = elem.dataSize;
soundData.uuid = elem.uuid;
soundData.reuseId = TVoiceMessageCell_ReuseId;
soundData.path = elem.path;
return soundData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return TDeskIMCommonLocalizableString(TUIKitMessageTypeVoice); // @"[Voice]";
}
- (Class)getReplyQuoteViewDataClass {
return NSClassFromString(@"TDeskVoiceReplyQuoteViewData");
}
- (Class)getReplyQuoteViewClass {
return NSClassFromString(@"TDeskVoiceReplyQuoteView");
}
- (instancetype)initWithDirection:(TDeskMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
if (direction == MsgDirectionIncoming) {
self.cellLayout = [TDeskMessageCellLayout incommingVoiceMessageLayout];
_voiceImage = TUIChatDynamicImage(@"chat_voice_message_receiver_voice_normal_img",
[[TDeskImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_receiver_normal")]);
_voiceImage = [_voiceImage rtl_imageFlippedForRightToLeftLayoutDirection];
_voiceAnimationImages = [NSArray arrayWithObjects:[self.class formatImageByName:@"message_voice_receiver_playing_1"],
[self.class formatImageByName:@"message_voice_receiver_playing_2"],
[self.class formatImageByName:@"message_voice_receiver_playing_3"], nil];
_voiceTop = [[self class] incommingVoiceTop];
} else {
self.cellLayout = [TDeskMessageCellLayout outgoingVoiceMessageLayout];
_voiceImage = TUIChatDynamicImage(@"chat_voice_message_sender_voice_normal_img",
[[TDeskImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_sender_normal")]);
_voiceImage = [_voiceImage rtl_imageFlippedForRightToLeftLayoutDirection];
_voiceAnimationImages = [NSArray arrayWithObjects:[self.class formatImageByName:@"message_voice_sender_playing_1"],
[self.class formatImageByName:@"message_voice_sender_playing_2"],
[self.class formatImageByName:@"message_voice_sender_playing_3"], nil];
_voiceTop = [[self class] outgoingVoiceTop];
}
_voiceHeight = 21;
}
return self;
}
+ (UIImage *)formatImageByName:(NSString *)imgName {
NSString *path = TUIChatImagePath(imgName);
UIImage *img = [[TDeskImageCache sharedInstance] getResourceFromCache:path];
return [img rtl_imageFlippedForRightToLeftLayoutDirection];
}
- (NSString *)getVoicePath:(BOOL *)isExist {
NSString *path = nil;
BOOL isDir = false;
*isExist = NO;
if (self.direction == MsgDirectionOutgoing) {
if (_path.length) {
path = [NSString stringWithFormat:@"%@%@", TUIKit_Voice_Path, _path.lastPathComponent];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
}
if (!*isExist) {
if (_uuid.length) {
path = [NSString stringWithFormat:@"%@%@.amr", TUIKit_Voice_Path, _uuid];
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
if (!isDir) {
*isExist = YES;
}
}
}
}
return path;
}
- (V2TIMSoundElem *)getIMSoundElem {
V2TIMMessage *imMsg = self.innerMessage;
if (imMsg.elemType == V2TIM_ELEM_TYPE_SOUND) {
return imMsg.soundElem;
}
return nil;
}
- (void)playVoiceMessage {
if (self.isPlaying) {
[self stopVoiceMessage];
return;
}
self.isPlaying = YES;
if (self.innerMessage.localCustomInt == 0) self.innerMessage.localCustomInt = 1;
V2TIMSoundElem *imSound = [self getIMSoundElem];
BOOL isExist = NO;
if (self.uuid.length == 0) {
self.uuid = imSound.uuid;
}
NSString *path = [self getVoicePath:&isExist];
if (isExist) {
[self playInternal:path];
} else {
if (self.isDownloading) {
return;
}
//
self.isDownloading = YES;
@weakify(self);
[imSound downloadSound:path
progress:^(NSInteger curSize, NSInteger totalSize) {
}
succ:^{
@strongify(self);
self.isDownloading = NO;
[self playInternal:path];
}
fail:^(int code, NSString *msg) {
@strongify(self);
self.isDownloading = NO;
[self stopVoiceMessage];
}];
}
}
- (void)playInternal:(NSString *)path {
if (!self.isPlaying) return;
// play current
TUIVoiceAudioPlaybackStyle playbackStyle = [self.class getAudioplaybackStyle];
if(playbackStyle == TUIVoiceAudioPlaybackStyleHandset) {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
}
else {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
}
NSURL *url = [NSURL fileURLWithPath:path];
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
self.audioPlayer.delegate = self;
bool result = [self.audioPlayer play];
if (!result) {
self.wavPath = [[path stringByDeletingPathExtension] stringByAppendingString:@".wav"];
NSURL *url = [NSURL fileURLWithPath:self.wavPath];
[self.audioPlayer stop];
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
self.audioPlayer.delegate = self;
[self.audioPlayer play];
}
@weakify(self);
if (@available(iOS 10.0, *)) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1
repeats:YES
block:^(NSTimer *_Nonnull timer) {
@strongify(self);
[self updateProgress];
}];
} else {
// Fallback on earlier versions
}
}
//The style of audio playback.
+ (TUIVoiceAudioPlaybackStyle)getAudioplaybackStyle {
NSString *style = [NSUserDefaults.standardUserDefaults objectForKey:@"tdesk_audioPlaybackStyle"];
if ([style isEqualToString:@"1"]) {
return TUIVoiceAudioPlaybackStyleLoudspeaker;
} else if ([style isEqualToString:@"2"]) {
return TUIVoiceAudioPlaybackStyleHandset;
}
return TUIVoiceAudioPlaybackStyleLoudspeaker;
}
+ (void)changeAudioPlaybackStyle {
TUIVoiceAudioPlaybackStyle style = [self getAudioplaybackStyle];
if (style == TUIVoiceAudioPlaybackStyleLoudspeaker) {
[NSUserDefaults.standardUserDefaults setObject:@"2" forKey:@"tdesk_audioPlaybackStyle"];
}
else {
[NSUserDefaults.standardUserDefaults setObject:@"1" forKey:@"tdesk_audioPlaybackStyle"];
}
[NSUserDefaults.standardUserDefaults synchronize];
}
- (void)updateProgress {
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
self.currentTime = self.audioPlayer.currentTime;
});
}
- (void)stopVoiceMessage {
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer stop];
self.audioPlayer = nil;
}
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
self.isPlaying = NO;
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
{
[self stopVoiceMessage];
[[NSFileManager defaultManager] removeItemAtPath:self.wavPath error:nil];
if (self.audioPlayerDidFinishPlayingBlock) {
self.audioPlayerDidFinishPlayingBlock();
}
}
static CGFloat gIncommingVoiceTop = 12;
+ (void)setIncommingVoiceTop:(CGFloat)incommingVoiceTop {
gIncommingVoiceTop = incommingVoiceTop;
}
+ (CGFloat)incommingVoiceTop {
return gIncommingVoiceTop;
}
static CGFloat gOutgoingVoiceTop = 12;
+ (void)setOutgoingVoiceTop:(CGFloat)outgoingVoiceTop {
gOutgoingVoiceTop = outgoingVoiceTop;
}
+ (CGFloat)outgoingVoiceTop {
return gOutgoingVoiceTop;
}
@end
//
// TDeskEvaluationCellData.h
// TUIChat
//
// Created by xia on 2022/6/10.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskEvaluationCellData : TDeskBubbleMessageCellData
@property(nonatomic, assign) NSInteger score;
@property(nonatomic, copy) NSString *desc;
@property(nonatomic, copy) NSString *comment;
@end
NS_ASSUME_NONNULL_END
//
// TDeskEvaluationCellData.m
// TUIChat
//
// Created by xia on 2022/6/10.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIEvaluationCellData.h"
@implementation TDeskEvaluationCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
if (param == nil) {
return nil;
}
TDeskEvaluationCellData *cellData = [[TDeskEvaluationCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
cellData.innerMessage = message;
cellData.desc = message.customElem.desc;
cellData.score = [param[@"score"] integerValue];
cellData.comment = param[@"comment"];
return cellData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
return message.customElem.desc;
}
- (CGSize)contentSize {
CGRect rect = [self.comment boundingRectWithSize:CGSizeMake(215, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]}
context:nil];
CGSize size = CGSizeMake(245, ceilf(rect.size.height));
size.height += self.comment.length > 0 ? 88 : 50;
return size;
}
@end
//
// MyCustomCellData.h
// TUIKitDemo
//
// Created by annidyfeng on 2019/6/10.
// Copyright © 2019 Tencent. All rights reserved.
//
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskLinkCellData : TDeskBubbleMessageCellData
@property NSString *text;
@property NSString *link;
@end
NS_ASSUME_NONNULL_END
//
// MyCustomCellData.m
// TUIKitDemo
//
// Created by annidyfeng on 2019/6/10.
// Copyright © 2019 Tencent. All rights reserved.
//
#import "TDesk_TUILinkCellData.h"
@implementation TDeskLinkCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
TDeskLinkCellData *cellData = [[TDeskLinkCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
cellData.msgID = message.msgID;
cellData.text = param[@"text"];
cellData.link = param[@"link"];
cellData.avatarUrl = [NSURL URLWithString:message.faceURL];
return cellData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
return param[@"text"];
}
- (CGSize)contentSize {
CGFloat textMaxWidth = 245.f;
CGRect rect = [self.text boundingRectWithSize:CGSizeMake(textMaxWidth, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]}
context:nil];
CGSize size = CGSizeMake(textMaxWidth + 15, rect.size.height + 56);
return size;
}
@end
//
// TDeskOrderCellData.h
// TUIChat
//
// Created by xia on 2022/6/10.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
NS_ASSUME_NONNULL_BEGIN
@interface TDeskOrderCellData : TDeskBubbleMessageCellData
@property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) NSString *desc;
@property(nonatomic, copy) NSString *price;
@property(nonatomic, copy) NSString *imageUrl;
@property(nonatomic, copy) NSString *link;
@end
NS_ASSUME_NONNULL_END
//
// TDeskOrderCellData.m
// TUIChat
//
// Created by xia on 2022/6/10.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIOrderCellData.h"
@implementation TDeskOrderCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
TDeskOrderCellData *cellData = [[TDeskOrderCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
cellData.innerMessage = message;
cellData.msgID = message.msgID;
cellData.title = param[@"title"];
cellData.desc = param[@"description"];
cellData.imageUrl = param[@"imageUrl"];
cellData.link = param[@"link"];
cellData.price = param[@"price"];
cellData.avatarUrl = [NSURL URLWithString:message.faceURL];
return cellData;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
return param[@"title"];
}
- (CGSize)contentSize {
CGSize size = CGSizeMake(245, 80);
return size;
}
@end
//
// TDeskFileReplyQuoteViewData.h
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIVoiceReplyQuoteViewData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDeskFileReplyQuoteViewData : TDeskVoiceReplyQuoteViewData
@end
NS_ASSUME_NONNULL_END
//
// TDeskFileReplyQuoteViewData.m
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIFileReplyQuoteViewData.h"
#import <TDeskCore/TDesk_TUIThemeManager.h>
#import "TDesk_TUIFileMessageCellData.h"
@implementation TDeskFileReplyQuoteViewData
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData {
if (originCellData == nil) {
return nil;
}
if (![originCellData isKindOfClass:TDeskFileMessageCellData.class]) {
return nil;
}
TDeskFileReplyQuoteViewData *myData = [[TDeskFileReplyQuoteViewData alloc] init];
myData.text = [(TDeskFileMessageCellData *)originCellData fileName];
myData.icon = TUIChatCommonBundleImage(@"msg_file");
myData.originCellData = originCellData;
return myData;
}
@end
//
// TDeskImageReplyQuoteViewData.h
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIReplyMessageCellData.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, TDeskImageReplyQuoteStatus) {
TUIImageReplyQuoteStatusInit,
TUIImageReplyQuoteStatusDownloading,
TUIImageReplyQuoteStatusSuccess,
TUIImageReplyQuoteStatusFailed,
};
@interface TDeskImageReplyQuoteViewData : TDeskReplyQuoteViewData
@property(nonatomic, assign) TDeskImageReplyQuoteStatus imageStatus;
@property(nonatomic, strong) UIImage *image;
@property(nonatomic, assign) CGSize imageSize;
+ (CGSize)displaySizeWithOriginSize:(CGSize)originSize;
- (void)downloadImage;
@end
NS_ASSUME_NONNULL_END
//
// TDeskImageReplyQuoteViewData.m
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIImageReplyQuoteViewData.h"
#import "TDesk_TUIImageMessageCellData.h"
@implementation TDeskImageReplyQuoteViewData
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData {
if (originCellData == nil) {
return nil;
}
if (![originCellData isKindOfClass:TDeskImageMessageCellData.class]) {
return nil;
}
TDeskImageReplyQuoteViewData *myData = [[TDeskImageReplyQuoteViewData alloc] init];
V2TIMImage *thumb = nil;
for (V2TIMImage *image in originCellData.innerMessage.imageElem.imageList) {
if (image.type == V2TIM_IMAGE_TYPE_THUMB) {
thumb = image;
break;
}
}
myData.imageSize = [TDeskImageReplyQuoteViewData displaySizeWithOriginSize:CGSizeMake(thumb ? thumb.width : 60, thumb ? thumb.height : 60)];
myData.originCellData = originCellData;
myData.imageStatus = TUIImageReplyQuoteStatusInit;
return myData;
}
- (CGSize)contentSize:(CGFloat)maxWidth {
return self.imageSize;
}
+ (CGSize)displaySizeWithOriginSize:(CGSize)originSize {
if (originSize.width == 0 || originSize.width == 0) {
return CGSizeZero;
}
CGFloat max = 60;
CGFloat w = 0, h = 0;
if (originSize.width > originSize.height) {
w = max;
h = max * originSize.height / originSize.width;
} else {
w = max * originSize.width / originSize.height;
h = max;
}
return CGSizeMake(w, h);
}
- (void)downloadImage {
@weakify(self);
self.imageStatus = TUIImageReplyQuoteStatusDownloading;
if ([self.originCellData isKindOfClass:TDeskImageMessageCellData.class]) {
TDeskImageMessageCellData *imageData = (TDeskImageMessageCellData *)self.originCellData;
[imageData downloadImage:TImage_Type_Thumb
finish:^{
@strongify(self);
self.image = imageData.thumbImage;
self.imageStatus = TUIImageReplyQuoteStatusSuccess;
if (self.onFinish) {
self.onFinish();
}
}];
}
}
@end
//
// TDeskMergeReplyQuoteViewData.h
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIReplyMessageCellData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDeskMergeReplyQuoteViewData : TDeskReplyQuoteViewData
@property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) NSString *abstract;
@end
NS_ASSUME_NONNULL_END
//
// TDeskMergeReplyQuoteViewData.m
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIMergeReplyQuoteViewData.h"
#import <TDeskCommon/TDesk_NSString+TUIEmoji.h>
#import "TDesk_TUIMergeMessageCellData.h"
@implementation TDeskMergeReplyQuoteViewData
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData {
if (originCellData == nil) {
return nil;
}
if (![originCellData isKindOfClass:TDeskMergeMessageCellData.class]) {
return nil;
}
TDeskMergeReplyQuoteViewData *myData = [[TDeskMergeReplyQuoteViewData alloc] init];
myData.title = [(TDeskMergeMessageCellData *)originCellData title];
NSAttributedString *abstract = [(TDeskMergeMessageCellData *)originCellData abstractAttributedString];
myData.abstract = abstract.string;
myData.originCellData = originCellData;
return myData;
}
- (CGSize)contentSize:(CGFloat)maxWidth {
CGFloat singleHeight = [UIFont systemFontOfSize:10.0].lineHeight;
NSAttributedString *titleAttributeString = [self.title getFormatEmojiStringWithFont:[UIFont systemFontOfSize:10.0] emojiLocations:nil];
CGRect titleRect = [titleAttributeString boundingRectWithSize:CGSizeMake(maxWidth, singleHeight)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
context:nil];
CGFloat width = titleRect.size.width;
CGFloat height = titleRect.size.height;
return CGSizeMake(MIN(width, maxWidth), height);
}
@end
//
// TDeskReplyMessageCellData.h
// TUIChat
//
// Created by harvy on 2021/11/11.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <ImSDK_Plus/ImSDK_Plus.h>
#import <TDeskCommon/TDesk_TUIBubbleMessageCellData.h>
#import "TDesk_TUIChatDefine.h"
#import "TDesk_TUIReplyQuoteViewData.h"
NS_ASSUME_NONNULL_BEGIN
@class TDeskReplyMessageCellData;
@interface TDeskReplyMessageCellData : TDeskBubbleMessageCellData
/**
* The original message ID
*/
@property(nonatomic, copy) NSString *__nullable originMsgID;
/**
* The default abstract of original message
*/
@property(nonatomic, copy) NSString *__nullable msgAbstract;
/**
* The sender of original message
*/
@property(nonatomic, copy) NSString *__nullable sender;
/**
* The sender of original message
*/
@property(nonatomic, copy) NSString *__nullable faceURL;
/**
* The type of original message
*/
@property(nonatomic, assign) V2TIMElemType originMsgType;
/**
*
* Original message
*/
@property(nonatomic, strong) V2TIMMessage *__nullable originMessage;
@property(nonatomic, strong) TDeskMessageCellData *originCellData;
@property(nonatomic, strong) TDeskReplyQuoteViewData *quoteData;
@property(nonatomic, assign) BOOL showRevokedOriginMessage;
/**
* The content of replying the original message
*/
@property(nonatomic, copy) NSString *content;
@property(nonatomic, strong, readonly) NSAttributedString *attributeString;
/**
* The size of quote view, including @senderSize and @quotePlaceholderSize
*/
@property(nonatomic, assign) CGSize quoteSize;
/**
* The size of label which displays the sender displayname
*/
@property(nonatomic, assign) CGSize senderSize;
/**
* The size of customize quote view
*/
@property(nonatomic, assign) CGSize quotePlaceholderSize;
/**
* The size of label which displays the content of replying the original message.
*/
@property(nonatomic, assign) CGSize replyContentSize;
@property(nonatomic, copy) TUIReplyAsyncLoadFinish onFinish;
/**
* The message ID of the root message which is replyed at first.
*/
@property(nonatomic, copy) NSString *messageRootID;
@property(nonatomic) UIColor *textColor;
@property(nonatomic, strong) NSString *selectContent;
@property(nonatomic, strong) NSMutableArray<NSDictionary<NSValue *, NSAttributedString *> *> *emojiLocations;
// Deprecated
// Search `loadOriginMessageFromReplyData` in TDeskMessageDataProvider+MessageDeal
//- (void)loadOriginMessage:(void(^)(void))callback;
- (TDeskReplyQuoteViewData *)getQuoteData:(TDeskMessageCellData *)originCellData;
- (CGSize)quotePlaceholderSizeWithType:(V2TIMElemType)type data:(TDeskReplyQuoteViewData *)data;
@end
@interface TDeskReferenceMessageCellData : TDeskReplyMessageCellData
@property(nonatomic, assign) CGSize textSize;
@property(nonatomic, assign) CGPoint textOrigin;
@end
NS_ASSUME_NONNULL_END
//
// TDeskReplyMessageCellData.m
// TUIChat
//
// Created by harvy on 2021/11/11.
// Copyright © 2023 Tencent. All rights reserved.
//
/**
The protocol format of the custom field cloudMessageData of the message
{
"messageReply":{
"messageID": "xxxx0xxx=xx",
"messageAbstract":"origin message abstract..."
"messageSender":"NickName/99618",
"messageType": "1/2/..",
"version":"1",
}
}
*/
#import "TDesk_TUIReplyMessageCellData.h"
#import <TDeskCommon/TDesk_NSString+TUIEmoji.h>
#import "TDesk_TUIFileMessageCellData.h"
#import "TDesk_TUIImageMessageCellData.h"
#import "TDesk_TUIMergeMessageCellData.h"
#import "TDesk_TUITextMessageCellData.h"
#import "TDesk_TUIVideoMessageCellData.h"
#import "TDesk_TUIVoiceMessageCellData.h"
#import "TDesk_TUICloudCustomDataTypeCenter.h"
#import "TDesk_TUIFileReplyQuoteViewData.h"
#import "TDesk_TUIImageReplyQuoteViewData.h"
#import "TDesk_TUIMergeReplyQuoteViewData.h"
#import "TDesk_TUIReplyPreviewData.h"
#import "TDesk_TUITextReplyQuoteViewData.h"
#import "TDesk_TUIVideoReplyQuoteViewData.h"
#import "TDesk_TUIVoiceReplyQuoteViewData.h"
@implementation TDeskReplyMessageCellData
{
NSString *_sender;
}
- (void)setSender:(NSString *)sender {
_sender = sender;
}
- (NSString *__nullable)sender {
if (self.originMessage) {
return self.originMessage.nameCard ? : (self.originMessage.friendRemark ? : (self.originMessage.nickName ? : self.originMessage.sender));
}
return _sender;
}
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
if (message.cloudCustomData == nil) {
return nil;
}
__block TDeskReplyMessageCellData *replyData = nil;
[message doThingsInContainsCloudCustomOfDataType:TUICloudCustomDataType_MessageReply
callback:^(BOOL isContains, id obj) {
if (isContains) {
if (obj && [obj isKindOfClass:NSDictionary.class]) {
NSDictionary *reply = (NSDictionary *)obj;
// This message is a "reply message"
replyData = [[TDeskReplyMessageCellData alloc]
initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
replyData.reuseId = TReplyMessageCell_ReuseId;
replyData.originMsgID = reply[@"messageID"];
replyData.msgAbstract = reply[@"messageAbstract"];
replyData.sender = reply[@"messageSender"];
replyData.originMsgType = (V2TIMElemType)[reply[@"messageType"] integerValue];
replyData.content = message.textElem.text;
replyData.messageRootID = reply[@"messageRootID"];
}
}
}];
return replyData;
}
- (instancetype)initWithDirection:(TDeskMsgDirection)direction {
self = [super initWithDirection:direction];
if (self) {
if (direction == MsgDirectionIncoming) {
self.cellLayout = [TDeskMessageCellLayout incommingTextMessageLayout];
} else {
self.cellLayout = [TDeskMessageCellLayout outgoingTextMessageLayout];
}
_emojiLocations = [NSMutableArray array];
}
return self;
}
- (CGSize)quotePlaceholderSizeWithType:(V2TIMElemType)type data:(TDeskReplyQuoteViewData *)data {
if (data == nil) {
return CGSizeMake(20, 20);
}
return [data contentSize:TReplyQuoteView_Max_Width - 12];
}
- (TDeskReplyQuoteViewData *)getQuoteData:(TDeskMessageCellData *)originCellData {
TDeskReplyQuoteViewData *quoteData = nil;
Class class = [originCellData getReplyQuoteViewDataClass];
BOOL hasRiskContent = originCellData.innerMessage.hasRiskContent;
if (hasRiskContent && [TDeskIMConfig isClassicEntrance]){
// Return text reply data in default
TDeskTextReplyQuoteViewData *myData = [[TDeskTextReplyQuoteViewData alloc] init];
myData.text = [TDeskReplyPreviewData displayAbstract:self.originMsgType abstract:self.msgAbstract withFileName:NO isRisk:hasRiskContent];
quoteData = myData;
}
else if (class && [class respondsToSelector:@selector(getReplyQuoteViewData:)]) {
quoteData = [class getReplyQuoteViewData:originCellData];
}
else {
}
if (quoteData == nil) {
//
// Return text reply data in default
TDeskTextReplyQuoteViewData *myData = [[TDeskTextReplyQuoteViewData alloc] init];
myData.text = [TDeskReplyPreviewData displayAbstract:self.originMsgType abstract:self.msgAbstract withFileName:NO isRisk:hasRiskContent];
quoteData = myData;
}
quoteData.originCellData = originCellData;
@weakify(self);
quoteData.onFinish = ^{
@strongify(self);
if (self.onFinish) {
self.onFinish();
}
};
return quoteData;
}
@end
@implementation TDeskReferenceMessageCellData
+ (TDeskMessageCellData *)getCellData:(V2TIMMessage *)message {
if (message.cloudCustomData == nil) {
return nil;
}
__block TDeskReplyMessageCellData *replyData = nil;
[message doThingsInContainsCloudCustomOfDataType:TUICloudCustomDataType_MessageReference
callback:^(BOOL isContains, id obj) {
if (isContains) {
if (obj && [obj isKindOfClass:NSDictionary.class]) {
NSDictionary *reply = (NSDictionary *)obj;
if ([reply isKindOfClass:NSDictionary.class]) {
// This message is 「quote message」which indicating the original message
replyData = [[TDeskReferenceMessageCellData alloc]
initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
replyData.reuseId = TUIReferenceMessageCell_ReuseId;
replyData.originMsgID = reply[@"messageID"];
replyData.msgAbstract = reply[@"messageAbstract"];
replyData.sender = reply[@"messageSender"];
replyData.originMsgType = (V2TIMElemType)[reply[@"messageType"] integerValue];
replyData.content = message.textElem.text; // text only
}
}
}
}];
return replyData;
}
@end
//
// TDeskReplyPreviewData.h
// TUIChat
//
// Created by wyl on 2022/3/22.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TDesk_TUIChatDefine.h"
@class V2TIMMessage;
@interface TDeskReplyPreviewData : NSObject
/**
* The message ID of the replyed original message
*/
@property(nonatomic, copy) NSString *msgID;
/**
* The abstract of the replyed original message
*/
@property(nonatomic, copy) NSString *msgAbstract;
/**
* The sender's displayname of the replyed original message. Nickname is prior than userID.
*/
@property(nonatomic, copy) NSString *sender;
/**
* The faceURL of the replyed original message
*/
@property(nonatomic, copy) NSString *faceURL;
/**
* The message type of the replyed original message. For details, see the enumeration value of V2TIMElemType.
*/
@property(nonatomic, assign) NSInteger type;
/**
* The replyed original message
*/
@property(nonatomic, strong) V2TIMMessage *originMessage;
// Message reply root RootID (not necessarily the msgID of the originMessage above, but the ID of the message at the top)
@property(nonatomic, copy) NSString *messageRootID;
+ (NSString *)displayAbstract:(NSInteger)type abstract:(NSString *)abstract withFileName:(BOOL)withFilename isRisk:(BOOL)isRisk;
@end
@interface TDeskReferencePreviewData : TDeskReplyPreviewData
@end
//
// TDeskReplyPreviewData.m
// TUIChat
//
// Created by wyl on 2022/3/22.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIReplyPreviewData.h"
#import <TDeskCommon/TDesk_TIMDefine.h>
@implementation TDeskReplyPreviewData
+ (NSString *)displayAbstract:(NSInteger)type abstract:(NSString *)abstract withFileName:(BOOL)withFilename isRisk:(BOOL)isRisk {
NSString *text = abstract;
if (type == V2TIM_ELEM_TYPE_IMAGE) {
text = isRisk? TDeskIMCommonLocalizableString(TUIkitMessageTypeRiskImage):TDeskIMCommonLocalizableString(TUIkitMessageTypeImage);
} else if (type == V2TIM_ELEM_TYPE_VIDEO) {
text = isRisk? TDeskIMCommonLocalizableString(TUIkitMessageTypeRiskVideo):TDeskIMCommonLocalizableString(TUIkitMessageTypeVideo);
} else if (type == V2TIM_ELEM_TYPE_SOUND) {
text = isRisk? TDeskIMCommonLocalizableString(TUIkitMessageTypeRiskVoice):TDeskIMCommonLocalizableString(TUIKitMessageTypeVoice);
} else if (type == V2TIM_ELEM_TYPE_FACE) {
text = TDeskIMCommonLocalizableString(TUIKitMessageTypeAnimateEmoji);
} else if (type == V2TIM_ELEM_TYPE_FILE) {
if (withFilename) {
text = [NSString stringWithFormat:@"%@%@", TDeskIMCommonLocalizableString(TUIkitMessageTypeFile), abstract];
;
} else {
text = TDeskIMCommonLocalizableString(TUIkitMessageTypeFile);
}
}
return text;
}
@end
@implementation TDeskReferencePreviewData
@end
//
// TDeskReplyQuoteViewData.h
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "TDesk_TUIChatDefine.h"
@class TDeskMessageCellData;
NS_ASSUME_NONNULL_BEGIN
@interface TDeskReplyQuoteViewData : NSObject
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData;
- (CGSize)contentSize:(CGFloat)maxWidth;
/**
* If you want to download the custom reply content asynchronously, you need to call the callback after the download is complete, and the TUI will be
* automatically refreshed.
*/
@property(nonatomic, copy) TUIReplyQuoteAsyncLoadFinish onFinish;
@property(nonatomic, strong) TDeskMessageCellData *originCellData;
@property(nonatomic, assign) BOOL supportForReply;
@property(nonatomic, assign) BOOL showRevokedOriginMessage;
@end
NS_ASSUME_NONNULL_END
//
// TDeskReplyQuoteViewData.m
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIReplyQuoteViewData.h"
@implementation TDeskReplyQuoteViewData
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData {
return nil;
}
- (CGSize)contentSize:(CGFloat)maxWidth {
return CGSizeZero;
}
@end
//
// TDeskTextReplyQuoteViewData.h
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIReplyMessageCellData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDeskTextReplyQuoteViewData : TDeskReplyQuoteViewData
@property(nonatomic, copy) NSString *text;
@end
NS_ASSUME_NONNULL_END
//
// TDeskTextReplyQuoteViewData.m
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUITextReplyQuoteViewData.h"
#import <TDeskCommon/TDesk_NSString+TUIEmoji.h>
#import "TDesk_TUITextMessageCellData.h"
@implementation TDeskTextReplyQuoteViewData
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData {
if (originCellData == nil) {
return nil;
}
if (![originCellData isKindOfClass:TDeskTextMessageCellData.class]) {
return nil;
}
TDeskTextReplyQuoteViewData *myData = [[TDeskTextReplyQuoteViewData alloc] init];
myData.text = [(TDeskTextMessageCellData *)originCellData content];
myData.originCellData = originCellData;
return myData;
}
- (CGSize)contentSize:(CGFloat)maxWidth {
NSAttributedString *attributeString = nil;
BOOL showRevokeStr = (self.originCellData.innerMessage.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) &&
!self.showRevokedOriginMessage;
if (showRevokeStr) {
NSString * revokeStr = self.supportForReply?
TDeskIMCommonLocalizableString(TUIKitRepliesOriginMessageRevoke):
TDeskIMCommonLocalizableString(TUIKitReferenceOriginMessageRevoke);
attributeString = [revokeStr getFormatEmojiStringWithFont:[UIFont systemFontOfSize:10.0] emojiLocations:nil];
} else {
attributeString = [self.text getFormatEmojiStringWithFont:[UIFont systemFontOfSize:10.0] emojiLocations:nil];
}
CGSize size = [@"0" sizeWithAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:10.0]}];
CGRect rect = [attributeString boundingRectWithSize:CGSizeMake(maxWidth, size.height * 2)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
context:nil];
CGFloat h = rect.size.height < size.height * 2 ? rect.size.height : size.height * 2;
if (showRevokeStr && self.supportForReply) {
h = size.height *2;
}
return CGSizeMake(rect.size.width, h);
}
@end
//
// TDeskVideoReplyQuoteViewData.h
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIImageReplyQuoteViewData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDeskVideoReplyQuoteViewData : TDeskImageReplyQuoteViewData
@end
NS_ASSUME_NONNULL_END
//
// TDeskVideoReplyQuoteViewData.m
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIVideoReplyQuoteViewData.h"
#import "TDesk_TUIVideoMessageCellData.h"
@implementation TDeskVideoReplyQuoteViewData
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData {
if (originCellData == nil) {
return nil;
}
if (![originCellData isKindOfClass:TDeskVideoMessageCellData.class]) {
return nil;
}
TDeskVideoReplyQuoteViewData *myData = [[TDeskVideoReplyQuoteViewData alloc] init];
CGSize snapSize = CGSizeMake(originCellData.innerMessage.videoElem ? originCellData.innerMessage.videoElem.snapshotWidth : 0,
originCellData.innerMessage.videoElem ? originCellData.innerMessage.videoElem.snapshotHeight : 0);
myData.imageSize = [TDeskVideoReplyQuoteViewData displaySizeWithOriginSize:snapSize];
myData.originCellData = originCellData;
return myData;
}
- (void)downloadImage {
[super downloadImage];
@weakify(self);
if ([self.originCellData isKindOfClass:TDeskVideoMessageCellData.class]) {
TDeskVideoMessageCellData *videoData = (TDeskVideoMessageCellData *)self.originCellData;
[videoData downloadThumb:^{
@strongify(self);
self.image = videoData.thumbImage;
if (self.onFinish) {
self.onFinish();
}
}];
}
}
@end
//
// TDeskVoiceReplyQuoteViewData.h
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUITextReplyQuoteViewData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDeskVoiceReplyQuoteViewData : TDeskTextReplyQuoteViewData
@property(nonatomic, strong) UIImage *icon;
@end
NS_ASSUME_NONNULL_END
//
// TDeskVoiceReplyQuoteViewData.m
// TUIChat
//
// Created by harvy on 2021/11/25.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIVoiceReplyQuoteViewData.h"
#import <TDeskCommon/TDesk_NSString+TUIEmoji.h>
#import <TDeskCore/TDesk_TUIThemeManager.h>
#import "TDesk_TUIVoiceMessageCellData.h"
@implementation TDeskVoiceReplyQuoteViewData
+ (instancetype)getReplyQuoteViewData:(TDeskMessageCellData *)originCellData {
if (originCellData == nil) {
return nil;
}
if (![originCellData isKindOfClass:TDeskVoiceMessageCellData.class]) {
return nil;
}
TDeskVoiceReplyQuoteViewData *myData = [[TDeskVoiceReplyQuoteViewData alloc] init];
myData.text = [NSString stringWithFormat:@"%d\"", [(TDeskVoiceMessageCellData *)originCellData duration]];
myData.icon = TUIChatCommonBundleImage(@"voice_reply");
myData.originCellData = originCellData;
return myData;
}
- (CGSize)contentSize:(CGFloat)maxWidth {
CGFloat marginWidth = 18;
CGSize size = [@"0" sizeWithAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:10.0]}];
CGRect rect = [self.text boundingRectWithSize:CGSizeMake(maxWidth - marginWidth, size.height)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:10.0]}
context:nil];
return CGSizeMake(rect.size.width + marginWidth, size.height);
}
@end
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import <TDeskCommon/TDesk_TUIMessageCellData.h>
#import "TDesk_TUIChatConversationModel.h"
@class TDeskChatBaseDataProvider;
NS_ASSUME_NONNULL_BEGIN
@protocol TDeskChatBaseDataProviderDelegate <NSObject>
@required
- (NSString *)dataProvider:(TDeskChatBaseDataProvider *)dataProvider mergeForwardTitleWithMyName:(NSString *)name;
- (NSString *)dataProvider:(TDeskChatBaseDataProvider *)dataProvider mergeForwardMsgAbstactForMessage:(V2TIMMessage *)message;
- (void)dataProvider:(TDeskChatBaseDataProvider *)dataProvider sendMessage:(V2TIMMessage *)message;
- (void)onSelectPhotoMoreCellData;
- (void)onTakePictureMoreCellData;
- (void)onTakeVideoMoreCellData;
- (void)onSelectFileMoreCellData;
@end
@interface TDeskChatBaseDataProvider : NSObject
@property(nonatomic, weak) id<TDeskChatBaseDataProviderDelegate> delegate;
- (void)getForwardMessageWithCellDatas:(NSArray<TDeskMessageCellData *> *)uiMsgs
toTargets:(NSArray<TDeskChatConversationModel *> *)targets
Merge:(BOOL)merge
ResultBlock:(void (^)(TDeskChatConversationModel *targetConversation, NSArray<V2TIMMessage *> *msgs))resultBlock
fail:(nullable V2TIMFail)fail;
- (NSString *)abstractDisplayWithMessage:(V2TIMMessage *)msg;
@end
#pragma mark - TDeskChatBaseDataProvider (IMSDK)
@interface TDeskChatBaseDataProvider (IMSDK)
+ (void)getTotalUnreadMessageCountWithSuccBlock:(void (^)(UInt64 totalCount))succ fail:(nullable V2TIMFail)fail;
+ (void)saveDraftWithConversationID:(NSString *)conversationId Text:(NSString *)text;
+ (void)findMessages:(NSArray *)msgIDs callback:(void (^)(BOOL succ, NSString *error_message, NSArray *msgs))callback;
#pragma mark - C2C
+ (void)getFriendInfoWithUserId:(nullable NSString *)userID
SuccBlock:(void (^)(V2TIMFriendInfoResult *friendInfoResult))succ
failBlock:(nullable V2TIMFail)fail;
+ (void)getUserInfoWithUserId:(NSString *)userID SuccBlock:(void (^)(V2TIMUserFullInfo *userInfo))succ failBlock:(nullable V2TIMFail)fail;
#pragma mark - Group
+ (void)getGroupInfoWithGroupID:(NSString *)groupID SuccBlock:(void (^)(V2TIMGroupInfoResult *groupResult))succ failBlock:(nullable V2TIMFail)fail;
@end
NS_ASSUME_NONNULL_END
//
// TDeskMessageDataProvider+ProtectedAPI.h
// TXIMSDK_TUIKit_iOS
//
// Created by kayev on 2021/7/9.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIMessageBaseDataProvider.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDeskMessageBaseDataProvider ()
@property(nonatomic) NSMutableArray<TDeskMessageCellData *> *uiMsgs_;
@property(nonatomic) NSMutableDictionary<NSString *, NSNumber *> *heightCache_;
@property(nonatomic) BOOL isLoadingData;
@property(nonatomic) BOOL isNoMoreMsg;
@property(nonatomic) BOOL isFirstLoad;
@property(nonatomic) V2TIMMessage *msgForDate;
- (nullable TDeskMessageCellData *)getSystemMsgFromDate:(NSDate *)date;
- (NSMutableArray *)transUIMsgFromIMMsg:(NSArray *)msgs;
- (void)onRecvNewMessage:(V2TIMMessage *)msg;
@end
NS_ASSUME_NONNULL_END
//
// TDeskMessageDataProvider+MessageDeal.h
// TUIChat
//
// Created by wyl on 2022/3/22.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TDesk_TUIMessageDataProvider.h"
#import "TDesk_TUIReplyMessageCellData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDeskMessageDataProvider (MessageDeal)
- (void)loadOriginMessageFromReplyData:(TDeskReplyMessageCellData *)replycellData dealCallback:(void (^)(void))callback;
@end
NS_ASSUME_NONNULL_END
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment