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

View File

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

View File

@ -1,5 +1,6 @@
// ignore_for_file: unnecessary_getters_setters
import 'package:collection/collection.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/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/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 = [];
for (var i = 0; i < list.length; i++) {
bool found = false;
@ -32,10 +34,14 @@ class TUIConversationViewModel extends ChangeNotifier {
static const String conversationC2CPrefix = "c2c_";
static const String conversationGroupPrefix = "group_";
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
final ConversationService _conversationService = serviceLocator<ConversationService>();
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
final TUIChatGlobalModel _chatGlobalModel = serviceLocator<TUIChatGlobalModel>();
final TUISelfInfoViewModel selfInfoViewModel =
serviceLocator<TUISelfInfoViewModel>();
final ConversationService _conversationService =
serviceLocator<ConversationService>();
final FriendshipServices _friendshipServices =
serviceLocator<FriendshipServices>();
final TUIChatGlobalModel _chatGlobalModel =
serviceLocator<TUIChatGlobalModel>();
final MessageService _messageService = serviceLocator<MessageService>();
late V2TimConversationListener _conversationListener;
List<V2TimConversation?> _conversationList = [];
@ -45,7 +51,8 @@ class TUIConversationViewModel extends ChangeNotifier {
bool _haveMoreData = true;
int _totalUnReadCount = 0;
String? _scrollToConversation;
final TUIChatGlobalModel globalChatModel = serviceLocator<TUIChatGlobalModel>();
final TUIChatGlobalModel globalChatModel =
serviceLocator<TUIChatGlobalModel>();
String _nextSeq = "0";
ConversationLifeCycle? _lifeCycle;
@ -54,10 +61,13 @@ class TUIConversationViewModel extends ChangeNotifier {
if (PlatformUtils().isWeb) {
try {
_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 = [...pinnedConversation, ..._conversationList];
// ignore: empty_catches
@ -69,7 +79,8 @@ class TUIConversationViewModel extends ChangeNotifier {
}
V2TimConversation? getConversation(String conversationID) {
return _conversationList.firstWhere((element) => element?.conversationID == conversationID);
return _conversationList.firstWhereOrNull(
(element) => element?.conversationID == conversationID);
}
String? get scrollToConversation => _scrollToConversation;
@ -116,7 +127,8 @@ class TUIConversationViewModel extends ChangeNotifier {
}
TUIConversationViewModel() {
_conversationListener = V2TimConversationListener(onConversationChanged: (conversationList) {
_conversationListener =
V2TimConversationListener(onConversationChanged: (conversationList) {
_onConversationListChanged(conversationList);
}, onNewConversation: (conversationList) {
_addNewConversation(conversationList);
@ -129,7 +141,7 @@ class TUIConversationViewModel extends ChangeNotifier {
if (!PlatformUtils().isWeb) {
loadInitConversation();
}
}, onConversationDeleted:(List<String> conversationIDList) {
}, onConversationDeleted: (List<String> conversationIDList) {
_onConversationDeleted(conversationIDList);
for (var conversationID in conversationIDList) {
String resultID = "";
@ -162,7 +174,8 @@ class TUIConversationViewModel extends ChangeNotifier {
Future<void> loadData({required int count}) async {
_haveMoreData = true;
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 ?? "";
final conversationList = conversationResult?.conversationList;
if (conversationList != null) {
@ -175,8 +188,12 @@ class TUIConversationViewModel extends ChangeNotifier {
} else {
combinedConversationList = [..._conversationList, ...conversationList];
}
final List<V2TimConversation?> finalConversationList = await _lifeCycle?.conversationListWillMount(combinedConversationList) ?? combinedConversationList;
_conversationList = removeDuplicates<V2TimConversation?>(finalConversationList, (item1, item2) => item1?.conversationID == item2?.conversationID);
final List<V2TimConversation?> finalConversationList = await _lifeCycle
?.conversationListWillMount(combinedConversationList) ??
combinedConversationList;
_conversationList = removeDuplicates<V2TimConversation?>(
finalConversationList,
(item1, item2) => item1?.conversationID == item2?.conversationID);
notifyListeners();
}
_totalUnReadCount = await _conversationService.getTotalUnreadCount();
@ -193,11 +210,15 @@ class TUIConversationViewModel extends ChangeNotifier {
required String conversationID,
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 {
if (_lifeCycle?.shouldClearHistoricalMessageForConversation != null && await _lifeCycle!.shouldClearHistoricalMessageForConversation(convID) == false) {
Future<V2TimCallback?> clearHistoryMessage(
{required String convID, required int convType}) async {
if (_lifeCycle?.shouldClearHistoricalMessageForConversation != null &&
await _lifeCycle!.shouldClearHistoricalMessageForConversation(convID) ==
false) {
return null;
}
@ -211,17 +232,22 @@ class TUIConversationViewModel extends ChangeNotifier {
}
searchFriends(String searchKey) async {
final res = await _friendshipServices.searchFriends(searchParam: V2TimFriendSearchParam(keywordList: [searchKey]));
final res = await _friendshipServices.searchFriends(
searchParam: V2TimFriendSearchParam(keywordList: [searchKey]));
return res;
}
Future<V2TimCallback?> deleteConversation({required String conversationID}) async {
if (_lifeCycle?.shouldDeleteConversation != null && await _lifeCycle!.shouldDeleteConversation(conversationID) == false) {
Future<V2TimCallback?> deleteConversation(
{required String conversationID}) async {
if (_lifeCycle?.shouldDeleteConversation != null &&
await _lifeCycle!.shouldDeleteConversation(conversationID) == false) {
return null;
}
final res = await _conversationService.deleteConversation(conversationID: conversationID);
final res = await _conversationService.deleteConversation(
conversationID: conversationID);
if (res.code == 0) {
_conversationList.removeWhere((element) => element?.conversationID == conversationID);
_conversationList
.removeWhere((element) => element?.conversationID == conversationID);
notifyListeners();
}
return res;
@ -229,9 +255,11 @@ class TUIConversationViewModel extends ChangeNotifier {
_onConversationListChanged(List<V2TimConversation> list) {
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) {
_conversationList.setAll(index, [list[element]] as List<V2TimConversation?>);
_conversationList.setAll(
index, [list[element]] as List<V2TimConversation?>);
} else {
_conversationList.add(list[element]);
}
@ -242,10 +270,13 @@ class TUIConversationViewModel extends ChangeNotifier {
_onConversationDeleted(List<String> list) {
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) {
_conversationList.removeAt(index);
_conversationList = removeDuplicates<V2TimConversation?>(_conversationList, (item1, item2) => item1?.conversationID == item2?.conversationID);
_conversationList = removeDuplicates<V2TimConversation?>(
_conversationList,
(item1, item2) => item1?.conversationID == item2?.conversationID);
}
}
notifyListeners();
@ -253,16 +284,19 @@ class TUIConversationViewModel extends ChangeNotifier {
_addNewConversation(List<V2TimConversation> 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();
}
setConversationListener() {
_conversationService.addConversationListener(listener: _conversationListener);
_conversationService.addConversationListener(
listener: _conversationListener);
}
removeConversationListener() {
_conversationService.removeConversationListener(listener: _conversationListener);
_conversationService.removeConversationListener(
listener: _conversationListener);
}
Future<V2TimCallback> setConversationDraft({
@ -272,19 +306,25 @@ class TUIConversationViewModel extends ChangeNotifier {
String? groupID,
bool isAllowWeb = true,
}) 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) {
webDraftMap[conversationID] = draftText ?? "";
return V2TimCallback(code: 0, desc: "");
} else {
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;
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;
} 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_slidable/flutter_slidable.dart';
import 'package:flutter_slidable_plus_plus/flutter_slidable_plus_plus.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/business_logic/life_cycle/block_list_life_cycle.dart';
@ -88,7 +88,8 @@ class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
child: Text(
showName,
style: TextStyle(
color: theme.black, fontSize: isDesktopScreen ? 14 : 18),
color: theme.black,
fontSize: isDesktopScreen ? 14 : 18),
),
)),
if (isDesktopScreen)
@ -132,7 +133,6 @@ class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
return widget.itemBuilder ?? _itemBuilder;
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
return MultiProvider(

View File

@ -5,7 +5,6 @@ import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_clipboard/image_clipboard.dart';
import 'package:open_file/open_file.dart';
import 'package:provider/provider.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;
if (widget.message.localCustomData != null) {
final LocalCustomDataModel localCustomData = LocalCustomDataModel.fromMap(
json.decode(TencentUtils.checkString(widget.message.localCustomData) ?? "{}"));
if (localCustomData.translatedText != null && localCustomData.translatedText != "") {
json.decode(
TencentUtils.checkString(widget.message.localCustomData) ??
"{}"));
if (localCustomData.translatedText != null &&
localCustomData.translatedText != "") {
showTranslation = false;
}
}
final dynamicQuote =
model.chatConfig.isAtWhenReplyDynamic?.call(widget.message);
@ -405,12 +406,6 @@ class TIMUIKitMessageTooltipState
} catch (e) {}
}
Future<void> copyImageToClipboard(String imagePath) async {
ImageClipboard().copyImage(imagePath);
// final DesktopClipboard desktopClipboard = DesktopClipboard();
// desktopClipboard.copyImage(imagePath);
}
_onTap(String operation, TUIChatSeparateViewModel model) async {
final messageItem = widget.message;
final msgID = messageItem.msgID as String;
@ -494,13 +489,6 @@ class TIMUIKitMessageTooltipState
infoCode: 6660408));
// ignore: empty_catches
} 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;
case "replyMessage":

View File

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

View File

@ -2,7 +2,7 @@ import 'dart:math';
import 'package:flutter/material.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:scroll_to_index/scroll_to_index.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/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 {
/// the callback after clicking conversation item
@ -140,11 +144,14 @@ class ConversationItemSlidePanel extends TIMUIKitStatelessWidget {
}
class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
final TUIConversationViewModel model = serviceLocator<TUIConversationViewModel>();
final TUIConversationViewModel model =
serviceLocator<TUIConversationViewModel>();
late TIMUIKitConversationController _timuiKitConversationController;
final TUIThemeViewModel themeViewModel = serviceLocator<TUIThemeViewModel>();
final TUIFriendShipViewModel friendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
final TUIGroupListenerModel groupListenerModel = serviceLocator<TUIGroupListenerModel>();
final TUIFriendShipViewModel friendShipViewModel =
serviceLocator<TUIFriendShipViewModel>();
final TUIGroupListenerModel groupListenerModel =
serviceLocator<TUIGroupListenerModel>();
late AutoScrollController _autoScrollController;
@override
@ -168,21 +175,30 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
}
_clearHistory(V2TimConversation conversationItem) {
_timuiKitConversationController.clearHistoryMessage(conversation: conversationItem);
_timuiKitConversationController.clearHistoryMessage(
conversation: conversationItem);
}
_pinConversation(V2TimConversation conversation) {
_timuiKitConversationController.pinConversation(conversationID: conversation.conversationID, isPinned: !conversation.isPinned!);
_timuiKitConversationController.pinConversation(
conversationID: conversation.conversationID,
isPinned: !conversation.isPinned!);
}
_deleteConversation(V2TimConversation conversation) {
_timuiKitConversationController.deleteConversation(conversationID: conversation.conversationID);
_timuiKitConversationController.deleteConversation(
conversationID: conversation.conversationID);
}
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) {
filteredConversationList = filteredConversationList.where(widget.conversationCollector!).toList();
filteredConversationList = filteredConversationList
.where(widget.conversationCollector!)
.toList();
}
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: [
if (!PlatformUtils().isWeb)
ColumnMenuItem(
@ -220,7 +237,11 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
}),
ColumnMenuItem(
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: () {
onClose();
_pinConversation(conversationItem);
@ -245,7 +266,8 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) {
_clearHistory(conversationItem);
},
backgroundColor: theme.conversationItemSliderClearBgColor ?? CommonColor.primaryColor,
backgroundColor: theme.conversationItemSliderClearBgColor ??
CommonColor.primaryColor,
foregroundColor: theme.conversationItemSliderTextColor,
label: TIM_t("清除"),
spacing: 0,
@ -255,7 +277,8 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) {
_pinConversation(conversationItem);
},
backgroundColor: theme.conversationItemSliderPinBgColor ?? CommonColor.infoColor,
backgroundColor:
theme.conversationItemSliderPinBgColor ?? CommonColor.infoColor,
foregroundColor: theme.conversationItemSliderTextColor,
label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"),
),
@ -263,14 +286,16 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) {
_deleteConversation(conversationItem);
},
backgroundColor: theme.conversationItemSliderDeleteBgColor ?? Colors.red,
backgroundColor:
theme.conversationItemSliderDeleteBgColor ?? Colors.red,
foregroundColor: theme.conversationItemSliderTextColor,
label: TIM_t("删除"),
)
];
}
Widget _getSecondaryMenu(V2TimConversation conversation, VoidCallback onClose) {
Widget _getSecondaryMenu(
V2TimConversation conversation, VoidCallback onClose) {
if (widget.itemSecondaryMenuBuilder != null) {
return widget.itemSecondaryMenuBuilder!(conversation, onClose);
}
@ -289,19 +314,23 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: model),
ChangeNotifierProvider.value(value: friendShipViewModel),
ChangeNotifierProvider.value(value: groupListenerModel)],
ChangeNotifierProvider.value(value: groupListenerModel)
],
builder: (BuildContext context, Widget? w) {
final _model = Provider.of<TUIConversationViewModel>(context);
bool haveMoreData = _model.haveMoreData;
final _friendShipViewModel = Provider.of<TUIFriendShipViewModel>(context);
final _friendShipViewModel =
Provider.of<TUIFriendShipViewModel>(context);
_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;
if (needUpdate != null) {
groupListenerModel.needUpdate = null;
@ -313,12 +342,14 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
} else if (needUpdate.updateType == UpdateType.kickedFromGroup) {
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: "${TIM_t("您已被踢出")}${needUpdate!.extraData}",
infoRecommendText:
"${TIM_t("您已被踢出")}${needUpdate!.extraData}",
infoCode: 6661402));
}
}
List<V2TimConversation?> filteredConversationList = getFilteredConversation();
List<V2TimConversation?> filteredConversationList =
getFilteredConversation();
if (TencentUtils.checkString(_model.scrollToConversation) != null) {
_onScrollToConversation(_model.scrollToConversation!);
@ -340,15 +371,21 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
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) {
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;
@ -365,14 +402,21 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
lastMessageBuilder: widget.lastMessageBuilder,
faceUrl: conversationItem.faceUrl ?? "",
nickName: conversationItem.showName ?? "",
isDisturb: (conversationItem.groupType == "Meeting" ? false : conversationItem
.recvOpt != 0),
isDisturb:
(conversationItem.groupType == "Meeting"
? false
: conversationItem.recvOpt != 0),
lastMsg: conversationItem.lastMessage,
isPined: isPined,
groupAtInfoList: conversationItem.groupAtInfoList ?? [],
groupAtInfoList:
conversationItem.groupAtInfoList ?? [],
unreadCount: conversationItem.unreadCount ?? 0,
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,
convType: conversationItem.type),
onTap: () => onTapConvItem(conversationItem),
@ -389,12 +433,23 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
child: InkWell(
onSecondaryTapDown: (details) {
TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.conversationSecondaryMenu,
operationKey: TUIKitWideModalOperationKey
.conversationSecondaryMenu,
isDarkBackground: false,
borderRadius: const BorderRadius.all(Radius.circular(4)),
borderRadius: const BorderRadius.all(
Radius.circular(4)),
context: context,
offset: Offset(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));
offset: Offset(
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(),
),
@ -403,10 +458,19 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
key: ValueKey(conversationItem.conversationID),
controller: _autoScrollController,
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(
@ -420,7 +484,9 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
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/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:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.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();
}
class GroupProfileGroupManageState extends TIMUIKitState<GroupProfileGroupManage> {
class GroupProfileGroupManageState
extends TIMUIKitState<GroupProfileGroupManage> {
bool isShowManageBox = false;
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
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);
return Container(
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(
children: [
InkWell(
onTap: () {
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) ==
DeviceType.Desktop;
if (!isDesktopScreen) {
Navigator.push(
context,
@ -61,12 +72,15 @@ class GroupProfileGroupManageState extends TIMUIKitState<GroupProfileGroupManage
children: [
Text(
TIM_t("群管理"),
style: TextStyle(fontSize: isDesktopScreen ? 14 : 16, color: theme.darkTextColor),
style: TextStyle(
fontSize: isDesktopScreen ? 14 : 16,
color: theme.darkTextColor),
),
AnimatedRotation(
turns: isShowManageBox ? 0.25 : 0,
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();
}
class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupManagePage> {
class _GroupProfileGroupManagePageState
extends TIMUIKitState<GroupProfileGroupManagePage> {
int? serverTime;
@override
@ -113,20 +128,38 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
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) {
final memberList = Provider.of<TUIGroupProfileModel>(context).groupMemberList;
final memberList =
Provider.of<TUIGroupProfileModel>(context).groupMemberList;
final theme = Provider.of<TUIThemeViewModel>(context).theme;
final isAllMuted = widget.model.groupInfo?.isAllMuted ?? false;
final bool isAllowMuteMember = (widget.model.groupInfo?.groupType ?? "") != GroupType.Work;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final bool isAllowMuteMember =
(widget.model.groupInfo?.groupType ?? "") != GroupType.Work;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
Widget managePage() {
return Column(
children: [
Container(
padding: EdgeInsets.only(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))),
padding: EdgeInsets.only(
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(
onTap: isDesktopScreen
? null
@ -134,7 +167,8 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GroupProfileSetManagerPage(
builder: (context) =>
GroupProfileSetManagerPage(
model: widget.model,
),
));
@ -143,12 +177,22 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(TIM_t("群管理员"), style: TextStyle(fontSize: 14, color: theme.darkTextColor)),
Text(TIM_t("群管理员"),
style: TextStyle(
fontSize: 14,
color: theme.darkTextColor)),
],
)
: Row(
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)
Container(
padding: const EdgeInsets.only(top: 12, left: 16, bottom: 12, right: 12),
decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))),
padding: const EdgeInsets.only(
top: 12, left: 16, bottom: 12, right: 12),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
TIM_t("全员禁言"),
style: TextStyle(fontSize: 16, color: theme.darkTextColor),
style: TextStyle(
fontSize: 16, color: theme.darkTextColor),
),
CupertinoSwitch(
value: isAllMuted,
@ -180,7 +231,9 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(TIM_t("禁言"), style: TextStyle(fontSize: 14, color: theme.darkTextColor)),
Text(TIM_t("禁言"),
style: TextStyle(
fontSize: 14, color: theme.darkTextColor)),
],
),
if (isDesktopScreen)
@ -200,12 +253,14 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
),
if (!isDesktopScreen)
Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 16),
color: theme.weakBackgroundColor,
alignment: Alignment.topLeft,
child: Text(
TIM_t("全员禁言开启后,只允许群主和管理员发言。"),
style: TextStyle(fontSize: 12, color: theme.weakTextColor),
style:
TextStyle(fontSize: 12, color: theme.weakTextColor),
),
),
if (!isAllMuted && isAllowMuteMember)
@ -221,7 +276,14 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
: const EdgeInsets.only(
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(
children: [
Icon(
@ -242,15 +304,21 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
key: groupProfileAddAdminKey,
appbarTitle: TIM_t("设置禁言"),
memberList: memberList.where((element) {
final isMute = (serverTime != null ? (element?.muteUntil ?? 0) > serverTime! : false);
final isMember = element!.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
final isMute = (serverTime != null
? (element?.muteUntil ?? 0) > serverTime!
: false);
final isMember = element!.role ==
GroupMemberRoleType
.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
return !isMute && isMember;
}).toList(),
selectCompletedHandler: (context, selectedMember) async {
selectCompletedHandler:
(context, selectedMember) async {
if (selectedMember.isNotEmpty) {
for (var member in selectedMember) {
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());
} else {
Navigator.push(context, MaterialPageRoute(builder: (context) => muteMember()));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => muteMember()));
}
},
),
if (!isAllMuted && isAllowMuteMember)
...memberList
.where((element) => (serverTime != null ? (element?.muteUntil ?? 0) > serverTime! : false))
.where((element) => (serverTime != null
? (element?.muteUntil ?? 0) > serverTime!
: false))
.map((e) => Container(
padding: isDesktopScreen ? const EdgeInsets.only(left: 16) : null,
padding: isDesktopScreen
? const EdgeInsets.only(left: 16)
: null,
child: GestureDetector(
onSecondaryTapDown: (details) {
TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.setUnmute,
operationKey:
TUIKitWideModalOperationKey.setUnmute,
isDarkBackground: false,
borderRadius: const BorderRadius.all(Radius.circular(4)),
borderRadius: const BorderRadius.all(
Radius.circular(4)),
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: [
ColumnMenuItem(
label: TIM_t("删除"),
icon: const Icon(Icons.remove_circle_outline, size: 16),
icon: const Icon(
Icons.remove_circle_outline,
size: 16),
onClick: () {
widget.model.muteGroupMember(e.userID, false, serverTime);
widget.model.muteGroupMember(
e.userID,
false,
serverTime);
onClose();
}),
]));
@ -299,17 +386,21 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
child: _buildListItem(
context,
e!,
ActionPane(motion: const DrawerMotion(), children: [
SlidableAction(
onPressed: (_) {
widget.model.muteGroupMember(e.userID, false, serverTime);
},
flex: 1,
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
])),
ActionPane(
motion: const DrawerMotion(),
children: [
SlidableAction(
onPressed: (_) {
widget.model.muteGroupMember(
e.userID, false, serverTime);
},
flex: 1,
backgroundColor: theme.cautionColor ??
CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
])),
),
))
.toList()
@ -324,7 +415,8 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
appBar: AppBar(
title: Text(
TIM_t("群管理"),
style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
style:
TextStyle(color: theme.appbarTextColor, fontSize: 17),
),
backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
shadowColor: theme.weakDividerColor,
@ -369,9 +461,11 @@ _getShowName(V2TimGroupMemberFullInfo? item) {
: 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 isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
Widget nameItem() {
return Container(
@ -390,42 +484,67 @@ Widget _buildListItem(BuildContext context, V2TimGroupMemberFullInfo memberInfo,
),
title: Row(
children: [
Text(_getShowName(memberInfo), style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)),
Text(_getShowName(memberInfo),
style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)),
],
),
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 {
final TUIGroupProfileModel model;
const GroupProfileSetManagerPage({Key? key, required this.model}) : super(key: key);
const GroupProfileSetManagerPage({Key? key, required this.model})
: super(key: key);
@override
State<StatefulWidget> createState() => _GroupProfileSetManagerPageState();
}
class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetManagerPage> {
List<V2TimGroupMemberFullInfo?> _getAdminMemberList(List<V2TimGroupMemberFullInfo?> memberList) {
return memberList.where((member) => member?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN).toList();
class _GroupProfileSetManagerPageState
extends TIMUIKitState<GroupProfileSetManagerPage> {
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) {
return memberList.where((member) => member?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER).toList();
List<V2TimGroupMemberFullInfo?> _getOwnerList(
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);
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 ownerList = _getOwnerList(memberList);
final String option2 = adminList.length.toString();
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
Widget adminPage() {
return SingleChildScrollView(
@ -451,7 +571,8 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
Container(
alignment: Alignment.topLeft,
color: theme.weakDividerColor,
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
padding:
const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
child: Text(
TIM_t("群主"),
style: TextStyle(fontSize: 14, color: theme.weakTextColor),
@ -469,7 +590,9 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
...ownerList
.map(
(e) => Container(
padding: isDesktopScreen ? const EdgeInsets.only(left: 16) : null,
padding: isDesktopScreen
? const EdgeInsets.only(left: 16)
: null,
child: _buildListItem(context, e!, null),
),
)
@ -478,9 +601,11 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
Container(
alignment: Alignment.topLeft,
color: theme.weakDividerColor,
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
padding:
const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
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),
),
),
@ -489,7 +614,8 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(top: 10, bottom: 4, left: 16),
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),
),
),
@ -501,7 +627,14 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
padding: const EdgeInsets.symmetric(
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(
children: [
Icon(
@ -529,9 +662,15 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
},
child: (onClose) => GroupProfileAddAdmin(
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("设置管理员"),
selectCompletedHandler: (context, selectedMember) async {
selectCompletedHandler:
(context, selectedMember) async {
if (selectedMember.isNotEmpty) {
for (var member in selectedMember) {
final userID = member!.userID;
@ -546,9 +685,15 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
MaterialPageRoute(
builder: (context) => GroupProfileAddAdmin(
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("设置管理员"),
selectCompletedHandler: (context, selectedMember) async {
selectCompletedHandler:
(context, selectedMember) async {
if (selectedMember.isNotEmpty) {
for (var member in selectedMember) {
final userID = member!.userID;
@ -564,15 +709,22 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
.map((e) => GestureDetector(
onSecondaryTapDown: (details) {
TUIKitWidePopup.showPopupWindow(
operationKey: TUIKitWideModalOperationKey.deleteAdmin,
operationKey:
TUIKitWideModalOperationKey.deleteAdmin,
isDarkBackground: false,
borderRadius: const BorderRadius.all(Radius.circular(4)),
borderRadius:
const BorderRadius.all(Radius.circular(4)),
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: [
ColumnMenuItem(
label: TIM_t("删除"),
icon: const Icon(Icons.remove_circle_outline, size: 16),
icon: const Icon(
Icons.remove_circle_outline,
size: 16),
onClick: () {
_removeAdmin(context, e);
onClose();
@ -580,21 +732,26 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
]));
},
child: Container(
padding: isDesktopScreen ? const EdgeInsets.only(left: 16) : null,
padding: isDesktopScreen
? const EdgeInsets.only(left: 16)
: null,
child: _buildListItem(
context,
e!,
ActionPane(motion: const DrawerMotion(), children: [
SlidableAction(
onPressed: (_) {
_removeAdmin(context, e);
},
flex: 1,
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
])),
ActionPane(
motion: const DrawerMotion(),
children: [
SlidableAction(
onPressed: (_) {
_removeAdmin(context, e);
},
flex: 1,
backgroundColor: theme.cautionColor ??
CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
])),
),
))
.toList(),
@ -628,9 +785,16 @@ class _GroupProfileSetManagerPageState extends TIMUIKitState<GroupProfileSetMana
class GroupProfileAddAdmin extends StatefulWidget {
final List<V2TimGroupMemberFullInfo?> memberList;
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
State<StatefulWidget> createState() => _GroupProfileAddAdminState();
@ -664,8 +828,14 @@ class _GroupProfileAddAdminState extends TIMUIKitState<GroupProfileAddAdmin> {
),
...widget.memberList
.map((e) => Container(
decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))),
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 16),
child: InkWell(
onTap: () {
final isChecked = selectedMemberList.contains(e);
@ -697,7 +867,8 @@ class _GroupProfileAddAdminState extends TIMUIKitState<GroupProfileAddAdmin> {
const SizedBox(
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: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:provider/provider.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
final String? groupType;
final Function(List<V2TimGroupMemberFullInfo> selectedMember)? onSelectedMemberChange;
final Function(List<V2TimGroupMemberFullInfo> selectedMember)?
onSelectedMemberChange;
// 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
final Function()? touchBottomCallBack;
@ -53,7 +56,8 @@ class GroupProfileMemberList extends StatefulWidget {
State<StatefulWidget> createState() => _GroupProfileMemberListState();
}
class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList> {
class _GroupProfileMemberListState
extends TIMUIKitState<GroupProfileMemberList> {
List<V2TimGroupMemberFullInfo> selectedMemberList = [];
_getShowName(V2TimGroupMemberFullInfo? item) {
@ -70,12 +74,14 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
: userID;
}
List<ISuspensionBeanImpl> _getShowList(List<V2TimGroupMemberFullInfo?> memberList) {
List<ISuspensionBeanImpl> _getShowList(
List<V2TimGroupMemberFullInfo?> memberList) {
final List<ISuspensionBeanImpl> showList = List.empty(growable: true);
for (var i = 0; i < memberList.length; i++) {
final item = memberList[i];
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: "@"));
} else {
String pinyin = PinyinHelper.getPinyinE(showName);
@ -94,17 +100,26 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
if (widget.canAtAll) {
final canAtGroupType = ["Work", "Public", "Meeting"];
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;
}
Widget _buildListItem(BuildContext context, V2TimGroupMemberFullInfo memberInfo) {
Widget _buildListItem(
BuildContext context, V2TimGroupMemberFullInfo memberInfo) {
final theme = Provider.of<TUIThemeViewModel>(context).theme;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
final isGroupMember = memberInfo.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
final isGroupMember =
memberInfo.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
return Container(
color: Colors.white,
child: Slidable(
@ -117,7 +132,8 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
}
},
flex: 1,
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
backgroundColor:
theme.cautionColor ?? CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
@ -134,21 +150,28 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
child: CheckBoxButton(
onChanged: (isChecked) {
if (isChecked) {
if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) {
if (widget.maxSelectNum != null &&
selectedMemberList.length >=
widget.maxSelectNum!) {
return;
}
selectedMemberList.add(memberInfo);
} else {
selectedMemberList.removeWhere((element) => element.userID == memberInfo.userID);
selectedMemberList.removeWhere((element) =>
element.userID == memberInfo.userID);
}
if (widget.onSelectedMemberChange != null) {
widget.onSelectedMemberChange!(selectedMemberList);
widget.onSelectedMemberChange!(
selectedMemberList);
}
setState(() {});
},
isChecked: selectedMemberList.where((element) => element.userID == memberInfo.userID).toList().isNotEmpty
),
isChecked: selectedMemberList
.where((element) =>
element.userID == memberInfo.userID)
.toList()
.isNotEmpty),
),
Container(
width: isDesktopScreen ? 30 : 36,
@ -160,8 +183,10 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
type: 1,
),
),
Text(_getShowName(memberInfo), style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)),
memberInfo.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER
Text(_getShowName(memberInfo),
style: TextStyle(fontSize: isDesktopScreen ? 14 : 16)),
memberInfo.role ==
GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER
? Container(
margin: const EdgeInsets.only(left: 5),
child: Text(TIM_t("群主"),
@ -171,11 +196,17 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
)),
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
decoration: BoxDecoration(
border: Border.all(color: theme.ownerColor ?? CommonColor.ownerColor, width: 1),
borderRadius: const BorderRadius.all(Radius.circular(4.0)),
border: Border.all(
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(
margin: const EdgeInsets.only(left: 5),
child: Text(TIM_t("管理员"),
@ -185,8 +216,12 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
)),
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
decoration: BoxDecoration(
border: Border.all(color: theme.adminColor ?? CommonColor.adminColor, width: 1),
borderRadius: const BorderRadius.all(Radius.circular(4.0)),
border: Border.all(
color: theme.adminColor ??
CommonColor.adminColor,
width: 1),
borderRadius: const BorderRadius.all(
Radius.circular(4.0)),
),
)
: Container()
@ -201,7 +236,8 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
if (isChecked) {
selectedMemberList.remove(memberInfo);
} else {
if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) {
if (widget.maxSelectNum != null &&
selectedMemberList.length >= widget.maxSelectNum!) {
return;
}
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 == '@') {
tag = TIM_t("群主、管理员");
}
@ -242,9 +284,11 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
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 maxScrollExtent = notification.metrics.maxScrollExtent;
@ -272,15 +316,19 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
child: Text(TIM_t("暂无群成员")),
)
: Container(
padding: isDesktopScreen ? const EdgeInsets.symmetric(horizontal: 16) : null,
padding: isDesktopScreen
? const EdgeInsets.symmetric(horizontal: 16)
: null,
child: AZListViewContainer(
memberList: showList,
susItemBuilder: (context, index) {
final model = showList[index];
return getSusItem(context, theme, model.getSuspensionTag());
return getSusItem(
context, theme, model.getSuspensionTag());
},
itemBuilder: (context, index) {
final memberInfo = showList[index].memberInfo as V2TimGroupMemberFullInfo;
final memberInfo = showList[index].memberInfo
as V2TimGroupMemberFullInfo;
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:flutter/material.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_state.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';
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 dynamic heroTag;
@ -34,7 +39,8 @@ class VideoScreen extends StatefulWidget {
class _VideoScreenState extends TIMUIKitState<VideoScreen> {
late VideoPlayerController videoPlayerController;
late ChewieController chewieController;
GlobalKey<ExtendedImageSlidePageState> slidePagekey = GlobalKey<ExtendedImageSlidePageState>();
GlobalKey<ExtendedImageSlidePageState> slidePagekey =
GlobalKey<ExtendedImageSlidePageState>();
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
bool isInit = false;
@ -64,7 +70,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
xhr.open('get', videoUrl);
xhr.responseType = 'arraybuffer';
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.click();
a.remove();
@ -110,7 +117,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
}
if (model.getMessageProgress(widget.message.msgID) == 100) {
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!;
} else {
savePath = model.getFileMessageLocation(widget.message.msgID);
@ -120,35 +128,62 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
var result = await ImageGallerySaver.saveFile(savePath);
if (PlatformUtils().isIOS) {
if (result['isSuccess']) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
}
} else {
if (result != null) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
}
}
}
} 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;
}
var result = await ImageGallerySaver.saveFile(savePath);
if (PlatformUtils().isIOS) {
if (result['isSuccess']) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
}
} else {
if (result != null) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
}
}
return;
@ -162,7 +197,9 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
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!);
if (f.existsSync()) {
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!);
if (f.existsSync()) {
return await _saveNetworkVideo(
@ -211,7 +249,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
setVideoPlayerController() async {
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);
File f = File(savePath);
if (f.existsSync()) {
@ -221,20 +260,26 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
}
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(
Uri.parse(widget.videoElement.videoPath!),
)
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null)
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) ==
null)
? VideoPlayerController.networkUrl(
Uri.parse(widget.videoElement.videoUrl!),
)
: VideoPlayerController.networkUrl(
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!))
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null)
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) ==
null)
? VideoPlayerController.networkUrl(
Uri.parse(widget.videoElement.videoUrl!),
)
@ -264,7 +309,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
@override
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();
}
super.didUpdateWidget(oldWidget);
@ -299,8 +345,10 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
return Colors.black;
}
double opacity = 0.0;
opacity = offset.distance / (Offset(size.width, size.height).distance / 2.0);
return Colors.black.withOpacity(min(1.0, max(1.0 - opacity, 0.0)));
opacity = offset.distance /
(Offset(size.width, size.height).distance / 2.0);
return Colors.black
.withOpacity(min(1.0, max(1.0 - opacity, 0.0)));
},
slideType: SlideType.onlyImage,
slideEndHandler: (
@ -322,13 +370,22 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
? Chewie(
controller: chewieController,
)
: const Center(child: CircularProgressIndicator(color: Colors.white))),
: const Center(
child: CircularProgressIndicator(
color: Colors.white))),
heroBuilderForSlidingPage: (Widget result) {
return Hero(
tag: widget.heroTag,
child: result,
flightShuttleBuilder: (BuildContext flightContext, Animation<double> animation, HeroFlightDirection flightDirection, BuildContext fromHeroContext, BuildContext toHeroContext) {
final Hero hero = (flightDirection == HeroFlightDirection.pop ? fromHeroContext.widget : toHeroContext.widget) as Hero;
flightShuttleBuilder: (BuildContext flightContext,
Animation<double> animation,
HeroFlightDirection flightDirection,
BuildContext fromHeroContext,
BuildContext toHeroContext) {
final Hero hero =
(flightDirection == HeroFlightDirection.pop
? fromHeroContext.widget
: toHeroContext.widget) as Hero;
return hero.child;
},

View File

@ -1,6 +1,6 @@
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.
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
repository: https://github.com/TencentCloud/chat-uikit-flutter
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
@ -30,12 +30,12 @@ dependencies:
tencent_super_tooltip: ^0.0.1
video_player: ^2.9.0
chewie: ^1.8.5
flutter_slidable: ^3.0.1
flutter_plugin_record_plus: ^0.0.17
flutter_slidable_plus_plus: ^0.1.0
flutter_plugin_record_plus: ^0.0.19
azlistview_all_platforms: ^2.1.2
lpinyin: ^2.0.3
transparent_image: ^2.0.0
image_gallery_saver: ^2.0.1
image_gallery_saver_plus: ^3.0.5
path_provider: ^2.0.8
cached_network_image: ^3.3.0
shared_preferences: ^2.0.13
@ -57,7 +57,7 @@ dependencies:
http: ^1.0.0
crypto: ^3.0.2
collection: ^1.15.0
flutter_image_compress: ^1.1.3
flutter_image_compress: ^2.3.0
uuid: ^3.0.6
open_file: ^3.3.2
tencent_keyboard_visibility: ^1.0.1
@ -75,7 +75,6 @@ dependencies:
just_audio: ^0.9.34
markdown: ^7.1.0
logger: ^2.0.1
image_clipboard: ^1.0.0+2
visibility_detector: ^0.4.0+2
dev_dependencies: