feat: Upgrade UIKit to 2.2.0

This commit is contained in:
anonymous 2023-08-18 16:40:21 +08:00
parent 2fefab6196
commit d0b60c68a8
40 changed files with 1393 additions and 691 deletions

View File

@ -1,3 +1,27 @@
## 2.2.0
### New Features
* Introduced a newly-designed set of Emoji image stickers, available for seamless integration within textual content, providing an enhanced user experience.
* Streamlined the implementation of stickers, removing the need for additional complex coding. Full functionality is enabled by default, with customization options available through the `stickerPanelConfig` configuration in `TIMUIKitChatConfig`.
* Extended support for rendering embedded image stickers within text messages when the `Markdown` parsing mode is activated, combining a rich, user-friendly experience with the ability to display formatted Markdown text.
### Improvements
* Enhanced group chat functionality on the Desktop, enabling mentions (`@` tag) to be inserted at any position within a composed message, rather than only at the end. Additionally, deleting `@` tags has been optimized.
* Maintained message sending permissions for the group owner and administrators during "mute all" scenarios.
* Enabled the use of a return `null` value for the `customHoverBar` to utilize the default.
* Refined the revoke button functionality for group administrators.
* Removed full-screen support for video previews on the Web and introduced an alternative "Open in New Window" button for an enlarged view.
* Implemented UIKit log recording to facilitate issue identification and troubleshooting.
* Introduced a delete button for the small PNG sticker selection panel on mobile devices, which previously was only available in the Unicode emoji selection panel.
### Bug Fixes
* Resolved an issue preventing photo capturing on devices running Android 12 or lower.
* Rectified display inaccuracies related to picture aspect ratios.
* Addressed several issues concerning voice and video calls.
## 2.1.3+1 ## 2.1.3+1
### New Features ### New Features

View File

@ -728,6 +728,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0+4" version: "1.2.0+4"
logger:
dependency: transitive
description:
name: logger
sha256: "66cb048220ca51cf9011da69fa581e4ee2bed4be6e82870d9e9baae75739da49"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -1193,41 +1201,41 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: tencent_chat_i18n_tool name: tencent_chat_i18n_tool
sha256: ac8171d2574ed18babedd0cb67e937e255bf02fcb72f55408d033f74d4b11949 sha256: "0ee982e814bedd0aea4751b972901c6cfcfb224cfeb8e13ae02e43c0b8a58bbc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.3+2" version: "2.2.0"
tencent_cloud_chat_sdk: tencent_cloud_chat_sdk:
dependency: transitive dependency: transitive
description: description:
name: tencent_cloud_chat_sdk name: tencent_cloud_chat_sdk
sha256: f98bdb55164051e2b196cac6e2e79e60248ed8351dc5a91d25568712ccb15839 sha256: "013f8c9d96bbeed06d5fe971b7802d8ddf830c776332d6c6de6ccb9de8956d83"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.7" version: "5.1.8"
tencent_cloud_chat_uikit: tencent_cloud_chat_uikit:
dependency: "direct main" dependency: "direct main"
description: description:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "2.1.3-preview.16" version: "2.2.0"
tencent_cloud_uikit_core: tencent_cloud_uikit_core:
dependency: transitive dependency: transitive
description: description:
name: tencent_cloud_uikit_core name: tencent_cloud_uikit_core
sha256: "0a0f43e4c4241b25d12a9e9f0ee91922ac800a42229d97e3d21d16041ace3104" sha256: acb3bae877428457318b8c5604a6c263957b6df3454ed3e30e8b6f620c6b2cd9
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.1.0"
tencent_im_base: tencent_im_base:
dependency: transitive dependency: transitive
description: description:
name: tencent_im_base name: tencent_im_base
sha256: "0db83050452486571ee4ac07280a2fb4bbc07d297a54235b5cf12e46e79267d0" sha256: bc5eb080090038d21c879480c06d3ed7cb4b1dcc2cbe894189613eadf08cf7c5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.1"
tencent_im_sdk_plugin_desktop: tencent_im_sdk_plugin_desktop:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1240,10 +1248,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: tencent_im_sdk_plugin_platform_interface name: tencent_im_sdk_plugin_platform_interface
sha256: "6a1f053567246148ad40667f2ab71d82bcee0d5d0c12e587340d2796c342b87e" sha256: "1f9814d654dc1ad0a4cb62936f0849defac058c3bdca471472efc8b64b63cc5e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.21" version: "0.3.22"
tencent_im_sdk_plugin_web: tencent_im_sdk_plugin_web:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1296,10 +1304,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: tim_ui_kit_sticker_plugin name: tim_ui_kit_sticker_plugin
sha256: c9b0c1261bb51a5dee54bb50c6a106061b1bb10731b9c815fc5175afa60175e2 sha256: db8143aea26eda5feec5ec2efc5a31b2c56928cd807537778829698f3c4efec5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "3.0.1+1"
transparent_image: transparent_image:
dependency: transitive dependency: transitive
description: description:

View File

@ -20,6 +20,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.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/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart'; import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@ -171,7 +172,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
void initForEachConversation(ConvType convType, String convID, void initForEachConversation(ConvType convType, String convID,
ValueChanged<String>? onChangeInputField, ValueChanged<String>? onChangeInputField,
{String? groupID}) async { {String? groupID,
List<V2TimGroupMemberFullInfo?>? preGroupMemberList}) async {
if (_isInit) { if (_isInit) {
return; return;
} }
@ -188,7 +190,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
if (conversationType == ConvType.group) { if (conversationType == ConvType.group) {
globalModel.refreshGroupApplicationList(); globalModel.refreshGroupApplicationList();
loadGroupInfo(groupID ?? convID); loadGroupInfo(groupID ?? convID);
loadGroupMemberList(groupID: groupID ?? convID); if(preGroupMemberList != null){
groupMemberList = preGroupMemberList;
selfMemberInfo = preGroupMemberList
.firstWhereOrNull((e) => e?.userID == selfModel.loginInfo?.userID);
}else{
await loadSelfMemberInfo(groupID: groupID ?? convID);
loadGroupMemberList(groupID: groupID ?? convID);
}
if(selfMemberInfo == null){
await loadSelfMemberInfo(groupID: groupID ?? convID);
}
} else { } else {
notifyListeners(); notifyListeners();
} }
@ -274,12 +286,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
// //
Future<bool> loadChatRecord({ Future<bool> loadChatRecord({
HistoryMsgGetTypeEnum? getType, // HistoryMsgGetTypeEnum? getType,
int lastMsgSeq = -1, // int lastMsgSeq = -1,
required int count, // required int count,
String? lastMsgID, // ID String? lastMsgID,
LoadDirection direction = LoadDirection direction = LoadDirection.previous,
LoadDirection.previous, // previous表示向上加载latest表示向下加载
}) async { }) async {
try { try {
// //
@ -349,20 +360,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
List<V2TimMessage> receivedList = await lifeCycle List<V2TimMessage> receivedList = await lifeCycle
?.didGetHistoricalMessageList(response.messageList) ?? ?.didGetHistoricalMessageList(response.messageList) ??
response.messageList; response.messageList;
globalModel.loadingMessage.remove(conversationID);
//
if (globalModel.loadingMessage[conversationID]?.isNotEmpty ?? false) {
if (direction == LoadDirection.previous) {
receivedList = _combineMessageList(
globalModel.messageListMap[conversationID]!, receivedList);
} else {
receivedList = receivedList.reversed.toList();
receivedList = _combineMessageList(
receivedList, globalModel.messageListMap[conversationID]!);
}
} else {
globalModel.loadingMessage.remove(conversationID);
}
// model // model
globalModel.setMessageList( globalModel.setMessageList(
@ -394,7 +392,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return haveMoreData; return haveMoreData;
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print('loadChatRecord error: $e'); outputLogger.i('loadChatRecord error: $e');
return false; return false;
} }
} }
@ -485,6 +483,23 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
} }
} }
Future<void> loadSelfMemberInfo({required String groupID}) async {
V2TimValueCallback<List<V2TimGroupMemberFullInfo>> getGroupMembersInfoRes =
await TencentImSDKPlugin.v2TIMManager
.getGroupManager()
.getGroupMembersInfo(
groupID: groupID,
memberList: [selfModel.loginInfo?.userID ?? ""],
);
if (getGroupMembersInfoRes.code == 0) {
final userList = getGroupMembersInfoRes.data;
selfMemberInfo = userList
?.firstWhereOrNull((e) => e.userID == selfModel.loginInfo?.userID);
notifyListeners();
}
return;
}
Future<void> loadGroupMemberList( Future<void> loadGroupMemberList(
{required String groupID, int count = 100, String? seq}) async { {required String groupID, int count = 100, String? seq}) async {
final String? nextSeq = await _loadGroupMemberListFunction( final String? nextSeq = await _loadGroupMemberListFunction(
@ -629,7 +644,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
globalModel.updateMessage( globalModel.updateMessage(
sendMsgRes, convID, id, convType, groupType, setInputField); sendMsgRes, convID, id, convType, groupType, setInputField);
} }
if(lifeCycle?.messageDidSend != null){ if (lifeCycle?.messageDidSend != null) {
lifeCycle!.messageDidSend(sendMsgRes); lifeCycle!.messageDidSend(sendMsgRes);
} }
@ -883,7 +898,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
globalModel.updateMessage(sendMsgRes, convID, globalModel.updateMessage(sendMsgRes, convID,
messageInfoWithSender.id ?? "", convType, groupType, setInputField); messageInfoWithSender.id ?? "", convType, groupType, setInputField);
if(lifeCycle?.messageDidSend != null){ if (lifeCycle?.messageDidSend != null) {
lifeCycle!.messageDidSend(sendMsgRes); lifeCycle!.messageDidSend(sendMsgRes);
} }
return sendMsgRes; return sendMsgRes;

View File

@ -1,6 +1,7 @@
// ignore_for_file: unnecessary_getters_setters, avoid_print // ignore_for_file: unnecessary_getters_setters, avoid_print
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
import 'package:tencent_im_base/tencent_im_base.dart'; import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/group_profile_life_cycle.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/group_profile_life_cycle.dart';
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart';
@ -110,7 +111,7 @@ class TUIGroupProfileModel extends ChangeNotifier {
if (res.code == 0 && groupMemberListRes != null) { if (res.code == 0 && groupMemberListRes != null) {
final groupMemberListTemp = groupMemberListRes.memberInfoList ?? []; final groupMemberListTemp = groupMemberListRes.memberInfoList ?? [];
// TODO // TODO
print( outputLogger.i(
"loadGroupMemberListfinish,groupMemberListTemp, ${groupMemberListRes.nextSeq}, ${groupMemberListTemp.length}"); "loadGroupMemberListfinish,groupMemberListTemp, ${groupMemberListRes.nextSeq}, ${groupMemberListTemp.length}");
_groupMemberList = [...?_groupMemberList, ...groupMemberListTemp]; _groupMemberList = [...?_groupMemberList, ...groupMemberListTemp];
_groupMemberListSeq = groupMemberListRes.nextSeq ?? "0"; _groupMemberListSeq = groupMemberListRes.nextSeq ?? "0";

View File

@ -15,6 +15,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.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/message.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
enum ConvType { none, c2c, group } enum ConvType { none, c2c, group }
@ -107,7 +108,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
} }
void addWaitingList(String msgID) { void addWaitingList(String msgID) {
print("add to waiting list success"); outputLogger.i("add to waiting list success");
bool contains = false; bool contains = false;
for (Map<String, String> element in _waitingDownloadList) { for (Map<String, String> element in _waitingDownloadList) {
String msgIDItem = element["msgID"] ?? ""; String msgIDItem = element["msgID"] ?? "";
@ -145,7 +146,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
isSnapshot: false, isSnapshot: false,
); );
print("start another download"); outputLogger.i("start another download");
} }
int getReceived(msgID) { int getReceived(msgID) {
@ -345,37 +346,6 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
} }
} }
Future<bool> refreshCurrentHistoryListForConversation(
{HistoryMsgGetTypeEnum getType =
HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG,
int lastMsgSeq = -1,
required int count,
String? lastMsgID,
required String convID,
required ConvType convType}) async {
final currentHistoryMsgList = messageListMap[convID];
final response = await _messageService.getHistoryMessageList(
count: count,
getType: getType,
userID: convType == ConvType.c2c ? convID : null,
groupID: convType == ConvType.group ? convID : null,
lastMsgID: lastMsgID,
lastMsgSeq: lastMsgSeq);
if (lastMsgID != null && currentHistoryMsgList != null) {
final newList = [...currentHistoryMsgList, ...response];
final List<V2TimMessage> msgList =
await _lifeCycle?.didGetHistoricalMessageList(newList) ?? newList;
setMessageList(convID, msgList);
} else {
final List<V2TimMessage> messageList =
await _lifeCycle?.didGetHistoricalMessageList(response) ?? response;
setMessageList(convID, messageList);
// The messages in first screen
}
notifyListeners();
return true;
}
clearData() { clearData() {
_messageListMap.clear(); _messageListMap.clear();
_currentConversationList.clear(); _currentConversationList.clear();
@ -416,10 +386,10 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
_preloadImageMap[msgItem.seq! + _preloadImageMap[msgItem.seq! +
msgItem.timestamp.toString() + msgItem.timestamp.toString() +
(msgItem.msgID ?? "")] = tempImg; (msgItem.msgID ?? "")] = tempImg;
print("cacheImage ${msgItem.msgID}"); outputLogger.i("cacheImage ${msgItem.msgID}");
})); }));
} catch (e) { } catch (e) {
print("cacheImage error ${msgItem.msgID}"); outputLogger.i("cacheImage error ${msgItem.msgID}");
} }
} }
} }
@ -443,7 +413,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
String msgIDItem = element["msgID"] ?? ""; String msgIDItem = element["msgID"] ?? "";
if (msgIDItem.isNotEmpty) { if (msgIDItem.isNotEmpty) {
if (msgID == msgIDItem) { if (msgID == msgIDItem) {
print("remove download"); outputLogger.i("remove download");
return true; return true;
} }
} }
@ -737,12 +707,12 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
} }
_onSendMessageProgress(V2TimMessage messagae, int progress) { _onSendMessageProgress(V2TimMessage messagae, int progress) {
print("message progress: $progress"); outputLogger.i("message progress: $progress");
} }
Future<void> onMessageDownloadProgressCallback( Future<void> onMessageDownloadProgressCallback(
V2TimMessageDownloadProgress messageProgress) async { V2TimMessageDownloadProgress messageProgress) async {
print(messageProgress.toJson()); outputLogger.i(messageProgress.toJson());
final currentProgress = getMessageProgress(messageProgress.msgID); final currentProgress = getMessageProgress(messageProgress.msgID);
if (messageProgress.isFinish && currentProgress < 100) { if (messageProgress.isFinish && currentProgress < 100) {
@ -811,7 +781,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
} }
} }
}catch(e){ }catch(e){
print("calculate error: ${messageProgress.toJson()}"); outputLogger.i("calculate error: ${messageProgress.toJson()}");
} }
} }
@ -1156,6 +1126,9 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
} }
} }
final returnValue = listWithTimestamp.reversed.toList(); final returnValue = listWithTimestamp.reversed.toList();
outputLogger.i(returnValue.map((e) => e.toJson())
.toList()
.toString());
return returnValue; return returnValue;
} }

View File

@ -4,6 +4,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_im_base/tencent_im_base.dart'; import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart';
@ -16,7 +17,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_config.dar
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/data_services/core/web_support/uikit_web_support.dart' import 'package:tencent_cloud_chat_uikit/data_services/core/web_support/uikit_web_support.dart'
if (dart.library.html) 'package:tencent_cloud_chat_uikit/data_services/core/web_support/uikit_web_support_implement.dart'; if (dart.library.html) 'package:tencent_cloud_chat_uikit/data_services/core/web_support/uikit_web_support_implement.dart';
typedef EmptyAvatarBuilder = Widget Function(BuildContext context); typedef EmptyAvatarBuilder = Widget Function(BuildContext context);
@ -63,7 +64,7 @@ class CoreServicesImpl implements CoreServices {
setGlobalConfig(TIMUIKitConfig? config) { setGlobalConfig(TIMUIKitConfig? config) {
final TUISelfInfoViewModel selfInfoViewModel = final TUISelfInfoViewModel selfInfoViewModel =
serviceLocator<TUISelfInfoViewModel>(); serviceLocator<TUISelfInfoViewModel>();
final TUISettingModel settingModel = serviceLocator<TUISettingModel>(); final TUISettingModel settingModel = serviceLocator<TUISettingModel>();
selfInfoViewModel.globalConfig = config; selfInfoViewModel.globalConfig = config;
settingModel.init(); settingModel.init();
@ -75,23 +76,28 @@ class CoreServicesImpl implements CoreServices {
} }
@override @override
Future<bool?> init({ Future<bool?> init(
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error. {
ValueChanged<TIMCallback>? onTUIKitCallbackListener, /// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
required int sdkAppID, ValueChanged<TIMCallback>? onTUIKitCallbackListener,
required LogLevelEnum loglevel, required int sdkAppID,
required V2TimSDKListener listener, required LogLevelEnum loglevel,
LanguageEnum? language, required V2TimSDKListener listener,
String? extraLanguage, LanguageEnum? language,
TIMUIKitConfig? config, String? extraLanguage,
TIMUIKitConfig? config,
/// Specify the current device platform, mobile or desktop, based on your needs. /// Specify the current device platform, mobile or desktop, based on your needs.
/// TUIKit will automatically determine the platform if no specification is provided. DeviceType? platform, /// TUIKit will automatically determine the platform if no specification is provided. DeviceType? platform,
DeviceType? platform, DeviceType? platform,
VoidCallback? onWebLoginSuccess}) async { String? uikitLogPath,
VoidCallback? onWebLoginSuccess}) async {
if (platform != null) { if (platform != null) {
TUIKitScreenUtils.deviceType = platform; TUIKitScreenUtils.deviceType = platform;
} }
if (TencentUtils.checkString(uikitLogPath) != null) {
logOutputGenerator(uikitLogPath!);
}
addIdentifier(); addIdentifier();
if (extraLanguage != null) { if (extraLanguage != null) {
Future.delayed(const Duration(milliseconds: 1), () { Future.delayed(const Duration(milliseconds: 1), () {
@ -167,13 +173,13 @@ class CoreServicesImpl implements CoreServices {
void addInitListener() { void addInitListener() {
final TUIFriendShipViewModel tuiFriendShipViewModel = final TUIFriendShipViewModel tuiFriendShipViewModel =
serviceLocator<TUIFriendShipViewModel>(); serviceLocator<TUIFriendShipViewModel>();
final TUIConversationViewModel tuiConversationViewModel = final TUIConversationViewModel tuiConversationViewModel =
serviceLocator<TUIConversationViewModel>(); serviceLocator<TUIConversationViewModel>();
final TUIChatGlobalModel tuiChatViewModel = final TUIChatGlobalModel tuiChatViewModel =
serviceLocator<TUIChatGlobalModel>(); serviceLocator<TUIChatGlobalModel>();
final TUIGroupListenerModel tuiGroupListenerModel = final TUIGroupListenerModel tuiGroupListenerModel =
serviceLocator<TUIGroupListenerModel>(); serviceLocator<TUIGroupListenerModel>();
tuiFriendShipViewModel.addFriendListener(); tuiFriendShipViewModel.addFriendListener();
tuiConversationViewModel.setConversationListener(); tuiConversationViewModel.setConversationListener();
@ -183,13 +189,13 @@ class CoreServicesImpl implements CoreServices {
void removeListener() { void removeListener() {
final TUIFriendShipViewModel tuiFriendShipViewModel = final TUIFriendShipViewModel tuiFriendShipViewModel =
serviceLocator<TUIFriendShipViewModel>(); serviceLocator<TUIFriendShipViewModel>();
final TUIConversationViewModel tuiConversationViewModel = final TUIConversationViewModel tuiConversationViewModel =
serviceLocator<TUIConversationViewModel>(); serviceLocator<TUIConversationViewModel>();
final TUIChatGlobalModel tuiChatViewModel = final TUIChatGlobalModel tuiChatViewModel =
serviceLocator<TUIChatGlobalModel>(); serviceLocator<TUIChatGlobalModel>();
final TUIGroupListenerModel tuiGroupListenerModel = final TUIGroupListenerModel tuiGroupListenerModel =
serviceLocator<TUIGroupListenerModel>(); serviceLocator<TUIGroupListenerModel>();
tuiFriendShipViewModel.removeFriendshipListener(); tuiFriendShipViewModel.removeFriendshipListener();
tuiConversationViewModel.removeConversationListener(); tuiConversationViewModel.removeConversationListener();
@ -203,17 +209,16 @@ class CoreServicesImpl implements CoreServices {
onCallback!(callbackValue); onCallback!(callbackValue);
}); });
} else { } else {
print( outputLogger.i(
"TUIKit Callback: ${callbackValue.type} - ${callbackValue "TUIKit Callback: ${callbackValue.type} - ${callbackValue.stackTrace}");
.stackTrace}");
} }
} }
initDataModel() { initDataModel() {
final TUIFriendShipViewModel tuiFriendShipViewModel = final TUIFriendShipViewModel tuiFriendShipViewModel =
serviceLocator<TUIFriendShipViewModel>(); serviceLocator<TUIFriendShipViewModel>();
final TUIConversationViewModel tuiConversationViewModel = final TUIConversationViewModel tuiConversationViewModel =
serviceLocator<TUIConversationViewModel>(); serviceLocator<TUIConversationViewModel>();
tuiFriendShipViewModel.initFriendShipModel(); tuiFriendShipViewModel.initFriendShipModel();
tuiConversationViewModel.initConversation(); tuiConversationViewModel.initConversation();
@ -221,11 +226,11 @@ class CoreServicesImpl implements CoreServices {
clearData() { clearData() {
final TUIFriendShipViewModel tuiFriendShipViewModel = final TUIFriendShipViewModel tuiFriendShipViewModel =
serviceLocator<TUIFriendShipViewModel>(); serviceLocator<TUIFriendShipViewModel>();
final TUIConversationViewModel tuiConversationViewModel = final TUIConversationViewModel tuiConversationViewModel =
serviceLocator<TUIConversationViewModel>(); serviceLocator<TUIConversationViewModel>();
final TUIChatGlobalModel tuiChatViewModel = final TUIChatGlobalModel tuiChatViewModel =
serviceLocator<TUIChatGlobalModel>(); serviceLocator<TUIChatGlobalModel>();
tuiFriendShipViewModel.clearData(); tuiFriendShipViewModel.clearData();
tuiConversationViewModel.clearData(); tuiConversationViewModel.clearData();
@ -235,18 +240,18 @@ class CoreServicesImpl implements CoreServices {
updateUserStatusList(List<V2TimUserStatus> newUserStatusList) { updateUserStatusList(List<V2TimUserStatus> newUserStatusList) {
try { try {
final TUISelfInfoViewModel selfInfoViewModel = final TUISelfInfoViewModel selfInfoViewModel =
serviceLocator<TUISelfInfoViewModel>(); serviceLocator<TUISelfInfoViewModel>();
if (selfInfoViewModel.globalConfig?.isShowOnlineStatus == false) { if (selfInfoViewModel.globalConfig?.isShowOnlineStatus == false) {
return; return;
} }
final TUIFriendShipViewModel tuiFriendShipViewModel = final TUIFriendShipViewModel tuiFriendShipViewModel =
serviceLocator<TUIFriendShipViewModel>(); serviceLocator<TUIFriendShipViewModel>();
final currentUserStatusList = tuiFriendShipViewModel.userStatusList; final currentUserStatusList = tuiFriendShipViewModel.userStatusList;
for (int i = 0; i < newUserStatusList.length; i++) { for (int i = 0; i < newUserStatusList.length; i++) {
final int indexInCurrentUserList = currentUserStatusList.indexWhere( final int indexInCurrentUserList = currentUserStatusList.indexWhere(
(element) => element.userID == newUserStatusList[i].userID); (element) => element.userID == newUserStatusList[i].userID);
if (indexInCurrentUserList == -1) { if (indexInCurrentUserList == -1) {
currentUserStatusList.add(newUserStatusList[i]); currentUserStatusList.add(newUserStatusList[i]);
} else { } else {
@ -290,7 +295,7 @@ class CoreServicesImpl implements CoreServices {
if (TencentUtils.checkString(_userID) == null) { if (TencentUtils.checkString(_userID) == null) {
V2TimValueCallback<String> getLoginUserRes = V2TimValueCallback<String> getLoginUserRes =
await TencentImSDKPlugin.v2TIMManager.getLoginUser(); await TencentImSDKPlugin.v2TIMManager.getLoginUser();
if (getLoginUserRes.code == 0) { if (getLoginUserRes.code == 0) {
_userID = getLoginUserRes.data ?? ""; _userID = getLoginUserRes.data ?? "";
} }
@ -319,7 +324,7 @@ class CoreServicesImpl implements CoreServices {
_loginInfo = _loginInfo =
res?.data!.firstWhereOrNull((element) => element.userID == _userID); res?.data!.firstWhereOrNull((element) => element.userID == _userID);
final TUISelfInfoViewModel selfInfoViewModel = final TUISelfInfoViewModel selfInfoViewModel =
serviceLocator<TUISelfInfoViewModel>(); serviceLocator<TUISelfInfoViewModel>();
if (_loginInfo != null) { if (_loginInfo != null) {
selfInfoViewModel.setLoginInfo(_loginInfo); selfInfoViewModel.setLoginInfo(_loginInfo);
} }
@ -374,10 +379,10 @@ class CoreServicesImpl implements CoreServices {
return TencentImSDKPlugin.v2TIMManager return TencentImSDKPlugin.v2TIMManager
.getOfflinePushManager() .getOfflinePushManager()
.setOfflinePushConfig( .setOfflinePushConfig(
businessID: businessID?.toDouble() ?? 0, businessID: businessID?.toDouble() ?? 0,
token: token, token: token,
isTPNSToken: isTPNSToken, isTPNSToken: isTPNSToken,
); );
} }
@override @override

View File

@ -51,17 +51,21 @@ class TIMUIKitChatController {
return model?.clearHistory(); return model?.clearHistory();
} }
/// refresh the history message list manually; /// Refresh the history message list manually;
/// Please provide `convType` and `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
Future<bool> refreshCurrentHistoryList( Future<bool> refreshCurrentHistoryList(
[String? convID, ConvType? convType]) async { [String? convID, ConvType? convType]) async {
if (convID != null && convType != null) { if(model != null){
return globalChatModel.refreshCurrentHistoryListForConversation( try{
count: 50, convID: convID, convType: convType); scrollController?.animateTo(
} else if (model != null) { scrollController!.position.minScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.ease,
);
}catch(e){}
return model!.loadDataFromController(); return model!.loadDataFromController();
} else {
return false;
} }
return false;
} }
/// Update single message at UI model /// Update single message at UI model

View File

@ -1,6 +1,7 @@
// ignore_for_file: avoid_print, prefer_typing_uninitialized_variables // ignore_for_file: avoid_print, prefer_typing_uninitialized_variables
import 'dart:ui'; import 'dart:ui';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
class Frame { class Frame {
static var orginalCallback; static var orginalCallback;
@ -27,7 +28,7 @@ class Frame {
if (orginalCallback != null) { if (orginalCallback != null) {
orginalCallback(timings); orginalCallback(timings);
} }
print("fps: $fps"); outputLogger.i("fps: $fps");
} }
static double get fps { static double get fps {

109
lib/ui/utils/logger.dart Normal file
View File

@ -0,0 +1,109 @@
import 'dart:convert';
import 'dart:io';
import 'package:logger/logger.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart' as p;
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
final outputLogger = Logger(
output: _outputLogger ?? logOutputGenerator(null),
);
TUIKitOutput? _outputLogger;
TUIKitOutput logOutputGenerator(String? path) {
_outputLogger = TUIKitOutput(path);
return _outputLogger!;
}
class TUIKitOutput extends LogOutput {
Future<void> createDirectoryIfNotExists(String path) async {
final directory = Directory(p.dirname(path));
if (await directory.exists() == false) {
await directory.create(recursive: true);
}
}
Future<void> deleteFilesOlderThanDays(String directoryPath, int days) async {
final directory = Directory(directoryPath);
final threshold = DateTime.now().subtract(Duration(days: days));
await for (var fileEntity in directory.list(followLinks: false)) {
if (fileEntity is File) {
final lastModified = await fileEntity.lastModified();
if (lastModified.isBefore(threshold)) {
await fileEntity.delete();
}
}
}
}
Future<String> getPlatformLogPath({String? path}) async {
if (TencentUtils.checkString(path) != null) {
outputLogger.i("The path to local log: $path");
return path!;
}
final String documentsDirectoryPath =
"${Platform.environment['USERPROFILE']}";
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String pkgName = packageInfo.packageName;
var timeName =
"${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}";
final logPath = p.join(documentsDirectoryPath, "Documents", ".TencentCloudChat",
pkgName, "uikit_log", 'Flutter-TUIKit-$timeName.log');
outputLogger.i("The path to local log: $logPath");
return logPath;
}
File? logFile;
TUIKitOutput(String? path) {
if (!PlatformUtils().isWeb) {
getPlatformLogPath(path: path).then((logFilePath) async {
await createDirectoryIfNotExists(logFilePath);
deleteFilesOlderThanDays(p.dirname(logFilePath), 7);
logFile = File(logFilePath);
if (logFile != null) {
if (!logFile!.existsSync()) {
logFile!.createSync(recursive: true);
}
}
});
}
}
@override
void output(OutputEvent event) {
var msg = "\n";
for (var line in event.lines) {
msg += "$line \n";
}
if (!PlatformUtils().isWeb) {
if (logFile != null) {
final sink = logFile!.openWrite(
mode: FileMode.append,
encoding: const SystemEncoding(),
);
sink.write(utf8.decode(utf8.encode(msg)));
sink.close();
} else {
Future.delayed(const Duration(seconds: 1)).then((value) {
if (logFile != null) {
final sink = logFile!.openWrite(
mode: FileMode.append,
encoding: const SystemEncoding(),
);
sink.write(msg);
sink.close();
}
});
}
} else {
print(msg);
}
}
}

View File

@ -7,6 +7,7 @@ import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.d
import 'package:tencent_cloud_chat_uikit/ui/constants/time.dart'; import 'package:tencent_cloud_chat_uikit/ui/constants/time.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
class MessageUtils { class MessageUtils {
// CallingData的方式和Trtc的方法一致 // CallingData的方式和Trtc的方法一致
@ -18,7 +19,7 @@ class MessageUtils {
return true; return true;
} }
} catch (e) { } catch (e) {
print("isCallingData json parse error"); outputLogger.i("isCallingData json parse error");
return false; return false;
} }
return false; return false;
@ -289,7 +290,7 @@ class MessageUtils {
orElse: () => null); orElse: () => null);
} }
} catch (e) { } catch (e) {
print('getImageFromImgList error ${e.toString()}'); outputLogger.i('getImageFromImgList error ${e.toString()}');
} }
return img; return img;
} }

View File

@ -46,8 +46,14 @@ class TimeAgo {
date; date;
} }
String getTimeStringForChat(int timeStamp) { String? getTimeStringForChat(int timeStamp) {
final DateTime date = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000); final DateTime date = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
final DateTime epochLimit = DateTime.utc(1971);
if (date.isBefore(epochLimit)) {
return null;
}
final Duration duration = DateTime.now().difference(date); final Duration duration = DateTime.now().difference(date);
final int diffDays = duration.inDays + final int diffDays = duration.inDays +
(duration.inMinutes > (duration.inMinutes >

View File

@ -279,7 +279,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// Whether to use the default emoji /// Whether to use the default emoji
final bool isUseDefaultEmoji; final bool isUseDefaultEmoji;
final List customEmojiStickerList; final List<CustomEmojiFaceData> customEmojiStickerList;
final V2TimGroupMemberFullInfo? groupMemberInfo; final V2TimGroupMemberFullInfo? groupMemberInfo;
@ -287,7 +287,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// replacing the default message hover action bar. /// replacing the default message hover action bar.
/// Applicable only on desktop platforms. /// Applicable only on desktop platforms.
/// If provided, the default message action functionality will appear in the right-click context menu instead. /// If provided, the default message action functionality will appear in the right-click context menu instead.
final Widget Function(V2TimMessage message)? customMessageHoverBarOnDesktop; final Widget? Function(V2TimMessage message)? customMessageHoverBarOnDesktop;
const TIMUIKitHistoryMessageListItem( const TIMUIKitHistoryMessageListItem(
{Key? key, {Key? key,
@ -1093,8 +1093,11 @@ class _TIMUIKItHistoryMessageListItemState
bool isDownloadWaiting) { bool isDownloadWaiting) {
final isDesktopScreen = final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final customHoverBar = widget.customMessageHoverBarOnDesktop != null
? widget.customMessageHoverBarOnDesktop!(message)
: null;
final wideHoverTipList = (model.chatConfig.isUseMessageHoverBarOnDesktop && final wideHoverTipList = (model.chatConfig.isUseMessageHoverBarOnDesktop &&
widget.customMessageHoverBarOnDesktop == null) customHoverBar == null)
? getMessageHoverControlBar(model, theme) ? getMessageHoverControlBar(model, theme)
: []; : [];
final lastItemName = final lastItemName =
@ -1106,6 +1109,7 @@ class _TIMUIKItHistoryMessageListItemState
children: [ children: [
if (isDesktopScreen && if (isDesktopScreen &&
isShowWideToolTip && isShowWideToolTip &&
customHoverBar == null &&
!((widget.message.elemType == 6 && isDownloadWaiting))) !((widget.message.elemType == 6 && isDownloadWaiting)))
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -1142,13 +1146,11 @@ class _TIMUIKItHistoryMessageListItemState
.toList(), .toList(),
), ),
), ),
if (isDesktopScreen && if (isDesktopScreen && isShowWideToolTip && customHoverBar != null)
isShowWideToolTip && customHoverBar,
widget.customMessageHoverBarOnDesktop != null)
widget.customMessageHoverBarOnDesktop!(message),
if (!isDesktopScreen || if (!isDesktopScreen ||
(model.chatConfig.isUseMessageHoverBarOnDesktop && (model.chatConfig.isUseMessageHoverBarOnDesktop &&
widget.customMessageHoverBarOnDesktop == null && customHoverBar == null &&
!isShowWideToolTip)) !isShowWideToolTip))
const SizedBox( const SizedBox(
height: 24, height: 24,

View File

@ -6,6 +6,7 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.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/message.dart';
@ -79,6 +80,8 @@ class TIMUIKitMessageTooltip extends StatefulWidget {
class TIMUIKitMessageTooltipState class TIMUIKitMessageTooltipState
extends TIMUIKitState<TIMUIKitMessageTooltip> { extends TIMUIKitState<TIMUIKitMessageTooltip> {
final TUIChatGlobalModel globalModal = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel globalModal = serviceLocator<TUIChatGlobalModel>();
final TUISelfInfoViewModel selfInfoViewModel =
serviceLocator<TUISelfInfoViewModel>();
bool isShowMoreSticker = false; bool isShowMoreSticker = false;
bool isShowOpenFile = false; bool isShowOpenFile = false;
String filePath = ""; String filePath = "";
@ -179,9 +182,9 @@ class TIMUIKitMessageTooltipState
} }
bool isAdminCanRecall() { bool isAdminCanRecall() {
if (widget.groupMemberInfo != null && if (widget.model.chatConfig.isGroupAdminRecallEnabled) {
widget.model.chatConfig.isGroupAdminRecallEnabled) { final selfMemberInfo = widget.groupMemberInfo ?? widget.model.selfMemberInfo;
final selfRole = widget.groupMemberInfo!.role; final selfRole = selfMemberInfo?.role;
return selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN || return selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN ||
selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER; selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER;
} else { } else {

View File

@ -16,6 +16,7 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageLi
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
enum LoadingPlace { enum LoadingPlace {
none, none,
@ -70,7 +71,7 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
/// Whether to use the default emoji /// Whether to use the default emoji
final bool isUseDefaultEmoji; final bool isUseDefaultEmoji;
final List customEmojiStickerList; final List<CustomEmojiFaceData> customEmojiStickerList;
final bool isAllowScroll; final bool isAllowScroll;
@ -82,7 +83,7 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
/// replacing the default message hover action bar. /// replacing the default message hover action bar.
/// Applicable only on desktop platforms. /// Applicable only on desktop platforms.
/// If provided, the default message action functionality will appear in the right-click context menu instead. /// If provided, the default message action functionality will appear in the right-click context menu instead.
final Widget Function(V2TimMessage message)? customMessageHoverBarOnDesktop; final Widget? Function(V2TimMessage message)? customMessageHoverBarOnDesktop;
const TIMUIKitHistoryMessageListContainer({ const TIMUIKitHistoryMessageListContainer({
Key? key, Key? key,

View File

@ -33,6 +33,7 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/image_screen.dart';
import 'package:transparent_image/transparent_image.dart'; import 'package:transparent_image/transparent_image.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
class TIMUIKitImageElem extends StatefulWidget { class TIMUIKitImageElem extends StatefulWidget {
final V2TimMessage message; final V2TimMessage message;
@ -58,7 +59,6 @@ class TIMUIKitImageElem extends StatefulWidget {
class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> { class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
double? networkImagePositionRadio; //
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
final MessageService _messageService = serviceLocator<MessageService>(); final MessageService _messageService = serviceLocator<MessageService>();
Widget? imageItem; Widget? imageItem;
@ -373,178 +373,145 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
} }
} }
Widget _renderNetworkImage( Future<double> calculateAspectRatio(ImageProvider imageProvider) async {
dynamic heroTag, double? positionRadio, TUITheme? theme, Completer<double> completer = Completer<double>();
{String? path, V2TimImage? originalImg, V2TimImage? smallImg}) {
try {
String originImgUrl = originalImg?.url ?? getOriginImgURL();
if (originImgUrl.isEmpty && smallImg?.url != null) {
originImgUrl = smallImg!.url!;
}
final imageWidget = Hero( final imageStream = imageProvider.resolve(const ImageConfiguration());
tag: heroTag, imageStream.addListener(
child: PlatformUtils().isWeb ImageStreamListener((imageInfo, synchronousCall) {
? Image.network(path ?? smallImg?.url ?? originalImg!.url!, if (imageInfo.image.width != 0 && imageInfo.image.height != 0) {
fit: BoxFit.contain) double aspectRatio = imageInfo.image.width / imageInfo.image.height;
: CachedNetworkImage( completer.complete(aspectRatio);
alignment: Alignment.topCenter, } else {
imageUrl: path ?? smallImg?.url ?? originalImg!.url!, // If unable to calculate aspect ratio, return default value of 0.5
errorWidget: (context, error, stackTrace) => errorPage(theme), completer.complete(0.5);
fit: BoxFit.contain, }
cacheKey: smallImg?.uuid ?? originalImg!.uuid, }, onError: (Object exception, StackTrace? stackTrace) {
placeholder: (context, url) => // If there's an error, return default value of 0.5
Image(image: MemoryImage(kTransparentImage)), completer.complete(1);
fadeInDuration: const Duration(milliseconds: 0), }),
);
return await completer.future;
}
void onClickImage({
required bool isNetworkImage,
dynamic heroTag,
TUITheme? theme,
String? imgUrl,
String? imgPath,
}) {
if (isNetworkImage) {
if (PlatformUtils().isWeb) {
TUIKitWidePopup.showMedia(
context: context,
mediaURL: widget.message.imageElem?.path ?? "",
onClickOrigin: () => launchUrl(
Uri.parse(widget.message.imageElem?.path ?? ""),
mode: LaunchMode.externalApplication,
)); ));
return;
return Stack( }
alignment: widget.message.isSelf ?? true if (PlatformUtils().isDesktop) {
? AlignmentDirectional.topEnd _handleOnTapPreviewImageOnDesktop(
: AlignmentDirectional.topStart, originImgUrl: imgUrl,
children: [ );
getImage( } else {
GestureDetector( Navigator.of(context).push(
onTap: () async { PageRouteBuilder(
if (PlatformUtils().isWeb) { opaque: false,
TUIKitWidePopup.showMedia( pageBuilder: (_, __, ___) => ImageScreen(
aspectRatio: positionRadio, imageProvider: CachedNetworkImageProvider(
context: context, imgUrl ?? "",
mediaURL: widget.message.imageElem?.path ?? "", cacheKey: widget.message.msgID,
onClickOrigin: () => launchUrl( ),
Uri.parse(widget.message.imageElem?.path ?? ""), heroTag: heroTag,
mode: LaunchMode.externalApplication, messageID: widget.message.msgID,
)); downloadFn: () async {
return; return await _saveImg(theme!);
} })),
if (PlatformUtils().isDesktop) { );
_handleOnTapPreviewImageOnDesktop( }
positionRadio: positionRadio, } else {
originImgUrl: originImgUrl, if (PlatformUtils().isDesktop) {
); TUIKitWidePopup.showMedia(
} else { mediaLocalPath: imgPath,
Navigator.of(context).push( context: context,
PageRouteBuilder( onClickOrigin: () => launchDesktopFile(imgPath ?? ""));
opaque: false, // set to false } else {
pageBuilder: (_, __, ___) => ImageScreen( Navigator.of(context).push(
imageProvider: CachedNetworkImageProvider( PageRouteBuilder(
path ?? originImgUrl, opaque: false, // set to false
cacheKey: widget.message.msgID, pageBuilder: (_, __, ___) => ImageScreen(
), imageProvider: FileImage(File(imgPath ?? "")),
heroTag: heroTag, heroTag: heroTag,
messageID: widget.message.msgID, messageID: widget.message.msgID,
downloadFn: () async { downloadFn: () async {
return await _saveImg(theme!); return await _saveImg(theme!);
})), }),
); ),
} );
}, }
child: positionRadio != null
? AspectRatio(
aspectRatio: positionRadio,
child: imageWidget,
)
: imageWidget,
),
imageElem: e)
],
);
} catch (e) {
return errorDisplay(context, theme);
} }
} }
Widget _renderLocalImage(String smallImage, dynamic heroTag, Widget _renderAllImage(
double? positionRadio, TUITheme? theme, String? originImage) { {dynamic heroTag,
double? currentPositionRadio = positionRadio; TUITheme? theme,
File imgF = File(smallImage); bool isNetworkImage = false,
String? webPath,
V2TimImage? originalImg,
V2TimImage? smallImg,
String? smallLocalPath,
String? originLocalPath}) {
Widget getImageWidget() {
if (isNetworkImage) {
return Hero(
tag: heroTag,
child: PlatformUtils().isWeb
? Image.network(webPath ?? smallImg?.url ?? originalImg!.url!,
fit: BoxFit.contain)
: CachedNetworkImage(
alignment: Alignment.topCenter,
imageUrl: webPath ?? smallImg?.url ?? originalImg!.url!,
errorWidget: (context, error, stackTrace) =>
errorPage(theme),
fit: BoxFit.contain,
cacheKey: smallImg?.uuid ?? originalImg!.uuid,
placeholder: (context, url) =>
Image(image: MemoryImage(kTransparentImage)),
fadeInDuration: const Duration(milliseconds: 0),
));
} else {
final imgPath = (TencentUtils.checkString(smallLocalPath) != null
bool isExist = imgF.existsSync(); ? smallLocalPath
if (!isExist) { : originLocalPath)!;
return errorDisplay(context, theme); return Hero(
tag: heroTag,
child: Image.file(File(imgPath), fit: BoxFit.contain));
}
} }
Image image = Image.file(imgF); ImageProvider imageProvider;
if (isNetworkImage) {
imageProvider = CachedNetworkImageProvider(
webPath ?? smallImg?.url ?? originalImg!.url!);
} else {
imageProvider = FileImage(File(smallLocalPath ?? originLocalPath!));
}
String showImage = (originImage != null && File(originImage).existsSync()) return GestureDetector(
? originImage onTap: () => onClickImage(
: smallImage; heroTag: heroTag,
isNetworkImage: isNetworkImage,
image.image imgUrl: webPath ?? smallImg?.url ?? originalImg?.url ?? "",
.resolve(const ImageConfiguration()) imgPath: (TencentUtils.checkString(originLocalPath) != null
.addListener(ImageStreamListener((image, synchronousCall) { ? originLocalPath
if (image.image.width != 0 && image.image.height != 0) { : smallLocalPath) ?? ""
currentPositionRadio = image.image.width / image.image.height; ),
} child: getImageWidget(),
}));
final message = widget.message;
final preloadImage = model.preloadImageMap[
message.seq! + message.timestamp.toString() + (message.msgID ?? "")];
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final imageWidget = Hero(
tag: heroTag,
child: preloadImage != null
? FittedBox(
fit: BoxFit.contain,
child: RawImage(
image: preloadImage,
fit: BoxFit.contain,
),
)
: FittedBox(
fit: BoxFit.contain,
child: Image.file(
File(smallImage),
fit: BoxFit.contain,
),
),
);
return Stack(
alignment: AlignmentDirectional.topStart,
children: [
if (!isDesktopScreen && currentPositionRadio != null)
AspectRatio(
aspectRatio: currentPositionRadio!,
child: Container(
decoration: const BoxDecoration(color: Colors.transparent),
),
),
getImage(
GestureDetector(
onTap: () {
if (PlatformUtils().isDesktop) {
TUIKitWidePopup.showMedia(
aspectRatio: positionRadio,
mediaLocalPath: showImage,
context: context,
onClickOrigin: () => launchDesktopFile(showImage));
} else {
Navigator.of(context).push(
PageRouteBuilder(
opaque: false, // set to false
pageBuilder: (_, __, ___) => ImageScreen(
imageProvider: FileImage(File(showImage)),
heroTag: heroTag,
messageID: widget.message.msgID,
downloadFn: () async {
return await _saveImg(theme!);
}),
),
);
}
},
child: positionRadio != null
? AspectRatio(
aspectRatio: positionRadio,
child: imageWidget,
)
: imageWidget),
imageElem: null)
],
); );
} }
@ -587,26 +554,6 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
isSnapshot: false); isSnapshot: false);
} }
} }
// feature
// setOnlineImageRatio();
}
void setOnlineImageRatio() {
if (networkImagePositionRadio == null) {
V2TimImage? smallImg = getImageFromList(V2TimImageTypesEnum.small);
V2TimImage? originalImg = getImageFromList(V2TimImageTypesEnum.original);
Image image = Image.network(smallImg?.url ?? originalImg?.url ?? "");
image.image
.resolve(const ImageConfiguration())
.addListener(ImageStreamListener((ImageInfo info, bool _) {
if (info.image.width != 0 && info.image.height != 0) {
setState(() {
networkImagePositionRadio = (info.image.width / info.image.height);
});
}
}));
}
} }
bool isNeedShowLocalPath() { bool isNeedShowLocalPath() {
@ -628,10 +575,13 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (PlatformUtils().isWeb && widget.message.imageElem!.path != null) { if (PlatformUtils().isWeb && widget.message.imageElem!.path != null) {
// Displaying on Web only // Displaying on Web only
return _renderNetworkImage(heroTag, positionRadio, theme, return _renderAllImage(
heroTag: heroTag,
theme: theme,
isNetworkImage: true,
smallImg: smallImg, smallImg: smallImg,
originalImg: originalImg, originalImg: originalImg,
path: widget.message.imageElem!.path); webPath: widget.message.imageElem!.path);
} }
try { try {
@ -639,36 +589,47 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
widget.message.imageElem!.path != null && widget.message.imageElem!.path != null &&
widget.message.imageElem!.path!.isNotEmpty && widget.message.imageElem!.path!.isNotEmpty &&
File(widget.message.imageElem!.path!).existsSync())) { File(widget.message.imageElem!.path!).existsSync())) {
return _renderLocalImage( return _renderAllImage(
widget.message.imageElem!.path!, smallLocalPath: widget.message.imageElem!.path!,
heroTag, heroTag: heroTag,
networkImagePositionRadio ?? positionRadio, theme: theme,
theme, originLocalPath: widget.message.imageElem!.path!);
widget.message.imageElem!.path!);
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print(e); outputLogger.i(e);
} }
try { try {
if (smallImg?.localUrl != null && if ((TencentUtils.checkString(smallImg?.localUrl) != null &&
smallImg?.localUrl != "" && File((smallImg?.localUrl!)!).existsSync()) ||
File((smallImg?.localUrl!)!).existsSync()) { (TencentUtils.checkString(originalImg?.localUrl) != null &&
return _renderLocalImage(smallImg!.localUrl!, heroTag, positionRadio, File((originalImg?.localUrl!)!).existsSync())) {
theme, originalImg?.localUrl); return _renderAllImage(
smallLocalPath: smallImg?.localUrl ?? "",
heroTag: heroTag,
theme: theme,
originLocalPath: originalImg?.localUrl);
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print(e); outputLogger.i(e);
return _renderNetworkImage(heroTag, positionRadio, theme, return _renderAllImage(
smallImg: smallImg, originalImg: originalImg); heroTag: heroTag,
theme: theme,
isNetworkImage: true,
smallImg: smallImg,
originalImg: originalImg);
} }
if ((smallImg?.url ?? originalImg?.url) != null && if ((smallImg?.url ?? originalImg?.url) != null &&
(smallImg?.url ?? originalImg?.url)!.isNotEmpty) { (smallImg?.url ?? originalImg?.url)!.isNotEmpty) {
return _renderNetworkImage(heroTag, positionRadio, theme, return _renderAllImage(
smallImg: smallImg, originalImg: originalImg); heroTag: heroTag,
theme: theme,
isNetworkImage: true,
smallImg: smallImg,
originalImg: originalImg);
} }
return errorDisplay(context, theme); return errorDisplay(context, theme);

View File

@ -22,6 +22,8 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_c
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/link_preview_entry.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/link_preview_entry.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/models/link_preview_content.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/models/link_preview_content.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_preview.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_preview.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
class TIMUIKitReplyElem extends StatefulWidget { class TIMUIKitReplyElem extends StatefulWidget {
final V2TimMessage message; final V2TimMessage message;
@ -35,7 +37,7 @@ class TIMUIKitReplyElem extends StatefulWidget {
final TUIChatSeparateViewModel chatModel; final TUIChatSeparateViewModel chatModel;
final bool? isShowMessageReaction; final bool? isShowMessageReaction;
final bool isUseDefaultEmoji; final bool isUseDefaultEmoji;
final List customEmojiStickerList; final List<CustomEmojiFaceData> customEmojiStickerList;
const TIMUIKitReplyElem({ const TIMUIKitReplyElem({
Key? key, Key? key,
@ -72,7 +74,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
: "{}")); : "{}"));
if (messageCloudCustomData.messageReply != null) { if (messageCloudCustomData.messageReply != null) {
final MessageRepliedData repliedMessage = final MessageRepliedData repliedMessage =
MessageRepliedData.fromJson(messageCloudCustomData.messageReply!); MessageRepliedData.fromJson(messageCloudCustomData.messageReply!);
return repliedMessage; return repliedMessage;
} }
return null; return null;
@ -95,8 +97,8 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
if (message == null) { if (message == null) {
try { try {
final RepliedMessageAbstract repliedMessageAbstract = final RepliedMessageAbstract repliedMessageAbstract =
RepliedMessageAbstract.fromJson( RepliedMessageAbstract.fromJson(
jsonDecode(cloudCustomData.messageAbstract)); jsonDecode(cloudCustomData.messageAbstract));
if (repliedMessageAbstract.isNotEmpty) { if (repliedMessageAbstract.isNotEmpty) {
message = V2TimMessage( message = V2TimMessage(
elemType: 0, elemType: 0,
@ -106,7 +108,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print(e.toString()); outputLogger.i(e.toString());
} }
} }
if (message != null) { if (message != null) {
@ -132,8 +134,8 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
_renderMessageSummary(TUITheme? theme) { _renderMessageSummary(TUITheme? theme) {
try { try {
final RepliedMessageAbstract repliedMessageAbstract = final RepliedMessageAbstract repliedMessageAbstract =
RepliedMessageAbstract.fromJson( RepliedMessageAbstract.fromJson(
jsonDecode(repliedMessage?.messageAbstract ?? "")); jsonDecode(repliedMessage?.messageAbstract ?? ""));
if (TencentUtils.checkString(repliedMessageAbstract.summary) != null) { if (TencentUtils.checkString(repliedMessageAbstract.summary) != null) {
return _defaultRawMessageText(repliedMessageAbstract.summary!, theme); return _defaultRawMessageText(repliedMessageAbstract.summary!, theme);
} }
@ -177,16 +179,15 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
if (isRevokedMsg) { if (isRevokedMsg) {
return _defaultRawMessageText( return _defaultRawMessageText(
isAdminRevoke ? TIM_t("[消息被管理员撤回]") : TIM_t("[消息被撤回]"), isAdminRevoke ? TIM_t("[消息被管理员撤回]") : TIM_t("[消息被撤回]"), theme);
theme);
} }
final messageType = message.elemType; final messageType = message.elemType;
final isSelf = message.isSelf ?? true; final isSelf = message.isSelf ?? true;
final customAbstractMessage = final customAbstractMessage =
widget.chatModel.abstractMessageBuilder != null widget.chatModel.abstractMessageBuilder != null
? widget.chatModel.abstractMessageBuilder!(message) ? widget.chatModel.abstractMessageBuilder!(message)
: null; : null;
if (customAbstractMessage != null) { if (customAbstractMessage != null) {
return _defaultRawMessageText( return _defaultRawMessageText(
customAbstractMessage, customAbstractMessage,
@ -302,14 +303,14 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
try { try {
final String localJSON = widget.message.localCustomData!; final String localJSON = widget.message.localCustomData!;
final LocalCustomDataModel? localPreviewInfo = final LocalCustomDataModel? localPreviewInfo =
LocalCustomDataModel.fromMap(json.decode(localJSON)); LocalCustomDataModel.fromMap(json.decode(localJSON));
if (localPreviewInfo != null && if (localPreviewInfo != null &&
!localPreviewInfo.isLinkPreviewEmpty()) { !localPreviewInfo.isLinkPreviewEmpty()) {
return Container( return Container(
margin: const EdgeInsets.only(top: 8), margin: const EdgeInsets.only(top: 8),
child: child:
// You can use this default widget [LinkPreviewWidget] to render preview card, or you can use custom widget. // You can use this default widget [LinkPreviewWidget] to render preview card, or you can use custom widget.
LinkPreviewWidget(linkPreview: localPreviewInfo), LinkPreviewWidget(linkPreview: localPreviewInfo),
); );
} else { } else {
return null; return null;
@ -344,7 +345,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
final defaultStyle = isFromSelf final defaultStyle = isFromSelf
? (theme.chatMessageItemFromSelfBgColor ?? ? (theme.chatMessageItemFromSelfBgColor ??
theme.lightPrimaryMaterialColor.shade50) theme.lightPrimaryMaterialColor.shade50)
: (theme.chatMessageItemFromOthersBgColor); : (theme.chatMessageItemFromOthersBgColor);
final backgroundColor = isShowJumpState final backgroundColor = isShowJumpState
@ -353,23 +354,29 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
final borderRadius = isFromSelf final borderRadius = isFromSelf
? const BorderRadius.only( ? const BorderRadius.only(
topLeft: Radius.circular(10), topLeft: Radius.circular(10),
topRight: Radius.circular(2), topRight: Radius.circular(2),
bottomLeft: Radius.circular(10), bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)) bottomRight: Radius.circular(10))
: const BorderRadius.only( : const BorderRadius.only(
topLeft: Radius.circular(2), topLeft: Radius.circular(2),
topRight: Radius.circular(10), topRight: Radius.circular(10),
bottomLeft: Radius.circular(10), bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)); bottomRight: Radius.circular(10));
final textWithLink = LinkPreviewEntry.getHyperlinksText( final textWithLink = LinkPreviewEntry.getHyperlinksText(
widget.message.textElem?.text ?? "", widget.message.textElem?.text ?? "",
widget.chatModel.chatConfig.isSupportMarkdownForTextMessage, widget.chatModel.chatConfig.isSupportMarkdownForTextMessage,
onLinkTap: widget.chatModel.chatConfig.onTapLink, onLinkTap: widget.chatModel.chatConfig.onTapLink,
isUseDefaultEmoji: widget.isUseDefaultEmoji, isUseQQPackage: (widget.chatModel.chatConfig.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget.chatModel.chatConfig
.stickerPanelConfig?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: widget.customEmojiStickerList, customEmojiStickerList: widget.customEmojiStickerList,
isEnableTextSelection: isEnableTextSelection:
widget.chatModel.chatConfig.isEnableTextSelection ?? false); widget.chatModel.chatConfig.isEnableTextSelection ?? false);
return Container( return Container(
padding: widget.textPadding ?? EdgeInsets.all(isDesktopScreen ? 12 : 10), padding: widget.textPadding ?? EdgeInsets.all(isDesktopScreen ? 12 : 10),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -377,10 +384,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
borderRadius: widget.borderRadius ?? borderRadius, borderRadius: widget.borderRadius ?? borderRadius,
), ),
constraints: constraints:
BoxConstraints(maxWidth: MediaQuery BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.6),
.of(context)
.size
.width * 0.6),
child: GestureDetector( child: GestureDetector(
onTap: _jumpToRawMsg, onTap: _jumpToRawMsg,
child: Column( child: Column(
@ -421,22 +425,34 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
// You can render the widget from extension directly, with a [TextStyle] optionally. // You can render the widget from extension directly, with a [TextStyle] optionally.
widget.chatModel.chatConfig.urlPreviewType != UrlPreviewType.none widget.chatModel.chatConfig.urlPreviewType != UrlPreviewType.none
? textWithLink!( ? textWithLink!(
style: widget.fontStyle ?? style: widget.fontStyle ??
TextStyle( TextStyle(
fontSize: isDesktopScreen ? 14 : 16, fontSize: isDesktopScreen ? 14 : 16,
textBaseline: TextBaseline.ideographic, textBaseline: TextBaseline.ideographic,
height: widget.chatModel.chatConfig.textHeight)) height: widget.chatModel.chatConfig.textHeight))
: ExtendedText(widget.message.textElem?.text ?? "", : ExtendedText(widget.message.textElem?.text ?? "",
softWrap: true, softWrap: true,
style: widget.fontStyle ?? style: widget.fontStyle ??
TextStyle( TextStyle(
fontSize: isDesktopScreen ? 14 : 16, fontSize: isDesktopScreen ? 14 : 16,
height: widget.chatModel.chatConfig.textHeight), height: widget.chatModel.chatConfig.textHeight),
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder( specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji: widget.isUseDefaultEmoji, isUseQQPackage: (widget
customEmojiStickerList: widget.customEmojiStickerList, .chatModel
showAtBackground: true, .chatConfig
)), .stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget
.chatModel
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: widget.customEmojiStickerList,
showAtBackground: true,
)),
// If the link preview info is available, render the preview card. // If the link preview info is available, render the preview card.
if (_renderPreviewWidget() != null && if (_renderPreviewWidget() != null &&
widget.chatModel.chatConfig.urlPreviewType == widget.chatModel.chatConfig.urlPreviewType ==

View File

@ -25,7 +25,7 @@ class TIMUIKitTextElem extends StatefulWidget {
final TUIChatSeparateViewModel chatModel; final TUIChatSeparateViewModel chatModel;
final bool? isShowMessageReaction; final bool? isShowMessageReaction;
final bool isUseDefaultEmoji; final bool isUseDefaultEmoji;
final List customEmojiStickerList; final List<CustomEmojiFaceData> customEmojiStickerList;
const TIMUIKitTextElem( const TIMUIKitTextElem(
{Key? key, {Key? key,
@ -167,7 +167,13 @@ class _TIMUIKitTextElemState extends TIMUIKitState<TIMUIKitTextElem> {
widget.message.textElem?.text ?? "", widget.message.textElem?.text ?? "",
widget.chatModel.chatConfig.isSupportMarkdownForTextMessage, widget.chatModel.chatConfig.isSupportMarkdownForTextMessage,
onLinkTap: widget.chatModel.chatConfig.onTapLink, onLinkTap: widget.chatModel.chatConfig.onTapLink,
isUseDefaultEmoji: widget.isUseDefaultEmoji, isUseQQPackage: (widget.chatModel.chatConfig.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget.chatModel.chatConfig
.stickerPanelConfig?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: widget.customEmojiStickerList, customEmojiStickerList: widget.customEmojiStickerList,
isEnableTextSelection: isEnableTextSelection:
widget.chatModel.chatConfig.isEnableTextSelection ?? false); widget.chatModel.chatConfig.isEnableTextSelection ?? false);
@ -232,7 +238,19 @@ class _TIMUIKitTextElemState extends TIMUIKitState<TIMUIKitTextElem> {
fontSize: isDesktopScreen ? 14 : 16, fontSize: isDesktopScreen ? 14 : 16,
height: widget.chatModel.chatConfig.textHeight), height: widget.chatModel.chatConfig.textHeight),
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder( specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji: widget.isUseDefaultEmoji, isUseQQPackage: (widget
.chatModel
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget
.chatModel
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: widget.customEmojiStickerList, customEmojiStickerList: widget.customEmojiStickerList,
showAtBackground: true, showAtBackground: true,
)), )),

View File

@ -23,7 +23,7 @@ class TIMUIKitTextTranslationElem extends StatefulWidget {
final TUIChatSeparateViewModel chatModel; final TUIChatSeparateViewModel chatModel;
final bool? isShowMessageReaction; final bool? isShowMessageReaction;
final bool isUseDefaultEmoji; final bool isUseDefaultEmoji;
final List customEmojiStickerList; final List<CustomEmojiFaceData> customEmojiStickerList;
const TIMUIKitTextTranslationElem( const TIMUIKitTextTranslationElem(
{Key? key, {Key? key,
@ -124,7 +124,13 @@ class _TIMUIKitTextTranslationElemState
final textWithLink = LinkPreviewEntry.getHyperlinksText(translateText ?? "", final textWithLink = LinkPreviewEntry.getHyperlinksText(translateText ?? "",
widget.chatModel.chatConfig.isSupportMarkdownForTextMessage, widget.chatModel.chatConfig.isSupportMarkdownForTextMessage,
onLinkTap: widget.chatModel.chatConfig.onTapLink, onLinkTap: widget.chatModel.chatConfig.onTapLink,
isUseDefaultEmoji: widget.isUseDefaultEmoji, isUseQQPackage: (widget.chatModel.chatConfig.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget.chatModel.chatConfig
.stickerPanelConfig?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: widget.customEmojiStickerList, customEmojiStickerList: widget.customEmojiStickerList,
isEnableTextSelection: isEnableTextSelection:
widget.chatModel.chatConfig.isEnableTextSelection ?? false); widget.chatModel.chatConfig.isEnableTextSelection ?? false);
@ -160,7 +166,19 @@ class _TIMUIKitTextTranslationElemState
fontSize: isDesktopScreen ? 14 : 16, fontSize: isDesktopScreen ? 14 : 16,
height: widget.chatModel.chatConfig.textHeight), height: widget.chatModel.chatConfig.textHeight),
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder( specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji: widget.isUseDefaultEmoji, isUseQQPackage: (widget
.chatModel
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget
.chatModel
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: widget.customEmojiStickerList, customEmojiStickerList: widget.customEmojiStickerList,
showAtBackground: true, showAtBackground: true,
)), )),

View File

@ -4,7 +4,6 @@ import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';

View File

@ -1,15 +1,16 @@
// ignore_for_file: file_names // ignore_for_file: file_names
import 'package:extended_text_field/extended_text_field.dart'; import 'package:extended_text_field/extended_text_field.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart';
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
import 'emoji_text.dart'; import 'emoji_text.dart';
class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder { class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
DefaultSpecialTextSpanBuilder({ DefaultSpecialTextSpanBuilder({
this.isUseDefaultEmoji = false, this.isUseQQPackage = false,
this.isUseTencentCloudChatPackage = false,
this.customEmojiStickerList = const [], this.customEmojiStickerList = const [],
this.showAtBackground = false, this.showAtBackground = false,
}); });
@ -17,9 +18,11 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
/// whether show background for @somebody /// whether show background for @somebody
final bool showAtBackground; final bool showAtBackground;
final bool isUseDefaultEmoji; final bool isUseQQPackage;
final List customEmojiStickerList; final bool isUseTencentCloudChatPackage;
final List<CustomEmojiFaceData> customEmojiStickerList;
@override @override
SpecialText? createSpecialText(String flag, SpecialText? createSpecialText(String flag,
@ -33,8 +36,9 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
///index is end index of start flag, so text start index should be index-(flag.length-1) ///index is end index of start flag, so text start index should be index-(flag.length-1)
if (isStart(flag, EmojiText.flag)) { if (isStart(flag, EmojiText.flag)) {
return EmojiText(textStyle, return EmojiText(textStyle,
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
start: index! - (EmojiText.flag.length - 1), start: index! - (EmojiText.flag.length - 1),
isUseDefaultEmoji: isUseDefaultEmoji, isUseQQPackage: isUseQQPackage,
customEmojiStickerList: customEmojiStickerList); customEmojiStickerList: customEmojiStickerList);
} else if (isStart(flag, HttpText.flag)) { } else if (isStart(flag, HttpText.flag)) {
return HttpText(textStyle, onTap, return HttpText(textStyle, onTap,

View File

@ -1,44 +1,54 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:extended_text/extended_text.dart'; import 'package:extended_text/extended_text.dart';
import 'package:tim_ui_kit_sticker_plugin/constant/emoji.dart'; import 'package:tim_ui_kit_sticker_plugin/constant/emoji.dart';
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
///emoji/image text ///emoji/image text
class EmojiText extends SpecialText { class EmojiText extends SpecialText {
EmojiText(TextStyle? textStyle, EmojiText(TextStyle? textStyle,
{this.start, {this.start,
this.isUseDefaultEmoji = false, this.isUseTencentCloudChatPackage = false,
this.isUseQQPackage = false,
this.customEmojiStickerList = const []}) this.customEmojiStickerList = const []})
: super(EmojiText.flag, ']', textStyle); : super(EmojiText.flag, ']', textStyle);
static const String flag = '['; static const String flag = '[';
final int? start; final int? start;
final bool isUseDefaultEmoji; final bool isUseQQPackage;
final List customEmojiStickerList; final bool isUseTencentCloudChatPackage;
final List<CustomEmojiFaceData> customEmojiStickerList;
@override @override
InlineSpan finishText() { InlineSpan finishText() {
final String key = toString(); final String key = toString();
final EmojiUtil emojiUtil = EmojiUtil(
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
isUseQQPackage: isUseQQPackage,
customEmojiStickerList: customEmojiStickerList);
if (EmojiUtil( if (emojiUtil.emojiMap.containsKey(key)) {
isUseDefaultEmoji: isUseDefaultEmoji,
customEmojiStickerList: customEmojiStickerList)
.instance
.emojiMap
.containsKey(key)) {
double size = 16; double size = 16;
final TextStyle ts = textStyle!; final TextStyle ts = textStyle!;
if (ts.fontSize != null) { if (ts.fontSize != null) {
size = ts.fontSize! * 1.15; size = ts.fontSize! * 1.44;
} }
if (isUseDefaultEmoji == true) { if (isUseQQPackage == true &&
(emojiUtil.emojiKeyCategoryMap["4349"]?.contains(key) ?? false)) {
return ImageSpan( return ImageSpan(
AssetImage( AssetImage(emojiUtil.emojiMap[key]!,
EmojiUtil( package: "tim_ui_kit_sticker_plugin"),
isUseDefaultEmoji: isUseDefaultEmoji, actualText: key,
customEmojiStickerList: customEmojiStickerList) imageWidth: size,
.instance imageHeight: size,
.emojiMap[key]!, start: start!,
package: "tencent_im_base"), // fit: BoxFit.cover,
margin: const EdgeInsets.all(0));
} else if (isUseTencentCloudChatPackage == true &&
(emojiUtil.emojiKeyCategoryMap["tcc1"]?.contains(key) ?? false)) {
return ImageSpan(
AssetImage(emojiUtil.emojiMap[key]!,
package: "tim_ui_kit_sticker_plugin"),
actualText: key, actualText: key,
imageWidth: size, imageWidth: size,
imageHeight: size, imageHeight: size,
@ -46,12 +56,7 @@ class EmojiText extends SpecialText {
// fit: BoxFit.cover, // fit: BoxFit.cover,
margin: const EdgeInsets.all(0)); margin: const EdgeInsets.all(0));
} else { } else {
return ImageSpan( return ImageSpan(AssetImage(emojiUtil.emojiMap[key]!),
AssetImage(EmojiUtil(
isUseDefaultEmoji: isUseDefaultEmoji,
customEmojiStickerList: customEmojiStickerList)
.instance
.emojiMap[key]!),
actualText: key, actualText: key,
imageWidth: size, imageWidth: size,
imageHeight: size, imageHeight: size,
@ -66,39 +71,90 @@ class EmojiText extends SpecialText {
} }
class EmojiUtil { class EmojiUtil {
EmojiUtil( // Private constructor initializing the emoji data
{this.isUseDefaultEmoji = false, this.customEmojiStickerList = const []}); EmojiUtil._internal(
EmojiUtil._(this.isUseDefaultEmoji, this.customEmojiStickerList) { {required this.isUseQQPackage,
if (isUseDefaultEmoji == true) { required this.isUseTencentCloudChatPackage,
for (int i = 0; i < ConstData.emojiList.length; i++) { required this.customEmojiStickerList}) {
for (int j = 0; j < ConstData.emojiList[i].list.length; j++) { _emojiMap.addAll(loadDefaultEmojis());
String? emojiName = ConstData.emojiList[i].list[j].split('.png')[0];
_emojiMap['[$emojiName]'] = final customEmojis = loadCustomEmojis();
'$_emojiFilePath/${ConstData.emojiList[i].name}/$emojiName.png'; _emojiMap.addAll(customEmojis.$1);
_emojiMap['[${ConstData.emojiMapList[emojiName]}]'] = _emojiKeyCategoryMap["custom"] = customEmojis.$2;
'$_emojiFilePath/${ConstData.emojiList[i].name}/$emojiName.png'; }
}
} final bool isUseQQPackage;
} else { final bool isUseTencentCloudChatPackage;
for (int i = 0; i < customEmojiStickerList.length; i++) { final List<CustomEmojiFaceData> customEmojiStickerList;
for (int j = 0; j < customEmojiStickerList[i].list.length; j++) {
String? emojiName = // Load the default emojis into a Map
customEmojiStickerList[i].list[j].split('.png')[0]; Map<String, String> loadDefaultEmojis() {
_emojiMap['[$emojiName]'] = Map<String, String> defaultEmojiMap = {};
'$_emojiFilePath/${customEmojiStickerList[i].name}/$emojiName.png'; for (final emojiGroup in TUIKitStickerConstData.emojiList) {
final groupName = emojiGroup.name;
final keyList = [];
if ((isUseQQPackage && groupName == "4349") ||
(isUseTencentCloudChatPackage && groupName == "tcc1")) {
for (final emoji in emojiGroup.list) {
String emojiName = emoji.split('.png')[0];
defaultEmojiMap['[$emojiName]'] =
'$_emojiFilePath/$groupName/$emojiName.png';
keyList.add('[$emojiName]');
if (groupName == "4349") {
final zhKey = TUIKitStickerConstData.emojiMapList[emojiName];
defaultEmojiMap['[$zhKey]'] =
'$_emojiFilePath/$groupName/$emojiName.png';
keyList.add('[$zhKey]');
}
} }
_emojiKeyCategoryMap[groupName] = keyList;
} }
} }
return defaultEmojiMap;
} }
final List customEmojiStickerList;
final bool isUseDefaultEmoji; // Load the custom emojis into a Map
(Map<String, String>, List<String>) loadCustomEmojis() {
Map<String, String> customEmojiMap = {};
List<String> keyList = [];
for (final customEmojiGroup in customEmojiStickerList) {
for (final customEmoji in customEmojiGroup.list) {
String customEmojiName = customEmoji.split('.png')[0];
customEmojiMap['[$customEmojiName]'] =
'$_emojiFilePath/${customEmojiGroup.name}/$customEmojiName.png';
keyList.add('[$customEmojiName]');
}
}
return (customEmojiMap, keyList);
}
// A Map instance variable to store the emojis and their paths
final Map<String, String> _emojiMap = <String, String>{}; final Map<String, String> _emojiMap = <String, String>{};
// A getter method for _emojiMap
Map<String, String> get emojiMap => _emojiMap; Map<String, String> get emojiMap => _emojiMap;
// A Map instance variable to store the emojis and their paths
final Map<String, List> _emojiKeyCategoryMap = <String, List>{};
// A getter method for _emojiMap
Map<String, List> get emojiKeyCategoryMap => _emojiKeyCategoryMap;
// An instance variable to store the emoji file path
final String _emojiFilePath = 'assets/custom_face_resource'; final String _emojiFilePath = 'assets/custom_face_resource';
// EmojiUitl? _instance;
// Singleton pattern to avoid creating multiple instances of EmojiUtil
static EmojiUtil? _instance; static EmojiUtil? _instance;
EmojiUtil get instance =>
_instance ??= EmojiUtil._(isUseDefaultEmoji, customEmojiStickerList); // Factory constructor to return the singleton instance of EmojiUtil with custom parameters
factory EmojiUtil(
{bool isUseQQPackage = false,
bool isUseTencentCloudChatPackage = false,
List<CustomEmojiFaceData> customEmojiStickerList = const []}) {
return _instance ??= EmojiUtil._internal(
isUseQQPackage: isUseQQPackage,
customEmojiStickerList: customEmojiStickerList,
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage);
}
} }

View File

@ -4,6 +4,7 @@ import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
class EmojiPanel extends TIMUIKitStatelessWidget { class EmojiPanel extends TIMUIKitStatelessWidget {
final void Function(int unicode) onTapEmoji; final void Function(int unicode) onTapEmoji;
@ -22,7 +23,7 @@ class EmojiPanel extends TIMUIKitStatelessWidget {
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
// ignore: avoid_print // ignore: avoid_print
print(TIM_t( outputLogger.i(TIM_t(
"暂未安装表情包插件如需使用表情相关功能请根据本文档安装https://cloud.tencent.com/document/product/269/70746")); "暂未安装表情包插件如需使用表情相关功能请根据本文档安装https://cloud.tencent.com/document/product/269/70746"));
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Column(

View File

@ -29,6 +29,7 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
// ignore: unnecessary_import // ignore: unnecessary_import
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
class MorePanelConfig { class MorePanelConfig {
final bool showGalleryPickAction; final bool showGalleryPickAction;
@ -357,41 +358,44 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
_sendImageMessage(TUIChatSeparateViewModel model, TUITheme theme) async { _sendImageMessage(TUIChatSeparateViewModel model, TUITheme theme) async {
try { try {
if (PlatformUtils().isMobile){ if (PlatformUtils().isMobile) {
if(PlatformUtils().isAndroid){ if (PlatformUtils().isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
if ((androidInfo.version.sdkInt) >= 33) { if ((androidInfo.version.sdkInt) >= 33) {
final videos = await Permissions.checkPermission( final videos = await Permissions.checkPermission(
context,Permission.videos.value, context,
Permission.videos.value,
theme, theme,
); );
final photos = await Permissions.checkPermission( final photos = await Permissions.checkPermission(
context,Permission.photos.value, context,
Permission.photos.value,
theme, theme,
); );
if(!videos && !photos){ if (!videos && !photos) {
return; return;
} }
} else { } else {
final storage = await Permissions.checkPermission( final storage = await Permissions.checkPermission(
context, Permission.storage.value, context,
Permission.storage.value,
theme, theme,
); );
if(!storage){ if (!storage) {
return; return;
} }
} }
}else{ } else {
final photos = await Permissions.checkPermission( final photos = await Permissions.checkPermission(
context, context,
Permission.photos.value, Permission.photos.value,
theme, theme,
); );
if(!photos){ if (!photos) {
return; return;
} }
} }
} }
final convID = widget.conversationID; final convID = widget.conversationID;
final convType = widget.conversationType; final convType = widget.conversationType;
@ -446,7 +450,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
} }
} }
} catch (err) { } catch (err) {
print("err: $err"); outputLogger.i("err: $err");
} }
} }
@ -462,18 +466,41 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
)) { )) {
return; return;
} }
if (!await Permissions.checkPermission(
context,
Permission.photos.value,
theme,
)) {
return;
}
await Permissions.checkPermission( await Permissions.checkPermission(
context, context,
Permission.microphone.value, Permission.microphone.value,
theme, theme,
); );
if (PlatformUtils().isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
if ((androidInfo.version.sdkInt) >= 33) {
if (!await Permissions.checkPermission(
context,
Permission.photos.value,
theme,
)) {
return;
}
} else {
if (!await Permissions.checkPermission(
context,
Permission.storage.value,
theme,
)) {
return;
}
}
} else {
if (!await Permissions.checkPermission(
context,
Permission.photos.value,
theme,
)) {
return;
}
}
final convID = widget.conversationID; final convID = widget.conversationID;
final convType = widget.conversationType; final convType = widget.conversationType;
final pickedFile = await CameraPicker.pickFromCamera(context, final pickedFile = await CameraPicker.pickFromCamera(context,
@ -498,7 +525,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
// Toast.showToast(ToastType.fail, TIM_t("图片不能为空"), context); // Toast.showToast(ToastType.fail, TIM_t("图片不能为空"), context);
} }
} catch (error) { } catch (error) {
print("err: $error"); outputLogger.i("err: $error");
} }
} }
@ -524,7 +551,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
convType: convType), convType: convType),
context); context);
} catch (e) { } catch (e) {
print("_sendFileErr: ${e.toString()}"); outputLogger.i("_sendFileErr: ${e.toString()}");
} }
} }
@ -558,7 +585,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
convType: convType), convType: convType),
context); context);
} catch (e) { } catch (e) {
print("_sendFileErr: ${e.toString()}"); outputLogger.i("_sendFileErr: ${e.toString()}");
} }
} }
@ -589,7 +616,8 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
} }
String? option2 = result.files.single.path ?? ""; String? option2 = result.files.single.path ?? "";
print(TIM_t_para("选择成功{{option2}}", "选择成功$option2")(option2: option2)); outputLogger
.i(TIM_t_para("选择成功{{option2}}", "选择成功$option2")(option2: option2));
File file = File(result.files.single.path!); File file = File(result.files.single.path!);
final int size = file.lengthSync(); final int size = file.lengthSync();
@ -606,7 +634,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
throw TypeError(); throw TypeError();
} }
} catch (e) { } catch (e) {
print("_sendFileErr: ${e.toString()}"); outputLogger.i("_sendFileErr: ${e.toString()}");
} }
} }
@ -709,35 +737,35 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
runSpacing: 20, runSpacing: 20,
children: itemList(model, theme) children: itemList(model, theme)
.map((item) => InkWell( .map((item) => InkWell(
onTap: () { onTap: () {
if (item.onTap != null) { if (item.onTap != null) {
item.onTap!(context); item.onTap!(context);
} }
}, },
child: widget.morePanelConfig?.actionBuilder != null child: widget.morePanelConfig?.actionBuilder != null
? widget.morePanelConfig?.actionBuilder!(item) ? widget.morePanelConfig?.actionBuilder!(item)
: SizedBox( : SizedBox(
height: 94, height: 94,
width: 64, width: 64,
child: Column( child: Column(
children: [ children: [
Container( Container(
height: 64, height: 64,
width: 64, width: 64,
margin: const EdgeInsets.only(bottom: 4), margin: const EdgeInsets.only(bottom: 4),
decoration: const BoxDecoration( decoration: const BoxDecoration(
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(5))), BorderRadius.all(Radius.circular(5))),
child: item.icon, child: item.icon,
), ),
Text( Text(
item.title, item.title,
style: TextStyle( style: TextStyle(
fontSize: 12, color: theme.darkTextColor), fontSize: 12, color: theme.darkTextColor),
) )
], ],
), ),
))) )))
.toList(), .toList(),
), ),
), ),

View File

@ -15,6 +15,7 @@ import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/sound_record.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/sound_record.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
class SendSoundMessage extends StatefulWidget { class SendSoundMessage extends StatefulWidget {
/// conversation ID /// conversation ID
@ -255,12 +256,12 @@ class _SendSoundMessageState extends TIMUIKitState<SendSoundMessage> {
path: soundPath!, duration: recordDuration!.ceil(), model: model); path: soundPath!, duration: recordDuration!.ceil(), model: model);
} }
} else if (status == "onStart") { } else if (status == "onStart") {
print("start record"); outputLogger.i("start record");
setState(() { setState(() {
isRecording = true; isRecording = true;
}); });
} else { } else {
print(status); outputLogger.i(status);
} }
}); });
final amplitudesResponseSubscription = final amplitudesResponseSubscription =

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,6 @@ import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_send_sound_message.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_send_sound_message.dart';
import 'package:extended_text_field/extended_text_field.dart'; import 'package:extended_text_field/extended_text_field.dart';
import 'package:tencent_keyboard_visibility/tencent_keyboard_visibility.dart'; import 'package:tencent_keyboard_visibility/tencent_keyboard_visibility.dart';
@ -91,7 +90,9 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget {
final VoidCallback goDownBottom; final VoidCallback goDownBottom;
final List customEmojiStickerList; final List<CustomEmojiFaceData> customEmojiStickerList;
final List<CustomStickerPackage> stickerPackageList;
const TIMUIKitTextFieldLayoutNarrow( const TIMUIKitTextFieldLayoutNarrow(
{Key? key, {Key? key,
@ -125,7 +126,8 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget {
required this.showMorePanel, required this.showMorePanel,
this.hintText, this.hintText,
required this.customEmojiStickerList, required this.customEmojiStickerList,
this.controller}) this.controller,
required this.stickerPackageList})
: super(key: key); : super(key: key);
@override @override
@ -185,51 +187,71 @@ class _TIMUIKitTextFieldLayoutNarrowState
} }
} }
Widget _getBottomContainer() { Widget _getBottomContainer(TUITheme theme) {
if (showEmojiPanel) { if (showEmojiPanel) {
return widget.customStickerPanel != null return widget.customStickerPanel != null
? widget.customStickerPanel!( ? widget.customStickerPanel!(
sendTextMessage: () { sendTextMessage: () {
widget.onEmojiSubmitted(); widget.onEmojiSubmitted();
setSendButton(); setSendButton();
}, },
sendFaceMessage: widget.onCustomEmojiFaceSubmitted, sendFaceMessage: widget.onCustomEmojiFaceSubmitted,
deleteText: () { deleteText: () {
widget.backSpaceText(); widget.backSpaceText();
setSendButton(); setSendButton();
}, },
addText: (int unicode) { addText: (int unicode) {
final newText = String.fromCharCode(unicode); final newText = String.fromCharCode(unicode);
widget.addStickerToText(newText); widget.addStickerToText(newText);
setSendButton(); setSendButton();
// handleSetDraftText(); // handleSetDraftText();
}, },
addCustomEmojiText: ((String singleEmojiName) { addCustomEmojiText: ((String singleEmojiName) {
String? emojiName = singleEmojiName.split('.png')[0]; String? emojiName = singleEmojiName.split('.png')[0];
if (widget.isUseDefaultEmoji && if (widget.isUseDefaultEmoji &&
widget.languageType == 'zh' && widget.languageType == 'zh' &&
ConstData.emojiMapList[emojiName] != null && TUIKitStickerConstData.emojiMapList[emojiName] != null &&
ConstData.emojiMapList[emojiName] != '') { TUIKitStickerConstData.emojiMapList[emojiName] != '') {
emojiName = ConstData.emojiMapList[emojiName]; emojiName = TUIKitStickerConstData.emojiMapList[emojiName];
} }
final newText = '[$emojiName]'; final newText = '[$emojiName]';
widget.addStickerToText(newText); widget.addStickerToText(newText);
setSendButton(); setSendButton();
}), }),
defaultCustomEmojiStickerList: defaultCustomEmojiStickerList: widget.isUseDefaultEmoji
widget.isUseDefaultEmoji ? ConstData.emojiList : []) ? TUIKitStickerConstData.emojiList
: EmojiPanel(onTapEmoji: (unicode) { : [])
final newText = String.fromCharCode(unicode); : StickerPanel(
widget.addStickerToText(newText); isWideScreen: false,
setSendButton(); sendTextMsg: () {
// handleSetDraftText(); widget.onEmojiSubmitted();
}, onSubmitted: () { setSendButton();
widget.onEmojiSubmitted(); },
setSendButton(); sendFaceMsg: widget.onCustomEmojiFaceSubmitted,
}, delete: () { deleteText: () {
widget.backSpaceText(); widget.backSpaceText();
setSendButton(); setSendButton();
}); },
addText: (int unicode) {
final newText = String.fromCharCode(unicode);
widget.addStickerToText(newText);
setSendButton();
// handleSetDraftText();
},
addCustomEmojiText: ((String singleEmojiName) {
String? emojiName = singleEmojiName.split('.png')[0];
if (widget.isUseDefaultEmoji &&
widget.languageType == 'zh' &&
TUIKitStickerConstData.emojiMapList[emojiName] != null &&
TUIKitStickerConstData.emojiMapList[emojiName] != '') {
emojiName = TUIKitStickerConstData.emojiMapList[emojiName];
}
final newText = '[$emojiName]';
widget.addStickerToText(newText);
setSendButton();
}),
customStickerPackageList: widget.stickerPackageList,
lightPrimaryColor: theme.lightPrimaryColor);
} }
if (showMore) { if (showMore) {
@ -310,23 +332,22 @@ class _TIMUIKitTextFieldLayoutNarrowState
}); });
}; };
} }
String getAbstractMessage(V2TimMessage message) { String getAbstractMessage(V2TimMessage message) {
final String? customAbstractMessage = widget final String? customAbstractMessage =
.model.abstractMessageBuilder != null ? widget.model widget.model.abstractMessageBuilder != null
.abstractMessageBuilder!(widget.model.repliedMessage!) : null; ? widget.model.abstractMessageBuilder!(widget.model.repliedMessage!)
return customAbstractMessage ?? MessageUtils : null;
.getAbstractMessageAsync( return customAbstractMessage ??
widget.model.repliedMessage!, widget.model.groupMemberList ?? []); MessageUtils.getAbstractMessageAsync(
widget.model.repliedMessage!, widget.model.groupMemberList ?? []);
} }
_buildRepliedMessage(V2TimMessage? repliedMessage) { _buildRepliedMessage(V2TimMessage? repliedMessage) {
final haveRepliedMessage = repliedMessage != null; final haveRepliedMessage = repliedMessage != null;
if (haveRepliedMessage) { if (haveRepliedMessage) {
final text = final text =
"${MessageUtils.getDisplayName( "${MessageUtils.getDisplayName(widget.model.repliedMessage!)}:${getAbstractMessage(repliedMessage)}";
widget.model.repliedMessage!)}:${getAbstractMessage(
repliedMessage
)}";
return Container( return Container(
color: widget.backgroundColor ?? hexToColor("f5f5f6"), color: widget.backgroundColor ?? hexToColor("f5f5f6"),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
@ -514,8 +535,19 @@ class _TIMUIKitTextFieldLayoutNarrowState
.isWeb .isWeb
? null ? null
: DefaultSpecialTextSpanBuilder( : DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji: isUseQQPackage: (widget
.model
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji, widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget
.model
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: customEmojiStickerList:
widget.customEmojiStickerList, widget.customEmojiStickerList,
showAtBackground: true, showAtBackground: true,
@ -609,9 +641,7 @@ class _TIMUIKitTextFieldLayoutNarrowState
height: max(_getBottomHeight(), 0.0), height: max(_getBottomHeight(), 0.0),
child: ListView( child: ListView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: [ children: [_getBottomContainer(theme)],
_getBottomContainer()
],
), ),
), ),
], ],

View File

@ -25,7 +25,6 @@ import 'package:tencent_cloud_chat_uikit/ui/utils/screen_shot.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/drag_widget.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/drag_widget.dart';
import 'package:extended_text_field/extended_text_field.dart'; import 'package:extended_text_field/extended_text_field.dart';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
@ -36,6 +35,7 @@ import 'package:flutter_svg/flutter_svg.dart';
// ignore: unnecessary_import // ignore: unnecessary_import
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
class DesktopControlBarItem { class DesktopControlBarItem {
final String item; final String item;
@ -147,11 +147,13 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
final VoidCallback goDownBottom; final VoidCallback goDownBottom;
final List customEmojiStickerList; final List<CustomEmojiFaceData> customEmojiStickerList;
/// Conversation need search /// Conversation need search
final V2TimConversation currentConversation; final V2TimConversation currentConversation;
final List<CustomStickerPackage> stickerPackageList;
const TIMUIKitTextFieldLayoutWide( const TIMUIKitTextFieldLayoutWide(
{Key? key, {Key? key,
this.customStickerPanel, this.customStickerPanel,
@ -186,7 +188,8 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
this.controller, this.controller,
required this.currentConversation, required this.currentConversation,
required this.theme, required this.theme,
required this.chatConfig}) required this.chatConfig,
required this.stickerPackageList})
: super(key: key); : super(key: key);
@override @override
@ -230,7 +233,7 @@ class _TIMUIKitTextFieldLayoutWideState
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print(e); outputLogger.i(e);
} }
generateDefaultControlBarItems(); generateDefaultControlBarItems();
} }
@ -243,7 +246,7 @@ class _TIMUIKitTextFieldLayoutWideState
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print("Paste image failed: ${e.toString()}"); outputLogger.i("Paste image failed: ${e.toString()}");
} }
} }
@ -387,25 +390,60 @@ class _TIMUIKitTextFieldLayoutWideState
String? emojiName = singleEmojiName.split('.png')[0]; String? emojiName = singleEmojiName.split('.png')[0];
if (widget.isUseDefaultEmoji && if (widget.isUseDefaultEmoji &&
widget.languageType == 'zh' && widget.languageType == 'zh' &&
ConstData.emojiMapList[emojiName] != null && TUIKitStickerConstData.emojiMapList[emojiName] !=
ConstData.emojiMapList[emojiName] != '') { null &&
emojiName = ConstData.emojiMapList[emojiName]; TUIKitStickerConstData.emojiMapList[emojiName] !=
'') {
emojiName =
TUIKitStickerConstData.emojiMapList[emojiName];
} }
final newText = '[$emojiName]'; final newText = '[$emojiName]';
widget.addStickerToText(newText); widget.addStickerToText(newText);
entry?.remove(); entry?.remove();
entry = null; entry = null;
}), }),
defaultCustomEmojiStickerList: defaultCustomEmojiStickerList: widget.isUseDefaultEmoji
widget.isUseDefaultEmoji ? ConstData.emojiList : []) ? TUIKitStickerConstData.emojiList
: EmojiPanel(onTapEmoji: (unicode) { : [])
final newText = String.fromCharCode(unicode); : StickerPanel(
widget.addStickerToText(newText); isWideScreen: true,
}, onSubmitted: () { height: widget.chatConfig.desktopStickerPanelHeight,
widget.onEmojiSubmitted(); width: 350,
}, delete: () { sendTextMsg: null,
widget.backSpaceText(); sendFaceMsg: (_, __){
}), widget.onCustomEmojiFaceSubmitted(_, __);
entry?.remove();
entry = null;
},
deleteText: () {
widget.backSpaceText();
},
addText: (int unicode) {
final newText = String.fromCharCode(unicode);
widget.addStickerToText(newText);
entry?.remove();
entry = null;
},
addCustomEmojiText: ((String singleEmojiName) {
String? emojiName = singleEmojiName.split('.png')[0];
if (widget.isUseDefaultEmoji &&
widget.languageType == 'zh' &&
TUIKitStickerConstData.emojiMapList[emojiName] !=
null &&
TUIKitStickerConstData.emojiMapList[emojiName] !=
'') {
emojiName =
TUIKitStickerConstData.emojiMapList[emojiName];
}
final newText = '[$emojiName]';
widget.addStickerToText(newText);
entry?.remove();
entry = null;
}),
customStickerPackageList: widget.stickerPackageList,
bottomColor: theme.weakBackgroundColor,
backgroundColor: theme.wideBackgroundColor,
lightPrimaryColor: theme.lightPrimaryColor),
), ),
)); ));
}); });
@ -477,7 +515,7 @@ class _TIMUIKitTextFieldLayoutWideState
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print("_sendFileErr: ${e.toString()}"); outputLogger.i("_sendFileErr: ${e.toString()}");
} }
} }
@ -568,7 +606,7 @@ class _TIMUIKitTextFieldLayoutWideState
context); context);
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print("_sendFileErr: ${e.toString()}"); outputLogger.i("_sendFileErr: ${e.toString()}");
} }
} }
@ -603,7 +641,7 @@ class _TIMUIKitTextFieldLayoutWideState
context); context);
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
print("_sendFileErr: ${e.toString()}"); outputLogger.i("_sendFileErr: ${e.toString()}");
} }
} }
@ -730,7 +768,7 @@ class _TIMUIKitTextFieldLayoutWideState
} }
} catch (err) { } catch (err) {
// ignore: avoid_print // ignore: avoid_print
print("send media err: $err"); outputLogger.i("send media err: $err");
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO, type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频文件异常"), infoRecommendText: TIM_t("视频文件异常"),
@ -1051,7 +1089,19 @@ class _TIMUIKitTextFieldLayoutWideState
specialTextSpanBuilder: PlatformUtils().isWeb specialTextSpanBuilder: PlatformUtils().isWeb
? null ? null
: DefaultSpecialTextSpanBuilder( : DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji: widget.isUseDefaultEmoji, isUseQQPackage: (widget
.model
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true) ||
widget.isUseDefaultEmoji,
isUseTencentCloudChatPackage: widget
.model
.chatConfig
.stickerPanelConfig
?.useTencentCloudChatStickerPackage ??
true,
customEmojiStickerList: customEmojiStickerList:
widget.customEmojiStickerList, widget.customEmojiStickerList,
showAtBackground: true, showAtBackground: true,

View File

@ -18,6 +18,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/constants/history_message_constant.dart';
import 'package:tencent_cloud_chat_uikit/ui/controller/tim_uikit_chat_controller.dart'; import 'package:tencent_cloud_chat_uikit/ui/controller/tim_uikit_chat_controller.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/frame.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/frame.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/at_member_panel.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/at_member_panel.dart';
@ -138,7 +139,8 @@ class TIMUIKitChat extends StatefulWidget {
/// The top fixed widget. /// The top fixed widget.
final Widget? topFixWidget; final Widget? topFixWidget;
final List customEmojiStickerList; /// Specify the custom small png emoji packages.
final List<CustomEmojiFaceData> customEmojiStickerList;
final Widget? customAppBar; final Widget? customAppBar;
@ -151,17 +153,26 @@ class TIMUIKitChat extends StatefulWidget {
/// replacing the default message hover action bar. /// replacing the default message hover action bar.
/// Applicable only on desktop platforms. /// Applicable only on desktop platforms.
/// If provided, the default message action functionality will appear in the right-click context menu instead. /// If provided, the default message action functionality will appear in the right-click context menu instead.
final Widget Function(V2TimMessage message)? customMessageHoverBarOnDesktop; /// Returns `null` to use default hover bar.
final Widget? Function(V2TimMessage message)? customMessageHoverBarOnDesktop;
/// Custom text field /// Custom text field
final Widget Function(BuildContext context)? textFieldBuilder; final Widget Function(BuildContext context)? textFieldBuilder;
/// An optional parameter `groupMemberList` can be provided.
/// `groupMemberList` accepts a list of nullable `V2TimGroupMemberFullInfo` objects.
/// The purpose of this parameter is to allow the client to supply a pre-fetched list
/// of group member information. If this list is provided, it will not make
/// additional network requests to fetch the group member information internally.
List<V2TimGroupMemberFullInfo?>? groupMemberList;
TIMUIKitChat( TIMUIKitChat(
{Key? key, {Key? key,
this.groupID, this.groupID,
required this.conversation, required this.conversation,
this.conversationID, this.conversationID,
this.conversationType, this.conversationType,
this.groupMemberList,
this.conversationShowName, this.conversationShowName,
this.abstractMessageBuilder, this.abstractMessageBuilder,
this.onTapAvatar, this.onTapAvatar,
@ -241,11 +252,9 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
model.abstractMessageBuilder = widget.abstractMessageBuilder; model.abstractMessageBuilder = widget.abstractMessageBuilder;
model.onTapAvatar = widget.onTapAvatar; model.onTapAvatar = widget.onTapAvatar;
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
if (kProfileMode) { widget.endTime = DateTime.now().millisecondsSinceEpoch;
widget.endTime = DateTime.now().millisecondsSinceEpoch; int timeSpend = widget.endTime - widget.startTime;
int timeSpend = widget.endTime - widget.startTime; outputLogger.i("Page render time:$timeSpend ms");
print("Page render time:$timeSpend ms");
}
}); });
Future.delayed(const Duration(milliseconds: 500), () { Future.delayed(const Duration(milliseconds: 500), () {
updateDraft(); updateDraft();
@ -372,6 +381,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
scrollController: autoController, scrollController: autoController,
textFieldController: textFieldController, textFieldController: textFieldController,
conversationID: _getConvID(), conversationID: _getConvID(),
groupMemberList: widget.groupMemberList,
conversationType: _getConvType(), conversationType: _getConvType(),
lifeCycle: widget.lifeCycle, lifeCycle: widget.lifeCycle,
config: widget.config, config: widget.config,
@ -408,7 +418,9 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
model.loadGroupInfo(_getConvID()); model.loadGroupInfo(_getConvID());
break; break;
case UpdateType.memberList: case UpdateType.memberList:
model.loadGroupMemberList(groupID: _getConvID()); if(widget.groupMemberList == null){
model.loadGroupMemberList(groupID: _getConvID());
}
model.loadGroupInfo(_getConvID()); model.loadGroupInfo(_getConvID());
break; break;
default: default:
@ -416,6 +428,27 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
} }
} }
List<CustomEmojiFaceData> customImageSmallPngEmojiPackages = [];
if (widget.config?.stickerPanelConfig?.customStickerPackages !=
null &&
widget.config!.stickerPanelConfig!.customStickerPackages
.isNotEmpty) {
customImageSmallPngEmojiPackages = widget
.config!.stickerPanelConfig!.customStickerPackages
.where((element) => element.isEmoji == true)
.map((e) {
return CustomEmojiFaceData(
name: e.name,
isEmoji: true,
icon: e.menuItem.url ?? "",
list: e.stickerList.map((e) => e.url ?? "").toList());
}).toList();
}
if (customImageSmallPngEmojiPackages.isEmpty) {
customImageSmallPngEmojiPackages
.addAll(widget.customEmojiStickerList);
}
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
textFieldController.hideAllPanel(); textFieldController.hideAllPanel();
@ -538,7 +571,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
model: model, model: model,
controller: textFieldController, controller: textFieldController,
customEmojiStickerList: customEmojiStickerList:
widget.customEmojiStickerList, customImageSmallPngEmojiPackages,
isUseDefaultEmoji: isUseDefaultEmoji:
widget.config!.isUseDefaultEmoji, widget.config!.isUseDefaultEmoji,
customStickerPanel: customStickerPanel:
@ -632,10 +665,18 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
final AutoScrollController? scrollController; final AutoScrollController? scrollController;
/// An optional parameter `groupMemberList` can be provided.
/// `groupMemberList` accepts a list of nullable `V2TimGroupMemberFullInfo` objects.
/// The purpose of this parameter is to allow the client to supply a pre-fetched list
/// of group member information. If this list is provided, it will not make
/// additional network requests to fetch the group member information internally.
List<V2TimGroupMemberFullInfo?>? groupMemberList;
TIMUIKitChatProviderScope( TIMUIKitChatProviderScope(
{Key? key, {Key? key,
this.child, this.child,
this.providers, this.providers,
this.groupMemberList,
this.textFieldController, this.textFieldController,
required this.builder, required this.builder,
this.model, this.model,
@ -665,6 +706,7 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
(String value) { (String value) {
textFieldController?.textEditingController?.text = value; textFieldController?.textEditingController?.text = value;
}, },
preGroupMemberList: groupMemberList,
groupID: groupID, groupID: groupID,
); );
model?.showC2cMessageEditStatus = (conversationType == ConvType.c2c model?.showC2cMessageEditStatus = (conversationType == ConvType.c2c

View File

@ -22,7 +22,41 @@ class TimeDividerConfig {
TimeDividerConfig({this.timeInterval, this.timestampParser}); TimeDividerConfig({this.timeInterval, this.timestampParser});
} }
/// StickerPanelConfig is a configuration class for the sticker panel component.
/// It allows customization of specific features such as display options for the
/// message area, sticker packages, unicode emoji lists, and custom sticker packages.
class StickerPanelConfig {
/// Determines whether to use the QQ Sticker Package.
/// Default value: true
final bool useQQStickerPackage;
/// Determines whether to use the Tencent Cloud Chat Sticker Package.
/// Default value: true
final bool useTencentCloudChatStickerPackage;
/// A list of unicode emoji, represented as integers.
/// Default value: a list of common Unicode Emojis.
/// To exclude Unicode Emoji from the display, pass an empty list.
final List<int> unicodeEmojiList;
/// A list of CustomStickerPackage instances, where each instance represents a sticker package.
/// Default value: an empty list.
final List<CustomStickerPackage> customStickerPackages;
StickerPanelConfig({
this.useQQStickerPackage = true,
this.useTencentCloudChatStickerPackage = true,
this.unicodeEmojiList = TUIKitStickerConstData.defaultUnicodeEmojiList,
this.customStickerPackages = const [],
});
}
class TIMUIKitChatConfig { class TIMUIKitChatConfig {
/// A StickerPanelConfig instance to configure the sticker panel's behavior and appearance.
/// This includes options such as display settings, usage of specific sticker packages,
/// unicode emoji lists, and custom sticker packages.
final StickerPanelConfig? stickerPanelConfig;
/// Customize the time divider among the two messages. /// Customize the time divider among the two messages.
final TimeDividerConfig? timeDividerConfig; final TimeDividerConfig? timeDividerConfig;
@ -135,7 +169,6 @@ class TIMUIKitChatConfig {
final String Function(String data)? faceURISuffix; final String Function(String data)? faceURISuffix;
/// Controls whether text and replied messages can be displayed with Markdown formatting. /// Controls whether text and replied messages can be displayed with Markdown formatting.
/// When enabled, small image stickers, including QQ stickers, will not work in message items.
/// Also, when enabled, `isEnableTextSelection` will not works. /// Also, when enabled, `isEnableTextSelection` will not works.
/// [Default]: false. /// [Default]: false.
final bool isSupportMarkdownForTextMessage; final bool isSupportMarkdownForTextMessage;
@ -210,6 +243,7 @@ class TIMUIKitChatConfig {
{this.onTapLink, {this.onTapLink,
this.timeDividerConfig, this.timeDividerConfig,
this.desktopStickerPanelHeight = 400, this.desktopStickerPanelHeight = 400,
this.stickerPanelConfig,
this.isGroupAdminRecallEnabled = false, this.isGroupAdminRecallEnabled = false,
this.isAutoReportRead = true, this.isAutoReportRead = true,
this.faceURIPrefix, this.faceURIPrefix,

View File

@ -88,13 +88,13 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
Widget _getTimeStringForChatWidget(BuildContext context, TUITheme theme) { Widget _getTimeStringForChatWidget(BuildContext context, TUITheme theme) {
try { try {
if (draftTimestamp != null && draftTimestamp != 0) { if (draftTimestamp != null && draftTimestamp != 0) {
return Text(TimeAgo().getTimeStringForChat(draftTimestamp as int), return Text(TimeAgo().getTimeStringForChat(draftTimestamp as int) ?? "",
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: theme.conversationItemTitmeTextColor, color: theme.conversationItemTitmeTextColor,
)); ));
} else if (lastMsg != null) { } else if (lastMsg != null) {
return Text(TimeAgo().getTimeStringForChat(lastMsg!.timestamp as int), return Text(TimeAgo().getTimeStringForChat(lastMsg!.timestamp as int) ?? "",
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
color: theme.conversationItemTitmeTextColor, color: theme.conversationItemTitmeTextColor,

View File

@ -5,6 +5,7 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
@ -83,7 +84,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
}); });
} }
} else { } else {
final newText = await _getLastMsgShowText(widget.lastMsg, widget.context); final newText = await _getLastMsgShowText(widget.lastMsg, widget.context) ?? "";
if (mounted) { if (mounted) {
setState(() { setState(() {
groupTipsAbstractText = newText; groupTipsAbstractText = newText;
@ -92,7 +93,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
} }
} }
Future<String> _getLastMsgShowText( Future<String?> _getLastMsgShowText(
V2TimMessage? message, BuildContext context) async { V2TimMessage? message, BuildContext context) async {
final msgType = message!.elemType; final msgType = message!.elemType;
switch (msgType) { switch (msgType) {
@ -120,7 +121,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
case MessageElemType.V2TIM_ELEM_TYPE_MERGER: case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
return TIM_t("[聊天记录]"); return TIM_t("[聊天记录]");
default: default:
return TIM_t("未知消息"); return null;
} }
} }
@ -162,7 +163,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
Text(_getAtMessage(), Text(_getAtMessage(),
style: TextStyle( style: TextStyle(
color: theme.cautionColor, fontSize: widget.fontSize)), color: theme.cautionColor, fontSize: widget.fontSize)),
Expanded( if(TencentUtils.checkString(groupTipsAbstractText) != null)Expanded(
child: Text( child: Text(
groupTipsAbstractText, groupTipsAbstractText,
softWrap: true, softWrap: true,

View File

@ -143,6 +143,8 @@ class _ContactListState extends TIMUIKitState<ContactList> {
onChanged: (isChecked) { onChanged: (isChecked) {
if (isChecked) { if (isChecked) {
if (selectedMemberIsOverFlow()) { if (selectedMemberIsOverFlow()) {
selectedMember = [item];
setState(() {});
return; return;
} }
selectedMember.add(item); selectedMember.add(item);
@ -285,6 +287,8 @@ class _ContactListState extends TIMUIKitState<ContactList> {
selectedMember.remove(memberInfo); selectedMember.remove(memberInfo);
} else { } else {
if (selectedMemberIsOverFlow()) { if (selectedMemberIsOverFlow()) {
selectedMember = [memberInfo];
setState(() {});
return; return;
} }
selectedMember.add(memberInfo); selectedMember.add(memberInfo);

View File

@ -212,7 +212,7 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
} }
_doubleClickAnimationListener = () { _doubleClickAnimationListener = () {
//print(_animation.value); //outputLogger.i(_animation.value);
state.handleDoubleTap( state.handleDoubleTap(
scale: _doubleClickAnimation!.value, scale: _doubleClickAnimation!.value,
doubleTapPosition: pointerDownPosition); doubleTapPosition: pointerDownPosition);

View File

@ -0,0 +1,29 @@
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/emoji_text.dart';
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
RegExp emojiExp = RegExp(r'\[([\u4e00-\u9fa5A-Za-z0-9]+)\]');
String mdTextCompiler(String originalText, {
bool isUseQQPackage = false,
bool isUseTencentCloudChatPackage = false,
List<CustomEmojiFaceData> customEmojiStickerList = const [],
}) {
String text = originalText;
final EmojiUtil emojiUtil = EmojiUtil(
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
isUseQQPackage: isUseQQPackage,
customEmojiStickerList: customEmojiStickerList);
text = text.replaceAllMapped(emojiExp, (match) {
String key = match.group(0)!;
// Check if the emoji exists in the emoji map
if (emojiUtil.emojiMap.containsKey(key)) {
String assetPath = emojiUtil.emojiMap[key]!;
return '![sticker](resource:$assetPath#22x22)';
}
return key;
});
return text;
}

View File

@ -3,6 +3,7 @@ import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_preview.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_preview.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_text.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/widgets/link_text.dart';
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
import 'models/link_preview_content.dart'; import 'models/link_preview_content.dart';
@ -11,11 +12,15 @@ class LinkPreviewEntry {
static LinkPreviewText? getHyperlinksText(String messageText, bool isMarkdown, static LinkPreviewText? getHyperlinksText(String messageText, bool isMarkdown,
{Function(String)? onLinkTap, {Function(String)? onLinkTap,
bool isEnableTextSelection = false, bool isEnableTextSelection = false,
bool isUseDefaultEmoji = false, bool isUseQQPackage = false,
List customEmojiStickerList = const []}) { bool isUseTencentCloudChatPackage = false,
List<CustomEmojiFaceData> customEmojiStickerList = const []}) {
return ({TextStyle? style}) { return ({TextStyle? style}) {
return isMarkdown return isMarkdown
? LinkTextMarkdown( ? LinkTextMarkdown(
isUseQQPackage: isUseQQPackage,
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
customEmojiStickerList: customEmojiStickerList,
isEnableTextSelection: isEnableTextSelection, isEnableTextSelection: isEnableTextSelection,
messageText: addSpaceAfterLeftBracket( messageText: addSpaceAfterLeftBracket(
addSpaceBeforeHttp(replaceSingleNewlineWithTwo(messageText))), addSpaceBeforeHttp(replaceSingleNewlineWithTwo(messageText))),
@ -26,7 +31,8 @@ class LinkPreviewEntry {
messageText: messageText, messageText: messageText,
style: style, style: style,
onLinkTap: onLinkTap, onLinkTap: onLinkTap,
isUseDefaultEmoji: isUseDefaultEmoji, isUseQQPackage: isUseQQPackage,
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
customEmojiStickerList: customEmojiStickerList); customEmojiStickerList: customEmojiStickerList);
}; };
} }

View File

@ -0,0 +1,91 @@
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<String> parts = src.split('#');
if (parts.isEmpty) {
return const SizedBox();
}
final String path = parts.first;
double? width;
double? height;
if (parts.length == 2) {
final List<String> 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);
}
}

View File

@ -5,10 +5,17 @@ import 'package:extended_text/extended_text.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.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_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/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
import 'package:markdown/markdown.dart' as md; import 'package:markdown/markdown.dart' as md;
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
typedef ImageBuilder = Widget Function(
Uri uri, String? imageDirectory, double? width, double? height);
class LinkTextMarkdown extends TIMStatelessWidget { class LinkTextMarkdown extends TIMStatelessWidget {
/// Callback for when link is tapped /// Callback for when link is tapped
@ -22,9 +29,18 @@ class LinkTextMarkdown extends TIMStatelessWidget {
final bool? isEnableTextSelection; final bool? isEnableTextSelection;
final bool isUseQQPackage;
final bool isUseTencentCloudChatPackage;
final List<CustomEmojiFaceData> customEmojiStickerList;
const LinkTextMarkdown( const LinkTextMarkdown(
{Key? key, {Key? key,
required this.messageText, required this.messageText,
this.isUseQQPackage = false,
this.isUseTencentCloudChatPackage = false,
this.customEmojiStickerList = const [],
this.isEnableTextSelection, this.isEnableTextSelection,
this.onLinkTap, this.onLinkTap,
this.style}) this.style})
@ -33,7 +49,10 @@ class LinkTextMarkdown extends TIMStatelessWidget {
@override @override
Widget timBuild(BuildContext context) { Widget timBuild(BuildContext context) {
return MarkdownBody( return MarkdownBody(
data: messageText, data: mdTextCompiler(messageText,
isUseQQPackage: isUseQQPackage,
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
customEmojiStickerList: customEmojiStickerList),
selectable: isEnableTextSelection ?? false, selectable: isEnableTextSelection ?? false,
styleSheet: MarkdownStyleSheet.fromTheme(ThemeData( styleSheet: MarkdownStyleSheet.fromTheme(ThemeData(
textTheme: TextTheme( textTheme: TextTheme(
@ -67,9 +86,11 @@ class LinkText extends TIMStatelessWidget {
/// text style for default words /// text style for default words
final TextStyle? style; final TextStyle? style;
final bool isUseDefaultEmoji; final bool isUseQQPackage;
final List customEmojiStickerList; final bool isUseTencentCloudChatPackage;
final List<CustomEmojiFaceData> customEmojiStickerList;
final bool? isEnableTextSelection; final bool? isEnableTextSelection;
@ -79,7 +100,8 @@ class LinkText extends TIMStatelessWidget {
this.onLinkTap, this.onLinkTap,
this.isEnableTextSelection, this.isEnableTextSelection,
this.style, this.style,
this.isUseDefaultEmoji = false, this.isUseQQPackage = false,
this.isUseTencentCloudChatPackage = false,
this.customEmojiStickerList = const []}) this.customEmojiStickerList = const []})
: super(key: key); : super(key: key);
@ -151,26 +173,10 @@ class LinkText extends TIMStatelessWidget {
}, },
style: style ?? const TextStyle(fontSize: 16.0), style: style ?? const TextStyle(fontSize: 16.0),
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder( specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji: isUseDefaultEmoji, isUseQQPackage: isUseQQPackage,
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
customEmojiStickerList: customEmojiStickerList, customEmojiStickerList: customEmojiStickerList,
showAtBackground: true, showAtBackground: true,
)); ));
} }
} }
class TextBuilder extends MarkdownElementBuilder {
@override
Widget? visitText(md.Text text, TextStyle? preferredStyle) {
return Text(text.textContent);
}
}
class RawHtmlSyntax extends md.InlineSyntax {
RawHtmlSyntax() : super(r'<.+?>');
@override
bool onMatch(md.InlineParser parser, Match match) {
parser.addNode(md.Text(match[0]!));
return true;
}
}

View File

@ -324,13 +324,15 @@ class TUIKitWidePopup {
if (isLocalResource) { if (isLocalResource) {
videoController = VideoPlayerController.file(File(mediaPath)); videoController = VideoPlayerController.file(File(mediaPath));
} else { } else {
videoController = VideoPlayerController.networkUrl(Uri.parse(mediaPath)); videoController =
VideoPlayerController.networkUrl(Uri.parse(mediaPath));
} }
await videoController.initialize(); await videoController.initialize();
aspectRatioFinal = videoController.value.aspectRatio; aspectRatioFinal = videoController.value.aspectRatio;
chewieController = ChewieController( chewieController = ChewieController(
allowFullScreen: false,
videoPlayerController: videoController, videoPlayerController: videoController,
aspectRatio: aspectRatioFinal, aspectRatio: aspectRatioFinal,
autoPlay: true, autoPlay: true,
@ -340,12 +342,9 @@ class TUIKitWidePopup {
mediaWidget = Chewie(controller: chewieController); mediaWidget = Chewie(controller: chewieController);
} else { } else {
mediaWidget = FittedBox( mediaWidget = isLocalResource
fit: BoxFit.contain, ? Image.file(File(mediaPath), fit: BoxFit.contain)
child: isLocalResource : Image.network(mediaPath, fit: BoxFit.contain);
? Image.file(File(mediaPath), fit: BoxFit.contain)
: Image.network(mediaPath, fit: BoxFit.contain),
);
} }
showDialog( showDialog(

View File

@ -442,7 +442,7 @@ packages:
source: hosted source: hosted
version: "11.0.2" version: "11.0.2"
fast_i18n: fast_i18n:
dependency: "direct dev" dependency: transitive
description: description:
name: fast_i18n name: fast_i18n
sha256: f0039a3c1f5f3b7deafefdbb5222d7eb1ee9c2c2fe1222b648b285711b2c7570 sha256: f0039a3c1f5f3b7deafefdbb5222d7eb1ee9c2c2fe1222b648b285711b2c7570
@ -811,6 +811,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0+4" version: "1.2.0+4"
logger:
dependency: "direct main"
description:
name: logger
sha256: "66cb048220ca51cf9011da69fa581e4ee2bed4be6e82870d9e9baae75739da49"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -1276,42 +1284,42 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: tencent_chat_i18n_tool name: tencent_chat_i18n_tool
sha256: ac8171d2574ed18babedd0cb67e937e255bf02fcb72f55408d033f74d4b11949 sha256: "0ee982e814bedd0aea4751b972901c6cfcfb224cfeb8e13ae02e43c0b8a58bbc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.3+2" version: "2.2.0"
tencent_cloud_chat_sdk: tencent_cloud_chat_sdk:
dependency: transitive dependency: transitive
description: description:
name: tencent_cloud_chat_sdk name: tencent_cloud_chat_sdk
sha256: f98bdb55164051e2b196cac6e2e79e60248ed8351dc5a91d25568712ccb15839 sha256: "013f8c9d96bbeed06d5fe971b7802d8ddf830c776332d6c6de6ccb9de8956d83"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.7" version: "5.1.8"
tencent_cloud_uikit_core: tencent_cloud_uikit_core:
dependency: "direct main" dependency: "direct main"
description: description:
name: tencent_cloud_uikit_core name: tencent_cloud_uikit_core
sha256: "0a0f43e4c4241b25d12a9e9f0ee91922ac800a42229d97e3d21d16041ace3104" sha256: acb3bae877428457318b8c5604a6c263957b6df3454ed3e30e8b6f620c6b2cd9
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.1.0"
tencent_im_base: tencent_im_base:
dependency: "direct main" dependency: "direct main"
description: description:
name: tencent_im_base name: tencent_im_base
sha256: "0db83050452486571ee4ac07280a2fb4bbc07d297a54235b5cf12e46e79267d0" sha256: bc5eb080090038d21c879480c06d3ed7cb4b1dcc2cbe894189613eadf08cf7c5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.1"
tencent_im_sdk_plugin_platform_interface: tencent_im_sdk_plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: tencent_im_sdk_plugin_platform_interface name: tencent_im_sdk_plugin_platform_interface
sha256: "6a1f053567246148ad40667f2ab71d82bcee0d5d0c12e587340d2796c342b87e" sha256: "1f9814d654dc1ad0a4cb62936f0849defac058c3bdca471472efc8b64b63cc5e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.21" version: "0.3.22"
tencent_keyboard_visibility: tencent_keyboard_visibility:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1356,10 +1364,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: tim_ui_kit_sticker_plugin name: tim_ui_kit_sticker_plugin
sha256: c9b0c1261bb51a5dee54bb50c6a106061b1bb10731b9c815fc5175afa60175e2 sha256: db8143aea26eda5feec5ec2efc5a31b2c56928cd807537778829698f3c4efec5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "3.0.1+1"
timing: timing:
dependency: transitive dependency: transitive
description: description:

View File

@ -1,6 +1,6 @@
name: tencent_cloud_chat_uikit name: tencent_cloud_chat_uikit
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences. description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
version: 2.1.3+1 version: 2.2.0
homepage: https://www.tencentcloud.com/products/im?from=pub homepage: https://www.tencentcloud.com/products/im?from=pub
repository: https://github.com/TencentCloud/tc-chat-uikit-flutter repository: https://github.com/TencentCloud/tc-chat-uikit-flutter
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
@ -62,12 +62,12 @@ dependencies:
uuid: ^3.0.6 uuid: ^3.0.6
tencent_open_file: ^4.0.10 tencent_open_file: ^4.0.10
tencent_keyboard_visibility: ^1.0.1 tencent_keyboard_visibility: ^1.0.1
tim_ui_kit_sticker_plugin: ^2.2.1 tim_ui_kit_sticker_plugin: ^3.0.1+1
tencent_im_base: ^2.0.1 tencent_im_base: ^3.0.1
fc_native_video_thumbnail: any fc_native_video_thumbnail: any
audioplayers: ^3.0.1 audioplayers: ^3.0.1
path: ^1.8.1 path: ^1.8.1
tencent_cloud_uikit_core: ^1.0.7 tencent_cloud_uikit_core: ^1.1.0
pasteboard: ^0.2.0 pasteboard: ^0.2.0
desktop_drop: ^0.4.1 desktop_drop: ^0.4.1
device_info_plus: any device_info_plus: any
@ -75,11 +75,11 @@ dependencies:
csslib: 0.17.2 csslib: 0.17.2
diff_match_patch: ^0.4.1 diff_match_patch: ^0.4.1
markdown: ^7.1.0 markdown: ^7.1.0
logger: ^2.0.1
dev_dependencies: dev_dependencies:
flutter_lints: ^1.0.0 flutter_lints: ^1.0.0
build_runner: any build_runner: any
fast_i18n: ^5.12.2
lints: ^1.0.1 lints: ^1.0.1
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the