tencent_cloud_chat_uikit source code update to version 3.1.0+2

This commit is contained in:
vinsonswang 2024-12-12 20:39:12 +08:00
parent 5700746752
commit e7c38ef149
12 changed files with 1251 additions and 637 deletions

View File

@ -112,38 +112,38 @@ _coreInstance.login(
全部场景码清单如下: 全部场景码清单如下:
| 场景码 `infoCode` | 推荐提示语 `infoRecommendText` | 场景描述 | | 场景码 `infoCode` | 推荐提示语 `infoRecommendText` | 场景描述 |
| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | |------------------| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 6660101 | 好友申请已发送 | 用户申请添加其他用户为联系人 | | 6660101(3.0.1废弃)| 好友申请已发送 | 用户申请添加其他用户为联系人 |
| 6660102 | 该用户已是好友 | 用户申请添加其他已是好友的用户为好友时,触发 `onTapAlreadyFriendsItem` 回调 | | 6660102(3.0.1废弃)| 该用户已是好友 | 用户申请添加其他已是好友的用户为好友时,触发 `onTapAlreadyFriendsItem` 回调 |
| 6660201 | 群申请已发送 | 用户申请加入需要管理员审批的群聊 | | 6660201 | 群申请已发送 | 用户申请加入需要管理员审批的群聊 |
| 6660202 | 您已是群成员 | 用户申请加群时,判断用户已经是当前群成员,触发 `onTapExistGroup` 回调 | | 6660202 | 您已是群成员 | 用户申请加群时,判断用户已经是当前群成员,触发 `onTapExistGroup` 回调 |
| 6660401 | 无法定位到原消息 | 当用户需要跳转至@消息或者是引用消息时,在消息列表中查不到目标消息 | | 6660401(3.0.1废弃)| 无法定位到原消息 | 当用户需要跳转至@消息或者是引用消息时,在消息列表中查不到目标消息 |
| 6660402 | 视频保存成功 | 用户在消息列表,点开视频消息后,选择保存视频 | | 6660402 | 视频保存成功 | 用户在消息列表,点开视频消息后,选择保存视频 |
| 6660403 | 视频保存失败 | 用户在消息列表,点开视频消息后,选择保存视频 | | 6660403 | 视频保存失败 | 用户在消息列表,点开视频消息后,选择保存视频 |
| 6660404 | 说话时间太短 | 用户发送了过短的语音消息 | | 6660404 | 说话时间太短 | 用户发送了过短的语音消息 |
| 6660405 | 发送失败,视频不能大于 100MB | 用户试图发送大于 100MB 的视频 | | 6660405(3.0.1废弃)| 发送失败,视频不能大于 100MB | 用户试图发送大于 100MB 的视频 |
| 6660406 | 图片保存成功 | 用户在消息列表,点开图片大图后,选择保存图片 | | 6660406 | 图片保存成功 | 用户在消息列表,点开图片大图后,选择保存图片 |
| 6660407 | 图片保存失败 | 用户在消息列表,点开图片大图后,选择保存图片 | | 6660407 | 图片保存失败 | 用户在消息列表,点开图片大图后,选择保存图片 |
| 6660408 | 已复制 | 用户在弹窗内选择复制文字消息 | | 6660408 | 已复制 | 用户在弹窗内选择复制文字消息 |
| 6660409 | 暂未实现 | 用户在弹窗内选择非标功能 | | 6660409 | 暂未实现 | 用户在弹窗内选择非标功能 |
| 6660410 | 其他文件正在接收中 | 用户点击下载文件消息时,前序下载任务还未完成 | | 6660410 | 其他文件正在接收中 | 用户点击下载文件消息时,前序下载任务还未完成 |
| 6660411 | 正在接收中 | 用户点击下载文件消息 | | 6660411 | 正在接收中 | 用户点击下载文件消息 |
| 6660412 | 视频消息仅限 mp4 格式 | 用户发送了一条非 mp4 格式的视频消息 | | 6660412 | 视频消息仅限 mp4 格式 | 用户发送了一条非 mp4 格式的视频消息 |
| 6660413 | 已加入待下载队列,其他文件下载中 | 已加入待下载队列,其他文件下载中 | | 6660413 | 已加入待下载队列,其他文件下载中 | 已加入待下载队列,其他文件下载中 |
| 6661001 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改群资料 | | 6661001 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改群资料 |
| 6661002 | 无网络连接,无法查看群成员 | 当用户试图在无网络环境下,修改群资料 | | 6661002 | 无网络连接,无法查看群成员 | 当用户试图在无网络环境下,修改群资料 |
| 6661003 | 成功取消管理员身份 | 用户将群内其他用户移除管理员 | | 6661003 | 成功取消管理员身份 | 用户将群内其他用户移除管理员 |
| 6661201 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改自己或联系人的资料 | | 6661201 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改自己或联系人的资料 |
| 6661202 | 好友添加成功 | 在资料页添加其他用户为好友,并自动添加成功,无需验证 | | 6661202(3.0.1废弃)| 好友添加成功 | 在资料页添加其他用户为好友,并自动添加成功,无需验证 |
| 6661203 | 好友申请已发出 | 在资料页添加其他用户为好友,对方设置需要验证 | | 6661203(3.0.1废弃)| 好友申请已发出 | 在资料页添加其他用户为好友,对方设置需要验证 |
| 6661204 | 当前用户在黑名单 | 在资料页添加其他用户为好友,对方在自己的黑名单内 | | 6661204(3.0.1废弃)| 当前用户在黑名单 | 在资料页添加其他用户为好友,对方在自己的黑名单内 |
| 6661205 | 好友添加失败 | 在资料页添加其他用户为好友,添加失败,可能是由于对方禁止加好友 | | 6661205(3.0.1废弃)| 好友添加失败 | 在资料页添加其他用户为好友,添加失败,可能是由于对方禁止加好友 |
| 6661206 | 好友删除成功 | 在资料页删除其他用户为好友,成功 | | 6661206(3.0.1废弃)| 好友删除成功 | 在资料页删除其他用户为好友,成功 |
| 6661207 | 好友删除失败 | 在资料页删除其他用户为好友,失败 | | 6661207(3.0.1废弃)| 好友删除失败 | 在资料页删除其他用户为好友,失败 |
| 6661401 | 输入不能为空 | 当用户在录入信息时,输入了空字符串 | | 6661401 | 输入不能为空 | 当用户在录入信息时,输入了空字符串 |
| 6661402 | 请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法 | 用户退出群或解散群时,为提供返回首页办法 | | 6661402(3.0.1废弃)| 请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法 | 用户退出群或解散群时,为提供返回首页办法 |
| 6661403 | 设备存储空间不足,建议清理,以获得更好使用体验 | 在login成功后会自动检测设备存储空间如果不足1GB会提示存储空间不足 | | 6661403 | 设备存储空间不足,建议清理,以获得更好使用体验 | 在login成功后会自动检测设备存储空间如果不足1GB会提示存储空间不足 |
## TIMUIKitConversation ## TIMUIKitConversation

View File

@ -32,7 +32,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
final TUIChatModelTools tools = serviceLocator<TUIChatModelTools>(); final TUIChatModelTools tools = serviceLocator<TUIChatModelTools>();
final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>(); final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>();
final TUIConversationViewModel conversationViewModel = serviceLocator<TUIConversationViewModel>(); final TUIConversationViewModel conversationViewModel =
serviceLocator<TUIConversationViewModel>();
final _uuid = const Uuid(); final _uuid = const Uuid();
ChatLifeCycle? lifeCycle; ChatLifeCycle? lifeCycle;
@ -135,7 +136,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
List<V2TimMessage> currentHistoryMsgList = getOriginMessageList(); List<V2TimMessage> currentHistoryMsgList = getOriginMessageList();
for (var v2TimMessage in currentHistoryMsgList) { for (var v2TimMessage in currentHistoryMsgList) {
if (_selectedPositions.containsKey(v2TimMessage.msgID) && _selectedPositions[v2TimMessage.msgID]!) { if (_selectedPositions.containsKey(v2TimMessage.msgID) &&
_selectedPositions[v2TimMessage.msgID]!) {
selectList.add(v2TimMessage); selectList.add(v2TimMessage);
} }
} }
@ -368,7 +370,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
? haveMoreLatestData = false ? haveMoreLatestData = false
: tempHaveMoreData = false; : tempHaveMoreData = false;
// //
final currentRecordList = globalModel.messageListMap[conversationID]; final currentRecordList = globalModel.messageListMap[conversationID];
@ -478,7 +479,10 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
_getMsgReadReceipt(List<V2TimMessage> message) async { _getMsgReadReceipt(List<V2TimMessage> message) async {
final msgID = message final msgID = message
.where((e) => (e.isSelf ?? true) && (e.needReadReceipt ?? false) && (e.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC)) .where((e) =>
(e.isSelf ?? true) &&
(e.needReadReceipt ?? false) &&
(e.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC))
.map((e) => e.msgID ?? '') .map((e) => e.msgID ?? '')
.toList(); .toList();
if (msgID.isNotEmpty) { if (msgID.isNotEmpty) {
@ -587,12 +591,12 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
_notify(); _notify();
} }
} }
void _notify(){ void _notify() {
try{ try {
notifyListeners(); notifyListeners();
}catch(e){ } catch (e) {
debugPrint(e.toString()); debugPrint(e.toString());
} }
} }
@ -920,9 +924,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
_repliedMessage = null; _repliedMessage = null;
final sendMsgRes = await _messageService.sendMessage( final sendMsgRes = await _messageService.sendMessage(
cloudCustomData: cloudCustomData: TencentUtils.checkString(
TencentUtils.checkString(messageInfoWithSender?.cloudCustomData) ?? messageInfoWithSender?.cloudCustomData) ??
json.encode(cloudCustomData), json.encode(cloudCustomData),
id: textMessageInfo.id as String, id: textMessageInfo.id as String,
offlinePushInfo: tools.buildMessagePushInfo( offlinePushInfo: tools.buildMessagePushInfo(
messageInfoWithSender, convID, convType), messageInfoWithSender, convID, convType),
@ -1149,9 +1153,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
for (var conversation in conversationList) { for (var conversation in conversationList) {
final convID = conversation.groupID ?? conversation.userID ?? ""; final convID = conversation.groupID ?? conversation.userID ?? "";
final convType = conversation.type; final convType = conversation.type;
List<V2TimMessage> currentHistoryMsgList = globalModel.messageListMap[conversationID] ?? []; List<V2TimMessage> currentHistoryMsgList =
globalModel.messageListMap[conversationID] ?? [];
for (var message in selectedMessages) { for (var message in selectedMessages) {
final forwardMessageInfo = await _messageService.createForwardMessage(msgID: message.msgID!); final forwardMessageInfo =
await _messageService.createForwardMessage(msgID: message.msgID!);
final messageInfo = forwardMessageInfo!.messageInfo; final messageInfo = forwardMessageInfo!.messageInfo;
if (messageInfo != null) { if (messageInfo != null) {
tools.setUserInfoForMessage(messageInfo, forwardMessageInfo.id); tools.setUserInfoForMessage(messageInfo, forwardMessageInfo.id);
@ -1159,11 +1165,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
addSendingMessageID(messageInfo.id); addSendingMessageID(messageInfo.id);
// //
if (convID == conversationID) { if (convID == conversationID) {
if (globalModel.getMessageListPosition(convID) != HistoryMessagePosition.notShowLatest) { if (globalModel.getMessageListPosition(convID) !=
currentHistoryMsgList = [ HistoryMessagePosition.notShowLatest) {
messageInfo, currentHistoryMsgList = [messageInfo, ...currentHistoryMsgList];
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList); globalModel.setMessageList(conversationID, currentHistoryMsgList);
_notify(); _notify();
} }
@ -1198,7 +1202,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
for (var conversation in conversationList) { for (var conversation in conversationList) {
final convID = conversation.groupID ?? conversation.userID ?? ""; final convID = conversation.groupID ?? conversation.userID ?? "";
final convType = conversation.type; final convType = conversation.type;
List<V2TimMessage> currentHistoryMsgList = globalModel.messageListMap[conversationID] ?? []; List<V2TimMessage> currentHistoryMsgList =
globalModel.messageListMap[conversationID] ?? [];
final mergerMessageInfo = await _messageService.createMergerMessage( final mergerMessageInfo = await _messageService.createMergerMessage(
msgIDList: msgIDList, msgIDList: msgIDList,
title: title, title: title,
@ -1211,11 +1216,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
addSendingMessageID(messageInfo.id); addSendingMessageID(messageInfo.id);
// //
if (convID == conversationID) { if (convID == conversationID) {
if (globalModel.getMessageListPosition(convID) != HistoryMessagePosition.notShowLatest) { if (globalModel.getMessageListPosition(convID) !=
currentHistoryMsgList = [ HistoryMessagePosition.notShowLatest) {
messageInfo, currentHistoryMsgList = [messageInfo, ...currentHistoryMsgList];
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList); globalModel.setMessageList(conversationID, currentHistoryMsgList);
_notify(); _notify();
} }
@ -1244,16 +1247,14 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null; return null;
} }
currentHistoryMsgList.removeWhere((element) => element.msgID == message.msgID); currentHistoryMsgList
.removeWhere((element) => element.msgID == message.msgID);
message.status = MessageStatus.V2TIM_MSG_STATUS_SENDING; message.status = MessageStatus.V2TIM_MSG_STATUS_SENDING;
addSendingMessageID(message.msgID); addSendingMessageID(message.msgID);
globalModel.setMessageList(convID, currentHistoryMsgList); globalModel.setMessageList(convID, currentHistoryMsgList);
if (globalModel.getMessageListPosition(conversationID) != if (globalModel.getMessageListPosition(conversationID) !=
HistoryMessagePosition.notShowLatest) { HistoryMessagePosition.notShowLatest) {
currentHistoryMsgList = [ currentHistoryMsgList = [message, ...currentHistoryMsgList];
message,
...currentHistoryMsgList
];
globalModel.setMessageList(conversationID, currentHistoryMsgList); globalModel.setMessageList(conversationID, currentHistoryMsgList);
_notify(); _notify();
} }
@ -1263,7 +1264,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
msgID: message.msgID ?? "", onlineUserOnly: false); msgID: message.msgID ?? "", onlineUserOnly: false);
removeSendingMessageID(message.msgID ?? ""); removeSendingMessageID(message.msgID ?? "");
if (globalModel.getMessageListPosition(conversationID) != if (globalModel.getMessageListPosition(conversationID) !=
HistoryMessagePosition.notShowLatest) { HistoryMessagePosition.notShowLatest) {
globalModel.updateMessage( globalModel.updateMessage(
res, convID, message.msgID!, convType, groupType, setInputField); res, convID, message.msgID!, convType, groupType, setInputField);
} }
@ -1426,7 +1427,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
for (var msgID in msgIDs) { for (var msgID in msgIDs) {
messageList.removeWhere((element) => element.msgID == msgID); messageList.removeWhere((element) => element.msgID == msgID);
} }
globalModel.setMessageList(conversationID, messageList, isDeleteMsg: true); globalModel.setMessageList(conversationID, messageList,
isDeleteMsg: true);
} }
} }
@ -1492,10 +1494,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
// //
bool? hasDelayedRenderSendingStatus(String id) { bool? hasDelayedRenderSendingStatus(String id) {
if (_sendingMessageIDMap.containsKey(id)) { if (_sendingMessageIDMap.containsKey(id)) {
return _sendingMessageIDMap[id]; return _sendingMessageIDMap[id];
} }
print("sending test, hasDelayedRenderSendingStatus false:${id}");
return true; return true;
} }

View File

@ -1,5 +1,6 @@
// ignore_for_file: unnecessary_getters_setters // ignore_for_file: unnecessary_getters_setters
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/conversation_life_cycle.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/conversation_life_cycle.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
@ -11,7 +12,8 @@ import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
List<T> removeDuplicates<T>(List<T> list, bool Function(T first, T second) isEqual) { List<T> removeDuplicates<T>(
List<T> list, bool Function(T first, T second) isEqual) {
List<T> output = []; List<T> output = [];
for (var i = 0; i < list.length; i++) { for (var i = 0; i < list.length; i++) {
bool found = false; bool found = false;
@ -32,10 +34,14 @@ class TUIConversationViewModel extends ChangeNotifier {
static const String conversationC2CPrefix = "c2c_"; static const String conversationC2CPrefix = "c2c_";
static const String conversationGroupPrefix = "group_"; static const String conversationGroupPrefix = "group_";
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>(); final TUISelfInfoViewModel selfInfoViewModel =
final ConversationService _conversationService = serviceLocator<ConversationService>(); serviceLocator<TUISelfInfoViewModel>();
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>(); final ConversationService _conversationService =
final TUIChatGlobalModel _chatGlobalModel = serviceLocator<TUIChatGlobalModel>(); serviceLocator<ConversationService>();
final FriendshipServices _friendshipServices =
serviceLocator<FriendshipServices>();
final TUIChatGlobalModel _chatGlobalModel =
serviceLocator<TUIChatGlobalModel>();
final MessageService _messageService = serviceLocator<MessageService>(); final MessageService _messageService = serviceLocator<MessageService>();
late V2TimConversationListener _conversationListener; late V2TimConversationListener _conversationListener;
List<V2TimConversation?> _conversationList = []; List<V2TimConversation?> _conversationList = [];
@ -45,7 +51,8 @@ class TUIConversationViewModel extends ChangeNotifier {
bool _haveMoreData = true; bool _haveMoreData = true;
int _totalUnReadCount = 0; int _totalUnReadCount = 0;
String? _scrollToConversation; String? _scrollToConversation;
final TUIChatGlobalModel globalChatModel = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel globalChatModel =
serviceLocator<TUIChatGlobalModel>();
String _nextSeq = "0"; String _nextSeq = "0";
ConversationLifeCycle? _lifeCycle; ConversationLifeCycle? _lifeCycle;
@ -54,10 +61,13 @@ class TUIConversationViewModel extends ChangeNotifier {
if (PlatformUtils().isWeb) { if (PlatformUtils().isWeb) {
try { try {
_conversationList.sort((a, b) { _conversationList.sort((a, b) {
return b!.lastMessage!.timestamp!.compareTo(a!.lastMessage!.timestamp!); return b!.lastMessage!.timestamp!
.compareTo(a!.lastMessage!.timestamp!);
}); });
final pinnedConversation = _conversationList.where((element) => element?.isPinned == true).toList(); final pinnedConversation = _conversationList
.where((element) => element?.isPinned == true)
.toList();
_conversationList.removeWhere((element) => element?.isPinned == true); _conversationList.removeWhere((element) => element?.isPinned == true);
_conversationList = [...pinnedConversation, ..._conversationList]; _conversationList = [...pinnedConversation, ..._conversationList];
// ignore: empty_catches // ignore: empty_catches
@ -69,7 +79,8 @@ class TUIConversationViewModel extends ChangeNotifier {
} }
V2TimConversation? getConversation(String conversationID) { V2TimConversation? getConversation(String conversationID) {
return _conversationList.firstWhere((element) => element?.conversationID == conversationID); return _conversationList.firstWhereOrNull(
(element) => element?.conversationID == conversationID);
} }
String? get scrollToConversation => _scrollToConversation; String? get scrollToConversation => _scrollToConversation;
@ -116,7 +127,8 @@ class TUIConversationViewModel extends ChangeNotifier {
} }
TUIConversationViewModel() { TUIConversationViewModel() {
_conversationListener = V2TimConversationListener(onConversationChanged: (conversationList) { _conversationListener =
V2TimConversationListener(onConversationChanged: (conversationList) {
_onConversationListChanged(conversationList); _onConversationListChanged(conversationList);
}, onNewConversation: (conversationList) { }, onNewConversation: (conversationList) {
_addNewConversation(conversationList); _addNewConversation(conversationList);
@ -129,7 +141,7 @@ class TUIConversationViewModel extends ChangeNotifier {
if (!PlatformUtils().isWeb) { if (!PlatformUtils().isWeb) {
loadInitConversation(); loadInitConversation();
} }
}, onConversationDeleted:(List<String> conversationIDList) { }, onConversationDeleted: (List<String> conversationIDList) {
_onConversationDeleted(conversationIDList); _onConversationDeleted(conversationIDList);
for (var conversationID in conversationIDList) { for (var conversationID in conversationIDList) {
String resultID = ""; String resultID = "";
@ -162,7 +174,8 @@ class TUIConversationViewModel extends ChangeNotifier {
Future<void> loadData({required int count}) async { Future<void> loadData({required int count}) async {
_haveMoreData = true; _haveMoreData = true;
final isRefresh = _nextSeq == "0"; final isRefresh = _nextSeq == "0";
final conversationResult = await _conversationService.getConversationList(nextSeq: _nextSeq, count: count); final conversationResult = await _conversationService.getConversationList(
nextSeq: _nextSeq, count: count);
_nextSeq = conversationResult?.nextSeq ?? ""; _nextSeq = conversationResult?.nextSeq ?? "";
final conversationList = conversationResult?.conversationList; final conversationList = conversationResult?.conversationList;
if (conversationList != null) { if (conversationList != null) {
@ -175,8 +188,12 @@ class TUIConversationViewModel extends ChangeNotifier {
} else { } else {
combinedConversationList = [..._conversationList, ...conversationList]; combinedConversationList = [..._conversationList, ...conversationList];
} }
final List<V2TimConversation?> finalConversationList = await _lifeCycle?.conversationListWillMount(combinedConversationList) ?? combinedConversationList; final List<V2TimConversation?> finalConversationList = await _lifeCycle
_conversationList = removeDuplicates<V2TimConversation?>(finalConversationList, (item1, item2) => item1?.conversationID == item2?.conversationID); ?.conversationListWillMount(combinedConversationList) ??
combinedConversationList;
_conversationList = removeDuplicates<V2TimConversation?>(
finalConversationList,
(item1, item2) => item1?.conversationID == item2?.conversationID);
notifyListeners(); notifyListeners();
} }
_totalUnReadCount = await _conversationService.getTotalUnreadCount(); _totalUnReadCount = await _conversationService.getTotalUnreadCount();
@ -193,11 +210,15 @@ class TUIConversationViewModel extends ChangeNotifier {
required String conversationID, required String conversationID,
required bool isPinned, required bool isPinned,
}) { }) {
return _conversationService.pinConversation(conversationID: conversationID, isPinned: isPinned); return _conversationService.pinConversation(
conversationID: conversationID, isPinned: isPinned);
} }
Future<V2TimCallback?> clearHistoryMessage({required String convID, required int convType}) async { Future<V2TimCallback?> clearHistoryMessage(
if (_lifeCycle?.shouldClearHistoricalMessageForConversation != null && await _lifeCycle!.shouldClearHistoricalMessageForConversation(convID) == false) { {required String convID, required int convType}) async {
if (_lifeCycle?.shouldClearHistoricalMessageForConversation != null &&
await _lifeCycle!.shouldClearHistoricalMessageForConversation(convID) ==
false) {
return null; return null;
} }
@ -211,17 +232,22 @@ class TUIConversationViewModel extends ChangeNotifier {
} }
searchFriends(String searchKey) async { searchFriends(String searchKey) async {
final res = await _friendshipServices.searchFriends(searchParam: V2TimFriendSearchParam(keywordList: [searchKey])); final res = await _friendshipServices.searchFriends(
searchParam: V2TimFriendSearchParam(keywordList: [searchKey]));
return res; return res;
} }
Future<V2TimCallback?> deleteConversation({required String conversationID}) async { Future<V2TimCallback?> deleteConversation(
if (_lifeCycle?.shouldDeleteConversation != null && await _lifeCycle!.shouldDeleteConversation(conversationID) == false) { {required String conversationID}) async {
if (_lifeCycle?.shouldDeleteConversation != null &&
await _lifeCycle!.shouldDeleteConversation(conversationID) == false) {
return null; return null;
} }
final res = await _conversationService.deleteConversation(conversationID: conversationID); final res = await _conversationService.deleteConversation(
conversationID: conversationID);
if (res.code == 0) { if (res.code == 0) {
_conversationList.removeWhere((element) => element?.conversationID == conversationID); _conversationList
.removeWhere((element) => element?.conversationID == conversationID);
notifyListeners(); notifyListeners();
} }
return res; return res;
@ -229,9 +255,11 @@ class TUIConversationViewModel extends ChangeNotifier {
_onConversationListChanged(List<V2TimConversation> list) { _onConversationListChanged(List<V2TimConversation> list) {
for (int element = 0; element < list.length; element++) { for (int element = 0; element < list.length; element++) {
int index = _conversationList.indexWhere((item) => item!.conversationID == list[element].conversationID); int index = _conversationList.indexWhere(
(item) => item!.conversationID == list[element].conversationID);
if (index > -1) { if (index > -1) {
_conversationList.setAll(index, [list[element]] as List<V2TimConversation?>); _conversationList.setAll(
index, [list[element]] as List<V2TimConversation?>);
} else { } else {
_conversationList.add(list[element]); _conversationList.add(list[element]);
} }
@ -242,10 +270,13 @@ class TUIConversationViewModel extends ChangeNotifier {
_onConversationDeleted(List<String> list) { _onConversationDeleted(List<String> list) {
for (int i = 0; i < list.length; i++) { for (int i = 0; i < list.length; i++) {
int index = _conversationList.indexWhere((item) => item!.conversationID == list[i]); int index = _conversationList
.indexWhere((item) => item!.conversationID == list[i]);
if (index > -1) { if (index > -1) {
_conversationList.removeAt(index); _conversationList.removeAt(index);
_conversationList = removeDuplicates<V2TimConversation?>(_conversationList, (item1, item2) => item1?.conversationID == item2?.conversationID); _conversationList = removeDuplicates<V2TimConversation?>(
_conversationList,
(item1, item2) => item1?.conversationID == item2?.conversationID);
} }
} }
notifyListeners(); notifyListeners();
@ -253,16 +284,19 @@ class TUIConversationViewModel extends ChangeNotifier {
_addNewConversation(List<V2TimConversation> list) { _addNewConversation(List<V2TimConversation> list) {
_conversationList.addAll(list); _conversationList.addAll(list);
_conversationList = removeDuplicates<V2TimConversation?>(_conversationList, (item1, item2) => item1?.conversationID == item2?.conversationID); _conversationList = removeDuplicates<V2TimConversation?>(_conversationList,
(item1, item2) => item1?.conversationID == item2?.conversationID);
notifyListeners(); notifyListeners();
} }
setConversationListener() { setConversationListener() {
_conversationService.addConversationListener(listener: _conversationListener); _conversationService.addConversationListener(
listener: _conversationListener);
} }
removeConversationListener() { removeConversationListener() {
_conversationService.removeConversationListener(listener: _conversationListener); _conversationService.removeConversationListener(
listener: _conversationListener);
} }
Future<V2TimCallback> setConversationDraft({ Future<V2TimCallback> setConversationDraft({
@ -272,19 +306,25 @@ class TUIConversationViewModel extends ChangeNotifier {
String? groupID, String? groupID,
bool isAllowWeb = true, bool isAllowWeb = true,
}) async { }) async {
assert(!isTopic || (groupID != null && groupID.isNotEmpty), "When 'isTopic' is true, 'groupID' must not be null or empty."); assert(!isTopic || (groupID != null && groupID.isNotEmpty),
"When 'isTopic' is true, 'groupID' must not be null or empty.");
if (PlatformUtils().isWeb && isAllowWeb) { if (PlatformUtils().isWeb && isAllowWeb) {
webDraftMap[conversationID] = draftText ?? ""; webDraftMap[conversationID] = draftText ?? "";
return V2TimCallback(code: 0, desc: ""); return V2TimCallback(code: 0, desc: "");
} else { } else {
if (isTopic) { if (isTopic) {
final topicInfoList = await TencentImSDKPlugin.v2TIMManager.getGroupManager().getTopicInfoList(groupID: groupID!, topicIDList: [conversationID]); final topicInfoList = await TencentImSDKPlugin.v2TIMManager
.getGroupManager()
.getTopicInfoList(groupID: groupID!, topicIDList: [conversationID]);
final topicInfo = topicInfoList.data?.first.topicInfo; final topicInfo = topicInfoList.data?.first.topicInfo;
topicInfo?.draftText = draftText; topicInfo?.draftText = draftText;
final res = await TencentImSDKPlugin.v2TIMManager.getGroupManager().setTopicInfo(groupID: groupID, topicInfo: topicInfo!); final res = await TencentImSDKPlugin.v2TIMManager
.getGroupManager()
.setTopicInfo(groupID: groupID, topicInfo: topicInfo!);
return res; return res;
} else { } else {
return _conversationService.setConversationDraft(conversationID: conversationID, draftText: draftText); return _conversationService.setConversationDraft(
conversationID: conversationID, draftText: draftText);
} }
} }
} }

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable_plus_plus/flutter_slidable_plus_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/block_list_life_cycle.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/block_list_life_cycle.dart';
@ -88,7 +88,8 @@ class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
child: Text( child: Text(
showName, showName,
style: TextStyle( style: TextStyle(
color: theme.black, fontSize: isDesktopScreen ? 14 : 18), color: theme.black,
fontSize: isDesktopScreen ? 14 : 18),
), ),
)), )),
if (isDesktopScreen) if (isDesktopScreen)
@ -132,7 +133,6 @@ class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
return widget.itemBuilder ?? _itemBuilder; return widget.itemBuilder ?? _itemBuilder;
} }
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
return MultiProvider( return MultiProvider(

View File

@ -5,7 +5,6 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:image_clipboard/image_clipboard.dart';
import 'package:open_file/open_file.dart'; import 'package:open_file/open_file.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
@ -196,13 +195,15 @@ class TIMUIKitMessageTooltipState
bool showTranslation = true; bool showTranslation = true;
if (widget.message.localCustomData != null) { if (widget.message.localCustomData != null) {
final LocalCustomDataModel localCustomData = LocalCustomDataModel.fromMap( final LocalCustomDataModel localCustomData = LocalCustomDataModel.fromMap(
json.decode(TencentUtils.checkString(widget.message.localCustomData) ?? "{}")); json.decode(
if (localCustomData.translatedText != null && localCustomData.translatedText != "") { TencentUtils.checkString(widget.message.localCustomData) ??
"{}"));
if (localCustomData.translatedText != null &&
localCustomData.translatedText != "") {
showTranslation = false; showTranslation = false;
} }
} }
final dynamicQuote = final dynamicQuote =
model.chatConfig.isAtWhenReplyDynamic?.call(widget.message); model.chatConfig.isAtWhenReplyDynamic?.call(widget.message);
@ -405,12 +406,6 @@ class TIMUIKitMessageTooltipState
} catch (e) {} } catch (e) {}
} }
Future<void> copyImageToClipboard(String imagePath) async {
ImageClipboard().copyImage(imagePath);
// final DesktopClipboard desktopClipboard = DesktopClipboard();
// desktopClipboard.copyImage(imagePath);
}
_onTap(String operation, TUIChatSeparateViewModel model) async { _onTap(String operation, TUIChatSeparateViewModel model) async {
final messageItem = widget.message; final messageItem = widget.message;
final msgID = messageItem.msgID as String; final msgID = messageItem.msgID as String;
@ -494,13 +489,6 @@ class TIMUIKitMessageTooltipState
infoCode: 6660408)); infoCode: 6660408));
// ignore: empty_catches // ignore: empty_catches
} catch (e) {} } catch (e) {}
} else if (widget.message.elemType ==
MessageElemType.V2TIM_ELEM_TYPE_IMAGE) {
final savePath = (TencentUtils.checkString(
widget.message.imageElem!.imageList?[0]?.localUrl) ??
TencentUtils.checkString(widget.message.imageElem?.path) ??
"");
copyImageToClipboard(savePath);
} }
break; break;
case "replyMessage": case "replyMessage":

View File

@ -12,7 +12,7 @@ import 'package:crypto/crypto.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:open_file/open_file.dart'; import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -172,7 +172,8 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (model.getMessageProgress(widget.message.msgID) == 100) { if (model.getMessageProgress(widget.message.msgID) == 100) {
String savePath; String savePath;
if (widget.message.imageElem!.path != null && if (widget.message.imageElem!.path != null &&
widget.message.imageElem!.path != '' && File(widget.message.imageElem!.path!).existsSync()) { widget.message.imageElem!.path != '' &&
File(widget.message.imageElem!.path!).existsSync()) {
savePath = widget.message.imageElem!.path!; savePath = widget.message.imageElem!.path!;
} else { } else {
savePath = model.getFileMessageLocation(widget.message.msgID); savePath = model.getFileMessageLocation(widget.message.msgID);
@ -593,7 +594,6 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
Widget? _renderImage(dynamic heroTag, TUITheme theme, Widget? _renderImage(dynamic heroTag, TUITheme theme,
{V2TimImage? originalImg, V2TimImage? smallImg}) { {V2TimImage? originalImg, V2TimImage? smallImg}) {
double positionRadio = 1.0; double positionRadio = 1.0;
if (smallImg?.width != null && if (smallImg?.width != null &&
smallImg?.height != null && smallImg?.height != null &&

View File

@ -2,7 +2,7 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable_plus_plus/flutter_slidable_plus_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
@ -22,11 +22,15 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit
import 'package:tencent_cloud_chat_uikit/ui/widgets/customize_ball_pulse_header.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/customize_ball_pulse_header.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
typedef ConversationItemBuilder = Widget Function(V2TimConversation conversationItem, [V2TimUserStatus? onlineStatus]); typedef ConversationItemBuilder = Widget Function(
V2TimConversation conversationItem,
[V2TimUserStatus? onlineStatus]);
typedef ConversationItemSlideBuilder = List<ConversationItemSlidePanel> Function(V2TimConversation conversationItem); typedef ConversationItemSlideBuilder = List<ConversationItemSlidePanel>
Function(V2TimConversation conversationItem);
typedef ConversationItemSecondaryMenuBuilder = Widget Function(V2TimConversation conversationItem, VoidCallback onClose); typedef ConversationItemSecondaryMenuBuilder = Widget Function(
V2TimConversation conversationItem, VoidCallback onClose);
class TIMUIKitConversation extends StatefulWidget { class TIMUIKitConversation extends StatefulWidget {
/// the callback after clicking conversation item /// the callback after clicking conversation item
@ -140,11 +144,14 @@ class ConversationItemSlidePanel extends TIMUIKitStatelessWidget {
} }
class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> { class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
final TUIConversationViewModel model = serviceLocator<TUIConversationViewModel>(); final TUIConversationViewModel model =
serviceLocator<TUIConversationViewModel>();
late TIMUIKitConversationController _timuiKitConversationController; late TIMUIKitConversationController _timuiKitConversationController;
final TUIThemeViewModel themeViewModel = serviceLocator<TUIThemeViewModel>(); final TUIThemeViewModel themeViewModel = serviceLocator<TUIThemeViewModel>();
final TUIFriendShipViewModel friendShipViewModel = serviceLocator<TUIFriendShipViewModel>(); final TUIFriendShipViewModel friendShipViewModel =
final TUIGroupListenerModel groupListenerModel = serviceLocator<TUIGroupListenerModel>(); serviceLocator<TUIFriendShipViewModel>();
final TUIGroupListenerModel groupListenerModel =
serviceLocator<TUIGroupListenerModel>();
late AutoScrollController _autoScrollController; late AutoScrollController _autoScrollController;
@override @override
@ -168,21 +175,30 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
} }
_clearHistory(V2TimConversation conversationItem) { _clearHistory(V2TimConversation conversationItem) {
_timuiKitConversationController.clearHistoryMessage(conversation: conversationItem); _timuiKitConversationController.clearHistoryMessage(
conversation: conversationItem);
} }
_pinConversation(V2TimConversation conversation) { _pinConversation(V2TimConversation conversation) {
_timuiKitConversationController.pinConversation(conversationID: conversation.conversationID, isPinned: !conversation.isPinned!); _timuiKitConversationController.pinConversation(
conversationID: conversation.conversationID,
isPinned: !conversation.isPinned!);
} }
_deleteConversation(V2TimConversation conversation) { _deleteConversation(V2TimConversation conversation) {
_timuiKitConversationController.deleteConversation(conversationID: conversation.conversationID); _timuiKitConversationController.deleteConversation(
conversationID: conversation.conversationID);
} }
List<V2TimConversation?> getFilteredConversation() { List<V2TimConversation?> getFilteredConversation() {
List<V2TimConversation?> filteredConversationList = model.conversationList.where((element) => (element?.groupID != null || element?.userID != null)).toList(); List<V2TimConversation?> filteredConversationList = model.conversationList
.where(
(element) => (element?.groupID != null || element?.userID != null))
.toList();
if (widget.conversationCollector != null) { if (widget.conversationCollector != null) {
filteredConversationList = filteredConversationList.where(widget.conversationCollector!).toList(); filteredConversationList = filteredConversationList
.where(widget.conversationCollector!)
.toList();
} }
return filteredConversationList; return filteredConversationList;
} }
@ -208,7 +224,8 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
} }
} }
Widget _defaultSecondaryMenu(V2TimConversation conversationItem, VoidCallback onClose) { Widget _defaultSecondaryMenu(
V2TimConversation conversationItem, VoidCallback onClose) {
return TUIKitColumnMenu(data: [ return TUIKitColumnMenu(data: [
if (!PlatformUtils().isWeb) if (!PlatformUtils().isWeb)
ColumnMenuItem( ColumnMenuItem(
@ -220,7 +237,11 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
}), }),
ColumnMenuItem( ColumnMenuItem(
label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"), label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"),
icon: Icon(conversationItem.isPinned! ? Icons.vertical_align_bottom : Icons.vertical_align_top, size: 16), icon: Icon(
conversationItem.isPinned!
? Icons.vertical_align_bottom
: Icons.vertical_align_top,
size: 16),
onClick: () { onClick: () {
onClose(); onClose();
_pinConversation(conversationItem); _pinConversation(conversationItem);
@ -245,7 +266,8 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) { onPressed: (context) {
_clearHistory(conversationItem); _clearHistory(conversationItem);
}, },
backgroundColor: theme.conversationItemSliderClearBgColor ?? CommonColor.primaryColor, backgroundColor: theme.conversationItemSliderClearBgColor ??
CommonColor.primaryColor,
foregroundColor: theme.conversationItemSliderTextColor, foregroundColor: theme.conversationItemSliderTextColor,
label: TIM_t("清除"), label: TIM_t("清除"),
spacing: 0, spacing: 0,
@ -255,7 +277,8 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) { onPressed: (context) {
_pinConversation(conversationItem); _pinConversation(conversationItem);
}, },
backgroundColor: theme.conversationItemSliderPinBgColor ?? CommonColor.infoColor, backgroundColor:
theme.conversationItemSliderPinBgColor ?? CommonColor.infoColor,
foregroundColor: theme.conversationItemSliderTextColor, foregroundColor: theme.conversationItemSliderTextColor,
label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"), label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"),
), ),
@ -263,14 +286,16 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) { onPressed: (context) {
_deleteConversation(conversationItem); _deleteConversation(conversationItem);
}, },
backgroundColor: theme.conversationItemSliderDeleteBgColor ?? Colors.red, backgroundColor:
theme.conversationItemSliderDeleteBgColor ?? Colors.red,
foregroundColor: theme.conversationItemSliderTextColor, foregroundColor: theme.conversationItemSliderTextColor,
label: TIM_t("删除"), label: TIM_t("删除"),
) )
]; ];
} }
Widget _getSecondaryMenu(V2TimConversation conversation, VoidCallback onClose) { Widget _getSecondaryMenu(
V2TimConversation conversation, VoidCallback onClose) {
if (widget.itemSecondaryMenuBuilder != null) { if (widget.itemSecondaryMenuBuilder != null) {
return widget.itemSecondaryMenuBuilder!(conversation, onClose); return widget.itemSecondaryMenuBuilder!(conversation, onClose);
} }
@ -289,19 +314,23 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme; final theme = value.theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return MultiProvider( return MultiProvider(
providers: [ providers: [
ChangeNotifierProvider.value(value: model), ChangeNotifierProvider.value(value: model),
ChangeNotifierProvider.value(value: friendShipViewModel), ChangeNotifierProvider.value(value: friendShipViewModel),
ChangeNotifierProvider.value(value: groupListenerModel)], ChangeNotifierProvider.value(value: groupListenerModel)
],
builder: (BuildContext context, Widget? w) { builder: (BuildContext context, Widget? w) {
final _model = Provider.of<TUIConversationViewModel>(context); final _model = Provider.of<TUIConversationViewModel>(context);
bool haveMoreData = _model.haveMoreData; bool haveMoreData = _model.haveMoreData;
final _friendShipViewModel = Provider.of<TUIFriendShipViewModel>(context); final _friendShipViewModel =
Provider.of<TUIFriendShipViewModel>(context);
_model.lifeCycle = widget.lifeCycle; _model.lifeCycle = widget.lifeCycle;
final TUIGroupListenerModel groupListenerModel = Provider.of<TUIGroupListenerModel>(context, listen: true); final TUIGroupListenerModel groupListenerModel =
Provider.of<TUIGroupListenerModel>(context, listen: true);
final NeedUpdate? needUpdate = groupListenerModel.needUpdate; final NeedUpdate? needUpdate = groupListenerModel.needUpdate;
if (needUpdate != null) { if (needUpdate != null) {
groupListenerModel.needUpdate = null; groupListenerModel.needUpdate = null;
@ -313,12 +342,14 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
} else if (needUpdate.updateType == UpdateType.kickedFromGroup) { } else if (needUpdate.updateType == UpdateType.kickedFromGroup) {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO, type: TIMCallbackType.INFO,
infoRecommendText: "${TIM_t("您已被踢出")}${needUpdate!.extraData}", infoRecommendText:
"${TIM_t("您已被踢出")}${needUpdate!.extraData}",
infoCode: 6661402)); infoCode: 6661402));
} }
} }
List<V2TimConversation?> filteredConversationList = getFilteredConversation(); List<V2TimConversation?> filteredConversationList =
getFilteredConversation();
if (TencentUtils.checkString(_model.scrollToConversation) != null) { if (TencentUtils.checkString(_model.scrollToConversation) != null) {
_onScrollToConversation(_model.scrollToConversation!); _onScrollToConversation(_model.scrollToConversation!);
@ -340,15 +371,21 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
final conversationItem = filteredConversationList[index]; final conversationItem = filteredConversationList[index];
final V2TimUserStatus? onlineStatus = _friendShipViewModel.userStatusList.firstWhere((item) => item.userID == conversationItem?.userID, orElse: () => V2TimUserStatus(statusType: 0)); final V2TimUserStatus? onlineStatus =
_friendShipViewModel.userStatusList.firstWhere(
(item) => item.userID == conversationItem?.userID,
orElse: () => V2TimUserStatus(statusType: 0));
if (widget.itemBuilder != null) { if (widget.itemBuilder != null) {
return widget.itemBuilder!(conversationItem!, onlineStatus); return widget.itemBuilder!(
conversationItem!, onlineStatus);
} }
final slideChildren = _getSlideBuilder()(conversationItem!); final slideChildren =
_getSlideBuilder()(conversationItem!);
final isCurrent = conversationItem.conversationID == model.selectedConversation?.conversationID; final isCurrent = conversationItem.conversationID ==
model.selectedConversation?.conversationID;
final isPined = conversationItem.isPinned ?? false; final isPined = conversationItem.isPinned ?? false;
@ -365,14 +402,21 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
lastMessageBuilder: widget.lastMessageBuilder, lastMessageBuilder: widget.lastMessageBuilder,
faceUrl: conversationItem.faceUrl ?? "", faceUrl: conversationItem.faceUrl ?? "",
nickName: conversationItem.showName ?? "", nickName: conversationItem.showName ?? "",
isDisturb: (conversationItem.groupType == "Meeting" ? false : conversationItem isDisturb:
.recvOpt != 0), (conversationItem.groupType == "Meeting"
? false
: conversationItem.recvOpt != 0),
lastMsg: conversationItem.lastMessage, lastMsg: conversationItem.lastMessage,
isPined: isPined, isPined: isPined,
groupAtInfoList: conversationItem.groupAtInfoList ?? [], groupAtInfoList:
conversationItem.groupAtInfoList ?? [],
unreadCount: conversationItem.unreadCount ?? 0, unreadCount: conversationItem.unreadCount ?? 0,
draftText: conversationItem.draftText, draftText: conversationItem.draftText,
onlineStatus: (widget.isShowOnlineStatus && conversationItem.userID != null && conversationItem.userID!.isNotEmpty) ? onlineStatus : null, onlineStatus: (widget.isShowOnlineStatus &&
conversationItem.userID != null &&
conversationItem.userID!.isNotEmpty)
? onlineStatus
: null,
draftTimestamp: conversationItem.draftTimestamp, draftTimestamp: conversationItem.draftTimestamp,
convType: conversationItem.type), convType: conversationItem.type),
onTap: () => onTapConvItem(conversationItem), onTap: () => onTapConvItem(conversationItem),
@ -389,12 +433,23 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
child: InkWell( child: InkWell(
onSecondaryTapDown: (details) { onSecondaryTapDown: (details) {
TUIKitWidePopup.showPopupWindow( TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.conversationSecondaryMenu, operationKey: TUIKitWideModalOperationKey
.conversationSecondaryMenu,
isDarkBackground: false, isDarkBackground: false,
borderRadius: const BorderRadius.all(Radius.circular(4)), borderRadius: const BorderRadius.all(
Radius.circular(4)),
context: context, context: context,
offset: Offset(min(details.globalPosition.dx, MediaQuery.of(context).size.width - 80), min(details.globalPosition.dy, MediaQuery.of(context).size.height - 130)), offset: Offset(
child: (onClose) => _getSecondaryMenu(conversationItem, onClose)); min(
details.globalPosition.dx,
MediaQuery.of(context).size.width -
80),
min(
details.globalPosition.dy,
MediaQuery.of(context).size.height -
130)),
child: (onClose) => _getSecondaryMenu(
conversationItem, onClose));
}, },
child: conversationLineItem(), child: conversationLineItem(),
), ),
@ -403,10 +458,19 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
key: ValueKey(conversationItem.conversationID), key: ValueKey(conversationItem.conversationID),
controller: _autoScrollController, controller: _autoScrollController,
index: index, index: index,
child: Slidable(groupTag: 'conversation-list', child: conversationLineItem(), endActionPane: ActionPane(extentRatio: slideChildren.length > 2 ? 0.77 : 0.5, motion: const DrawerMotion(), children: slideChildren)), child: Slidable(
groupTag: 'conversation-list',
child: conversationLineItem(),
endActionPane: ActionPane(
extentRatio:
slideChildren.length > 2 ? 0.77 : 0.5,
motion: const DrawerMotion(),
children: slideChildren)),
)); ));
}) })
: (widget.emptyBuilder != null ? widget.emptyBuilder!() : Container()); : (widget.emptyBuilder != null
? widget.emptyBuilder!()
: Container());
} }
return TUIKitScreenUtils.getDeviceWidget( return TUIKitScreenUtils.getDeviceWidget(
@ -420,7 +484,9 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
child: conversationList(), child: conversationList(),
), ),
), ),
desktopWidget: Scrollbar(controller: _autoScrollController, child: conversationList())); desktopWidget: Scrollbar(
controller: _autoScrollController,
child: conversationList()));
}); });
} }
} }

View File

@ -2,7 +2,7 @@ import 'dart:math';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable_plus_plus/flutter_slidable_plus_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
@ -26,23 +26,34 @@ class GroupProfileGroupManage extends StatefulWidget {
State<StatefulWidget> createState() => GroupProfileGroupManageState(); State<StatefulWidget> createState() => GroupProfileGroupManageState();
} }
class GroupProfileGroupManageState extends TIMUIKitState<GroupProfileGroupManage> { class GroupProfileGroupManageState
extends TIMUIKitState<GroupProfileGroupManage> {
bool isShowManageBox = false; bool isShowManageBox = false;
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme; final TUITheme theme = value.theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final model = Provider.of<TUIGroupProfileModel>(context); final model = Provider.of<TUIGroupProfileModel>(context);
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(color: Colors.white, border: isDesktopScreen ? null : Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), decoration: BoxDecoration(
color: Colors.white,
border: isDesktopScreen
? null
: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
child: Column( child: Column(
children: [ children: [
InkWell( InkWell(
onTap: () { onTap: () {
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) ==
DeviceType.Desktop;
if (!isDesktopScreen) { if (!isDesktopScreen) {
Navigator.push( Navigator.push(
context, context,
@ -61,12 +72,15 @@ class GroupProfileGroupManageState extends TIMUIKitState<GroupProfileGroupManage
children: [ children: [
Text( Text(
TIM_t("群管理"), TIM_t("群管理"),
style: TextStyle(fontSize: isDesktopScreen ? 14 : 16, color: theme.darkTextColor), style: TextStyle(
fontSize: isDesktopScreen ? 14 : 16,
color: theme.darkTextColor),
), ),
AnimatedRotation( AnimatedRotation(
turns: isShowManageBox ? 0.25 : 0, turns: isShowManageBox ? 0.25 : 0,
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
child: Icon(Icons.keyboard_arrow_right, color: theme.weakTextColor), child: Icon(Icons.keyboard_arrow_right,
color: theme.weakTextColor),
) )
], ],
), ),
@ -94,7 +108,8 @@ class GroupProfileGroupManagePage extends StatefulWidget {
State<StatefulWidget> createState() => _GroupProfileGroupManagePageState(); State<StatefulWidget> createState() => _GroupProfileGroupManagePageState();
} }
class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupManagePage> { class _GroupProfileGroupManagePageState
extends TIMUIKitState<GroupProfileGroupManagePage> {
int? serverTime; int? serverTime;
@override @override
@ -113,20 +128,38 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
return MultiProvider( return MultiProvider(
providers: [ChangeNotifierProvider.value(value: widget.model), ChangeNotifierProvider.value(value: serviceLocator<TUIThemeViewModel>())], providers: [
ChangeNotifierProvider.value(value: widget.model),
ChangeNotifierProvider.value(
value: serviceLocator<TUIThemeViewModel>())
],
builder: (context, w) { builder: (context, w) {
final memberList = Provider.of<TUIGroupProfileModel>(context).groupMemberList; final memberList =
Provider.of<TUIGroupProfileModel>(context).groupMemberList;
final theme = Provider.of<TUIThemeViewModel>(context).theme; final theme = Provider.of<TUIThemeViewModel>(context).theme;
final isAllMuted = widget.model.groupInfo?.isAllMuted ?? false; final isAllMuted = widget.model.groupInfo?.isAllMuted ?? false;
final bool isAllowMuteMember = (widget.model.groupInfo?.groupType ?? "") != GroupType.Work; final bool isAllowMuteMember =
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; (widget.model.groupInfo?.groupType ?? "") != GroupType.Work;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
Widget managePage() { Widget managePage() {
return Column( return Column(
children: [ children: [
Container( Container(
padding: EdgeInsets.only(top: 12, left: isDesktopScreen ? 0 : 16, bottom: isDesktopScreen ? 0 : 12, right: isDesktopScreen ? 0 : 12), padding: EdgeInsets.only(
decoration: BoxDecoration(color: Colors.white, border: isDesktopScreen ? null : Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), top: 12,
left: isDesktopScreen ? 0 : 16,
bottom: isDesktopScreen ? 0 : 12,
right: isDesktopScreen ? 0 : 12),
decoration: BoxDecoration(
color: Colors.white,
border: isDesktopScreen
? null
: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
child: InkWell( child: InkWell(
onTap: isDesktopScreen onTap: isDesktopScreen
? null ? null
@ -134,7 +167,8 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => GroupProfileSetManagerPage( builder: (context) =>
GroupProfileSetManagerPage(
model: widget.model, model: widget.model,
), ),
)); ));
@ -143,12 +177,22 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(TIM_t("群管理员"), style: TextStyle(fontSize: 14, color: theme.darkTextColor)), Text(TIM_t("群管理员"),
style: TextStyle(
fontSize: 14,
color: theme.darkTextColor)),
], ],
) )
: Row( : Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text(TIM_t("设置管理员"), style: TextStyle(fontSize: isDesktopScreen ? 14 : 16, color: theme.darkTextColor)), Icon(Icons.keyboard_arrow_right, color: theme.weakTextColor)], children: [
Text(TIM_t("设置管理员"),
style: TextStyle(
fontSize: isDesktopScreen ? 14 : 16,
color: theme.darkTextColor)),
Icon(Icons.keyboard_arrow_right,
color: theme.weakTextColor)
],
), ),
), ),
), ),
@ -158,14 +202,21 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
), ),
if (!isDesktopScreen) if (!isDesktopScreen)
Container( Container(
padding: const EdgeInsets.only(top: 12, left: 16, bottom: 12, right: 12), padding: const EdgeInsets.only(
decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), top: 12, left: 16, bottom: 12, right: 12),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
TIM_t("全员禁言"), TIM_t("全员禁言"),
style: TextStyle(fontSize: 16, color: theme.darkTextColor), style: TextStyle(
fontSize: 16, color: theme.darkTextColor),
), ),
CupertinoSwitch( CupertinoSwitch(
value: isAllMuted, value: isAllMuted,
@ -180,7 +231,9 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(TIM_t("禁言"), style: TextStyle(fontSize: 14, color: theme.darkTextColor)), Text(TIM_t("禁言"),
style: TextStyle(
fontSize: 14, color: theme.darkTextColor)),
], ],
), ),
if (isDesktopScreen) if (isDesktopScreen)
@ -200,12 +253,14 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
), ),
if (!isDesktopScreen) if (!isDesktopScreen)
Container( Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 16),
color: theme.weakBackgroundColor, color: theme.weakBackgroundColor,
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Text( child: Text(
TIM_t("全员禁言开启后,只允许群主和管理员发言。"), TIM_t("全员禁言开启后,只允许群主和管理员发言。"),
style: TextStyle(fontSize: 12, color: theme.weakTextColor), style:
TextStyle(fontSize: 12, color: theme.weakTextColor),
), ),
), ),
if (!isAllMuted && isAllowMuteMember) if (!isAllMuted && isAllowMuteMember)
@ -221,7 +276,14 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
: const EdgeInsets.only( : const EdgeInsets.only(
bottom: 4, bottom: 4,
), ),
decoration: isDesktopScreen ? null : BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), decoration: isDesktopScreen
? null
: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
child: Row( child: Row(
children: [ children: [
Icon( Icon(
@ -242,15 +304,21 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
key: groupProfileAddAdminKey, key: groupProfileAddAdminKey,
appbarTitle: TIM_t("设置禁言"), appbarTitle: TIM_t("设置禁言"),
memberList: memberList.where((element) { memberList: memberList.where((element) {
final isMute = (serverTime != null ? (element?.muteUntil ?? 0) > serverTime! : false); final isMute = (serverTime != null
final isMember = element!.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER; ? (element?.muteUntil ?? 0) > serverTime!
: false);
final isMember = element!.role ==
GroupMemberRoleType
.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
return !isMute && isMember; return !isMute && isMember;
}).toList(), }).toList(),
selectCompletedHandler: (context, selectedMember) async { selectCompletedHandler:
(context, selectedMember) async {
if (selectedMember.isNotEmpty) { if (selectedMember.isNotEmpty) {
for (var member in selectedMember) { for (var member in selectedMember) {
final userID = member!.userID; final userID = member!.userID;
widget.model.muteGroupMember(userID, true, serverTime); widget.model
.muteGroupMember(userID, true, serverTime);
} }
} }
}, },
@ -269,29 +337,48 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
}, },
child: (onClose) => muteMember()); child: (onClose) => muteMember());
} else { } else {
Navigator.push(context, MaterialPageRoute(builder: (context) => muteMember())); Navigator.push(
context,
MaterialPageRoute(
builder: (context) => muteMember()));
} }
}, },
), ),
if (!isAllMuted && isAllowMuteMember) if (!isAllMuted && isAllowMuteMember)
...memberList ...memberList
.where((element) => (serverTime != null ? (element?.muteUntil ?? 0) > serverTime! : false)) .where((element) => (serverTime != null
? (element?.muteUntil ?? 0) > serverTime!
: false))
.map((e) => Container( .map((e) => Container(
padding: isDesktopScreen ? const EdgeInsets.only(left: 16) : null, padding: isDesktopScreen
? const EdgeInsets.only(left: 16)
: null,
child: GestureDetector( child: GestureDetector(
onSecondaryTapDown: (details) { onSecondaryTapDown: (details) {
TUIKitWidePopup.showPopupWindow( TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.setUnmute, operationKey:
TUIKitWideModalOperationKey.setUnmute,
isDarkBackground: false, isDarkBackground: false,
borderRadius: const BorderRadius.all(Radius.circular(4)), borderRadius: const BorderRadius.all(
Radius.circular(4)),
context: context, context: context,
offset: Offset(min(details.globalPosition.dx, MediaQuery.of(context).size.width - 80), details.globalPosition.dy), offset: Offset(
min(
details.globalPosition.dx,
MediaQuery.of(context).size.width -
80),
details.globalPosition.dy),
child: (onClose) => TUIKitColumnMenu(data: [ child: (onClose) => TUIKitColumnMenu(data: [
ColumnMenuItem( ColumnMenuItem(
label: TIM_t("删除"), label: TIM_t("删除"),
icon: const Icon(Icons.remove_circle_outline, size: 16), icon: const Icon(
Icons.remove_circle_outline,
size: 16),
onClick: () { onClick: () {
widget.model.muteGroupMember(e.userID, false, serverTime); widget.model.muteGroupMember(
e.userID,
false,
serverTime);
onClose(); onClose();
}), }),
])); ]));
@ -299,17 +386,21 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
child: _buildListItem( child: _buildListItem(
context, context,
e!, e!,
ActionPane(motion: const DrawerMotion(), children: [ ActionPane(
SlidableAction( motion: const DrawerMotion(),
onPressed: (_) { children: [
widget.model.muteGroupMember(e.userID, false, serverTime); SlidableAction(
}, onPressed: (_) {
flex: 1, widget.model.muteGroupMember(
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor, e.userID, false, serverTime);
autoClose: true, },
label: TIM_t("删除"), flex: 1,
) backgroundColor: theme.cautionColor ??
])), CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
])),
), ),
)) ))
.toList() .toList()
@ -324,7 +415,8 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
TIM_t("群管理"), TIM_t("群管理"),
style: TextStyle(color: theme.appbarTextColor, fontSize: 17), style:
TextStyle(color: theme.appbarTextColor, fontSize: 17),
), ),
backgroundColor: theme.appbarBgColor ?? theme.primaryColor, backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
shadowColor: theme.weakDividerColor, shadowColor: theme.weakDividerColor,
@ -369,9 +461,11 @@ _getShowName(V2TimGroupMemberFullInfo? item) {
: userID; : userID;
} }
Widget _buildListItem(BuildContext context, V2TimGroupMemberFullInfo memberInfo, ActionPane? endActionPane) { Widget _buildListItem(BuildContext context, V2TimGroupMemberFullInfo memberInfo,
ActionPane? endActionPane) {
final theme = Provider.of<TUIThemeViewModel>(context).theme; final theme = Provider.of<TUIThemeViewModel>(context).theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
Widget nameItem() { Widget nameItem() {
return Container( return Container(
@ -390,42 +484,67 @@ Widget _buildListItem(BuildContext context, V2TimGroupMemberFullInfo memberInfo,
), ),
title: Row( title: Row(
children: [ children: [
Text(_getShowName(memberInfo), style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)), Text(_getShowName(memberInfo),
style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)),
], ],
), ),
onTap: () {}, onTap: () {},
), ),
if (!isDesktopScreen) Divider(thickness: 1, indent: 74, endIndent: 0, color: theme.weakDividerColor, height: 0) if (!isDesktopScreen)
Divider(
thickness: 1,
indent: 74,
endIndent: 0,
color: theme.weakDividerColor,
height: 0)
]), ]),
); );
} }
return TUIKitScreenUtils.getDeviceWidget(context: context, desktopWidget: nameItem(), defaultWidget: SingleChildScrollView(child: Slidable(endActionPane: endActionPane, child: nameItem()))); return TUIKitScreenUtils.getDeviceWidget(
context: context,
desktopWidget: nameItem(),
defaultWidget: SingleChildScrollView(
child: Slidable(endActionPane: endActionPane, child: nameItem())));
} }
/// ///
class GroupProfileSetManagerPage extends StatefulWidget { class GroupProfileSetManagerPage extends StatefulWidget {
final TUIGroupProfileModel model; final TUIGroupProfileModel model;
const GroupProfileSetManagerPage({Key? key, required this.model}) : super(key: key); const GroupProfileSetManagerPage({Key? key, required this.model})
: super(key: key);
@override @override
State<StatefulWidget> createState() => _GroupProfileSetManagerPageState(); State<StatefulWidget> createState() => _GroupProfileSetManagerPageState();
} }
class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetManagerPage> { class _GroupProfileSetManagerPageState
List<V2TimGroupMemberFullInfo?> _getAdminMemberList(List<V2TimGroupMemberFullInfo?> memberList) { extends TIMUIKitState<GroupProfileSetManagerPage> {
return memberList.where((member) => member?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN).toList(); List<V2TimGroupMemberFullInfo?> _getAdminMemberList(
List<V2TimGroupMemberFullInfo?> memberList) {
return memberList
.where((member) =>
member?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN)
.toList();
} }
List<V2TimGroupMemberFullInfo?> _getOwnerList(List<V2TimGroupMemberFullInfo?> memberList) { List<V2TimGroupMemberFullInfo?> _getOwnerList(
return memberList.where((member) => member?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER).toList(); List<V2TimGroupMemberFullInfo?> memberList) {
return memberList
.where((member) =>
member?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER)
.toList();
} }
_removeAdmin(BuildContext context, V2TimGroupMemberFullInfo memberFullInfo) async { _removeAdmin(
BuildContext context, V2TimGroupMemberFullInfo memberFullInfo) async {
final res = await widget.model.setMemberToNormal(memberFullInfo.userID); final res = await widget.model.setMemberToNormal(memberFullInfo.userID);
if (res.code == 0) { if (res.code == 0) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("成功取消管理员身份"), infoCode: 6661003)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("成功取消管理员身份"),
infoCode: 6661003));
} }
} }
@ -441,7 +560,8 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
final adminList = _getAdminMemberList(memberList); final adminList = _getAdminMemberList(memberList);
final ownerList = _getOwnerList(memberList); final ownerList = _getOwnerList(memberList);
final String option2 = adminList.length.toString(); final String option2 = adminList.length.toString();
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
Widget adminPage() { Widget adminPage() {
return SingleChildScrollView( return SingleChildScrollView(
@ -451,7 +571,8 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
Container( Container(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
color: theme.weakDividerColor, color: theme.weakDividerColor,
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), padding:
const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
child: Text( child: Text(
TIM_t("群主"), TIM_t("群主"),
style: TextStyle(fontSize: 14, color: theme.weakTextColor), style: TextStyle(fontSize: 14, color: theme.weakTextColor),
@ -469,7 +590,9 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
...ownerList ...ownerList
.map( .map(
(e) => Container( (e) => Container(
padding: isDesktopScreen ? const EdgeInsets.only(left: 16) : null, padding: isDesktopScreen
? const EdgeInsets.only(left: 16)
: null,
child: _buildListItem(context, e!, null), child: _buildListItem(context, e!, null),
), ),
) )
@ -478,9 +601,11 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
Container( Container(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
color: theme.weakDividerColor, color: theme.weakDividerColor,
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), padding:
const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
child: Text( child: Text(
TIM_t_para("管理员 ({{option2}}/10)", "管理员 ($option2/10)")(option2: option2), TIM_t_para("管理员 ({{option2}}/10)", "管理员 ($option2/10)")(
option2: option2),
style: TextStyle(fontSize: 14, color: theme.weakTextColor), style: TextStyle(fontSize: 14, color: theme.weakTextColor),
), ),
), ),
@ -489,7 +614,8 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
padding: const EdgeInsets.only(top: 10, bottom: 4, left: 16), padding: const EdgeInsets.only(top: 10, bottom: 4, left: 16),
child: Text( child: Text(
TIM_t_para("管理员 ({{option2}}/10)", "管理员 ($option2/10)")(option2: option2), TIM_t_para("管理员 ({{option2}}/10)", "管理员 ($option2/10)")(
option2: option2),
style: TextStyle(fontSize: 14, color: theme.primaryColor), style: TextStyle(fontSize: 14, color: theme.primaryColor),
), ),
), ),
@ -501,7 +627,14 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 12, vertical: 12,
), ),
decoration: isDesktopScreen ? null : BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), decoration: isDesktopScreen
? null
: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
child: Row( child: Row(
children: [ children: [
Icon( Icon(
@ -529,9 +662,15 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
}, },
child: (onClose) => GroupProfileAddAdmin( child: (onClose) => GroupProfileAddAdmin(
key: groupProfileAddAdminKey, key: groupProfileAddAdminKey,
memberList: memberList.where((element) => element?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER).toList(), memberList: memberList
.where((element) =>
element?.role ==
GroupMemberRoleType
.V2TIM_GROUP_MEMBER_ROLE_MEMBER)
.toList(),
appbarTitle: TIM_t("设置管理员"), appbarTitle: TIM_t("设置管理员"),
selectCompletedHandler: (context, selectedMember) async { selectCompletedHandler:
(context, selectedMember) async {
if (selectedMember.isNotEmpty) { if (selectedMember.isNotEmpty) {
for (var member in selectedMember) { for (var member in selectedMember) {
final userID = member!.userID; final userID = member!.userID;
@ -546,9 +685,15 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
MaterialPageRoute( MaterialPageRoute(
builder: (context) => GroupProfileAddAdmin( builder: (context) => GroupProfileAddAdmin(
key: groupProfileAddAdminKey, key: groupProfileAddAdminKey,
memberList: memberList.where((element) => element?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER).toList(), memberList: memberList
.where((element) =>
element?.role ==
GroupMemberRoleType
.V2TIM_GROUP_MEMBER_ROLE_MEMBER)
.toList(),
appbarTitle: TIM_t("设置管理员"), appbarTitle: TIM_t("设置管理员"),
selectCompletedHandler: (context, selectedMember) async { selectCompletedHandler:
(context, selectedMember) async {
if (selectedMember.isNotEmpty) { if (selectedMember.isNotEmpty) {
for (var member in selectedMember) { for (var member in selectedMember) {
final userID = member!.userID; final userID = member!.userID;
@ -564,15 +709,22 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
.map((e) => GestureDetector( .map((e) => GestureDetector(
onSecondaryTapDown: (details) { onSecondaryTapDown: (details) {
TUIKitWidePopup.showPopupWindow( TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.deleteAdmin, operationKey:
TUIKitWideModalOperationKey.deleteAdmin,
isDarkBackground: false, isDarkBackground: false,
borderRadius: const BorderRadius.all(Radius.circular(4)), borderRadius:
const BorderRadius.all(Radius.circular(4)),
context: context, context: context,
offset: Offset(min(details.globalPosition.dx, MediaQuery.of(context).size.width - 80), details.globalPosition.dy), offset: Offset(
min(details.globalPosition.dx,
MediaQuery.of(context).size.width - 80),
details.globalPosition.dy),
child: (onClose) => TUIKitColumnMenu(data: [ child: (onClose) => TUIKitColumnMenu(data: [
ColumnMenuItem( ColumnMenuItem(
label: TIM_t("删除"), label: TIM_t("删除"),
icon: const Icon(Icons.remove_circle_outline, size: 16), icon: const Icon(
Icons.remove_circle_outline,
size: 16),
onClick: () { onClick: () {
_removeAdmin(context, e); _removeAdmin(context, e);
onClose(); onClose();
@ -580,21 +732,26 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
])); ]));
}, },
child: Container( child: Container(
padding: isDesktopScreen ? const EdgeInsets.only(left: 16) : null, padding: isDesktopScreen
? const EdgeInsets.only(left: 16)
: null,
child: _buildListItem( child: _buildListItem(
context, context,
e!, e!,
ActionPane(motion: const DrawerMotion(), children: [ ActionPane(
SlidableAction( motion: const DrawerMotion(),
onPressed: (_) { children: [
_removeAdmin(context, e); SlidableAction(
}, onPressed: (_) {
flex: 1, _removeAdmin(context, e);
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor, },
autoClose: true, flex: 1,
label: TIM_t("删除"), backgroundColor: theme.cautionColor ??
) CommonColor.cautionColor,
])), autoClose: true,
label: TIM_t("删除"),
)
])),
), ),
)) ))
.toList(), .toList(),
@ -628,9 +785,16 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
class GroupProfileAddAdmin extends StatefulWidget { class GroupProfileAddAdmin extends StatefulWidget {
final List<V2TimGroupMemberFullInfo?> memberList; final List<V2TimGroupMemberFullInfo?> memberList;
final String appbarTitle; final String appbarTitle;
final void Function(BuildContext context, List<V2TimGroupMemberFullInfo?> selectedMemberList)? selectCompletedHandler; final void Function(BuildContext context,
List<V2TimGroupMemberFullInfo?> selectedMemberList)?
selectCompletedHandler;
const GroupProfileAddAdmin({Key? key, required this.memberList, this.selectCompletedHandler, required this.appbarTitle}) : super(key: key); const GroupProfileAddAdmin(
{Key? key,
required this.memberList,
this.selectCompletedHandler,
required this.appbarTitle})
: super(key: key);
@override @override
State<StatefulWidget> createState() => _GroupProfileAddAdminState(); State<StatefulWidget> createState() => _GroupProfileAddAdminState();
@ -664,8 +828,14 @@ class _GroupProfileAddAdminState extends TIMUIKitState<GroupProfileAddAdmin> {
), ),
...widget.memberList ...widget.memberList
.map((e) => Container( .map((e) => Container(
decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))), decoration: BoxDecoration(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), color: Colors.white,
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 16),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
final isChecked = selectedMemberList.contains(e); final isChecked = selectedMemberList.contains(e);
@ -697,7 +867,8 @@ class _GroupProfileAddAdminState extends TIMUIKitState<GroupProfileAddAdmin> {
const SizedBox( const SizedBox(
width: 10, width: 10,
), ),
Text(_getShowName(e), style: const TextStyle(fontSize: 16)) Text(_getShowName(e),
style: const TextStyle(fontSize: 16))
], ],
), ),
), ),

View File

@ -2,7 +2,7 @@
import 'package:azlistview_all_platforms/azlistview_all_platforms.dart'; import 'package:azlistview_all_platforms/azlistview_all_platforms.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable_plus_plus/flutter_slidable_plus_plus.dart';
import 'package:lpinyin/lpinyin.dart'; import 'package:lpinyin/lpinyin.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
@ -24,9 +24,12 @@ class GroupProfileMemberList extends StatefulWidget {
// when the @ need filter some group types // when the @ need filter some group types
final String? groupType; final String? groupType;
final Function(List<V2TimGroupMemberFullInfo> selectedMember)? onSelectedMemberChange; final Function(List<V2TimGroupMemberFullInfo> selectedMember)?
onSelectedMemberChange;
// notice: onTapMemberItem and onSelectedMemberChange use together will triger together // notice: onTapMemberItem and onSelectedMemberChange use together will triger together
final Function(V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails)? onTapMemberItem; final Function(
V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails)?
onTapMemberItem;
// When sliding to the bottom bar callBack // When sliding to the bottom bar callBack
final Function()? touchBottomCallBack; final Function()? touchBottomCallBack;
@ -53,7 +56,8 @@ class GroupProfileMemberList extends StatefulWidget {
State<StatefulWidget> createState() => _GroupProfileMemberListState(); State<StatefulWidget> createState() => _GroupProfileMemberListState();
} }
class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList> { class _GroupProfileMemberListState
extends TIMUIKitState<GroupProfileMemberList> {
List<V2TimGroupMemberFullInfo> selectedMemberList = []; List<V2TimGroupMemberFullInfo> selectedMemberList = [];
_getShowName(V2TimGroupMemberFullInfo? item) { _getShowName(V2TimGroupMemberFullInfo? item) {
@ -70,12 +74,14 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
: userID; : userID;
} }
List<ISuspensionBeanImpl> _getShowList(List<V2TimGroupMemberFullInfo?> memberList) { List<ISuspensionBeanImpl> _getShowList(
List<V2TimGroupMemberFullInfo?> memberList) {
final List<ISuspensionBeanImpl> showList = List.empty(growable: true); final List<ISuspensionBeanImpl> showList = List.empty(growable: true);
for (var i = 0; i < memberList.length; i++) { for (var i = 0; i < memberList.length; i++) {
final item = memberList[i]; final item = memberList[i];
final showName = _getShowName(item); final showName = _getShowName(item);
if (item?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER || item?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN) { if (item?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER ||
item?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN) {
showList.add(ISuspensionBeanImpl(memberInfo: item, tagIndex: "@")); showList.add(ISuspensionBeanImpl(memberInfo: item, tagIndex: "@"));
} else { } else {
String pinyin = PinyinHelper.getPinyinE(showName); String pinyin = PinyinHelper.getPinyinE(showName);
@ -94,17 +100,26 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
if (widget.canAtAll) { if (widget.canAtAll) {
final canAtGroupType = ["Work", "Public", "Meeting"]; final canAtGroupType = ["Work", "Public", "Meeting"];
if (canAtGroupType.contains(widget.groupType)) { if (canAtGroupType.contains(widget.groupType)) {
showList.insert(0, ISuspensionBeanImpl(memberInfo: V2TimGroupMemberFullInfo(userID: GroupProfileMemberList.AT_ALL_USER_ID, nickName: TIM_t("所有人")), tagIndex: "")); showList.insert(
0,
ISuspensionBeanImpl(
memberInfo: V2TimGroupMemberFullInfo(
userID: GroupProfileMemberList.AT_ALL_USER_ID,
nickName: TIM_t("所有人")),
tagIndex: ""));
} }
} }
return showList; return showList;
} }
Widget _buildListItem(BuildContext context, V2TimGroupMemberFullInfo memberInfo) { Widget _buildListItem(
BuildContext context, V2TimGroupMemberFullInfo memberInfo) {
final theme = Provider.of<TUIThemeViewModel>(context).theme; final theme = Provider.of<TUIThemeViewModel>(context).theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop; final isDesktopScreen =
final isGroupMember = memberInfo.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER; TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
final isGroupMember =
memberInfo.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
return Container( return Container(
color: Colors.white, color: Colors.white,
child: Slidable( child: Slidable(
@ -117,7 +132,8 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
} }
}, },
flex: 1, flex: 1,
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor, backgroundColor:
theme.cautionColor ?? CommonColor.cautionColor,
autoClose: true, autoClose: true,
label: TIM_t("删除"), label: TIM_t("删除"),
) )
@ -134,21 +150,28 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
child: CheckBoxButton( child: CheckBoxButton(
onChanged: (isChecked) { onChanged: (isChecked) {
if (isChecked) { if (isChecked) {
if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) { if (widget.maxSelectNum != null &&
selectedMemberList.length >=
widget.maxSelectNum!) {
return; return;
} }
selectedMemberList.add(memberInfo); selectedMemberList.add(memberInfo);
} else { } else {
selectedMemberList.removeWhere((element) => element.userID == memberInfo.userID); selectedMemberList.removeWhere((element) =>
element.userID == memberInfo.userID);
} }
if (widget.onSelectedMemberChange != null) { if (widget.onSelectedMemberChange != null) {
widget.onSelectedMemberChange!(selectedMemberList); widget.onSelectedMemberChange!(
selectedMemberList);
} }
setState(() {}); setState(() {});
}, },
isChecked: selectedMemberList.where((element) => element.userID == memberInfo.userID).toList().isNotEmpty isChecked: selectedMemberList
), .where((element) =>
element.userID == memberInfo.userID)
.toList()
.isNotEmpty),
), ),
Container( Container(
width: isDesktopScreen ? 30 : 36, width: isDesktopScreen ? 30 : 36,
@ -160,8 +183,10 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
type: 1, type: 1,
), ),
), ),
Text(_getShowName(memberInfo), style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)), Text(_getShowName(memberInfo),
memberInfo.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)),
memberInfo.role ==
GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER
? Container( ? Container(
margin: const EdgeInsets.only(left: 5), margin: const EdgeInsets.only(left: 5),
child: Text(TIM_t("群主"), child: Text(TIM_t("群主"),
@ -171,11 +196,17 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
)), )),
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0), padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: theme.ownerColor ?? CommonColor.ownerColor, width: 1), border: Border.all(
borderRadius: const BorderRadius.all(Radius.circular(4.0)), color: theme.ownerColor ??
CommonColor.ownerColor,
width: 1),
borderRadius:
const BorderRadius.all(Radius.circular(4.0)),
), ),
) )
: memberInfo.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN : memberInfo.role ==
GroupMemberRoleType
.V2TIM_GROUP_MEMBER_ROLE_ADMIN
? Container( ? Container(
margin: const EdgeInsets.only(left: 5), margin: const EdgeInsets.only(left: 5),
child: Text(TIM_t("管理员"), child: Text(TIM_t("管理员"),
@ -185,8 +216,12 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
)), )),
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0), padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: theme.adminColor ?? CommonColor.adminColor, width: 1), border: Border.all(
borderRadius: const BorderRadius.all(Radius.circular(4.0)), color: theme.adminColor ??
CommonColor.adminColor,
width: 1),
borderRadius: const BorderRadius.all(
Radius.circular(4.0)),
), ),
) )
: Container() : Container()
@ -201,7 +236,8 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
if (isChecked) { if (isChecked) {
selectedMemberList.remove(memberInfo); selectedMemberList.remove(memberInfo);
} else { } else {
if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) { if (widget.maxSelectNum != null &&
selectedMemberList.length >= widget.maxSelectNum!) {
return; return;
} }
selectedMemberList.add(memberInfo); selectedMemberList.add(memberInfo);
@ -213,11 +249,17 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
} }
}, },
), ),
Divider(thickness: 1, indent: 74, endIndent: 0, color: theme.weakBackgroundColor, height: 0) Divider(
thickness: 1,
indent: 74,
endIndent: 0,
color: theme.weakBackgroundColor,
height: 0)
]))); ])));
} }
static Widget getSusItem(BuildContext context, TUITheme theme, String tag, {double susHeight = 40}) { static Widget getSusItem(BuildContext context, TUITheme theme, String tag,
{double susHeight = 40}) {
if (tag == '@') { if (tag == '@') {
tag = TIM_t("群主、管理员"); tag = TIM_t("群主、管理员");
} }
@ -242,9 +284,11 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme; final TUITheme theme = value.theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop; final isDesktopScreen =
TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
final throteFunction = OptimizeUtils.throttle((ScrollNotification notification) { final throteFunction =
OptimizeUtils.throttle((ScrollNotification notification) {
final pixels = notification.metrics.pixels; final pixels = notification.metrics.pixels;
// //
final maxScrollExtent = notification.metrics.maxScrollExtent; final maxScrollExtent = notification.metrics.maxScrollExtent;
@ -272,15 +316,19 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
child: Text(TIM_t("暂无群成员")), child: Text(TIM_t("暂无群成员")),
) )
: Container( : Container(
padding: isDesktopScreen ? const EdgeInsets.symmetric(horizontal: 16) : null, padding: isDesktopScreen
? const EdgeInsets.symmetric(horizontal: 16)
: null,
child: AZListViewContainer( child: AZListViewContainer(
memberList: showList, memberList: showList,
susItemBuilder: (context, index) { susItemBuilder: (context, index) {
final model = showList[index]; final model = showList[index];
return getSusItem(context, theme, model.getSuspensionTag()); return getSusItem(
context, theme, model.getSuspensionTag());
}, },
itemBuilder: (context, index) { itemBuilder: (context, index) {
final memberInfo = showList[index].memberInfo as V2TimGroupMemberFullInfo; final memberInfo = showList[index].memberInfo
as V2TimGroupMemberFullInfo;
return _buildListItem(context, memberInfo); return _buildListItem(context, memberInfo);
}), }),

View File

@ -8,7 +8,7 @@ import 'package:device_info_plus/device_info_plus.dart';
import 'package:extended_image/extended_image.dart'; import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
@ -21,7 +21,12 @@ import 'package:universal_html/html.dart' as html;
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
class VideoScreen extends StatefulWidget { class VideoScreen extends StatefulWidget {
const VideoScreen({required this.message, required this.heroTag, required this.videoElement, Key? key}) : super(key: key); const VideoScreen(
{required this.message,
required this.heroTag,
required this.videoElement,
Key? key})
: super(key: key);
final V2TimMessage message; final V2TimMessage message;
final dynamic heroTag; final dynamic heroTag;
@ -34,7 +39,8 @@ class VideoScreen extends StatefulWidget {
class _VideoScreenState extends TIMUIKitState<VideoScreen> { class _VideoScreenState extends TIMUIKitState<VideoScreen> {
late VideoPlayerController videoPlayerController; late VideoPlayerController videoPlayerController;
late ChewieController chewieController; late ChewieController chewieController;
GlobalKey<ExtendedImageSlidePageState> slidePagekey = GlobalKey<ExtendedImageSlidePageState>(); GlobalKey<ExtendedImageSlidePageState> slidePagekey =
GlobalKey<ExtendedImageSlidePageState>();
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
bool isInit = false; bool isInit = false;
@ -64,7 +70,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
xhr.open('get', videoUrl); xhr.open('get', videoUrl);
xhr.responseType = 'arraybuffer'; xhr.responseType = 'arraybuffer';
xhr.onLoad.listen((event) { xhr.onLoad.listen((event) {
final a = html.AnchorElement(href: html.Url.createObjectUrl(html.Blob([xhr.response]))); final a = html.AnchorElement(
href: html.Url.createObjectUrl(html.Blob([xhr.response])));
a.download = '${md5.convert(utf8.encode(videoUrl)).toString()}$suffix'; a.download = '${md5.convert(utf8.encode(videoUrl)).toString()}$suffix';
a.click(); a.click();
a.remove(); a.remove();
@ -110,7 +117,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
} }
if (model.getMessageProgress(widget.message.msgID) == 100) { if (model.getMessageProgress(widget.message.msgID) == 100) {
String savePath; String savePath;
if (widget.message.videoElem!.localVideoUrl != null && widget.message.videoElem!.localVideoUrl != '') { if (widget.message.videoElem!.localVideoUrl != null &&
widget.message.videoElem!.localVideoUrl != '') {
savePath = widget.message.videoElem!.localVideoUrl!; savePath = widget.message.videoElem!.localVideoUrl!;
} else { } else {
savePath = model.getFileMessageLocation(widget.message.msgID); savePath = model.getFileMessageLocation(widget.message.msgID);
@ -120,35 +128,62 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
var result = await ImageGallerySaver.saveFile(savePath); var result = await ImageGallerySaver.saveFile(savePath);
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (result['isSuccess']) { if (result['isSuccess']) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} else { } else {
if (result != null) { if (result != null) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} }
} }
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("the message is downloading"), infoCode: -1)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("the message is downloading"),
infoCode: -1));
} }
return; return;
} }
var result = await ImageGallerySaver.saveFile(savePath); var result = await ImageGallerySaver.saveFile(savePath);
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (result['isSuccess']) { if (result['isSuccess']) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} else { } else {
if (result != null) { if (result != null) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} }
return; return;
@ -162,7 +197,9 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
isAsset: true, isAsset: true,
); );
} }
if (widget.videoElement.videoPath != '' && widget.videoElement.videoPath != null && File(widget.videoElement.videoPath!).existsSync()) { if (widget.videoElement.videoPath != '' &&
widget.videoElement.videoPath != null &&
File(widget.videoElement.videoPath!).existsSync()) {
File f = File(widget.videoElement.videoPath!); File f = File(widget.videoElement.videoPath!);
if (f.existsSync()) { if (f.existsSync()) {
return await _saveNetworkVideo( return await _saveNetworkVideo(
@ -172,7 +209,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
); );
} }
} }
if (widget.videoElement.localVideoUrl != '' && widget.videoElement.localVideoUrl != null) { if (widget.videoElement.localVideoUrl != '' &&
widget.videoElement.localVideoUrl != null) {
File f = File(widget.videoElement.localVideoUrl!); File f = File(widget.videoElement.localVideoUrl!);
if (f.existsSync()) { if (f.existsSync()) {
return await _saveNetworkVideo( return await _saveNetworkVideo(
@ -211,7 +249,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
setVideoPlayerController() async { setVideoPlayerController() async {
if (!PlatformUtils().isWeb) { if (!PlatformUtils().isWeb) {
if (TencentUtils.checkString(widget.message.msgID) != null && widget.videoElement.localVideoUrl == null) { if (TencentUtils.checkString(widget.message.msgID) != null &&
widget.videoElement.localVideoUrl == null) {
String savePath = model.getFileMessageLocation(widget.message.msgID); String savePath = model.getFileMessageLocation(widget.message.msgID);
File f = File(savePath); File f = File(savePath);
if (f.existsSync()) { if (f.existsSync()) {
@ -221,20 +260,26 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
} }
VideoPlayerController player = PlatformUtils().isWeb VideoPlayerController player = PlatformUtils().isWeb
? ((TencentUtils.checkString(widget.videoElement.videoPath) != null) || widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING ? ((TencentUtils.checkString(widget.videoElement.videoPath) != null) ||
widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING
? VideoPlayerController.networkUrl( ? VideoPlayerController.networkUrl(
Uri.parse(widget.videoElement.videoPath!), Uri.parse(widget.videoElement.videoPath!),
) )
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null) : (TencentUtils.checkString(widget.videoElement.localVideoUrl) ==
null)
? VideoPlayerController.networkUrl( ? VideoPlayerController.networkUrl(
Uri.parse(widget.videoElement.videoUrl!), Uri.parse(widget.videoElement.videoUrl!),
) )
: VideoPlayerController.networkUrl( : VideoPlayerController.networkUrl(
Uri.parse(widget.videoElement.localVideoUrl!), Uri.parse(widget.videoElement.localVideoUrl!),
)) ))
: ((TencentUtils.checkString(widget.videoElement.videoPath) != null || widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING) && File(widget.videoElement.videoPath!).existsSync()) : ((TencentUtils.checkString(widget.videoElement.videoPath) != null ||
widget.message.status ==
MessageStatus.V2TIM_MSG_STATUS_SENDING) &&
File(widget.videoElement.videoPath!).existsSync())
? VideoPlayerController.file(File(widget.videoElement.videoPath!)) ? VideoPlayerController.file(File(widget.videoElement.videoPath!))
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null) : (TencentUtils.checkString(widget.videoElement.localVideoUrl) ==
null)
? VideoPlayerController.networkUrl( ? VideoPlayerController.networkUrl(
Uri.parse(widget.videoElement.videoUrl!), Uri.parse(widget.videoElement.videoUrl!),
) )
@ -264,7 +309,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
@override @override
didUpdateWidget(oldWidget) { didUpdateWidget(oldWidget) {
if (oldWidget.videoElement.videoUrl != widget.videoElement.videoUrl || oldWidget.videoElement.videoPath != widget.videoElement.videoPath) { if (oldWidget.videoElement.videoUrl != widget.videoElement.videoUrl ||
oldWidget.videoElement.videoPath != widget.videoElement.videoPath) {
setVideoPlayerController(); setVideoPlayerController();
} }
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
@ -299,8 +345,10 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
return Colors.black; return Colors.black;
} }
double opacity = 0.0; double opacity = 0.0;
opacity = offset.distance / (Offset(size.width, size.height).distance / 2.0); opacity = offset.distance /
return Colors.black.withOpacity(min(1.0, max(1.0 - opacity, 0.0))); (Offset(size.width, size.height).distance / 2.0);
return Colors.black
.withOpacity(min(1.0, max(1.0 - opacity, 0.0)));
}, },
slideType: SlideType.onlyImage, slideType: SlideType.onlyImage,
slideEndHandler: ( slideEndHandler: (
@ -322,13 +370,22 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
? Chewie( ? Chewie(
controller: chewieController, controller: chewieController,
) )
: const Center(child: CircularProgressIndicator(color: Colors.white))), : const Center(
child: CircularProgressIndicator(
color: Colors.white))),
heroBuilderForSlidingPage: (Widget result) { heroBuilderForSlidingPage: (Widget result) {
return Hero( return Hero(
tag: widget.heroTag, tag: widget.heroTag,
child: result, child: result,
flightShuttleBuilder: (BuildContext flightContext, Animation<double> animation, HeroFlightDirection flightDirection, BuildContext fromHeroContext, BuildContext toHeroContext) { flightShuttleBuilder: (BuildContext flightContext,
final Hero hero = (flightDirection == HeroFlightDirection.pop ? fromHeroContext.widget : toHeroContext.widget) as Hero; Animation<double> animation,
HeroFlightDirection flightDirection,
BuildContext fromHeroContext,
BuildContext toHeroContext) {
final Hero hero =
(flightDirection == HeroFlightDirection.pop
? fromHeroContext.widget
: toHeroContext.widget) as Hero;
return hero.child; return hero.child;
}, },

View File

@ -1,6 +1,6 @@
name: tencent_cloud_chat_uikit name: tencent_cloud_chat_uikit
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences. description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
version: 3.1.0 version: 3.1.0+2
homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn
repository: https://github.com/TencentCloud/chat-uikit-flutter repository: https://github.com/TencentCloud/chat-uikit-flutter
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
@ -30,12 +30,12 @@ dependencies:
tencent_super_tooltip: ^0.0.1 tencent_super_tooltip: ^0.0.1
video_player: ^2.9.0 video_player: ^2.9.0
chewie: ^1.8.5 chewie: ^1.8.5
flutter_slidable: ^3.0.1 flutter_slidable_plus_plus: ^0.1.0
flutter_plugin_record_plus: ^0.0.17 flutter_plugin_record_plus: ^0.0.19
azlistview_all_platforms: ^2.1.2 azlistview_all_platforms: ^2.1.2
lpinyin: ^2.0.3 lpinyin: ^2.0.3
transparent_image: ^2.0.0 transparent_image: ^2.0.0
image_gallery_saver: ^2.0.1 image_gallery_saver_plus: ^3.0.5
path_provider: ^2.0.8 path_provider: ^2.0.8
cached_network_image: ^3.3.0 cached_network_image: ^3.3.0
shared_preferences: ^2.0.13 shared_preferences: ^2.0.13
@ -57,7 +57,7 @@ dependencies:
http: ^1.0.0 http: ^1.0.0
crypto: ^3.0.2 crypto: ^3.0.2
collection: ^1.15.0 collection: ^1.15.0
flutter_image_compress: ^1.1.3 flutter_image_compress: ^2.3.0
uuid: ^3.0.6 uuid: ^3.0.6
open_file: ^3.3.2 open_file: ^3.3.2
tencent_keyboard_visibility: ^1.0.1 tencent_keyboard_visibility: ^1.0.1
@ -75,7 +75,6 @@ dependencies:
just_audio: ^0.9.34 just_audio: ^0.9.34
markdown: ^7.1.0 markdown: ^7.1.0
logger: ^2.0.1 logger: ^2.0.1
image_clipboard: ^1.0.0+2
visibility_detector: ^0.4.0+2 visibility_detector: ^0.4.0+2
dev_dependencies: dev_dependencies: