提交
This commit is contained in:
16
TUIKit/TUIChat/BaseCellData/Base/TUIGroupCreatedCellData.h
Normal file
16
TUIKit/TUIChat/BaseCellData/Base/TUIGroupCreatedCellData.h
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
* This file declares the data source for TUIGroupCreatedCell
|
||||
*/
|
||||
|
||||
#import <TIMCommon/TUISystemMessageCellData.h>
|
||||
|
||||
@interface TUIGroupCreatedCellData : TUISystemMessageCellData
|
||||
|
||||
@property(nonatomic, copy) NSString *opUser;
|
||||
@property(nonatomic, strong) NSNumber *cmd;
|
||||
|
||||
@end
|
||||
87
TUIKit/TUIChat/BaseCellData/Base/TUIGroupCreatedCellData.m
Normal file
87
TUIKit/TUIChat/BaseCellData/Base/TUIGroupCreatedCellData.m
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// TUIGroupCreatedCellData.m
|
||||
// TUIKitDemo
|
||||
//
|
||||
// Created by annidyfeng on 2019/6/10.
|
||||
// Copyright © 2019 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIGroupCreatedCellData.h"
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
|
||||
@implementation TUIGroupCreatedCellData
|
||||
|
||||
+ (TUIGroupCreatedCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
TUIGroupCreatedCellData *cellData = [[TUIGroupCreatedCellData 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 = TIMCommonLocalizableString(TUICommunityCreateTipsMessage);
|
||||
} else {
|
||||
localizableContent = TIMCommonLocalizableString(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 = [TUITool 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 = TIMCommonLocalizableString(TUICommunityCreateTipsMessage);
|
||||
} else {
|
||||
localizableContent = TIMCommonLocalizableString(TUIGroupCreateTipsMessage);
|
||||
}
|
||||
}
|
||||
NSString * opUser = [self.class getOpUserName:msg]?:param[@"opUser"];
|
||||
NSString *str = [NSString stringWithFormat:@"\"%@\" %@", opUser, localizableContent];
|
||||
return rtlString(str);
|
||||
}
|
||||
|
||||
@end
|
||||
58
TUIKit/TUIChat/BaseCellData/Base/TUIInputMoreCellData.h
Normal file
58
TUIKit/TUIChat/BaseCellData/Base/TUIInputMoreCellData.h
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
// 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.
|
||||
* - TUIInputMoreCellData 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);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIInputMoreCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 【Module name】TUIInputMoreCellData
|
||||
* 【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 TUIInputMoreCellData : 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
|
||||
9
TUIKit/TUIChat/BaseCellData/Base/TUIInputMoreCellData.m
Normal file
9
TUIKit/TUIChat/BaseCellData/Base/TUIInputMoreCellData.m
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import "TUIInputMoreCellData.h"
|
||||
|
||||
@implementation TUIInputMoreCellData
|
||||
|
||||
@end
|
||||
39
TUIKit/TUIChat/BaseCellData/Base/TUIMemberCellData.h
Normal file
39
TUIKit/TUIChat/BaseCellData/Base/TUIMemberCellData.h
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/******************************************************************************
|
||||
*
|
||||
* This file declares the TUIMemberCellData class.
|
||||
* It provides a data source for the TUIMemberCell class, which is mainly used in the message read member list interface, etc.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMemberDescribeCellData : TUICommonCellData
|
||||
|
||||
@property(nonatomic, copy) NSString *title;
|
||||
@property(nonatomic, copy) UIImage *icon;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUIMemberCellData : TUICommonCellData
|
||||
|
||||
@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
|
||||
47
TUIKit/TUIChat/BaseCellData/Base/TUIMemberCellData.m
Normal file
47
TUIKit/TUIChat/BaseCellData/Base/TUIMemberCellData.m
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// TUIMemberCellData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by xia on 2022/3/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIMemberCellData.h"
|
||||
|
||||
@implementation TUIMemberDescribeCellData
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMemberCellData
|
||||
|
||||
- (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
|
||||
67
TUIKit/TUIChat/BaseCellData/Base/TUITextMessageCellData.h
Normal file
67
TUIKit/TUIChat/BaseCellData/Base/TUITextMessageCellData.h
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
* This file declares the TUITextMessageCellData class.
|
||||
* This class inherits from TUIBubbleMessageCellData and is used to store a series of data and information required by the text message unit.
|
||||
*/
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* 【Module name】 TUITextMessageCellData
|
||||
* 【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 TUITextMessageCellData : TUIBubbleMessageCellData
|
||||
|
||||
/**
|
||||
* 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
|
||||
124
TUIKit/TUIChat/BaseCellData/Base/TUITextMessageCellData.m
Normal file
124
TUIKit/TUIChat/BaseCellData/Base/TUITextMessageCellData.m
Normal file
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// TUITextMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUITextMessageCellData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/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 TUITextMessageCellData ()
|
||||
|
||||
@property(nonatomic, assign) CGSize size;
|
||||
@property(nonatomic, assign) CGFloat containerWidth;
|
||||
|
||||
@property(nonatomic, strong) NSMutableAttributedString *attributedString;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUITextMessageCellData
|
||||
{
|
||||
NSString *_content;
|
||||
}
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
TUITextMessageCellData *textData = [[TUITextMessageCellData 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(@"TUITextReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUITextReplyQuoteView");
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingTextMessageLayout];
|
||||
} else {
|
||||
self.cellLayout = [TUIMessageCellLayout 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
|
||||
42
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.h
Normal file
42
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.h
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This file declares the TUIFaceMessageCellData class.
|
||||
* This class inherits from TUIMessageCellData and is used to store a series of data and information required by the emoticon message unit.
|
||||
*/
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* 【Module name】TUIFaceMessageCellData
|
||||
* 【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 TUIFaceMessageCellData : TUIBubbleMessageCellData
|
||||
|
||||
/**
|
||||
*
|
||||
* 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
|
||||
34
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.m
Normal file
34
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.m
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// TFaceMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFaceMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@implementation TUIFaceMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMFaceElem *elem = message.faceElem;
|
||||
TUIFaceMessageCellData *faceData = [[TUIFaceMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
faceData.groupIndex = elem.index;
|
||||
faceData.faceName = [[NSString alloc] initWithData:elem.data encoding:NSUTF8StringEncoding];
|
||||
for (TUIFaceGroup *group in [TIMConfig 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 TIMCommonLocalizableString(TUIKitMessageTypeAnimateEmoji);
|
||||
}
|
||||
|
||||
@end
|
||||
72
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.h
Normal file
72
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.h
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIFileMessageCellData : TUIBubbleMessageCellData <TUIMessageCellDataFileUploadProtocol, TUIMessageCellDataFileDownloadProtocol>
|
||||
|
||||
/**
|
||||
* 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
|
||||
148
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.m
Normal file
148
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.m
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// TUIFileMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFileMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
#import "TUIMessageProgressManager.h"
|
||||
|
||||
@interface TUIFileMessageCellData ()
|
||||
@property(nonatomic, strong) NSMutableArray *progressBlocks;
|
||||
@property(nonatomic, strong) NSMutableArray *responseBlocks;
|
||||
@end
|
||||
|
||||
@implementation TUIFileMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMFileElem *elem = message.fileElem;
|
||||
TUIFileMessageCellData *fileData = [[TUIFileMessageCellData 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 TIMCommonLocalizableString(TUIkitMessageTypeFile); // @"[File]";
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIFileReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIFileReplyQuoteView");
|
||||
}
|
||||
|
||||
- (int)length {
|
||||
if (self.innerMessage) {
|
||||
_length = self.innerMessage.fileElem.fileSize;
|
||||
}
|
||||
return _length;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)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 = [TUIMessageProgressManager.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)];
|
||||
[TUIMessageProgressManager.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];
|
||||
[TUIMessageProgressManager.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
|
||||
22
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.h
Normal file
22
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// TUIGroupNoticeCellData.h
|
||||
// TUIGroup
|
||||
//
|
||||
// Created by harvy on 2022/1/11.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIGroupNoticeCellData : NSObject
|
||||
|
||||
@property(nonatomic, copy) NSString *name;
|
||||
@property(nonatomic, copy) NSString *desc;
|
||||
@property(nonatomic, weak) id target;
|
||||
@property(nonatomic, assign) SEL selector;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
13
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.m
Normal file
13
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.m
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// TUIGroupNoticeCellData.m
|
||||
// TUIGroup
|
||||
//
|
||||
// Created by harvy on 2022/1/11.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIGroupNoticeCellData.h"
|
||||
|
||||
@implementation TUIGroupNoticeCellData
|
||||
|
||||
@end
|
||||
106
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.h
Normal file
106
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.h
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import "TUIChatDefine.h"
|
||||
#import "TUIMessageItem.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIImageMessageCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
*
|
||||
* 【Module name】 TUIImageMessageCellData
|
||||
* 【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 TUIImageMessageCellData : TUIBubbleMessageCellData <TUIMessageCellDataFileUploadProtocol>
|
||||
|
||||
@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
|
||||
227
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.m
Normal file
227
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.m
Normal file
@@ -0,0 +1,227 @@
|
||||
//
|
||||
// TUIImageMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIImageMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
|
||||
@interface TUIImageMessageCellData ()
|
||||
@property(nonatomic, assign) BOOL isDownloading;
|
||||
@property(nonatomic, copy) TUIImageMessageDownloadCallback onFinish;
|
||||
@end
|
||||
|
||||
@implementation TUIImageMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMImageElem *elem = message.imageElem;
|
||||
TUIImageMessageCellData *imageData = [[TUIImageMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
imageData.path = [elem.path safePathString];
|
||||
imageData.items = [NSMutableArray array];
|
||||
for (V2TIMImage *item in elem.imageList) {
|
||||
TUIImageItem *itemData = [[TUIImageItem 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 TIMCommonLocalizableString(TUIkitMessageTypeImage); // @"[Image]";
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIImageReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIImageReplyQuoteView");
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
_uploadProgress = 100;
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingImageMessageLayout];
|
||||
} else {
|
||||
self.cellLayout = [TUIMessageCellLayout 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) {
|
||||
TUIImageItem *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 {
|
||||
[TUITool asyncDecodeImage:path
|
||||
complete:^(NSString *path, UIImage *image) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"image.sd_imageFormat: %ld path:%@ image.sd_imageData.length :%lu",(long)image.sd_imageFormat,path,(unsigned long)image.sd_imageData.length);
|
||||
if (![path tui_containsString:@".gif"] || (image.sd_imageFormat != SDImageFormatGIF) ) {
|
||||
[[SDImageCache sharedImageCache] storeImageToMemory:image forKey:cacheKey];
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* The gif image is too large to be cached in memory
|
||||
* Only cache images less than 1M
|
||||
*/
|
||||
if (image.sd_imageData.length < 1 * 1024 * 1024) {
|
||||
[[SDImageCache sharedImageCache] storeImageToMemory:image forKey:cacheKey];
|
||||
}
|
||||
}
|
||||
finishBlock(image);
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (TUIImageItem *)getTImageItem:(TUIImageType)type {
|
||||
for (TUIImageItem *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
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This file declares the TUIJoinGroupMessageCellData 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 <TIMCommon/TUISystemMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIJoinGroupMessageCellData : TUISystemMessageCellData
|
||||
|
||||
/**
|
||||
*
|
||||
* 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
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
#import "TUIJoinGroupMessageCellData.h"
|
||||
|
||||
@implementation TUIJoinGroupMessageCellData
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
self.userNameList = [NSMutableArray array];
|
||||
self.userIDList = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
25
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.h
Normal file
25
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.h
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIMenuCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface TUIMenuCellData : NSObject
|
||||
|
||||
/**
|
||||
* Access path for grouped thumbnails in grouping units
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
|
||||
@property(nonatomic, assign) BOOL isSelected;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
9
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.m
Normal file
9
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.m
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import "TUIMenuCellData.h"
|
||||
|
||||
@implementation TUIMenuCellData
|
||||
|
||||
@end
|
||||
28
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.h
Normal file
28
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// TUIMergeMessageCellData.h
|
||||
// Pods
|
||||
//
|
||||
// Created by harvy on 2020/12/9.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMergeMessageCellData : TUIMessageCellData
|
||||
|
||||
@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
|
||||
119
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.m
Normal file
119
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.m
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// TUIMergeMessageCellData.m
|
||||
// Pods
|
||||
//
|
||||
// Created by harvy on 2020/12/9.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIMergeMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import "TUITextMessageCellData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
|
||||
@implementation TUIMergeMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMMergerElem *elem = message.mergerElem;
|
||||
if (elem.layersOverLimit) {
|
||||
TUITextMessageCellData *limitCell = [[TUITextMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
limitCell.content = TIMCommonLocalizableString(TUIKitRelayLayerLimitTips);
|
||||
return limitCell;
|
||||
}
|
||||
|
||||
TUIMergeMessageCellData *mergeData = [[TUIMergeMessageCellData 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:@"[%@]", TIMCommonLocalizableString(TUIKitRelayChatHistory)];
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIMergeReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIMergeReplyQuoteView");
|
||||
}
|
||||
|
||||
- (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 tui_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
|
||||
19
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.h
Normal file
19
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUITypingStatusCellData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/7/4.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUITypingStatusCellData : TUIMessageCellData
|
||||
|
||||
@property(nonatomic, assign) NSInteger typingStatus;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
25
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.m
Normal file
25
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.m
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TUITypingStatusCellData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/7/4.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUITypingStatusCellData.h"
|
||||
|
||||
@implementation TUITypingStatusCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
TUITypingStatusCellData *cellData = [[TUITypingStatusCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.msgID = message.msgID;
|
||||
|
||||
if ([param.allKeys containsObject:@"typingStatus"]) {
|
||||
cellData.typingStatus = [param[@"typingStatus"] intValue];
|
||||
}
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
@end
|
||||
60
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.h
Normal file
60
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.h
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
* 1. This file declares the TUIVideoItem class, TUISnapshotItem class, and TUIVideoMessageCellData class.
|
||||
* - TUIVideoItem corresponds to V2TIMVideoElem in the IM SDK. We convert the classes in the SDK to TUIVideoItem, which is convenient for us to further
|
||||
* process and operate the data.
|
||||
* - TUISnapshotItem corresponds to the video cover class in the IM SDK. It is still an image in essence, but is bound to the corresponding Video.
|
||||
* - TUIVideoMessageCellData inherits from the TUIMessageCellData 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 <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import "TUIChatDefine.h"
|
||||
#import "TUIMessageItem.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIVideoMessageCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface TUIVideoMessageCellData : TUIBubbleMessageCellData <TUIMessageCellDataFileUploadProtocol>
|
||||
|
||||
@property(nonatomic, strong) UIImage *thumbImage;
|
||||
@property(nonatomic, strong) NSString *videoPath;
|
||||
@property(nonatomic, strong) NSString *snapshotPath;
|
||||
@property(nonatomic, strong) TUIVideoItem *videoItem;
|
||||
@property(nonatomic, strong) TUISnapshotItem *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;
|
||||
|
||||
+ (TUIMessageCellData *)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
|
||||
290
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.m
Normal file
290
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.m
Normal file
@@ -0,0 +1,290 @@
|
||||
//
|
||||
// TUIVideoMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIVideoMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
#import <TUICore/TUILogin.h>
|
||||
|
||||
#define TVideo_Block_Progress @"TVideo_Block_Progress";
|
||||
#define TVideo_Block_Response @"TVideo_Block_Response";
|
||||
|
||||
@interface TUIVideoMessageCellData ()
|
||||
@property(nonatomic, strong) NSString *videoUrl;
|
||||
@property(nonatomic, assign) BOOL isDownloadingSnapshot;
|
||||
@property(nonatomic, assign) BOOL isDownloadingVideo;
|
||||
@property(nonatomic, copy) TUIVideoMessageDownloadCallback onFinish;
|
||||
@end
|
||||
|
||||
@implementation TUIVideoMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMVideoElem *elem = message.videoElem;
|
||||
TUIVideoMessageCellData *videoData = [[TUIVideoMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
videoData.videoPath = [elem.videoPath safePathString];
|
||||
videoData.snapshotPath = [elem.snapshotPath safePathString];
|
||||
|
||||
videoData.videoItem = [[TUIVideoItem alloc] init];
|
||||
videoData.videoItem.uuid = elem.videoUUID;
|
||||
videoData.videoItem.type = elem.videoType;
|
||||
videoData.videoItem.length = elem.videoSize;
|
||||
videoData.videoItem.duration = elem.duration;
|
||||
|
||||
videoData.snapshotItem = [[TUISnapshotItem 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;
|
||||
}
|
||||
|
||||
+ (TUIMessageCellData *)placeholderCellDataWithSnapshotUrl:(NSString *)snapshotUrl thubImage:(UIImage *)thubImage {
|
||||
TUIVideoMessageCellData *videoData = [[TUIVideoMessageCellData alloc] initWithDirection:(MsgDirectionOutgoing)];
|
||||
videoData.thumbImage = thubImage;
|
||||
videoData.snapshotPath = [snapshotUrl safePathString];
|
||||
videoData.videoItem = [[TUIVideoItem alloc] init];
|
||||
videoData.snapshotItem = [[TUISnapshotItem alloc] init];
|
||||
videoData.snapshotItem.size = CGSizeEqualToSize(thubImage.size, CGSizeZero) ? CGSizeMake(kScale375(100), kScale375(100)) : thubImage.size;
|
||||
videoData.reuseId = TVideoMessageCell_ReuseId;
|
||||
videoData.avatarUrl = [NSURL URLWithString:[TUILogin getFaceUrl]];
|
||||
videoData.isPlaceHolderCellData = YES;
|
||||
return videoData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUIkitMessageTypeVideo);
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIVideoReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIVideoReplyQuoteView");
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
_uploadProgress = 100;
|
||||
_isDownloadingVideo = NO;
|
||||
_isDownloadingSnapshot = NO;
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingVideoMessageLayout];
|
||||
} else {
|
||||
self.cellLayout = [TUIMessageCellLayout 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);
|
||||
[TUITool 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 = _videoPath;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
else {
|
||||
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
|
||||
66
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.h
Normal file
66
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.h
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TUIVoiceAudioPlaybackStyle) {
|
||||
TUIVoiceAudioPlaybackStyleLoudspeaker = 1,
|
||||
TUIVoiceAudioPlaybackStyleHandset = 2,
|
||||
};
|
||||
|
||||
@interface TUIVoiceMessageCellData : TUIBubbleMessageCellData
|
||||
|
||||
@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
|
||||
263
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.m
Normal file
263
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.m
Normal file
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// TUIVoiceMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIVoiceMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
@import AVFoundation;
|
||||
|
||||
@interface TUIVoiceMessageCellData () <AVAudioPlayerDelegate>
|
||||
@property AVAudioPlayer *audioPlayer;
|
||||
@property NSString *wavPath;
|
||||
@property(nonatomic, strong) NSTimer *timer;
|
||||
@end
|
||||
|
||||
@implementation TUIVoiceMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMSoundElem *elem = message.soundElem;
|
||||
TUIVoiceMessageCellData *soundData = [[TUIVoiceMessageCellData 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 TIMCommonLocalizableString(TUIKitMessageTypeVoice); // @"[Voice]";
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIVoiceReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIVoiceReplyQuoteView");
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingVoiceMessageLayout];
|
||||
_voiceImage = TUIChatDynamicImage(@"chat_voice_message_receiver_voice_normal_img",
|
||||
[[TUIImageCache 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 = [TUIMessageCellLayout outgoingVoiceMessageLayout];
|
||||
_voiceImage = TUIChatDynamicImage(@"chat_voice_message_sender_voice_normal_img",
|
||||
[[TUIImageCache 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 = [[TUIImageCache 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:@"tui_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:@"tui_audioPlaybackStyle"];
|
||||
}
|
||||
else {
|
||||
[NSUserDefaults.standardUserDefaults setObject:@"1" forKey:@"tui_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
|
||||
21
TUIKit/TUIChat/BaseCellData/Custom/TUIEvaluationCellData.h
Normal file
21
TUIKit/TUIChat/BaseCellData/Custom/TUIEvaluationCellData.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// TUIEvaluationCellData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by xia on 2022/6/10.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIEvaluationCellData : TUIBubbleMessageCellData
|
||||
|
||||
@property(nonatomic, assign) NSInteger score;
|
||||
@property(nonatomic, copy) NSString *desc;
|
||||
@property(nonatomic, copy) NSString *comment;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
40
TUIKit/TUIChat/BaseCellData/Custom/TUIEvaluationCellData.m
Normal file
40
TUIKit/TUIChat/BaseCellData/Custom/TUIEvaluationCellData.m
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// TUIEvaluationCellData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by xia on 2022/6/10.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIEvaluationCellData.h"
|
||||
|
||||
@implementation TUIEvaluationCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUIEvaluationCellData *cellData = [[TUIEvaluationCellData 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
|
||||
19
TUIKit/TUIChat/BaseCellData/Custom/TUILinkCellData.h
Normal file
19
TUIKit/TUIChat/BaseCellData/Custom/TUILinkCellData.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MyCustomCellData.h
|
||||
// TUIKitDemo
|
||||
//
|
||||
// Created by annidyfeng on 2019/6/10.
|
||||
// Copyright © 2019 Tencent. All rights reserved.
|
||||
//
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUILinkCellData : TUIBubbleMessageCellData
|
||||
|
||||
@property NSString *text;
|
||||
@property NSString *link;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
38
TUIKit/TUIChat/BaseCellData/Custom/TUILinkCellData.m
Normal file
38
TUIKit/TUIChat/BaseCellData/Custom/TUILinkCellData.m
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// MyCustomCellData.m
|
||||
// TUIKitDemo
|
||||
//
|
||||
// Created by annidyfeng on 2019/6/10.
|
||||
// Copyright © 2019 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUILinkCellData.h"
|
||||
|
||||
@implementation TUILinkCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
TUILinkCellData *cellData = [[TUILinkCellData 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
|
||||
17
TUIKit/TUIChat/BaseCellData/Custom/TUILocalTipsCellData.h
Normal file
17
TUIKit/TUIChat/BaseCellData/Custom/TUILocalTipsCellData.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TUILocalTipsCellData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by yiliangwang on 2025/3/18.
|
||||
// Copyright © 2025 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUISystemMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUILocalTipsCellData : TUISystemMessageCellData
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
29
TUIKit/TUIChat/BaseCellData/Custom/TUILocalTipsCellData.m
Normal file
29
TUIKit/TUIChat/BaseCellData/Custom/TUILocalTipsCellData.m
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// TUILocalTipsCellData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by yiliangwang on 2025/3/18.
|
||||
// Copyright © 2025 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUILocalTipsCellData.h"
|
||||
|
||||
@implementation TUILocalTipsCellData
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
TUILocalTipsCellData *cellData = [[TUILocalTipsCellData alloc] initWithDirection:MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
cellData.msgID = message.msgID;
|
||||
cellData.content = param[@"content"];
|
||||
cellData.reuseId = TSystemMessageCell_ReuseId;
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
return param[@"content"];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
23
TUIKit/TUIChat/BaseCellData/Custom/TUIOrderCellData.h
Normal file
23
TUIKit/TUIChat/BaseCellData/Custom/TUIOrderCellData.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// TUIOrderCellData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by xia on 2022/6/10.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIOrderCellData : TUIBubbleMessageCellData
|
||||
|
||||
@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
|
||||
37
TUIKit/TUIChat/BaseCellData/Custom/TUIOrderCellData.m
Normal file
37
TUIKit/TUIChat/BaseCellData/Custom/TUIOrderCellData.m
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// TUIOrderCellData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by xia on 2022/6/10.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIOrderCellData.h"
|
||||
|
||||
@implementation TUIOrderCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
TUIOrderCellData *cellData = [[TUIOrderCellData 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
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TUIFileReplyQuoteViewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIVoiceReplyQuoteViewData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIFileReplyQuoteViewData : TUIVoiceReplyQuoteViewData
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// TUIFileReplyQuoteViewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFileReplyQuoteViewData.h"
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIFileMessageCellData.h"
|
||||
|
||||
@implementation TUIFileReplyQuoteViewData
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)originCellData {
|
||||
if (originCellData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![originCellData isKindOfClass:TUIFileMessageCellData.class]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUIFileReplyQuoteViewData *myData = [[TUIFileReplyQuoteViewData alloc] init];
|
||||
myData.text = [(TUIFileMessageCellData *)originCellData fileName];
|
||||
myData.icon = TUIChatCommonBundleImage(@"msg_file");
|
||||
myData.originCellData = originCellData;
|
||||
return myData;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// TUIImageReplyQuoteViewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIReplyMessageCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
typedef NS_ENUM(NSUInteger, TUIImageReplyQuoteStatus) {
|
||||
TUIImageReplyQuoteStatusInit,
|
||||
TUIImageReplyQuoteStatusDownloading,
|
||||
TUIImageReplyQuoteStatusSuccess,
|
||||
TUIImageReplyQuoteStatusFailed,
|
||||
};
|
||||
|
||||
@interface TUIImageReplyQuoteViewData : TUIReplyQuoteViewData
|
||||
|
||||
@property(nonatomic, assign) TUIImageReplyQuoteStatus imageStatus;
|
||||
|
||||
@property(nonatomic, strong) UIImage *image;
|
||||
|
||||
@property(nonatomic, assign) CGSize imageSize;
|
||||
|
||||
+ (CGSize)displaySizeWithOriginSize:(CGSize)originSize;
|
||||
- (void)downloadImage;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// TUIImageReplyQuoteViewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIImageReplyQuoteViewData.h"
|
||||
#import "TUIImageMessageCellData.h"
|
||||
|
||||
@implementation TUIImageReplyQuoteViewData
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)originCellData {
|
||||
if (originCellData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![originCellData isKindOfClass:TUIImageMessageCellData.class]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUIImageReplyQuoteViewData *myData = [[TUIImageReplyQuoteViewData 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 = [TUIImageReplyQuoteViewData 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:TUIImageMessageCellData.class]) {
|
||||
TUIImageMessageCellData *imageData = (TUIImageMessageCellData *)self.originCellData;
|
||||
[imageData downloadImage:TImage_Type_Thumb
|
||||
finish:^{
|
||||
@strongify(self);
|
||||
self.image = imageData.thumbImage;
|
||||
self.imageStatus = TUIImageReplyQuoteStatusSuccess;
|
||||
if (self.onFinish) {
|
||||
self.onFinish();
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// TUIMergeReplyQuoteViewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIReplyMessageCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMergeReplyQuoteViewData : TUIReplyQuoteViewData
|
||||
|
||||
@property(nonatomic, copy) NSString *title;
|
||||
@property(nonatomic, copy) NSString *abstract;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// TUIMergeReplyQuoteViewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIMergeReplyQuoteViewData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
#import "TUIMergeMessageCellData.h"
|
||||
|
||||
@implementation TUIMergeReplyQuoteViewData
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)originCellData {
|
||||
if (originCellData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![originCellData isKindOfClass:TUIMergeMessageCellData.class]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUIMergeReplyQuoteViewData *myData = [[TUIMergeReplyQuoteViewData alloc] init];
|
||||
myData.title = [(TUIMergeMessageCellData *)originCellData title];
|
||||
NSAttributedString *abstract = [(TUIMergeMessageCellData *)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
|
||||
107
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyMessageCellData.h
Normal file
107
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyMessageCellData.h
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// TUIReplyMessageCellData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/11.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <ImSDK_Plus/ImSDK_Plus.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import "TUIChatDefine.h"
|
||||
#import "TUIReplyQuoteViewData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TUIReplyMessageCellData;
|
||||
|
||||
@interface TUIReplyMessageCellData : TUIBubbleMessageCellData
|
||||
|
||||
/**
|
||||
* 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) TUIMessageCellData *originCellData;
|
||||
@property(nonatomic, strong) TUIReplyQuoteViewData *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 TUIMessageDataProvider+MessageDeal
|
||||
//- (void)loadOriginMessage:(void(^)(void))callback;
|
||||
|
||||
- (TUIReplyQuoteViewData *)getQuoteData:(TUIMessageCellData *)originCellData;
|
||||
- (CGSize)quotePlaceholderSizeWithType:(V2TIMElemType)type data:(TUIReplyQuoteViewData *)data;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIReferenceMessageCellData : TUIReplyMessageCellData
|
||||
|
||||
@property(nonatomic, assign) CGSize textSize;
|
||||
@property(nonatomic, assign) CGPoint textOrigin;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
173
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyMessageCellData.m
Normal file
173
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyMessageCellData.m
Normal file
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// TUIReplyMessageCellData.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 "TUIReplyMessageCellData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
#import "TUIFileMessageCellData.h"
|
||||
#import "TUIImageMessageCellData.h"
|
||||
#import "TUIMergeMessageCellData.h"
|
||||
#import "TUITextMessageCellData.h"
|
||||
#import "TUIVideoMessageCellData.h"
|
||||
#import "TUIVoiceMessageCellData.h"
|
||||
|
||||
#import "TUICloudCustomDataTypeCenter.h"
|
||||
#import "TUIFileReplyQuoteViewData.h"
|
||||
#import "TUIImageReplyQuoteViewData.h"
|
||||
#import "TUIMergeReplyQuoteViewData.h"
|
||||
#import "TUIReplyPreviewData.h"
|
||||
#import "TUITextReplyQuoteViewData.h"
|
||||
#import "TUIVideoReplyQuoteViewData.h"
|
||||
#import "TUIVoiceReplyQuoteViewData.h"
|
||||
|
||||
@implementation TUIReplyMessageCellData
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
if (message.cloudCustomData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block TUIReplyMessageCellData *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 = [[TUIReplyMessageCellData 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:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingTextMessageLayout];
|
||||
} else {
|
||||
self.cellLayout = [TUIMessageCellLayout outgoingTextMessageLayout];
|
||||
}
|
||||
_emojiLocations = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)quotePlaceholderSizeWithType:(V2TIMElemType)type data:(TUIReplyQuoteViewData *)data {
|
||||
if (data == nil) {
|
||||
return CGSizeMake(20, 20);
|
||||
}
|
||||
|
||||
return [data contentSize:TReplyQuoteView_Max_Width - 12];
|
||||
}
|
||||
|
||||
- (TUIReplyQuoteViewData *)getQuoteData:(TUIMessageCellData *)originCellData {
|
||||
TUIReplyQuoteViewData *quoteData = nil;
|
||||
Class class = [originCellData getReplyQuoteViewDataClass];
|
||||
BOOL hasRiskContent = originCellData.innerMessage.hasRiskContent;
|
||||
if (hasRiskContent && [TIMConfig isClassicEntrance]){
|
||||
// Return text reply data in default
|
||||
TUITextReplyQuoteViewData *myData = [[TUITextReplyQuoteViewData alloc] init];
|
||||
myData.text = [TUIReplyPreviewData 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
|
||||
TUITextReplyQuoteViewData *myData = [[TUITextReplyQuoteViewData alloc] init];
|
||||
myData.text = [TUIReplyPreviewData 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 TUIReferenceMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
if (message.cloudCustomData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block TUIReplyMessageCellData *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 = [[TUIReferenceMessageCellData 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
|
||||
55
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyPreviewData.h
Normal file
55
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyPreviewData.h
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// TUIReplyPreviewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/3/22.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TUIChatDefine.h"
|
||||
|
||||
@class V2TIMMessage;
|
||||
|
||||
@interface TUIReplyPreviewData : 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 TUIReferencePreviewData : TUIReplyPreviewData
|
||||
|
||||
@end
|
||||
39
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyPreviewData.m
Normal file
39
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyPreviewData.m
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// TUIReplyPreviewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/3/22.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIReplyPreviewData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@implementation TUIReplyPreviewData
|
||||
|
||||
+ (NSString *)displayAbstract:(NSInteger)type abstract:(NSString *)abstract withFileName:(BOOL)withFilename isRisk:(BOOL)isRisk {
|
||||
NSString *text = abstract;
|
||||
if (type == V2TIM_ELEM_TYPE_IMAGE) {
|
||||
text = isRisk? TIMCommonLocalizableString(TUIkitMessageTypeRiskImage):TIMCommonLocalizableString(TUIkitMessageTypeImage);
|
||||
} else if (type == V2TIM_ELEM_TYPE_VIDEO) {
|
||||
text = isRisk? TIMCommonLocalizableString(TUIkitMessageTypeRiskVideo):TIMCommonLocalizableString(TUIkitMessageTypeVideo);
|
||||
} else if (type == V2TIM_ELEM_TYPE_SOUND) {
|
||||
text = isRisk? TIMCommonLocalizableString(TUIkitMessageTypeRiskVoice):TIMCommonLocalizableString(TUIKitMessageTypeVoice);
|
||||
} else if (type == V2TIM_ELEM_TYPE_FACE) {
|
||||
text = TIMCommonLocalizableString(TUIKitMessageTypeAnimateEmoji);
|
||||
} else if (type == V2TIM_ELEM_TYPE_FILE) {
|
||||
if (withFilename) {
|
||||
text = [NSString stringWithFormat:@"%@%@", TIMCommonLocalizableString(TUIkitMessageTypeFile), abstract];
|
||||
;
|
||||
} else {
|
||||
text = TIMCommonLocalizableString(TUIkitMessageTypeFile);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIReferencePreviewData
|
||||
|
||||
@end
|
||||
37
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyQuoteViewData.h
Normal file
37
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyQuoteViewData.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// TUIReplyQuoteViewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIChatDefine.h"
|
||||
|
||||
@class TUIMessageCellData;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIReplyQuoteViewData : NSObject
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)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) TUIMessageCellData *originCellData;
|
||||
|
||||
@property(nonatomic, assign) BOOL supportForReply;
|
||||
|
||||
@property(nonatomic, assign) BOOL showRevokedOriginMessage;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
21
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyQuoteViewData.m
Normal file
21
TUIKit/TUIChat/BaseCellData/Reply/TUIReplyQuoteViewData.m
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// TUIReplyQuoteViewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIReplyQuoteViewData.h"
|
||||
|
||||
@implementation TUIReplyQuoteViewData
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)originCellData {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGSize)contentSize:(CGFloat)maxWidth {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUITextReplyQuoteViewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIReplyMessageCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUITextReplyQuoteViewData : TUIReplyQuoteViewData
|
||||
|
||||
@property(nonatomic, copy) NSString *text;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// TUITextReplyQuoteViewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUITextReplyQuoteViewData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
#import "TUITextMessageCellData.h"
|
||||
|
||||
@implementation TUITextReplyQuoteViewData
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)originCellData {
|
||||
if (originCellData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![originCellData isKindOfClass:TUITextMessageCellData.class]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUITextReplyQuoteViewData *myData = [[TUITextReplyQuoteViewData alloc] init];
|
||||
myData.text = [(TUITextMessageCellData *)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?
|
||||
TIMCommonLocalizableString(TUIKitRepliesOriginMessageRevoke):
|
||||
TIMCommonLocalizableString(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
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TUIVideoReplyQuoteViewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIImageReplyQuoteViewData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIVideoReplyQuoteViewData : TUIImageReplyQuoteViewData
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// TUIVideoReplyQuoteViewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIVideoReplyQuoteViewData.h"
|
||||
#import "TUIVideoMessageCellData.h"
|
||||
|
||||
@implementation TUIVideoReplyQuoteViewData
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)originCellData {
|
||||
if (originCellData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![originCellData isKindOfClass:TUIVideoMessageCellData.class]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUIVideoReplyQuoteViewData *myData = [[TUIVideoReplyQuoteViewData alloc] init];
|
||||
CGSize snapSize = CGSizeMake(originCellData.innerMessage.videoElem ? originCellData.innerMessage.videoElem.snapshotWidth : 0,
|
||||
originCellData.innerMessage.videoElem ? originCellData.innerMessage.videoElem.snapshotHeight : 0);
|
||||
myData.imageSize = [TUIVideoReplyQuoteViewData displaySizeWithOriginSize:snapSize];
|
||||
myData.originCellData = originCellData;
|
||||
return myData;
|
||||
}
|
||||
|
||||
- (void)downloadImage {
|
||||
[super downloadImage];
|
||||
|
||||
@weakify(self);
|
||||
if ([self.originCellData isKindOfClass:TUIVideoMessageCellData.class]) {
|
||||
TUIVideoMessageCellData *videoData = (TUIVideoMessageCellData *)self.originCellData;
|
||||
[videoData downloadThumb:^{
|
||||
@strongify(self);
|
||||
self.image = videoData.thumbImage;
|
||||
if (self.onFinish) {
|
||||
self.onFinish();
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUIVoiceReplyQuoteViewData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUITextReplyQuoteViewData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIVoiceReplyQuoteViewData : TUITextReplyQuoteViewData
|
||||
|
||||
@property(nonatomic, strong) UIImage *icon;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// TUIVoiceReplyQuoteViewData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by harvy on 2021/11/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIVoiceReplyQuoteViewData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIVoiceMessageCellData.h"
|
||||
@implementation TUIVoiceReplyQuoteViewData
|
||||
|
||||
+ (instancetype)getReplyQuoteViewData:(TUIMessageCellData *)originCellData {
|
||||
if (originCellData == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![originCellData isKindOfClass:TUIVoiceMessageCellData.class]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUIVoiceReplyQuoteViewData *myData = [[TUIVoiceReplyQuoteViewData alloc] init];
|
||||
myData.text = [NSString stringWithFormat:@"%d\"", [(TUIVoiceMessageCellData *)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
|
||||
Reference in New Issue
Block a user