diff --git a/CHANGELOG.md b/CHANGELOG.md index 70b9dfa..ed58a06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 2.2.1 + +### New Features + +* Introduced a new `groupMemberList` configuration in `TUIKitChat`; when specified, TUIKit will not load it automatically, optimizing network traffic usage. +* Added support for image copying on desktop platforms. + +### Bug Fixes + +* Fixed an issue preventing the removal of image loading status. +* Resolved a problem that prevented images from being saved to the device gallery. +* Addressed a potential issue causing the `mentionOtherMemberInGroup` function in `TIMUIKitChatController` to fail. +* Corrected an issue that could lead to improper image rendering. + ## 2.2.0 ### New Features diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc index 681dc9c..c0a73ba 100644 --- a/example/linux/flutter/generated_plugin_registrant.cc +++ b/example/linux/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) image_clipboard_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ImageClipboardPlugin"); + image_clipboard_plugin_register_with_registrar(image_clipboard_registrar); g_autoptr(FlPluginRegistrar) pasteboard_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); pasteboard_plugin_register_with_registrar(pasteboard_registrar); diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake index 2c433ef..6b86133 100644 --- a/example/linux/flutter/generated_plugins.cmake +++ b/example/linux/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux desktop_drop file_selector_linux + image_clipboard pasteboard url_launcher_linux ) diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 7913e42..908651a 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import desktop_drop import device_info_plus import fc_native_video_thumbnail import file_selector_macos +import image_clipboard import package_info_plus import pasteboard import path_provider_foundation @@ -24,6 +25,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FcNativeVideoThumbnailPlugin.register(with: registry.registrar(forPlugin: "FcNativeVideoThumbnailPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + ImageClipboardPlugin.register(with: registry.registrar(forPlugin: "ImageClipboardPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/example/pubspec.lock b/example/pubspec.lock index d4c67dd..94cb203 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -608,6 +608,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image_clipboard: + dependency: transitive + description: + name: image_clipboard + sha256: "1939365dc3b65acd3b1199176a330180075f4e803a6512f5622d050b62e7eff4" + url: "https://pub.dev" + source: hosted + version: "1.0.0+2" image_gallery_saver: dependency: transitive description: @@ -1219,7 +1227,7 @@ packages: path: ".." relative: true source: path - version: "2.2.0" + version: "2.2.1-preview.0" tencent_cloud_uikit_core: dependency: transitive description: diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc index f81340b..73a5047 100644 --- a/example/windows/flutter/generated_plugin_registrant.cc +++ b/example/windows/flutter/generated_plugin_registrant.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FcNativeVideoThumbnailPluginCApi")); FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + ImageClipboardPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ImageClipboardPluginCApi")); PasteboardPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PasteboardPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake index c350c7b..ee66cc6 100644 --- a/example/windows/flutter/generated_plugins.cmake +++ b/example/windows/flutter/generated_plugins.cmake @@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST desktop_drop fc_native_video_thumbnail file_selector_windows + image_clipboard pasteboard permission_handler_windows url_launcher_windows diff --git a/lib/business_logic/life_cycle/chat_life_cycle.dart b/lib/business_logic/life_cycle/chat_life_cycle.dart index 1a2d1d0..15ed3e4 100644 --- a/lib/business_logic/life_cycle/chat_life_cycle.dart +++ b/lib/business_logic/life_cycle/chat_life_cycle.dart @@ -31,9 +31,8 @@ class ChatLifeCycle { /// You can make a second confirmation here by a modal, etc. FutureBool Function(String conversationID) shouldClearHistoricalMessageList; - /// Before rendering a message to message list. - bool Function(V2TimMessage msgID) messageShouldMount; + bool Function(V2TimMessage msg) messageShouldMount; ChatLifeCycle({ this.shouldClearHistoricalMessageList = 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 d90e0ba..bb0f10f 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 @@ -184,7 +184,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier { _groupType = null; isGroupExist = true; _groupInfo = null; - groupMemberList?.clear(); + groupMemberList = null; selfMemberInfo = null; if (conversationType == ConvType.group) { 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 1351748..da3ddaf 100644 --- a/lib/business_logic/view_models/tui_chat_global_model.dart +++ b/lib/business_logic/view_models/tui_chat_global_model.dart @@ -16,6 +16,7 @@ import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart'; +import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; enum ConvType { none, c2c, group } @@ -49,7 +50,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { ChatLifeCycle? _lifeCycle; bool _isDownloading = false; final List> _waitingDownloadList = - List.empty(growable: true); // example {"savePath":"","url":"",msgId:""} + List.empty(growable: true); // example {"savePath":"","url":"",msgId:""} int _totalUnreadCount = 0; String localKeyPrefix = "TUIKit_conversation_stored_"; String localMsgIDListKey = "TUIKit_conversation_list"; @@ -63,7 +64,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { List? _groupApplicationList; String Function(V2TimMessage message)? _abstractMessageBuilder; final Map _c2cMessageEditStatusMap = - Map.from({}); // 0 normal 1 sending + Map.from({}); // 0 normal 1 sending final Map _c2cMessageFromUserActiveMap = Map.from({}); final Map _c2cMessageActiveTimer = Map.from({}); bool _showC2cMessageEditStatus = true; @@ -196,17 +197,15 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { Map get messageReadReceiptMap => _messageReadReceiptMap; - String get currentSelectedConv => - _currentConversationList.isNotEmpty - ? _currentConversationList[_currentConversationList.length - 1] + String get currentSelectedConv => _currentConversationList.isNotEmpty + ? _currentConversationList[_currentConversationList.length - 1] .conversationID - : ""; + : ""; - ConvType? get currentSelectedConvType => - _currentConversationList.isNotEmpty - ? _currentConversationList[_currentConversationList.length - 1] + ConvType? get currentSelectedConvType => _currentConversationList.isNotEmpty + ? _currentConversationList[_currentConversationList.length - 1] .conversationType - : null; + : null; setCurrentConversation(CurrentConversation value) { _currentConversationList.add(value); @@ -242,13 +241,13 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } _c2cMessageStatusShowTimer[userID] = Timer.periodic(const Duration(seconds: 5), (timer) { - _c2cMessageEditStatusMap[userID] = 0; - Timer? t = _c2cMessageStatusShowTimer[userID]; - if (t != null && t.isActive) { - // 取消当前的定时器 - t.cancel(); - } - }); + _c2cMessageEditStatusMap[userID] = 0; + Timer? t = _c2cMessageStatusShowTimer[userID]; + if (t != null && t.isActive) { + // 取消当前的定时器 + t.cancel(); + } + }); } notifyListeners(); } @@ -333,9 +332,10 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { prefs.remove(localMsgIDListKey); } - Future updateMessageFromController({required String msgID, - required String conversationID, - required ConvType conversationType}) async { + Future updateMessageFromController( + {required String msgID, + required String conversationID, + required ConvType conversationType}) async { final TUIChatModelTools tools = serviceLocator(); V2TimMessage? newMessage = await tools.getExistingMessageByID( msgID: msgID, @@ -364,7 +364,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { _preLoadImage(List msgList) { List needPreViewList = - msgList.sublist(0, max(0, min(5, msgList.length - 1))); + msgList.sublist(0, max(0, min(5, msgList.length - 1))); for (var msgItem in needPreViewList) { V2TimImage? getImageFromList(V2TimImageTypesEnum imgType) { V2TimImage? img = MessageUtils.getImageFromImgList( @@ -382,12 +382,12 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { image.resolve(configuration).addListener( ImageStreamListener((ImageInfo image, bool synchronousCall) { - final tempImg = image.image; - _preloadImageMap[msgItem.seq! + - msgItem.timestamp.toString() + - (msgItem.msgID ?? "")] = tempImg; - outputLogger.i("cacheImage ${msgItem.msgID}"); - })); + final tempImg = image.image; + _preloadImageMap[msgItem.seq! + + msgItem.timestamp.toString() + + (msgItem.msgID ?? "")] = tempImg; + outputLogger.i("cacheImage ${msgItem.msgID}"); + })); } catch (e) { outputLogger.i("cacheImage error ${msgItem.msgID}"); } @@ -488,13 +488,13 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } _c2cMessageActiveTimer[msg.sender ?? ""] = Timer.periodic(const Duration(seconds: 30), (timer) { - _c2cMessageFromUserActiveMap[msg.sender ?? ""] = false; - Timer? t = _c2cMessageActiveTimer[msg.sender ?? ""]; - if (t != null && t.isActive) { - // 取消当前的定时器 - t.cancel(); - } - }); + _c2cMessageFromUserActiveMap[msg.sender ?? ""] = false; + Timer? t = _c2cMessageActiveTimer[msg.sender ?? ""]; + if (t != null && t.isActive) { + // 取消当前的定时器 + t.cancel(); + } + }); } } } catch (err) { @@ -512,14 +512,14 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } V2TimMsgCreateInfoResult? res = await _messageService.createCustomMessage( data: json.encode({ - "businessID": "user_typing_status", - "typingStatus": isEditing == true ? 1 : 0, - "userAction": 14, - "version": 0, - "actionParam": isEditing == true - ? "EIMAMSG_InputStatus_Ing" - : "EIMAMSG_InputStatus_End" - })); + "businessID": "user_typing_status", + "typingStatus": isEditing == true ? 1 : 0, + "userAction": 14, + "version": 0, + "actionParam": isEditing == true + ? "EIMAMSG_InputStatus_Ing" + : "EIMAMSG_InputStatus_End" + })); if (res != null) { _sendMessage( id: res.id!, @@ -534,9 +534,9 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { void refreshGroupApplicationList() async { final res = await _groupServices.getGroupApplicationList(); _groupApplicationList = res.data?.groupApplicationList?.map((item) { - final V2TimGroupApplication applicationItem = item!; - return applicationItem; - }).toList() ?? + final V2TimGroupApplication applicationItem = item!; + return applicationItem; + }).toList() ?? []; notifyListeners(); } @@ -632,17 +632,14 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { final activeMessageList = _messageListMap[convID ?? currentSelectedConv]; if (activeMessageList != null) { final findeIndex = - activeMessageList.indexWhere((element) => element.msgID == msgID); + activeMessageList.indexWhere((element) => element.msgID == msgID); if (findeIndex != -1) { final findeIndex = - activeMessageList.indexWhere((element) => element.msgID == msgID); + activeMessageList.indexWhere((element) => element.msgID == msgID); if (findeIndex != -1) { final targetItem = activeMessageList[findeIndex]; targetItem.status = MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED; - targetItem.id = DateTime - .now() - .millisecondsSinceEpoch - .toString(); + targetItem.id = DateTime.now().millisecondsSinceEpoch.toString(); activeMessageList[findeIndex] = targetItem; } } @@ -652,10 +649,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } onMessageModified(V2TimMessage modifiedMessage, [String? convID]) async { - modifiedMessage.id = DateTime - .now() - .millisecondsSinceEpoch - .toString(); + modifiedMessage.id = DateTime.now().millisecondsSinceEpoch.toString(); final String? exactId = TencentUtils.checkString(modifiedMessage.userID) ?? TencentUtils.checkString(modifiedMessage.groupID); final activeMessageList = _messageListMap[convID ?? exactId]; @@ -716,8 +710,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { final currentProgress = getMessageProgress(messageProgress.msgID); if (messageProgress.isFinish && currentProgress < 100) { - V2TimMessage? message = await _findAndRetrieveMessage( - messageProgress.msgID); + V2TimMessage? message = + await _findAndRetrieveMessage(messageProgress.msgID); _handleFinishedDownload(messageProgress, message); return; } @@ -726,23 +720,25 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } Future _findAndRetrieveMessage(String messageId) async { - final messages = await _messageService.findMessages( - messageIDList: [messageId]); + final messages = + await _messageService.findMessages(messageIDList: [messageId]); return messages?.first; } - void _handleFinishedDownload(V2TimMessageDownloadProgress messageProgress, - V2TimMessage? message) { + void _handleFinishedDownload( + V2TimMessageDownloadProgress messageProgress, V2TimMessage? message) { if (message != null) { - bool isImageType = message.elemType == - MessageElemType.V2TIM_ELEM_TYPE_IMAGE; - bool isVideoType = message.elemType == - MessageElemType.V2TIM_ELEM_TYPE_VIDEO; + bool isImageType = + message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE; + bool isVideoType = + message.elemType == MessageElemType.V2TIM_ELEM_TYPE_VIDEO; + final originalImageType = PlatformUtils().isIOS ? 1 : 0; if (!isImageType && !isVideoType) { _updateMessageLocationAndDownloadFile(messageProgress); - } else if ((isImageType && messageProgress.type == 0) || isVideoType) { - Future.delayed(const Duration(seconds: 1), () => - _updateMessageAndDownloadFile(message, messageProgress)); + } else if ((isImageType && messageProgress.type == originalImageType) || + isVideoType) { + Future.delayed(const Duration(seconds: 1), + () => _updateMessageAndDownloadFile(message, messageProgress)); } else { return; } @@ -751,8 +747,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } } - void _updateMessageAndDownloadFile(V2TimMessage message, - V2TimMessageDownloadProgress messageProgress) { + void _updateMessageAndDownloadFile( + V2TimMessage message, V2TimMessageDownloadProgress messageProgress) { updateAsyncMessage( message, TencentUtils.checkString(message.userID) ?? @@ -769,18 +765,19 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { downloadFile(); } - void _updateProgressIfNeeded(V2TimMessageDownloadProgress messageProgress, - int currentProgress) { - try{ + void _updateProgressIfNeeded( + V2TimMessageDownloadProgress messageProgress, int currentProgress) { + try { if (messageProgress.totalSize != -1 && !messageProgress.isFinish) { - int progress = min(99, + int progress = min( + 99, (messageProgress.currentSize / messageProgress.totalSize * 100) .floor()); if (progress > 1 && progress > currentProgress) { setMessageProgress(messageProgress.msgID, progress); } } - }catch(e){ + } catch (e) { outputLogger.i("calculate error: ${messageProgress.toJson()}"); } } @@ -868,13 +865,11 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { final TUIChatModelTools tools = serviceLocator(); List currentHistoryMsgList = _messageListMap[convID] ?? []; V2TimMsgCreateInfoResult? textMessageInfo = - await _messageService.createTextMessage(text: text); + await _messageService.createTextMessage(text: text); textMessageInfo = await _messageService.createTextAtMessage( text: text + - "\n@${TencentUtils.checkString(messageBeenReplied.nickName) ?? - TencentUtils.checkString(messageBeenReplied.sender) ?? - TencentUtils.checkString(messageBeenReplied.userID)}", + "\n@${TencentUtils.checkString(messageBeenReplied.nickName) ?? TencentUtils.checkString(messageBeenReplied.sender) ?? TencentUtils.checkString(messageBeenReplied.userID)}", atUserList: [ TencentUtils.checkString(messageBeenReplied.sender) ?? TencentUtils.checkString(messageBeenReplied.userID) ?? @@ -886,7 +881,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { if (messageInfo != null) { final messageInfoWithSender = messageInfo.sender == null ? tools.setUserInfoForMessage( - messageInfo, messageInfo.id ?? textMessageInfo.id ?? "") + messageInfo, messageInfo.id ?? textMessageInfo.id ?? "") : messageInfo; final hasNickName = messageBeenReplied.nickName != null && @@ -927,8 +922,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { return null; } - Future setLocalCustomData(String msgID, String localCustomData, - String conversationID) async { + Future setLocalCustomData( + String msgID, String localCustomData, String conversationID) async { final res = await _messageService.setLocalCustomData( msgID: msgID, localCustomData: localCustomData); List messageList = _messageListMap[conversationID] ?? []; @@ -947,8 +942,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { return false; } - Future setLocalCustomInt(String msgID, int localCustomInt, - String conversationID) async { + Future setLocalCustomInt( + String msgID, int localCustomInt, String conversationID) async { final res = await _messageService.setLocalCustomInt( msgID: msgID, localCustomInt: localCustomInt); List messageList = _messageListMap[conversationID] ?? []; @@ -985,7 +980,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { String receiver = convType == ConvType.c2c ? convID : ''; String groupID = convType == ConvType.group ? convID : ''; final oldGroupType = - groupType != null ? GroupReceptAllowType.values[groupType.index] : null; + groupType != null ? GroupReceptAllowType.values[groupType.index] : null; final sendMsgRes = await _messageService.sendMessage( id: id, receiver: receiver, @@ -993,8 +988,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { chatConfig.isShowGroupReadingStatus && convType == ConvType.group && ((chatConfig.groupReadReceiptPermissionList != null && - chatConfig.groupReadReceiptPermissionList! - .contains(groupType)) || + chatConfig.groupReadReceiptPermissionList! + .contains(groupType)) || (chatConfig.groupReadReceiptPermisionList != null && chatConfig.groupReadReceiptPermisionList! .contains(oldGroupType))), @@ -1014,7 +1009,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { if (isEditStatusMessage == false) { updateMessage(sendMsgRes, convID, id, convType, groupType, setInputField); } - if(_lifeCycle?.messageDidSend != null){ + if (_lifeCycle?.messageDidSend != null) { _lifeCycle!.messageDidSend(sendMsgRes); } @@ -1030,7 +1025,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { notifyListeners(); } - updateMessage(V2TimValueCallback sendMsgRes, + updateMessage( + V2TimValueCallback sendMsgRes, String convID, String id, ConvType convType, @@ -1045,10 +1041,10 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } final findIdIndex = - currentHistoryMsgList.indexWhere((element) => element.id == id); + currentHistoryMsgList.indexWhere((element) => element.id == id); final targetIndex = findIdIndex == -1 ? currentHistoryMsgList - .indexWhere((element) => element.msgID == sendMsgResData.msgID) + .indexWhere((element) => element.msgID == sendMsgResData.msgID) : findIdIndex; if (targetIndex != -1) { currentHistoryMsgList[targetIndex] = sendMsgResData; @@ -1059,13 +1055,13 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { loadingMessage[convID]!.removeWhere((element) => element.id == id); } final oldGroupType = - groupType != null ? GroupReceptAllowType.values[groupType.index] : null; + 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.groupReadReceiptPermissionList! + .contains(groupType)) || (chatConfig.groupReadReceiptPermisionList != null && chatConfig.groupReadReceiptPermisionList! .contains(oldGroupType)))) { @@ -1076,12 +1072,11 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { notifyListeners(); } - void updateAsyncMessage(V2TimMessage message, - String convID,) { - message.id = DateTime - .now() - .millisecondsSinceEpoch - .toString(); + void updateAsyncMessage( + V2TimMessage message, + String convID, + ) { + message.id = DateTime.now().millisecondsSinceEpoch.toString(); final activeMessageList = _messageListMap[convID]; if (activeMessageList == null || activeMessageList.isEmpty) { @@ -1108,11 +1103,11 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { { if (listWithTimestamp.isEmpty || (listWithTimestamp[listWithTimestamp.length - 1].timestamp != - null && + null && item.timestamp != null && (item.timestamp! - - listWithTimestamp[listWithTimestamp.length - 1] - .timestamp! > + listWithTimestamp[listWithTimestamp.length - 1] + .timestamp! > interval))) { listWithTimestamp.add(V2TimMessage( userID: '', @@ -1126,15 +1121,13 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } } final returnValue = listWithTimestamp.reversed.toList(); - outputLogger.i(returnValue.map((e) => e.toJson()) - .toList() - .toString()); + outputLogger.i(returnValue.map((e) => e.toJson()).toList().toString()); return returnValue; } HistoryMessagePosition getMessageListPosition(String? conversationID) { final HistoryMessagePosition? position = - _historyMessagePositionMap[conversationID]; + _historyMessagePositionMap[conversationID]; if (position == null) { _historyMessagePositionMap[conversationID ?? currentSelectedConv] = HistoryMessagePosition.bottom; @@ -1144,8 +1137,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass { } } - void setMessageListPosition(String conversationID, - HistoryMessagePosition position) { + void setMessageListPosition( + String conversationID, HistoryMessagePosition position) { _historyMessagePositionMap[conversationID] = position; notifyListeners(); } diff --git a/lib/ui/utils/logger.dart b/lib/ui/utils/logger.dart index 21e31ea..6c3ce30 100644 --- a/lib/ui/utils/logger.dart +++ b/lib/ui/utils/logger.dart @@ -102,8 +102,6 @@ class TUIKitOutput extends LogOutput { } }); } - } else { - print(msg); } } } diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart index dda6a3e..3752502 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:image_clipboard/image_clipboard.dart'; import 'package:provider/provider.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; @@ -83,7 +84,7 @@ class TIMUIKitMessageTooltipState final TUISelfInfoViewModel selfInfoViewModel = serviceLocator(); bool isShowMoreSticker = false; - bool isShowOpenFile = false; + bool fileBeenDownloaded = false; String filePath = ""; @override @@ -98,11 +99,11 @@ class TIMUIKitMessageTooltipState (widget.message.fileElem == null && widget.message.imageElem == null && widget.message.videoElem == null)) { - isShowOpenFile = false; + fileBeenDownloaded = false; return; } if (PlatformUtils().isWeb) { - isShowOpenFile = true; + fileBeenDownloaded = true; return; } if (PlatformUtils().isDesktop) { @@ -115,7 +116,7 @@ class TIMUIKitMessageTooltipState File f = File(savePath); if (f.existsSync() && widget.message.msgID != null) { filePath = savePath; - isShowOpenFile = true; + fileBeenDownloaded = true; return; } } else if (widget.message.imageElem != null) { @@ -124,19 +125,19 @@ class TIMUIKitMessageTooltipState null && File(widget.message.imageElem!.imageList![0]!.localUrl!) .existsSync()) { - isShowOpenFile = true; + fileBeenDownloaded = true; return; } } else if (widget.message.videoElem != null) { if (TencentUtils.checkString(widget.message.videoElem!.localVideoUrl) != null && File(widget.message.videoElem!.localVideoUrl!).existsSync()) { - isShowOpenFile = true; + fileBeenDownloaded = true; return; } } } - isShowOpenFile = false; + fileBeenDownloaded = false; } bool isRevocable(int timestamp, int upperTimeLimit) => @@ -183,7 +184,8 @@ class TIMUIKitMessageTooltipState bool isAdminCanRecall() { if (widget.model.chatConfig.isGroupAdminRecallEnabled) { - final selfMemberInfo = widget.groupMemberInfo ?? widget.model.selfMemberInfo; + final selfMemberInfo = + widget.groupMemberInfo ?? widget.model.selfMemberInfo; final selfRole = selfMemberInfo?.role; return selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN || selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER; @@ -205,24 +207,31 @@ class TIMUIKitMessageTooltipState final shouldShowForwardAction = !(widget.message.customElem?.data != null && MessageUtils.isCallingData(widget.message.customElem!.data!)); final tooltipsConfig = widget.toolTipsConfig; + final messageCanCopy = widget.message.elemType == + MessageElemType.V2TIM_ELEM_TYPE_TEXT || + (isDesktopScreen && + widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE && + fileBeenDownloaded); + final List defaultTipsList = [ - if (isShowOpenFile) + if (fileBeenDownloaded) MessageToolTipItem( label: TIM_t("打开"), id: "open", iconImageAsset: "images/open_in_new.png", onClick: () => _onTap("open", model)), - if (isShowOpenFile && PlatformUtils().isDesktop) + if (fileBeenDownloaded && PlatformUtils().isDesktop) MessageToolTipItem( label: PlatformUtils().isMacOS ? TIM_t("在访达中打开") : TIM_t("查看文件夹"), id: "finder", iconImageAsset: "images/folder_open.png", onClick: () => _onTap("finder", model)), - MessageToolTipItem( - label: TIM_t("复制"), - id: "copyMessage", - iconImageAsset: "images/copy_message.png", - onClick: () => _onTap("copyMessage", model)), + if (messageCanCopy) + MessageToolTipItem( + label: TIM_t("复制"), + id: "copyMessage", + iconImageAsset: "images/copy_message.png", + onClick: () => _onTap("copyMessage", model)), if (shouldShowForwardAction && !isVoteMessage(widget.message)) MessageToolTipItem( label: TIM_t("转发"), @@ -263,8 +272,7 @@ class TIMUIKitMessageTooltipState defaultFormattedTipsList = defaultTipsList.where((element) { final type = element.id; if (type == "copyMessage") { - return tooltipsConfig.showCopyMessage && - widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT; + return tooltipsConfig.showCopyMessage; } if (type == "forwardMessage") { return tooltipsConfig.showForwardMessage && @@ -402,6 +410,12 @@ class TIMUIKitMessageTooltipState } catch (e) {} } + Future copyImageToClipboard(String imagePath) async { + ImageClipboard().copyImage(imagePath); + // final DesktopClipboard desktopClipboard = DesktopClipboard(); + // desktopClipboard.copyImage(imagePath); + } + _onTap(String operation, TUIChatSeparateViewModel model) async { final messageItem = widget.message; final msgID = messageItem.msgID as String; @@ -485,6 +499,13 @@ class TIMUIKitMessageTooltipState infoCode: 6660408)); // ignore: empty_catches } catch (e) {} + } else if (widget.message.elemType == + MessageElemType.V2TIM_ELEM_TYPE_IMAGE) { + final savePath = (TencentUtils.checkString( + widget.message.imageElem!.imageList?[0]?.localUrl) ?? + TencentUtils.checkString(widget.message.imageElem?.path) ?? + ""); + copyImageToClipboard(savePath); } break; case "replyMessage": diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart index 3b46644..cfefdd5 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_image_elem.dart @@ -114,7 +114,7 @@ class _TIMUIKitImageElem extends TIMUIKitState { Future _saveImageToLocal( context, String imageUrl, { - bool isAsset = true, + bool isLocalResource = true, TUITheme? theme, }) async { if (PlatformUtils().isWeb) { @@ -163,8 +163,7 @@ class _TIMUIKitImageElem extends TIMUIKitState { } } - // 本地资源的情况下 - if (!isAsset) { + if (!isLocalResource) { if (widget.message.msgID == null || widget.message.msgID!.isEmpty) { return; } @@ -216,9 +215,6 @@ class _TIMUIKitImageElem extends TIMUIKitState { } return; } - // model.setMessageProgress(widget.message.msgID!, 0); - // var result = - // await ImageGallerySaver.saveImage(Uint8List.fromList(response)); var result = await ImageGallerySaver.saveFile(imageUrl); @@ -251,44 +247,44 @@ class _TIMUIKitImageElem extends TIMUIKitState { } Future _saveImg(TUITheme theme) async { - String? path = widget.message.imageElem!.path; - if (path != null && PlatformUtils().isWeb - ? true - : File(path!).existsSync()) { - return await _saveImageToLocal(context, path, - isAsset: true, theme: theme); - } else { - String imgUrl = getOriginImgURL(); - if (widget.message.imageElem!.imageList![0]!.localUrl != '' && - widget.message.imageElem!.imageList![0]!.localUrl != null) { - File f = File(widget.message.imageElem!.imageList![0]!.localUrl!); - if (f.existsSync()) { - return await _saveImageToLocal( - context, - widget.message.imageElem!.imageList![0]!.localUrl!, - isAsset: true, - theme: theme, - ); + try { + String? imageUrl; + bool isAssetBool = false; + final imageElem = widget.message.imageElem; + + if (imageElem != null) { + final originUrl = getOriginImgURL(); + final localUrl = imageElem.imageList?.firstOrNull?.localUrl; + final filePath = imageElem.path; + final isWeb = PlatformUtils().isWeb; + + if (!isWeb && filePath != null && File(filePath).existsSync()) { + imageUrl = filePath; + isAssetBool = true; + } else if (localUrl != null && + (!isWeb && File(localUrl).existsSync())) { + imageUrl = localUrl; + isAssetBool = true; + } else { + imageUrl = originUrl; + isAssetBool = false; } } - if (widget.message.imageElem!.path != '' && - widget.message.imageElem!.path != null) { - File f = File(widget.message.imageElem!.path!); - if (f.existsSync()) { - return await _saveImageToLocal( - context, - widget.message.imageElem!.path!, - isAsset: true, - theme: theme, - ); - } + + if (imageUrl != null) { + return await _saveImageToLocal( + context, + imageUrl, + isLocalResource: isAssetBool, + theme: theme, + ); } - return await _saveImageToLocal( - context, - imgUrl, - isAsset: false, - theme: theme, - ); + } catch (e) { + onTIMCallback(TIMCallback( + infoCode: 6660414, + infoRecommendText: TIM_t("正在下载中"), + type: TIMCallbackType.INFO)); + return; } } @@ -398,7 +394,7 @@ class _TIMUIKitImageElem extends TIMUIKitState { void onClickImage({ required bool isNetworkImage, dynamic heroTag, - TUITheme? theme, + required TUITheme theme, String? imgUrl, String? imgPath, }) { @@ -429,7 +425,7 @@ class _TIMUIKitImageElem extends TIMUIKitState { heroTag: heroTag, messageID: widget.message.msgID, downloadFn: () async { - return await _saveImg(theme!); + return await _saveImg(theme); })), ); } @@ -448,7 +444,7 @@ class _TIMUIKitImageElem extends TIMUIKitState { heroTag: heroTag, messageID: widget.message.msgID, downloadFn: () async { - return await _saveImg(theme!); + return await _saveImg(theme); }), ), ); @@ -458,7 +454,7 @@ class _TIMUIKitImageElem extends TIMUIKitState { Widget _renderAllImage( {dynamic heroTag, - TUITheme? theme, + required TUITheme theme, bool isNetworkImage = false, String? webPath, V2TimImage? originalImg, @@ -485,7 +481,6 @@ class _TIMUIKitImageElem extends TIMUIKitState { )); } else { final imgPath = (TencentUtils.checkString(smallLocalPath) != null - ? smallLocalPath : originLocalPath)!; return Hero( @@ -504,24 +499,34 @@ class _TIMUIKitImageElem extends TIMUIKitState { return GestureDetector( onTap: () => onClickImage( + theme: theme, heroTag: heroTag, isNetworkImage: isNetworkImage, imgUrl: webPath ?? smallImg?.url ?? originalImg?.url ?? "", imgPath: (TencentUtils.checkString(originLocalPath) != null - ? originLocalPath - : smallLocalPath) ?? "" - ), + ? originLocalPath + : smallLocalPath) ?? + ""), child: getImageWidget(), ); } - @override - void initState() { - super.initState(); + void initImages() async { if (!PlatformUtils().isWeb && TencentUtils.checkString(widget.message.msgID) != null) { - if (TencentUtils.checkString( - widget.message.imageElem!.imageList![0]!.localUrl) == + if (widget.message.imageElem?.imageList == null || + widget.message.imageElem!.imageList!.isEmpty) { + final response = await _messageService.getMessageOnlineUrl( + msgID: widget.message.msgID!); + final elem = response.data; + if (elem != null && elem.imageElem != null) { + widget.message.imageElem = elem.imageElem; + } + } + if (widget.message.imageElem?.imageList == null || + widget.message.imageElem!.imageList!.isEmpty || + TencentUtils.checkString( + widget.message.imageElem?.imageList?[0]?.localUrl) == null || !File(widget.message.imageElem!.imageList![0]!.localUrl!) .existsSync()) { @@ -531,9 +536,11 @@ class _TIMUIKitImageElem extends TIMUIKitState { imageType: 0, isSnapshot: false); } - if (TencentUtils.checkString( - widget.message.imageElem!.imageList![1]!.localUrl) == - null || + if (widget.message.imageElem?.imageList == null || + widget.message.imageElem!.imageList!.length < 2 && + TencentUtils.checkString( + widget.message.imageElem?.imageList?[1]?.localUrl) == + null || !File(widget.message.imageElem!.imageList![1]!.localUrl!) .existsSync()) { _messageService.downloadMessage( @@ -542,8 +549,10 @@ class _TIMUIKitImageElem extends TIMUIKitState { imageType: 1, isSnapshot: false); } - if (TencentUtils.checkString( - widget.message.imageElem!.imageList![2]!.localUrl) == + if (widget.message.imageElem?.imageList != null || + widget.message.imageElem!.imageList!.length < 3 || + TencentUtils.checkString( + widget.message.imageElem?.imageList?[2]?.localUrl) == null || !File(widget.message.imageElem!.imageList![2]!.localUrl!) .existsSync()) { @@ -556,6 +565,12 @@ class _TIMUIKitImageElem extends TIMUIKitState { } } + @override + void initState() { + super.initState(); + initImages(); + } + bool isNeedShowLocalPath() { final current = (DateTime.now().millisecondsSinceEpoch / 1000).ceil(); final timeStamp = widget.message.timestamp ?? current; @@ -563,7 +578,7 @@ class _TIMUIKitImageElem extends TIMUIKitState { (isSent || current - timeStamp < 300); } - Widget? _renderImage(dynamic heroTag, TUITheme? theme, + Widget? _renderImage(dynamic heroTag, TUITheme theme, {V2TimImage? originalImg, V2TimImage? smallImg}) { double? positionRadio; if (smallImg?.width != null && 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 192492c..eeb037c 100644 --- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart +++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart @@ -8,6 +8,7 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; +import 'package:collection/collection.dart'; import 'package:scroll_to_index/scroll_to_index.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'; @@ -213,11 +214,11 @@ class _InputTextFieldState extends TIMUIKitState { currentCursor = value; } - RegExp EmojiRegex() => RegExp( + RegExp emojiRegex() => RegExp( r'[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26F9(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC3\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC08\uDC26](?:\u200D\u2B1B)?|[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC2\uDECE-\uDEDB\uDEE0-\uDEE8]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)'); bool isEmoji(String input) { - return EmojiRegex().hasMatch(input); + return emojiRegex().hasMatch(input); } void deleteStickerFromText() { @@ -454,7 +455,7 @@ class _InputTextFieldState extends TIMUIKitState { focusNode.requestFocus(); } else { final memberInfo = widget.model.groupMemberList - ?.firstWhere((element) => element?.userID == userID) ?? + ?.firstWhereOrNull((element) => element?.userID == userID) ?? V2TimGroupMemberFullInfo( userID: userID ?? "", nickName: nickName, diff --git a/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart b/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart index c5eb4b2..4a74fac 100644 --- a/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart +++ b/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart @@ -296,6 +296,9 @@ class _TUIChatState extends TIMUIKitState { if (oldWidget.textFieldBuilder != null && widget.textFieldBuilder == null) { textFieldController = TIMUIKitInputTextFieldController(); } + if (oldWidget.groupMemberList != widget.groupMemberList) { + model.groupMemberList = widget.groupMemberList; + } } updateDraft() async { diff --git a/lib/ui/widgets/center_loading.dart b/lib/ui/widgets/center_loading.dart deleted file mode 100644 index d206fea..0000000 --- a/lib/ui/widgets/center_loading.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart'; -import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; -import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; - -import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; -import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; - -class CenterLoading extends TIMUIKitStatelessWidget { - CenterLoading({Key? key, this.messageID}) : super(key: key); - final String? messageID; - @override - Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { - final TUITheme theme = value.theme; - - return MultiProvider( - providers: [ - ChangeNotifierProvider.value( - value: serviceLocator()), - ], - builder: (context, w) { - final progress = Provider.of(context) - .getMessageProgress(messageID); - return (progress == 0 || progress == 100) - ? Container() - : Center( - child: CircularProgressIndicator( - value: progress / 100, - backgroundColor: Colors.white, - valueColor: AlwaysStoppedAnimation(theme.primaryColor))); - }); - } -} diff --git a/lib/ui/widgets/image_screen.dart b/lib/ui/widgets/image_screen.dart index 9ad377a..c3de026 100644 --- a/lib/ui/widgets/image_screen.dart +++ b/lib/ui/widgets/image_screen.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; -import 'package:tencent_cloud_chat_uikit/ui/widgets/center_loading.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/gestured_image.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/image_hero.dart'; @@ -152,7 +151,8 @@ class _ImageScreenState extends TIMUIKitState color: Colors.black, child: const Center( child: CircularProgressIndicator( - color: Colors.white))); + color: Colors.white)) + ); case LoadState.completed: final screenHeight = MediaQuery.of(context).size.height; @@ -266,7 +266,6 @@ class _ImageScreenState extends TIMUIKitState }, ), ), - CenterLoading(messageID: widget.messageID), if (isLoading) Container( child: LoadingAnimationWidget.staggeredDotsWave( diff --git a/lib/ui/widgets/link_preview/renderer/md_image.dart b/lib/ui/widgets/link_preview/renderer/md_image.dart deleted file mode 100644 index f3b74f3..0000000 --- a/lib/ui/widgets/link_preview/renderer/md_image.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; - -/// Type for a function that creates image widgets. -typedef ImageBuilder = Widget Function( - Uri uri, String? imageDirectory, double? width, double? height); - -Widget _handleDataSchemeUri( - Uri uri, final double? width, final double? height) { - final String mimeType = uri.data!.mimeType; - if (mimeType.startsWith('image/')) { - return Image.memory( - uri.data!.contentAsBytes(), - width: width, - height: height, - ); - } else if (mimeType.startsWith('text/')) { - return Text(uri.data!.contentAsString()); - } - return const SizedBox(); -} - -class MDImageRenderer extends StatelessWidget{ - final String src; - final String? title; - final String? alt; - MDImageRenderer({super.key, required this.src, this.title, this.alt}); - - /// A default image builder handling http/https, resource, and file URLs. -// ignore: prefer_function_declarations_over_variables - final ImageBuilder kDefaultImageBuilder = ( - Uri uri, - String? imageDirectory, - double? width, - double? height, - ) { - if (uri.scheme == 'http' || uri.scheme == 'https') { - return Image.network(uri.toString(), width: width, height: height); - } else if (uri.scheme == 'data') { - return _handleDataSchemeUri(uri, width, height); - } else if (uri.scheme == 'resource') { - return Image.asset(uri.path, width: width, height: height); - } else { - final Uri fileUri = imageDirectory != null - ? Uri.parse(imageDirectory + uri.toString()) - : uri; - if (fileUri.scheme == 'http' || fileUri.scheme == 'https') { - return Image.network(fileUri.toString(), width: width, height: height); - } else { - return Image.file(File.fromUri(fileUri), width: width, height: height); - } - } - }; - - Widget _buildImage(String src, String? title, String? alt) { - final List parts = src.split('#'); - if (parts.isEmpty) { - return const SizedBox(); - } - - final String path = parts.first; - double? width; - double? height; - if (parts.length == 2) { - final List dimensions = parts.last.split('x'); - if (dimensions.length == 2) { - width = double.parse(dimensions[0]); - height = double.parse(dimensions[1]); - } - } - - final Uri uri = Uri.parse(path); - Widget child; - if (false) { - // child = imageBuilder!(uri, title, alt); - } else { - child = kDefaultImageBuilder(uri, "", width, height); - } - - return GestureDetector(onTap:(){ - - }, child: child); - } - - @override - Widget build(BuildContext context) { - throw _buildImage(src, title, alt); - } - -} \ No newline at end of file diff --git a/lib/ui/widgets/link_preview/widgets/link_text.dart b/lib/ui/widgets/link_preview/widgets/link_text.dart index 64094e1..092405a 100644 --- a/lib/ui/widgets/link_preview/widgets/link_text.dart +++ b/lib/ui/widgets/link_preview/widgets/link_text.dart @@ -5,9 +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/emoji_text.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/compiler/md_text.dart'; -import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/renderer/md_image.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'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart'; diff --git a/pubspec.lock b/pubspec.lock index 6a64ae4..18315f1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -683,6 +683,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image_clipboard: + dependency: "direct main" + description: + name: image_clipboard + sha256: "1939365dc3b65acd3b1199176a330180075f4e803a6512f5622d050b62e7eff4" + url: "https://pub.dev" + source: hosted + version: "1.0.0+2" image_gallery_saver: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6b2a259..b83cd19 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ 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: 2.2.0 -homepage: https://www.tencentcloud.com/products/im?from=pub +version: 2.2.1 +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/tc-chat-uikit-flutter documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html @@ -76,6 +76,7 @@ dependencies: diff_match_patch: ^0.4.1 markdown: ^7.1.0 logger: ^2.0.1 + image_clipboard: ^1.0.0+2 dev_dependencies: flutter_lints: ^1.0.0