diff --git a/CHANGELOG.md b/CHANGELOG.md index bd8b286..195aa26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 3.1.0 +## Bug Fixes +* The interface for deleting messages is changed to the interface for deleting cloud messages. +* C2C messages support read receipts +* Fix and optimize some issues + # 3.0.0 ## Breaking Changes * Migrated to Flutter 3.24.0 diff --git a/README.md b/README.md index b2c569a..91c8c49 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +# +It is recommended to download the source code from [pub.dev](https://pub.dev/packages/tencent_cloud_chat_uikit/versions) + ## Product Introduction You only need to integrate Chat SDK to easily gain chat, conversation, group capabilities, and you can also communicate with other products such as whiteboards through signaling messages. Chat can cover various business scenarios, support the access and use of various platforms, and fully meet the communication needs. diff --git a/example/1400187352_3130303435333633/im.db b/example/1400187352_3130303435333633/im.db deleted file mode 100644 index 84687bc..0000000 Binary files a/example/1400187352_3130303435333633/im.db and /dev/null differ diff --git a/example/1400187352_3130303435333633/im.db-shm b/example/1400187352_3130303435333633/im.db-shm deleted file mode 100644 index 722a46c..0000000 Binary files a/example/1400187352_3130303435333633/im.db-shm and /dev/null differ diff --git a/example/1400187352_3130303435333633/im.db-wal b/example/1400187352_3130303435333633/im.db-wal deleted file mode 100644 index 07d73d7..0000000 Binary files a/example/1400187352_3130303435333633/im.db-wal and /dev/null differ diff --git a/example/1400187352_3130303435333633/msg_0.db b/example/1400187352_3130303435333633/msg_0.db deleted file mode 100644 index 84687bc..0000000 Binary files a/example/1400187352_3130303435333633/msg_0.db and /dev/null differ diff --git a/example/1400187352_3130303435333633/msg_0.db-shm b/example/1400187352_3130303435333633/msg_0.db-shm deleted file mode 100644 index 015c0c0..0000000 Binary files a/example/1400187352_3130303435333633/msg_0.db-shm and /dev/null differ diff --git a/example/1400187352_3130303435333633/msg_0.db-wal b/example/1400187352_3130303435333633/msg_0.db-wal deleted file mode 100644 index c5a8d0a..0000000 Binary files a/example/1400187352_3130303435333633/msg_0.db-wal and /dev/null differ diff --git a/example/imsdk_C.mmap2 b/example/imsdk_C.mmap2 deleted file mode 100755 index e5b999e..0000000 Binary files a/example/imsdk_C.mmap2 and /dev/null differ diff --git a/example/imsdk_C_20221229.xlog b/example/imsdk_C_20221229.xlog deleted file mode 100644 index c27d095..0000000 Binary files a/example/imsdk_C_20221229.xlog and /dev/null differ diff --git a/example/imsdk_api_report b/example/imsdk_api_report deleted file mode 100644 index 3bd4f45..0000000 Binary files a/example/imsdk_api_report and /dev/null differ diff --git a/example/imsdk_config_1400187352 b/example/imsdk_config_1400187352 deleted file mode 100644 index a41f4d5..0000000 Binary files a/example/imsdk_config_1400187352 and /dev/null differ diff --git a/example/imsdk_sensitive_word_1400187352 b/example/imsdk_sensitive_word_1400187352 deleted file mode 100644 index b8faf10..0000000 --- a/example/imsdk_sensitive_word_1400187352 +++ /dev/null @@ -1,2 +0,0 @@ - -Hϼhѽ#%-a \ No newline at end of file diff --git a/lib/business_logic/listener_model/tui_group_listener_model.dart b/lib/business_logic/listener_model/tui_group_listener_model.dart index 60b6e76..ac164db 100644 --- a/lib/business_logic/listener_model/tui_group_listener_model.dart +++ b/lib/business_logic/listener_model/tui_group_listener_model.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:flutter/cupertino.dart'; -import 'package:tencent_im_base/tencent_im_base.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; @@ -15,6 +14,8 @@ class NeedUpdate { final String groupID; final UpdateType updateType; final String extraData; + int? groupInfoSubType; + String? ownerID; NeedUpdate(this.groupID, this.updateType, this.extraData); } @@ -44,10 +45,11 @@ class TUIGroupListenerModel extends ChangeNotifier { onMemberKicked: (groupID, opUser, memberList) async { if (_isLoginUserKickedFromGroup(groupID, memberList)) { _deleteGroupConversation(groupID); + + final groupName = await _getGroupName(groupID); + _needUpdate = NeedUpdate(groupID, UpdateType.kickedFromGroup, groupName); + notifyListeners(); } - final groupName = await _getGroupName(groupID); - _needUpdate = NeedUpdate(groupID, UpdateType.kickedFromGroup, groupName); - notifyListeners(); }, onMemberEnter: (String groupID, List memberList) { _needUpdate = NeedUpdate(groupID, UpdateType.memberList, ""); @@ -59,6 +61,12 @@ class TUIGroupListenerModel extends ChangeNotifier { }, onGroupInfoChanged: (groupID, changeInfos) { _needUpdate = NeedUpdate(groupID, UpdateType.groupInfo, ""); + for (V2TimGroupChangeInfo info in changeInfos) { + if (info.type == GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER) { + _needUpdate!.groupInfoSubType = GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER; + _needUpdate!.ownerID = info.value; + } + } notifyListeners(); }, onReceiveJoinApplication: diff --git a/lib/business_logic/separate_models/tui_chat_separate_view_model.dart b/lib/business_logic/separate_models/tui_chat_separate_view_model.dart index 1f84f74..7259b4f 100644 --- a/lib/business_logic/separate_models/tui_chat_separate_view_model.dart +++ b/lib/business_logic/separate_models/tui_chat_separate_view_model.dart @@ -5,13 +5,12 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; -// ignore: unnecessary_import -import 'package:flutter/foundation.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/chat_life_cycle.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_model_tools.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; +import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart'; import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart'; @@ -33,6 +32,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { final TUIChatGlobalModel globalModel = serviceLocator(); final TUIChatModelTools tools = serviceLocator(); final TUISelfInfoViewModel selfModel = serviceLocator(); + final TUIConversationViewModel conversationViewModel = serviceLocator(); final _uuid = const Uuid(); ChatLifeCycle? lifeCycle; @@ -45,7 +45,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier { bool haveMoreLatestData = false; String _currentPlayedMsgId = ""; GroupReceiptAllowType? _groupType; - List _multiSelectedMessageList = []; + // List _multiSelectedMessageList = []; + Map _selectedPositions = {}; V2TimMessage? _repliedMessage; String _jumpMsgID = ""; bool _isGroupExist = true; @@ -68,7 +69,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier { String? _groupID; Map get groupUserShowName => _groupUserShowName; - final List _sendingMessageIDList = []; + // value 的 bool 值表示是否已经延迟显示过发送进度 + final Map _sendingMessageIDMap = {}; + Map _readReceiptMap = {}; set groupUserShowName(Map value) { _groupUserShowName = value; @@ -124,11 +127,35 @@ class TUIChatSeparateViewModel extends ChangeNotifier { _notify(); } - List get multiSelectedMessageList => _multiSelectedMessageList; + List getSelectedMessageList() { + List selectList = []; + if (_selectedPositions.isEmpty) { + return selectList; + } - set multiSelectedMessageList(List value) { - _multiSelectedMessageList = value; - _notify(); + List currentHistoryMsgList = getOriginMessageList(); + for (var v2TimMessage in currentHistoryMsgList) { + if (_selectedPositions.containsKey(v2TimMessage.msgID) && _selectedPositions[v2TimMessage.msgID]!) { + selectList.add(v2TimMessage); + } + } + + return selectList.reversed.toList(); + } + + List getSelectedMessageIDList() { + List selectList = []; + if (_selectedPositions.isEmpty) { + return selectList; + } + + for (String msgID in _selectedPositions.keys) { + if (_selectedPositions[msgID]!) { + selectList.add(msgID); + } + } + + return selectList; } V2TimMessage? get repliedMessage => _repliedMessage; @@ -233,7 +260,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { globalModel.setMessageListPosition( conversationID, HistoryMessagePosition.bottom); globalModel.setChatConfig(chatConfig); - globalModel.clearRecivedNewMessageCount(); + globalModel.clearReceivedNewMessageCount(); if (conversationType == ConvType.group) { _groupID = groupID; @@ -290,7 +317,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { required int seq, }) async { List msgList = []; - haveMoreData = false; + bool tempHaveMoreData = false; final previousResponse = await _messageService.getHistoryMessageListWithComplete( @@ -300,7 +327,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { groupID: conversationType == ConvType.group ? conversationID : null, lastMsgSeq: max(seq, 0)); msgList = previousResponse?.messageList ?? []; - haveMoreData = !(previousResponse?.isFinished ?? false); + tempHaveMoreData = !(previousResponse?.isFinished ?? false); haveMoreLatestData = true; globalModel.setMessageListPosition( conversationID, HistoryMessagePosition.notShowLatest); @@ -318,15 +345,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier { globalModel.setMessageList(conversationID, msgList, needResetNewMessageCount: false); - if (chatConfig.isShowGroupReadingStatus && - conversationType == ConvType.group) { + if (chatConfig.isShowReadingStatus) { _getMsgReadReceipt(msgList); } - if (chatConfig.isReportGroupReadingStatus && - conversationType == ConvType.group) { - _setMsgReadReceipt(msgList); - } + haveMoreData = tempHaveMoreData; return haveMoreData; } @@ -339,10 +362,12 @@ class TUIChatSeparateViewModel extends ChangeNotifier { LoadDirection direction = LoadDirection.previous, }) async { try { + bool tempHaveMoreData = false; // 根据加载方向设置是否还能继续加载更多消息 direction == LoadDirection.latest ? haveMoreLatestData = false - : haveMoreData = false; + : tempHaveMoreData = false; + // 获取当前聊天对话的历史消息列表 final currentRecordList = globalModel.messageListMap[conversationID]; @@ -368,8 +393,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier { if (direction == LoadDirection.latest) { haveMoreLatestData = !response.isFinished; } else { - haveMoreData = !response.isFinished; + tempHaveMoreData = !response.isFinished; } + _notify(); // 根据lastMsgID判断是否为分页加载 @@ -380,7 +406,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { // 根据加载方向拼接消息列表 if (direction == LoadDirection.latest) { globalModel.receivedNewMessageCount = - globalModel.receivedMessageListCount + messageList.length; + globalModel.receivedNewMessageCount + messageList.length; messageList = messageList.reversed.toList(); newList = _combineMessageList(messageList, currentRecordList); } else { @@ -413,16 +439,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier { } // 获取已读未读状态 - if (chatConfig.isShowGroupReadingStatus && - conversationType == ConvType.group && - response.messageList.isNotEmpty) { + if (chatConfig.isShowReadingStatus && response.messageList.isNotEmpty) { _getMsgReadReceipt(response.messageList); } - if (chatConfig.isReportGroupReadingStatus && - conversationType == ConvType.group && - response.messageList.isNotEmpty) { - _setMsgReadReceipt(response.messageList); - } // 根据加载方向更新是否还能继续加载更多消息 if (direction == LoadDirection.latest && !haveMoreLatestData) { @@ -431,6 +450,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { } _notify(); + haveMoreData = tempHaveMoreData; return haveMoreData; } catch (e) { // ignore: avoid_print @@ -439,7 +459,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { } } -// 拼接聊天记录 + // 拼接聊天记录 List _combineMessageList( List first, List second) { return [...first, ...second]; @@ -458,7 +478,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { _getMsgReadReceipt(List message) async { final msgID = message - .where((e) => (e.isSelf ?? true) && (e.needReadReceipt ?? false)) + .where((e) => (e.isSelf ?? true) && (e.needReadReceipt ?? false) && (e.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC)) .map((e) => e.msgID ?? '') .toList(); if (msgID.isNotEmpty) { @@ -491,9 +511,21 @@ class TUIChatSeparateViewModel extends ChangeNotifier { msgID: message.msgID!, localCustomData: message.localCustomData ?? ""); } - _setMsgReadReceipt(List message) async { + addToMessageReadReceiptList(V2TimMessage message) { + if (chatConfig.isShowReadingStatus) { + if (message.msgID != null) { + _readReceiptMap[message.msgID!] = message; + } + + Future.delayed(const Duration(milliseconds: 200), () { + _setMsgReadReceipt(_readReceiptMap.values.toList()); + }); + } + } + + _setMsgReadReceipt(List messageList) async { final msgIDList = List.empty(growable: true); - for (var item in message) { + for (var item in messageList) { final isSelf = item.isSelf ?? true; final needReadReceipt = item.needReadReceipt ?? false; final isRead = item.isRead ?? false; @@ -514,7 +546,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier { } markMessageAsRead() async { - globalModel.unreadCountForConversation = 0; if (conversationType == ConvType.c2c) { return _messageService.markC2CMessageAsRead(userID: conversationID); } @@ -655,9 +686,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier { if (convType == ConvType.group && _groupType == null) { await loadGroupInfo(groupID); } - final oldGroupType = _groupType != null - ? GroupReceptAllowType.values[_groupType!.index] - : null; if (messageInfo != null) { setLoadingMessageMap(convID, messageInfo); } @@ -667,15 +695,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { isExcludedFromUnreadCount: isExcludedFromUnreadCount ?? false, id: id, receiver: receiver, - needReadReceipt: needReadReceipt ?? - chatConfig.isShowGroupReadingStatus && - convType == ConvType.group && - ((chatConfig.groupReadReceiptPermissionList != null && - chatConfig.groupReadReceiptPermissionList! - .contains(_groupType)) || - (chatConfig.groupReadReceiptPermisionList != null && - chatConfig.groupReadReceiptPermisionList! - .contains(oldGroupType))), + needReadReceipt: needReadReceipt ?? chatConfig.isShowReadingStatus, groupID: groupID, offlinePushInfo: offlinePushInfo, onlineUserOnly: onlineUserOnly ?? false, @@ -708,7 +728,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { } int getConversationUnreadCount() { - return globalModel.unreadCountForConversation; + return globalModel.unreadCountForTongue; } Future?> sendTextAtMessage( @@ -871,9 +891,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier { final V2TimMessage? messageInfo = textMessageInfo!.messageInfo; final receiver = convType == ConvType.c2c ? convID : ''; final groupID = convType == ConvType.group ? convID : ''; - final oldGroupType = _groupType != null - ? GroupReceptAllowType.values[_groupType!.index] - : null; if (messageInfo != null) { V2TimMessage messageInfoWithSender = tools.setUserInfoForMessage(messageInfo, textMessageInfo.id!); @@ -909,14 +926,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { id: textMessageInfo.id as String, offlinePushInfo: tools.buildMessagePushInfo( messageInfoWithSender, convID, convType), - needReadReceipt: chatConfig.isShowGroupReadingStatus && - convType == ConvType.group && - ((chatConfig.groupReadReceiptPermissionList != null && - chatConfig.groupReadReceiptPermissionList! - .contains(_groupType)) || - (chatConfig.groupReadReceiptPermisionList != null && - chatConfig.groupReadReceiptPermisionList! - .contains(oldGroupType))), + needReadReceipt: chatConfig.isShowReadingStatus, groupID: groupID, receiver: receiver); _notify(); @@ -1135,15 +1145,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier { sendForwardMessage({ required List conversationList, }) async { - final selectedMessages = List.from(_multiSelectedMessageList); - if (conversationType == ConvType.c2c) { - selectedMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); - } else if (conversationType == ConvType.group) { - selectedMessages.sort((a, b) => a.seq.compareTo(b.seq)); - } + final selectedMessages = getSelectedMessageList(); for (var conversation in conversationList) { final convID = conversation.groupID ?? conversation.userID ?? ""; final convType = conversation.type; + List currentHistoryMsgList = globalModel.messageListMap[conversationID] ?? []; for (var message in selectedMessages) { final forwardMessageInfo = await _messageService.createForwardMessage(msgID: message.msgID!); final messageInfo = forwardMessageInfo!.messageInfo; @@ -1151,6 +1157,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier { tools.setUserInfoForMessage(messageInfo, forwardMessageInfo.id); messageInfo.status = MessageStatus.V2TIM_MSG_STATUS_SENDING; addSendingMessageID(messageInfo.id); + // 如果转发的会话是当前会话,则直接添加到当前会话的消息列表中 + if (convID == conversationID) { + if (globalModel.getMessageListPosition(convID) != HistoryMessagePosition.notShowLatest) { + currentHistoryMsgList = [ + messageInfo, + ...currentHistoryMsgList + ]; + globalModel.setMessageList(conversationID, currentHistoryMsgList); + _notify(); + } + } await Future.delayed(Duration(milliseconds: 100), () { _sendMessage( id: forwardMessageInfo.id!, @@ -1174,13 +1191,14 @@ class TUIChatSeparateViewModel extends ChangeNotifier { required List abstractList, required BuildContext context, }) async { - final List msgIDList = _multiSelectedMessageList + final List msgIDList = getSelectedMessageList() .map((e) => e.msgID ?? "") .where((element) => element != "") .toList(); for (var conversation in conversationList) { final convID = conversation.groupID ?? conversation.userID ?? ""; final convType = conversation.type; + List currentHistoryMsgList = globalModel.messageListMap[conversationID] ?? []; final mergerMessageInfo = await _messageService.createMergerMessage( msgIDList: msgIDList, title: title, @@ -1191,6 +1209,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier { tools.setUserInfoForMessage(messageInfo, mergerMessageInfo.id); messageInfo.status = MessageStatus.V2TIM_MSG_STATUS_SENDING; addSendingMessageID(messageInfo.id); + // 如果转发的会话是当前会话,则直接添加到当前会话的消息列表中 + if (convID == conversationID) { + if (globalModel.getMessageListPosition(convID) != HistoryMessagePosition.notShowLatest) { + currentHistoryMsgList = [ + messageInfo, + ...currentHistoryMsgList + ]; + globalModel.setMessageList(conversationID, currentHistoryMsgList); + _notify(); + } + } _sendMessage( id: mergerMessageInfo.id!, convID: convID, @@ -1215,24 +1244,34 @@ class TUIChatSeparateViewModel extends ChangeNotifier { return null; } - int messageIndex = currentHistoryMsgList.indexWhere((element) => element.msgID == message.msgID); - if (messageIndex != -1) { - // 取出界面的消息列表,设置转发的这条消息状态为 sending - currentHistoryMsgList[messageIndex].status = MessageStatus.V2TIM_MSG_STATUS_SENDING; - addSendingMessageID(message.msgID); - globalModel.setMessageList(convID, currentHistoryMsgList); - // 重发该消息 - final res = await _messageService.reSendMessage( - msgID: message.msgID ?? "", onlineUserOnly: false); - removeSendingMessageID(message.msgID ?? ""); - final messageInfo = res.data; - // 重发完成后,更新消息列表中的这条消息 - currentHistoryMsgList[messageIndex] = messageInfo!; - globalModel.setMessageList(convID, currentHistoryMsgList); - return res; + 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 + ]; + globalModel.setMessageList(conversationID, currentHistoryMsgList); + _notify(); } - return null; + // 重发该消息 + final res = await _messageService.reSendMessage( + msgID: message.msgID ?? "", onlineUserOnly: false); + removeSendingMessageID(message.msgID ?? ""); + if (globalModel.getMessageListPosition(conversationID) != + HistoryMessagePosition.notShowLatest) { + globalModel.updateMessage( + res, convID, message.msgID!, convType, groupType, setInputField); + } + + if (lifeCycle?.messageDidSend != null) { + lifeCycle!.messageDidSend(res); + } + return res; } Future?> sendTextMessage( @@ -1322,8 +1361,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier { return; } final messageList = getOriginMessageList(); - final res = await _messageService.deleteMessageFromLocalStorage( - msgID: msgID, webMessageInstance: webMessageInstance); + final res = await _messageService.deleteMessages( + msgIDs: [msgID], webMessageInstanceList: [webMessageInstance]); if (res.code == 0) { messageList.removeWhere((element) { return element.msgID == msgID || (id != null && element.id == id); @@ -1368,26 +1407,18 @@ class TUIChatSeparateViewModel extends ChangeNotifier { return res; } - addToMultiSelectedMessageList(V2TimMessage message) { - _multiSelectedMessageList.add(message); - _notify(); - } + setMessageItemChecked(V2TimMessage message, bool isChecked) { + if (message.msgID != null) { + _selectedPositions[message.msgID!] = isChecked; + } - removeFromMultiSelectedMessageList(V2TimMessage message) { - _multiSelectedMessageList.remove(message); _notify(); } deleteSelectedMsg() async { List messageList = getOriginMessageList(); - final msgIDs = _multiSelectedMessageList - .map((e) => e.msgID ?? "") - .where((element) => element != "") - .toList(); - final webMessageInstanceList = _multiSelectedMessageList - .map((e) => e.messageFromWeb) - .where((element) => element != null) - .toList(); + final msgIDs = getSelectedMessageIDList(); + final webMessageInstanceList = getSelectedMessageIDList(); final res = await _messageService.deleteMessages( msgIDs: msgIDs, webMessageInstanceList: webMessageInstanceList); @@ -1395,14 +1426,14 @@ class TUIChatSeparateViewModel extends ChangeNotifier { for (var msgID in msgIDs) { messageList.removeWhere((element) => element.msgID == msgID); } - globalModel.setMessageList(conversationID, messageList); + globalModel.setMessageList(conversationID, messageList, isDeleteMsg: true); } } updateMultiSelectStatus(bool isSelect) { _isMultiSelect = isSelect; if (!isSelect) { - _multiSelectedMessageList.clear(); + _selectedPositions.clear(); } _notify(); } @@ -1440,6 +1471,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { } showLatestUnread() { + globalModel.unreadCountForTongue = 0; markMessageAsRead(); globalModel.setMessageListPosition( conversationID, HistoryMessagePosition.bottom); @@ -1448,25 +1480,56 @@ class TUIChatSeparateViewModel extends ChangeNotifier { // 添加发送中的消息的 id 或者 msgID(id 不存在时使用 msgID) void addSendingMessageID(String? id) { if (id?.isNotEmpty == true) { - _sendingMessageIDList.add(id!); + _sendingMessageIDMap[id!] = false; } } // 移除发送中的消息的 id 或者 msgID(id 不存在时使用 msgID) void removeSendingMessageID(String id) { - bool hasID = _sendingMessageIDList.contains(id); - if (hasID) { - _sendingMessageIDList.remove(id); + _sendingMessageIDMap.remove(id); + } + + // 是否已经延迟渲染 + bool? hasDelayedRenderSendingStatus(String id) { + if (_sendingMessageIDMap.containsKey(id)) { + return _sendingMessageIDMap[id]; + } + + print("sending test, hasDelayedRenderSendingStatus false:${id}"); + return true; + } + + // 设置已经延迟渲染过的消息 + void setDelayedRenderSendingStatus(String id) { + if (_sendingMessageIDMap.containsKey(id)) { + _sendingMessageIDMap[id] = true; } } - // 内存中有发送中的消息的 id 或者 msgID(id 不存在时使用 msgID) - bool hasSendingMessageID(String id) { - return _sendingMessageIDList.contains(id); + bool isVoteMessage(V2TimMessage message) { + bool isVote = false; + V2TimCustomElem? custom = message.customElem; + + if (custom != null) { + String? data = custom.data; + if (data != null && data.isNotEmpty) { + try { + Map mapData = json.decode(data); + if (mapData["businessID"] == "group_poll") { + isVote = true; + } + } catch (err) { + // err + } + } + } + return isVote; } @override void dispose() { + markMessageAsRead(); + globalModel.unreadCountForTongue = 0; globalModel.clearCurrentConversation(); _isInit = false; super.dispose(); diff --git a/lib/business_logic/separate_models/tui_group_profile_model.dart b/lib/business_logic/separate_models/tui_group_profile_model.dart index 2f0ad33..69f17cf 100644 --- a/lib/business_logic/separate_models/tui_group_profile_model.dart +++ b/lib/business_logic/separate_models/tui_group_profile_model.dart @@ -25,7 +25,7 @@ class TUIGroupProfileModel extends ChangeNotifier { List? _groupMemberList; String _groupMemberListSeq = "0"; V2TimGroupInfo? _groupInfo; - Function(String userID, TapDownDetails? tapDetails)? onClickUser; + Function(V2TimGroupMemberFullInfo groupMemberFullInfo, TapDownDetails? tapDetails)? onClickUser; GroupProfileLifeCycle? get lifeCycle => _lifeCycle; @@ -233,6 +233,33 @@ class TUIGroupProfileModel extends ChangeNotifier { return res; } + void onOwnerChanged(String? userID) { + if (userID == null) { + return; + } + + // 把之前的群主更新为普通成员 + final preOwnerIndex = _groupMemberList!.indexWhere((e) => e!.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER); + if (preOwnerIndex != -1) { + final preOwnerElem = _groupMemberList![preOwnerIndex]; + preOwnerElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER; + + print("preOwnerUserID: ${preOwnerElem?.userID}"); + } + + // 设置新的群主 + final targetIndex = _groupMemberList!.indexWhere((e) => e!.userID == userID); + if (targetIndex != -1) { + final targetElem = _groupMemberList![targetIndex]; + targetElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER; + _groupMemberList![targetIndex] = targetElem; + + print("newOwnerUserID: ${targetElem?.userID}"); + } + + notifyListeners(); + } + bool canInviteMember() { final groupType = _groupInfo?.groupType; return groupType == GroupType.Work || groupType == "Private"; diff --git a/lib/business_logic/separate_models/tui_profile_view_model.dart b/lib/business_logic/separate_models/tui_profile_view_model.dart index 6e517e5..caf4d69 100644 --- a/lib/business_logic/separate_models/tui_profile_view_model.dart +++ b/lib/business_logic/separate_models/tui_profile_view_model.dart @@ -143,7 +143,7 @@ class TUIProfileViewModel extends ChangeNotifier { } } - Future deleteFriend(String userID) async { + Future deleteFriend(String userID, {bool needUpdateData = true}) async { if (_lifeCycle?.shouldDeleteFriend != null && await _lifeCycle!.shouldDeleteFriend(userID) == false) { return null; @@ -152,9 +152,13 @@ class TUIProfileViewModel extends ChangeNotifier { userIDList: [userID], deleteType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH); if (res != null) { - loadData(userID: userID); + _conversationService.deleteConversation(conversationID: "c2c_$userID"); + if (needUpdateData) { + loadData(userID: userID); + } return res.first; } + return null; } @@ -171,49 +175,6 @@ class TUIProfileViewModel extends ChangeNotifier { return res; } - // 1:男 女:2 - Future updateGender(int gender) async { - final res = await _coreServices.setSelfInfo( - userFullInfo: V2TimUserFullInfo.fromJson( - {"gender": gender}, - ), - ); - if (res.code == 0) { - _userProfile?.friendInfo!.userProfile!.gender = gender; - notifyListeners(); - } - - return res; - } - - Future updateNickName(String nickName) async { - final res = await _coreServices.setSelfInfo( - userFullInfo: V2TimUserFullInfo.fromJson( - {"nickName": nickName}, - ), - ); - - if (res.code == 0) { - _userProfile?.friendInfo!.userProfile!.nickName = nickName; - notifyListeners(); - } - - return res; - } - - Future updateSelfSignature(String selfSignature) async { - final res = await _coreServices.setSelfInfo( - userFullInfo: V2TimUserFullInfo.fromJson( - {"selfSignature": selfSignature}, - ), - ); - if (res.code == 0) { - _userProfile?.friendInfo!.userProfile!.selfSignature = selfSignature; - notifyListeners(); - } - return res; - } - Future addFriend(String userID) async { if (_lifeCycle?.shouldAddFriend != null && await _lifeCycle!.shouldAddFriend(userID) == false) { @@ -225,6 +186,7 @@ class TUIProfileViewModel extends ChangeNotifier { loadData(userID: userID); return res.data; } + return null; } @@ -291,13 +253,14 @@ class TUIProfileViewModel extends ChangeNotifier { newSelfInfo, ), ); + if (res.code == 0) { newSelfInfo.forEach((key, value) { updateUserInfo(key, value); }); - notifyListeners(); } + return res; } } diff --git a/lib/business_logic/view_models/tui_chat_global_model.dart b/lib/business_logic/view_models/tui_chat_global_model.dart index 874e179..d0cf707 100644 --- a/lib/business_logic/view_models/tui_chat_global_model.dart +++ b/lib/business_logic/view_models/tui_chat_global_model.dart @@ -50,10 +50,12 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { String localMsgIDListKey = "TUIKit_conversation_list"; late V2TimAdvancedMsgListener advancedMsgListener; - int _unreadCountForConversation = 0; + int _unreadCountForTongue = 0; // use for generate a new sliver list to show received message list int _receivedNewMessageCount = 0; + final List _receivedUnreadMessageList = []; + TIMUIKitChatConfig chatConfig = const TIMUIKitChatConfig(); List? _groupApplicationList; String Function(V2TimMessage message)? _abstractMessageBuilder; @@ -170,15 +172,23 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { return _totalUnreadCount; } - int get receivedMessageListCount { - return _receivedNewMessageCount; + set totalUnReadCount(int newValue) { + _totalUnreadCount = newValue; + notifyListeners(); } + int get receivedNewMessageCount => _receivedNewMessageCount; + set receivedNewMessageCount(int value) { _receivedNewMessageCount = value; } - int get unreadCountForConversation => _unreadCountForConversation; + int get unreadCountForTongue => _unreadCountForTongue; + + set unreadCountForTongue(int value) { + _unreadCountForTongue = value; + notifyListeners(); + } List get groupApplicationList => _groupApplicationList ?? []; @@ -201,9 +211,15 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { if (_currentConversationList.isNotEmpty) { _currentConversationList.removeLast(); } + + _receivedUnreadMessageList.clear(); // notifyListeners(); } + void removeMessageList(String conversationID) { + _messageListMap.remove(conversationID); + } + V2TimMessageReceipt? getMessageReadReceipt(String msgID) { return messageReadReceiptMap[msgID]; } @@ -250,16 +266,6 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { _groupApplicationList = value; } - set unreadCountForConversation(int value) { - _unreadCountForConversation = value; - notifyListeners(); - } - - set totalUnReadCount(int newValue) { - _totalUnreadCount = newValue; - notifyListeners(); - } - setChatConfig(TIMUIKitChatConfig config) { chatConfig = config; } @@ -324,7 +330,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { notifyListeners(); } - clearRecivedNewMessageCount() { + clearReceivedNewMessageCount() { _receivedNewMessageCount = 0; } @@ -523,36 +529,31 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { _checkFromUserisActive(msgComing); final convType = TencentUtils.checkString(newMsg.groupID) != null ? ConvType.group : ConvType.c2c; if (convID != null && convID == currentSelectedConv) { + // when receive new message in the current chat page, we need to mark the message as read. + if (chatConfig.isAutoReportRead) { + Future.delayed(const Duration(seconds: 1), () { + markMessageAsRead( + convID: convID, + convType: convType, + ); + }); + } + final position = getMessageListPosition(convID); if (position == HistoryMessagePosition.notShowLatest) { return; } - if (position == HistoryMessagePosition.bottom && unreadCountForConversation == 0) { - _unreadCountForConversation = 0; - if (chatConfig.isAutoReportRead) { - Future.delayed(const Duration(seconds: 1), () { - markMessageAsRead( - convID: convID, - convType: convType, - ); - }); - } + if (position == HistoryMessagePosition.bottom && unreadCountForTongue == 0) { + _unreadCountForTongue = 0; _receivedNewMessageCount = 0; final tempCurrentMsgList = _messageListMap[convID] ?? []; _messageListMap[convID] = [newMsg, ...tempCurrentMsgList]; notifyListeners(); - final messageID = newMsg.msgID; - final needReadReceipt = newMsg.needReadReceipt ?? false; - if (needReadReceipt && messageID != null && msgComing.groupID != null && chatConfig.isReportGroupReadingStatus && chatConfig.isAutoReportRead) { - // only group message send message read receipt - Future.delayed(const Duration(seconds: 1), () { - sendMessageReadReceipts([messageID]); - }); - } } else { if (convID == currentSelectedConv) { - unreadCountForConversation++; + unreadCountForTongue++; _receivedNewMessageCount++; + _receivedUnreadMessageList.add(newMsg); final currentMsg = _messageListMap[convID] ?? []; _messageListMap[convID] = [newMsg, ...currentMsg]; notifyListeners(); @@ -565,11 +566,6 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } } - sendMessageReadReceipts(List messageIDList) async { - final res = await _messageService.sendMessageReadReceipts(messageIDList: messageIDList); - return res; - } - onMessageRevoked(String msgID, [String? convID]) { final activeMessageList = _messageListMap[convID ?? currentSelectedConv]; if (activeMessageList != null) { @@ -581,8 +577,23 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { targetItem.status = MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED; targetItem.id = DateTime.now().millisecondsSinceEpoch.toString(); activeMessageList[findeIndex] = targetItem; + + bool isUnreadMessage = _receivedUnreadMessageList.any((element) => element.msgID == msgID); + if (!(targetItem.isSelf ?? true) && isUnreadMessage) { + if (_unreadCountForTongue > 0) { + if (_unreadCountForTongue > 0) { + _unreadCountForTongue--; + } + if (_receivedNewMessageCount > 0) { + _receivedNewMessageCount--; + } + + _receivedUnreadMessageList.removeWhere((element) => element.msgID == targetItem.msgID); + } + } } } + _messageListMap[convID ?? currentSelectedConv] = activeMessageList; notifyListeners(); } @@ -884,15 +895,10 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { }) async { String receiver = convType == ConvType.c2c ? convID : ''; String groupID = convType == ConvType.group ? convID : ''; - final oldGroupType = groupType != null ? GroupReceptAllowType.values[groupType.index] : null; final sendMsgRes = await _messageService.sendMessage( id: id, receiver: receiver, - needReadReceipt: needReadReceipt ?? - chatConfig.isShowGroupReadingStatus && - convType == ConvType.group && - ((chatConfig.groupReadReceiptPermissionList != null && chatConfig.groupReadReceiptPermissionList!.contains(groupType)) || - (chatConfig.groupReadReceiptPermisionList != null && chatConfig.groupReadReceiptPermisionList!.contains(oldGroupType))), + needReadReceipt: needReadReceipt ?? chatConfig.isShowReadingStatus, groupID: groupID, priority: priority, localCustomData: localCustomData, @@ -916,21 +922,25 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { return sendMsgRes; } - void setMessageList(String conversationID, List messageList, {bool needResetNewMessageCount = true}) { + void setMessageList(String conversationID, List messageList, {bool needResetNewMessageCount = true, bool isDeleteMsg = false}) { _messageListMap[conversationID] = messageList; if (needResetNewMessageCount) { _receivedNewMessageCount = 0; } + + if (isDeleteMsg) { + HistoryMessagePosition position = getMessageListPosition(conversationID); + if (position == HistoryMessagePosition.awayTwoScreen) { + _historyMessagePositionMap[conversationID] = HistoryMessagePosition.notShowLatest; + } + } + notifyListeners(); } updateMessage(V2TimValueCallback sendMsgRes, String convID, String id, ConvType convType, GroupReceiptAllowType? groupType, ValueChanged? setInputField) { List currentHistoryMsgList = _messageListMap[convID] ?? []; final V2TimMessage sendMsgResData = sendMsgRes.data as V2TimMessage; - if ([80001, 80002].contains(sendMsgRes.code) && sendMsgRes.data?.textElem?.text != null && setInputField != null) { - setInputField(sendMsgRes.data!.textElem!.text ?? ""); - } - final findIdIndex = currentHistoryMsgList.indexWhere((element) => element.id == id); final targetIndex = findIdIndex == -1 ? currentHistoryMsgList.indexWhere((element) => element.msgID == sendMsgResData.msgID) : findIdIndex; if (targetIndex != -1) { @@ -941,12 +951,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { if (loadingMessage[convID] != null && loadingMessage[convID]!.isNotEmpty) { loadingMessage[convID]!.removeWhere((element) => element.id == id); } - final oldGroupType = groupType != null ? GroupReceptAllowType.values[groupType.index] : null; - if (chatConfig.isShowGroupReadingStatus && - convType == ConvType.group && - sendMsgRes.data?.msgID != null && - ((chatConfig.groupReadReceiptPermissionList != null && chatConfig.groupReadReceiptPermissionList!.contains(groupType)) || - (chatConfig.groupReadReceiptPermisionList != null && chatConfig.groupReadReceiptPermisionList!.contains(oldGroupType)))) { + if (chatConfig.isShowReadingStatus && sendMsgRes.data?.msgID != null) { _messageReadReceiptMap[sendMsgRes.data!.msgID!] = V2TimMessageReceipt(timestamp: 0, userID: "", readCount: 0); } _messageListMap[convID] = currentHistoryMsgList; diff --git a/lib/business_logic/view_models/tui_conversation_view_model.dart b/lib/business_logic/view_models/tui_conversation_view_model.dart index f7257c7..d01e5a7 100644 --- a/lib/business_logic/view_models/tui_conversation_view_model.dart +++ b/lib/business_logic/view_models/tui_conversation_view_model.dart @@ -29,6 +29,9 @@ List removeDuplicates(List list, bool Function(T first, T second) isEqu } class TUIConversationViewModel extends ChangeNotifier { + static const String conversationC2CPrefix = "c2c_"; + static const String conversationGroupPrefix = "group_"; + final TUISelfInfoViewModel selfInfoViewModel = serviceLocator(); final ConversationService _conversationService = serviceLocator(); final FriendshipServices _friendshipServices = serviceLocator(); @@ -65,6 +68,10 @@ class TUIConversationViewModel extends ChangeNotifier { return _conversationList; } + V2TimConversation? getConversation(String conversationID) { + return _conversationList.firstWhere((element) => element?.conversationID == conversationID); + } + String? get scrollToConversation => _scrollToConversation; set scrollToConversation(String? value) { @@ -124,6 +131,18 @@ class TUIConversationViewModel extends ChangeNotifier { } }, onConversationDeleted:(List conversationIDList) { _onConversationDeleted(conversationIDList); + for (var conversationID in conversationIDList) { + String resultID = ""; + if (conversationID.startsWith(conversationC2CPrefix)) { + resultID = conversationID.replaceFirst(conversationC2CPrefix, ""); + } else if (conversationID.startsWith(conversationGroupPrefix)) { + resultID = conversationID.replaceFirst(conversationGroupPrefix, ""); + } + + if (resultID != "") { + _chatGlobalModel.removeMessageList(resultID); + } + } }); } diff --git a/lib/business_logic/view_models/tui_friendship_view_model.dart b/lib/business_logic/view_models/tui_friendship_view_model.dart index b21e2cc..14c7f50 100644 --- a/lib/business_logic/view_models/tui_friendship_view_model.dart +++ b/lib/business_logic/view_models/tui_friendship_view_model.dart @@ -171,6 +171,17 @@ class TUIFriendShipViewModel extends ChangeNotifier { return; } + Future isFriend(String userID) async { + final List res = await _friendshipServices.getFriendList() ?? []; + for (V2TimFriendInfo info in res) { + if (info.userID == userID) { + return true; + } + } + + return false; + } + Future loadBlockListData() async { final blockListRes = await _friendshipServices.getBlackList(); _blockList = blockListRes ?? []; diff --git a/lib/data_services/friendShip/friendship_services_implements.dart b/lib/data_services/friendShip/friendship_services_implements.dart index e03936f..23c2d59 100644 --- a/lib/data_services/friendShip/friendship_services_implements.dart +++ b/lib/data_services/friendShip/friendship_services_implements.dart @@ -1,6 +1,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart'; import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/error_message_converter.dart'; import 'package:tencent_im_base/tencent_im_base.dart'; class FriendshipServicesImpl implements FriendshipServices { @@ -77,17 +78,34 @@ class FriendshipServicesImpl implements FriendshipServices { friendGroup: friendGroup, addSource: addSource, ); - if (result.code != 0 || - (result.code == 0 && - result.data?.resultCode != 0 && - result.data?.resultCode != 30539 && - result.data?.resultCode != 30515)) { + if (result.code != 0) { + _coreService.callOnCallback(TIMCallback( + type: TIMCallbackType.API_ERROR, + errorMsg: result.desc, + errorCode: result.code, + infoRecommendText: TIM_t("好友添加失败"), + )); + } else if (result.code == 0 && result.data?.resultCode != 0) { + String recommendText = ""; + if (result.data != null && result.data!.resultCode != null) { + recommendText = ErrorMessageConverter.getErrorMessage(result.data!.resultCode!); + } + _coreService.callOnCallback(TIMCallback( type: TIMCallbackType.API_ERROR, errorMsg: result.code == 0 ? result.data?.resultInfo : result.desc, errorCode: result.code == 0 ? result.data?.resultCode : result.code, + infoRecommendText: recommendText, + )); + } else { + _coreService.callOnCallback(TIMCallback( + type: TIMCallbackType.API_ERROR, + errorMsg: result.desc, + errorCode: result.code, + infoRecommendText: TIM_t("好友添加成功"), )); } + return result; } @@ -118,12 +136,18 @@ class FriendshipServicesImpl implements FriendshipServices { .getFriendshipManager() .deleteFromFriendList(userIDList: userIDList, deleteType: deleteType); if (res.code == 0) { + _coreService.callOnCallback(TIMCallback( + type: TIMCallbackType.API_ERROR, + errorMsg: res.desc, + errorCode: res.code, + infoRecommendText: TIM_t("好友删除成功"))); return res.data; } else { _coreService.callOnCallback(TIMCallback( type: TIMCallbackType.API_ERROR, errorMsg: res.desc, - errorCode: res.code)); + errorCode: res.code, + infoRecommendText: TIM_t("好友删除失败"))); return null; } } diff --git a/lib/data_services/group/group_services_implement.dart b/lib/data_services/group/group_services_implement.dart index 986d58e..0495c4d 100644 --- a/lib/data_services/group/group_services_implement.dart +++ b/lib/data_services/group/group_services_implement.dart @@ -1,6 +1,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart'; import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/error_message_converter.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart'; import 'package:tencent_im_base/tencent_im_base.dart'; @@ -249,10 +250,12 @@ class GroupServicesImpl extends GroupServices { final result = await TencentImSDKPlugin.v2TIMManager .joinGroup(groupID: groupID, message: message); if (result.code != 0) { + String recommendText = ErrorMessageConverter.getErrorMessage(result.code); _coreService.callOnCallback(TIMCallback( type: TIMCallbackType.API_ERROR, errorMsg: result.desc, - errorCode: result.code)); + errorCode: result.code, + infoRecommendText: recommendText)); } return result; } diff --git a/lib/data_services/message/message_service_implement.dart b/lib/data_services/message/message_service_implement.dart index 151a1c7..3a7221b 100644 --- a/lib/data_services/message/message_service_implement.dart +++ b/lib/data_services/message/message_service_implement.dart @@ -3,9 +3,11 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; +import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart'; import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart'; import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/error_message_converter.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_im_base/tencent_im_base.dart'; @@ -271,10 +273,12 @@ class MessageServiceImpl extends MessageService { .getMessageManager() .reSendMessage(msgID: msgID, onlineUserOnly: onlineUserOnly ?? false); if (res.code != 0) { + String recommendText = ErrorMessageConverter.getErrorMessage(res.code); _coreService.callOnCallback(TIMCallback( type: TIMCallbackType.API_ERROR, errorMsg: res.desc, - errorCode: res.code)); + errorCode: res.code, + infoRecommendText: recommendText)); } return res; } @@ -359,10 +363,12 @@ class MessageServiceImpl extends MessageService { cloudCustomData: cloudCustomData, ); if (result.code != 0) { + String recommendText = ErrorMessageConverter.getErrorMessage(result.code); _coreService.callOnCallback(TIMCallback( type: TIMCallbackType.API_ERROR, errorMsg: result.desc, - errorCode: result.code)); + errorCode: result.code, + infoRecommendText: recommendText)); } return result; } @@ -471,7 +477,7 @@ class MessageServiceImpl extends MessageService { return TencentImSDKPlugin.v2TIMManager .getConversationManager() .cleanConversationUnreadMessageCount( - conversationID: "c2c_$userID", + conversationID: "${TUIConversationViewModel.conversationC2CPrefix}$userID", cleanTimestamp: 0, cleanSequence: 0, ); @@ -486,7 +492,7 @@ class MessageServiceImpl extends MessageService { return TencentImSDKPlugin.v2TIMManager .getConversationManager() .cleanConversationUnreadMessageCount( - conversationID: "group_$groupID", + conversationID: "${TUIConversationViewModel.conversationGroupPrefix}$groupID", cleanTimestamp: 0, cleanSequence: 0, ); diff --git a/lib/ui/controller/tim_uikit_profile_controller.dart b/lib/ui/controller/tim_uikit_profile_controller.dart index f2fe078..44bb944 100644 --- a/lib/ui/controller/tim_uikit_profile_controller.dart +++ b/lib/ui/controller/tim_uikit_profile_controller.dart @@ -81,14 +81,28 @@ class TIMUIKitProfileController { } Future updateSelfSignature(String selfSignature) { - return model.updateSelfSignature(selfSignature); + Map infoMap = {"selfSignature": selfSignature}; + return model.updateSelfInfo(infoMap); } Future updateNickName(String nickName) { - return model.updateNickName(nickName); + Map infoMap = {"nickName": nickName}; + return model.updateSelfInfo(infoMap); } + /// 1:男 2:女 Future updateGender(int gender) { - return model.updateGender(gender); + Map infoMap = {"gender": gender}; + return model.updateSelfInfo(infoMap); + } + + Future updateBirthday(int birthday) { + Map infoMap = {"birthday": birthday}; + return model.updateSelfInfo(infoMap); + } + + Future updateAvatar(String url) { + Map infoMap = {"faceUrl" : url}; + return model.updateSelfInfo(infoMap); } } diff --git a/lib/ui/utils/error_message_converter.dart b/lib/ui/utils/error_message_converter.dart new file mode 100644 index 0000000..1c28742 --- /dev/null +++ b/lib/ui/utils/error_message_converter.dart @@ -0,0 +1,20 @@ +import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; + +class ErrorMessageConverter { + static Map errorCodeMap = { + 10007: TIM_t("操作权限不足"), + 20007: TIM_t("发送单聊消息,被对方拉黑,禁止发送。"), + 30010: TIM_t("您的好友数已达系统上限"), + 30014: TIM_t("对方的好友数已达系统上限"), + 30015: TIM_t("对方已是您的好友"), + 30515: TIM_t("被加好友在自己的黑名单中"), + 30516: TIM_t("对方已禁止加好友"), + 30525: TIM_t("您已被被对方设置为黑名单"), + 30539: TIM_t("等待好友审核同意"), + }; + + static String getErrorMessage(int code) { + return errorCodeMap[code] ?? ""; + } + +} \ No newline at end of file diff --git a/lib/ui/utils/sound_record.dart b/lib/ui/utils/sound_record.dart index 6ec80b8..c6ffff3 100644 --- a/lib/ui/utils/sound_record.dart +++ b/lib/ui/utils/sound_record.dart @@ -1,6 +1,5 @@ import 'dart:async'; -// import 'package:audioplayers/audioplayers.dart'; import 'package:flutter_plugin_record_plus/const/play_state.dart'; import 'package:flutter_plugin_record_plus/const/response.dart'; import 'package:flutter_plugin_record_plus/index.dart'; diff --git a/lib/ui/utils/time_ago.dart b/lib/ui/utils/time_ago.dart index 34cc0ca..3f6424d 100644 --- a/lib/ui/utils/time_ago.dart +++ b/lib/ui/utils/time_ago.dart @@ -27,6 +27,16 @@ class TimeAgo { return (month.length == 1 ? '0' : '') + month + '/' + (date.length == 1 ? '0' : '') + date; } + static String getMonth(DateTime dateTime) { + String month = dateTime.month.toString(); + return (month.length == 1 ? '0' : '') + month; + } + + static String getDay(DateTime dateTime) { + String day = dateTime.day.toString(); + return (day.length == 1 ? '0' : '') + day; + } + String? getTimeStringForChat(int timeStamp) { final DateTime date = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000); final DateTime epochLimit = DateTime.utc(1971); diff --git a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart index 90006ed..7a02666 100644 --- a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart +++ b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart @@ -71,10 +71,6 @@ class _TIMUIKitAddFriendState extends TIMUIKitState { if (checkFriend != null) { final res = checkFriend.first; if (res.resultCode == 0 && res.resultType != 0) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("该用户已是好友"), - infoCode: 6660102)); widget.onTapAlreadyFriendsItem(userID); return; } diff --git a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart index 6d71fba..2d86f47 100644 --- a/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart +++ b/lib/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart @@ -203,29 +203,12 @@ class _SendApplicationState extends TIMUIKitState { return; } - final res = await _friendshipServices.addFriend( + _friendshipServices.addFriend( userID: userID, addType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH, remark: remark, addWording: addWording, friendGroup: friendGroup); - - if (res.code == 0 && res.data?.resultCode == 0) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("好友添加成功"), - infoCode: 6661202)); - } else if (res.code == 0 && res.data?.resultCode == 30539) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("好友申请已发出"), - infoCode: 6661203)); - } else if (res.code == 0 && res.data?.resultCode == 30515) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("当前用户在黑名单"), - infoCode: 6661204)); - } }, child: Text(TIM_t("发送"))), ) diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart index f4a628a..2e854cf 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart @@ -84,11 +84,11 @@ class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState 20 && !isClickShowPrevious) { - return MessageListTongueType.showPrevious; - } + // if ((widget.conversation.unreadCount ?? 0) > 20 && !isClickShowPrevious) { + // return MessageListTongueType.showPrevious; + // } - if (globalModel.unreadCountForConversation > 0) { + if (globalModel.unreadCountForTongue > 0) { return MessageListTongueType.showUnread; } @@ -116,12 +116,12 @@ class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState 0) { + } else if (value.item1 == HistoryMessagePosition.awayTwoScreen || value.item2 > 0) { widget.model.showLatestUnread(); widget.scrollController.animateTo( widget.scrollController.position.minScrollExtent, @@ -162,9 +162,9 @@ class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState textType(BuildContext context) { final option1 = unreadCount.toString(); final option2 = atNum.toString(); - final option3 = previousCount.toString(); + // final option3 = previousCount.toString(); final String atMeString = option2 != "" ? TIM_t_para("有{{option2}}条@我消息", "有$option2条@我消息")(option2: option2) : TIM_t("有人@我"); return { - MessageListTongueType.showPrevious: - TIM_t_para("{{option3}}条未读消息", "$option3条未读消息")(option3: option3), + // MessageListTongueType.showPrevious: + // TIM_t_para("{{option3}}条未读消息", "$option3条未读消息")(option3: option3), MessageListTongueType.toLatest: TIM_t("回到最新位置"), MessageListTongueType.showUnread: TIM_t_para("{{option1}}条新消息", "$option1条新消息")(option1: option1), diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart index 871c32b..772d3ec 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart @@ -75,7 +75,7 @@ class TIMUIKitHistoryMessageList extends StatefulWidget { final V2TimMessage? initFindingMsg; /// use for load more message - final Future Function(String?, LoadDirection direction, [int?]) onLoadMore; + final Future Function(String?, LoadDirection direction, [int?, int?]) onLoadMore; /// configuration for list view final TIMUIKitHistoryMessageListConfig? mainHistoryListConfig; @@ -111,6 +111,7 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState= 0; i--) { @@ -258,9 +259,10 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState(); - final receivedNewMessageList = globalModel.receivedMessageListCount; - final shouldShowUnreadMessage = receivedNewMessageList > 0; - final unreadMessageList = _getReceivedMessageList(receivedNewMessageList); + final receivedNewMessageCount = globalModel.receivedNewMessageCount; + final shouldShowUnreadMessage = receivedNewMessageCount > 0; + final unreadMessageList = _getReceivedMessageList(receivedNewMessageCount); final readMessageList = messageList.sublist(unreadMessageList.length, messageList.length).toList(); final throttleFunction = OptimizeUtils.multiThrottle((index, LoadDirection direction) async { @@ -429,7 +431,7 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState mapData = json.decode(data); - if (mapData["businessID"] == "group_poll") { - isvote = true; - } - } catch (err) { - // err - } - } - } - return isvote; - } - - List getMessageHoverControlBar(TUIChatSeparateViewModel model, TUITheme theme) { + List getWideMessageHoverControlBar(TUIChatSeparateViewModel model, TUITheme theme) { return [ if (widget.isUseMessageReaction ?? false) MessageHoverControlItem( @@ -939,7 +928,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState _conditionalDelay() async { + if (!(model.hasDelayedRenderSendingStatus(message.id ?? message.msgID!) ?? true)) { + model.setDelayedRenderSendingStatus(message.id ?? message.msgID!); + await Future.delayed(const Duration(seconds: 1)); + } + } + return Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -1041,7 +1036,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState snapshot) { - if (snapshot.connectionState == ConnectionState.done && model.hasSendingMessageID(message.id ?? message.msgID!)) { + if (snapshot.connectionState == ConnectionState.done) { return Container( padding: const EdgeInsets.only(bottom: 3), margin: const EdgeInsets.only(right: 6), - child: RotationTransition( - turns: Tween(begin: 0.0, end: 1.0).animate(_animationController), - child: Icon(Icons.rotate_right, color: theme.cautionColor, size: 18), - ), + width: 12.0, + height: 15.0, + child: CircularProgressIndicator(strokeWidth: 1.0), ); } else { return Container(); @@ -1075,21 +1069,9 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState Container( - padding: EdgeInsets.only(left: isSelf ? 0 : 16, right: isSelf ? 16 : 0), - margin: widget.padding ?? const EdgeInsets.only(bottom: 20), - child: Row( - key: _key, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (model.isMultiSelect) - Container( - margin: EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0), - child: CheckBoxButton( - isChecked: model.multiSelectedMessageList.contains(message), - onChanged: (value) { - if (value) { - model.addToMultiSelectedMessageList(message); - } else { - model.removeFromMultiSelectedMessageList(message); - } - }, - ), - ), - Expanded( - child: MouseRegion( - onEnter: (_) { - if (isDesktopScreen && model.chatConfig.isUseMessageHoverBarOnDesktop) { - setState(() { - isShowWideToolTip = true; - }); - } - }, - onExit: (_) { - if (isDesktopScreen && model.chatConfig.isUseMessageHoverBarOnDesktop) { - Tooltip.dismissAllToolTips(); - Future.delayed(const Duration(milliseconds: 100), () { - setState(() { - isShowWideToolTip = false; - }); - }); - } - }, - child: GestureDetector( - behavior: model.isMultiSelect ? HitTestBehavior.translucent : null, - onTap: () { - if (model.isMultiSelect) { - final checked = model.multiSelectedMessageList.contains(message); - if (checked) { - model.removeFromMultiSelectedMessageList(message); - } else { - model.addToMultiSelectedMessageList(message); + return VisibilityDetector( + key: Key(message.id ?? message.msgID!), + // 判断消息是否可见 + onVisibilityChanged: (visibilityInfo) { + var visiblePercentage = visibilityInfo.visibleFraction * 100; + if (visiblePercentage > 50) { + model.addToMessageReadReceiptList(message); + } + }, + child: LayoutBuilder( + builder: (context, constraints) => Container( + padding: EdgeInsets.only(left: isSelf ? 0 : 16, right: isSelf ? 16 : 0), + margin: widget.padding ?? const EdgeInsets.only(bottom: 20), + child: Row( + key: _key, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (model.isMultiSelect) + Container( + margin: EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0), + child: CheckBoxButton( + isChecked: model.getSelectedMessageList().contains(message), + onChanged: (value) { + model.setMessageItemChecked(message, value); + }, + ), + ), + Expanded( + child: MouseRegion( + onEnter: (_) { + if (isDesktopScreen && model.chatConfig.isUseMessageHoverBarOnDesktop) { + setState(() { + isShowWideToolTip = true; + }); } - } else { - return; - } - }, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: isSelf ? MainAxisAlignment.end : MainAxisAlignment.start, - children: [ - if (!isSelf && widget.showAvatar) - GestureDetector( - onLongPress: () { - if (widget.onLongPressForOthersHeadPortrait != null) {} - if (model.chatConfig.isAllowLongPressAvatarToAt) { - widget.onLongPressForOthersHeadPortrait!(message.sender, message.nickName); - } - }, - onTapDown: isDesktopScreen - ? (details) { - if (widget.onTapForOthersPortrait != null && widget.allowAvatarTap) { - widget.onTapForOthersPortrait!(message.sender ?? "", details); - } + }, + onExit: (_) { + if (isDesktopScreen && model.chatConfig.isUseMessageHoverBarOnDesktop) { + Tooltip.dismissAllToolTips(); + Future.delayed(const Duration(milliseconds: 100), () { + setState(() { + isShowWideToolTip = false; + }); + }); + } + }, + child: GestureDetector( + behavior: model.isMultiSelect ? HitTestBehavior.translucent : null, + onTap: () { + if (model.isMultiSelect) { + final checked = model.getSelectedMessageList().contains(message); + model.setMessageItemChecked(message, !checked); + } else { + return; + } + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: isSelf ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + if (!isSelf && widget.showAvatar) + GestureDetector( + onLongPress: () { + if (widget.onLongPressForOthersHeadPortrait != null) {} + if (model.chatConfig.isAllowLongPressAvatarToAt) { + widget.onLongPressForOthersHeadPortrait!(message.sender, message.nickName); } - : null, - onTap: isDesktopScreen - ? null - : () { - 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()); - } - }, - onSecondaryTapDown: isDesktopScreen - ? (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, - child: SizedBox( - width: 40, - height: 40, - child: Avatar( - faceUrl: message.faceUrl ?? "", - showName: MessageUtils.getDisplayName(message), - ), - ), - ), - ), - if (isSelf && widget.message.elemType == 6 && isDownloadWaiting) - Container( - margin: const EdgeInsets.only(top: 46, right: 10), - child: LoadingAnimationWidget.threeArchedCircle( - color: theme.weakTextColor ?? Colors.grey, - size: 20, - ), - ), - Container( - margin: widget.showAvatar ? (isSelf ? const EdgeInsets.only(right: 13) : const EdgeInsets.only(left: 13)) : null, - child: Column( - crossAxisAlignment: isSelf ? CrossAxisAlignment.end : CrossAxisAlignment.start, - children: [ - if ((isSelf && isShowNickNameForSelf) || (!isSelf && isShowNickNameForOthers)) - widget.topRowBuilder != null - ? widget.topRowBuilder!(context, message) + }, + onTapDown: isDesktopScreen + ? (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()); + } + }, + onSecondaryTap: isDesktopScreen + ? null + : () { + 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); + } + } + : null, + child: widget.userAvatarBuilder != null + ? widget.userAvatarBuilder!(context, message) : Container( - margin: const EdgeInsets.only(bottom: 4), - child: ConstrainedBox( - 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), + 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), ), - )), - Row( - crossAxisAlignment: CrossAxisAlignment.end, + ), + ), + ), + if (isSelf && widget.message.elemType == 6 && isDownloadWaiting) + Container( + margin: const EdgeInsets.only(top: 46, right: 10), + child: LoadingAnimationWidget.threeArchedCircle( + color: theme.weakTextColor ?? Colors.grey, + size: 20, + ), + ), + Container( + margin: widget.showAvatar ? (isSelf ? const EdgeInsets.only(right: 13) : const EdgeInsets.only(left: 13)) : null, + child: Column( + crossAxisAlignment: isSelf ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ - if (isSelf) renderHoverTipAndReadStatus(model, isSelf, message, isPeerRead, theme, isDownloadWaiting), - Container( - constraints: BoxConstraints( - maxWidth: constraints.maxWidth * 0.77, - ), - child: Builder(builder: (context) { - return Column( - crossAxisAlignment: (message.isSelf ?? true) ? CrossAxisAlignment.end : CrossAxisAlignment.start, - children: [ - GestureDetector( + 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), + child: Text( + MessageUtils.getDisplayName(message), + overflow: TextOverflow.ellipsis, + style: widget.themeData?.nickNameTextStyle ?? TextStyle(fontSize: 12, color: theme.weakTextColor), + ), + )), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + 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)), onSecondaryTapDown: (details) { if (widget.onLongPress != null) { @@ -1337,58 +1317,57 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState mapData = json.decode(data); - if (mapData["businessID"] == "group_poll") { - isvote = true; - } - } catch (err) { - // err - } - } - } - return isvote; - } - bool isAdminCanRecall() { if (widget.model.chatConfig.isGroupAdminRecallEnabled) { final selfMemberInfo = @@ -212,6 +193,15 @@ class TIMUIKitMessageTooltipState (isDesktopScreen && widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE && fileBeenDownloaded); + 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 != "") { + showTranslation = false; + } + } + final dynamicQuote = model.chatConfig.isAtWhenReplyDynamic?.call(widget.message); @@ -235,7 +225,7 @@ class TIMUIKitMessageTooltipState id: "copyMessage", iconImageAsset: "images/copy_message.png", onClick: () => _onTap("copyMessage", model)), - if (shouldShowForwardAction && !isVoteMessage(widget.message)) + if (shouldShowForwardAction && !model.isVoteMessage(widget.message)) MessageToolTipItem( label: TIM_t("转发"), id: "forwardMessage", @@ -253,16 +243,17 @@ class TIMUIKitMessageTooltipState id: "multiSelect", iconImageAsset: "images/multi_message.png", onClick: () => _onTap("multiSelect", model)), - MessageToolTipItem( - label: TIM_t("翻译"), - id: "translate", - iconImageAsset: "images/translate.png", - onClick: () => _onTap("translate", model)), MessageToolTipItem( label: TIM_t("删除"), id: "delete", iconImageAsset: "images/delete_message.png", onClick: () => _onTap("delete", model)), + if (showTranslation) + MessageToolTipItem( + label: TIM_t("翻译"), + id: "translate", + iconImageAsset: "images/translate.png", + onClick: () => _onTap("translate", model)), if (shouldShowRevokeAction) MessageToolTipItem( label: TIM_t("撤回"), @@ -480,10 +471,10 @@ class TIMUIKitMessageTooltipState break; case "multiSelect": model.updateMultiSelectStatus(true); - model.addToMultiSelectedMessageList(widget.message); + model.setMessageItemChecked(widget.message, true); break; case "forwardMessage": - model.addToMultiSelectedMessageList(widget.message); + model.setMessageItemChecked(widget.message, true); Navigator.push( context, MaterialPageRoute( diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart index 46de4f2..599971d 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list_container.dart @@ -125,15 +125,18 @@ class _TIMUIKitHistoryMessageListContainerState List historyMessageList = []; - Future requestForData(String? lastMsgID, LoadDirection direction, + Future requestForData(String? lastMsgID, LoadDirection direction, TUIChatSeparateViewModel model, - [int? count]) async { - if ((direction == LoadDirection.previous && model.haveMoreData) || + [int? count, int? lastSeq]) async { + if ((direction == LoadDirection.previous) || (direction == LoadDirection.latest && model.haveMoreLatestData)) { - await model.loadChatRecord( + return await model.loadChatRecord( direction: direction, count: count ?? (kIsWeb ? 15 : HistoryMessageDartConstant.getCount), - lastMsgID: lastMsgID); + lastMsgID: lastMsgID, + lastMsgSeq: lastSeq ?? -1,); + } else { + return false; } } @@ -203,8 +206,8 @@ class _TIMUIKitHistoryMessageListContainerState tongueItemBuilder: widget.tongueItemBuilder, initFindingMsg: widget.initFindingMsg, messageList: messageList, - onLoadMore: (String? a, LoadDirection direction, [int? b]) async { - return await requestForData(a, direction, model, b); + onLoadMore: (String? a, LoadDirection direction, [int? b, int? lastSeq]) async { + return await requestForData(a, direction, model, b, lastSeq); }, ); }, diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart index 8f21c6e..3b2e7d4 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_message_read_receipt.dart @@ -21,80 +21,91 @@ class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget { @override Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { final TUITheme theme = value.theme; - final TUIChatSeparateViewModel model = - Provider.of(context, listen: false); + final TUIChatSeparateViewModel model = Provider.of(context, listen: false); final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; return Selector( builder: (context, value, child) { - // if (value == null || value.unreadCount == 0 && value.readCount == 0) { - // return Container(); - // } - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if ((value?.readCount ?? 0) > 0) { - if(isDesktopScreen){ - TUIKitWidePopup.showPopupWindow( - operationKey: TUIKitWideModalOperationKey.messageReadDetails, - context: context, - width: MediaQuery.of(context).size.width * 0.5, - height: MediaQuery.of(context).size.height * 0.8, - title: TIM_t("消息详情"), - child: (onClose) => MessageReadReceipt( - model: model, - onTapAvatar: onTapAvatar, - messageItem: messageItem, - unreadCount: value?.unreadCount ?? 0, - readCount: value?.readCount ?? 0) - ); - }else{ - if (value?.unreadCount == 0) { - return; + if (model.conversationType == ConvType.c2c) { + bool isPeerRead = false; + if (value != null) { + isPeerRead = value.isPeerRead ?? false; + } + return Container( + padding: const EdgeInsets.only(bottom: 3), + margin: const EdgeInsets.only(right: 6), + child: Text( + isPeerRead ? TIM_t("已读") : TIM_t("未读"), + style: TextStyle(color: theme.chatMessageItemUnreadStatusTextColor, fontSize: 12), + ), + ); + } else { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if ((value?.readCount ?? 0) > 0) { + if(isDesktopScreen){ + TUIKitWidePopup.showPopupWindow( + operationKey: TUIKitWideModalOperationKey.messageReadDetails, + context: context, + width: MediaQuery.of(context).size.width * 0.5, + height: MediaQuery.of(context).size.height * 0.8, + title: TIM_t("消息详情"), + child: (onClose) => MessageReadReceipt( + model: model, + onTapAvatar: onTapAvatar, + messageItem: messageItem, + unreadCount: value?.unreadCount ?? 0, + readCount: value?.readCount ?? 0) + ); + } else { + if (value?.unreadCount == 0) { + return; + } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MessageReadReceipt( + model: model, + onTapAvatar: onTapAvatar, + messageItem: messageItem, + unreadCount: value?.unreadCount ?? 0, + readCount: value?.readCount ?? 0))); } - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => MessageReadReceipt( - model: model, - onTapAvatar: onTapAvatar, - messageItem: messageItem, - unreadCount: value?.unreadCount ?? 0, - readCount: value?.readCount ?? 0))); } - } - }, - child: Container( - padding: EdgeInsets.only( - bottom: 3, right: 6, left: 6, top: isDesktopScreen ? 2 : 6), - child: ((value?.unreadCount ?? 0) == 0 && (value?.readCount ?? 0) > 0) - ? Icon( - Icons.check_circle_outline, - size: 18, - color: theme.weakTextColor, - ) - : Container( - width: 14, - height: 14, - alignment: Alignment.center, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - width: 1.3, - color: (value?.readCount ?? 0) > 0 - ? theme.primaryColor! - : theme.weakTextColor!)), - child: (value?.readCount ?? 0) > 0 - ? Text( - '${value?.readCount ?? 0}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 8, color: theme.primaryColor), - ) - : null, - ), - ), - ); + }, + child: Container( + padding: EdgeInsets.only( + bottom: 3, right: 6, left: 6, top: isDesktopScreen ? 2 : 6), + child: ((value?.unreadCount ?? 0) == 0 && (value?.readCount ?? 0) > 0) + ? Icon( + Icons.check_circle_outline, + size: 18, + color: theme.weakTextColor, + ) + : Container( + width: 14, + height: 14, + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + width: 1.3, + color: (value?.readCount ?? 0) > 0 + ? theme.primaryColor! + : theme.weakTextColor!)), + child: (value?.readCount ?? 0) > 0 + ? Text( + '${value?.readCount ?? 0}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 8, color: theme.primaryColor), + ) + : null, + ), + ), + ); + } }, selector: (c, model) { return model.getMessageReadReceipt(messageItem.msgID ?? ""); diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart index f1dba03..e10b1e8 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_file_elem.dart @@ -143,25 +143,31 @@ class _TIMUIKitFileElemState extends TIMUIKitState { TencentUtils.checkString(widget.message.fileElem!.localUrl) ?? widget.message.fileElem?.path ?? ''; + File f = File(savePath); - if (f.existsSync() && widget.messageID != null) { - filePath = savePath; - if (downloadProgress != 100) { - setState(() { - downloadProgress = 100; - }); + if (widget.messageID != null) { + if (f.existsSync()) { + filePath = savePath; + if (downloadProgress != 100) { + setState(() { + downloadProgress = 100; + }); + } + if (model.getMessageProgress(widget.messageID) != 100) { + model.setMessageProgress(widget.messageID!, 100); + } + if (advancedMsgListener != null) { + TencentImSDKPlugin.v2TIMManager + .getMessageManager() + .removeAdvancedMsgListener(listener: advancedMsgListener); + advancedMsgListener = null; + } + return true; + } else { + model.setMessageProgress(widget.messageID!, 0); } - if (model.getMessageProgress(widget.messageID) != 100) { - model.setMessageProgress(widget.messageID!, 100); - } - if (advancedMsgListener != null) { - TencentImSDKPlugin.v2TIMManager - .getMessageManager() - .removeAdvancedMsgListener(listener: advancedMsgListener); - advancedMsgListener = null; - } - return true; } + return false; } @@ -409,7 +415,7 @@ class _TIMUIKitFileElemState extends TIMUIKitState { child: ConstrainedBox( constraints: const BoxConstraints(maxHeight: 72), child: Container( - width: 237, + width: 170, decoration: BoxDecoration( border: Border.all( color: theme.weakDividerColor ?? diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart index 0dc3c91..69c5f36 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart @@ -289,13 +289,12 @@ class _TIMUIKitReplyElemState extends TIMUIKitState { } void _jumpToRawMsg() { - if (rawMessage?.timestamp != null) { + if (rawMessage?.status != MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED && rawMessage?.timestamp != null) { widget.scrollToIndex(rawMessage); } else { onTIMCallback(TIMCallback( type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("无法定位到原消息"), - infoCode: 6660401)); + infoRecommendText: TIM_t("无法定位到原消息"))); } } diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_sound_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_sound_elem.dart index f7b384b..c01d772 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_sound_elem.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_sound_elem.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:math'; -// import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; import 'package:just_audio/just_audio.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; @@ -76,6 +75,10 @@ class _TIMUIKitSoundElemState extends TIMUIKitState { } else { SoundPlayer.play(url: stateElement.url!); widget.chatModel.currentPlayedMsgId = widget.msgID; + + setState(() { + isPlaying = widget.chatModel.currentPlayedMsgId != '' && widget.chatModel.currentPlayedMsgId == widget.msgID; + }); } } @@ -98,14 +101,6 @@ class _TIMUIKitSoundElemState extends TIMUIKitState { } } - @override - void didUpdateWidget(oldWidget) { - super.didUpdateWidget(oldWidget); - setState(() { - isPlaying = widget.chatModel.currentPlayedMsgId != '' && widget.chatModel.currentPlayedMsgId == widget.msgID; - }); - } - @override void initState() { super.initState(); @@ -113,6 +108,9 @@ class _TIMUIKitSoundElemState extends TIMUIKitState { subscription = SoundPlayer.playStateListener(listener: (PlayerState state) { if (state.processingState == ProcessingState.completed) { widget.chatModel.currentPlayedMsgId = ""; + setState(() { + isPlaying = false; + }); } }); diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_text_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_text_elem.dart index 23e6871..ad2a18d 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_text_elem.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_text_elem.dart @@ -253,6 +253,7 @@ class _TIMUIKitTextElemState extends TIMUIKitState { true, customEmojiStickerList: widget.customEmojiStickerList, showAtBackground: true, + checkHttpLink: true, )), // If the link preview info is available, render the preview card. if (_renderPreviewWidget() != null && diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart index 7277368..859064a 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart @@ -13,6 +13,7 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder { this.isUseTencentCloudChatPackage = false, this.customEmojiStickerList = const [], this.showAtBackground = false, + this.checkHttpLink = true, }); /// whether show background for @somebody @@ -22,6 +23,8 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder { final bool isUseTencentCloudChatPackage; + final bool checkHttpLink; + final List customEmojiStickerList; @override @@ -40,7 +43,7 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder { start: index! - (EmojiText.flag.length - 1), isUseQQPackage: isUseQQPackage, customEmojiStickerList: customEmojiStickerList); - } else if (isStart(flag, HttpText.flag)) { + } else if (isStart(flag, HttpText.flag) && checkHttpLink) { return HttpText(textStyle, onTap, start: index! - (HttpText.flag.length - 1)); } diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart index 1c345b3..6c469a9 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart @@ -8,7 +8,7 @@ class HttpText extends SpecialText { HttpText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap, {this.start}) : super(flag, flag, textStyle, onTap: onTap); - static const String flag = '\$'; + static const String flag = '!@TURL#*&\$'; final int? start; @override InlineSpan finishText() { @@ -30,16 +30,3 @@ class HttpText extends SpecialText { }); } } - -List dollarList = [ - '\$Dota2\$', - '\$Dota2 Ti9\$', - '\$CN dota best dota\$', - '\$Flutter\$', - '\$CN dev best dev\$', - '\$UWP\$', - '\$Nevermore\$', - '\$FlutterCandies\$', - '\$ExtendedImage\$', - '\$ExtendedText\$', -]; diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_at_text.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_at_text.dart index 0bb631b..7bd905b 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_at_text.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_at_text.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.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_self_info_view_model.dart'; import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; @@ -17,9 +18,7 @@ class AtText extends StatefulWidget { final V2TimGroupInfo? groupInfo; final List? groupMemberList; final VoidCallback? closeFunc; - final Function( - V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails)? - onChooseMember; + final Function(List memberInfo)? onChooseMember; final bool canAtAll; // some Group type cant @all @@ -42,10 +41,13 @@ class AtText extends StatefulWidget { class _AtTextState extends TIMUIKitState { final GroupServices _groupServices = serviceLocator(); + final TUISelfInfoViewModel _selfInfoViewModel = serviceLocator(); List? groupMemberList; List? searchMemberList; + List selectedGroupMemberList = []; + @override void initState() { groupMemberList = widget.groupMemberList; @@ -58,16 +60,15 @@ class _AtTextState extends TIMUIKitState { super.dispose(); } - _onTapMemberItem( - V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails) { + void _submitAtMemberList() { if (widget.closeFunc != null) { widget.closeFunc!(); } if (widget.onChooseMember != null) { - widget.onChooseMember!(memberInfo, tapDetails); + widget.onChooseMember!(selectedGroupMemberList); } else { - Navigator.pop(context, memberInfo); + Navigator.pop(context, selectedGroupMemberList); } } @@ -113,13 +114,30 @@ class _AtTextState extends TIMUIKitState { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { final TUITheme theme = value.theme; + V2TimUserFullInfo? loginUserInfo = _selfInfoViewModel.loginInfo; + if (loginUserInfo != null) { + searchMemberList?.removeWhere((memberInfo) { + return memberInfo?.userID == loginUserInfo.userID; + }); + } + Widget mentionedMembersBody() { return GroupProfileMemberList( groupType: widget.groupType ?? "", memberList: searchMemberList ?? [], - onTapMemberItem: _onTapMemberItem, canAtAll: widget.canAtAll, + canSelectMember: true, canSlideDelete: false, + onSelectedMemberChange: (selectedMemberList) { + selectedGroupMemberList = selectedMemberList; + bool isAtAllSelected = selectedGroupMemberList.where((element) { + return element.userID == GroupProfileMemberList.AT_ALL_USER_ID; + }).isNotEmpty; + + if (isAtAllSelected) { + _submitAtMemberList(); + } + }, touchBottomCallBack: () { // Get all by once, unnecessary to load more }, @@ -168,6 +186,20 @@ class _AtTextState extends TIMUIKitState { fontSize: 17, ), ), + actions: [ + TextButton( + onPressed: () { + _submitAtMemberList(); + }, + child: Text( + TIM_t("确定"), + style: TextStyle( + color: theme.appbarTextColor, + fontSize: 14, + ), + ), + ) + ], ), body: mentionedMembersBody())); } diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_more_panel.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_more_panel.dart index 023aaa4..ae8cbaa 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_more_panel.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_more_panel.dart @@ -32,6 +32,10 @@ import 'package:universal_html/html.dart' as html; import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart'; class MorePanelConfig { + static final int FILE_MAX_SIZE = 100 * 1024 * 1024; + static final int VIDEO_MAX_SIZE = 100 * 1024 * 1024; + static final int IMAGE_MAX_SIZE = 28 * 1024 * 1024; + final bool showGalleryPickAction; final bool showCameraAction; final bool showFilePickAction; @@ -316,20 +320,19 @@ class _MorePanelState extends TIMUIKitState { }).toList(); } - _sendVideoMessage(AssetEntity asset, TUIChatSeparateViewModel model) async { - final plugin = FcNativeVideoThumbnail(); - final originFile = await asset.originFile; - final size = await originFile!.length(); - if (size >= 104857600) { + _sendVideoMessage(AssetEntity asset, int size, TUIChatSeparateViewModel model) async { + if (size >= MorePanelConfig.VIDEO_MAX_SIZE) { onTIMCallback(TIMCallback( type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("发送失败,视频不能大于100MB"), - infoCode: 6660405)); + infoRecommendText: TIM_t("文件大小超出了限制"))); return; } + final plugin = FcNativeVideoThumbnail(); + final originFile = await asset.originFile; + final duration = asset.videoDuration.inSeconds; - final filePath = originFile.path; + final filePath = originFile!.path; final convID = widget.conversationID; final convType = widget.conversationType; @@ -341,9 +344,9 @@ class _MorePanelState extends TIMUIKitState { srcFile: originFile.path, destFile: tempPath, format: 'jpeg', - width: 128, + width: 1280, quality: 100, - height: 128, + height: 1280, ); MessageUtils.handleMessageError( model.sendVideoMessage( @@ -407,8 +410,16 @@ class _MorePanelState extends TIMUIKitState { final originFile = await asset.originFile; final filePath = originFile?.path; final type = asset.type; + final size = await originFile!.length(); if (filePath != null) { if (type == AssetType.image) { + if (size >= MorePanelConfig.IMAGE_MAX_SIZE) { + onTIMCallback(TIMCallback( + type: TIMCallbackType.INFO, + infoRecommendText: TIM_t("文件大小超出了限制"))); + return; + } + MessageUtils.handleMessageError( model.sendImageMessage( imagePath: filePath, @@ -418,7 +429,7 @@ class _MorePanelState extends TIMUIKitState { } if (type == AssetType.video) { - _sendVideoMessage(asset, model); + _sendVideoMessage(asset, size, model); } } } @@ -480,7 +491,15 @@ class _MorePanelState extends TIMUIKitState { final originFile = await pickedFile?.originFile; if (originFile != null) { final type = pickedFile!.type; + final size = await originFile!.length(); if (type == AssetType.image) { + if (size >= MorePanelConfig.IMAGE_MAX_SIZE) { + onTIMCallback(TIMCallback( + type: TIMCallbackType.INFO, + infoRecommendText: TIM_t("文件大小超出了限制"))); + return; + } + MessageUtils.handleMessageError( model.sendImageMessage( imagePath: originFile.path, @@ -489,7 +508,7 @@ class _MorePanelState extends TIMUIKitState { context); } if (type == AssetType.video) { - _sendVideoMessage(pickedFile, model); + _sendVideoMessage(pickedFile, size, model); } } else { // Toast.showToast(ToastType.fail, TIM_t("图片不能为空"), context); @@ -591,6 +610,13 @@ class _MorePanelState extends TIMUIKitState { File file = File(result.files.single.path!); final int size = file.lengthSync(); + if (size >= MorePanelConfig.FILE_MAX_SIZE) { + onTIMCallback(TIMCallback( + type: TIMCallbackType.INFO, + infoRecommendText: TIM_t("文件大小超出了限制"))); + return; + } + final String savePath = file.path; MessageUtils.handleMessageError( @@ -643,13 +669,21 @@ class _MorePanelState extends TIMUIKitState { _goToVideoUI(String type) async { if (!PlatformUtils().isWeb) { - final hasCameraPermission = type == TYPE_VIDEO - ? await Permissions.checkPermission(context, Permission.camera.value) - : true; - final hasMicphonePermission = await Permissions.checkPermission( - context, Permission.microphone.value); - if (!hasCameraPermission || !hasMicphonePermission) { - return; + bool hasCameraPermission = false; + bool hasMicrophonePermission = false; + if (type == TYPE_VIDEO) { + hasCameraPermission = await Permissions.checkPermission(context, Permission.camera.value); + hasMicrophonePermission = await Permissions.checkPermission( + context, Permission.microphone.value); + if (!hasCameraPermission || !hasMicrophonePermission) { + return; + } + } else { + hasMicrophonePermission = await Permissions.checkPermission( + context, Permission.microphone.value); + if (!hasMicrophonePermission) { + return; + } } } diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart index bf1e4dd..ffe4036 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart @@ -192,7 +192,7 @@ class _InputTextFieldState extends TIMUIKitState { return stickerPackageList; } - setCurrentCursor(int? value) { + _setCurrentCursor(int? value) { currentCursor = value; } @@ -203,7 +203,7 @@ class _InputTextFieldState extends TIMUIKitState { return emojiRegex().hasMatch(input); } - void deleteStickerFromText() { + void _deleteStickerFromText() { String originalText = textEditingController.text; if (originalText == zeroWidthSpace) { @@ -239,9 +239,21 @@ class _InputTextFieldState extends TIMUIKitState { focusNode.requestFocus(); } } + + if (originalText.isEmpty && textEditingController.text.isEmpty) { + widget.model.repliedMessage = null; + } } - void addStickerToText(String sticker) { + void _onDeleteText(String oldText) { + if (oldText.isEmpty) { + if (widget.model.repliedMessage != null) { + widget.model.repliedMessage = null; + } + } + } + + void _addStickerToText(String sticker) { final currentText = textEditingController.text; if (currentCursor != null && currentCursor! > -1 && currentCursor! < currentText.length + 1) { final firstString = currentText.substring(0, currentCursor); @@ -267,13 +279,13 @@ class _InputTextFieldState extends TIMUIKitState { String text = textEditingController.text; String convID = id ?? widget.conversationID; final isTopic = convID.contains("@TOPIC#"); - String conversationID = isTopic ? convID : ((convType ?? widget.conversationType) == ConvType.c2c ? "c2c_$convID" : "group_$convID"); + String conversationID = isTopic ? convID : ((convType ?? widget.conversationType) == ConvType.c2c ? "${TUIConversationViewModel.conversationC2CPrefix}$convID" : "${TUIConversationViewModel.conversationGroupPrefix}$convID"); String draftText = _filterU200b(text); return await conversationModel.setConversationDraft(groupID: groupID ?? widget.groupID, isTopic: isTopic, isAllowWeb: widget.model.chatConfig.isUseDraftOnWeb, conversationID: conversationID, draftText: draftText); } -// 和onSubmitted一样,只是保持焦点的不同 - onEmojiSubmitted() { + // 和onSubmitted一样,只是保持焦点的不同 + _onEmojiSubmitted() { lastText = ""; final text = textEditingController.text.trim(); final convType = widget.conversationType; @@ -303,8 +315,8 @@ class _InputTextFieldState extends TIMUIKitState { currentCursor = null; } -// index为emoji的index,data为baseurl+name - onCustomEmojiFaceSubmitted(int index, String data) { + // index为emoji的index,data为baseurl+name + _onCustomEmojiFaceSubmitted(int index, String data) { final convType = widget.conversationType; // This part of the code is written to adapt to the Native side requirements. @@ -610,19 +622,25 @@ class _InputTextFieldState extends TIMUIKitState { isAddingAtSearchWords = false; } } else if (textLength > 0 && text[textLength - 1] == "@" && lastText.length < textLength) { - V2TimGroupMemberFullInfo? memberInfo = await Navigator.push( + List selectedAtMemberList = await Navigator.push( context, MaterialPageRoute( builder: (context) => AtText(groupMemberList: model.groupMemberList, groupInfo: model.groupInfo, groupID: groupID, canAtAll: canAtAll, groupType: widget.groupType), ), ); - final showName = _getShowName(memberInfo); - if (memberInfo != null) { - mentionedMembersMap["@$showName"] = memberInfo; - textEditingController.text = "$text$showName "; - lastText = "$text$showName "; + + for (int i = 0; i < selectedAtMemberList.length; ++i) { + V2TimGroupMemberFullInfo memberInfo = selectedAtMemberList[i]; + final showName = _getShowName(memberInfo); + if (memberInfo != null) { + mentionedMembersMap["@$showName"] = memberInfo; + String addAtCharacter = i == 0 ? "" : "@"; + textEditingController.text = "${textEditingController.text}$addAtCharacter$showName "; + lastText = "${textEditingController.text}$addAtCharacter$showName "; + } } } + lastText = textEditingController.text; } @@ -858,12 +876,13 @@ class _InputTextFieldState extends TIMUIKitState { context: context, defaultWidget: TIMUIKitTextFieldLayoutNarrow( stickerPackageList: stickerPackageList, - onEmojiSubmitted: onEmojiSubmitted, - onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted, - backSpaceText: deleteStickerFromText, - addStickerToText: addStickerToText, + onEmojiSubmitted: _onEmojiSubmitted, + onCustomEmojiFaceSubmitted: _onCustomEmojiFaceSubmitted, + backSpaceText: _deleteStickerFromText, + addStickerToText: _addStickerToText, customStickerPanel: widget.customStickerPanel, onChanged: widget.onChanged, + onDeleteText: _onDeleteText, backgroundColor: widget.backgroundColor, morePanelConfig: widget.morePanelConfig, repliedMessage: value, @@ -876,7 +895,7 @@ class _InputTextFieldState extends TIMUIKitState { conversationType: widget.conversationType, focusNode: focusNode, controller: widget.controller, - setCurrentCursor: setCurrentCursor, + setCurrentCursor: _setCurrentCursor, onCursorChange: _onCursorChange, model: model, handleSendEditStatus: _handleSendEditStatus, @@ -895,10 +914,10 @@ class _InputTextFieldState extends TIMUIKitState { chatConfig: widget.chatConfig ?? widget.model.chatConfig, theme: theme, currentConversation: widget.currentConversation, - onEmojiSubmitted: onEmojiSubmitted, - onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted, - backSpaceText: deleteStickerFromText, - addStickerToText: addStickerToText, + onEmojiSubmitted: _onEmojiSubmitted, + onCustomEmojiFaceSubmitted: _onCustomEmojiFaceSubmitted, + backSpaceText: _deleteStickerFromText, + addStickerToText: _addStickerToText, customStickerPanel: widget.customStickerPanel, onChanged: widget.onChanged, backgroundColor: widget.backgroundColor, @@ -913,7 +932,7 @@ class _InputTextFieldState extends TIMUIKitState { conversationType: widget.conversationType, focusNode: focusNode, controller: widget.controller, - setCurrentCursor: setCurrentCursor, + setCurrentCursor: _setCurrentCursor, onCursorChange: _onCursorChange, model: model, handleSendEditStatus: _handleSendEditStatus, diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart index e0d8fec..8c9c31c 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:extended_text_field/extended_text_field.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.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'; @@ -81,6 +82,8 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget { final V2TimMessage? repliedMessage; + final void Function(String)? onDeleteText; + /// show send emoji icon final bool showSendEmoji; @@ -112,6 +115,7 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget { required this.model, this.backgroundColor, this.onChanged, + this.onDeleteText, required this.handleSendEditStatus, required this.handleAtText, required this.handleSoftKeyBoardDelete, @@ -444,60 +448,78 @@ class _TIMUIKitTextFieldLayoutNarrowState extends TIMUIKitState? groupReadReceiptPermisionList; /// Control which group can send message read receipt. + /// [Deprecated: ] not support. final List? groupReadReceiptPermissionList; /// Control if show self name in group chat. @@ -271,7 +275,7 @@ class TIMUIKitChatConfig { this.isShowSelfNameInGroup = false, this.isAtWhenReplyDynamic, this.offlinePushInfo, - @Deprecated("Please use [isShowGroupReadingStatus] instead") + @Deprecated("Please use [isShowReadingStatus] instead") this.isShowGroupMessageReadReceipt = true, this.upperRecallTime = 120, this.isShowOthersNameInGroup = true, @@ -281,8 +285,9 @@ class TIMUIKitChatConfig { this.notificationTitle = "", this.notificationIOSSound = "", this.isAllowSoundMessage = true, - @Deprecated("Please use [groupReadReceiptPermissionList] instead") + @Deprecated("not support") this.groupReadReceiptPermisionList, + @Deprecated("not support") this.groupReadReceiptPermissionList, this.isAllowEmojiPanel = true, this.isAllowShowMorePanel = true, @@ -294,6 +299,7 @@ class TIMUIKitChatConfig { this.isEnableTextSelection, this.additionalDesktopMessageHoverBarItem, this.isShowGroupReadingStatus = true, + @Deprecated("Please use [isShowReadingStatus] instead") this.isReportGroupReadingStatus = true, this.showC2cMessageEditStatus = true, this.additionalDesktopControlBarItems, diff --git a/lib/ui/views/TIMUIKitChat/tim_uikit_multi_select_panel.dart b/lib/ui/views/TIMUIKitChat/tim_uikit_multi_select_panel.dart index 688d220..06d92af 100644 --- a/lib/ui/views/TIMUIKitChat/tim_uikit_multi_select_panel.dart +++ b/lib/ui/views/TIMUIKitChat/tim_uikit_multi_select_panel.dart @@ -14,6 +14,8 @@ import 'package:tencent_im_base/tencent_im_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; class MultiSelectPanel extends TIMUIKitStatelessWidget { + final int forwardMsgNumLimit = 30; + final ConvType conversationType; MultiSelectPanel({Key? key, required this.conversationType}) @@ -21,6 +23,39 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget { _handleForwardMessage(BuildContext context, bool isMergerForward, TUIChatSeparateViewModel model) { + + // 是否有选中消息 + if (model.getSelectedMessageList().isEmpty) { + onTIMCallback(TIMCallback( + type: TIMCallbackType.INFO, + infoRecommendText: TIM_t("请选择要操作的消息!"))); + return; + } + + for (var v2TimMessage in model.getSelectedMessageList()) { + // 失败消息不支持转发 + if (v2TimMessage.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) { + onTIMCallback(TIMCallback( + type: TIMCallbackType.INFO, + infoRecommendText: TIM_t("发送失败消息不支持转发!"))); + return; + } + + // 投票消息不支持转发 + if (model.isVoteMessage(v2TimMessage)) { + onTIMCallback(TIMCallback( + type: TIMCallbackType.INFO, + infoRecommendText: TIM_t("投票消息不支持转发!"))); + return; + } + } + + // 逐条转发限制在 30 条以内 + if (!isMergerForward && model.getSelectedMessageList().length > forwardMsgNumLimit) { + _showForwardLimitDialog(context); + return; + } + Navigator.push( context, MaterialPageRoute( @@ -31,6 +66,33 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget { ))); } + // 弹出逐条转发超限的对话框 + Future _showForwardLimitDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text(TIM_t("转发消息过多,暂不支持逐条转发")), + actions: [ + CupertinoDialogAction( + child: Text(TIM_t("确定")), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + CupertinoDialogAction( + child: Text(TIM_t("取消")), + isDestructiveAction: true, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + _handleForwardMessageWide(BuildContext context, bool isMergerForward, TUIChatSeparateViewModel model) { TUIKitWidePopup.showPopupWindow( diff --git a/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation.dart b/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation.dart index 47a00c8..78be327 100644 --- a/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation.dart +++ b/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation.dart @@ -362,7 +362,6 @@ class _TIMUIKitConversationState extends TIMUIKitState { child: GestureDetector( child: TIMUIKitConversationItem( isCurrent: isCurrent, - isShowDraft: widget.isShowDraft, lastMessageBuilder: widget.lastMessageBuilder, faceUrl: conversationItem.faceUrl ?? "", nickName: conversationItem.showName ?? "", diff --git a/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_item.dart b/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_item.dart index 1e0af71..a323084 100644 --- a/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_item.dart +++ b/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_item.dart @@ -31,14 +31,8 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget { final int? convType; final bool isCurrent; - /// Control if shows the identifier that the conversation has a draft text, inputted in previous. - /// Also, you'd better specifying the `draftText` field for `TIMUIKitChat`, from the `draftText` in `V2TimConversation`, - /// to meet the identifier shows here. - final bool isShowDraft; - TIMUIKitConversationItem({ Key? key, - required this.isShowDraft, required this.faceUrl, required this.nickName, required this.lastMsg, @@ -56,22 +50,20 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget { Widget _getShowMsgWidget(BuildContext context) { final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; - if (isShowDraft && draftText != null && draftText != "") { - return TIMUIKitDraftText( - context: context, - draftText: draftText ?? "", - fontSize: isDesktopScreen ? 12 : 14, - ); - } else if (lastMsg != null) { - if (lastMessageBuilder != null && + if (lastMsg != null && lastMessageBuilder != null && lastMessageBuilder!(lastMsg, groupAtInfoList) != null) { return lastMessageBuilder!(lastMsg, groupAtInfoList)!; } + + if (lastMsg != null || (draftText != null && draftText != "")) { return TIMUIKitLastMsg( fontSize: isDesktopScreen ? 12 : 14, groupAtInfoList: groupAtInfoList, lastMsg: lastMsg, + isDisturb: isDisturb, + unreadCount: unreadCount, context: context, + draftText: draftText ?? "", ); } @@ -81,8 +73,7 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget { } bool isHaveSecondLine() { - return (isShowDraft && draftText != null && draftText != "") || - (lastMsg != null); + return (draftText != null && draftText != "") || (lastMsg != null); } Widget _getTimeStringForChatWidget(BuildContext context, TUITheme theme) { diff --git a/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart b/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart index bc7ddb9..f73adc4 100644 --- a/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart +++ b/lib/ui/views/TIMUIKitConversation/tim_uikit_conversation_last_msg.dart @@ -2,12 +2,16 @@ import 'dart:convert'; +import 'package:extended_text/extended_text.dart'; import 'package:flutter/material.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'; import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart'; +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart'; +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit_conversation_draft_text.dart'; import 'package:tencent_im_base/tencent_im_base.dart'; class TIMUIKitLastMsg extends StatefulWidget { @@ -15,8 +19,20 @@ class TIMUIKitLastMsg extends StatefulWidget { final List groupAtInfoList; final BuildContext context; final double fontSize; + bool isDisturb; + int unreadCount; + String draftText; - const TIMUIKitLastMsg({Key? key, this.lastMsg, required this.groupAtInfoList, required this.context, this.fontSize = 14.0}) : super(key: key); + TIMUIKitLastMsg( + {Key? key, + this.lastMsg, + required this.groupAtInfoList, + this.isDisturb = false, + this.unreadCount = 0, + required this.draftText, + required this.context, + this.fontSize = 14.0}) + : super(key: key); @override State createState() => _TIMUIKitLastMsgState(); @@ -34,7 +50,10 @@ class _TIMUIKitLastMsgState extends TIMUIKitState { @override void didUpdateWidget(covariant TIMUIKitLastMsg oldWidget) { super.didUpdateWidget(oldWidget); - if ((oldWidget.lastMsg?.msgID != widget.lastMsg?.msgID) || (oldWidget.lastMsg?.id != widget.lastMsg?.id) || (oldWidget.lastMsg?.status != widget.lastMsg?.status)) { + if ((oldWidget.lastMsg?.msgID != widget.lastMsg?.msgID) || + (oldWidget.lastMsg?.id != widget.lastMsg?.id) || + (oldWidget.lastMsg?.status != widget.lastMsg?.status) || + (oldWidget.unreadCount != widget.unreadCount)) { _getMsgElem(); } } @@ -63,22 +82,33 @@ class _TIMUIKitLastMsgState extends TIMUIKitState { final isAdminRevoke = revokeStatus.$2; if (isRevokedMessage) { final isSelf = widget.lastMsg!.isSelf ?? true; - final option1 = isAdminRevoke ? TIM_t("管理员") : (isSelf ? TIM_t("您") : widget.lastMsg!.nickName ?? widget.lastMsg?.sender); + final option1 = + isAdminRevoke ? TIM_t("管理员") : (isSelf ? TIM_t("您") : widget.lastMsg!.nickName ?? widget.lastMsg?.sender); if (mounted) { setState(() { groupTipsAbstractText = TIM_t_para("{{option1}}撤回了一条消息", "$option1撤回了一条消息")(option1: option1); }); } } else { - final newText = await _getLastMsgShowText(widget.lastMsg, widget.context) ?? ""; + String msgShowText = await _getLastMsgShowText(widget.lastMsg, widget.context) ?? ""; if (mounted) { setState(() { - groupTipsAbstractText = newText; + groupTipsAbstractText = msgShowText; }); } } } + String _getDisturbUnreadCountInfo() { + if (widget.isDisturb && widget.unreadCount > 0) { + final option1 = widget.unreadCount.toString(); + String unreadCountText = TIM_t_para("[{{option1}} 条]", "[$option1 条]")(option1: option1); + return unreadCountText; + } + + return ""; + } + Future _getLastMsgShowText(V2TimMessage? message, BuildContext context) async { final msgType = message!.elemType; switch (msgType) { @@ -121,37 +151,87 @@ class _TIMUIKitLastMsgState extends TIMUIKitState { } String _getAtMessage() { + bool atMe = false; + bool atAll = false; String msg = ""; for (var item in widget.groupAtInfoList) { if (item!.atType == 1) { - msg = TIM_t("[有人@我] "); - } else { - msg = TIM_t("[@所有人] "); + atMe = true; + continue; + } else if (item!.atType == 2) { + atAll = true; + continue; + } else if (item!.atType == 3) { + atMe = true; + atAll = true; + continue; } } + + if (atAll && atMe) { + msg = TIM_t("[@所有人][有人@我]"); + } else if (atAll) { + msg = TIM_t("[@所有人]"); + } else if (atMe) { + msg = TIM_t("[有人@我]"); + } + return msg; } + String _getDraftShowText() { + final draftShowText = TIM_t("草稿"); + return '[$draftShowText]'; + } + @override Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { + final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final TUITheme theme = value.theme; final icon = _getIconByMsgStatus(context); + String disturbUnreadCountInfo = _getDisturbUnreadCountInfo(); return Row(children: [ if (icon != null) Container( margin: const EdgeInsets.only(right: 2), child: icon, ), - if (widget.groupAtInfoList.isNotEmpty) Text(_getAtMessage(), style: TextStyle(color: theme.cautionColor, fontSize: widget.fontSize)), - if (TencentUtils.checkString(groupTipsAbstractText) != null) + if (widget.groupAtInfoList.isNotEmpty) + Text(_getAtMessage(), style: TextStyle(color: theme.cautionColor, fontSize: widget.fontSize)), + if (widget.draftText != null && widget.draftText != "") + Text(_getDraftShowText(), style: TextStyle(color: theme.conversationItemDraftTextColor, fontSize: widget.fontSize)), + if (disturbUnreadCountInfo != "") + Text(disturbUnreadCountInfo, style: TextStyle(color: theme.weakTextColor, fontSize: widget.fontSize)), + if (widget.draftText != null && widget.draftText != "") Expanded( - child: Text( - groupTipsAbstractText, - softWrap: true, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(height: 1, color: theme.weakTextColor, fontSize: widget.fontSize), - )), + child: ExtendedText( + groupTipsAbstractText, + softWrap: true, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(height: 1, color: theme.weakTextColor, fontSize: widget.fontSize), + specialTextSpanBuilder: DefaultSpecialTextSpanBuilder( + isUseQQPackage: true, + isUseTencentCloudChatPackage: true, + showAtBackground: true, + ) + ), + ), + if (widget.draftText == null || widget.draftText == "" && TencentUtils.checkString(groupTipsAbstractText) != null) + Expanded( + child: ExtendedText( + groupTipsAbstractText, + softWrap: true, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(height: 1, color: theme.weakTextColor, fontSize: widget.fontSize), + specialTextSpanBuilder: DefaultSpecialTextSpanBuilder( + isUseQQPackage: true, + isUseTencentCloudChatPackage: true, + showAtBackground: true, + ) + ), + ) ]); } } diff --git a/lib/ui/views/TIMUIKitGroupProfile/group_member/tui_group_member_list.dart b/lib/ui/views/TIMUIKitGroupProfile/group_member/tui_group_member_list.dart index a397b31..0d9aa31 100644 --- a/lib/ui/views/TIMUIKitGroupProfile/group_member/tui_group_member_list.dart +++ b/lib/ui/views/TIMUIKitGroupProfile/group_member/tui_group_member_list.dart @@ -104,9 +104,9 @@ class GroupProfileMemberListPageState memberList: searchMemberList ?? groupProfileModel.groupMemberList, removeMember: _kickedOffMember, touchBottomCallBack: () {}, - onTapMemberItem: (friendInfo, details) { + onTapMemberItem: (memberInfo, details) { if (widget.model.onClickUser != null) { - widget.model.onClickUser!(friendInfo.userID, details); + widget.model.onClickUser!(memberInfo, details); } }, ); @@ -134,9 +134,9 @@ class GroupProfileMemberListPageState memberList: searchMemberList ?? groupProfileModel.groupMemberList, removeMember: _kickedOffMember, touchBottomCallBack: () {}, - onTapMemberItem: (friendInfo, details) { + onTapMemberItem: (memberInfo, details) { if (widget.model.onClickUser != null) { - widget.model.onClickUser!(friendInfo.userID, details); + widget.model.onClickUser!(memberInfo, details); } }, ) diff --git a/lib/ui/views/TIMUIKitGroupProfile/tim_uikit_group_profile.dart b/lib/ui/views/TIMUIKitGroupProfile/tim_uikit_group_profile.dart index d04fb83..42083e6 100644 --- a/lib/ui/views/TIMUIKitGroupProfile/tim_uikit_group_profile.dart +++ b/lib/ui/views/TIMUIKitGroupProfile/tim_uikit_group_profile.dart @@ -55,7 +55,7 @@ class TIMUIKitGroupProfile extends StatefulWidget { /// The callback after user clicking a user, /// you may navigating to the specific profile page, or anywhere you want. - final Function(String userID, TapDownDetails? tapDetails)? onClickUser; + final Function(V2TimGroupMemberFullInfo groupMemberFullInfo, TapDownDetails? tapDetails)? onClickUser; const TIMUIKitGroupProfile( {Key? key, @@ -154,6 +154,10 @@ class _TIMUIKitGroupProfileState extends TIMUIKitState { groupListenerModel.needUpdate = null; switch (needUpdate.updateType) { case UpdateType.groupInfo: + if (needUpdate.groupInfoSubType == GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER) { + model.onOwnerChanged(needUpdate.ownerID); + } + model.loadGroupInfo(widget.groupID); break; case UpdateType.memberList: diff --git a/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_ui_group_profile_widget.dart b/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_ui_group_profile_widget.dart index 4753ad5..5ca92c5 100644 --- a/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_ui_group_profile_widget.dart +++ b/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_ui_group_profile_widget.dart @@ -5,7 +5,7 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/t import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_add_opt.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_detail_card.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_manage.dart'; -import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_tile.dart'; +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_title.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_message_disturb.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_name_card.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_notification.dart'; @@ -27,7 +27,7 @@ class TIMUIKitGroupProfileWidget { } static Widget memberTile() { - return GroupMemberTile(); + return GroupMemberTitle(); } static Widget groupNotification({ diff --git a/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_tile.dart b/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_title.dart similarity index 98% rename from lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_tile.dart rename to lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_title.dart index d0e34fc..ee3f97f 100644 --- a/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_tile.dart +++ b/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_title.dart @@ -15,8 +15,8 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart'; import 'package:tencent_im_base/tencent_im_base.dart'; -class GroupMemberTile extends TIMUIKitStatelessWidget { - GroupMemberTile({ +class GroupMemberTitle extends TIMUIKitStatelessWidget { + GroupMemberTitle({ Key? key, }) : super(key: key); @@ -44,7 +44,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget { return InkWell( onTapDown: (details) { if (model.onClickUser != null && element?.userID != null) { - model.onClickUser!(element!.userID, details); + model.onClickUser!(element!, details); } }, child: SizedBox( diff --git a/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_type.dart b/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_type.dart index edacc6e..ae36bd0 100644 --- a/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_type.dart +++ b/lib/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_type.dart @@ -45,26 +45,22 @@ class GroupProfileType extends TIMUIKitStatelessWidget { padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), decoration: BoxDecoration( color: Colors.white, - border: isDesktopScreen - ? null - : Border( - bottom: BorderSide( - color: theme.weakDividerColor ?? - CommonColor.weakDividerColor))), + border: isDesktopScreen ? null : Border( + bottom: BorderSide( + color: + theme.weakDividerColor ?? CommonColor.weakDividerColor))), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( TIM_t("群类型"), style: TextStyle( - fontSize: isDesktopScreen ? 14 : 16, - color: theme.darkTextColor), + fontSize: isDesktopScreen ? 14 : 16, color: theme.darkTextColor), ), Text( groupType, style: TextStyle( - fontSize: isDesktopScreen ? 14 : 16, - color: theme.weakTextColor), + fontSize: isDesktopScreen ? 14 : 16, color: theme.weakTextColor), ) ], ), diff --git a/lib/ui/views/TIMUIKitProfile/tim_uikit_profile.dart b/lib/ui/views/TIMUIKitProfile/tim_uikit_profile.dart index 319e3d1..5ca1bd0 100644 --- a/lib/ui/views/TIMUIKitProfile/tim_uikit_profile.dart +++ b/lib/ui/views/TIMUIKitProfile/tim_uikit_profile.dart @@ -240,51 +240,14 @@ class _TIMUIKitProfileState extends TIMUIKitState { } void handleAddFriend() async { - model.addFriend(userInfo.userID).then((res) { - if (res == null) { - throw Error(); - } - if (res.resultCode == 0) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("好友添加成功"), - infoCode: 6661202)); - } else if (res.resultCode == 30539) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("好友申请已发出"), - infoCode: 6661203)); - } else if (res.resultCode == 30515) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("当前用户在黑名单"), - infoCode: 6661204)); - } - }).catchError((error) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("好友添加失败"), - infoCode: 6661205)); - }); + model.addFriend(userInfo.userID); } - void handleDeleteFriend() { - model.deleteFriend(userInfo.userID).then((res) { - if (res == null) { - throw Error(); - } - if (res.resultCode != 0 && res.resultCode != null) { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("好友删除失败"), - infoCode: 6661207)); - } else { - onTIMCallback(TIMCallback( - type: TIMCallbackType.INFO, - infoRecommendText: TIM_t("好友删除成功"), - infoCode: 6661206)); - } - }); + Future handleDeleteFriend() async { + var result = await model.deleteFriend(userInfo.userID, needUpdateData: false); + if (result != null) { + Navigator.pop(context); + } } List _renderWidgetsWithOrder(List order) { diff --git a/lib/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_widget.dart b/lib/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_widget.dart index 0b99b99..4c5c1f7 100644 --- a/lib/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_widget.dart +++ b/lib/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_widget.dart @@ -263,7 +263,6 @@ class TIMUIKitProfileWidget extends TIMUIKitClass { return TIMUIKitOperationItem( smallCardMode: smallCardMode, isEmpty: false, - showAllowEditStatus: false, operationName: TIM_t("生日"), operationRightWidget: Text(formatter.format(date), textAlign: isDesktopScreen ? null : TextAlign.end), @@ -272,7 +271,6 @@ class TIMUIKitProfileWidget extends TIMUIKitClass { return TIMUIKitOperationItem( smallCardMode: smallCardMode, isEmpty: false, - showAllowEditStatus: false, operationName: TIM_t("生日"), operationRightWidget: Text(TIM_t("未填写"), textAlign: isDesktopScreen ? null : TextAlign.end), diff --git a/lib/ui/views/TIMUIKitSearch/tim_uikit_search_friend.dart b/lib/ui/views/TIMUIKitSearch/tim_uikit_search_friend.dart index 85d314d..07feebd 100644 --- a/lib/ui/views/TIMUIKitSearch/tim_uikit_search_friend.dart +++ b/lib/ui/views/TIMUIKitSearch/tim_uikit_search_friend.dart @@ -66,16 +66,24 @@ class TIMUIKitSearchFriendState extends TIMUIKitState { int convIndex = _conversationList .indexWhere((item) => conv.friendInfo?.userID == item?.userID); V2TimConversation conversation = _conversationList[convIndex]!; + late String? showNickName; + if (conv.friendInfo?.friendRemark != null && conv.friendInfo?.friendRemark != "") { + showNickName = conv.friendInfo?.friendRemark; + } else if (conv.friendInfo?.userProfile?.nickName != null && conv.friendInfo?.userProfile?.nickName != "") { + showNickName = conv.friendInfo?.userProfile?.nickName; + } else { + showNickName = conv.friendInfo?.userID; + } + return TIMUIKitSearchItem( onClick: () { widget.onTapConversation(conversation, null); }, faceUrl: conv.friendInfo?.userProfile?.faceUrl ?? "", showName: "", - lineOne: conversation.showName ?? - conversation.userID ?? - conv.friendInfo?.userID ?? - "", + + lineOne: conversation.userID!, + lineTwo: TIM_t("昵称") + ":" + showNickName!, ); }).toList(), _renderShowALl(filteredFriendResultList.length), diff --git a/lib/ui/widgets/forward_message_screen.dart b/lib/ui/widgets/forward_message_screen.dart index ed2c073..ce7986f 100644 --- a/lib/ui/widgets/forward_message_screen.dart +++ b/lib/ui/widgets/forward_message_screen.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart'; @@ -53,7 +54,7 @@ class _ForwardMessageScreenState extends TIMUIKitState { } List _getAbstractList() { - return widget.model.multiSelectedMessageList.map((e) { + return widget.model.getSelectedMessageList().map((e) { final sender = (e.nickName != null && e.nickName!.isNotEmpty) ? e.nickName : e.sender; @@ -62,6 +63,11 @@ class _ForwardMessageScreenState extends TIMUIKitState { } handleForwardMessage() async { + var confirmResult = await _showConfirmForwardDialog(context); + if (confirmResult == null) { + return; + } + if (widget.isMergerForward) { await widget.model.sendMergerMessage( conversationList: _conversationList, @@ -82,6 +88,33 @@ class _ForwardMessageScreenState extends TIMUIKitState { } } + // 弹出转发确认对话框 + Future _showConfirmForwardDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text(TIM_t("您确定进行转发吗?")), + actions: [ + CupertinoDialogAction( + child: Text(TIM_t("确定")), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + CupertinoDialogAction( + child: Text(TIM_t("取消")), + isDestructiveAction: true, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + @override void dispose() { super.dispose(); diff --git a/lib/ui/widgets/gestured_image.dart b/lib/ui/widgets/gestured_image.dart index f74745f..66e5437 100644 --- a/lib/ui/widgets/gestured_image.dart +++ b/lib/ui/widgets/gestured_image.dart @@ -184,7 +184,8 @@ class GesturedImageState extends ExtendedImageGestureState { gestureDetails = GestureDetails( totalScale: _gestureConfig!.initialScale, offset: Offset.zero, - )..initialAlignment = _gestureConfig!.initialAlignment; + initialAlignment: _gestureConfig!.initialAlignment, + ); } @override @@ -433,7 +434,8 @@ class GesturedImageState extends ExtendedImageGestureState { _gestureDetails = GestureDetails( totalScale: _gestureConfig!.initialScale, offset: Offset.zero, - )..initialAlignment = _gestureConfig!.initialAlignment; + initialAlignment: _gestureConfig!.initialAlignment, + ); } if (_gestureConfig!.cacheGesture) { diff --git a/lib/ui/widgets/group_member_list.dart b/lib/ui/widgets/group_member_list.dart index 2feadcd..45c94ff 100644 --- a/lib/ui/widgets/group_member_list.dart +++ b/lib/ui/widgets/group_member_list.dart @@ -15,6 +15,7 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/radio_button.dart'; import 'package:tencent_im_base/tencent_im_base.dart'; class GroupProfileMemberList extends StatefulWidget { + static String AT_ALL_USER_ID = "__kImSDK_MesssageAtALL__"; final List memberList; final Function(String userID)? removeMember; final bool canSlideDelete; @@ -53,7 +54,7 @@ class GroupProfileMemberList extends StatefulWidget { } class _GroupProfileMemberListState extends TIMUIKitState { - List selectedMember = []; + List selectedMemberList = []; _getShowName(V2TimGroupMemberFullInfo? item) { final friendRemark = item?.friendRemark ?? ""; @@ -93,7 +94,7 @@ class _GroupProfileMemberListState extends TIMUIKitState if (widget.canAtAll) { final canAtGroupType = ["Work", "Public", "Meeting"]; if (canAtGroupType.contains(widget.groupType)) { - showList.insert(0, ISuspensionBeanImpl(memberInfo: V2TimGroupMemberFullInfo(userID: "__kImSDK_MesssageAtALL__", nickName: TIM_t("所有人")), tagIndex: "")); + showList.insert(0, ISuspensionBeanImpl(memberInfo: V2TimGroupMemberFullInfo(userID: GroupProfileMemberList.AT_ALL_USER_ID, nickName: TIM_t("所有人")), tagIndex: "")); } } @@ -133,19 +134,21 @@ class _GroupProfileMemberListState extends TIMUIKitState child: CheckBoxButton( onChanged: (isChecked) { if (isChecked) { - if (widget.maxSelectNum != null && selectedMember.length >= widget.maxSelectNum!) { + if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) { return; } - selectedMember.add(memberInfo); + selectedMemberList.add(memberInfo); } else { - selectedMember.remove(memberInfo); + selectedMemberList.removeWhere((element) => element.userID == memberInfo.userID); } + if (widget.onSelectedMemberChange != null) { - widget.onSelectedMemberChange!(selectedMember); + widget.onSelectedMemberChange!(selectedMemberList); } setState(() {}); }, - isChecked: selectedMember.contains(memberInfo)), + isChecked: selectedMemberList.where((element) => element.userID == memberInfo.userID).toList().isNotEmpty + ), ), Container( width: isDesktopScreen ? 30 : 36, @@ -194,17 +197,17 @@ class _GroupProfileMemberListState extends TIMUIKitState widget.onTapMemberItem!(memberInfo, null); } if (widget.canSelectMember) { - final isChecked = selectedMember.contains(memberInfo); + final isChecked = selectedMemberList.contains(memberInfo); if (isChecked) { - selectedMember.remove(memberInfo); + selectedMemberList.remove(memberInfo); } else { - if (widget.maxSelectNum != null && selectedMember.length >= widget.maxSelectNum!) { + if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) { return; } - selectedMember.add(memberInfo); + selectedMemberList.add(memberInfo); } if (widget.onSelectedMemberChange != null) { - widget.onSelectedMemberChange!(selectedMember); + widget.onSelectedMemberChange!(selectedMemberList); } setState(() {}); } diff --git a/lib/ui/widgets/image_screen.dart b/lib/ui/widgets/image_screen.dart index abfe6a9..50d987e 100644 --- a/lib/ui/widgets/image_screen.dart +++ b/lib/ui/widgets/image_screen.dart @@ -42,13 +42,13 @@ class _ImageScreenState extends TIMUIKitState double initialScale = 1.0; bool isLoading = false; - GlobalKey slidePagekey = + GlobalKey slidePageKey = GlobalKey(); GlobalKey extendedImageGestureKey = GlobalKey(); void close() { - slidePagekey.currentState!.popPage(); + slidePageKey.currentState!.popPage(); Navigator.pop(context); } @@ -78,6 +78,10 @@ class _ImageScreenState extends TIMUIKitState @override Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { return OrientationBuilder(builder: ((context, orientation) { + if (extendedImageGestureKey.currentState != null) { + extendedImageGestureKey.currentState!.reset(); + } + return Material( color: Colors.transparent, child: Container( @@ -94,7 +98,7 @@ class _ImageScreenState extends TIMUIKitState bottom: 0, right: 0, child: ExtendedImageSlidePage( - key: slidePagekey, + key: slidePageKey, slideAxis: SlideAxis.both, slidePageBackgroundHandler: (Offset offset, Size size) { if (orientation == Orientation.landscape) { @@ -123,7 +127,7 @@ class _ImageScreenState extends TIMUIKitState onTap: close, child: HeroWidget( tag: widget.heroTag, - slidePagekey: slidePagekey, + slidePagekey: slidePageKey, child: ExtendedImage( image: widget.imageProvider, extendedImageGestureKey: @@ -179,6 +183,7 @@ class _ImageScreenState extends TIMUIKitState 1 / fitWidthScale; // fittedHeight doubleTapScales[1] = 1 / fitWidthScale; } + return GesturedImage(state, key: extendedImageGestureKey); case LoadState.failed: @@ -188,7 +193,7 @@ class _ImageScreenState extends TIMUIKitState }, onDoubleTap: (ExtendedImageGestureState state) { ///you can use define pointerDownPosition as you can, - ///default value is double tap pointer down postion. + ///default value is double tap pointer down position. final Offset? pointerDownPosition = state.pointerDownPosition; final double? begin = diff --git a/lib/ui/widgets/link_preview/widgets/link_text.dart b/lib/ui/widgets/link_preview/widgets/link_text.dart index fdb4bbf..a0bd62c 100644 --- a/lib/ui/widgets/link_preview/widgets/link_text.dart +++ b/lib/ui/widgets/link_preview/widgets/link_text.dart @@ -5,6 +5,7 @@ import 'package:extended_text/extended_text.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/compiler/md_text.dart'; import 'package:tencent_im_base/base_widgets/tim_stateless_widget.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart'; @@ -125,7 +126,7 @@ class LinkText extends TIMStatelessWidget { } if (LinkUtils.urlReg.hasMatch(c)) { - contentData += '\$' + c + '\$'; + contentData += HttpText.flag + c + HttpText.flag; _contentList.add(TextSpan( text: c, style: TextStyle(color: LinkUtils.hexToColor("015fff")), @@ -160,12 +161,12 @@ class LinkText extends TIMStatelessWidget { Widget timBuild(BuildContext context) { return ExtendedText(_getContentSpan(messageText, context), softWrap: true, onSpecialTextTap: (dynamic parameter) { - if (parameter.toString().startsWith('\$')) { + if (parameter.toString().startsWith(HttpText.flag)) { if (onLinkTap != null) { - onLinkTap!((parameter.toString()).replaceAll('\$', '')); + onLinkTap!((parameter.toString()).replaceAll(HttpText.flag, '')); } else { LinkUtils.launchURL( - context, (parameter.toString()).replaceAll('\$', '')); + context, (parameter.toString()).replaceAll(HttpText.flag, '')); } } }, diff --git a/lib/ui/widgets/video_screen.dart b/lib/ui/widgets/video_screen.dart index fa3e602..ff8f856 100644 --- a/lib/ui/widgets/video_screen.dart +++ b/lib/ui/widgets/video_screen.dart @@ -243,15 +243,14 @@ class _VideoScreenState extends TIMUIKitState { )); await player.initialize(); WidgetsBinding.instance.addPostFrameCallback((_) { - double w = getVideoWidth(); - double h = getVideoHeight(); + double aspectRatio = player.value.aspectRatio; ChewieController controller = ChewieController( videoPlayerController: player, autoPlay: true, looping: false, showControlsOnInitialize: false, allowPlaybackSpeedChanging: false, - aspectRatio: w == 0 || h == 0 ? null : w / h, + aspectRatio: aspectRatio, customControls: VideoCustomControls(downloadFn: () async { return await _saveVideo(); })); diff --git a/pubspec.yaml b/pubspec.yaml index 67c8d1c..7d9963b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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.0.0 +version: 3.1.0 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 @@ -21,17 +21,17 @@ dependencies: sdk: flutter adaptive_action_sheet: ^2.0.1 provider: ^6.0.1 - intl: any + intl: ^0.19.0 get_it: ^7.2.0 dotted_border: ^2.0.0+2 flutter_svg: ^2.0.6 image_picker: ^0.8.5+3 file_picker: ^5.3.0 tencent_super_tooltip: ^0.0.1 - video_player: ^2.7.0 - chewie: ^1.7.5 + video_player: ^2.9.0 + chewie: ^1.8.5 flutter_slidable: ^3.0.1 - flutter_plugin_record_plus: ^0.0.16 + flutter_plugin_record_plus: ^0.0.17 azlistview_all_platforms: ^2.1.2 lpinyin: ^2.0.3 transparent_image: ^2.0.0 @@ -40,13 +40,13 @@ dependencies: cached_network_image: ^3.3.0 shared_preferences: ^2.0.13 scroll_to_index: ^2.1.1 - wechat_assets_picker: ^8.9.0-dev.1 - wechat_camera_picker: ^4.2.0-dev.2 + wechat_assets_picker: ^9.3.3 + wechat_camera_picker: ^4.3.4 flutter_easyrefresh: ^2.2.1 - extended_image: '>=8.2.0 <=8.2.4' + extended_image: ^9.0.0 extended_text_field: ^16.0.0 extended_text: ^14.0.0 - package_info_plus: ^4.0.1 + package_info_plus: ^8.0.0 loading_animation_widget: ^1.1.0+3 permission_handler: ^10.2.0 tuple: ^2.0.0 @@ -63,19 +63,20 @@ dependencies: tencent_keyboard_visibility: ^1.0.1 tim_ui_kit_sticker_plugin: ^3.2.0 tencent_im_base: ^8.0.0 - fc_native_video_thumbnail: ^0.11.1 + fc_native_video_thumbnail: ^0.16.0 path: ^1.8.1 tencent_cloud_uikit_core: ^1.6.0 pasteboard: ^0.2.0 desktop_drop: ^0.4.4 - device_info_plus: any + device_info_plus: ^10.1.2 cross_file: ^0.3.3+4 - csslib: 0.17.2 + csslib: ^0.17.2 diff_match_patch: ^0.4.1 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: flutter_lints: ^1.0.0