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

@ -113,16 +113,16 @@ _coreInstance.login(
全部场景码清单如下:
| 场景码 `infoCode` | 推荐提示语 `infoRecommendText` | 场景描述 |
| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 6660101 | 好友申请已发送 | 用户申请添加其他用户为联系人 |
| 6660102 | 该用户已是好友 | 用户申请添加其他已是好友的用户为好友时,触发 `onTapAlreadyFriendsItem` 回调 |
|------------------| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 6660101(3.0.1废弃)| 好友申请已发送 | 用户申请添加其他用户为联系人 |
| 6660102(3.0.1废弃)| 该用户已是好友 | 用户申请添加其他已是好友的用户为好友时,触发 `onTapAlreadyFriendsItem` 回调 |
| 6660201 | 群申请已发送 | 用户申请加入需要管理员审批的群聊 |
| 6660202 | 您已是群成员 | 用户申请加群时,判断用户已经是当前群成员,触发 `onTapExistGroup` 回调 |
| 6660401 | 无法定位到原消息 | 当用户需要跳转至@消息或者是引用消息时,在消息列表中查不到目标消息 |
| 6660401(3.0.1废弃)| 无法定位到原消息 | 当用户需要跳转至@消息或者是引用消息时,在消息列表中查不到目标消息 |
| 6660402 | 视频保存成功 | 用户在消息列表,点开视频消息后,选择保存视频 |
| 6660403 | 视频保存失败 | 用户在消息列表,点开视频消息后,选择保存视频 |
| 6660404 | 说话时间太短 | 用户发送了过短的语音消息 |
| 6660405 | 发送失败,视频不能大于 100MB | 用户试图发送大于 100MB 的视频 |
| 6660405(3.0.1废弃)| 发送失败,视频不能大于 100MB | 用户试图发送大于 100MB 的视频 |
| 6660406 | 图片保存成功 | 用户在消息列表,点开图片大图后,选择保存图片 |
| 6660407 | 图片保存失败 | 用户在消息列表,点开图片大图后,选择保存图片 |
| 6660408 | 已复制 | 用户在弹窗内选择复制文字消息 |
@ -135,14 +135,14 @@ _coreInstance.login(
| 6661002 | 无网络连接,无法查看群成员 | 当用户试图在无网络环境下,修改群资料 |
| 6661003 | 成功取消管理员身份 | 用户将群内其他用户移除管理员 |
| 6661201 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改自己或联系人的资料 |
| 6661202 | 好友添加成功 | 在资料页添加其他用户为好友,并自动添加成功,无需验证 |
| 6661203 | 好友申请已发出 | 在资料页添加其他用户为好友,对方设置需要验证 |
| 6661204 | 当前用户在黑名单 | 在资料页添加其他用户为好友,对方在自己的黑名单内 |
| 6661205 | 好友添加失败 | 在资料页添加其他用户为好友,添加失败,可能是由于对方禁止加好友 |
| 6661206 | 好友删除成功 | 在资料页删除其他用户为好友,成功 |
| 6661207 | 好友删除失败 | 在资料页删除其他用户为好友,失败 |
| 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 | 请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法 | 用户退出群或解散群时,为提供返回首页办法 |
| 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) {
@ -920,8 +924,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
_repliedMessage = null;
final sendMsgRes = await _messageService.sendMessage(
cloudCustomData:
TencentUtils.checkString(messageInfoWithSender?.cloudCustomData) ??
cloudCustomData: TencentUtils.checkString(
messageInfoWithSender?.cloudCustomData) ??
json.encode(cloudCustomData),
id: textMessageInfo.id as String,
offlinePushInfo: tools.buildMessagePushInfo(
@ -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();
}
@ -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);
}
}
@ -1495,7 +1497,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
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);
@ -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

@ -58,7 +58,8 @@ typedef MessageRowBuilder = Widget? Function(
Function onScrollToIndexBegin,
);
typedef MessageNickNameBuilder = Widget Function(BuildContext context, V2TimMessage message, TUIChatSeparateViewModel model);
typedef MessageNickNameBuilder = Widget Function(
BuildContext context, V2TimMessage message, TUIChatSeparateViewModel model);
typedef MessageItemContent = Widget? Function(
V2TimMessage message,
@ -71,7 +72,8 @@ class MessageHoverControlItem {
Widget icon;
ValueChanged<TapDownDetails> onClick;
MessageHoverControlItem({required this.name, required this.icon, required this.onClick});
MessageHoverControlItem(
{required this.name, required this.icon, required this.onClick});
}
class MessageItemBuilder {
@ -139,7 +141,11 @@ class MessageToolTipItem {
final String iconImageAsset;
final VoidCallback onClick;
MessageToolTipItem({required this.label, required this.id, required this.iconImageAsset, required this.onClick});
MessageToolTipItem(
{required this.label,
required this.id,
required this.iconImageAsset,
required this.onClick});
}
class ToolTipsConfig {
@ -165,10 +171,12 @@ class ToolTipsConfig {
bool showTranslation;
/// A builder for additional custom items. We recommend using `additionalMessageToolTips` instead of this field since version 2.0, as you only need to provide the data rather than the whole widget. This makes usage easier and you don't need to worry about the UI display.
final Widget? Function(V2TimMessage message, Function() closeTooltip, [Key? key, BuildContext? context])? additionalItemBuilder;
final Widget? Function(V2TimMessage message, Function() closeTooltip,
[Key? key, BuildContext? context])? additionalItemBuilder;
/// A list of additional message tooltip menu items, provided with the data only. We recommend using this field instead of the previous `additionalItemBuilder`.
List<MessageToolTipItem> Function(V2TimMessage message, Function() closeTooltip)? additionalMessageToolTips;
List<MessageToolTipItem> Function(
V2TimMessage message, Function() closeTooltip)? additionalMessageToolTips;
ToolTipsConfig(
{this.showDeleteMessage = true,
@ -179,7 +187,8 @@ class ToolTipsConfig {
this.showCopyMessage = true,
this.showForwardMessage = true,
this.additionalMessageToolTips,
@Deprecated("Please use `additionalMessageToolTips` instead. You are now only expected to specify the data, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.")
@Deprecated(
"Please use `additionalMessageToolTips` instead. You are now only expected to specify the data, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.")
this.additionalItemBuilder});
}
@ -188,10 +197,12 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
final V2TimMessage message;
/// tap remote user avatar callback function
final void Function(String userID, TapDownDetails tapDetails)? onTapForOthersPortrait;
final void Function(String userID, TapDownDetails tapDetails)?
onTapForOthersPortrait;
/// secondary tap remote user avatar callback function
final void Function(String userID, TapDownDetails tapDetails)? onSecondaryTapForOthersPortrait;
final void Function(String userID, TapDownDetails tapDetails)?
onSecondaryTapForOthersPortrait;
/// the function use for reply message, when click replied message can scroll to it.
final Function? onScrollToIndex;
@ -200,7 +211,8 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
final Function? onScrollToIndexBegin;
/// the callback for long press event, except myself avatar
final Function(String? userId, String? nickName)? onLongPressForOthersHeadPortrait;
final Function(String? userId, String? nickName)?
onLongPressForOthersHeadPortrait;
/// message item builder, works for customize all message types and row layout.
final MessageItemBuilder? messageItemBuilder;
@ -220,7 +232,8 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// Auto mention user when send reply message
final bool allowAtUserWhenReply;
@Deprecated("Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
@Deprecated(
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
/// allow show user nick name
final bool showNickName;
@ -241,16 +254,19 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
final EdgeInsetsGeometry? textPadding;
/// avatar builder
final Widget Function(BuildContext context, V2TimMessage message)? userAvatarBuilder;
final Widget Function(BuildContext context, V2TimMessage message)?
userAvatarBuilder;
/// theme info for message and avatar
final MessageThemeData? themeData;
/// builder for nick name row
final Widget Function(BuildContext context, V2TimMessage message)? topRowBuilder;
final Widget Function(BuildContext context, V2TimMessage message)?
topRowBuilder;
/// builder for bottom raw which under message content
final Widget Function(BuildContext context, V2TimMessage message)? bottomRowBuilder;
final Widget Function(BuildContext context, V2TimMessage message)?
bottomRowBuilder;
// open MessageReaction
final bool? isUseMessageReaction;
@ -271,7 +287,9 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
const TIMUIKitHistoryMessageListItem(
{Key? key,
required this.message,
@Deprecated("Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead") this.showNickName = false,
@Deprecated(
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
this.showNickName = false,
this.onScrollToIndex,
this.onScrollToIndexBegin,
this.onTapForOthersPortrait,
@ -308,7 +326,9 @@ class TipsActionItem extends TIMUIKitStatelessWidget {
final String icon;
final String? package;
TipsActionItem({Key? key, required this.label, required this.icon, this.package}) : super(key: key);
TipsActionItem(
{Key? key, required this.label, required this.icon, this.package})
: super(key: key);
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
@ -336,13 +356,16 @@ class TipsActionItem extends TIMUIKitStatelessWidget {
}
}
class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistoryMessageListItem> with SingleTickerProviderStateMixin {
class _TIMUIKItHistoryMessageListItemState
extends TIMUIKitState<TIMUIKitHistoryMessageListItem>
with SingleTickerProviderStateMixin {
SuperTooltip? tooltip;
late AnimationController _animationController;
// ignore: unused_field
final MessageService _messageService = serviceLocator<MessageService>();
final TUISelfInfoViewModel selfInfoModel = serviceLocator<TUISelfInfoViewModel>();
final TUISelfInfoViewModel selfInfoModel =
serviceLocator<TUISelfInfoViewModel>();
final TUIThemeViewModel themeModel = serviceLocator<TUIThemeViewModel>();
// bool isChecked = false;
@ -353,7 +376,9 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
@override
void initState() {
super.initState();
_animationController = AnimationController(duration: const Duration(seconds: 1), vsync: this)..repeat();
_animationController =
AnimationController(duration: const Duration(seconds: 1), vsync: this)
..repeat();
}
closeTooltip() {
@ -361,10 +386,15 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
}
bool isReplyMessage(V2TimMessage message) {
final hasCustomData = message.cloudCustomData != null && message.cloudCustomData != "";
final hasCustomData =
message.cloudCustomData != null && message.cloudCustomData != "";
if (hasCustomData) {
try {
final CloudCustomData messageCloudCustomData = CloudCustomData.fromJson(json.decode(TencentUtils.checkString(message.cloudCustomData) != null ? message.cloudCustomData! : "{}"));
final CloudCustomData messageCloudCustomData = CloudCustomData.fromJson(
json.decode(
TencentUtils.checkString(message.cloudCustomData) != null
? message.cloudCustomData!
: "{}"));
if (messageCloudCustomData.messageReply != null) {
MessageRepliedData.fromJson(messageCloudCustomData.messageReply!);
return true;
@ -377,7 +407,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
return false;
}
(bool isRevoke, bool isRevokeByAdmin) isRevokeMessage(V2TimMessage message, TUIChatSeparateViewModel model) {
(bool isRevoke, bool isRevokeByAdmin) isRevokeMessage(
V2TimMessage message, TUIChatSeparateViewModel model) {
if (message.status == 6) {
return (true, false);
} else if (model.chatConfig.isGroupAdminRecallEnabled) {
@ -393,9 +424,11 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
return (false, false);
}
Widget _messageItemBuilder(V2TimMessage messageItem, TUIChatSeparateViewModel model) {
Widget _messageItemBuilder(
V2TimMessage messageItem, TUIChatSeparateViewModel model) {
final msgType = messageItem.elemType;
final isShowJump = (model.jumpMsgID == messageItem.msgID) && (messageItem.msgID?.isNotEmpty ?? false);
final isShowJump = (model.jumpMsgID == messageItem.msgID) &&
(messageItem.msgID?.isNotEmpty ?? false);
final MessageItemBuilder? messageItemBuilder = widget.messageItemBuilder;
final isFromSelf = messageItem.isSelf ?? true;
void clearJump() {
@ -406,7 +439,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
switch (msgType) {
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
final customWidget = messageItemBuilder?.customMessageItemBuilder != null
final customWidget =
messageItemBuilder?.customMessageItemBuilder != null
? messageItemBuilder!.customMessageItemBuilder!(
messageItem,
isShowJump,
@ -450,7 +484,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
);
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
if (isReplyMessage(messageItem)) {
final customWidget = messageItemBuilder?.textReplyMessageItemBuilder != null
final customWidget =
messageItemBuilder?.textReplyMessageItemBuilder != null
? messageItemBuilder!.textReplyMessageItemBuilder!(
messageItem,
isShowJump,
@ -532,7 +567,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
isShowMessageReaction: widget.isUseMessageReaction,
);
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
final customWidget = messageItemBuilder?.groupTipsMessageItemBuilder != null
final customWidget =
messageItemBuilder?.groupTipsMessageItemBuilder != null
? messageItemBuilder!.groupTipsMessageItemBuilder!(
messageItem,
isShowJump,
@ -574,7 +610,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
isShowMessageReaction: widget.isUseMessageReaction,
);
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
final customWidget = messageItemBuilder?.locationMessageItemBuilder != null
final customWidget =
messageItemBuilder?.locationMessageItemBuilder != null
? messageItemBuilder!.locationMessageItemBuilder!(
messageItem,
isShowJump,
@ -583,7 +620,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
: null;
return customWidget ?? Text(TIM_t("[位置]"));
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
final customWidget = messageItemBuilder?.mergerMessageItemBuilder != null
final customWidget =
messageItemBuilder?.mergerMessageItemBuilder != null
? messageItemBuilder!.mergerMessageItemBuilder!(
messageItem,
isShowJump,
@ -608,7 +646,11 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
Widget _groupTipsMessageBuilder(TUIChatSeparateViewModel model) {
final messageItem = widget.message;
return Container(padding: const EdgeInsets.only(bottom: 20), child: TIMUIKitGroupTipsElem(groupTipsElem: messageItem.groupTipsElem!, groupMemberList: model.groupMemberList ?? []));
return Container(
padding: const EdgeInsets.only(bottom: 20),
child: TIMUIKitGroupTipsElem(
groupTipsElem: messageItem.groupTipsElem!,
groupMemberList: model.groupMemberList ?? []));
}
Widget _selfRevokeEditMessageBuilder(theme, TUIChatSeparateViewModel model) {
@ -624,7 +666,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
text: TIM_t("重新编辑"),
recognizer: TapGestureRecognizer()
..onTap = () {
widget.textFieldController?.setTextField(widget.message.textElem?.text ?? "");
widget.textFieldController
?.setTextField(widget.message.textElem?.text ?? "");
},
style: TextStyle(color: theme.primaryColor),
)
@ -641,12 +684,15 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
));
}
Widget _timeDividerBuilder(theme, int timeStamp, TUIChatSeparateViewModel model) {
Widget _timeDividerBuilder(
theme, int timeStamp, TUIChatSeparateViewModel model) {
return Container(
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(vertical: 20),
child: Text(
model.chatConfig.timeDividerConfig?.timestampParser != null ? (model.chatConfig.timeDividerConfig?.timestampParser!(timeStamp))! : TimeAgo().getTimeForMessage(timeStamp),
model.chatConfig.timeDividerConfig?.timestampParser != null
? (model.chatConfig.timeDividerConfig?.timestampParser!(timeStamp))!
: TimeAgo().getTimeForMessage(timeStamp),
style: widget.themeData?.timelineTextStyle ??
TextStyle(
fontSize: 12,
@ -670,7 +716,10 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
width: 100,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [const Color(0x00C0E1FF), theme.primaryColor ?? CommonColor.lightPrimaryColor]),
gradient: LinearGradient(colors: [
const Color(0x00C0E1FF),
theme.primaryColor ?? CommonColor.lightPrimaryColor
]),
)),
),
),
@ -702,7 +751,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
);
}
bool isRevocable(int timestamp) => (DateTime.now().millisecondsSinceEpoch / 1000).ceil() - timestamp < 120;
bool isRevocable(int timestamp) =>
(DateTime.now().millisecondsSinceEpoch / 1000).ceil() - timestamp < 120;
// TODO :
@ -723,21 +773,38 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
final screenHeight = MediaQuery.of(context).size.height;
final screenWidth = MediaQuery.of(context).size.width;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isLongMessage = context.size!.height + 350 > screenHeight && !(isDesktopScreen);
final tapDetails = (isDesktopScreen || isLongMessage) ? (details ?? _tapDetails) : details;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isLongMessage =
context.size!.height + 350 > screenHeight && !(isDesktopScreen);
final tapDetails =
(isDesktopScreen || isLongMessage) ? (details ?? _tapDetails) : details;
final isSelf = message.isSelf ?? true;
final targetWidth = min(MediaQuery.of(context).size.width * 0.84, 350).toDouble();
final double dx = !isSelf ? min(tapDetails?.globalPosition.dx ?? targetWidth, screenWidth - targetWidth) : max(tapDetails?.globalPosition.dx ?? targetWidth, targetWidth).toDouble();
final double dy = min(tapDetails?.globalPosition.dy ?? MediaQuery.of(context).size.height, MediaQuery.of(context).size.height - 320).toDouble();
final targetWidth =
min(MediaQuery.of(context).size.width * 0.84, 350).toDouble();
final double dx = !isSelf
? min(tapDetails?.globalPosition.dx ?? targetWidth,
screenWidth - targetWidth)
: max(tapDetails?.globalPosition.dx ?? targetWidth, targetWidth)
.toDouble();
final double dy = min(
tapDetails?.globalPosition.dy ?? MediaQuery.of(context).size.height,
MediaQuery.of(context).size.height - 320)
.toDouble();
final finalTapDetail = tapDetails != null
? TapDownDetails(
globalPosition: Offset(dx, dy),
)
: null;
initTools(context: c, model: model, isShowMoreSticker: isShowMoreSticker, details: finalTapDetail, theme: theme, isFromWideToolTip: isFromWideTooltip);
initTools(
context: c,
model: model,
isShowMoreSticker: isShowMoreSticker,
details: finalTapDetail,
theme: theme,
isFromWideToolTip: isFromWideTooltip);
tooltip!.show(c, targetCenter: finalTapDetail?.globalPosition);
}
@ -750,15 +817,26 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
}
}
Future<V2TimValueCallback<V2TimMessageChangeInfo>> _modifySticker(int sticker) async {
Future<V2TimValueCallback<V2TimMessageChangeInfo>> _modifySticker(
int sticker) async {
return await Future.delayed(const Duration(milliseconds: 50), () async {
return await MessageReactionUtils.clickOnSticker(widget.message, sticker);
});
}
initTools({BuildContext? context, bool isLongMessage = false, required TUIChatSeparateViewModel model, TUITheme? theme, bool? isShowMoreSticker, TapDownDetails? details, bool? isFromWideToolTip}) {
final isUseMessageReaction = widget.message.elemType == 2 ? false : model.chatConfig.isUseMessageReaction;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
initTools(
{BuildContext? context,
bool isLongMessage = false,
required TUIChatSeparateViewModel model,
TUITheme? theme,
bool? isShowMoreSticker,
TapDownDetails? details,
bool? isFromWideToolTip}) {
final isUseMessageReaction = widget.message.elemType == 2
? false
: model.chatConfig.isUseMessageReaction;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isSelf = widget.message.isSelf ?? true;
double arrowTipDistance = 30;
double arrowBaseWidth = 10;
@ -767,7 +845,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
TooltipDirection popupDirection = TooltipDirection.up;
double? left;
double? right;
SelectEmojiPanelPosition selectEmojiPanelPosition = SelectEmojiPanelPosition.down;
SelectEmojiPanelPosition selectEmojiPanelPosition =
SelectEmojiPanelPosition.down;
if (context != null) {
RenderBox? box = _key.currentContext?.findRenderObject() as RenderBox?;
if (details != null && box != null) {
@ -790,7 +869,9 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
Offset offset = box.localToGlobal(Offset.zero);
double boxWidth = box.size.width;
if (isSelf) {
right = screenWidth - offset.dx - ((isUseMessageReaction) ? boxWidth : (boxWidth / 1.3));
right = screenWidth -
offset.dx -
((isUseMessageReaction) ? boxWidth : (boxWidth / 1.3));
} else {
left = offset.dx;
}
@ -802,7 +883,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
popupDirection = TooltipDirection.down;
}
}
arrowTipDistance = (context.size!.height / 2).roundToDouble() + (isLongMessage ? -120 : 10);
arrowTipDistance = (context.size!.height / 2).roundToDouble() +
(isLongMessage ? -120 : 10);
}
}
@ -812,7 +894,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
widget.toolTipsConfig?.showReplyMessage = false;
}
if (widget.message.status != MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL && !(widget.message.hasRiskContent ?? false)) {
if (widget.message.status != MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL &&
!(widget.message.hasRiskContent ?? false)) {
widget.toolTipsConfig?.showForwardMessage = true;
} else {
widget.toolTipsConfig?.showForwardMessage = false;
@ -835,7 +918,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
showCloseButton: ShowCloseButton.none,
touchThroughAreaShape: ClipAreaShape.rectangle,
content: TIMUIKitMessageTooltip(
iSUseDefaultHoverBar: model.chatConfig.isUseMessageHoverBarOnDesktop && widget.customMessageHoverBarOnDesktop == null,
iSUseDefaultHoverBar: model.chatConfig.isUseMessageHoverBarOnDesktop &&
widget.customMessageHoverBarOnDesktop == null,
model: model,
groupMemberInfo: widget.groupMemberInfo,
isShowMoreSticker: isShowMoreSticker ?? false,
@ -843,7 +927,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
isUseMessageReaction: isUseMessageReaction,
message: widget.message,
allowAtUserWhenReply: widget.allowAtUserWhenReply,
onLongPressForOthersHeadPortrait: widget.onLongPressForOthersHeadPortrait,
onLongPressForOthersHeadPortrait:
widget.onLongPressForOthersHeadPortrait,
selectEmojiPanelPosition: selectEmojiPanelPosition,
onCloseTooltip: () => tooltip?.close(),
onSelectSticker: (int value) {
@ -854,7 +939,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
);
}
Widget _getMessageItemBuilder(V2TimMessage message, int? messageStatues, TUIChatSeparateViewModel model) {
Widget _getMessageItemBuilder(V2TimMessage message, int? messageStatues,
TUIChatSeparateViewModel model) {
final messageBuilder = _messageItemBuilder;
return messageBuilder(widget.message, model);
@ -896,7 +982,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
}
}
List<MessageHoverControlItem> getWideMessageHoverControlBar(TUIChatSeparateViewModel model, TUITheme theme) {
List<MessageHoverControlItem> getWideMessageHoverControlBar(
TUIChatSeparateViewModel model, TUITheme theme) {
return [
if (widget.isUseMessageReaction ?? false)
MessageHoverControlItem(
@ -907,7 +994,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
color: hexToColor("8f959e"),
),
onClick: (details) {
_onOpenToolTip(context, widget.message, model, theme, details, true, true);
_onOpenToolTip(
context, widget.message, model, theme, details, true, true);
},
),
if (widget.toolTipsConfig?.showReplyMessage ?? true)
@ -921,14 +1009,21 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
onClick: (_) {
model.repliedMessage = widget.message;
final isSelf = widget.message.isSelf ?? true;
final isGroup = TencentUtils.checkString(widget.message.groupID) != null;
final isAtWhenReply = !isSelf && isGroup && widget.allowAtUserWhenReply && widget.onLongPressForOthersHeadPortrait != null;
final isGroup =
TencentUtils.checkString(widget.message.groupID) != null;
final isAtWhenReply = !isSelf &&
isGroup &&
widget.allowAtUserWhenReply &&
widget.onLongPressForOthersHeadPortrait != null;
/// If replying to a self message, do not add a at tag, only requestFocus.
widget.onLongPressForOthersHeadPortrait!(!isAtWhenReply ? null : widget.message.sender, !isAtWhenReply ? null : widget.message.nickName);
widget.onLongPressForOthersHeadPortrait!(
!isAtWhenReply ? null : widget.message.sender,
!isAtWhenReply ? null : widget.message.nickName);
},
),
if ((widget.toolTipsConfig?.showForwardMessage ?? true) && !model.isVoteMessage(widget.message))
if ((widget.toolTipsConfig?.showForwardMessage ?? true) &&
!model.isVoteMessage(widget.message))
MessageHoverControlItem(
name: TIM_t("转发"),
icon: Icon(
@ -968,7 +1063,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
color: hexToColor("8f959e"),
),
onClick: (details) {
_onOpenToolTip(context, widget.message, model, theme, details, true, false);
_onOpenToolTip(
context, widget.message, model, theme, details, true, false);
},
),
...?model.chatConfig.additionalDesktopMessageHoverBarItem
@ -978,17 +1074,37 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
_onMsgSendFailIconTap(V2TimMessage message, TUIChatSeparateViewModel model) {
final convID = model.conversationID;
final convType = model.conversationType;
MessageUtils.handleMessageError(model.reSendFailMessage(message: message, convType: convType ?? ConvType.c2c, convID: convID), context);
MessageUtils.handleMessageError(
model.reSendFailMessage(
message: message,
convType: convType ?? ConvType.c2c,
convID: convID),
context);
}
Widget renderHoverTipAndReadStatus(TUIChatSeparateViewModel model, bool isSelf, V2TimMessage message, bool isPeerRead, TUITheme theme, bool isDownloadWaiting) {
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final customHoverBar = widget.customMessageHoverBarOnDesktop != null ? widget.customMessageHoverBarOnDesktop!(message) : null;
final wideHoverTipList = (model.chatConfig.isUseMessageHoverBarOnDesktop && customHoverBar == null) ? getWideMessageHoverControlBar(model, theme) : [];
final lastItemName = wideHoverTipList.isNotEmpty ? wideHoverTipList.last.name : "";
Widget renderHoverTipAndReadStatus(
TUIChatSeparateViewModel model,
bool isSelf,
V2TimMessage message,
bool isPeerRead,
TUITheme theme,
bool isDownloadWaiting) {
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final customHoverBar = widget.customMessageHoverBarOnDesktop != null
? widget.customMessageHoverBarOnDesktop!(message)
: null;
final wideHoverTipList = (model.chatConfig.isUseMessageHoverBarOnDesktop &&
customHoverBar == null)
? getWideMessageHoverControlBar(model, theme)
: [];
final lastItemName =
wideHoverTipList.isNotEmpty ? wideHoverTipList.last.name : "";
// 1
Future<void> _conditionalDelay() async {
if (!(model.hasDelayedRenderSendingStatus(message.id ?? message.msgID!) ?? true)) {
if (!(model.hasDelayedRenderSendingStatus(message.id ?? message.msgID!) ??
true)) {
model.setDelayedRenderSendingStatus(message.id ?? message.msgID!);
await Future.delayed(const Duration(seconds: 1));
}
@ -999,9 +1115,14 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (isDesktopScreen && isShowWideToolTip && customHoverBar == null && !((widget.message.elemType == 6 && isDownloadWaiting)))
if (isDesktopScreen &&
isShowWideToolTip &&
customHoverBar == null &&
!((widget.message.elemType == 6 && isDownloadWaiting)))
Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4), border: Border.all(color: hexToColor("d9dde0"), width: 1)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(color: hexToColor("d9dde0"), width: 1)),
margin: const EdgeInsets.symmetric(horizontal: 4),
child: Row(
children: wideHoverTipList
@ -1033,12 +1154,17 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
.toList(),
),
),
if (isDesktopScreen && isShowWideToolTip && customHoverBar != null) customHoverBar,
if (!isDesktopScreen || (model.chatConfig.isUseMessageHoverBarOnDesktop && customHoverBar == null && !isShowWideToolTip))
if (isDesktopScreen && isShowWideToolTip && customHoverBar != null)
customHoverBar,
if (!isDesktopScreen ||
(model.chatConfig.isUseMessageHoverBarOnDesktop &&
customHoverBar == null &&
!isShowWideToolTip))
const SizedBox(
height: 20,
),
if (isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL)
if (isSelf &&
message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL)
Container(
padding: const EdgeInsets.only(bottom: 3),
margin: const EdgeInsets.only(right: 6),
@ -1069,7 +1195,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
},
),
if (model.chatConfig.isShowReadingStatus &&
isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC &&
isSelf &&
message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC &&
(message.needReadReceipt ?? false) &&
!model.isVoteMessage(widget.message))
TIMUIKitMessageReadReceipt(
@ -1082,13 +1209,16 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context);
final isDownloadWaiting = context.select<TUIChatGlobalModel, bool>((value) => value.isWaiting(widget.message.msgID ?? ""));
final TUIChatSeparateViewModel model =
Provider.of<TUIChatSeparateViewModel>(context);
final isDownloadWaiting = context.select<TUIChatGlobalModel, bool>(
(value) => value.isWaiting(widget.message.msgID ?? ""));
final TUITheme theme = value.theme;
final message = widget.message;
final msgType = message.elemType;
final isSelf = message.isSelf ?? true;
final isGroupTipsMsg = msgType == MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS;
final isGroupTipsMsg =
msgType == MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS;
final revokeStatus = isRevokeMessage(message, model);
final isRevokedMsg = revokeStatus.$1;
@ -1098,10 +1228,14 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
final isLatestDivider = msgType == 101;
final isPeerRead = message.isPeerRead ?? false;
final isGroupMessage = model.conversationType == ConvType.group;
final bool isRevokeEditable = widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
final isShowNickNameForSelf = isGroupMessage && model.chatConfig.isShowSelfNameInGroup;
final isShowNickNameForOthers = isGroupMessage && model.chatConfig.isShowOthersNameInGroup;
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final bool isRevokeEditable =
widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
final isShowNickNameForSelf =
isGroupMessage && model.chatConfig.isShowSelfNameInGroup;
final isShowNickNameForOthers =
isGroupMessage && model.chatConfig.isShowOthersNameInGroup;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
if (isTimeDivider) {
return _timeDividerBuilder(theme, message.timestamp ?? 0, model);
}
@ -1116,7 +1250,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
if (isGroupTipsMsg) {
if (widget.messageItemBuilder?.groupTipsMessageItemBuilder != null) {
final groupTipsMessage = widget.messageItemBuilder!.groupTipsMessageItemBuilder!(
final groupTipsMessage =
widget.messageItemBuilder!.groupTipsMessageItemBuilder!(
message,
(model.jumpMsgID == message.msgID),
clearJump,
@ -1127,8 +1262,16 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
}
if (isRevokedMsg) {
final displayName = isAdminRevoke ? TIM_t("管理员") : (isSelf ? TIM_t("") : TencentUtils.checkString(message.nickName) ?? TencentUtils.checkString(message.sender) ?? message.userID);
return isSelf && isRevokeEditable && isRevocable(message.timestamp!) ? _selfRevokeEditMessageBuilder(theme, model) : _revokedMessageBuilder(theme, displayName ?? "");
final displayName = isAdminRevoke
? TIM_t("管理员")
: (isSelf
? TIM_t("")
: TencentUtils.checkString(message.nickName) ??
TencentUtils.checkString(message.sender) ??
message.userID);
return isSelf && isRevokeEditable && isRevocable(message.timestamp!)
? _selfRevokeEditMessageBuilder(theme, model)
: _revokedMessageBuilder(theme, displayName ?? "");
}
// 使
@ -1157,7 +1300,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
},
child: LayoutBuilder(
builder: (context, constraints) => Container(
padding: EdgeInsets.only(left: isSelf ? 0 : 16, right: isSelf ? 16 : 0),
padding:
EdgeInsets.only(left: isSelf ? 0 : 16, right: isSelf ? 16 : 0),
margin: widget.padding ?? const EdgeInsets.only(bottom: 20),
child: Row(
key: _key,
@ -1166,7 +1310,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
children: [
if (model.isMultiSelect)
Container(
margin: EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0),
margin: EdgeInsets.only(
right: 12, top: 10, left: isSelf ? 16 : 0),
child: CheckBoxButton(
isChecked: model.getSelectedMessageList().contains(message),
onChanged: (value) {
@ -1177,14 +1322,16 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
Expanded(
child: MouseRegion(
onEnter: (_) {
if (isDesktopScreen && model.chatConfig.isUseMessageHoverBarOnDesktop) {
if (isDesktopScreen &&
model.chatConfig.isUseMessageHoverBarOnDesktop) {
setState(() {
isShowWideToolTip = true;
});
}
},
onExit: (_) {
if (isDesktopScreen && model.chatConfig.isUseMessageHoverBarOnDesktop) {
if (isDesktopScreen &&
model.chatConfig.isUseMessageHoverBarOnDesktop) {
Tooltip.dismissAllToolTips();
Future.delayed(const Duration(milliseconds: 100), () {
setState(() {
@ -1194,10 +1341,13 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
}
},
child: GestureDetector(
behavior: model.isMultiSelect ? HitTestBehavior.translucent : null,
behavior: model.isMultiSelect
? HitTestBehavior.translucent
: null,
onTap: () {
if (model.isMultiSelect) {
final checked = model.getSelectedMessageList().contains(message);
final checked =
model.getSelectedMessageList().contains(message);
model.setMessageItemChecked(message, !checked);
} else {
return;
@ -1205,59 +1355,81 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: isSelf ? MainAxisAlignment.end : MainAxisAlignment.start,
mainAxisAlignment: isSelf
? MainAxisAlignment.end
: MainAxisAlignment.start,
children: [
if (!isSelf && widget.showAvatar)
GestureDetector(
onLongPress: () {
if (widget.onLongPressForOthersHeadPortrait != null) {}
if (widget.onLongPressForOthersHeadPortrait !=
null) {}
if (model.chatConfig.isAllowLongPressAvatarToAt) {
widget.onLongPressForOthersHeadPortrait!(message.sender, message.nickName);
widget.onLongPressForOthersHeadPortrait!(
message.sender, message.nickName);
}
},
onTapDown: isDesktopScreen
? (details) {
if (widget.onTapForOthersPortrait != null && widget.allowAvatarTap) {
widget.onTapForOthersPortrait!(message.sender ?? "", details);
if (widget.onTapForOthersPortrait != null &&
widget.allowAvatarTap) {
widget.onTapForOthersPortrait!(
message.sender ?? "", details);
}
}
: null,
onTap: isDesktopScreen
? null
: () {
if (widget.onTapForOthersPortrait != null && widget.allowAvatarTap) {
widget.onTapForOthersPortrait!(message.sender ?? "", TapDownDetails());
if (widget.onTapForOthersPortrait != null &&
widget.allowAvatarTap) {
widget.onTapForOthersPortrait!(
message.sender ?? "",
TapDownDetails());
}
},
onSecondaryTap: isDesktopScreen
? null
: () {
if (widget.onSecondaryTapForOthersPortrait != null && widget.allowAvatarTap) {
widget.onSecondaryTapForOthersPortrait!(message.sender ?? "", TapDownDetails());
if (widget.onSecondaryTapForOthersPortrait !=
null &&
widget.allowAvatarTap) {
widget.onSecondaryTapForOthersPortrait!(
message.sender ?? "",
TapDownDetails());
}
},
onSecondaryTapDown: isDesktopScreen
? (details) {
if (widget.onSecondaryTapForOthersPortrait != null && widget.allowAvatarTap) {
widget.onSecondaryTapForOthersPortrait!(message.sender ?? "", details);
if (widget.onSecondaryTapForOthersPortrait !=
null &&
widget.allowAvatarTap) {
widget.onSecondaryTapForOthersPortrait!(
message.sender ?? "", details);
}
}
: null,
child: widget.userAvatarBuilder != null
? widget.userAvatarBuilder!(context, message)
: Container(
margin: (isSelf && isShowNickNameForSelf) || (!isSelf && isShowNickNameForOthers) ? const EdgeInsets.only(top: 2) : null,
margin: (isSelf && isShowNickNameForSelf) ||
(!isSelf && isShowNickNameForOthers)
? const EdgeInsets.only(top: 2)
: null,
child: SizedBox(
width: 40,
height: 40,
child: Avatar(
faceUrl: message.faceUrl ?? "",
showName: MessageUtils.getDisplayName(message),
showName: MessageUtils.getDisplayName(
message),
),
),
),
),
if (isSelf && widget.message.elemType == 6 && isDownloadWaiting)
if (isSelf &&
widget.message.elemType == 6 &&
isDownloadWaiting)
Container(
margin: const EdgeInsets.only(top: 46, right: 10),
child: LoadingAnimationWidget.threeArchedCircle(
@ -1266,52 +1438,96 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
),
),
Container(
margin: widget.showAvatar ? (isSelf ? const EdgeInsets.only(right: 13) : const EdgeInsets.only(left: 13)) : null,
margin: widget.showAvatar
? (isSelf
? const EdgeInsets.only(right: 13)
: const EdgeInsets.only(left: 13))
: null,
child: Column(
crossAxisAlignment: isSelf ? CrossAxisAlignment.end : CrossAxisAlignment.start,
crossAxisAlignment: isSelf
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
if ((isSelf && isShowNickNameForSelf) || (!isSelf && isShowNickNameForOthers))
if ((isSelf && isShowNickNameForSelf) ||
(!isSelf && isShowNickNameForOthers))
widget.topRowBuilder != null
? widget.topRowBuilder!(context, message)
: Container(
// margin: const EdgeInsets.only(bottom: 4),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width / 1.7),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context)
.size
.width /
1.7),
child: Text(
MessageUtils.getDisplayName(message),
overflow: TextOverflow.ellipsis,
style: widget.themeData?.nickNameTextStyle ?? TextStyle(fontSize: 12, color: theme.weakTextColor),
style: widget.themeData
?.nickNameTextStyle ??
TextStyle(
fontSize: 12,
color: theme.weakTextColor),
),
)),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (isSelf) renderHoverTipAndReadStatus(model, isSelf, message, isPeerRead, theme, isDownloadWaiting),
if (isSelf)
renderHoverTipAndReadStatus(
model,
isSelf,
message,
isPeerRead,
theme,
isDownloadWaiting),
Container(
constraints: BoxConstraints(
maxWidth: constraints.maxWidth * 0.77,
),
child: Builder(builder: (context) {
return GestureDetector(
child: IgnorePointer(ignoring: model.isMultiSelect, child: _getMessageItemBuilder(message, message.status, model)),
child: IgnorePointer(
ignoring: model.isMultiSelect,
child: _getMessageItemBuilder(
message,
message.status,
model)),
onSecondaryTapDown: (details) {
if (widget.onLongPress != null) {
widget.onLongPress!(context, message);
widget.onLongPress!(
context, message);
return;
}
if (!PlatformUtils().isMobile) {
if (widget.allowLongPress) {
_onOpenToolTip(context, message, model, theme, details, false, false);
_onOpenToolTip(
context,
message,
model,
theme,
details,
false,
false);
}
}
},
onLongPress: () {
if (widget.onLongPress != null) {
widget.onLongPress!(context, message);
widget.onLongPress!(
context, message);
return;
}
if (widget.allowLongPress && !isDesktopScreen) {
_onOpenToolTip(context, message, model, theme, null, false, false);
if (widget.allowLongPress &&
!isDesktopScreen) {
_onOpenToolTip(
context,
message,
model,
theme,
null,
false,
false);
}
},
onTapDown: (details) {
@ -1320,24 +1536,46 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
);
}),
),
if (!isSelf && message.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND && message.localCustomInt != null && message.localCustomInt != HistoryMessageDartConstant.read)
Padding(padding: const EdgeInsets.only(left: 5, bottom: 12), child: Icon(Icons.circle, color: theme.cautionColor, size: 10)),
if (!isSelf) renderHoverTipAndReadStatus(model, isSelf, message, isPeerRead, theme, isDownloadWaiting),
if (!isSelf &&
message.elemType ==
MessageElemType
.V2TIM_ELEM_TYPE_SOUND &&
message.localCustomInt != null &&
message.localCustomInt !=
HistoryMessageDartConstant.read)
Padding(
padding: const EdgeInsets.only(
left: 5, bottom: 12),
child: Icon(Icons.circle,
color: theme.cautionColor,
size: 10)),
if (!isSelf)
renderHoverTipAndReadStatus(
model,
isSelf,
message,
isPeerRead,
theme,
isDownloadWaiting),
],
),
TIMUIKitTextTranslationElem(
message: message,
isUseDefaultEmoji: widget.isUseDefaultEmoji,
customEmojiStickerList: widget.customEmojiStickerList,
customEmojiStickerList:
widget.customEmojiStickerList,
isFromSelf: message.isSelf ?? true,
isShowJump: false,
clearJump: () {},
chatModel: model),
if (widget.bottomRowBuilder != null) widget.bottomRowBuilder!(context, message)
if (widget.bottomRowBuilder != null)
widget.bottomRowBuilder!(context, message)
],
),
),
if (!isSelf && widget.message.elemType == 6 && isDownloadWaiting)
if (!isSelf &&
widget.message.elemType == 6 &&
isDownloadWaiting)
Container(
margin: const EdgeInsets.only(top: 46, left: 10),
child: LoadingAnimationWidget.threeArchedCircle(
@ -1353,11 +1591,17 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
height: 40,
child: InkWell(
onTapDown: (details) {
if (widget.onTapForOthersPortrait != null && widget.allowAvatarTap) {
widget.onTapForOthersPortrait!(message.sender ?? "", details);
if (widget.onTapForOthersPortrait !=
null &&
widget.allowAvatarTap) {
widget.onTapForOthersPortrait!(
message.sender ?? "", details);
}
},
child: Avatar(faceUrl: message.faceUrl ?? "", showName: MessageUtils.getDisplayName(message)),
child: Avatar(
faceUrl: message.faceUrl ?? "",
showName: MessageUtils.getDisplayName(
message)),
),
),
],

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,13 +386,17 @@ class _GroupProfileGroupManagePageState extends TIMUIKitState<GroupProfileGroupM
child: _buildListItem(
context,
e!,
ActionPane(motion: const DrawerMotion(), children: [
ActionPane(
motion: const DrawerMotion(),
children: [
SlidableAction(
onPressed: (_) {
widget.model.muteGroupMember(e.userID, false, serverTime);
widget.model.muteGroupMember(
e.userID, false, serverTime);
},
flex: 1,
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
backgroundColor: theme.cautionColor ??
CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
@ -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,17 +732,22 @@ 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: [
ActionPane(
motion: const DrawerMotion(),
children: [
SlidableAction(
onPressed: (_) {
_removeAdmin(context, e);
},
flex: 1,
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
backgroundColor: theme.cautionColor ??
CommonColor.cautionColor,
autoClose: true,
label: TIM_t("删除"),
)
@ -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: