增加换肤功能
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// TUIBotBranchCell.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCell.h>
|
||||
#import "TUIBotBranchCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIBotBranchItemCell : UITableViewCell
|
||||
@property (nonatomic, assign) BranchMsgSubType subType;
|
||||
@property (nonatomic, strong) UIImageView *topLine;
|
||||
@property (nonatomic, strong) UILabel *numberLabel;
|
||||
@property (nonatomic, strong) UILabel *contentLabel;
|
||||
@property (nonatomic, strong) UIImageView *arrowView;
|
||||
@end
|
||||
|
||||
@interface TUIBotBranchCell : TUIBubbleMessageCell
|
||||
@property (nonatomic, strong) UIImageView *headerBkView;
|
||||
@property (nonatomic, strong) UIImageView *headerDotView;
|
||||
@property (nonatomic, strong) UILabel *headerLabel;
|
||||
@property (nonatomic, strong) UIButton *headerRefreshBtn;
|
||||
@property (nonatomic, strong) UIImageView *headerRefreshView;
|
||||
@property (nonatomic, strong) UITableView *itemsTableView;
|
||||
|
||||
- (void)fillWithData:(TUIBotBranchCellData *)data;
|
||||
|
||||
@property (nonatomic, strong) TUIBotBranchCellData *customData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,339 @@
|
||||
//
|
||||
// TUIBotBranchCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import "TUIBotBranchCell.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TIMCommon/TIMRTLUtil.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
@implementation TUIBotBranchItemCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.contentView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
_topLine = [[UIImageView alloc] init];
|
||||
[_topLine setImage:TUICustomerServicePluginBundleThemeImage(@"bot_branch_cell_dotted_line_img", @"branch_cell_dotted_line")];
|
||||
[self.contentView addSubview:_topLine];
|
||||
|
||||
_numberLabel = [[UILabel alloc] init];
|
||||
_numberLabel.font = [UIFont systemFontOfSize:17];
|
||||
_numberLabel.numberOfLines = 0;
|
||||
_numberLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_numberLabel.textColor = TUICustomerServicePluginDynamicColor(@"bot_branch_cell_number_text_color", @"#006EFF");
|
||||
[self.contentView addSubview:_numberLabel];
|
||||
|
||||
_contentLabel = [[UILabel alloc] init];
|
||||
_contentLabel.font = [UIFont systemFontOfSize:14];
|
||||
_contentLabel.numberOfLines = 0;
|
||||
_contentLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_contentLabel.textColor = TUICustomerServicePluginDynamicColor(@"bot_branch_cell_content_text_color", @"#333333");
|
||||
[self.contentView addSubview:_contentLabel];
|
||||
|
||||
_arrowView = [[UIImageView alloc] init];
|
||||
UIImage *arrowImage = TUICustomerServicePluginBundleThemeImage(@"bot_branch_cell_arrow_img", @"branch_cell_arrow");
|
||||
[_arrowView setImage:[arrowImage rtl_imageFlippedForRightToLeftLayoutDirection]];
|
||||
[self.contentView addSubview:_arrowView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// this is Apple's recommended place for adding/updating constraints
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
[self.topLine mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUIBotBranchCellMargin);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(self.mm_w - TUIBotBranchCellMargin * 2);
|
||||
make.height.mas_equalTo(0.5);
|
||||
}];
|
||||
|
||||
if (BranchMsgSubType_Welcome == self.subType) {
|
||||
CGFloat height = [TUICustomerServicePluginDataProvider calcBranchCellHeightOfContent:self.contentLabel.text];
|
||||
[self.numberLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUIBotBranchCellMargin);
|
||||
make.centerY.mas_equalTo(self.contentView.mas_centerY);
|
||||
make.width.mas_equalTo(20);
|
||||
make.height.mas_equalTo(20);
|
||||
}];
|
||||
[self.contentLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.numberLabel.mas_trailing).offset(TUIBotBranchCellMargin);
|
||||
make.centerY.mas_equalTo(self.contentView.mas_centerY);
|
||||
make.width.mas_equalTo(self.mm_w - TUIBotBranchCellMargin * 4 - kScale375(16) - 6);
|
||||
make.height.mas_equalTo(height);
|
||||
}];
|
||||
} else {
|
||||
[self.contentLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUIBotBranchCellMargin);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(self.mm_w - TUIBotBranchCellMargin * 2 - kScale375(16) - 6);
|
||||
make.height.mas_equalTo([TUICustomerServicePluginDataProvider calcBranchCellHeightOfContent:self.contentLabel.text]);
|
||||
}];
|
||||
}
|
||||
|
||||
[self.arrowView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.contentLabel.mas_trailing).offset(6);
|
||||
make.centerY.mas_equalTo(self.contentView);
|
||||
make.width.mas_equalTo(kScale375(16));
|
||||
make.height.mas_equalTo(kScale375(16));
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#define BranchItemCellMaxCountPerPage 4
|
||||
@interface TUIBotBranchCell() <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIBotBranchCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
_headerBkView = [[UIImageView alloc] init];
|
||||
UIImage *headerBkImage = TUICustomerServicePluginBundleThemeImage(@"bot_branch_cell_head_bk_img", @"branch_cell_head_bk");
|
||||
[_headerBkView setImage:[headerBkImage rtl_imageFlippedForRightToLeftLayoutDirection]];
|
||||
[self.container addSubview:_headerBkView];
|
||||
|
||||
_headerDotView = [[UIImageView alloc] init];
|
||||
[_headerDotView setBackgroundColor:TUICustomerServicePluginDynamicColor(@"bot_branch_cell_header_dot_color", @"#FFFFFF")];
|
||||
_headerDotView.layer.cornerRadius = kScale375(8) / 2;
|
||||
_headerDotView.layer.masksToBounds = YES;
|
||||
[self.container addSubview:_headerDotView];
|
||||
|
||||
_headerLabel = [[UILabel alloc] init];
|
||||
_headerLabel.font = [UIFont systemFontOfSize:14];
|
||||
_headerLabel.numberOfLines = 0;
|
||||
_headerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"bot_branch_cell_header_text_color_1", @"#FFFFFF");
|
||||
[self.container addSubview:_headerLabel];
|
||||
|
||||
_headerRefreshBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_headerRefreshBtn.backgroundColor = [UIColor clearColor];
|
||||
[_headerRefreshBtn setTitle:TIMCommonLocalizableString(TUIChatBotChangeQuestion) forState:UIControlStateNormal];
|
||||
[_headerRefreshBtn.titleLabel setFont:[UIFont systemFontOfSize:14]];
|
||||
[_headerRefreshBtn setTitleColor:TUICustomerServicePluginDynamicColor(@"bot_branch_cell_refresh_btn_color", @"#006EFF") forState:UIControlStateNormal];
|
||||
[_headerRefreshBtn addTarget:self action:@selector(onRefresh) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.container addSubview:_headerRefreshBtn];
|
||||
|
||||
_headerRefreshView = [[UIImageView alloc] init];
|
||||
[_headerRefreshView setImage:TUICustomerServicePluginBundleThemeImage(@"bot_branch_cell_refresh_img", @"branch_cell_refresh")];
|
||||
UITapGestureRecognizer *tag = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onRefresh)];
|
||||
[_headerRefreshView addGestureRecognizer:tag];
|
||||
_headerRefreshView.userInteractionEnabled = YES;
|
||||
[self.container addSubview:_headerRefreshView];
|
||||
|
||||
_itemsTableView = [[UITableView alloc] init];
|
||||
_itemsTableView.tableFooterView = [[UIView alloc] init];
|
||||
// _itemsTableView.backgroundColor = TUICoreDynamicColor(@"customer_service_brance_bg_color", @"#FFFFFF");
|
||||
_itemsTableView.backgroundColor = [UIColor clearColor];
|
||||
_itemsTableView.delegate = self;
|
||||
_itemsTableView.dataSource = self;
|
||||
_itemsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
[_itemsTableView registerClass:[TUIBotBranchItemCell class] forCellReuseIdentifier:@"item_cell"];
|
||||
[self.container addSubview:_itemsTableView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onRefresh {
|
||||
if (!self.customData || 0 == self.customData.items.count) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSUInteger pageCount = self.customData.items.count / BranchItemCellMaxCountPerPage;
|
||||
if (self.customData.items.count % BranchItemCellMaxCountPerPage > 0) {
|
||||
pageCount++;
|
||||
}
|
||||
|
||||
NSUInteger oldPageIndex = self.customData.pageIndex;
|
||||
if (self.customData.pageIndex < pageCount - 1) {
|
||||
self.customData.pageIndex++;
|
||||
} else {
|
||||
self.customData.pageIndex = 0;
|
||||
}
|
||||
|
||||
if (self.customData.pageIndex != oldPageIndex) {
|
||||
NSUInteger location = self.customData.pageIndex * BranchItemCellMaxCountPerPage;
|
||||
NSUInteger length = MIN(self.customData.items.count - location, BranchItemCellMaxCountPerPage);
|
||||
self.customData.pageItems = [self.customData.items subarrayWithRange:NSMakeRange(location, length)];
|
||||
|
||||
[self notifyCellSizeChanged];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyCellSizeChanged {
|
||||
NSDictionary *param = @{TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey_Message : self.customData.innerMessage};
|
||||
[TUICore notifyEvent:TUICore_TUIPluginNotify
|
||||
subKey:TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey
|
||||
object:nil
|
||||
param:param];
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUIBotBranchCellData *)data {
|
||||
[super fillWithData:data];
|
||||
|
||||
self.customData = data;
|
||||
self.headerLabel.text = data.header;
|
||||
if (BranchMsgSubType_Welcome == data.subType) {
|
||||
self.headerDotView.hidden = NO;
|
||||
self.headerRefreshBtn.hidden = NO;
|
||||
self.headerRefreshView.hidden = NO;
|
||||
self.headerBkView.hidden = NO;
|
||||
self.headerLabel.font = [UIFont systemFontOfSize:14];
|
||||
self.headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"bot_branch_cell_header_text_color_1", @"#FFFFFF");
|
||||
} else {
|
||||
self.headerDotView.hidden = YES;
|
||||
self.headerRefreshBtn.hidden = YES;
|
||||
self.headerRefreshView.hidden = YES;
|
||||
self.headerBkView.hidden = YES;
|
||||
self.headerLabel.font = [UIFont boldSystemFontOfSize:14];
|
||||
self.headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"bot_branch_cell_header_text_color_2", @"#000000");
|
||||
}
|
||||
[self.itemsTableView reloadData];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
// Override, the size of bubble content.
|
||||
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
|
||||
NSAssert([data isKindOfClass:TUIBotBranchCellData.class],
|
||||
@"data must be a kind of TUIBotBranchCellData");
|
||||
TUIBotBranchCellData *branchCellData = (TUIBotBranchCellData *)data;
|
||||
if (!branchCellData.pageItems) {
|
||||
if (BranchMsgSubType_Welcome == branchCellData.subType) {
|
||||
branchCellData.pageItems = [branchCellData.items subarrayWithRange:
|
||||
NSMakeRange(0, MIN(branchCellData.items.count, BranchItemCellMaxCountPerPage))];
|
||||
} else {
|
||||
branchCellData.pageItems = branchCellData.items;
|
||||
}
|
||||
}
|
||||
return [TUICustomerServicePluginDataProvider calcBranchCellSize:branchCellData.header items:branchCellData.pageItems];
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
CGFloat cellHeight = [TUICustomerServicePluginDataProvider calcBranchCellSize:self.customData.header
|
||||
items:self.customData.pageItems].height;
|
||||
CGSize tableViewSize = [TUICustomerServicePluginDataProvider calcBranchCellSizeOfTableView:self.customData.pageItems];
|
||||
CGSize headerLabelSize = [TUICustomerServicePluginDataProvider calcBranchCellSizeOfHeader:self.customData.header];
|
||||
CGSize headerSize = CGSizeMake(self.container.mm_w, headerLabelSize.height + TUIBotBranchCellMargin + TUIBotBranchCellInnerMargin);
|
||||
|
||||
self.container
|
||||
.mm_width(TUIBotBranchCellWidth)
|
||||
.mm_height(cellHeight);
|
||||
|
||||
if (BranchMsgSubType_Welcome == self.customData.subType) {
|
||||
[self.headerBkView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(0);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(headerSize.width);
|
||||
make.height.mas_equalTo(headerSize.height);
|
||||
}];
|
||||
|
||||
[self.headerDotView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUIBotBranchCellMargin);
|
||||
make.centerY.mas_equalTo(self.headerBkView);
|
||||
make.width.mas_equalTo(TUIBotBranchCellInnerMargin);
|
||||
make.height.mas_equalTo(TUIBotBranchCellInnerMargin);
|
||||
}];
|
||||
|
||||
[self.headerRefreshView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.trailing.mas_equalTo(self.container.mas_trailing).offset(-TUIBotBranchCellMargin);
|
||||
make.centerY.mas_equalTo(self.headerBkView);
|
||||
make.width.mas_equalTo(12);
|
||||
make.height.mas_equalTo(16);
|
||||
}];
|
||||
|
||||
[self.headerRefreshBtn sizeToFit];
|
||||
[self.headerRefreshBtn mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.trailing.mas_equalTo(self.headerRefreshView.mas_leading).offset(-TUIBotBranchCellMargin);
|
||||
make.centerY.mas_equalTo(self.headerBkView);
|
||||
make.size.mas_equalTo(self.headerRefreshBtn.frame.size);
|
||||
}];
|
||||
|
||||
[self.headerLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.headerDotView.mas_trailing).offset(TUIBotBranchCellMargin);
|
||||
make.trailing.mas_equalTo(self.headerRefreshBtn.mas_leading).offset(-TUIBotBranchCellMargin);
|
||||
make.centerY.mas_equalTo(self.headerBkView);
|
||||
make.height.mas_equalTo(headerLabelSize.height);
|
||||
}];
|
||||
} else {
|
||||
[self.headerLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUIBotBranchCellMargin);
|
||||
make.top.mas_equalTo(TUIBotBranchCellMargin);
|
||||
make.width.mas_equalTo(headerLabelSize.width);
|
||||
make.height.mas_equalTo(headerLabelSize.height);
|
||||
}];
|
||||
}
|
||||
|
||||
[self.itemsTableView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(0);
|
||||
make.top.mas_equalTo(headerSize.height);
|
||||
make.width.mas_equalTo(tableViewSize.width);
|
||||
make.height.mas_equalTo(tableViewSize.height);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate & UITableViewDataSource
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return [TUICustomerServicePluginDataProvider calcBranchCellHeightOfTableView:self.customData.pageItems row:indexPath.row];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.customData.pageItems.count <= indexPath.row) {
|
||||
return;
|
||||
}
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
||||
|
||||
NSString *content = self.customData.pageItems[indexPath.row];
|
||||
[TUICustomerServicePluginDataProvider sendTextMessage:content];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.customData.pageItems.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.customData.pageItems.count <= indexPath.row) {
|
||||
return nil;
|
||||
}
|
||||
TUIBotBranchItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"item_cell" forIndexPath:indexPath];
|
||||
cell.subType = self.customData.subType;
|
||||
if (BranchMsgSubType_Welcome == self.customData.subType) {
|
||||
cell.numberLabel.hidden = NO;
|
||||
cell.numberLabel.text = @(indexPath.row + 1).stringValue;
|
||||
} else {
|
||||
cell.numberLabel.hidden = YES;
|
||||
}
|
||||
cell.contentLabel.text = self.customData.pageItems[indexPath.row];
|
||||
|
||||
// tell constraints they need updating
|
||||
[cell setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[cell updateConstraintsIfNeeded];
|
||||
|
||||
[cell layoutIfNeeded];
|
||||
return cell;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// TUIBotBranchCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, BranchMsgSubType) {
|
||||
BranchMsgSubType_Welcome = 0,
|
||||
BranchMsgSubType_Clarify = 1,
|
||||
};
|
||||
|
||||
@interface TUIBotBranchCellData : TUIBubbleMessageCellData
|
||||
|
||||
@property (nonatomic, assign) BranchMsgSubType subType;
|
||||
@property (nonatomic, copy) NSString *header;
|
||||
@property (nonatomic, strong) NSMutableArray *items;
|
||||
@property (nonatomic, assign) NSUInteger pageIndex;
|
||||
@property (nonatomic, strong) NSArray *pageItems;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// TUIBotBranchCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import "TUIBotBranchCellData.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
|
||||
@implementation TUIBotBranchCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUIBotBranchCellData *cellData = [[TUIBotBranchCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
|
||||
NSString *subType = param[@"subtype"];
|
||||
if ([subType isEqualToString:@"welcome_msg"]) {
|
||||
cellData.subType = BranchMsgSubType_Welcome;
|
||||
} else if ([subType isEqualToString:@"clarify_msg"]) {
|
||||
cellData.subType = BranchMsgSubType_Clarify;
|
||||
}
|
||||
|
||||
NSDictionary *content = param[@"content"];
|
||||
cellData.header = content[@"title"];
|
||||
NSArray *items = content[@"items"];
|
||||
for (NSDictionary *item in items) {
|
||||
[cellData.items addObject:item[@"content"]];
|
||||
}
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUIBotBranchCellData *cellData = [[TUIBotBranchCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
NSDictionary *content = param[@"content"];
|
||||
return content[@"title"];
|
||||
}
|
||||
|
||||
// Override
|
||||
- (BOOL)canForward {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)items {
|
||||
if (!_items) {
|
||||
_items = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _items;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
// Created by lynx on 2024/3/1.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCell.h>
|
||||
#import "TUIBotRichTextCellData.h"
|
||||
|
||||
@interface TUIBotRichTextCell : TUIBubbleMessageCell
|
||||
@property TUIBotRichTextCellData *webViewData;
|
||||
|
||||
- (void)fillWithData:(TUIBotRichTextCellData *)data;
|
||||
@end
|
||||
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// TUIBotRichTextCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2024/3/1.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIBotRichTextCell.h"
|
||||
#import <TUIChat/TUITextMessageCell.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
@interface TUIBotRichTextCell ()<WKNavigationDelegate>
|
||||
@property(nonatomic, strong) WKWebView *webView;
|
||||
@property(nonatomic, strong) UIColor *webViewBkColor;
|
||||
@property(nonatomic, strong) UIColor *webViewTextColor;
|
||||
@end
|
||||
|
||||
@implementation TUIBotRichTextCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
[self.container addSubview:self.webView];
|
||||
self.webViewTextColor = [TUITextMessageCell incommingTextColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
||||
self.webViewBkColor = nil;
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.body.style.backgroundColor=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewBkColor]] completionHandler:nil];
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.getElementById('content').style.color=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewTextColor]] completionHandler:nil];
|
||||
}
|
||||
|
||||
- (WKWebView *)webView {
|
||||
if (!_webView) {
|
||||
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
||||
NSString *script = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
|
||||
WKUserScript *wkUserScript = [[WKUserScript alloc] initWithSource:script injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
|
||||
WKUserContentController *wkUserController = [[WKUserContentController alloc] init];
|
||||
[wkUserController addUserScript: wkUserScript];
|
||||
config.userContentController = wkUserController;
|
||||
_webView = [[WKWebView alloc]initWithFrame:self.bounds configuration:config];
|
||||
_webView.navigationDelegate = self;
|
||||
_webView.backgroundColor = [UIColor clearColor];
|
||||
[_webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
|
||||
_webView.scrollView.scrollEnabled = NO;
|
||||
|
||||
NSString *bundlePath = TUIBundlePath(TUICustomerServicePluginBundle,TUICustomerServicePluginBundle_Key_Class);
|
||||
NSString *path = [bundlePath stringByAppendingPathComponent:@"markdown.html"];
|
||||
if (path) {
|
||||
NSURL *url = [NSURL fileURLWithPath:path];
|
||||
NSURLRequest *requset = [NSURLRequest requestWithURL:url];
|
||||
[_webView loadRequest:requset];
|
||||
}
|
||||
}
|
||||
return _webView;
|
||||
}
|
||||
|
||||
- (UIColor *)webViewBkColor {
|
||||
if (!_webViewBkColor && self.bubbleView.image) {
|
||||
UIImage *image = [self.bubbleView.image.imageAsset imageWithTraitCollection:[UITraitCollection currentTraitCollection]];
|
||||
_webViewBkColor = [self colorFromImage:image point:CGPointMake(trunc(10), trunc(10))];
|
||||
}
|
||||
return _webViewBkColor;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
// this is Apple's recommended place for adding/updating constraints
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
[self.webView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(10);
|
||||
make.top.mas_equalTo(2);
|
||||
make.width.mas_equalTo(self.container.mm_w - 10 * 2);
|
||||
make.height.mas_equalTo(self.container.mm_h - 2 * 2);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUIBotRichTextCellData *)data {
|
||||
// set data
|
||||
[super fillWithData:data];
|
||||
self.webViewData = data;
|
||||
[self loadMarkdownContent];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)loadMarkdownContent {
|
||||
NSString *content = [self getMarkdownContentWithMarkdowString:self.webViewData.content];
|
||||
NSString *js = [NSString stringWithFormat:@"javascript:parseMarkdown(\"%@\",true)", content];
|
||||
[self.webView evaluateJavaScript:js completionHandler:nil];
|
||||
// Set webview background color
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.body.style.backgroundColor=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewBkColor]] completionHandler:nil];
|
||||
// Set webview text color
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.getElementById('content').style.color=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewTextColor]] completionHandler:nil];
|
||||
// Webview disables sliding and zooming
|
||||
NSString *injectionJSString = @"var script = document.createElement('meta');script.name = 'viewport';"
|
||||
"script.content=\"width=device-width, user-scalable=no\";"
|
||||
"document.getElementsByTagName('head')[0].appendChild(script);";
|
||||
[self.webView evaluateJavaScript:injectionJSString completionHandler:nil];
|
||||
}
|
||||
|
||||
- (NSString *)getMarkdownContentWithMarkdowString:(NSString *)markdown {
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"\r"withString:@""];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"\n"withString:@"\\n"];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"<br>" withString:@"\\n"];
|
||||
return markdown;
|
||||
}
|
||||
|
||||
#pragma mark - TUIMessageCellProtocol
|
||||
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
|
||||
if ([data isKindOfClass:[TUIBotRichTextCellData class]]) {
|
||||
return CGSizeMake(TRichTextMessageCell_Width_Max, [(TUIBotRichTextCellData *)data cellHeight]);
|
||||
}
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
#pragma mark - WKNavigationDelegate
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
||||
[self loadMarkdownContent];
|
||||
[self updateCellSize];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
|
||||
if ([keyPath isEqualToString:@"contentSize"]) {
|
||||
[self limitUpdateCellSize];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
|
||||
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
|
||||
if ([[UIApplication sharedApplication] canOpenURL:navigationAction.request.URL]) {
|
||||
[[UIApplication sharedApplication] openURL:navigationAction.request.URL options:@{} completionHandler:nil];
|
||||
}
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
} else {
|
||||
decisionHandler (WKNavigationActionPolicyAllow);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)limitUpdateCellSize {
|
||||
uint64_t curTs = [[NSDate date] timeIntervalSince1970];
|
||||
uint64_t interval = 1; // s
|
||||
if (curTs - self.webViewData.lastUpdateTs >= interval && self.webViewData.lastUpdateTs) {
|
||||
self.webViewData.lastUpdateTs = curTs;
|
||||
[self updateCellSize];
|
||||
} else {
|
||||
if (self.webViewData.delayUpdate) {
|
||||
return;
|
||||
}
|
||||
self.webViewData.delayUpdate = YES;
|
||||
@weakify(self)
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
@strongify(self)
|
||||
[self updateCellSize];
|
||||
self.webViewData.delayUpdate = NO;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateCellSize {
|
||||
@weakify(self);
|
||||
[self.webView evaluateJavaScript:@"document.body.scrollWidth" completionHandler:^(id _Nullable result,NSError *_Nullable error) {
|
||||
CGFloat scrollWidth= [result doubleValue];
|
||||
[self.webView evaluateJavaScript:@"document.body.scrollHeight"completionHandler:^(id _Nullable result,NSError*_Nullable error) {
|
||||
@strongify(self)
|
||||
CGFloat scrollHeight = [result doubleValue];
|
||||
CGFloat ratio = CGRectGetWidth(self.webView.frame) /scrollWidth;
|
||||
CGFloat webHeight = scrollHeight * ratio;
|
||||
if (self.webViewData.cellHeight != webHeight) {
|
||||
self.webViewData.cellHeight = webHeight;
|
||||
[self notifyCellSizeChanged];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)notifyCellSizeChanged {
|
||||
NSDictionary *param = @{TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey_Message : self.webViewData.innerMessage};
|
||||
[TUICore notifyEvent:TUICore_TUIPluginNotify
|
||||
subKey:TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey
|
||||
object:nil
|
||||
param:param];
|
||||
}
|
||||
|
||||
#pragma mark Util
|
||||
- (UIColor *)colorFromImage:(UIImage *)image point:(CGPoint)point {
|
||||
CGImageRef cgImage = image.CGImage;
|
||||
NSUInteger width = image.size.width;
|
||||
NSUInteger height = image.size.height;
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
int bytesPerPixel = 4;
|
||||
int bytesPerRow = bytesPerPixel * 1;
|
||||
NSUInteger bitsPerComponent = 8;
|
||||
unsigned char pixelData[4] = { 0, 0, 0, 0 };
|
||||
CGContextRef context = CGBitmapContextCreate(pixelData,
|
||||
1,
|
||||
1,
|
||||
bitsPerComponent,
|
||||
bytesPerRow,
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
|
||||
// Draw the pixel we are interested in onto the bitmap context
|
||||
CGContextTranslateCTM(context, -point.x, point.y-(CGFloat)height);
|
||||
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage);
|
||||
CGContextRelease(context);
|
||||
|
||||
// Convert color values [0..255] to floats [0.0..1.0]
|
||||
CGFloat red = (CGFloat)pixelData[0] / 255.0f;
|
||||
CGFloat green = (CGFloat)pixelData[1] / 255.0f;
|
||||
CGFloat blue = (CGFloat)pixelData[2] / 255.0f;
|
||||
CGFloat alpha = (CGFloat)pixelData[3] / 255.0f;
|
||||
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
|
||||
}
|
||||
|
||||
- (NSString *)hexStringFromColor:(UIColor *)color {
|
||||
if (!color) {
|
||||
return @"";
|
||||
}
|
||||
const CGFloat *components = CGColorGetComponents(color.CGColor);
|
||||
|
||||
CGFloat r = components[0];
|
||||
CGFloat g = components[1];
|
||||
CGFloat b = components[2];
|
||||
|
||||
return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
|
||||
lroundf(r * 255),
|
||||
lroundf(g * 255),
|
||||
lroundf(b * 255)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
// Created by lynx on 2024/3/1.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIBotRichTextCellData : TUIBubbleMessageCellData
|
||||
@property(nonatomic, strong) NSString *content;
|
||||
@property(nonatomic, assign) CGFloat cellHeight;
|
||||
@property(nonatomic, assign) CGFloat lastUpdateTs;
|
||||
@property(nonatomic, assign) BOOL delayUpdate;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// TUIBotRichTextCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2024/3/1.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIBotRichTextCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@implementation TUIBotRichTextCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUIBotRichTextCellData *cellData = [[TUIBotRichTextCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
cellData.content = param[@"content"];
|
||||
cellData.reuseId = TRichTextMessageCell_ReuserId;
|
||||
cellData.status = Msg_Status_Init;
|
||||
cellData.cellHeight = TRichTextMessageCell_Height_Default;
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return @"Rich Text";
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TUIChatBotStreamTextCell.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import <TUIChat/TUITextMessageCell.h>
|
||||
#import "TUIBotStreamTextCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIBotStreamTextCell : TUITextMessageCell
|
||||
- (void)fillWithData:(TUIBotStreamTextCellData *)data;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// TUIChatBotStreamTextCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import "TUIBotStreamTextCell.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
@implementation TUIBotStreamTextCell
|
||||
- (void)fillWithData:(TUIBotStreamTextCellData *)data {
|
||||
[super fillWithData:data];
|
||||
// 在线 Push 的文本需要流式展示
|
||||
if (Msg_Source_OnlinePush == data.source) {
|
||||
data.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
|
||||
NSTimeInterval period = 0.05;
|
||||
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, period * NSEC_PER_SEC);
|
||||
dispatch_source_set_timer(data.timer, start, period * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
|
||||
dispatch_source_set_event_handler(data.timer, ^{
|
||||
if (data.displayedContentLength == data.contentString.length) {
|
||||
[self stopTimer:data.timer];
|
||||
return;
|
||||
}
|
||||
data.displayedContentLength++;
|
||||
|
||||
if (self.textView.attributedText.length > 1 &&
|
||||
[self getAttributeStringRect:self.textView.attributedText].size.height >
|
||||
[self getAttributeStringRect:[self.textView.attributedText attributedSubstringFromRange:
|
||||
NSMakeRange(0, self.textView.attributedText.length - 1)]].size.height) {
|
||||
[self stopTimer:data.timer];
|
||||
[self notifyCellSizeChanged];
|
||||
} else {
|
||||
UIColor *textColor = self.class.incommingTextColor;
|
||||
UIFont *textFont = self.class.incommingTextFont;
|
||||
if (data.direction == MsgDirectionIncoming) {
|
||||
textColor = self.class.incommingTextColor;
|
||||
textFont = self.class.incommingTextFont;
|
||||
} else {
|
||||
textColor = self.class.outgoingTextColor;
|
||||
textFont = self.class.outgoingTextFont;
|
||||
}
|
||||
self.textView.attributedText = [data getContentAttributedString:textFont];
|
||||
self.textView.textColor = textColor;
|
||||
[self updateCellConstraints];
|
||||
}
|
||||
});
|
||||
dispatch_resume(data.timer);
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)getAttributeStringRect:(NSAttributedString *)attributeString {
|
||||
return [attributeString boundingRectWithSize:CGSizeMake(TTextMessageCell_Text_Width_Max, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil];
|
||||
}
|
||||
|
||||
- (void)stopTimer:(dispatch_source_t)timer {
|
||||
if (timer) {
|
||||
dispatch_source_cancel(timer);
|
||||
timer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyCellSizeChanged {
|
||||
NSDictionary *param = @{TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey_Message : self.textData.innerMessage};
|
||||
[TUICore notifyEvent:TUICore_TUIPluginNotify
|
||||
subKey:TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey
|
||||
object:nil
|
||||
param:param];
|
||||
}
|
||||
|
||||
- (void)updateCellConstraints {
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUIBotStreamTextCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import <TUIChat/TUITextMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIBotStreamTextCellData : TUITextMessageCellData
|
||||
@property(nonatomic, strong) dispatch_source_t timer;
|
||||
@property(nonatomic, strong) UIFont *contentFont;
|
||||
@property(nonatomic, strong) NSAttributedString *contentString;
|
||||
@property(nonatomic, assign) NSInteger displayedContentLength;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// TUIBotStreamTextCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2023/10/30.
|
||||
//
|
||||
|
||||
#import "TUIBotStreamTextCellData.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
#ifndef CGFLOAT_CEIL
|
||||
#ifdef CGFLOAT_IS_DOUBLE
|
||||
#define CGFLOAT_CEIL(value) ceil(value)
|
||||
#else
|
||||
#define CGFLOAT_CEIL(value) ceilf(value)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@implementation TUIBotStreamTextCellData
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUIBotStreamTextCellData *cellData = [[TUIBotStreamTextCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
cellData.content = [self getDisplayString:message];
|
||||
cellData.displayedContentLength = 0;
|
||||
cellData.reuseId = TTextMessageCell_ReuseId;
|
||||
cellData.status = Msg_Status_Init;
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return @"";
|
||||
}
|
||||
NSMutableString *displyString = [NSMutableString string];
|
||||
NSArray *chunks = param[@"chunks"];
|
||||
for (NSString *chunk in chunks) {
|
||||
[displyString appendString:chunk];
|
||||
}
|
||||
return [displyString copy];
|
||||
}
|
||||
|
||||
- (BOOL)customReloadCellWithNewMsg:(V2TIMMessage *)newMessage {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:newMessage.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return YES;
|
||||
}
|
||||
NSString *newContent = [self.class getDisplayString:newMessage];
|
||||
if (newContent.length <= self.content.length) {
|
||||
return YES;
|
||||
}
|
||||
self.innerMessage = newMessage;
|
||||
self.content = [self.class getDisplayString:newMessage];
|
||||
[TUICore notifyEvent:TUICore_TUIPluginNotify
|
||||
subKey:TUICore_TUIPluginNotify_DidChangePluginViewSubKey
|
||||
object:nil
|
||||
param:@{TUICore_TUIPluginNotify_DidChangePluginViewSubKey_Data : self}];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)getContentAttributedString:(UIFont *)textFont {
|
||||
self.contentFont = textFont;
|
||||
self.contentString = [super getContentAttributedString:textFont];
|
||||
if (Msg_Source_OnlinePush == self.source) {
|
||||
if (self.displayedContentLength < self.contentString.length) {
|
||||
return [self.contentString attributedSubstringFromRange:NSMakeRange(0, self.displayedContentLength + 1)];
|
||||
}
|
||||
}
|
||||
return self.contentString;
|
||||
}
|
||||
|
||||
- (CGSize)getContentAttributedStringSize:(NSAttributedString *)attributeString maxTextSize:(CGSize)maxTextSize {
|
||||
CGSize size = [super getContentAttributedStringSize:attributeString maxTextSize:maxTextSize];
|
||||
if (size.height > CGFLOAT_CEIL(self.contentFont.lineHeight)) {
|
||||
return CGSizeMake(TTextMessageCell_Text_Width_Max, size.height);
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// TUICustomerServicePluginBranchCell.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCell.h>
|
||||
#import "TUICustomerServicePluginBranchCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginBranchItemCell : UITableViewCell
|
||||
|
||||
@property (nonatomic, strong) UIImageView *topLine;
|
||||
@property (nonatomic, strong) UILabel *contentLabel;
|
||||
@property (nonatomic, strong) UIImageView *arrowView;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUICustomerServicePluginBranchCell : TUIBubbleMessageCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *headerLabel;
|
||||
//@property (nonatomic, strong) NSMutableArray *itemButtons;
|
||||
@property (nonatomic, strong) UITableView *itemsTableView;
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginBranchCellData *)data;
|
||||
|
||||
@property (nonatomic, strong) TUICustomerServicePluginBranchCellData *customData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// TUICustomerServicePluginBranchCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginBranchCell.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@implementation TUICustomerServicePluginBranchItemCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.contentView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
_topLine = [[UIImageView alloc] init];
|
||||
[_topLine setImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_branch_dotted_line_img", @"dotted_line")];
|
||||
[self.contentView addSubview:_topLine];
|
||||
|
||||
_contentLabel = [[UILabel alloc] init];
|
||||
_contentLabel.font = [UIFont systemFontOfSize:16];
|
||||
_contentLabel.numberOfLines = 0;
|
||||
_contentLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_contentLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_branch_content_text_color", @"#368DFF");
|
||||
[self.contentView addSubview:_contentLabel];
|
||||
|
||||
_arrowView = [[UIImageView alloc] init];
|
||||
[_arrowView setImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_branch_arrow_img", @"arrow")];
|
||||
[self.contentView addSubview:_arrowView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// this is Apple's recommended place for adding/updating constraints
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
[self.topLine mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(self.mm_w - TUICustomerServicePluginBranchCellMargin * 2);
|
||||
make.height.mas_equalTo(0.5);
|
||||
}];
|
||||
|
||||
[self.contentLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(self.mm_w - TUICustomerServicePluginBranchCellMargin * 2 - 5 - 6);
|
||||
make.height.mas_equalTo([TUICustomerServicePluginDataProvider calcBranchCellHeightOfContent:self.contentLabel.text]);
|
||||
}];
|
||||
|
||||
[self.arrowView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.contentLabel.mas_trailing).offset(6);
|
||||
make.top.mas_equalTo((self.mm_h - self.arrowView.mm_h) / 2.0);
|
||||
make.width.mas_equalTo(5);
|
||||
make.height.mas_equalTo(9);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUICustomerServicePluginBranchCell() <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginBranchCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
_headerLabel = [[UILabel alloc] init];
|
||||
_headerLabel.font = [UIFont systemFontOfSize:16];
|
||||
_headerLabel.numberOfLines = 0;
|
||||
_headerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_branch_header_text_color", @"#000000");
|
||||
[self.container addSubview:_headerLabel];
|
||||
|
||||
_itemsTableView = [[UITableView alloc] init];
|
||||
_itemsTableView.tableFooterView = [[UIView alloc] init];
|
||||
// _itemsTableView.backgroundColor = TUICoreDynamicColor(@"customer_service_brance_bg_color", @"#FFFFFF");
|
||||
_itemsTableView.backgroundColor = [UIColor clearColor];
|
||||
_itemsTableView.delegate = self;
|
||||
_itemsTableView.dataSource = self;
|
||||
_itemsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
[_itemsTableView registerClass:[TUICustomerServicePluginBranchItemCell class] forCellReuseIdentifier:@"item_cell"];
|
||||
[self.container addSubview:_itemsTableView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginBranchCellData *)data {
|
||||
[super fillWithData:data];
|
||||
|
||||
self.customData = data;
|
||||
self.headerLabel.text = data.header;
|
||||
[self.itemsTableView reloadData];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
// Override, the size of bubble content.
|
||||
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
|
||||
NSAssert([data isKindOfClass:TUICustomerServicePluginBranchCellData.class],
|
||||
@"data must be a kind of TUICustomerServicePluginBranchCellData");
|
||||
TUICustomerServicePluginBranchCellData *branchCellData = (TUICustomerServicePluginBranchCellData *)data;
|
||||
return [TUICustomerServicePluginDataProvider calcBranchCellSize:branchCellData.header
|
||||
items:branchCellData.items];
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
CGFloat cellHeight = [TUICustomerServicePluginDataProvider calcBranchCellSize:self.customData.header
|
||||
items:self.customData.items].height;
|
||||
CGSize tableViewSize = [TUICustomerServicePluginDataProvider calcBranchCellSizeOfTableView:self.customData.items];
|
||||
CGSize headerSize= [TUICustomerServicePluginDataProvider calcBranchCellSizeOfHeader:self.customData.header];
|
||||
|
||||
self.container
|
||||
.mm_width(TUICustomerServicePluginBranchCellWidth)
|
||||
.mm_height(cellHeight);
|
||||
|
||||
[self.headerLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.top.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.width.mas_equalTo(headerSize.width);
|
||||
make.height.mas_equalTo(headerSize.height);
|
||||
}];
|
||||
|
||||
[self.itemsTableView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(0);
|
||||
make.top.mas_equalTo(self.headerLabel.mas_bottom).offset(TUICustomerServicePluginBranchCellInnerMargin);
|
||||
make.width.mas_equalTo(tableViewSize.width);
|
||||
make.height.mas_equalTo(tableViewSize.height);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate & UITableViewDataSource
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return [TUICustomerServicePluginDataProvider calcBranchCellHeightOfTableView:self.customData.items row:indexPath.row];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.customData.items.count <= indexPath.row) {
|
||||
return;
|
||||
}
|
||||
if (self.customData.selected) {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
||||
return;
|
||||
}
|
||||
NSString *content = self.customData.items[indexPath.row];
|
||||
[TUICustomerServicePluginDataProvider sendTextMessage:content];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.customData.items.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.customData.items.count <= indexPath.row) {
|
||||
return nil;
|
||||
}
|
||||
NSString *content = self.customData.items[indexPath.row];
|
||||
TUICustomerServicePluginBranchItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"item_cell" forIndexPath:indexPath];
|
||||
cell.contentLabel.text = content;
|
||||
|
||||
// tell constraints they need updating
|
||||
[cell setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[cell updateConstraintsIfNeeded];
|
||||
|
||||
[cell layoutIfNeeded];
|
||||
return cell;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// TUICustomerServicePluginBranchCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginBranchCellData : TUIBubbleMessageCellData
|
||||
|
||||
@property (nonatomic, copy) NSString *header;
|
||||
@property (nonatomic, strong) NSMutableArray *items;
|
||||
@property (nonatomic, copy) NSString *selectedContent;
|
||||
@property (nonatomic, assign) BOOL selected;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// TUICustomerServicePluginBranchCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginBranchCellData.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
|
||||
@implementation TUICustomerServicePluginBranchCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUICustomerServicePluginBranchCellData *cellData = [[TUICustomerServicePluginBranchCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
NSDictionary *content = param[@"content"];
|
||||
cellData.header = content[@"header"];
|
||||
NSArray *items = content[@"items"];
|
||||
for (NSDictionary *item in items) {
|
||||
[cellData.items addObject:item[@"content"]];
|
||||
}
|
||||
if ([content.allKeys containsObject:@"selected"]) {
|
||||
NSDictionary *selected = content[@"selected"];
|
||||
cellData.selectedContent = selected[@"content"];
|
||||
cellData.selected = YES;
|
||||
}
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUICustomerServiceBranchMessage);
|
||||
}
|
||||
|
||||
// Override
|
||||
- (BOOL)canForward {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)items {
|
||||
if (!_items) {
|
||||
_items = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _items;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TUICustomerServicePluginCardCell.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCell.h>
|
||||
#import "TUICustomerServicePluginCardCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginCardCell : TUIBubbleMessageCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *headerLabel;
|
||||
@property (nonatomic, strong) UILabel *descLabel;
|
||||
@property (nonatomic, strong) UIImageView *picView;
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginCardCellData *)data;
|
||||
|
||||
@property (nonatomic, strong) TUICustomerServicePluginCardCellData *customData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// TUICustomerServicePluginCardCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginCardCell.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
|
||||
@interface TUICustomerServicePluginCardCell()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginCardCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
_headerLabel = [[UILabel alloc] init];
|
||||
_headerLabel.font = [UIFont systemFontOfSize:12];
|
||||
_headerLabel.numberOfLines = 3;
|
||||
_headerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_card_header_text_color", @"#000000");
|
||||
[self.container addSubview:_headerLabel];
|
||||
|
||||
_descLabel = [[UILabel alloc] init];
|
||||
_descLabel.font = [UIFont systemFontOfSize:16];
|
||||
_descLabel.numberOfLines = 1;
|
||||
_descLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_descLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_card_desc_text_color", @"#FF6C2E");
|
||||
|
||||
[self.container addSubview:_descLabel];
|
||||
|
||||
_picView = [[UIImageView alloc] init];
|
||||
_picView.layer.cornerRadius = 8;
|
||||
_picView.layer.masksToBounds = YES;
|
||||
[self.container addSubview:_picView];
|
||||
|
||||
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapped:)];
|
||||
[self.container addGestureRecognizer:recognizer];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onTapped:(UITapGestureRecognizer *)recognizer {
|
||||
NSURL *url = [NSURL URLWithString:self.customData.jumpURL ? : @""];
|
||||
[TUITool openLinkWithURL:url];
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginCardCellData *)data {
|
||||
[super fillWithData:data];
|
||||
|
||||
self.customData = data;
|
||||
self.headerLabel.text = data.header;
|
||||
self.descLabel.text = data.desc;
|
||||
[self.picView sd_setImageWithURL:[NSURL URLWithString:data.picURL] placeholderImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_card_pic_placeholder_img", @"card_pic_placeholder")];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
// Override, the size of bubble content.
|
||||
+ (CGSize)getContentSize:(TUICustomerServicePluginCardCellData *)data {
|
||||
return CGSizeMake(TUICustomerServicePluginCardBubbleWidth, 112);
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
[self.picView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(14);
|
||||
make.top.mas_equalTo(13);
|
||||
make.width.mas_equalTo(86);
|
||||
make.height.mas_equalTo(86);
|
||||
}];
|
||||
|
||||
CGSize headerSize = [TUICustomerServicePluginDataProvider calcCardHeaderSize:self.customData.header];
|
||||
[self.headerLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.picView.mas_trailing).offset(12);
|
||||
make.top.mas_equalTo(13);
|
||||
make.width.mas_equalTo(headerSize.width);
|
||||
make.height.mas_equalTo(headerSize.height);
|
||||
}];
|
||||
|
||||
[self.descLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.picView.mas_trailing).offset(12);
|
||||
make.bottom.mas_equalTo(self.container.mas_bottom).mas_offset(-13);
|
||||
make.width.mas_equalTo(headerSize.width);
|
||||
make.height.mas_equalTo(22);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// TUICustomerServicePluginCardCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginCardCellData : TUIBubbleMessageCellData
|
||||
|
||||
@property (nonatomic, copy) NSString *header;
|
||||
@property (nonatomic, copy) NSString *desc;
|
||||
@property (nonatomic, copy) NSString *picURL;
|
||||
@property (nonatomic, copy) NSString *jumpURL;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// TUICustomerServicePluginCardCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginCardCellData.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
|
||||
@implementation TUICustomerServicePluginCardCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUICustomerServicePluginCardCellData *cellData = [[TUICustomerServicePluginCardCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
NSDictionary *content = param[@"content"];
|
||||
cellData.header = content[@"header"];
|
||||
cellData.desc = content[@"desc"];
|
||||
cellData.picURL = content[@"pic"];
|
||||
cellData.jumpURL = content[@"url"];
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUICustomerServiceCardMessage);
|
||||
}
|
||||
|
||||
// Override
|
||||
- (BOOL)canForward {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// TUICustomerServicePluginCollectionCell.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCell.h>
|
||||
#import "TUICustomerServicePluginCollectionCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginCollectionItemCell : UITableViewCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *contentLabel;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUICustomerServicePluginCollectionCell : TUIBubbleMessageCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *headerLabel;
|
||||
@property (nonatomic, strong) UITableView *itemsTableView;
|
||||
@property (nonatomic, strong) UITextField *inputTextField;
|
||||
@property (nonatomic, strong) UIButton *confirmButton;
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginCollectionCellData *)data;
|
||||
|
||||
@property (nonatomic, strong) TUICustomerServicePluginCollectionCellData *customData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,272 @@
|
||||
//
|
||||
// TUICustomerServicePluginCollectionCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginCollectionCell.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
@implementation TUICustomerServicePluginCollectionItemCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.contentView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
_contentLabel = [[UILabel alloc] init];
|
||||
_contentLabel.font = [UIFont systemFontOfSize:16];
|
||||
_contentLabel.numberOfLines = 0;
|
||||
_contentLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_contentLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_collection_content_text_color", @"#368DFF");
|
||||
[self.contentView addSubview:_contentLabel];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
[self.contentLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(TUICustomerServicePluginBranchCellWidth - TUICustomerServicePluginBranchCellMargin * 2);
|
||||
make.height.mas_equalTo([TUICustomerServicePluginDataProvider calcCollectionCellHeightOfContent:self.contentLabel.text]);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUICustomerServicePluginCollectionCell() <UITableViewDelegate, UITableViewDataSource, TUINotificationProtocol>
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginCollectionCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
[self setupHeaderLabel];
|
||||
[self setupListCollectionViews];
|
||||
[self setupInputCollectionViews];
|
||||
|
||||
[TUICore registerEvent:TUICore_TUIChatNotify
|
||||
subKey:TUICore_TUIChatNotify_KeyboardWillHideSubKey
|
||||
object:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupHeaderLabel {
|
||||
_headerLabel = [[UILabel alloc] init];
|
||||
_headerLabel.font = [UIFont systemFontOfSize:16];
|
||||
_headerLabel.numberOfLines = 0;
|
||||
_headerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_collection_header_text_color", @"#000000");
|
||||
_headerLabel.backgroundColor = [UIColor clearColor];
|
||||
[self.container addSubview:_headerLabel];
|
||||
}
|
||||
|
||||
- (void)setupListCollectionViews {
|
||||
_itemsTableView = [[UITableView alloc] init];
|
||||
_itemsTableView.tableFooterView = [[UIView alloc] init];
|
||||
// _itemsTableView.backgroundColor = TUICoreDynamicColor(@"customer_service_brance_bg_color", @"#FFFFFF");
|
||||
_itemsTableView.backgroundColor = [UIColor clearColor];
|
||||
_itemsTableView.delegate = self;
|
||||
_itemsTableView.dataSource = self;
|
||||
_itemsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
[_itemsTableView registerClass:[TUICustomerServicePluginCollectionItemCell class] forCellReuseIdentifier:@"item_cell"];
|
||||
_itemsTableView.hidden = YES;
|
||||
[self.container addSubview:_itemsTableView];
|
||||
}
|
||||
|
||||
- (void)setupInputCollectionViews {
|
||||
_inputTextField = [[UITextField alloc] init];
|
||||
_inputTextField.backgroundColor = TUICustomerServicePluginDynamicColor(@"customer_service_collection_textfield_bg_color", @"#FFFFFF");
|
||||
_inputTextField.hidden = YES;
|
||||
_inputTextField.font = [UIFont systemFontOfSize:16];
|
||||
[self.container addSubview:_inputTextField];
|
||||
UIView *blankView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 6, 20)];
|
||||
blankView.backgroundColor = [UIColor clearColor];
|
||||
_inputTextField.leftViewMode = UITextFieldViewModeAlways;
|
||||
_inputTextField.leftView = blankView;
|
||||
|
||||
_confirmButton = [UIButton new];
|
||||
_confirmButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
|
||||
_confirmButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_confirmButton setTitleColor:TUICustomerServicePluginDynamicColor(@"customer_service_collection_textfield_text_color", @"#000000")
|
||||
forState:UIControlStateNormal];
|
||||
[_confirmButton setImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_collection_submit_img", @"submit") forState:UIControlStateNormal];
|
||||
[_confirmButton addTarget:self action:@selector(onConfirmButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_confirmButton.hidden = YES;
|
||||
[self.container addSubview:_confirmButton];
|
||||
}
|
||||
|
||||
- (void)hideListCollectionViews {
|
||||
self.itemsTableView.hidden = YES;
|
||||
self.inputTextField.hidden = NO;
|
||||
self.confirmButton.hidden = NO;
|
||||
}
|
||||
|
||||
- (void)hideInputCollectionViews {
|
||||
self.itemsTableView.hidden = NO;
|
||||
self.inputTextField.hidden = YES;
|
||||
self.confirmButton.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)onConfirmButtonClicked:(UIButton *)sender {
|
||||
self.inputTextField.userInteractionEnabled = NO;
|
||||
|
||||
NSString *content = self.inputTextField.text;
|
||||
[TUICustomerServicePluginDataProvider sendTextMessage:content];
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginCollectionCellData *)data {
|
||||
[super fillWithData:data];
|
||||
|
||||
self.customData = data;
|
||||
self.headerLabel.text = data.header;
|
||||
|
||||
if (data.type == 1) {
|
||||
[self hideInputCollectionViews];
|
||||
[self.itemsTableView reloadData];
|
||||
} else {
|
||||
[self hideListCollectionViews];
|
||||
self.inputTextField.text = self.customData.selectedContent;
|
||||
|
||||
BOOL canFill = self.customData.selectedContent.length == 0;
|
||||
self.inputTextField.enabled = canFill;
|
||||
self.confirmButton.enabled = canFill;
|
||||
}
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
// Override, the size of bubble content.
|
||||
+ (CGSize)getContentSize:(TUICustomerServicePluginCollectionCellData *)data {
|
||||
if (data.type == 1) {
|
||||
return [TUICustomerServicePluginDataProvider calcCollectionCellSize:data.header items:data.items];
|
||||
} else {
|
||||
return [TUICustomerServicePluginDataProvider calcCollectionInputCellSize:data.header];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
if (self.customData.type == 1) {
|
||||
CGFloat cellHeight = [TUICustomerServicePluginDataProvider calcBranchCellSize:self.customData.header
|
||||
items:self.customData.items].height;
|
||||
CGSize tableViewSize = [TUICustomerServicePluginDataProvider calcCollectionCellSizeOfTableView:self.customData.items];
|
||||
CGSize headerSize= [TUICustomerServicePluginDataProvider calcCollectionCellSizeOfHeader:self.customData.header];
|
||||
|
||||
self.container
|
||||
.mm_width(TUICustomerServicePluginBranchCellWidth)
|
||||
.mm_height(cellHeight);
|
||||
|
||||
[self.headerLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.top.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.width.mas_equalTo(headerSize.width);
|
||||
make.height.mas_equalTo(headerSize.height);
|
||||
}];
|
||||
|
||||
[self.itemsTableView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(0);
|
||||
make.top.mas_equalTo(self.headerLabel.mas_bottom).offset(TUICustomerServicePluginBranchCellInnerMargin);
|
||||
make.width.mas_equalTo(tableViewSize.width);
|
||||
make.height.mas_equalTo(tableViewSize.height);
|
||||
}];
|
||||
} else {
|
||||
self.container
|
||||
.mm_width(TUICustomerServicePluginInputCellWidth)
|
||||
.mm_height([TUICustomerServicePluginDataProvider calcCollectionInputCellSize:self.customData.header].height);
|
||||
|
||||
[self.headerLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.top.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.width.mas_equalTo([TUICustomerServicePluginDataProvider calcCollectionInputCellSizeOfHeader:self.customData.header].width);
|
||||
make.height.mas_equalTo([TUICustomerServicePluginDataProvider calcCollectionInputCellSizeOfHeader:self.customData.header].height);
|
||||
}];
|
||||
|
||||
[self.inputTextField mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(TUICustomerServicePluginBranchCellMargin);
|
||||
make.top.mas_equalTo(self.headerLabel.mas_bottom).offset(6);
|
||||
make.width.mas_equalTo(TUICustomerServicePluginInputCellWidth - TUICustomerServicePluginBranchCellMargin * 2 - 40);
|
||||
make.height.mas_equalTo(36);
|
||||
}];
|
||||
|
||||
[self setupInputTextFieldCorners];
|
||||
|
||||
[self.confirmButton mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.inputTextField.mas_trailing);
|
||||
make.top.mas_equalTo(self.headerLabel.mas_bottom).offset(6);
|
||||
make.width.mas_equalTo(40);
|
||||
make.height.mas_equalTo(36);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupInputTextFieldCorners {
|
||||
UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerBottomLeft;
|
||||
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:self.inputTextField.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(8, 8)];
|
||||
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
|
||||
layer.path = bezierPath.CGPath;
|
||||
self.inputTextField.layer.mask = layer;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate & UITableViewDataSource
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return [TUICustomerServicePluginDataProvider calcCollectionCellHeightOfTableView:self.customData.items row:indexPath.row];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.customData.items.count <= indexPath.row) {
|
||||
return;
|
||||
}
|
||||
NSString *content = self.customData.items[indexPath.row];
|
||||
[TUICustomerServicePluginDataProvider sendTextMessage:content];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.customData.items.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.customData.items.count <= indexPath.row) {
|
||||
return nil;
|
||||
}
|
||||
NSString *content = self.customData.items[indexPath.row];
|
||||
TUICustomerServicePluginCollectionItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"item_cell" forIndexPath:indexPath];
|
||||
cell.contentLabel.text = content;
|
||||
// tell constraints they need updating
|
||||
[cell setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[cell updateConstraintsIfNeeded];
|
||||
|
||||
[cell layoutIfNeeded];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - TUINotificationProtocol
|
||||
- (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(nullable id)anObject param:(nullable NSDictionary *)param {
|
||||
if ([key isEqualToString:TUICore_TUIChatNotify] &&
|
||||
[subKey isEqualToString:TUICore_TUIChatNotify_KeyboardWillHideSubKey]) {
|
||||
[self.inputTextField resignFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// TUICustomerServicePluginCollectionCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginCollectionCellData : TUIBubbleMessageCellData
|
||||
|
||||
@property (nonatomic, copy) NSString *header;
|
||||
@property (nonatomic, strong) NSMutableArray *items;
|
||||
@property (nonatomic, copy) NSString *selectedContent;
|
||||
@property (nonatomic, assign) NSInteger type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// TUICustomerServicePluginCollectionCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginCollectionCellData.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
|
||||
@implementation TUICustomerServicePluginCollectionCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUICustomerServicePluginCollectionCellData *cellData = [[TUICustomerServicePluginCollectionCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
NSDictionary *content = param[@"content"];
|
||||
cellData.header = content[@"header"];
|
||||
NSArray *items = content[@"items"];
|
||||
for (NSDictionary *item in items) {
|
||||
[cellData.items addObject:item[@"content"]];
|
||||
}
|
||||
cellData.type = [content[@"type"] integerValue];
|
||||
NSDictionary *selected = content[@"selected"];
|
||||
cellData.selectedContent = selected[@"content"];
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUICustomerServiceCollectInfomation);
|
||||
}
|
||||
|
||||
// Override
|
||||
- (BOOL)canForward {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)items {
|
||||
if (!_items) {
|
||||
_items = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _items;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// TUICustomerServicePluginEvaluationCell.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import "TUICustomerServicePluginEvaluationCellData.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginEvaluationCell : TUIMessageCell
|
||||
|
||||
@property (nonatomic, strong) UIView *backView;
|
||||
|
||||
@property (nonatomic, strong) UILabel *topLabel;
|
||||
@property (nonatomic, strong) UILabel *bottomLabel;
|
||||
|
||||
@property (nonatomic, strong) UILabel *headerLabel;
|
||||
@property (nonatomic, strong) UIButton *submitButton;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *scoreButtonArray;
|
||||
|
||||
|
||||
@property (nonatomic, strong) TUICustomerServicePluginEvaluationCellData *customData;
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginEvaluationCellData *)data;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,314 @@
|
||||
//
|
||||
// TUICustomerServicePluginEvaluationCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginEvaluationCell.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
@interface TUICustomerServicePluginEvaluationCell()
|
||||
|
||||
@property (nonatomic, strong) UIButton *selectedButton;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginEvaluationCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
_backView = [UIView new];
|
||||
_backView.backgroundColor = TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_bg_color", @"#FBFBFB");
|
||||
_backView.layer.cornerRadius = 10;
|
||||
_backView.layer.masksToBounds = YES;
|
||||
[self.container addSubview:_backView];
|
||||
|
||||
_topLabel = [[UILabel alloc] init];
|
||||
_topLabel.font = [UIFont systemFontOfSize:12];
|
||||
_topLabel.numberOfLines = 1;
|
||||
_topLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_topLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_top_text_color", @"#9A9A9A");
|
||||
_topLabel.text = @"感谢您使用我们的服务,请对此次服务进行评价!";
|
||||
_topLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.container addSubview:_topLabel];
|
||||
|
||||
_headerLabel = [[UILabel alloc] init];
|
||||
_headerLabel.font = [UIFont systemFontOfSize:12];
|
||||
_headerLabel.numberOfLines = 0;
|
||||
_headerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_header_text_color", @"#1C1C1C");
|
||||
[self.backView addSubview:_headerLabel];
|
||||
|
||||
_submitButton = [UIButton new];
|
||||
[_submitButton setTitle:TIMCommonLocalizableString(TUICustomerServiceSubmitEvaluation) forState:UIControlStateNormal];
|
||||
_submitButton.layer.cornerRadius = 4;
|
||||
_submitButton.layer.masksToBounds = YES;
|
||||
_submitButton.titleLabel.font = [UIFont systemFontOfSize:12];
|
||||
[_submitButton setTitleColor:TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_button_text_color", @"#FFFFFF")
|
||||
forState:UIControlStateNormal];
|
||||
[_submitButton addTarget:self action:@selector(onSubmitButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_submitButton.backgroundColor = TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_button_bg_color", @"#2F80ED");
|
||||
[self.backView addSubview:_submitButton];
|
||||
|
||||
_bottomLabel = [[UILabel alloc] init];
|
||||
_bottomLabel.font = [UIFont systemFontOfSize:12];
|
||||
_bottomLabel.numberOfLines = 0;
|
||||
_bottomLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_bottomLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_bottom_text_color", @"#9A9A9A");
|
||||
_bottomLabel.hidden = YES;
|
||||
_bottomLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.container addSubview:_bottomLabel];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onSubmitButtonClicked:(UIButton *)sender {
|
||||
NSString *itemID = [NSString stringWithFormat:@"%ld", (long)self.selectedButton.tag];
|
||||
NSDictionary *dict = @{@"menuSelected": @{@"id": itemID,
|
||||
@"content": self.customData.itemDict[itemID] ? : @"",
|
||||
@"sessionId": self.customData.sessionID ? : @""},
|
||||
@"src": BussinessID_Src_CustomerService_EvaluationSelected};
|
||||
NSData *data = [TUITool dictionary2JsonData:dict];
|
||||
[TUICustomerServicePluginDataProvider sendCustomMessageWithoutUpdateUI:data];
|
||||
}
|
||||
|
||||
- (void)onScoreButtonClicked:(UIButton *)sender {
|
||||
if (self.customData.isExpired) {
|
||||
return;
|
||||
}
|
||||
self.selectedButton = sender;
|
||||
for (int i = 0; i < self.scoreButtonArray.count; i++) {
|
||||
UIButton *button = self.scoreButtonArray[i];
|
||||
if (button.tag <= sender.tag) {
|
||||
[self updateScoreButton:button index:i selected:YES];
|
||||
} else {
|
||||
[self updateScoreButton:button index:i selected:NO];
|
||||
}
|
||||
}
|
||||
// The submit button can only be clicked after selecting ⭐️
|
||||
self.submitButton.enabled = YES;
|
||||
self.submitButton.alpha = 1.0;
|
||||
}
|
||||
|
||||
- (void)notifyCellSizeChanged {
|
||||
NSDictionary *param = @{TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey_Message : self.customData.innerMessage};
|
||||
[TUICore notifyEvent:TUICore_TUIPluginNotify
|
||||
subKey:TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey
|
||||
object:nil
|
||||
param:param];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
for (UIButton *button in self.scoreButtonArray) {
|
||||
[button removeFromSuperview];
|
||||
}
|
||||
[self.scoreButtonArray removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginEvaluationCellData *)data {
|
||||
[super fillWithData:data];
|
||||
BOOL isCellDataChanged = [self isCellDataChanged:self.customData newData:data];
|
||||
|
||||
self.customData = data;
|
||||
self.headerLabel.text = data.header;
|
||||
self.bottomLabel.text = data.tail;
|
||||
|
||||
if (data.isSelected) {
|
||||
for (int i = 0; i < data.totalScore; i++) {
|
||||
NSDictionary *item = data.items[i];
|
||||
UIButton *scoreButton = [[UIButton alloc] init];
|
||||
scoreButton.tag = [item[@"id"] integerValue];
|
||||
scoreButton.enabled = NO;
|
||||
scoreButton.alpha = 0.8;
|
||||
scoreButton.titleLabel.font = [UIFont systemFontOfSize:12];
|
||||
|
||||
if (i <= data.score) {
|
||||
[self updateScoreButton:scoreButton index:i selected:YES];
|
||||
} else {
|
||||
[self updateScoreButton:scoreButton index:i selected:NO];
|
||||
}
|
||||
[self.backView addSubview:scoreButton];
|
||||
[self.scoreButtonArray addObject:scoreButton];
|
||||
}
|
||||
self.submitButton.enabled = NO;
|
||||
self.submitButton.alpha = 0.8;
|
||||
self.bottomLabel.hidden = NO;
|
||||
|
||||
if (isCellDataChanged) {
|
||||
[self notifyCellSizeChanged];
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < data.totalScore; i++) {
|
||||
NSDictionary *item = data.items[i];
|
||||
|
||||
UIButton *scoreButton = [[UIButton alloc] init];
|
||||
[scoreButton addTarget:self
|
||||
action:@selector(onScoreButtonClicked:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
scoreButton.tag = [item[@"id"] integerValue];
|
||||
scoreButton.enabled = !self.customData.isExpired;
|
||||
scoreButton.alpha = 1.0;
|
||||
scoreButton.titleLabel.font = [UIFont systemFontOfSize:12];
|
||||
|
||||
[self updateScoreButton:scoreButton index:i selected:NO];
|
||||
[self.backView addSubview:scoreButton];
|
||||
[self.scoreButtonArray addObject:scoreButton];
|
||||
}
|
||||
|
||||
self.submitButton.enabled = NO;
|
||||
self.submitButton.alpha = 0.8;
|
||||
self.bottomLabel.hidden = YES;
|
||||
}
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)updateScoreButton:(UIButton *)button index:(int)i selected:(BOOL)selected {
|
||||
if (self.customData.type == 1) {
|
||||
// star
|
||||
if (selected) {
|
||||
[button setBackgroundImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_evaluation_star_selected_img", @"star_selected") forState:UIControlStateNormal];
|
||||
} else {
|
||||
[button setBackgroundImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_evaluation_star_unselected_img", @"star_unselected") forState:UIControlStateNormal];
|
||||
}
|
||||
} else if (self.customData.type == 2) {
|
||||
// number
|
||||
if (selected) {
|
||||
[button setBackgroundImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_evaluation_number_selected_img", @"number_selected") forState:UIControlStateNormal];
|
||||
[button setTitle:[NSString stringWithFormat:@"%d", i + 1] forState:UIControlStateNormal];
|
||||
[button setTitleColor:TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_button_selected_bg_color", @"#FFFFFF") forState:UIControlStateNormal];
|
||||
} else {
|
||||
[button setBackgroundImage:TUICustomerServicePluginBundleThemeImage(@"customer_service_evaluation_number_unselected_img", @"number_unselected") forState:UIControlStateNormal];
|
||||
[button setTitle:[NSString stringWithFormat:@"%d", i + 1] forState:UIControlStateNormal];
|
||||
[button setTitleColor:TUICustomerServicePluginDynamicColor(@"customer_service_evaluation_button_unselected_bg_color", @"#006EFF") forState:UIControlStateNormal];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override, the height of the cell
|
||||
+ (CGFloat)getHeight:(TUICustomerServicePluginEvaluationCellData *)data withWidth:(CGFloat)width {
|
||||
return [self getContentSize:data].height;
|
||||
}
|
||||
|
||||
// Override, the size of bubble content.
|
||||
+ (CGSize)getContentSize:(TUICustomerServicePluginEvaluationCellData *)data {
|
||||
return [TUICustomerServicePluginDataProvider calcEvaluationCellSize:data.header
|
||||
tail:data.tail
|
||||
score:data.totalScore
|
||||
selected:data.isSelected];
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
self.container.mm_fill();
|
||||
|
||||
[self.topLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(self);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(270);
|
||||
make.height.mas_equalTo(20);
|
||||
}];
|
||||
|
||||
[self.backView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(self);
|
||||
make.top.mas_equalTo(self.topLabel.mas_bottom).offset(20);
|
||||
make.width.mas_equalTo(TUICustomerServicePluginEvaluationBubbleWidth);
|
||||
make.height.mas_equalTo([TUICustomerServicePluginDataProvider calcEvaluationBubbleSize:self.customData.header
|
||||
score:self.customData.totalScore].height);
|
||||
}];
|
||||
|
||||
[self.headerLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(self.backView);
|
||||
make.top.mas_equalTo(20);
|
||||
make.width.mas_equalTo([TUICustomerServicePluginDataProvider calcEvaluationBubbleHeaderSize:self.customData.header].width);
|
||||
make.height.mas_equalTo([TUICustomerServicePluginDataProvider calcEvaluationBubbleHeaderSize:self.customData.header].height);
|
||||
}];
|
||||
|
||||
UIImageView *leftView = nil;
|
||||
UIImageView *upView = nil;
|
||||
float leftMargin = (self.backView.mm_w - 24 * 5 - 15 * 4) / 2.0;
|
||||
for (int i = 0; i < self.scoreButtonArray.count; i++) {
|
||||
UIImageView *scoreView = self.scoreButtonArray[i];
|
||||
if (i < 5) {
|
||||
// First row.
|
||||
[scoreView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
if (leftView == nil) {
|
||||
make.leading.mas_equalTo(leftMargin);
|
||||
} else {
|
||||
make.leading.mas_equalTo(leftView.mas_trailing).offset(15);
|
||||
}
|
||||
make.top.mas_equalTo(self.headerLabel.mas_bottom).offset(12);
|
||||
make.width.mas_equalTo(24);
|
||||
make.height.mas_equalTo(24);
|
||||
}];
|
||||
leftView = scoreView;
|
||||
upView = scoreView;
|
||||
} else {
|
||||
// Second row if exist.
|
||||
if (i == 5) {
|
||||
leftView = nil;
|
||||
}
|
||||
[scoreView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
if (leftView == nil) {
|
||||
make.leading.mas_equalTo(leftMargin);
|
||||
} else {
|
||||
make.leading.mas_equalTo(leftView.mas_trailing).offset(15);
|
||||
}
|
||||
make.top.mas_equalTo(upView.mas_bottom).offset(12);
|
||||
make.width.mas_equalTo(24);
|
||||
make.height.mas_equalTo(24);
|
||||
}];
|
||||
leftView = scoreView;
|
||||
}
|
||||
}
|
||||
|
||||
UIImageView *scoreView = self.scoreButtonArray.count <= 5 ? self.scoreButtonArray.firstObject : self.scoreButtonArray.lastObject;
|
||||
|
||||
if (scoreView) {
|
||||
[self.submitButton mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(self.backView);
|
||||
make.top.mas_equalTo(scoreView.mas_bottom).offset(12);
|
||||
make.width.mas_equalTo(140);
|
||||
make.height.mas_equalTo(30);
|
||||
}];
|
||||
}
|
||||
|
||||
if (self.customData.isSelected) {
|
||||
CGSize tailSize = [TUICustomerServicePluginDataProvider calcEvaluationBubbleTailSize:self.customData.tail];
|
||||
[self.bottomLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(self);
|
||||
make.top.mas_equalTo(self.backView.mas_bottom).offset(20);
|
||||
make.width.mas_equalTo(tailSize.width);
|
||||
make.height.mas_equalTo(tailSize.height);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableArray *)scoreButtonArray {
|
||||
if (!_scoreButtonArray) {
|
||||
_scoreButtonArray = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _scoreButtonArray;
|
||||
}
|
||||
|
||||
- (BOOL)isCellDataChanged:(TUICustomerServicePluginEvaluationCellData *)oldData
|
||||
newData:(TUICustomerServicePluginEvaluationCellData *)newData {
|
||||
if (oldData && newData && oldData.score == newData.score && oldData.isSelected == newData.isSelected) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// TUICustomerServicePluginEvaluationCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginEvaluationCellData : TUIMessageCellData
|
||||
|
||||
@property (nonatomic, copy) NSString *header;
|
||||
@property (nonatomic, copy) NSString *tail;
|
||||
|
||||
@property (nonatomic, assign) NSInteger totalScore;
|
||||
@property (nonatomic, assign) NSInteger score;
|
||||
|
||||
@property (nonatomic, assign) NSInteger type; // 1:star, 2:number
|
||||
@property (nonatomic, assign) BOOL isSelected;
|
||||
@property (nonatomic, copy) NSString *sessionID;
|
||||
@property (nonatomic, assign) NSTimeInterval expireTime;
|
||||
@property (nonatomic, assign) BOOL isExpired;
|
||||
|
||||
@property (nonatomic, strong) NSArray *items;
|
||||
@property (nonatomic, strong) NSMutableDictionary *itemDict;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// TUICustomerServicePluginEvaluationCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginEvaluationCellData.h"
|
||||
#import <TIMCommon/TUIMessageCellLayout.h>
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
|
||||
@implementation TUICustomerServicePluginEvaluationCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUICustomerServicePluginEvaluationCellData *cellData = [[TUICustomerServicePluginEvaluationCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
|
||||
NSDictionary *content = param[@"menuContent"];
|
||||
cellData.type = [content[@"type"] integerValue];
|
||||
cellData.header = content[@"head"];
|
||||
cellData.tail = content[@"tail"];
|
||||
|
||||
if ([content.allKeys containsObject:@"selected"]) {
|
||||
NSDictionary *selected = content[@"selected"];
|
||||
cellData.isSelected = selected.count > 0;
|
||||
NSInteger selectedID = [selected[@"id"] integerValue];
|
||||
cellData.score = selectedID < 100 ? selectedID : selectedID - 100;
|
||||
} else {
|
||||
cellData.isSelected = NO;
|
||||
}
|
||||
|
||||
cellData.sessionID = content[@"sessionId"];
|
||||
cellData.expireTime = [content[@"expireTime"] unsignedIntegerValue];
|
||||
|
||||
NSArray *items = content[@"menu"];
|
||||
cellData.totalScore = items.count;
|
||||
cellData.items = items;
|
||||
for (NSDictionary *item in items) {
|
||||
[cellData.itemDict setValue:item[@"content"] forKey:item[@"id"]];
|
||||
}
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUICustomerServiceSatisfactionEvaluation);
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction
|
||||
{
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
self.showAvatar = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Override
|
||||
- (BOOL)canForward {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)canLongPress {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)itemDict {
|
||||
if (!_itemDict) {
|
||||
_itemDict = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return _itemDict;
|
||||
}
|
||||
|
||||
- (BOOL)isExpired {
|
||||
NSTimeInterval cur = [[NSDate date] timeIntervalSince1970];
|
||||
return cur > self.expireTime;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// TUICustomerServicePluginInvisibleCell.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCell.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginInvisibleCell : TUIBubbleMessageCell
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUICustomerServicePluginInvisibleCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginInvisibleCell.h"
|
||||
|
||||
@implementation TUICustomerServicePluginInvisibleCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// TUICustomerServicePluginInvisibleCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginInvisibleCellData : TUIBubbleMessageCellData
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// TUICustomerServicePluginInvisibleCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginInvisibleCellData.h"
|
||||
#import "TUICustomerServicePluginPrivateConfig.h"
|
||||
|
||||
@implementation TUICustomerServicePluginInvisibleCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
TUICustomerServicePluginInvisibleCellData *cellData = [[TUICustomerServicePluginInvisibleCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.innerMessage = message;
|
||||
|
||||
if ([param[@"src"] isEqualToString: BussinessID_Src_CustomerService_EvaluationRule]) {
|
||||
NSDictionary *content = param[@"content"];
|
||||
NSInteger menuSendRuleFlag = [content[@"menuSendRuleFlag"] integerValue];
|
||||
[TUICustomerServicePluginPrivateConfig sharedInstance].canEvaluate = menuSendRuleFlag >> 2;
|
||||
}
|
||||
return cellData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data
|
||||
options:NSJSONReadingAllowFragments error:nil];
|
||||
if (param == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([param[@"src"] isEqualToString: BussinessID_Src_CustomerService_Timeout]) {
|
||||
return TIMCommonLocalizableString(TUICustomerServiceTimeout);
|
||||
} else if ([param[@"src"] isEqualToString: BussinessID_Src_CustomerService_End]) {
|
||||
return TIMCommonLocalizableString(TUICustomerServiceEnd);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Override
|
||||
- (BOOL)shouldHide {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TUICustomerServicePluginTypingCellData.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/16.
|
||||
//
|
||||
|
||||
#import <TUIChat/TUIChat.h>
|
||||
#import <TUIChat/TUITypingStatusCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginTypingCellData : TUITypingStatusCellData
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// TUICustomerServicePluginTypingCellData.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/16.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginTypingCellData.h"
|
||||
|
||||
@implementation TUICustomerServicePluginTypingCellData
|
||||
|
||||
+ (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[@"src"] isEqualToString: BussinessID_Src_CustomerService_Typing]) {
|
||||
cellData.typingStatus = 1;
|
||||
}
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// TUICustomerServicePluginDataProvider+CalculateSize.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/13.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define TUICustomerServicePluginBranchCellWidth (0.65 * Screen_Width)
|
||||
#define TUICustomerServicePluginBranchCellMargin 12
|
||||
#define TUICustomerServicePluginBranchCellInnerMargin 8
|
||||
#define TUICustomerServicePluginInputCellWidth (0.69 * Screen_Width)
|
||||
#define TUICustomerServicePluginEvaluationBubbleWidth (0.67 * Screen_Width)
|
||||
#define TUICustomerServicePluginCardBubbleWidth (0.65 * Screen_Width)
|
||||
|
||||
#define TUIBotBranchCellWidth (0.65 * Screen_Width)
|
||||
#define TUIBotBranchCellMargin 12
|
||||
#define TUIBotBranchCellInnerMargin 8
|
||||
|
||||
@interface TUICustomerServicePluginDataProvider (CalculateSize)
|
||||
|
||||
+ (CGSize)calcBranchCellSize:(NSString *)header items:(NSArray *)items;
|
||||
+ (CGSize)calcBranchCellSizeOfHeader:(NSString *)header;
|
||||
+ (CGSize)calcBranchCellSizeOfTableView:(NSArray *)items;
|
||||
+ (CGFloat)calcBranchCellHeightOfTableView:(NSArray *)items row:(NSInteger)row;
|
||||
+ (CGFloat)calcBranchCellHeightOfContent:(NSString *)content;
|
||||
|
||||
+ (CGSize)calcCollectionCellSize:(NSString *)header items:(NSArray *)items;
|
||||
+ (CGSize)calcCollectionCellSizeOfHeader:(NSString *)header;
|
||||
+ (CGSize)calcCollectionCellSizeOfTableView:(NSArray *)items;
|
||||
+ (CGFloat)calcCollectionCellHeightOfTableView:(NSArray *)items row:(NSInteger)row;
|
||||
+ (CGFloat)calcCollectionCellHeightOfContent:(NSString *)content;
|
||||
|
||||
+ (CGSize)calcCollectionInputCellSize:(NSString *)header;
|
||||
+ (CGSize)calcCollectionInputCellSizeOfHeader:(NSString *)header;
|
||||
|
||||
+ (CGSize)calcEvaluationCellSize:(NSString *)header
|
||||
tail:(NSString *)tail
|
||||
score:(NSInteger)score
|
||||
selected:(BOOL)selected;
|
||||
+ (CGSize)calcEvaluationBubbleSize:(NSString *)header score:(NSInteger)score;
|
||||
+ (CGSize)calcEvaluationBubbleHeaderSize:(NSString *)header;
|
||||
+ (CGSize)calcEvaluationBubbleScoreSize:(NSInteger)score;
|
||||
+ (CGSize)calcEvaluationBubbleTailSize:(NSString *)tail;
|
||||
|
||||
+ (CGSize)calcCardHeaderSize:(NSString *)header;
|
||||
|
||||
+ (CGSize)calcMenuCellSize:(NSString *)title;
|
||||
+ (CGSize)calcMenuCellButtonSize:(NSString *)title;
|
||||
|
||||
+ (CGSize)calcBotBranchCellSize:(NSString *)header items:(NSArray *)items;
|
||||
+ (CGSize)calcBotBranchCellSizeOfHeader:(NSString *)header;
|
||||
+ (CGSize)calcBotBranchCellSizeOfTableView:(NSArray *)items;
|
||||
+ (CGFloat)calcBotBranchCellHeightOfTableView:(NSArray *)items row:(NSInteger)row;
|
||||
+ (CGFloat)calcBotBranchCellHeightOfContent:(NSString *)content;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// TUICustomerServicePluginDataProvider+CalculateSize.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/13.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
#import <TUICore/TUIDefine.h>
|
||||
|
||||
@implementation TUICustomerServicePluginDataProvider (CalculateSize)
|
||||
|
||||
#pragma mark - Branch Cell
|
||||
+ (CGSize)calcBranchCellSize:(NSString *)header items:(NSArray *)items {
|
||||
float topBottomMargin = TUICustomerServicePluginBranchCellMargin;
|
||||
float marginBetweenHeaderAndTableView = TUICustomerServicePluginBranchCellInnerMargin;
|
||||
float headerHeight = [TUICustomerServicePluginDataProvider calcBranchCellSizeOfHeader:header].height;
|
||||
float tableViewHeight = [TUICustomerServicePluginDataProvider calcBranchCellSizeOfTableView:items].height;
|
||||
return CGSizeMake(TUICustomerServicePluginBranchCellWidth,
|
||||
headerHeight + tableViewHeight + topBottomMargin * 2 + marginBetweenHeaderAndTableView);
|
||||
}
|
||||
|
||||
+ (CGSize)calcBranchCellSizeOfHeader:(NSString *)header {
|
||||
float leftRightMargin = TUICustomerServicePluginBranchCellMargin;
|
||||
CGRect rect = [header boundingRectWithSize:CGSizeMake(TUICustomerServicePluginBranchCellWidth - leftRightMargin * 2, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:16] }
|
||||
context:nil];
|
||||
return CGSizeMake(ceilf(rect.size.width), ceilf(rect.size.height));
|
||||
}
|
||||
|
||||
+ (CGSize)calcBranchCellSizeOfTableView:(NSArray *)items {
|
||||
CGFloat height = 0;
|
||||
for (int i = 0; i < items.count; i++) {
|
||||
height += [self calcBranchCellHeightOfTableView:items row:i];
|
||||
}
|
||||
return CGSizeMake(TUICustomerServicePluginBranchCellWidth, height);
|
||||
}
|
||||
|
||||
+ (CGFloat)calcBranchCellHeightOfTableView:(NSArray *)items row:(NSInteger)row {
|
||||
if (row < 0 || row >= items.count) {
|
||||
return 0;
|
||||
}
|
||||
NSString *content = items[row];
|
||||
return [self calcBranchCellHeightOfContent:content];
|
||||
}
|
||||
|
||||
+ (CGFloat)calcBranchCellHeightOfContent:(NSString *)content {
|
||||
float width = TUICustomerServicePluginBranchCellWidth - TUICustomerServicePluginBranchCellMargin * 2 - 5 - 6;
|
||||
CGRect rect = [content boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:16] }
|
||||
context:nil];
|
||||
return MAX(rect.size.height + 16, 36);
|
||||
}
|
||||
|
||||
#pragma mark - Collection Cell
|
||||
+ (CGSize)calcCollectionCellSize:(NSString *)header items:(NSArray *)items {
|
||||
float topBottomMargin = TUICustomerServicePluginBranchCellMargin;
|
||||
float marginBetweenHeaderAndTableView = TUICustomerServicePluginBranchCellInnerMargin;
|
||||
float headerHeight = [TUICustomerServicePluginDataProvider calcCollectionCellSizeOfHeader:header].height;
|
||||
float tableViewHeight = [TUICustomerServicePluginDataProvider calcCollectionCellSizeOfTableView:items].height;
|
||||
return CGSizeMake(TUICustomerServicePluginBranchCellWidth,
|
||||
headerHeight + tableViewHeight + topBottomMargin * 2 + marginBetweenHeaderAndTableView);
|
||||
}
|
||||
|
||||
+ (CGSize)calcCollectionCellSizeOfHeader:(NSString *)header {
|
||||
float leftRightMargin = TUICustomerServicePluginBranchCellMargin;
|
||||
float width = TUICustomerServicePluginBranchCellWidth - leftRightMargin * 2;
|
||||
CGRect rect = [header boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:16] }
|
||||
context:nil];
|
||||
return CGSizeMake(width, rect.size.height);
|
||||
}
|
||||
|
||||
+ (CGSize)calcCollectionCellSizeOfTableView:(NSArray *)items {
|
||||
CGFloat height = 0;
|
||||
for (int i = 0; i < items.count; i++) {
|
||||
height += [self calcCollectionCellHeightOfTableView:items row:i];
|
||||
}
|
||||
return CGSizeMake(TUICustomerServicePluginBranchCellWidth, height);
|
||||
}
|
||||
|
||||
+ (CGFloat)calcCollectionCellHeightOfTableView:(NSArray *)items row:(NSInteger)row {
|
||||
if (row < 0 || row >= items.count) {
|
||||
return 0;
|
||||
}
|
||||
NSString *content = items[row];
|
||||
return [self calcBranchCellHeightOfContent:content];
|
||||
}
|
||||
|
||||
+ (CGFloat)calcCollectionCellHeightOfContent:(NSString *)content {
|
||||
float width = TUICustomerServicePluginBranchCellWidth - TUICustomerServicePluginBranchCellMargin * 2;
|
||||
CGRect rect = [content boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:16] }
|
||||
context:nil];
|
||||
return MAX(rect.size.height + 16, 36);
|
||||
}
|
||||
|
||||
#pragma mark - Collection input cell
|
||||
+ (CGSize)calcCollectionInputCellSize:(NSString *)header {
|
||||
CGFloat headerHeight = [self calcCollectionInputCellSizeOfHeader:header].height;
|
||||
return CGSizeMake(TUICustomerServicePluginInputCellWidth, headerHeight + 20 + 6 + 36);
|
||||
}
|
||||
|
||||
+ (CGSize)calcCollectionInputCellSizeOfHeader:(NSString *)header {
|
||||
float width = TUICustomerServicePluginInputCellWidth - TUICustomerServicePluginBranchCellMargin * 2;
|
||||
CGRect rect = [header boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:16] }
|
||||
context:nil];
|
||||
return CGSizeMake(width, MAX(rect.size.height, 22));
|
||||
}
|
||||
|
||||
#pragma mark - Evaluation cell
|
||||
+ (CGSize)calcEvaluationCellSize:(NSString *)header
|
||||
tail:(NSString *)tail
|
||||
score:(NSInteger)score
|
||||
selected:(BOOL)selected {
|
||||
float topLabelHeight = 20 + 20;
|
||||
float bubbleHeight = [self calcEvaluationBubbleSize:header score:score].height;
|
||||
float bottomHeight = 20;
|
||||
if (selected) {
|
||||
float height = [self calcEvaluationBubbleTailSize:tail].height;
|
||||
bottomHeight += height;
|
||||
}
|
||||
|
||||
return CGSizeMake(Screen_Width, topLabelHeight + bubbleHeight + bottomHeight);
|
||||
}
|
||||
|
||||
+ (CGSize)calcEvaluationBubbleSize:(NSString *)header score:(NSInteger)score {
|
||||
float width = TUICustomerServicePluginEvaluationBubbleWidth - TUICustomerServicePluginBranchCellMargin * 2;
|
||||
float headerHeight = [self calcEvaluationBubbleHeaderSize:header].height;
|
||||
float scoreHeight = [self calcEvaluationBubbleScoreSize:score].height;
|
||||
return CGSizeMake(width, 20 + headerHeight + 12 + scoreHeight + 12 + 30 + 20);
|
||||
}
|
||||
|
||||
+ (CGSize)calcEvaluationBubbleHeaderSize:(NSString *)header {
|
||||
float width = TUICustomerServicePluginEvaluationBubbleWidth - TUICustomerServicePluginBranchCellMargin * 2;
|
||||
CGRect rect = [header boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:12] }
|
||||
context:nil];
|
||||
return rect.size;
|
||||
}
|
||||
|
||||
+ (CGSize)calcEvaluationBubbleScoreSize:(NSInteger)score {
|
||||
float width = TUICustomerServicePluginEvaluationBubbleWidth - TUICustomerServicePluginBranchCellMargin * 2;
|
||||
return CGSizeMake(width, score <= 5 ? 24 : 24 * 2 + 15);
|
||||
}
|
||||
|
||||
+ (CGSize)calcEvaluationBubbleTailSize:(NSString *)tail {
|
||||
float width = Screen_Width - 52 * 2;
|
||||
CGRect rect = [tail boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:12] }
|
||||
context:nil];
|
||||
return CGSizeMake(width, rect.size.height + 20);
|
||||
}
|
||||
|
||||
#pragma mark - Card cell
|
||||
+ (CGSize)calcCardHeaderSize:(NSString *)header {
|
||||
float width = TUICustomerServicePluginCardBubbleWidth - 120;
|
||||
CGRect rect = [header boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:12] }
|
||||
context:nil];
|
||||
return CGSizeMake(width, rect.size.height);
|
||||
}
|
||||
|
||||
#pragma mark - Menu button
|
||||
+ (CGSize)calcMenuCellSize:(NSString *)title {
|
||||
CGSize size = [self calcMenuCellButtonSize:title];
|
||||
return CGSizeMake(size.width + 12, size.height + 8 + 6);
|
||||
}
|
||||
|
||||
+ (CGSize)calcMenuCellButtonSize:(NSString *)title {
|
||||
CGFloat margin = 28;
|
||||
CGRect rect = [title boundingRectWithSize:CGSizeMake(MAXFLOAT, 32)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:14] }
|
||||
context:nil];
|
||||
return CGSizeMake(rect.size.width + margin, 32);
|
||||
}
|
||||
|
||||
#pragma mark -Bot
|
||||
+ (CGSize)calcBotBranchCellSize:(NSString *)header items:(NSArray *)items {
|
||||
float topBottomMargin = TUIBotBranchCellMargin;
|
||||
float marginBetweenHeaderAndTableView = TUIBotBranchCellInnerMargin;
|
||||
float headerHeight = [TUICustomerServicePluginDataProvider calcBranchCellSizeOfHeader:header].height;
|
||||
float tableViewHeight = [TUICustomerServicePluginDataProvider calcBranchCellSizeOfTableView:items].height;
|
||||
return CGSizeMake(TUIBotBranchCellWidth,
|
||||
headerHeight + tableViewHeight + topBottomMargin * 2 + marginBetweenHeaderAndTableView);
|
||||
}
|
||||
|
||||
+ (CGSize)calcBotBranchCellSizeOfHeader:(NSString *)header {
|
||||
float leftRightMargin = TUIBotBranchCellMargin;
|
||||
CGRect rect = [header boundingRectWithSize:CGSizeMake(TUIBotBranchCellWidth - leftRightMargin * 2, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:16] }
|
||||
context:nil];
|
||||
return CGSizeMake(TUIBotBranchCellWidth, rect.size.height);
|
||||
}
|
||||
|
||||
+ (CGSize)calcBotBranchCellSizeOfTableView:(NSArray *)items {
|
||||
CGFloat height = 0;
|
||||
for (int i = 0; i < items.count; i++) {
|
||||
height += [self calcBranchCellHeightOfTableView:items row:i];
|
||||
}
|
||||
return CGSizeMake(TUIBotBranchCellWidth, height);
|
||||
}
|
||||
|
||||
+ (CGFloat)calcBotBranchCellHeightOfTableView:(NSArray *)items row:(NSInteger)row {
|
||||
if (row < 0 || row >= items.count) {
|
||||
return 0;
|
||||
}
|
||||
NSString *content = items[row];
|
||||
return [self calcBranchCellHeightOfContent:content];
|
||||
}
|
||||
|
||||
+ (CGFloat)calcBotBranchCellHeightOfContent:(NSString *)content {
|
||||
float width = TUIBotBranchCellWidth - TUIBotBranchCellMargin * 2 - 5 - 6;
|
||||
CGRect rect = [content boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:16] }
|
||||
context:nil];
|
||||
return MAX(rect.size.height + 16, 36);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// TUICustomerServicePluginDataProvider.h
|
||||
// Masonry
|
||||
//
|
||||
// Created by xia on 2023/6/12.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginDataProvider : NSObject
|
||||
|
||||
+ (void)sendTextMessage:(NSString *)text;
|
||||
|
||||
+ (void)sendCustomMessage:(NSData *)data;
|
||||
+ (void)sendCustomMessageWithoutUpdateUI:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// TUICustomerServicePluginDataProvider.m
|
||||
// Masonry
|
||||
//
|
||||
// Created by xia on 2023/6/12.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <ImSDK_Plus/ImSDK_Plus.h>
|
||||
|
||||
@implementation TUICustomerServicePluginDataProvider
|
||||
|
||||
+ (void)sendTextMessage:(NSString *)text {
|
||||
V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:text];
|
||||
if (message == nil) {
|
||||
return;
|
||||
}
|
||||
NSDictionary *param = @{TUICore_TUIChatService_SendMessageMethod_MsgKey: message};
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_SendMessageMethod
|
||||
param:param];
|
||||
}
|
||||
|
||||
+ (void)sendCustomMessage:(NSData *)data {
|
||||
V2TIMMessage *message = [[V2TIMManager sharedInstance] createCustomMessage:[self supplyCustomerServiceID:data]];
|
||||
if (message == nil) {
|
||||
return;
|
||||
}
|
||||
NSDictionary *param = @{TUICore_TUIChatService_SendMessageMethod_MsgKey: message};
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_SendMessageMethod
|
||||
param:param];
|
||||
}
|
||||
|
||||
+ (void)sendCustomMessageWithoutUpdateUI:(NSData *)data {
|
||||
V2TIMMessage *message = [[V2TIMManager sharedInstance] createCustomMessage:[self supplyCustomerServiceID:data]];
|
||||
if (message == nil) {
|
||||
return;
|
||||
}
|
||||
NSDictionary *param = @{TUICore_TUIChatService_SendMessageMethodWithoutUpdateUI_MsgKey: message};
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_SendMessageMethodWithoutUpdateUI
|
||||
param:param];
|
||||
}
|
||||
|
||||
+ (NSData *)supplyCustomerServiceID:(NSData *)data {
|
||||
NSDictionary *dic = [TUITool jsonData2Dictionary:data];
|
||||
if (!dic || 0 == dic.allKeys.count || [dic objectForKey:BussinessID_CustomerService]) {
|
||||
return data;
|
||||
}
|
||||
NSMutableDictionary *param = [NSMutableDictionary dictionaryWithDictionary:dic];
|
||||
[param setObject:@(0) forKey:BussinessID_CustomerService];
|
||||
return [TUITool dictionary2JsonData:param];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// TUICustomerServicePluginConfig.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/16.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TUICustomerServicePluginConfig;
|
||||
@class TUICustomerServicePluginMenuCellData;
|
||||
@class TUICustomerServicePluginProductInfo;
|
||||
|
||||
@protocol TUICustomerServicePluginConfigDataSource <NSObject>
|
||||
|
||||
@optional
|
||||
/**
|
||||
* 自定义修改浮层菜单的数据源。请在进入客服会话聊天界面前实现。
|
||||
* Customize or modify the data source of the floating menu. Please implement this method before entering the customer service conversation chat page.
|
||||
*/
|
||||
- (NSArray<TUICustomerServicePluginMenuCellData *> *)pluginConfig:(TUICustomerServicePluginConfig *)config shouldUpdateOldMenuItems:(NSArray *)oldItems;
|
||||
|
||||
/**
|
||||
* 自定义常用语。请在进入客服会话聊天界面前实现。
|
||||
* Customize or modify the common phrases. Please implement this method before entering the customer service conversation chat page.
|
||||
*/
|
||||
- (NSArray<NSString *> *)pluginConfig:(TUICustomerServicePluginConfig *)config shouldUpdateCommonPhrases:(NSArray *)oldItems;
|
||||
|
||||
/**
|
||||
* 自定义商品信息。请在进入客服会话聊天界面前实现。
|
||||
* Customize or modify the product infomation. Please implement this method before entering the customer service conversation chat page.
|
||||
*/
|
||||
- (TUICustomerServicePluginProductInfo *)pluginConfigShouldUpdateProductInfo:(TUICustomerServicePluginConfig *)config;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUICustomerServicePluginConfig : NSObject
|
||||
|
||||
+ (TUICustomerServicePluginConfig *)sharedInstance;
|
||||
|
||||
@property (nonatomic, weak) id<TUICustomerServicePluginConfigDataSource> delegate;
|
||||
|
||||
/**
|
||||
* Set up customer service account list
|
||||
*/
|
||||
@property (nonatomic, copy) NSArray *customerServiceAccounts;
|
||||
|
||||
/**
|
||||
* 客服插件在消息列表底部的菜单浮层的数据源。
|
||||
* The data source of the customer service plugin in the floating layer of the menu at the bottom of the message list.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSArray *menuItems;
|
||||
|
||||
/**
|
||||
* 常用语
|
||||
* The common phrases.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSArray *commonPhrases;
|
||||
|
||||
/**
|
||||
* 商品信息
|
||||
* The product info.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) TUICustomerServicePluginProductInfo *productInfo;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// TUICustomerServicePluginConfig.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/16.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginConfig.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import "TUICustomerServicePluginMenuView.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginExtensionObserver.h"
|
||||
#import "TUICustomerServicePluginPrivateConfig.h"
|
||||
#import "TUICustomerServicePluginProductInfo.h"
|
||||
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
|
||||
@implementation TUICustomerServicePluginConfig
|
||||
|
||||
+ (TUICustomerServicePluginConfig *)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static TUICustomerServicePluginConfig * g_sharedInstance = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
g_sharedInstance = [[TUICustomerServicePluginConfig alloc] init];
|
||||
|
||||
});
|
||||
return g_sharedInstance;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
- (void)setCustomerServiceAccounts:(NSArray *)customerServiceAccounts {
|
||||
[TUICustomerServicePluginPrivateConfig sharedInstance].customerServiceAccounts = customerServiceAccounts;
|
||||
}
|
||||
|
||||
- (NSArray *)menuItems {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(pluginConfig:shouldUpdateOldMenuItems:)]) {
|
||||
return [self.delegate pluginConfig:self shouldUpdateOldMenuItems:[self defaultMenuItems]];
|
||||
}
|
||||
return [self defaultMenuItems];
|
||||
}
|
||||
|
||||
- (NSArray *)commonPhrases {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(pluginConfig:shouldUpdateCommonPhrases:)]) {
|
||||
return [self.delegate pluginConfig:self shouldUpdateCommonPhrases:[self defaultCommonPhrases]];
|
||||
}
|
||||
return [self defaultCommonPhrases];
|
||||
}
|
||||
|
||||
- (TUICustomerServicePluginProductInfo *)productInfo {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(pluginConfigShouldUpdateProductInfo:)]) {
|
||||
return [self.delegate pluginConfigShouldUpdateProductInfo:self];
|
||||
}
|
||||
return [self defaultProductInfo];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
- (NSArray *)defaultMenuItems {
|
||||
NSMutableArray *dataSource = [NSMutableArray new];
|
||||
|
||||
TUICustomerServicePluginMenuCellData *product = [TUICustomerServicePluginMenuCellData new];
|
||||
product.title = TIMCommonLocalizableString(TUICustomerServiceSendProduct);
|
||||
product.target = TUICustomerServicePluginExtensionObserver.shareInstance;
|
||||
product.cselector = @selector(onProductClicked);
|
||||
[dataSource addObject:product];
|
||||
|
||||
TUICustomerServicePluginMenuCellData *phase = [TUICustomerServicePluginMenuCellData new];
|
||||
phase.title = TIMCommonLocalizableString(TUICustomerServiceCommonPhrase);
|
||||
phase.target = TUICustomerServicePluginExtensionObserver.shareInstance;
|
||||
phase.cselector = @selector(onPhraseClicked);
|
||||
[dataSource addObject:phase];
|
||||
|
||||
return [dataSource copy];
|
||||
}
|
||||
|
||||
- (NSArray *)defaultCommonPhrases {
|
||||
return @[
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseStock),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseCheaper),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseGift),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseShipping),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseDelivery),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseArrive),
|
||||
];
|
||||
}
|
||||
|
||||
- (TUICustomerServicePluginProductInfo *)defaultProductInfo {
|
||||
TUICustomerServicePluginProductInfo *info = [TUICustomerServicePluginProductInfo new];
|
||||
info.title = @"手工编织皮革提包2023新品女士迷你简约大方高端有档次";
|
||||
info.desc = @"¥788";
|
||||
info.picURL = @"https://qcloudimg.tencent-cloud.cn/raw/a811f634eab5023f973c9b224bc07a51.png";
|
||||
info.linkURL = @"https://cloud.tencent.com/document/product/269";
|
||||
return info;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// TUICustomerServicePluginExtensionObserver.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/12.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginExtensionObserver : NSObject
|
||||
|
||||
+ (instancetype)shareInstance;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,241 @@
|
||||
//
|
||||
// TUICustomerServicePluginExtensionObserver.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/12.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginExtensionObserver.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginCardInputView.h"
|
||||
#import "TUICustomerServicePluginConfig.h"
|
||||
#import "TUICustomerServicePluginPrivateConfig.h"
|
||||
#import "TUICustomerServicePluginAccountController.h"
|
||||
#import "TUICustomerServicePluginMenuView.h"
|
||||
#import "TUICustomerServicePluginPhraseView.h"
|
||||
#import <TUIChat/TUIBaseChatViewController.h>
|
||||
#import "TUICustomerServicePluginProductInfo.h"
|
||||
#import "TUICustomerServicePluginUserController.h"
|
||||
|
||||
@interface TUICustomerServicePluginExtensionObserver () <TUIExtensionProtocol>
|
||||
|
||||
@property (nonatomic, weak) TUIBaseChatViewController *superVC;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginExtensionObserver
|
||||
|
||||
static id _instance = nil;
|
||||
+ (void)load {
|
||||
[TUICore registerExtension:TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];
|
||||
[TUICore registerExtension:TUICore_TUIContactExtension_ContactMenu_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];
|
||||
[TUICore registerExtension:TUICore_TUIChatExtension_ChatVCBottomContainer_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];
|
||||
[TUICore registerExtension:TUICore_TUIChatExtension_NavigationMoreItem_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];
|
||||
[TUICore registerExtension:TUICore_TUIChatExtension_ClickAvatar_ClassicExtensionID object:TUICustomerServicePluginExtensionObserver.shareInstance];
|
||||
}
|
||||
|
||||
+ (instancetype)shareInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_instance = [[self alloc] init];
|
||||
});
|
||||
return _instance;
|
||||
}
|
||||
|
||||
#pragma mark - TUIExtensionProtocol
|
||||
#pragma mark -- GetExtension
|
||||
- (NSArray<TUIExtensionInfo *> *)onGetExtension:(NSString *)extensionID param:(NSDictionary *)param {
|
||||
if (![extensionID isKindOfClass:NSString.class]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([extensionID isEqualToString:TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID]) {
|
||||
return [self getInputViewMoreItemExtensionForClassicChat:param];
|
||||
} else if ([extensionID isEqualToString:TUICore_TUIChatExtension_InputViewMoreItem_MinimalistExtensionID]) {
|
||||
return [self getInputViewMoreItemExtensionForMinimalistChat:param];
|
||||
} else if ([extensionID isEqualToString:TUICore_TUIContactExtension_ContactMenu_ClassicExtensionID]) {
|
||||
return [self getContactMenuExtensionForClassicChat:param];
|
||||
} else if ([extensionID isEqualToString:TUICore_TUIContactExtension_ContactMenu_MinimalistExtensionID]) {
|
||||
return [self getContactMenuExtensionForMinimalistChat:param];
|
||||
} else if ([extensionID isEqualToString:TUICore_TUIChatExtension_NavigationMoreItem_ClassicExtensionID]) {
|
||||
return [self getNavigationMoreItemExtensionForClassicChat:param];
|
||||
} else if ([extensionID isEqualToString:TUICore_TUIChatExtension_ClickAvatar_ClassicExtensionID]) {
|
||||
return [self getClickAvtarExtensionForClassicChat:param];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
// InputViewMoreItem
|
||||
- (NSArray<TUIExtensionInfo *> *)getInputViewMoreItemExtensionForClassicChat:(NSDictionary *)param {
|
||||
if (![param isKindOfClass:NSDictionary.class]) {
|
||||
return nil;
|
||||
}
|
||||
NSString *userID = [param tui_objectForKey:TUICore_TUIChatExtension_InputViewMoreItem_UserID asClass:NSString.class];
|
||||
if (![TUICustomerServicePluginPrivateConfig.sharedInstance isCustomerServiceAccount:userID]) {
|
||||
return nil;
|
||||
}
|
||||
if (![TUICustomerServicePluginPrivateConfig sharedInstance].canEvaluate) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TUIExtensionInfo *evaluation = [[TUIExtensionInfo alloc] init];
|
||||
evaluation.weight = 100;
|
||||
evaluation.text = TIMCommonLocalizableString(TUIKitMoreEvaluation);
|
||||
evaluation.icon = TIMCommonBundleThemeImage(@"service_more_customer_service_evaluation_img", @"more_customer_service_evaluation");
|
||||
evaluation.onClicked = ^(NSDictionary *_Nonnull param) {
|
||||
NSData *data = [TUITool dictionary2JsonData:@{@"src": BussinessID_Src_CustomerService_EvaluationTrigger}];
|
||||
[TUICustomerServicePluginDataProvider sendCustomMessageWithoutUpdateUI:data];
|
||||
};
|
||||
return @[evaluation];
|
||||
}
|
||||
|
||||
- (NSArray<TUIExtensionInfo *> *)getInputViewMoreItemExtensionForMinimalistChat:(NSDictionary *)param {
|
||||
// todo: 精简版 chat 页面底部输入框扩展
|
||||
return nil;
|
||||
}
|
||||
|
||||
// ContactMenu
|
||||
- (NSArray<TUIExtensionInfo *> *)getContactMenuExtensionForClassicChat:(NSDictionary *)param {
|
||||
if (![param isKindOfClass:NSDictionary.class]) {
|
||||
return nil;
|
||||
}
|
||||
[TUICustomerServicePluginPrivateConfig checkCommercialAbility];
|
||||
|
||||
UINavigationController *nav = [param tui_objectForKey:TUICore_TUIContactExtension_ContactMenu_Nav asClass:UINavigationController.class];
|
||||
[TUITool addValueAddedUnsupportNeedContactNotificationInVC:nav debugOnly:YES];
|
||||
|
||||
TUIExtensionInfo *customerService = [[TUIExtensionInfo alloc] init];
|
||||
customerService.weight = 50;
|
||||
customerService.text = TIMCommonLocalizableString(TUICustomerServiceAccounts);
|
||||
customerService.icon = TUICustomerServicePluginBundleThemeImage(@"customer_service_contact_menu_icon_img", @"contact_customer_service");
|
||||
customerService.onClicked = ^(NSDictionary *_Nonnull param) {
|
||||
if (![TUICustomerServicePluginPrivateConfig isCustomerServiceSupported]) {
|
||||
[TUITool postValueAddedUnsupportNeedContactNotification:TIMCommonLocalizableString(TUICustomerService)];
|
||||
NSLog(@"TUICustomerService ability is not supported");
|
||||
return;
|
||||
}
|
||||
TUICustomerServicePluginAccountController *vc = [TUICustomerServicePluginAccountController new];
|
||||
[nav pushViewController:vc animated:YES];
|
||||
};
|
||||
return @[customerService];
|
||||
}
|
||||
|
||||
- (NSArray<TUIExtensionInfo *> *)getContactMenuExtensionForMinimalistChat:(NSDictionary *)param {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Navigation more item
|
||||
- (NSArray<TUIExtensionInfo *> *)getNavigationMoreItemExtensionForClassicChat:(NSDictionary *)param {
|
||||
if (![param isKindOfClass:NSDictionary.class]) {
|
||||
return nil;
|
||||
}
|
||||
NSString *userID = [param tui_objectForKey:TUICore_TUIChatExtension_NavigationMoreItem_UserID
|
||||
asClass:NSString.class];
|
||||
if (userID.length == 0 || ![TUICustomerServicePluginPrivateConfig.sharedInstance isCustomerServiceAccount:userID]) {
|
||||
return nil;
|
||||
}
|
||||
TUIExtensionInfo *info = [[TUIExtensionInfo alloc] init];
|
||||
info.icon = TIMCommonBundleThemeImage(@"chat_nav_more_menu_img", @"chat_nav_more_menu");
|
||||
info.weight = 200;
|
||||
info.onClicked = ^(NSDictionary *_Nonnull param) {
|
||||
UINavigationController *nav = [param tui_objectForKey:TUICore_TUIChatExtension_NavigationMoreItem_PushVC
|
||||
asClass:UINavigationController.class];
|
||||
if (nav) {
|
||||
[[V2TIMManager sharedInstance] getUsersInfo:@[userID]
|
||||
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
|
||||
TUICustomerServicePluginUserController *vc = [[TUICustomerServicePluginUserController alloc] initWithUserInfo:infoList.firstObject];
|
||||
[nav pushViewController:vc animated:YES];
|
||||
} fail:^(int code, NSString *desc) {
|
||||
|
||||
}];
|
||||
}
|
||||
};
|
||||
return @[info];
|
||||
}
|
||||
|
||||
// Customizing action when clicking avatar
|
||||
- (NSArray<TUIExtensionInfo *> *)getClickAvtarExtensionForClassicChat:(NSDictionary *)param {
|
||||
if (![param isKindOfClass:NSDictionary.class]) {
|
||||
return nil;
|
||||
}
|
||||
NSString *userID = [param tui_objectForKey:TUICore_TUIChatExtension_ClickAvatar_UserID
|
||||
asClass:NSString.class];
|
||||
if (userID.length == 0 || ![TUICustomerServicePluginPrivateConfig.sharedInstance isCustomerServiceAccount:userID]) {
|
||||
return nil;
|
||||
}
|
||||
TUIExtensionInfo *info = [[TUIExtensionInfo alloc] init];
|
||||
info.onClicked = ^(NSDictionary *_Nonnull param) {
|
||||
UINavigationController *nav = [param tui_objectForKey:TUICore_TUIChatExtension_ClickAvatar_PushVC
|
||||
asClass:UINavigationController.class];
|
||||
if (nav) {
|
||||
[[V2TIMManager sharedInstance] getUsersInfo:@[userID]
|
||||
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
|
||||
TUICustomerServicePluginUserController *vc = [[TUICustomerServicePluginUserController alloc] initWithUserInfo:infoList.firstObject];
|
||||
[nav pushViewController:vc animated:YES];
|
||||
} fail:^(int code, NSString *desc) {
|
||||
|
||||
}];
|
||||
}
|
||||
};
|
||||
return @[info];
|
||||
}
|
||||
|
||||
#pragma mark -- RaiseExtension
|
||||
- (BOOL)onRaiseExtension:(NSString *)extensionID parentView:(UIView *)parentView param:(nullable NSDictionary *)param {
|
||||
if ([extensionID isEqualToString:TUICore_TUIChatExtension_ChatVCBottomContainer_ClassicExtensionID]) {
|
||||
if (param == nil) {
|
||||
NSLog(@"TUIChat notify param is invalid");
|
||||
return NO;
|
||||
}
|
||||
NSString *userID = [param objectForKey:TUICore_TUIChatExtension_ChatVCBottomContainer_UserID];
|
||||
if (![TUICustomerServicePluginPrivateConfig.sharedInstance isOnlineShopping:userID]) {
|
||||
return NO;
|
||||
}
|
||||
if (![parentView isKindOfClass:UIView.class]) {
|
||||
return NO;
|
||||
}
|
||||
self.superVC = [param objectForKey:TUICore_TUIChatExtension_ChatVCBottomContainer_VC];
|
||||
TUICustomerServicePluginMenuView *view = [[TUICustomerServicePluginMenuView alloc] initWithDataSource:TUICustomerServicePluginConfig.sharedInstance.menuItems];
|
||||
[parentView addSubview:view];
|
||||
[view updateFrame];
|
||||
|
||||
[self notifyHeightChanged];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Menu Event reponse
|
||||
- (void)notifyHeightChanged {
|
||||
NSDictionary *param = @{TUICore_TUIPluginNotify_PluginViewDidAddToSuperviewSubKey_PluginViewHeight: @46};
|
||||
[TUICore notifyEvent:TUICore_TUIPluginNotify
|
||||
subKey:TUICore_TUIPluginNotify_PluginViewDidAddToSuperview
|
||||
object:nil
|
||||
param:param];
|
||||
}
|
||||
|
||||
- (void)onProductClicked {
|
||||
TUICustomerServicePluginProductInfo *info = TUICustomerServicePluginConfig.sharedInstance.productInfo;
|
||||
NSDictionary *dict = @{BussinessID_CustomerService: @0,
|
||||
@"src": BussinessID_Src_CustomerService_Card,
|
||||
@"content": @{@"header": info.title ?: @"",
|
||||
@"desc": info.desc ?: @"",
|
||||
@"pic": info.picURL ?: @"",
|
||||
@"url": info.linkURL ?: @""}
|
||||
};
|
||||
NSData *data = [TUITool dictionary2JsonData:dict];
|
||||
[TUICustomerServicePluginDataProvider sendCustomMessage:data];
|
||||
}
|
||||
|
||||
- (void)onPhraseClicked {
|
||||
[self.superVC.inputController reset];
|
||||
TUICustomerServicePluginPhraseView *view = [[TUICustomerServicePluginPhraseView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, Screen_Height)];
|
||||
UIWindow *window = [TUITool applicationKeywindow];
|
||||
[window addSubview:view];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// TUICustomerServicePluginService.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginService : NSObject
|
||||
|
||||
+ (TUICustomerServicePluginService *)sharedInstance;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,191 @@
|
||||
//
|
||||
// TUICustomerServicePluginService.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/5/30.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginService.h"
|
||||
#import <TUIChat/TUIChatConfig.h>
|
||||
#import <TUIChat/TUIChatConversationModel.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUILogin.h>
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import "TUICustomerServicePluginPrivateConfig.h"
|
||||
#import "TUICustomerServicePluginExtensionObserver.h"
|
||||
|
||||
@interface TUICustomerServicePluginService() <TUINotificationProtocol, TUIExtensionProtocol>
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginService
|
||||
|
||||
+ (void)load {
|
||||
NSLog(@"TUICustomerServicePluginService load");
|
||||
[TUICustomerServicePluginService sharedInstance];
|
||||
TUIRegisterThemeResourcePath(TUICustomerServicePluginThemePath, TUIThemeModuleCustomerService);
|
||||
}
|
||||
|
||||
+ (TUICustomerServicePluginService *)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static TUICustomerServicePluginService * g_sharedInstance = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
g_sharedInstance = [[TUICustomerServicePluginService alloc] init];
|
||||
});
|
||||
return g_sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
[self registerEvent];
|
||||
[self registerExtension];
|
||||
[self registerCustomMessageCell];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)registerEvent {
|
||||
[TUICore registerEvent:TUICore_TUIChatNotify
|
||||
subKey:TUICore_TUIChatNotify_ChatVC_ViewDidLoadSubKey
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)registerExtension {
|
||||
[TUICore registerExtension:TUICore_TUIChatExtension_GetChatConversationModelParams object:self];
|
||||
}
|
||||
|
||||
- (void)registerCustomMessageCell {
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Evaluation),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginEvaluationCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginEvaluationCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_EvaluationSelected),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginInvisibleCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginInvisibleCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Typing),
|
||||
TMessageCell_Name : @"TUIMessageCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginTypingCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Branch),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginBranchCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginBranchCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_End),
|
||||
TMessageCell_Name : @"TUIMessageCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginInvisibleCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Timeout),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginInvisibleCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginInvisibleCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_EvaluationRule),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginInvisibleCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginInvisibleCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_EvaluationTrigger),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginInvisibleCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginInvisibleCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Collection),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginCollectionCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginCollectionCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Card),
|
||||
TMessageCell_Name : @"TUICustomerServicePluginCardCell",
|
||||
TMessageCell_Data_Name : @"TUICustomerServicePluginCardCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Bot_Welcome_Clarify),
|
||||
TMessageCell_Name : @"TUIBotBranchCell",
|
||||
TMessageCell_Data_Name : @"TUIBotBranchCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Bot_Rich_Text),
|
||||
TMessageCell_Name : @"TUIBotRichTextCell",
|
||||
TMessageCell_Data_Name : @"TUIBotRichTextCellData"
|
||||
}
|
||||
];
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AppendCustomMessageMethod
|
||||
param:@{BussinessID : GetCustomerServiceBussinessID(BussinessID_Src_CustomerService_Bot_Stream_Text),
|
||||
TMessageCell_Name : @"TUIBotStreamTextCell",
|
||||
TMessageCell_Data_Name : @"TUIBotStreamTextCellData"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
#pragma mark - TUINotificationProtocol
|
||||
- (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(nullable id)anObject param:(nullable NSDictionary *)param {
|
||||
if ([key isEqualToString:TUICore_TUIChatNotify] &&
|
||||
[subKey isEqualToString:TUICore_TUIChatNotify_ChatVC_ViewDidLoadSubKey]) {
|
||||
if (param == nil) {
|
||||
NSLog(@"TUIChat notify param is invalid");
|
||||
return;
|
||||
}
|
||||
NSString *userID = [param objectForKey:TUICore_TUIChatNotify_ChatVC_ViewDidLoadSubKey_UserID];
|
||||
if (![TUICustomerServicePluginPrivateConfig.sharedInstance isCustomerServiceAccount:userID]) {
|
||||
return;
|
||||
}
|
||||
NSData *data = [TUITool dictionary2JsonData:@{@"src": BussinessID_Src_CustomerService_Request}];
|
||||
[TUICustomerServicePluginDataProvider sendCustomMessageWithoutUpdateUI:data];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - TUIExtensionProtocol
|
||||
- (nullable NSArray<TUIExtensionInfo *> *)onGetExtension:(NSString *)extensionID param:(nullable NSDictionary *)param {
|
||||
if ([extensionID isEqualToString:TUICore_TUIChatExtension_GetChatConversationModelParams]) {
|
||||
if (extensionID == nil) {
|
||||
NSLog(@"extensionID is invalid");
|
||||
return nil;
|
||||
}
|
||||
NSString *userID = [param objectForKey:TUICore_TUIChatExtension_GetChatConversationModelParams_UserID];
|
||||
if (!userID || ![TUICustomerServicePluginPrivateConfig.sharedInstance isCustomerServiceAccount:userID]) {
|
||||
return nil;
|
||||
}
|
||||
TUIExtensionInfo *extensionInfo = [[TUIExtensionInfo alloc] init];
|
||||
extensionInfo.data = @{TUICore_TUIChatExtension_GetChatConversationModelParams_MsgNeedReadReceipt : @(YES),
|
||||
TUICore_TUIChatExtension_GetChatConversationModelParams_EnableVideoCall : @(NO),
|
||||
TUICore_TUIChatExtension_GetChatConversationModelParams_EnableAudioCall : @(NO),
|
||||
TUICore_TUIChatExtension_GetChatConversationModelParams_EnableWelcomeCustomMessage : @(NO)};
|
||||
return @[extensionInfo];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// TUICustomerServicePluginAccountController.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/21.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginAccountController : UITableViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// TUICustomerServicePluginAccountController.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/21.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginAccountController.h"
|
||||
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "ReactiveObjC.h"
|
||||
#import <TUIContact/TUICommonContactCell.h>
|
||||
#import "TUICustomerServicePluginPrivateConfig.h"
|
||||
#import <TUIChat/TUIChatConversationModel.h>
|
||||
#import <TUIChat/TUIC2CChatViewController.h>
|
||||
|
||||
@interface TUICustomerServicePluginAccountController()
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *cellDatas;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginAccountController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#F2F3F5");
|
||||
|
||||
UILabel *titleLabel = [[UILabel alloc] init];
|
||||
titleLabel.text = TIMCommonLocalizableString(TUICustomerServiceAccounts);
|
||||
titleLabel.font = [UIFont boldSystemFontOfSize:17.0];
|
||||
titleLabel.textColor = TIMCommonDynamicColor(@"nav_title_text_color", @"#000000");
|
||||
[titleLabel sizeToFit];
|
||||
self.navigationItem.titleView = titleLabel;
|
||||
[self.tableView registerClass:[TUICommonContactCell class] forCellReuseIdentifier:@"CustomerServiceCell"];
|
||||
self.tableView.tableFooterView = [UIView new];
|
||||
self.tableView.backgroundColor = self.view.backgroundColor;
|
||||
self.tableView.rowHeight = 56;
|
||||
|
||||
[self getUserInfo];
|
||||
}
|
||||
|
||||
#pragma mark - Table view data source
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.cellDatas.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUICommonContactCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomerServiceCell"
|
||||
forIndexPath:indexPath];
|
||||
if (self.cellDatas.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
TUICommonContactCellData *data = self.cellDatas[indexPath.row];
|
||||
data.cselector = @selector(didSelectCustomerServiceAccount:);
|
||||
[cell fillWithData:data];
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 56;
|
||||
}
|
||||
|
||||
#pragma mark - Event response
|
||||
- (void)didSelectCustomerServiceAccount:(TUICommonContactCell *)cell {
|
||||
NSString *userID = cell.contactData.identifier;
|
||||
if (userID.length == 0) {
|
||||
return;
|
||||
}
|
||||
TUIChatConversationModel *conversationModel = [[TUIChatConversationModel alloc] init];
|
||||
conversationModel.userID = userID;
|
||||
conversationModel.conversationID = [NSString stringWithFormat:@"c2c_%@", userID];
|
||||
TUIC2CChatViewController *chatVC = [[TUIC2CChatViewController alloc] init];
|
||||
chatVC.conversationData = conversationModel;
|
||||
chatVC.title = conversationModel.title;
|
||||
[self.navigationController pushViewController:chatVC animated:YES];
|
||||
}
|
||||
|
||||
- (void)getUserInfo {
|
||||
NSArray *accounts = TUICustomerServicePluginPrivateConfig.sharedInstance.customerServiceAccounts;
|
||||
[[V2TIMManager sharedInstance] getUsersInfo:accounts
|
||||
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
|
||||
for (NSString *account in accounts) {
|
||||
for (V2TIMUserFullInfo *info in infoList) {
|
||||
if ([account isEqualToString:info.userID]) {
|
||||
TUICommonContactCellData *data = [TUICommonContactCellData new];
|
||||
data.identifier = info.userID;
|
||||
data.avatarUrl = [NSURL URLWithString:info.faceURL];
|
||||
data.title = (info.nickName.length > 0 ? info.nickName : info.userID);
|
||||
[self.cellDatas addObject:data];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
[self.tableView reloadData];
|
||||
} fail:^(int code, NSString *desc) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Getter
|
||||
- (NSMutableArray *)cellDatas {
|
||||
if (!_cellDatas) {
|
||||
_cellDatas = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return _cellDatas;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// TUICustomerServicePluginCardInputView.h
|
||||
// Masonry
|
||||
//
|
||||
// Created by xia on 2023/6/13.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginCardInputItemCell : UITableViewCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *descLabel;
|
||||
@property (nonatomic, strong) UITextField *inputTextField;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUICustomerServicePluginCardInputView : UIView
|
||||
|
||||
@property (nonatomic, strong) UIView *backView;
|
||||
@property (nonatomic, strong) UILabel *headerLabel;
|
||||
@property (nonatomic, strong) UITableView *itemsTableView;
|
||||
@property (nonatomic, strong) UIButton *submitButton;
|
||||
@property (nonatomic, strong) UIButton *closeButton;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,361 @@
|
||||
//
|
||||
// TUICustomerServicePluginCardInputView.m
|
||||
// Masonry
|
||||
//
|
||||
// Created by xia on 2023/6/13.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginCardInputView.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@interface TUICustomerServicePluginCardInputItemCell()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginCardInputItemCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.contentView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
_descLabel = [[UILabel alloc] init];
|
||||
_descLabel.font = [UIFont systemFontOfSize:14];
|
||||
_descLabel.numberOfLines = 1;
|
||||
_descLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_descLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_card_item_desc_text_color", @"#999999");
|
||||
[self.contentView addSubview:_descLabel];
|
||||
|
||||
_inputTextField = [[UITextField alloc] init];
|
||||
_inputTextField.backgroundColor = TUICustomerServicePluginDynamicColor(@"customer_service_card_input_bg_color", @"#F9F9F9");
|
||||
_inputTextField.layer.cornerRadius = 8;
|
||||
_inputTextField.layer.masksToBounds = YES;
|
||||
_inputTextField.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_card_input_text_color", @"#000000");
|
||||
_inputTextField.font = [UIFont systemFontOfSize:14];
|
||||
_inputTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
|
||||
[self.contentView addSubview:_inputTextField];
|
||||
|
||||
UIView *blankView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 16, 20)];
|
||||
blankView.backgroundColor = [UIColor clearColor];
|
||||
_inputTextField.leftViewMode = UITextFieldViewModeAlways;
|
||||
_inputTextField.leftView = blankView;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
[self.descLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(20);
|
||||
make.top.mas_equalTo(12);
|
||||
make.width.mas_equalTo(40);
|
||||
make.height.mas_equalTo(21);
|
||||
}];
|
||||
|
||||
[self.inputTextField mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(self.descLabel.mas_trailing).offset(8);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(self.mm_w - self.descLabel.mm_maxX - 20);
|
||||
make.height.mas_equalTo(46);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface TUICustomerServicePluginCardInputView()<UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property (nonatomic, strong) NSArray *defaultInfo;
|
||||
@property(nonatomic, assign) BOOL isKeyboardVisible;
|
||||
@property(nonatomic, assign) CGRect oldFrame;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginCardInputView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setupViews];
|
||||
[self addNotification];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupViews {
|
||||
UIColor *color = TUICustomerServicePluginDynamicColor(@"customer_service_card_bg_color", @"#000000");
|
||||
self.backgroundColor = [color colorWithAlphaComponent:0.5];
|
||||
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapped:)];
|
||||
[self addGestureRecognizer:tap];
|
||||
|
||||
[self addSubview:self.backView];
|
||||
|
||||
[self.backView addSubview:self.headerLabel];
|
||||
[self.backView addSubview:self.itemsTableView];
|
||||
[self.backView addSubview:self.submitButton];
|
||||
[self.backView addSubview:self.closeButton];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)addNotification {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)onTapped:(UITapGestureRecognizer *)recognizer {
|
||||
[self endEditing:YES];
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
[self.backView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(0);
|
||||
make.top.mas_equalTo(Screen_Height - 378);
|
||||
make.width.mas_equalTo(Screen_Width);
|
||||
make.height.mas_equalTo(378);
|
||||
}];
|
||||
|
||||
[self.headerLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(20);
|
||||
make.top.mas_equalTo(20);
|
||||
make.width.mas_equalTo(300);
|
||||
make.height.mas_equalTo(25);
|
||||
}];
|
||||
|
||||
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.trailing.mas_equalTo(20);
|
||||
make.top.mas_equalTo(22);
|
||||
make.width.mas_equalTo(50);
|
||||
make.height.mas_equalTo(24);
|
||||
}];
|
||||
|
||||
float tableViewHeight = self.defaultInfo.count * self.itemsTableView.rowHeight;
|
||||
[self.itemsTableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(0);
|
||||
make.top.mas_equalTo(self.headerLabel.mas_bottom).offset(20);
|
||||
make.width.mas_equalTo(self.backView.mas_width);
|
||||
make.height.mas_equalTo(tableViewHeight);
|
||||
}];
|
||||
|
||||
[self.submitButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(20);
|
||||
make.top.mas_equalTo(self.itemsTableView.mas_bottom).offset(20);
|
||||
make.width.mas_equalTo(self.backView.mas_width).offset(-20 * 2);
|
||||
make.height.mas_equalTo(46);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate & UITableViewDataSource
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.defaultInfo.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.defaultInfo.count <= indexPath.row) {
|
||||
return nil;
|
||||
}
|
||||
NSDictionary *info = self.defaultInfo[indexPath.row];
|
||||
TUICustomerServicePluginCardInputItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"item_cell" forIndexPath:indexPath];
|
||||
cell.descLabel.text = info[@"desc"];
|
||||
cell.inputTextField.placeholder = info[@"placeHolder"];
|
||||
|
||||
// TODO: debug 用,发布注意删掉
|
||||
if (info[@"content"]) {
|
||||
cell.inputTextField.text = info[@"content"];
|
||||
}
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - Keyboard
|
||||
- (void)keyboardWillShow:(NSNotification *)notification {
|
||||
if (self.isKeyboardVisible) {
|
||||
return;
|
||||
}
|
||||
self.oldFrame = self.backView.frame;
|
||||
|
||||
NSValue *keyBoardEndFrame = [[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey];
|
||||
CGRect endFrame = [keyBoardEndFrame CGRectValue];
|
||||
|
||||
[UIView animateWithDuration:0.25
|
||||
delay:0
|
||||
options:UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
[self setFrame:CGRectMake(self.frame.origin.x,
|
||||
self.frame.origin.y - endFrame.size.height,
|
||||
self.frame.size.width,
|
||||
self.frame.size.height)];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)keyboardDidShow:(NSNotification *)notification {
|
||||
self.isKeyboardVisible = YES;
|
||||
}
|
||||
|
||||
- (void)keyboardWillHide:(NSNotification *)notification {
|
||||
if (!self.isKeyboardVisible) {
|
||||
return;
|
||||
}
|
||||
[UIView animateWithDuration:0.25
|
||||
delay:0
|
||||
options:UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
[self setFrame:CGRectMake(0, 0, self.oldFrame.size.width, self.oldFrame.size.height)];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)keyboardDidHide:(NSNotification *)notification {
|
||||
self.isKeyboardVisible = NO;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Getter
|
||||
- (UIView *)backView {
|
||||
if (!_backView) {
|
||||
_backView = [[UIView alloc] init];
|
||||
_backView.backgroundColor = TUICustomerServicePluginDynamicColor(@"customer_service_card_backview_bg_color", @"#FFFFFF");
|
||||
}
|
||||
return _backView;
|
||||
}
|
||||
|
||||
- (UILabel *)headerLabel {
|
||||
if (!_headerLabel) {
|
||||
_headerLabel = [[UILabel alloc] init];
|
||||
_headerLabel.font = [UIFont systemFontOfSize:18];
|
||||
_headerLabel.numberOfLines = 0;
|
||||
_headerLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_headerLabel.text = TIMCommonLocalizableString(TUICustomerServiceFillProductInfo);
|
||||
_headerLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_card_header_text_color", @"#000000");
|
||||
}
|
||||
return _headerLabel;
|
||||
}
|
||||
|
||||
- (UITableView *)itemsTableView {
|
||||
if (!_itemsTableView) {
|
||||
_itemsTableView = [[UITableView alloc] init];
|
||||
_itemsTableView.tableFooterView = [[UIView alloc] init];
|
||||
// _itemsTableView.backgroundColor = TUICoreDynamicColor(@"customer_service_brance_bg_color", @"#FFFFFF");
|
||||
_itemsTableView.backgroundColor = [UIColor clearColor];
|
||||
_itemsTableView.delegate = self;
|
||||
_itemsTableView.dataSource = self;
|
||||
_itemsTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_itemsTableView.rowHeight = 56;
|
||||
[_itemsTableView registerClass:[TUICustomerServicePluginCardInputItemCell class] forCellReuseIdentifier:@"item_cell"];
|
||||
}
|
||||
return _itemsTableView;
|
||||
}
|
||||
|
||||
- (UIButton *)submitButton {
|
||||
if (!_submitButton) {
|
||||
_submitButton = [UIButton new];
|
||||
[_submitButton setTitle:TIMCommonLocalizableString(TUICustomerServiceSubmitProductInfo) forState:UIControlStateNormal];
|
||||
_submitButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
|
||||
_submitButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_submitButton setTitleColor:TUICustomerServicePluginDynamicColor(@"customer_service_card_submit_text_color", @"#FFFFFF")
|
||||
forState:UIControlStateNormal];
|
||||
_submitButton.layer.cornerRadius = 4;
|
||||
_submitButton.layer.masksToBounds = YES;
|
||||
[_submitButton setBackgroundColor:TUICustomerServicePluginDynamicColor(@"customer_service_card_submit_bg_color", @"#006EFF")];
|
||||
[_submitButton addTarget:self
|
||||
action:@selector(onSubmitButtonClicked:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _submitButton;
|
||||
}
|
||||
|
||||
- (void)onSubmitButtonClicked:(UIButton *)sender {
|
||||
NSString *header = [self getCellOfRow:0].inputTextField.text;
|
||||
NSString *desc = [self getCellOfRow:1].inputTextField.text;
|
||||
NSString *pic = [self getCellOfRow:2].inputTextField.text;
|
||||
NSString *url = [self getCellOfRow:3].inputTextField.text;
|
||||
|
||||
// TODO: 输入要做安全校验
|
||||
if (header.length == 0 || desc.length == 0 || pic.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *dict = @{@"src": BussinessID_Src_CustomerService_Card,
|
||||
@"content": @{@"header": header,
|
||||
@"desc": desc,
|
||||
@"pic": pic,
|
||||
@"url": url}
|
||||
};
|
||||
NSData *data = [TUITool dictionary2JsonData:dict];
|
||||
[TUICustomerServicePluginDataProvider sendCustomMessage:data];
|
||||
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
- (TUICustomerServicePluginCardInputItemCell *)getCellOfRow:(int)row {
|
||||
if (row >= self.defaultInfo.count) {
|
||||
return nil;
|
||||
}
|
||||
NSIndexPath *index = [NSIndexPath indexPathForRow:row inSection:0];
|
||||
return (TUICustomerServicePluginCardInputItemCell *)[self.itemsTableView cellForRowAtIndexPath:index];
|
||||
}
|
||||
|
||||
- (UIButton *)closeButton {
|
||||
if (!_closeButton) {
|
||||
_closeButton = [UIButton new];
|
||||
[_closeButton setTitle:TIMCommonLocalizableString(TUICustomerServiceClose) forState:UIControlStateNormal];
|
||||
_closeButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
|
||||
_closeButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_closeButton setTitleColor:TUICustomerServicePluginDynamicColor(@"customer_service_card_close_text_color", @"#3370FF")
|
||||
forState:UIControlStateNormal];
|
||||
[_closeButton addTarget:self
|
||||
action:@selector(onCloseButtonClicked:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _closeButton;
|
||||
}
|
||||
|
||||
- (void)onCloseButtonClicked:(UIButton *)sender {
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
- (NSArray *)defaultInfo {
|
||||
return @[
|
||||
@{@"desc": TIMCommonLocalizableString(TUICustomerServiceName),
|
||||
@"placeHolder": TIMCommonLocalizableString(TUICustomerServiceFillProductName),
|
||||
@"content": @"手工编织皮革提包2023新品女士迷你简约大方高端有档次"
|
||||
},
|
||||
@{@"desc": TIMCommonLocalizableString(TUICustomerServiceDesc),
|
||||
@"placeHolder": TIMCommonLocalizableString(TUICustomerServiceFillProductDesc),
|
||||
@"content": @"¥788"
|
||||
},
|
||||
@{@"desc": TIMCommonLocalizableString(TUICustomerServicePic),
|
||||
@"placeHolder": TIMCommonLocalizableString(TUICustomerServiceFillPicLink),
|
||||
@"content": @"https://qcloudimg.tencent-cloud.cn/raw/a811f634eab5023f973c9b224bc07a51.png"
|
||||
},
|
||||
@{@"desc": TIMCommonLocalizableString(TUICustomerServiceJumpLink),
|
||||
@"placeHolder": TIMCommonLocalizableString(TUICustomerServiceFillJumpLink),
|
||||
@"content": @"https://cloud.tencent.com/document/product/269"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// TUICustomerServicePluginMenuView.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/29.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginMenuCellData : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSString *title;
|
||||
@property (nonatomic, assign) SEL cselector;
|
||||
@property (nonatomic, strong) id target;
|
||||
|
||||
- (CGSize)calcSize;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUICustomerServicePluginMenuCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic, strong) UIButton *button;
|
||||
@property (nonatomic, strong) TUICustomerServicePluginMenuCellData *cellData;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUICustomerServicePluginMenuView : UIView
|
||||
|
||||
- (instancetype)initWithDataSource:(NSArray *)source;
|
||||
- (void)updateFrame;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// TUICustomerServicePluginMenuView.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/6/29.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginMenuView.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import "TUICustomerServicePluginDataProvider+CalculateSize.h"
|
||||
|
||||
@interface TUICustomerServicePluginMenuCellData()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginMenuCellData
|
||||
|
||||
- (CGSize)calcSize {
|
||||
return [TUICustomerServicePluginDataProvider calcMenuCellSize:self.title];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUICustomerServicePluginMenuCell()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginMenuCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.button = [UIButton new];
|
||||
self.button.layer.borderWidth = 1.0;
|
||||
self.button.layer.borderColor = TUICustomerServicePluginDynamicColor(@"customer_service_menu_button_border_color", @"#C5CBD4").CGColor;
|
||||
self.button.layer.cornerRadius = 16;
|
||||
self.button.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
[self.button setTitleColor:TUICustomerServicePluginDynamicColor(@"customer_service_menu_button_text_color", @"#8F959E") forState:UIControlStateNormal];
|
||||
self.button.backgroundColor = TUICustomerServicePluginDynamicColor(@"customer_service_menu_button_bg_color", @"#F6F7F9");
|
||||
[self addSubview:self.button];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUICustomerServicePluginMenuCellData *)cellData {
|
||||
self.cellData = cellData;
|
||||
[self.button setTitle:cellData.title forState:UIControlStateNormal];
|
||||
[self.button addTarget:cellData.target action:cellData.cselector forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
CGSize size = [TUICustomerServicePluginDataProvider calcMenuCellButtonSize:self.cellData.title];
|
||||
[self.button mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(12);
|
||||
make.top.mas_equalTo(8);
|
||||
make.width.mas_equalTo(size.width);
|
||||
make.height.mas_equalTo(size.height);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface TUICustomerServicePluginMenuView() <UICollectionViewDelegate, UICollectionViewDataSource>
|
||||
|
||||
@property (nonatomic, strong) UICollectionView *backView;
|
||||
@property (nonatomic, strong) NSMutableArray *dataSource;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginMenuView
|
||||
|
||||
- (instancetype)initWithDataSource:(NSArray *)source {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.dataSource = [source mutableCopy];
|
||||
[self setupViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupViews {
|
||||
self.backgroundColor = TUIChatDynamicColor(@"chat_input_controller_bg_color", @"#EBF0F6");
|
||||
[self addSubview:self.backView];
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
- (void)updateFrame {
|
||||
self.mm_left(0).mm_top(0).mm_width(Screen_Width).mm_height(46);
|
||||
self.backView.mm_fill();
|
||||
}
|
||||
|
||||
#pragma mark - Getter
|
||||
- (UICollectionView *)backView {
|
||||
if (!_backView) {
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
[layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
|
||||
_backView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:layout];
|
||||
_backView.delegate = self;
|
||||
_backView.dataSource = self;
|
||||
_backView.scrollEnabled = YES;
|
||||
_backView.backgroundColor = [UIColor clearColor];
|
||||
_backView.showsHorizontalScrollIndicator = NO;
|
||||
[_backView registerClass:[TUICustomerServicePluginMenuCell class] forCellWithReuseIdentifier:@"menuCell"];
|
||||
}
|
||||
return _backView;
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDataSource & Delegate
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return self.dataSource.count;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUICustomerServicePluginMenuCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"menuCell" forIndexPath:indexPath];
|
||||
TUICustomerServicePluginMenuCellData *cellData = self.dataSource[indexPath.row];
|
||||
[cell fillWithData:cellData];
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUICustomerServicePluginMenuCellData *cellData = self.dataSource[indexPath.row];
|
||||
return [cellData calcSize];
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
|
||||
return UIEdgeInsetsMake(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUICustomerServicePluginPhraseView.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/7/4.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUICustomerServicePluginPhraseView : UIView
|
||||
|
||||
@property (nonatomic, strong) UIView *backView;
|
||||
@property (nonatomic, strong) UITableView *itemsTableView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// TUICustomerServicePluginPhraseView.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/7/4.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginPhraseView.h"
|
||||
#import "TUICustomerServicePluginDataProvider.h"
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@interface TUICustomerServicePluginPhraseView() <UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginPhraseView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setupViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupViews {
|
||||
UIColor *color = TUICustomerServicePluginDynamicColor(@"customer_service_phrase_bg_color", @"#000000");
|
||||
self.backgroundColor = [color colorWithAlphaComponent:0.5];
|
||||
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapped:)];
|
||||
tap.delegate = self;
|
||||
[self addGestureRecognizer:tap];
|
||||
|
||||
[self addSubview:self.backView];
|
||||
[self.backView addSubview:self.itemsTableView];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)setupCorners {
|
||||
UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;
|
||||
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:self.backView.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(8, 8)];
|
||||
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
|
||||
layer.path = bezierPath.CGPath;
|
||||
self.backView.layer.mask = layer;
|
||||
}
|
||||
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
|
||||
float tableViewHeight = 5 * self.itemsTableView.rowHeight;
|
||||
[self.backView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(0);
|
||||
make.top.mas_equalTo(self.mas_bottom).offset(-tableViewHeight);
|
||||
make.width.mas_equalTo(self.mas_width);
|
||||
make.height.mas_equalTo(223);
|
||||
}];
|
||||
|
||||
self.itemsTableView.mm_fill();
|
||||
|
||||
[self setupCorners];
|
||||
}
|
||||
|
||||
- (void)onTapped:(UITapGestureRecognizer *)recognizer {
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
#pragma mark - UIGestureRecognizerDelegate
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
|
||||
if ([touch.view isDescendantOfView:self.itemsTableView]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate & UITableViewDataSource
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.defaultInfo.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.defaultInfo.count <= indexPath.row) {
|
||||
return nil;
|
||||
}
|
||||
NSString *phrase = self.defaultInfo[indexPath.row];
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"item_cell" forIndexPath:indexPath];
|
||||
cell.textLabel.text = phrase;
|
||||
cell.textLabel.font = [UIFont systemFontOfSize:14];
|
||||
cell.textLabel.textColor = TUICustomerServicePluginDynamicColor(@"customer_service_phrase_text_color", @"#000000");
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (self.defaultInfo.count <= indexPath.row) {
|
||||
return;
|
||||
}
|
||||
NSString *phase = self.defaultInfo[indexPath.row];
|
||||
[TUICustomerServicePluginDataProvider sendTextMessage:phase];
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
#pragma mark - Getter
|
||||
- (UIView *)backView {
|
||||
if (!_backView) {
|
||||
_backView = [[UIView alloc] init];
|
||||
_backView.backgroundColor = TUICustomerServicePluginDynamicColor(@"customer_service_phrase_backview_bg_color", @"#FFFFFF");
|
||||
}
|
||||
return _backView;
|
||||
}
|
||||
|
||||
- (UITableView *)itemsTableView {
|
||||
if (!_itemsTableView) {
|
||||
_itemsTableView = [[UITableView alloc] init];
|
||||
_itemsTableView.tableFooterView = [[UIView alloc] init];
|
||||
_itemsTableView.backgroundColor = [UIColor clearColor];
|
||||
_itemsTableView.delegate = self;
|
||||
_itemsTableView.dataSource = self;
|
||||
_itemsTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
|
||||
_itemsTableView.rowHeight = 44;
|
||||
_itemsTableView.bounces = NO;
|
||||
[_itemsTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"item_cell"];
|
||||
}
|
||||
return _itemsTableView;
|
||||
}
|
||||
|
||||
- (NSArray *)defaultInfo {
|
||||
return @[
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseStock),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseCheaper),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseGift),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseShipping),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseDelivery),
|
||||
TIMCommonLocalizableString(TUICustomerServiceCommonPhraseArrive),
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// TUICustomerServicePluginUserController.h
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/7/6.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class V2TIMUserInfo;
|
||||
|
||||
@interface TUICustomerServicePluginUserController : UITableViewController
|
||||
|
||||
- (instancetype)initWithUserInfo:(V2TIMUserInfo *)userInfo;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,238 @@
|
||||
//
|
||||
// TUICustomerServicePluginUserController.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by xia on 2023/7/6.
|
||||
//
|
||||
|
||||
#import "TUICustomerServicePluginUserController.h"
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
#import <TUIContact/TUICommonContactTextCell.h>
|
||||
#import <TUIContact/TUICommonContactSwitchCell.h>
|
||||
#import <TUIContact/TUICommonContactProfileCardCell.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
@interface TUICustomerServicePluginUserController ()
|
||||
|
||||
@property (nonatomic, copy) NSArray<NSArray *> *dataList;
|
||||
@property (nonatomic, strong) TUINaviBarIndicatorView *titleView;
|
||||
@property (nonatomic, strong) V2TIMUserInfo *userInfo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUICustomerServicePluginUserController
|
||||
|
||||
- (instancetype)initWithUserInfo:(V2TIMUserInfo *)userInfo {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.userInfo = userInfo;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self addLongPressGesture];
|
||||
|
||||
if (@available(iOS 15.0, *)) {
|
||||
self.tableView.sectionHeaderTopPadding = 0;
|
||||
}
|
||||
self.tableView.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#F2F3F5");
|
||||
[self.tableView registerClass:[TUICommonContactTextCell class] forCellReuseIdentifier:@"TextCell"];
|
||||
[self.tableView registerClass:[TUICommonContactSwitchCell class] forCellReuseIdentifier:@"SwitchCell"];
|
||||
[self.tableView registerClass:[TUICommonContactProfileCardCell class] forCellReuseIdentifier:@"CardCell"];
|
||||
self.tableView.delaysContentTouches = NO;
|
||||
|
||||
_titleView = [[TUINaviBarIndicatorView alloc] init];
|
||||
[_titleView setTitle:TIMCommonLocalizableString(ProfileDetails)];
|
||||
self.navigationItem.titleView = _titleView;
|
||||
self.navigationItem.title = @"";
|
||||
|
||||
[self loadData];
|
||||
}
|
||||
|
||||
- (void)loadData {
|
||||
NSMutableArray *list = @[].mutableCopy;
|
||||
[list addObject:({
|
||||
NSMutableArray *inlist = @[].mutableCopy;
|
||||
[inlist addObject:({
|
||||
TUICommonContactProfileCardCellData *personal = [[TUICommonContactProfileCardCellData alloc] init];
|
||||
personal.identifier = self.userInfo.userID;
|
||||
personal.avatarImage = DefaultAvatarImage;
|
||||
personal.avatarUrl = [NSURL URLWithString:self.userInfo.faceURL];
|
||||
personal.name = self.userInfo.nickName;
|
||||
personal.signature = TIMCommonLocalizableString(no_personal_signature);
|
||||
personal.reuseId = @"CardCell";
|
||||
personal.showSignature = YES;
|
||||
personal;
|
||||
})];
|
||||
inlist;
|
||||
})];
|
||||
|
||||
[list addObject:({
|
||||
NSMutableArray *inlist = @[].mutableCopy;
|
||||
[inlist addObject:({
|
||||
TUICommonContactSwitchCellData *data = TUICommonContactSwitchCellData.new;
|
||||
data.title = TIMCommonLocalizableString(ProfileMessageDoNotDisturb);
|
||||
data.cswitchSelector = @selector(onMessageDoNotDisturb:);
|
||||
data.reuseId = @"SwitchCell";
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[[V2TIMManager sharedInstance] getC2CReceiveMessageOpt:@[ self.userInfo.userID ]
|
||||
succ:^(NSArray<V2TIMUserReceiveMessageOptInfo *> *optList) {
|
||||
for (V2TIMReceiveMessageOptInfo *info in optList) {
|
||||
if ([info.userID isEqual:self.userInfo.userID]) {
|
||||
data.on = (info.receiveOpt == V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE);
|
||||
[weakSelf.tableView reloadData];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fail:nil];
|
||||
data;
|
||||
})];
|
||||
inlist;
|
||||
})];
|
||||
|
||||
[list addObject:({
|
||||
NSMutableArray *inlist = @[].mutableCopy;
|
||||
[inlist addObject:({
|
||||
TUICommonContactTextCellData *data = TUICommonContactTextCellData.new;
|
||||
data.key = TIMCommonLocalizableString(TUIKitClearAllChatHistory);
|
||||
data.showAccessory = YES;
|
||||
data.cselector = @selector(onClearHistoryChatMessage:);
|
||||
data.reuseId = @"TextCell";
|
||||
data;
|
||||
})];
|
||||
inlist;
|
||||
})];
|
||||
|
||||
self.dataList = list;
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)onClearHistoryChatMessage:(TUICommonContactTextCell *)cell {
|
||||
if (IS_NOT_EMPTY_NSSTRING(self.userInfo.userID)) {
|
||||
NSString *userID = self.userInfo.userID;
|
||||
@weakify(self);
|
||||
UIAlertController *ac = [UIAlertController alertControllerWithTitle:nil
|
||||
message:TIMCommonLocalizableString(TUIKitClearAllChatHistoryTips)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm)
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
@strongify(self);
|
||||
[V2TIMManager.sharedInstance clearC2CHistoryMessage:userID
|
||||
succ:^{
|
||||
[TUICore notifyEvent:TUICore_TUIConversationNotify
|
||||
subKey:TUICore_TUIConversationNotify_ClearConversationUIHistorySubKey
|
||||
object:self
|
||||
param:nil];
|
||||
[TUITool makeToast:@"success"];
|
||||
}
|
||||
fail:^(int code, NSString *desc) {
|
||||
[TUITool makeToastError:code msg:desc];
|
||||
}];
|
||||
}]];
|
||||
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel) style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:ac animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - TableView data source
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return self.dataList.count;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.dataList[section].count;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
|
||||
UIView *view = [[UIView alloc] init];
|
||||
view.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#F2F3F5");
|
||||
return view;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
|
||||
UIView *view = [[UIView alloc] init];
|
||||
view.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#F2F3F5");
|
||||
return view;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
|
||||
return section == 0 ? 0 : 10;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSObject *data = self.dataList[indexPath.section][indexPath.row];
|
||||
if ([data isKindOfClass:[TUICommonContactProfileCardCellData class]]) {
|
||||
TUICommonContactProfileCardCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CardCell" forIndexPath:indexPath];
|
||||
cell.delegate = self;
|
||||
[cell fillWithData:(TUICommonContactProfileCardCellData *)data];
|
||||
return cell;
|
||||
|
||||
} else if ([data isKindOfClass:[TUICommonContactTextCellData class]]) {
|
||||
TUICommonContactTextCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TextCell" forIndexPath:indexPath];
|
||||
[cell fillWithData:(TUICommonContactTextCellData *)data];
|
||||
return cell;
|
||||
|
||||
} else if ([data isKindOfClass:[TUICommonContactSwitchCellData class]]) {
|
||||
TUICommonContactSwitchCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SwitchCell" forIndexPath:indexPath];
|
||||
[cell fillWithData:(TUICommonContactSwitchCellData *)data];
|
||||
return cell;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
||||
TUICommonCellData *data = self.dataList[indexPath.section][indexPath.row];
|
||||
return [data heightOfWidth:Screen_Width];
|
||||
}
|
||||
|
||||
- (void)onMessageDoNotDisturb:(TUICommonContactSwitchCell *)cell {
|
||||
V2TIMReceiveMessageOpt opt;
|
||||
if (cell.switcher.on) {
|
||||
opt = V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE;
|
||||
} else {
|
||||
opt = V2TIM_RECEIVE_MESSAGE;
|
||||
}
|
||||
[[V2TIMManager sharedInstance] setC2CReceiveMessageOpt:@[ self.userInfo.userID ] opt:opt succ:nil fail:nil];
|
||||
}
|
||||
|
||||
- (void)addLongPressGesture {
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPressAtCell:)];
|
||||
[self.tableView addGestureRecognizer:longPress];
|
||||
}
|
||||
|
||||
- (void)didLongPressAtCell:(UILongPressGestureRecognizer *)longPress {
|
||||
if (longPress.state == UIGestureRecognizerStateBegan) {
|
||||
CGPoint point = [longPress locationInView:self.tableView];
|
||||
NSIndexPath *pathAtView = [self.tableView indexPathForRowAtPoint:point];
|
||||
NSObject *data = [self.tableView cellForRowAtIndexPath:pathAtView];
|
||||
|
||||
if ([data isKindOfClass:[TUICommonContactTextCell class]]) {
|
||||
TUICommonContactTextCell *textCell = (TUICommonContactTextCell *)data;
|
||||
if (textCell.textData.value && ![textCell.textData.value isEqualToString:@"未设置"]) {
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
pasteboard.string = textCell.textData.value;
|
||||
NSString *toastString = [NSString stringWithFormat:@"已将 %@ 复制到粘贴板", textCell.textData.key];
|
||||
[TUITool makeToast:toastString];
|
||||
}
|
||||
} else if ([data isKindOfClass:[TUICommonContactProfileCardCell class]]) {
|
||||
TUICommonContactProfileCardCell *profileCard = (TUICommonContactProfileCardCell *)data;
|
||||
if (profileCard.cardData.identifier) {
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
pasteboard.string = profileCard.cardData.identifier;
|
||||
NSString *toastString = [NSString stringWithFormat:@"已将该用户账号复制到粘贴板"];
|
||||
[TUITool makeToast:toastString];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user