feat: Upgrade UIKit to 2.2.0
This commit is contained in:
parent
2fefab6196
commit
d0b60c68a8
24
CHANGELOG.md
24
CHANGELOG.md
|
|
@ -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
|
||||
|
||||
### New Features
|
||||
|
|
|
|||
|
|
@ -728,6 +728,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0+4"
|
||||
logger:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "66cb048220ca51cf9011da69fa581e4ee2bed4be6e82870d9e9baae75739da49"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1193,41 +1201,41 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: tencent_chat_i18n_tool
|
||||
sha256: ac8171d2574ed18babedd0cb67e937e255bf02fcb72f55408d033f74d4b11949
|
||||
sha256: "0ee982e814bedd0aea4751b972901c6cfcfb224cfeb8e13ae02e43c0b8a58bbc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3+2"
|
||||
version: "2.2.0"
|
||||
tencent_cloud_chat_sdk:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tencent_cloud_chat_sdk
|
||||
sha256: f98bdb55164051e2b196cac6e2e79e60248ed8351dc5a91d25568712ccb15839
|
||||
sha256: "013f8c9d96bbeed06d5fe971b7802d8ddf830c776332d6c6de6ccb9de8956d83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.7"
|
||||
version: "5.1.8"
|
||||
tencent_cloud_chat_uikit:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "2.1.3-preview.16"
|
||||
version: "2.2.0"
|
||||
tencent_cloud_uikit_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tencent_cloud_uikit_core
|
||||
sha256: "0a0f43e4c4241b25d12a9e9f0ee91922ac800a42229d97e3d21d16041ace3104"
|
||||
sha256: acb3bae877428457318b8c5604a6c263957b6df3454ed3e30e8b6f620c6b2cd9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
version: "1.1.0"
|
||||
tencent_im_base:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tencent_im_base
|
||||
sha256: "0db83050452486571ee4ac07280a2fb4bbc07d297a54235b5cf12e46e79267d0"
|
||||
sha256: bc5eb080090038d21c879480c06d3ed7cb4b1dcc2cbe894189613eadf08cf7c5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "3.0.1"
|
||||
tencent_im_sdk_plugin_desktop:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1240,10 +1248,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: tencent_im_sdk_plugin_platform_interface
|
||||
sha256: "6a1f053567246148ad40667f2ab71d82bcee0d5d0c12e587340d2796c342b87e"
|
||||
sha256: "1f9814d654dc1ad0a4cb62936f0849defac058c3bdca471472efc8b64b63cc5e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.21"
|
||||
version: "0.3.22"
|
||||
tencent_im_sdk_plugin_web:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1296,10 +1304,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: tim_ui_kit_sticker_plugin
|
||||
sha256: c9b0c1261bb51a5dee54bb50c6a106061b1bb10731b9c815fc5175afa60175e2
|
||||
sha256: db8143aea26eda5feec5ec2efc5a31b2c56928cd807537778829698f3c4efec5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "3.0.1+1"
|
||||
transparent_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -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/services_locatar.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:uuid/uuid.dart';
|
||||
|
||||
|
|
@ -171,7 +172,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
|
||||
void initForEachConversation(ConvType convType, String convID,
|
||||
ValueChanged<String>? onChangeInputField,
|
||||
{String? groupID}) async {
|
||||
{String? groupID,
|
||||
List<V2TimGroupMemberFullInfo?>? preGroupMemberList}) async {
|
||||
if (_isInit) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -188,7 +190,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
if (conversationType == ConvType.group) {
|
||||
globalModel.refreshGroupApplicationList();
|
||||
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 {
|
||||
notifyListeners();
|
||||
}
|
||||
|
|
@ -274,12 +286,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
|
||||
// 加载聊天记录
|
||||
Future<bool> loadChatRecord({
|
||||
HistoryMsgGetTypeEnum? getType, // 获取聊天记录的方式
|
||||
int lastMsgSeq = -1, // 上一条消息的消息序号
|
||||
required int count, // 加载的消息数量
|
||||
String? lastMsgID, // 最后一条消息的ID
|
||||
LoadDirection direction =
|
||||
LoadDirection.previous, // 加载的方向,previous表示向上加载,latest表示向下加载
|
||||
HistoryMsgGetTypeEnum? getType,
|
||||
int lastMsgSeq = -1,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
LoadDirection direction = LoadDirection.previous,
|
||||
}) async {
|
||||
try {
|
||||
// 根据加载方向设置是否还能继续加载更多消息
|
||||
|
|
@ -349,20 +360,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
List<V2TimMessage> receivedList = await lifeCycle
|
||||
?.didGetHistoricalMessageList(response.messageList) ??
|
||||
response.messageList;
|
||||
|
||||
// 根据加载方向拼接消息列表
|
||||
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);
|
||||
}
|
||||
globalModel.loadingMessage.remove(conversationID);
|
||||
|
||||
// 更新聊天记录到全局model
|
||||
globalModel.setMessageList(
|
||||
|
|
@ -394,7 +392,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
return haveMoreData;
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print('loadChatRecord error: $e');
|
||||
outputLogger.i('loadChatRecord error: $e');
|
||||
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(
|
||||
{required String groupID, int count = 100, String? seq}) async {
|
||||
final String? nextSeq = await _loadGroupMemberListFunction(
|
||||
|
|
@ -629,7 +644,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
globalModel.updateMessage(
|
||||
sendMsgRes, convID, id, convType, groupType, setInputField);
|
||||
}
|
||||
if(lifeCycle?.messageDidSend != null){
|
||||
if (lifeCycle?.messageDidSend != null) {
|
||||
lifeCycle!.messageDidSend(sendMsgRes);
|
||||
}
|
||||
|
||||
|
|
@ -883,7 +898,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
globalModel.updateMessage(sendMsgRes, convID,
|
||||
messageInfoWithSender.id ?? "", convType, groupType, setInputField);
|
||||
if(lifeCycle?.messageDidSend != null){
|
||||
if (lifeCycle?.messageDidSend != null) {
|
||||
lifeCycle!.messageDidSend(sendMsgRes);
|
||||
}
|
||||
return sendMsgRes;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// ignore_for_file: unnecessary_getters_setters, avoid_print
|
||||
|
||||
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_cloud_chat_uikit/business_logic/life_cycle/group_profile_life_cycle.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) {
|
||||
final groupMemberListTemp = groupMemberListRes.memberInfoList ?? [];
|
||||
// TODO
|
||||
print(
|
||||
outputLogger.i(
|
||||
"loadGroupMemberListfinish,groupMemberListTemp, ${groupMemberListRes.nextSeq}, ${groupMemberListTemp.length}");
|
||||
_groupMemberList = [...?_groupMemberList, ...groupMemberListTemp];
|
||||
_groupMemberListSeq = groupMemberListRes.nextSeq ?? "0";
|
||||
|
|
|
|||
|
|
@ -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/ui/constants/history_message_constant.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
enum ConvType { none, c2c, group }
|
||||
|
||||
|
|
@ -107,7 +108,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
}
|
||||
|
||||
void addWaitingList(String msgID) {
|
||||
print("add to waiting list success");
|
||||
outputLogger.i("add to waiting list success");
|
||||
bool contains = false;
|
||||
for (Map<String, String> element in _waitingDownloadList) {
|
||||
String msgIDItem = element["msgID"] ?? "";
|
||||
|
|
@ -145,7 +146,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
isSnapshot: false,
|
||||
);
|
||||
|
||||
print("start another download");
|
||||
outputLogger.i("start another download");
|
||||
}
|
||||
|
||||
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() {
|
||||
_messageListMap.clear();
|
||||
_currentConversationList.clear();
|
||||
|
|
@ -416,10 +386,10 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
_preloadImageMap[msgItem.seq! +
|
||||
msgItem.timestamp.toString() +
|
||||
(msgItem.msgID ?? "")] = tempImg;
|
||||
print("cacheImage ${msgItem.msgID}");
|
||||
outputLogger.i("cacheImage ${msgItem.msgID}");
|
||||
}));
|
||||
} 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"] ?? "";
|
||||
if (msgIDItem.isNotEmpty) {
|
||||
if (msgID == msgIDItem) {
|
||||
print("remove download");
|
||||
outputLogger.i("remove download");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -737,12 +707,12 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
}
|
||||
|
||||
_onSendMessageProgress(V2TimMessage messagae, int progress) {
|
||||
print("message progress: $progress");
|
||||
outputLogger.i("message progress: $progress");
|
||||
}
|
||||
|
||||
Future<void> onMessageDownloadProgressCallback(
|
||||
V2TimMessageDownloadProgress messageProgress) async {
|
||||
print(messageProgress.toJson());
|
||||
outputLogger.i(messageProgress.toJson());
|
||||
final currentProgress = getMessageProgress(messageProgress.msgID);
|
||||
|
||||
if (messageProgress.isFinish && currentProgress < 100) {
|
||||
|
|
@ -811,7 +781,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
}
|
||||
}
|
||||
}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();
|
||||
outputLogger.i(returnValue.map((e) => e.toJson())
|
||||
.toList()
|
||||
.toString());
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:collection/collection.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/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_im_base/tencent_im_base.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/ui/utils/platform.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);
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ class CoreServicesImpl implements CoreServices {
|
|||
|
||||
setGlobalConfig(TIMUIKitConfig? config) {
|
||||
final TUISelfInfoViewModel selfInfoViewModel =
|
||||
serviceLocator<TUISelfInfoViewModel>();
|
||||
serviceLocator<TUISelfInfoViewModel>();
|
||||
final TUISettingModel settingModel = serviceLocator<TUISettingModel>();
|
||||
selfInfoViewModel.globalConfig = config;
|
||||
settingModel.init();
|
||||
|
|
@ -75,23 +76,28 @@ class CoreServicesImpl implements CoreServices {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<bool?> init({
|
||||
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
|
||||
ValueChanged<TIMCallback>? onTUIKitCallbackListener,
|
||||
required int sdkAppID,
|
||||
required LogLevelEnum loglevel,
|
||||
required V2TimSDKListener listener,
|
||||
LanguageEnum? language,
|
||||
String? extraLanguage,
|
||||
TIMUIKitConfig? config,
|
||||
Future<bool?> init(
|
||||
{
|
||||
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
|
||||
ValueChanged<TIMCallback>? onTUIKitCallbackListener,
|
||||
required int sdkAppID,
|
||||
required LogLevelEnum loglevel,
|
||||
required V2TimSDKListener listener,
|
||||
LanguageEnum? language,
|
||||
String? extraLanguage,
|
||||
TIMUIKitConfig? config,
|
||||
|
||||
/// 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,
|
||||
DeviceType? platform,
|
||||
VoidCallback? onWebLoginSuccess}) async {
|
||||
/// 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,
|
||||
DeviceType? platform,
|
||||
String? uikitLogPath,
|
||||
VoidCallback? onWebLoginSuccess}) async {
|
||||
if (platform != null) {
|
||||
TUIKitScreenUtils.deviceType = platform;
|
||||
}
|
||||
if (TencentUtils.checkString(uikitLogPath) != null) {
|
||||
logOutputGenerator(uikitLogPath!);
|
||||
}
|
||||
addIdentifier();
|
||||
if (extraLanguage != null) {
|
||||
Future.delayed(const Duration(milliseconds: 1), () {
|
||||
|
|
@ -167,13 +173,13 @@ class CoreServicesImpl implements CoreServices {
|
|||
|
||||
void addInitListener() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel =
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel =
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
final TUIChatGlobalModel tuiChatViewModel =
|
||||
serviceLocator<TUIChatGlobalModel>();
|
||||
serviceLocator<TUIChatGlobalModel>();
|
||||
final TUIGroupListenerModel tuiGroupListenerModel =
|
||||
serviceLocator<TUIGroupListenerModel>();
|
||||
serviceLocator<TUIGroupListenerModel>();
|
||||
|
||||
tuiFriendShipViewModel.addFriendListener();
|
||||
tuiConversationViewModel.setConversationListener();
|
||||
|
|
@ -183,13 +189,13 @@ class CoreServicesImpl implements CoreServices {
|
|||
|
||||
void removeListener() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel =
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel =
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
final TUIChatGlobalModel tuiChatViewModel =
|
||||
serviceLocator<TUIChatGlobalModel>();
|
||||
serviceLocator<TUIChatGlobalModel>();
|
||||
final TUIGroupListenerModel tuiGroupListenerModel =
|
||||
serviceLocator<TUIGroupListenerModel>();
|
||||
serviceLocator<TUIGroupListenerModel>();
|
||||
|
||||
tuiFriendShipViewModel.removeFriendshipListener();
|
||||
tuiConversationViewModel.removeConversationListener();
|
||||
|
|
@ -203,17 +209,16 @@ class CoreServicesImpl implements CoreServices {
|
|||
onCallback!(callbackValue);
|
||||
});
|
||||
} else {
|
||||
print(
|
||||
"TUIKit Callback: ${callbackValue.type} - ${callbackValue
|
||||
.stackTrace}");
|
||||
outputLogger.i(
|
||||
"TUIKit Callback: ${callbackValue.type} - ${callbackValue.stackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
initDataModel() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel =
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel =
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
|
||||
tuiFriendShipViewModel.initFriendShipModel();
|
||||
tuiConversationViewModel.initConversation();
|
||||
|
|
@ -221,11 +226,11 @@ class CoreServicesImpl implements CoreServices {
|
|||
|
||||
clearData() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel =
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel =
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
serviceLocator<TUIConversationViewModel>();
|
||||
final TUIChatGlobalModel tuiChatViewModel =
|
||||
serviceLocator<TUIChatGlobalModel>();
|
||||
serviceLocator<TUIChatGlobalModel>();
|
||||
|
||||
tuiFriendShipViewModel.clearData();
|
||||
tuiConversationViewModel.clearData();
|
||||
|
|
@ -235,18 +240,18 @@ class CoreServicesImpl implements CoreServices {
|
|||
updateUserStatusList(List<V2TimUserStatus> newUserStatusList) {
|
||||
try {
|
||||
final TUISelfInfoViewModel selfInfoViewModel =
|
||||
serviceLocator<TUISelfInfoViewModel>();
|
||||
serviceLocator<TUISelfInfoViewModel>();
|
||||
if (selfInfoViewModel.globalConfig?.isShowOnlineStatus == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel =
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
serviceLocator<TUIFriendShipViewModel>();
|
||||
final currentUserStatusList = tuiFriendShipViewModel.userStatusList;
|
||||
|
||||
for (int i = 0; i < newUserStatusList.length; i++) {
|
||||
final int indexInCurrentUserList = currentUserStatusList.indexWhere(
|
||||
(element) => element.userID == newUserStatusList[i].userID);
|
||||
(element) => element.userID == newUserStatusList[i].userID);
|
||||
if (indexInCurrentUserList == -1) {
|
||||
currentUserStatusList.add(newUserStatusList[i]);
|
||||
} else {
|
||||
|
|
@ -290,7 +295,7 @@ class CoreServicesImpl implements CoreServices {
|
|||
|
||||
if (TencentUtils.checkString(_userID) == null) {
|
||||
V2TimValueCallback<String> getLoginUserRes =
|
||||
await TencentImSDKPlugin.v2TIMManager.getLoginUser();
|
||||
await TencentImSDKPlugin.v2TIMManager.getLoginUser();
|
||||
if (getLoginUserRes.code == 0) {
|
||||
_userID = getLoginUserRes.data ?? "";
|
||||
}
|
||||
|
|
@ -319,7 +324,7 @@ class CoreServicesImpl implements CoreServices {
|
|||
_loginInfo =
|
||||
res?.data!.firstWhereOrNull((element) => element.userID == _userID);
|
||||
final TUISelfInfoViewModel selfInfoViewModel =
|
||||
serviceLocator<TUISelfInfoViewModel>();
|
||||
serviceLocator<TUISelfInfoViewModel>();
|
||||
if (_loginInfo != null) {
|
||||
selfInfoViewModel.setLoginInfo(_loginInfo);
|
||||
}
|
||||
|
|
@ -374,10 +379,10 @@ class CoreServicesImpl implements CoreServices {
|
|||
return TencentImSDKPlugin.v2TIMManager
|
||||
.getOfflinePushManager()
|
||||
.setOfflinePushConfig(
|
||||
businessID: businessID?.toDouble() ?? 0,
|
||||
token: token,
|
||||
isTPNSToken: isTPNSToken,
|
||||
);
|
||||
businessID: businessID?.toDouble() ?? 0,
|
||||
token: token,
|
||||
isTPNSToken: isTPNSToken,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -51,17 +51,21 @@ class TIMUIKitChatController {
|
|||
return model?.clearHistory();
|
||||
}
|
||||
|
||||
/// refresh the history message list manually;
|
||||
/// Please provide `convType` and `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||
/// Refresh the history message list manually;
|
||||
Future<bool> refreshCurrentHistoryList(
|
||||
[String? convID, ConvType? convType]) async {
|
||||
if (convID != null && convType != null) {
|
||||
return globalChatModel.refreshCurrentHistoryListForConversation(
|
||||
count: 50, convID: convID, convType: convType);
|
||||
} else if (model != null) {
|
||||
if(model != null){
|
||||
try{
|
||||
scrollController?.animateTo(
|
||||
scrollController!.position.minScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
}catch(e){}
|
||||
return model!.loadDataFromController();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Update single message at UI model
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// ignore_for_file: avoid_print, prefer_typing_uninitialized_variables
|
||||
|
||||
import 'dart:ui';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class Frame {
|
||||
static var orginalCallback;
|
||||
|
|
@ -27,7 +28,7 @@ class Frame {
|
|||
if (orginalCallback != null) {
|
||||
orginalCallback(timings);
|
||||
}
|
||||
print("fps: $fps");
|
||||
outputLogger.i("fps: $fps");
|
||||
}
|
||||
|
||||
static double get fps {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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:collection/collection.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class MessageUtils {
|
||||
// 判断CallingData的方式和Trtc的方法一致
|
||||
|
|
@ -18,7 +19,7 @@ class MessageUtils {
|
|||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
print("isCallingData json parse error");
|
||||
outputLogger.i("isCallingData json parse error");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -289,7 +290,7 @@ class MessageUtils {
|
|||
orElse: () => null);
|
||||
}
|
||||
} catch (e) {
|
||||
print('getImageFromImgList error ${e.toString()}');
|
||||
outputLogger.i('getImageFromImgList error ${e.toString()}');
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,8 +46,14 @@ class TimeAgo {
|
|||
date;
|
||||
}
|
||||
|
||||
String getTimeStringForChat(int timeStamp) {
|
||||
String? getTimeStringForChat(int timeStamp) {
|
||||
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 int diffDays = duration.inDays +
|
||||
(duration.inMinutes >
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
|||
/// Whether to use the default emoji
|
||||
final bool isUseDefaultEmoji;
|
||||
|
||||
final List customEmojiStickerList;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
final V2TimGroupMemberFullInfo? groupMemberInfo;
|
||||
|
||||
|
|
@ -287,7 +287,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
|||
/// replacing the default message hover action bar.
|
||||
/// Applicable only on desktop platforms.
|
||||
/// 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(
|
||||
{Key? key,
|
||||
|
|
@ -1093,8 +1093,11 @@ class _TIMUIKItHistoryMessageListItemState
|
|||
bool isDownloadWaiting) {
|
||||
final isDesktopScreen =
|
||||
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
final customHoverBar = widget.customMessageHoverBarOnDesktop != null
|
||||
? widget.customMessageHoverBarOnDesktop!(message)
|
||||
: null;
|
||||
final wideHoverTipList = (model.chatConfig.isUseMessageHoverBarOnDesktop &&
|
||||
widget.customMessageHoverBarOnDesktop == null)
|
||||
customHoverBar == null)
|
||||
? getMessageHoverControlBar(model, theme)
|
||||
: [];
|
||||
final lastItemName =
|
||||
|
|
@ -1106,6 +1109,7 @@ class _TIMUIKItHistoryMessageListItemState
|
|||
children: [
|
||||
if (isDesktopScreen &&
|
||||
isShowWideToolTip &&
|
||||
customHoverBar == null &&
|
||||
!((widget.message.elemType == 6 && isDownloadWaiting)))
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -1142,13 +1146,11 @@ class _TIMUIKItHistoryMessageListItemState
|
|||
.toList(),
|
||||
),
|
||||
),
|
||||
if (isDesktopScreen &&
|
||||
isShowWideToolTip &&
|
||||
widget.customMessageHoverBarOnDesktop != null)
|
||||
widget.customMessageHoverBarOnDesktop!(message),
|
||||
if (isDesktopScreen && isShowWideToolTip && customHoverBar != null)
|
||||
customHoverBar,
|
||||
if (!isDesktopScreen ||
|
||||
(model.chatConfig.isUseMessageHoverBarOnDesktop &&
|
||||
widget.customMessageHoverBarOnDesktop == null &&
|
||||
customHoverBar == null &&
|
||||
!isShowWideToolTip))
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'dart:math';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.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/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
|
||||
|
|
@ -79,6 +80,8 @@ class TIMUIKitMessageTooltip extends StatefulWidget {
|
|||
class TIMUIKitMessageTooltipState
|
||||
extends TIMUIKitState<TIMUIKitMessageTooltip> {
|
||||
final TUIChatGlobalModel globalModal = serviceLocator<TUIChatGlobalModel>();
|
||||
final TUISelfInfoViewModel selfInfoViewModel =
|
||||
serviceLocator<TUISelfInfoViewModel>();
|
||||
bool isShowMoreSticker = false;
|
||||
bool isShowOpenFile = false;
|
||||
String filePath = "";
|
||||
|
|
@ -179,9 +182,9 @@ class TIMUIKitMessageTooltipState
|
|||
}
|
||||
|
||||
bool isAdminCanRecall() {
|
||||
if (widget.groupMemberInfo != null &&
|
||||
widget.model.chatConfig.isGroupAdminRecallEnabled) {
|
||||
final selfRole = widget.groupMemberInfo!.role;
|
||||
if (widget.model.chatConfig.isGroupAdminRecallEnabled) {
|
||||
final selfMemberInfo = widget.groupMemberInfo ?? widget.model.selfMemberInfo;
|
||||
final selfRole = selfMemberInfo?.role;
|
||||
return selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN ||
|
||||
selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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/tim_uikit_chat_config.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 {
|
||||
none,
|
||||
|
|
@ -70,7 +71,7 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
|
|||
/// Whether to use the default emoji
|
||||
final bool isUseDefaultEmoji;
|
||||
|
||||
final List customEmojiStickerList;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
final bool isAllowScroll;
|
||||
|
||||
|
|
@ -82,7 +83,7 @@ class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
|
|||
/// replacing the default message hover action bar.
|
||||
/// Applicable only on desktop platforms.
|
||||
/// 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({
|
||||
Key? key,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/image_screen.dart';
|
|||
import 'package:transparent_image/transparent_image.dart';
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class TIMUIKitImageElem extends StatefulWidget {
|
||||
final V2TimMessage message;
|
||||
|
|
@ -58,7 +59,6 @@ class TIMUIKitImageElem extends StatefulWidget {
|
|||
|
||||
class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
||||
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
||||
double? networkImagePositionRadio; // 加这个字段用于异步获取被安全打击后的兜底图的比例
|
||||
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
|
||||
final MessageService _messageService = serviceLocator<MessageService>();
|
||||
Widget? imageItem;
|
||||
|
|
@ -373,178 +373,145 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
|||
}
|
||||
}
|
||||
|
||||
Widget _renderNetworkImage(
|
||||
dynamic heroTag, double? positionRadio, TUITheme? theme,
|
||||
{String? path, V2TimImage? originalImg, V2TimImage? smallImg}) {
|
||||
try {
|
||||
String originImgUrl = originalImg?.url ?? getOriginImgURL();
|
||||
if (originImgUrl.isEmpty && smallImg?.url != null) {
|
||||
originImgUrl = smallImg!.url!;
|
||||
}
|
||||
Future<double> calculateAspectRatio(ImageProvider imageProvider) async {
|
||||
Completer<double> completer = Completer<double>();
|
||||
|
||||
final imageWidget = Hero(
|
||||
tag: heroTag,
|
||||
child: PlatformUtils().isWeb
|
||||
? Image.network(path ?? smallImg?.url ?? originalImg!.url!,
|
||||
fit: BoxFit.contain)
|
||||
: CachedNetworkImage(
|
||||
alignment: Alignment.topCenter,
|
||||
imageUrl: path ?? 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),
|
||||
final imageStream = imageProvider.resolve(const ImageConfiguration());
|
||||
imageStream.addListener(
|
||||
ImageStreamListener((imageInfo, synchronousCall) {
|
||||
if (imageInfo.image.width != 0 && imageInfo.image.height != 0) {
|
||||
double aspectRatio = imageInfo.image.width / imageInfo.image.height;
|
||||
completer.complete(aspectRatio);
|
||||
} else {
|
||||
// If unable to calculate aspect ratio, return default value of 0.5
|
||||
completer.complete(0.5);
|
||||
}
|
||||
}, onError: (Object exception, StackTrace? stackTrace) {
|
||||
// If there's an error, return default value of 0.5
|
||||
completer.complete(1);
|
||||
}),
|
||||
);
|
||||
|
||||
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 Stack(
|
||||
alignment: widget.message.isSelf ?? true
|
||||
? AlignmentDirectional.topEnd
|
||||
: AlignmentDirectional.topStart,
|
||||
children: [
|
||||
getImage(
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
if (PlatformUtils().isWeb) {
|
||||
TUIKitWidePopup.showMedia(
|
||||
aspectRatio: positionRadio,
|
||||
context: context,
|
||||
mediaURL: widget.message.imageElem?.path ?? "",
|
||||
onClickOrigin: () => launchUrl(
|
||||
Uri.parse(widget.message.imageElem?.path ?? ""),
|
||||
mode: LaunchMode.externalApplication,
|
||||
));
|
||||
return;
|
||||
}
|
||||
if (PlatformUtils().isDesktop) {
|
||||
_handleOnTapPreviewImageOnDesktop(
|
||||
positionRadio: positionRadio,
|
||||
originImgUrl: originImgUrl,
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).push(
|
||||
PageRouteBuilder(
|
||||
opaque: false, // set to false
|
||||
pageBuilder: (_, __, ___) => ImageScreen(
|
||||
imageProvider: CachedNetworkImageProvider(
|
||||
path ?? originImgUrl,
|
||||
cacheKey: widget.message.msgID,
|
||||
),
|
||||
heroTag: heroTag,
|
||||
messageID: widget.message.msgID,
|
||||
downloadFn: () async {
|
||||
return await _saveImg(theme!);
|
||||
})),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: positionRadio != null
|
||||
? AspectRatio(
|
||||
aspectRatio: positionRadio,
|
||||
child: imageWidget,
|
||||
)
|
||||
: imageWidget,
|
||||
),
|
||||
imageElem: e)
|
||||
],
|
||||
);
|
||||
} catch (e) {
|
||||
return errorDisplay(context, theme);
|
||||
return;
|
||||
}
|
||||
if (PlatformUtils().isDesktop) {
|
||||
_handleOnTapPreviewImageOnDesktop(
|
||||
originImgUrl: imgUrl,
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).push(
|
||||
PageRouteBuilder(
|
||||
opaque: false,
|
||||
pageBuilder: (_, __, ___) => ImageScreen(
|
||||
imageProvider: CachedNetworkImageProvider(
|
||||
imgUrl ?? "",
|
||||
cacheKey: widget.message.msgID,
|
||||
),
|
||||
heroTag: heroTag,
|
||||
messageID: widget.message.msgID,
|
||||
downloadFn: () async {
|
||||
return await _saveImg(theme!);
|
||||
})),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (PlatformUtils().isDesktop) {
|
||||
TUIKitWidePopup.showMedia(
|
||||
mediaLocalPath: imgPath,
|
||||
context: context,
|
||||
onClickOrigin: () => launchDesktopFile(imgPath ?? ""));
|
||||
} else {
|
||||
Navigator.of(context).push(
|
||||
PageRouteBuilder(
|
||||
opaque: false, // set to false
|
||||
pageBuilder: (_, __, ___) => ImageScreen(
|
||||
imageProvider: FileImage(File(imgPath ?? "")),
|
||||
heroTag: heroTag,
|
||||
messageID: widget.message.msgID,
|
||||
downloadFn: () async {
|
||||
return await _saveImg(theme!);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget _renderLocalImage(String smallImage, dynamic heroTag,
|
||||
double? positionRadio, TUITheme? theme, String? originImage) {
|
||||
double? currentPositionRadio = positionRadio;
|
||||
File imgF = File(smallImage);
|
||||
Widget _renderAllImage(
|
||||
{dynamic heroTag,
|
||||
TUITheme? theme,
|
||||
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();
|
||||
if (!isExist) {
|
||||
return errorDisplay(context, theme);
|
||||
? smallLocalPath
|
||||
: originLocalPath)!;
|
||||
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())
|
||||
? originImage
|
||||
: smallImage;
|
||||
|
||||
image.image
|
||||
.resolve(const ImageConfiguration())
|
||||
.addListener(ImageStreamListener((image, synchronousCall) {
|
||||
if (image.image.width != 0 && image.image.height != 0) {
|
||||
currentPositionRadio = image.image.width / image.image.height;
|
||||
}
|
||||
}));
|
||||
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)
|
||||
],
|
||||
return GestureDetector(
|
||||
onTap: () => onClickImage(
|
||||
heroTag: heroTag,
|
||||
isNetworkImage: isNetworkImage,
|
||||
imgUrl: webPath ?? smallImg?.url ?? originalImg?.url ?? "",
|
||||
imgPath: (TencentUtils.checkString(originLocalPath) != null
|
||||
? originLocalPath
|
||||
: smallLocalPath) ?? ""
|
||||
),
|
||||
child: getImageWidget(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -587,26 +554,6 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
|||
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() {
|
||||
|
|
@ -628,10 +575,13 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
|||
|
||||
if (PlatformUtils().isWeb && widget.message.imageElem!.path != null) {
|
||||
// Displaying on Web only
|
||||
return _renderNetworkImage(heroTag, positionRadio, theme,
|
||||
return _renderAllImage(
|
||||
heroTag: heroTag,
|
||||
theme: theme,
|
||||
isNetworkImage: true,
|
||||
smallImg: smallImg,
|
||||
originalImg: originalImg,
|
||||
path: widget.message.imageElem!.path);
|
||||
webPath: widget.message.imageElem!.path);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -639,36 +589,47 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
|
|||
widget.message.imageElem!.path != null &&
|
||||
widget.message.imageElem!.path!.isNotEmpty &&
|
||||
File(widget.message.imageElem!.path!).existsSync())) {
|
||||
return _renderLocalImage(
|
||||
widget.message.imageElem!.path!,
|
||||
heroTag,
|
||||
networkImagePositionRadio ?? positionRadio,
|
||||
theme,
|
||||
widget.message.imageElem!.path!);
|
||||
return _renderAllImage(
|
||||
smallLocalPath: widget.message.imageElem!.path!,
|
||||
heroTag: heroTag,
|
||||
theme: theme,
|
||||
originLocalPath: widget.message.imageElem!.path!);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print(e);
|
||||
outputLogger.i(e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (smallImg?.localUrl != null &&
|
||||
smallImg?.localUrl != "" &&
|
||||
File((smallImg?.localUrl!)!).existsSync()) {
|
||||
return _renderLocalImage(smallImg!.localUrl!, heroTag, positionRadio,
|
||||
theme, originalImg?.localUrl);
|
||||
if ((TencentUtils.checkString(smallImg?.localUrl) != null &&
|
||||
File((smallImg?.localUrl!)!).existsSync()) ||
|
||||
(TencentUtils.checkString(originalImg?.localUrl) != null &&
|
||||
File((originalImg?.localUrl!)!).existsSync())) {
|
||||
return _renderAllImage(
|
||||
smallLocalPath: smallImg?.localUrl ?? "",
|
||||
heroTag: heroTag,
|
||||
theme: theme,
|
||||
originLocalPath: originalImg?.localUrl);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print(e);
|
||||
return _renderNetworkImage(heroTag, positionRadio, theme,
|
||||
smallImg: smallImg, originalImg: originalImg);
|
||||
outputLogger.i(e);
|
||||
return _renderAllImage(
|
||||
heroTag: heroTag,
|
||||
theme: theme,
|
||||
isNetworkImage: true,
|
||||
smallImg: smallImg,
|
||||
originalImg: originalImg);
|
||||
}
|
||||
|
||||
if ((smallImg?.url ?? originalImg?.url) != null &&
|
||||
(smallImg?.url ?? originalImg?.url)!.isNotEmpty) {
|
||||
return _renderNetworkImage(heroTag, positionRadio, theme,
|
||||
smallImg: smallImg, originalImg: originalImg);
|
||||
return _renderAllImage(
|
||||
heroTag: heroTag,
|
||||
theme: theme,
|
||||
isNetworkImage: true,
|
||||
smallImg: smallImg,
|
||||
originalImg: originalImg);
|
||||
}
|
||||
|
||||
return errorDisplay(context, theme);
|
||||
|
|
|
|||
|
|
@ -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/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/utils/logger.dart';
|
||||
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
|
||||
|
||||
class TIMUIKitReplyElem extends StatefulWidget {
|
||||
final V2TimMessage message;
|
||||
|
|
@ -35,7 +37,7 @@ class TIMUIKitReplyElem extends StatefulWidget {
|
|||
final TUIChatSeparateViewModel chatModel;
|
||||
final bool? isShowMessageReaction;
|
||||
final bool isUseDefaultEmoji;
|
||||
final List customEmojiStickerList;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
const TIMUIKitReplyElem({
|
||||
Key? key,
|
||||
|
|
@ -72,7 +74,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
: "{}"));
|
||||
if (messageCloudCustomData.messageReply != null) {
|
||||
final MessageRepliedData repliedMessage =
|
||||
MessageRepliedData.fromJson(messageCloudCustomData.messageReply!);
|
||||
MessageRepliedData.fromJson(messageCloudCustomData.messageReply!);
|
||||
return repliedMessage;
|
||||
}
|
||||
return null;
|
||||
|
|
@ -95,8 +97,8 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
if (message == null) {
|
||||
try {
|
||||
final RepliedMessageAbstract repliedMessageAbstract =
|
||||
RepliedMessageAbstract.fromJson(
|
||||
jsonDecode(cloudCustomData.messageAbstract));
|
||||
RepliedMessageAbstract.fromJson(
|
||||
jsonDecode(cloudCustomData.messageAbstract));
|
||||
if (repliedMessageAbstract.isNotEmpty) {
|
||||
message = V2TimMessage(
|
||||
elemType: 0,
|
||||
|
|
@ -106,7 +108,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
}
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print(e.toString());
|
||||
outputLogger.i(e.toString());
|
||||
}
|
||||
}
|
||||
if (message != null) {
|
||||
|
|
@ -132,8 +134,8 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
_renderMessageSummary(TUITheme? theme) {
|
||||
try {
|
||||
final RepliedMessageAbstract repliedMessageAbstract =
|
||||
RepliedMessageAbstract.fromJson(
|
||||
jsonDecode(repliedMessage?.messageAbstract ?? ""));
|
||||
RepliedMessageAbstract.fromJson(
|
||||
jsonDecode(repliedMessage?.messageAbstract ?? ""));
|
||||
if (TencentUtils.checkString(repliedMessageAbstract.summary) != null) {
|
||||
return _defaultRawMessageText(repliedMessageAbstract.summary!, theme);
|
||||
}
|
||||
|
|
@ -177,16 +179,15 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
|
||||
if (isRevokedMsg) {
|
||||
return _defaultRawMessageText(
|
||||
isAdminRevoke ? TIM_t("[消息被管理员撤回]") : TIM_t("[消息被撤回]"),
|
||||
theme);
|
||||
isAdminRevoke ? TIM_t("[消息被管理员撤回]") : TIM_t("[消息被撤回]"), theme);
|
||||
}
|
||||
|
||||
final messageType = message.elemType;
|
||||
final isSelf = message.isSelf ?? true;
|
||||
final customAbstractMessage =
|
||||
widget.chatModel.abstractMessageBuilder != null
|
||||
? widget.chatModel.abstractMessageBuilder!(message)
|
||||
: null;
|
||||
widget.chatModel.abstractMessageBuilder != null
|
||||
? widget.chatModel.abstractMessageBuilder!(message)
|
||||
: null;
|
||||
if (customAbstractMessage != null) {
|
||||
return _defaultRawMessageText(
|
||||
customAbstractMessage,
|
||||
|
|
@ -302,14 +303,14 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
try {
|
||||
final String localJSON = widget.message.localCustomData!;
|
||||
final LocalCustomDataModel? localPreviewInfo =
|
||||
LocalCustomDataModel.fromMap(json.decode(localJSON));
|
||||
LocalCustomDataModel.fromMap(json.decode(localJSON));
|
||||
if (localPreviewInfo != null &&
|
||||
!localPreviewInfo.isLinkPreviewEmpty()) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 8),
|
||||
child:
|
||||
// You can use this default widget [LinkPreviewWidget] to render preview card, or you can use custom widget.
|
||||
LinkPreviewWidget(linkPreview: localPreviewInfo),
|
||||
// You can use this default widget [LinkPreviewWidget] to render preview card, or you can use custom widget.
|
||||
LinkPreviewWidget(linkPreview: localPreviewInfo),
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
|
|
@ -344,7 +345,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
|
||||
final defaultStyle = isFromSelf
|
||||
? (theme.chatMessageItemFromSelfBgColor ??
|
||||
theme.lightPrimaryMaterialColor.shade50)
|
||||
theme.lightPrimaryMaterialColor.shade50)
|
||||
: (theme.chatMessageItemFromOthersBgColor);
|
||||
|
||||
final backgroundColor = isShowJumpState
|
||||
|
|
@ -353,23 +354,29 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
|
||||
final borderRadius = isFromSelf
|
||||
? const BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
topRight: Radius.circular(2),
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10))
|
||||
topLeft: Radius.circular(10),
|
||||
topRight: Radius.circular(2),
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10))
|
||||
: const BorderRadius.only(
|
||||
topLeft: Radius.circular(2),
|
||||
topRight: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10));
|
||||
topLeft: Radius.circular(2),
|
||||
topRight: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10));
|
||||
final textWithLink = LinkPreviewEntry.getHyperlinksText(
|
||||
widget.message.textElem?.text ?? "",
|
||||
widget.chatModel.chatConfig.isSupportMarkdownForTextMessage,
|
||||
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,
|
||||
isEnableTextSelection:
|
||||
widget.chatModel.chatConfig.isEnableTextSelection ?? false);
|
||||
widget.chatModel.chatConfig.isEnableTextSelection ?? false);
|
||||
return Container(
|
||||
padding: widget.textPadding ?? EdgeInsets.all(isDesktopScreen ? 12 : 10),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -377,10 +384,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
borderRadius: widget.borderRadius ?? borderRadius,
|
||||
),
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width * 0.6),
|
||||
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.6),
|
||||
child: GestureDetector(
|
||||
onTap: _jumpToRawMsg,
|
||||
child: Column(
|
||||
|
|
@ -421,22 +425,34 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
// You can render the widget from extension directly, with a [TextStyle] optionally.
|
||||
widget.chatModel.chatConfig.urlPreviewType != UrlPreviewType.none
|
||||
? textWithLink!(
|
||||
style: widget.fontStyle ??
|
||||
TextStyle(
|
||||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
textBaseline: TextBaseline.ideographic,
|
||||
height: widget.chatModel.chatConfig.textHeight))
|
||||
style: widget.fontStyle ??
|
||||
TextStyle(
|
||||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
textBaseline: TextBaseline.ideographic,
|
||||
height: widget.chatModel.chatConfig.textHeight))
|
||||
: ExtendedText(widget.message.textElem?.text ?? "",
|
||||
softWrap: true,
|
||||
style: widget.fontStyle ??
|
||||
TextStyle(
|
||||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
height: widget.chatModel.chatConfig.textHeight),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
)),
|
||||
softWrap: true,
|
||||
style: widget.fontStyle ??
|
||||
TextStyle(
|
||||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
height: widget.chatModel.chatConfig.textHeight),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseQQPackage: (widget
|
||||
.chatModel
|
||||
.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 (_renderPreviewWidget() != null &&
|
||||
widget.chatModel.chatConfig.urlPreviewType ==
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class TIMUIKitTextElem extends StatefulWidget {
|
|||
final TUIChatSeparateViewModel chatModel;
|
||||
final bool? isShowMessageReaction;
|
||||
final bool isUseDefaultEmoji;
|
||||
final List customEmojiStickerList;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
const TIMUIKitTextElem(
|
||||
{Key? key,
|
||||
|
|
@ -167,7 +167,13 @@ class _TIMUIKitTextElemState extends TIMUIKitState<TIMUIKitTextElem> {
|
|||
widget.message.textElem?.text ?? "",
|
||||
widget.chatModel.chatConfig.isSupportMarkdownForTextMessage,
|
||||
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,
|
||||
isEnableTextSelection:
|
||||
widget.chatModel.chatConfig.isEnableTextSelection ?? false);
|
||||
|
|
@ -232,7 +238,19 @@ class _TIMUIKitTextElemState extends TIMUIKitState<TIMUIKitTextElem> {
|
|||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
height: widget.chatModel.chatConfig.textHeight),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||
isUseQQPackage: (widget
|
||||
.chatModel
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true) ||
|
||||
widget.isUseDefaultEmoji,
|
||||
isUseTencentCloudChatPackage: widget
|
||||
.chatModel
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class TIMUIKitTextTranslationElem extends StatefulWidget {
|
|||
final TUIChatSeparateViewModel chatModel;
|
||||
final bool? isShowMessageReaction;
|
||||
final bool isUseDefaultEmoji;
|
||||
final List customEmojiStickerList;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
const TIMUIKitTextTranslationElem(
|
||||
{Key? key,
|
||||
|
|
@ -124,7 +124,13 @@ class _TIMUIKitTextTranslationElemState
|
|||
final textWithLink = LinkPreviewEntry.getHyperlinksText(translateText ?? "",
|
||||
widget.chatModel.chatConfig.isSupportMarkdownForTextMessage,
|
||||
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,
|
||||
isEnableTextSelection:
|
||||
widget.chatModel.chatConfig.isEnableTextSelection ?? false);
|
||||
|
|
@ -160,7 +166,19 @@ class _TIMUIKitTextTranslationElemState
|
|||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
height: widget.chatModel.chatConfig.textHeight),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||
isUseQQPackage: (widget
|
||||
.chatModel
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true) ||
|
||||
widget.isUseDefaultEmoji,
|
||||
isUseTencentCloudChatPackage: widget
|
||||
.chatModel
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -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_state.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/ui/widgets/avatar.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
|
||||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:extended_text_field/extended_text_field.dart';
|
||||
import 'package:flutter/material.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';
|
||||
|
||||
class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
|
||||
DefaultSpecialTextSpanBuilder({
|
||||
this.isUseDefaultEmoji = false,
|
||||
this.isUseQQPackage = false,
|
||||
this.isUseTencentCloudChatPackage = false,
|
||||
this.customEmojiStickerList = const [],
|
||||
this.showAtBackground = false,
|
||||
});
|
||||
|
|
@ -17,9 +18,11 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
|
|||
/// whether show background for @somebody
|
||||
final bool showAtBackground;
|
||||
|
||||
final bool isUseDefaultEmoji;
|
||||
final bool isUseQQPackage;
|
||||
|
||||
final List customEmojiStickerList;
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
@override
|
||||
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)
|
||||
if (isStart(flag, EmojiText.flag)) {
|
||||
return EmojiText(textStyle,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
start: index! - (EmojiText.flag.length - 1),
|
||||
isUseDefaultEmoji: isUseDefaultEmoji,
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
customEmojiStickerList: customEmojiStickerList);
|
||||
} else if (isStart(flag, HttpText.flag)) {
|
||||
return HttpText(textStyle, onTap,
|
||||
|
|
|
|||
|
|
@ -1,44 +1,54 @@
|
|||
import 'package:flutter/material.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/utils/tim_custom_face_data.dart';
|
||||
|
||||
///emoji/image text
|
||||
class EmojiText extends SpecialText {
|
||||
EmojiText(TextStyle? textStyle,
|
||||
{this.start,
|
||||
this.isUseDefaultEmoji = false,
|
||||
this.isUseTencentCloudChatPackage = false,
|
||||
this.isUseQQPackage = false,
|
||||
this.customEmojiStickerList = const []})
|
||||
: super(EmojiText.flag, ']', textStyle);
|
||||
static const String flag = '[';
|
||||
final int? start;
|
||||
final bool isUseDefaultEmoji;
|
||||
final List customEmojiStickerList;
|
||||
final bool isUseQQPackage;
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
@override
|
||||
InlineSpan finishText() {
|
||||
final String key = toString();
|
||||
final EmojiUtil emojiUtil = EmojiUtil(
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
customEmojiStickerList: customEmojiStickerList);
|
||||
|
||||
if (EmojiUtil(
|
||||
isUseDefaultEmoji: isUseDefaultEmoji,
|
||||
customEmojiStickerList: customEmojiStickerList)
|
||||
.instance
|
||||
.emojiMap
|
||||
.containsKey(key)) {
|
||||
if (emojiUtil.emojiMap.containsKey(key)) {
|
||||
double size = 16;
|
||||
|
||||
final TextStyle ts = textStyle!;
|
||||
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(
|
||||
AssetImage(
|
||||
EmojiUtil(
|
||||
isUseDefaultEmoji: isUseDefaultEmoji,
|
||||
customEmojiStickerList: customEmojiStickerList)
|
||||
.instance
|
||||
.emojiMap[key]!,
|
||||
package: "tencent_im_base"),
|
||||
AssetImage(emojiUtil.emojiMap[key]!,
|
||||
package: "tim_ui_kit_sticker_plugin"),
|
||||
actualText: key,
|
||||
imageWidth: size,
|
||||
imageHeight: size,
|
||||
start: start!,
|
||||
// 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,
|
||||
imageWidth: size,
|
||||
imageHeight: size,
|
||||
|
|
@ -46,12 +56,7 @@ class EmojiText extends SpecialText {
|
|||
// fit: BoxFit.cover,
|
||||
margin: const EdgeInsets.all(0));
|
||||
} else {
|
||||
return ImageSpan(
|
||||
AssetImage(EmojiUtil(
|
||||
isUseDefaultEmoji: isUseDefaultEmoji,
|
||||
customEmojiStickerList: customEmojiStickerList)
|
||||
.instance
|
||||
.emojiMap[key]!),
|
||||
return ImageSpan(AssetImage(emojiUtil.emojiMap[key]!),
|
||||
actualText: key,
|
||||
imageWidth: size,
|
||||
imageHeight: size,
|
||||
|
|
@ -66,39 +71,90 @@ class EmojiText extends SpecialText {
|
|||
}
|
||||
|
||||
class EmojiUtil {
|
||||
EmojiUtil(
|
||||
{this.isUseDefaultEmoji = false, this.customEmojiStickerList = const []});
|
||||
EmojiUtil._(this.isUseDefaultEmoji, this.customEmojiStickerList) {
|
||||
if (isUseDefaultEmoji == true) {
|
||||
for (int i = 0; i < ConstData.emojiList.length; i++) {
|
||||
for (int j = 0; j < ConstData.emojiList[i].list.length; j++) {
|
||||
String? emojiName = ConstData.emojiList[i].list[j].split('.png')[0];
|
||||
_emojiMap['[$emojiName]'] =
|
||||
'$_emojiFilePath/${ConstData.emojiList[i].name}/$emojiName.png';
|
||||
_emojiMap['[${ConstData.emojiMapList[emojiName]}]'] =
|
||||
'$_emojiFilePath/${ConstData.emojiList[i].name}/$emojiName.png';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < customEmojiStickerList.length; i++) {
|
||||
for (int j = 0; j < customEmojiStickerList[i].list.length; j++) {
|
||||
String? emojiName =
|
||||
customEmojiStickerList[i].list[j].split('.png')[0];
|
||||
_emojiMap['[$emojiName]'] =
|
||||
'$_emojiFilePath/${customEmojiStickerList[i].name}/$emojiName.png';
|
||||
// Private constructor initializing the emoji data
|
||||
EmojiUtil._internal(
|
||||
{required this.isUseQQPackage,
|
||||
required this.isUseTencentCloudChatPackage,
|
||||
required this.customEmojiStickerList}) {
|
||||
_emojiMap.addAll(loadDefaultEmojis());
|
||||
|
||||
final customEmojis = loadCustomEmojis();
|
||||
_emojiMap.addAll(customEmojis.$1);
|
||||
_emojiKeyCategoryMap["custom"] = customEmojis.$2;
|
||||
}
|
||||
|
||||
final bool isUseQQPackage;
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
// Load the default emojis into a Map
|
||||
Map<String, String> loadDefaultEmojis() {
|
||||
Map<String, String> defaultEmojiMap = {};
|
||||
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>{};
|
||||
|
||||
// A getter method for _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';
|
||||
// EmojiUitl? _instance;
|
||||
|
||||
// Singleton pattern to avoid creating multiple instances of EmojiUtil
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class EmojiPanel extends TIMUIKitStatelessWidget {
|
||||
final void Function(int unicode) onTapEmoji;
|
||||
|
|
@ -22,7 +23,7 @@ class EmojiPanel extends TIMUIKitStatelessWidget {
|
|||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
// ignore: avoid_print
|
||||
print(TIM_t(
|
||||
outputLogger.i(TIM_t(
|
||||
"暂未安装表情包插件,如需使用表情相关功能,请根据本文档安装:https://cloud.tencent.com/document/product/269/70746"));
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
|||
// ignore: unnecessary_import
|
||||
import 'dart:typed_data';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class MorePanelConfig {
|
||||
final bool showGalleryPickAction;
|
||||
|
|
@ -357,41 +358,44 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
|
||||
_sendImageMessage(TUIChatSeparateViewModel model, TUITheme theme) async {
|
||||
try {
|
||||
if (PlatformUtils().isMobile){
|
||||
if(PlatformUtils().isAndroid){
|
||||
if (PlatformUtils().isMobile) {
|
||||
if (PlatformUtils().isAndroid) {
|
||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
if ((androidInfo.version.sdkInt) >= 33) {
|
||||
final videos = await Permissions.checkPermission(
|
||||
context,Permission.videos.value,
|
||||
context,
|
||||
Permission.videos.value,
|
||||
theme,
|
||||
);
|
||||
final photos = await Permissions.checkPermission(
|
||||
context,Permission.photos.value,
|
||||
context,
|
||||
Permission.photos.value,
|
||||
theme,
|
||||
);
|
||||
if(!videos && !photos){
|
||||
if (!videos && !photos) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
final storage = await Permissions.checkPermission(
|
||||
context, Permission.storage.value,
|
||||
context,
|
||||
Permission.storage.value,
|
||||
theme,
|
||||
);
|
||||
if(!storage){
|
||||
if (!storage) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
final photos = await Permissions.checkPermission(
|
||||
context,
|
||||
Permission.photos.value,
|
||||
theme,
|
||||
);
|
||||
if(!photos){
|
||||
if (!photos) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final convID = widget.conversationID;
|
||||
final convType = widget.conversationType;
|
||||
|
|
@ -446,7 +450,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
}
|
||||
}
|
||||
} catch (err) {
|
||||
print("err: $err");
|
||||
outputLogger.i("err: $err");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -462,18 +466,41 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
)) {
|
||||
return;
|
||||
}
|
||||
if (!await Permissions.checkPermission(
|
||||
context,
|
||||
Permission.photos.value,
|
||||
theme,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
await Permissions.checkPermission(
|
||||
context,
|
||||
Permission.microphone.value,
|
||||
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 convType = widget.conversationType;
|
||||
final pickedFile = await CameraPicker.pickFromCamera(context,
|
||||
|
|
@ -498,7 +525,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
// Toast.showToast(ToastType.fail, TIM_t("图片不能为空"), context);
|
||||
}
|
||||
} catch (error) {
|
||||
print("err: $error");
|
||||
outputLogger.i("err: $error");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -524,7 +551,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
convType: convType),
|
||||
context);
|
||||
} catch (e) {
|
||||
print("_sendFileErr: ${e.toString()}");
|
||||
outputLogger.i("_sendFileErr: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +585,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
convType: convType),
|
||||
context);
|
||||
} 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 ?? "";
|
||||
print(TIM_t_para("选择成功{{option2}}", "选择成功$option2")(option2: option2));
|
||||
outputLogger
|
||||
.i(TIM_t_para("选择成功{{option2}}", "选择成功$option2")(option2: option2));
|
||||
|
||||
File file = File(result.files.single.path!);
|
||||
final int size = file.lengthSync();
|
||||
|
|
@ -606,7 +634,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
throw TypeError();
|
||||
}
|
||||
} catch (e) {
|
||||
print("_sendFileErr: ${e.toString()}");
|
||||
outputLogger.i("_sendFileErr: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -709,35 +737,35 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
runSpacing: 20,
|
||||
children: itemList(model, theme)
|
||||
.map((item) => InkWell(
|
||||
onTap: () {
|
||||
if (item.onTap != null) {
|
||||
item.onTap!(context);
|
||||
}
|
||||
},
|
||||
child: widget.morePanelConfig?.actionBuilder != null
|
||||
? widget.morePanelConfig?.actionBuilder!(item)
|
||||
: SizedBox(
|
||||
height: 94,
|
||||
width: 64,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 64,
|
||||
width: 64,
|
||||
margin: const EdgeInsets.only(bottom: 4),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5))),
|
||||
child: item.icon,
|
||||
),
|
||||
Text(
|
||||
item.title,
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: theme.darkTextColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
)))
|
||||
onTap: () {
|
||||
if (item.onTap != null) {
|
||||
item.onTap!(context);
|
||||
}
|
||||
},
|
||||
child: widget.morePanelConfig?.actionBuilder != null
|
||||
? widget.morePanelConfig?.actionBuilder!(item)
|
||||
: SizedBox(
|
||||
height: 94,
|
||||
width: 64,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 64,
|
||||
width: 64,
|
||||
margin: const EdgeInsets.only(bottom: 4),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5))),
|
||||
child: item.icon,
|
||||
),
|
||||
Text(
|
||||
item.title,
|
||||
style: TextStyle(
|
||||
fontSize: 12, color: theme.darkTextColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
)))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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/sound_record.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 {
|
||||
/// conversation ID
|
||||
|
|
@ -255,12 +256,12 @@ class _SendSoundMessageState extends TIMUIKitState<SendSoundMessage> {
|
|||
path: soundPath!, duration: recordDuration!.ceil(), model: model);
|
||||
}
|
||||
} else if (status == "onStart") {
|
||||
print("start record");
|
||||
outputLogger.i("start record");
|
||||
setState(() {
|
||||
isRecording = true;
|
||||
});
|
||||
} else {
|
||||
print(status);
|
||||
outputLogger.i(status);
|
||||
}
|
||||
});
|
||||
final amplitudesResponseSubscription =
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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/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/tim_uikit_emoji_panel.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:tencent_keyboard_visibility/tencent_keyboard_visibility.dart';
|
||||
|
|
@ -91,7 +90,9 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget {
|
|||
|
||||
final VoidCallback goDownBottom;
|
||||
|
||||
final List customEmojiStickerList;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
final List<CustomStickerPackage> stickerPackageList;
|
||||
|
||||
const TIMUIKitTextFieldLayoutNarrow(
|
||||
{Key? key,
|
||||
|
|
@ -125,7 +126,8 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget {
|
|||
required this.showMorePanel,
|
||||
this.hintText,
|
||||
required this.customEmojiStickerList,
|
||||
this.controller})
|
||||
this.controller,
|
||||
required this.stickerPackageList})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
|
@ -185,51 +187,71 @@ class _TIMUIKitTextFieldLayoutNarrowState
|
|||
}
|
||||
}
|
||||
|
||||
Widget _getBottomContainer() {
|
||||
Widget _getBottomContainer(TUITheme theme) {
|
||||
if (showEmojiPanel) {
|
||||
return widget.customStickerPanel != null
|
||||
? widget.customStickerPanel!(
|
||||
sendTextMessage: () {
|
||||
widget.onEmojiSubmitted();
|
||||
setSendButton();
|
||||
},
|
||||
sendFaceMessage: widget.onCustomEmojiFaceSubmitted,
|
||||
deleteText: () {
|
||||
widget.backSpaceText();
|
||||
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' &&
|
||||
ConstData.emojiMapList[emojiName] != null &&
|
||||
ConstData.emojiMapList[emojiName] != '') {
|
||||
emojiName = ConstData.emojiMapList[emojiName];
|
||||
}
|
||||
final newText = '[$emojiName]';
|
||||
widget.addStickerToText(newText);
|
||||
setSendButton();
|
||||
}),
|
||||
defaultCustomEmojiStickerList:
|
||||
widget.isUseDefaultEmoji ? ConstData.emojiList : [])
|
||||
: EmojiPanel(onTapEmoji: (unicode) {
|
||||
final newText = String.fromCharCode(unicode);
|
||||
widget.addStickerToText(newText);
|
||||
setSendButton();
|
||||
// handleSetDraftText();
|
||||
}, onSubmitted: () {
|
||||
widget.onEmojiSubmitted();
|
||||
setSendButton();
|
||||
}, delete: () {
|
||||
widget.backSpaceText();
|
||||
setSendButton();
|
||||
});
|
||||
sendTextMessage: () {
|
||||
widget.onEmojiSubmitted();
|
||||
setSendButton();
|
||||
},
|
||||
sendFaceMessage: widget.onCustomEmojiFaceSubmitted,
|
||||
deleteText: () {
|
||||
widget.backSpaceText();
|
||||
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();
|
||||
}),
|
||||
defaultCustomEmojiStickerList: widget.isUseDefaultEmoji
|
||||
? TUIKitStickerConstData.emojiList
|
||||
: [])
|
||||
: StickerPanel(
|
||||
isWideScreen: false,
|
||||
sendTextMsg: () {
|
||||
widget.onEmojiSubmitted();
|
||||
setSendButton();
|
||||
},
|
||||
sendFaceMsg: widget.onCustomEmojiFaceSubmitted,
|
||||
deleteText: () {
|
||||
widget.backSpaceText();
|
||||
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) {
|
||||
|
|
@ -310,23 +332,22 @@ class _TIMUIKitTextFieldLayoutNarrowState
|
|||
});
|
||||
};
|
||||
}
|
||||
|
||||
String getAbstractMessage(V2TimMessage message) {
|
||||
final String? customAbstractMessage = widget
|
||||
.model.abstractMessageBuilder != null ? widget.model
|
||||
.abstractMessageBuilder!(widget.model.repliedMessage!) : null;
|
||||
return customAbstractMessage ?? MessageUtils
|
||||
.getAbstractMessageAsync(
|
||||
widget.model.repliedMessage!, widget.model.groupMemberList ?? []);
|
||||
final String? customAbstractMessage =
|
||||
widget.model.abstractMessageBuilder != null
|
||||
? widget.model.abstractMessageBuilder!(widget.model.repliedMessage!)
|
||||
: null;
|
||||
return customAbstractMessage ??
|
||||
MessageUtils.getAbstractMessageAsync(
|
||||
widget.model.repliedMessage!, widget.model.groupMemberList ?? []);
|
||||
}
|
||||
|
||||
_buildRepliedMessage(V2TimMessage? repliedMessage) {
|
||||
final haveRepliedMessage = repliedMessage != null;
|
||||
if (haveRepliedMessage) {
|
||||
final text =
|
||||
"${MessageUtils.getDisplayName(
|
||||
widget.model.repliedMessage!)}:${getAbstractMessage(
|
||||
repliedMessage
|
||||
)}";
|
||||
"${MessageUtils.getDisplayName(widget.model.repliedMessage!)}:${getAbstractMessage(repliedMessage)}";
|
||||
return Container(
|
||||
color: widget.backgroundColor ?? hexToColor("f5f5f6"),
|
||||
alignment: Alignment.centerLeft,
|
||||
|
|
@ -514,8 +535,19 @@ class _TIMUIKitTextFieldLayoutNarrowState
|
|||
.isWeb
|
||||
? null
|
||||
: DefaultSpecialTextSpanBuilder(
|
||||
isUseDefaultEmoji:
|
||||
isUseQQPackage: (widget
|
||||
.model
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true) ||
|
||||
widget.isUseDefaultEmoji,
|
||||
isUseTencentCloudChatPackage: widget
|
||||
.model
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true,
|
||||
customEmojiStickerList:
|
||||
widget.customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
|
|
@ -609,9 +641,7 @@ class _TIMUIKitTextFieldLayoutNarrowState
|
|||
height: max(_getBottomHeight(), 0.0),
|
||||
child: ListView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: [
|
||||
_getBottomContainer()
|
||||
],
|
||||
children: [_getBottomContainer(theme)],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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/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/tim_uikit_emoji_panel.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/drag_widget.dart';
|
||||
import 'package:extended_text_field/extended_text_field.dart';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
|
|
@ -36,6 +35,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||
|
||||
// ignore: unnecessary_import
|
||||
import 'dart:typed_data';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class DesktopControlBarItem {
|
||||
final String item;
|
||||
|
|
@ -147,11 +147,13 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
|
|||
|
||||
final VoidCallback goDownBottom;
|
||||
|
||||
final List customEmojiStickerList;
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
/// Conversation need search
|
||||
final V2TimConversation currentConversation;
|
||||
|
||||
final List<CustomStickerPackage> stickerPackageList;
|
||||
|
||||
const TIMUIKitTextFieldLayoutWide(
|
||||
{Key? key,
|
||||
this.customStickerPanel,
|
||||
|
|
@ -186,7 +188,8 @@ class TIMUIKitTextFieldLayoutWide extends StatefulWidget {
|
|||
this.controller,
|
||||
required this.currentConversation,
|
||||
required this.theme,
|
||||
required this.chatConfig})
|
||||
required this.chatConfig,
|
||||
required this.stickerPackageList})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
|
@ -230,7 +233,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
|||
}
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print(e);
|
||||
outputLogger.i(e);
|
||||
}
|
||||
generateDefaultControlBarItems();
|
||||
}
|
||||
|
|
@ -243,7 +246,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
|||
}
|
||||
} catch (e) {
|
||||
// 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];
|
||||
if (widget.isUseDefaultEmoji &&
|
||||
widget.languageType == 'zh' &&
|
||||
ConstData.emojiMapList[emojiName] != null &&
|
||||
ConstData.emojiMapList[emojiName] != '') {
|
||||
emojiName = ConstData.emojiMapList[emojiName];
|
||||
TUIKitStickerConstData.emojiMapList[emojiName] !=
|
||||
null &&
|
||||
TUIKitStickerConstData.emojiMapList[emojiName] !=
|
||||
'') {
|
||||
emojiName =
|
||||
TUIKitStickerConstData.emojiMapList[emojiName];
|
||||
}
|
||||
final newText = '[$emojiName]';
|
||||
widget.addStickerToText(newText);
|
||||
entry?.remove();
|
||||
entry = null;
|
||||
}),
|
||||
defaultCustomEmojiStickerList:
|
||||
widget.isUseDefaultEmoji ? ConstData.emojiList : [])
|
||||
: EmojiPanel(onTapEmoji: (unicode) {
|
||||
final newText = String.fromCharCode(unicode);
|
||||
widget.addStickerToText(newText);
|
||||
}, onSubmitted: () {
|
||||
widget.onEmojiSubmitted();
|
||||
}, delete: () {
|
||||
widget.backSpaceText();
|
||||
}),
|
||||
defaultCustomEmojiStickerList: widget.isUseDefaultEmoji
|
||||
? TUIKitStickerConstData.emojiList
|
||||
: [])
|
||||
: StickerPanel(
|
||||
isWideScreen: true,
|
||||
height: widget.chatConfig.desktopStickerPanelHeight,
|
||||
width: 350,
|
||||
sendTextMsg: null,
|
||||
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) {
|
||||
// ignore: avoid_print
|
||||
print("_sendFileErr: ${e.toString()}");
|
||||
outputLogger.i("_sendFileErr: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -568,7 +606,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
|||
context);
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print("_sendFileErr: ${e.toString()}");
|
||||
outputLogger.i("_sendFileErr: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -603,7 +641,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
|||
context);
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print("_sendFileErr: ${e.toString()}");
|
||||
outputLogger.i("_sendFileErr: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -730,7 +768,7 @@ class _TIMUIKitTextFieldLayoutWideState
|
|||
}
|
||||
} catch (err) {
|
||||
// ignore: avoid_print
|
||||
print("send media err: $err");
|
||||
outputLogger.i("send media err: $err");
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("视频文件异常"),
|
||||
|
|
@ -1051,7 +1089,19 @@ class _TIMUIKitTextFieldLayoutWideState
|
|||
specialTextSpanBuilder: PlatformUtils().isWeb
|
||||
? null
|
||||
: DefaultSpecialTextSpanBuilder(
|
||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||
isUseQQPackage: (widget
|
||||
.model
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true) ||
|
||||
widget.isUseDefaultEmoji,
|
||||
isUseTencentCloudChatPackage: widget
|
||||
.model
|
||||
.chatConfig
|
||||
.stickerPanelConfig
|
||||
?.useTencentCloudChatStickerPackage ??
|
||||
true,
|
||||
customEmojiStickerList:
|
||||
widget.customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
|
|
|
|||
|
|
@ -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/controller/tim_uikit_chat_controller.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/platform.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.
|
||||
final Widget? topFixWidget;
|
||||
|
||||
final List customEmojiStickerList;
|
||||
/// Specify the custom small png emoji packages.
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
final Widget? customAppBar;
|
||||
|
||||
|
|
@ -151,17 +153,26 @@ class TIMUIKitChat extends StatefulWidget {
|
|||
/// replacing the default message hover action bar.
|
||||
/// Applicable only on desktop platforms.
|
||||
/// 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
|
||||
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(
|
||||
{Key? key,
|
||||
this.groupID,
|
||||
required this.conversation,
|
||||
this.conversationID,
|
||||
this.conversationType,
|
||||
this.groupMemberList,
|
||||
this.conversationShowName,
|
||||
this.abstractMessageBuilder,
|
||||
this.onTapAvatar,
|
||||
|
|
@ -241,11 +252,9 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
|||
model.abstractMessageBuilder = widget.abstractMessageBuilder;
|
||||
model.onTapAvatar = widget.onTapAvatar;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
if (kProfileMode) {
|
||||
widget.endTime = DateTime.now().millisecondsSinceEpoch;
|
||||
int timeSpend = widget.endTime - widget.startTime;
|
||||
print("Page render time:$timeSpend ms");
|
||||
}
|
||||
widget.endTime = DateTime.now().millisecondsSinceEpoch;
|
||||
int timeSpend = widget.endTime - widget.startTime;
|
||||
outputLogger.i("Page render time:$timeSpend ms");
|
||||
});
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
updateDraft();
|
||||
|
|
@ -372,6 +381,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
|||
scrollController: autoController,
|
||||
textFieldController: textFieldController,
|
||||
conversationID: _getConvID(),
|
||||
groupMemberList: widget.groupMemberList,
|
||||
conversationType: _getConvType(),
|
||||
lifeCycle: widget.lifeCycle,
|
||||
config: widget.config,
|
||||
|
|
@ -408,7 +418,9 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
|||
model.loadGroupInfo(_getConvID());
|
||||
break;
|
||||
case UpdateType.memberList:
|
||||
model.loadGroupMemberList(groupID: _getConvID());
|
||||
if(widget.groupMemberList == null){
|
||||
model.loadGroupMemberList(groupID: _getConvID());
|
||||
}
|
||||
model.loadGroupInfo(_getConvID());
|
||||
break;
|
||||
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(
|
||||
onTap: () {
|
||||
textFieldController.hideAllPanel();
|
||||
|
|
@ -538,7 +571,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
|
|||
model: model,
|
||||
controller: textFieldController,
|
||||
customEmojiStickerList:
|
||||
widget.customEmojiStickerList,
|
||||
customImageSmallPngEmojiPackages,
|
||||
isUseDefaultEmoji:
|
||||
widget.config!.isUseDefaultEmoji,
|
||||
customStickerPanel:
|
||||
|
|
@ -632,10 +665,18 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
|
|||
|
||||
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(
|
||||
{Key? key,
|
||||
this.child,
|
||||
this.providers,
|
||||
this.groupMemberList,
|
||||
this.textFieldController,
|
||||
required this.builder,
|
||||
this.model,
|
||||
|
|
@ -665,6 +706,7 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
|
|||
(String value) {
|
||||
textFieldController?.textEditingController?.text = value;
|
||||
},
|
||||
preGroupMemberList: groupMemberList,
|
||||
groupID: groupID,
|
||||
);
|
||||
model?.showC2cMessageEditStatus = (conversationType == ConvType.c2c
|
||||
|
|
|
|||
|
|
@ -22,7 +22,41 @@ class TimeDividerConfig {
|
|||
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 {
|
||||
/// 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.
|
||||
final TimeDividerConfig? timeDividerConfig;
|
||||
|
||||
|
|
@ -135,7 +169,6 @@ class TIMUIKitChatConfig {
|
|||
final String Function(String data)? faceURISuffix;
|
||||
|
||||
/// 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.
|
||||
/// [Default]: false.
|
||||
final bool isSupportMarkdownForTextMessage;
|
||||
|
|
@ -210,6 +243,7 @@ class TIMUIKitChatConfig {
|
|||
{this.onTapLink,
|
||||
this.timeDividerConfig,
|
||||
this.desktopStickerPanelHeight = 400,
|
||||
this.stickerPanelConfig,
|
||||
this.isGroupAdminRecallEnabled = false,
|
||||
this.isAutoReportRead = true,
|
||||
this.faceURIPrefix,
|
||||
|
|
|
|||
|
|
@ -88,13 +88,13 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
|
|||
Widget _getTimeStringForChatWidget(BuildContext context, TUITheme theme) {
|
||||
try {
|
||||
if (draftTimestamp != null && draftTimestamp != 0) {
|
||||
return Text(TimeAgo().getTimeStringForChat(draftTimestamp as int),
|
||||
return Text(TimeAgo().getTimeStringForChat(draftTimestamp as int) ?? "",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.conversationItemTitmeTextColor,
|
||||
));
|
||||
} else if (lastMsg != null) {
|
||||
return Text(TimeAgo().getTimeStringForChat(lastMsg!.timestamp as int),
|
||||
return Text(TimeAgo().getTimeStringForChat(lastMsg!.timestamp as int) ?? "",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: theme.conversationItemTitmeTextColor,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'dart:convert';
|
|||
import 'package:flutter/material.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/ui/utils/common_utils.dart';
|
||||
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
|
|||
});
|
||||
}
|
||||
} else {
|
||||
final newText = await _getLastMsgShowText(widget.lastMsg, widget.context);
|
||||
final newText = await _getLastMsgShowText(widget.lastMsg, widget.context) ?? "";
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
groupTipsAbstractText = newText;
|
||||
|
|
@ -92,7 +93,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> _getLastMsgShowText(
|
||||
Future<String?> _getLastMsgShowText(
|
||||
V2TimMessage? message, BuildContext context) async {
|
||||
final msgType = message!.elemType;
|
||||
switch (msgType) {
|
||||
|
|
@ -120,7 +121,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
|
|||
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
|
||||
return TIM_t("[聊天记录]");
|
||||
default:
|
||||
return TIM_t("未知消息");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,7 +163,7 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
|
|||
Text(_getAtMessage(),
|
||||
style: TextStyle(
|
||||
color: theme.cautionColor, fontSize: widget.fontSize)),
|
||||
Expanded(
|
||||
if(TencentUtils.checkString(groupTipsAbstractText) != null)Expanded(
|
||||
child: Text(
|
||||
groupTipsAbstractText,
|
||||
softWrap: true,
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ class _ContactListState extends TIMUIKitState<ContactList> {
|
|||
onChanged: (isChecked) {
|
||||
if (isChecked) {
|
||||
if (selectedMemberIsOverFlow()) {
|
||||
selectedMember = [item];
|
||||
setState(() {});
|
||||
return;
|
||||
}
|
||||
selectedMember.add(item);
|
||||
|
|
@ -285,6 +287,8 @@ class _ContactListState extends TIMUIKitState<ContactList> {
|
|||
selectedMember.remove(memberInfo);
|
||||
} else {
|
||||
if (selectedMemberIsOverFlow()) {
|
||||
selectedMember = [memberInfo];
|
||||
setState(() {});
|
||||
return;
|
||||
}
|
||||
selectedMember.add(memberInfo);
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
|
|||
}
|
||||
|
||||
_doubleClickAnimationListener = () {
|
||||
//print(_animation.value);
|
||||
//outputLogger.i(_animation.value);
|
||||
state.handleDoubleTap(
|
||||
scale: _doubleClickAnimation!.value,
|
||||
doubleTapPosition: pointerDownPosition);
|
||||
|
|
|
|||
|
|
@ -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 '';
|
||||
}
|
||||
return key;
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
|
@ -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/widgets/link_preview.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';
|
||||
|
||||
|
|
@ -11,11 +12,15 @@ class LinkPreviewEntry {
|
|||
static LinkPreviewText? getHyperlinksText(String messageText, bool isMarkdown,
|
||||
{Function(String)? onLinkTap,
|
||||
bool isEnableTextSelection = false,
|
||||
bool isUseDefaultEmoji = false,
|
||||
List customEmojiStickerList = const []}) {
|
||||
bool isUseQQPackage = false,
|
||||
bool isUseTencentCloudChatPackage = false,
|
||||
List<CustomEmojiFaceData> customEmojiStickerList = const []}) {
|
||||
return ({TextStyle? style}) {
|
||||
return isMarkdown
|
||||
? LinkTextMarkdown(
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
customEmojiStickerList: customEmojiStickerList,
|
||||
isEnableTextSelection: isEnableTextSelection,
|
||||
messageText: addSpaceAfterLeftBracket(
|
||||
addSpaceBeforeHttp(replaceSingleNewlineWithTwo(messageText))),
|
||||
|
|
@ -26,7 +31,8 @@ class LinkPreviewEntry {
|
|||
messageText: messageText,
|
||||
style: style,
|
||||
onLinkTap: onLinkTap,
|
||||
isUseDefaultEmoji: isUseDefaultEmoji,
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
customEmojiStickerList: customEmojiStickerList);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,10 +5,17 @@ import 'package:extended_text/extended_text.dart';
|
|||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/emoji_text.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/compiler/md_text.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/renderer/md_image.dart';
|
||||
import 'package:tencent_im_base/base_widgets/tim_stateless_widget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
|
||||
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 {
|
||||
/// Callback for when link is tapped
|
||||
|
|
@ -22,9 +29,18 @@ class LinkTextMarkdown extends TIMStatelessWidget {
|
|||
|
||||
final bool? isEnableTextSelection;
|
||||
|
||||
final bool isUseQQPackage;
|
||||
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
const LinkTextMarkdown(
|
||||
{Key? key,
|
||||
required this.messageText,
|
||||
this.isUseQQPackage = false,
|
||||
this.isUseTencentCloudChatPackage = false,
|
||||
this.customEmojiStickerList = const [],
|
||||
this.isEnableTextSelection,
|
||||
this.onLinkTap,
|
||||
this.style})
|
||||
|
|
@ -33,7 +49,10 @@ class LinkTextMarkdown extends TIMStatelessWidget {
|
|||
@override
|
||||
Widget timBuild(BuildContext context) {
|
||||
return MarkdownBody(
|
||||
data: messageText,
|
||||
data: mdTextCompiler(messageText,
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
customEmojiStickerList: customEmojiStickerList),
|
||||
selectable: isEnableTextSelection ?? false,
|
||||
styleSheet: MarkdownStyleSheet.fromTheme(ThemeData(
|
||||
textTheme: TextTheme(
|
||||
|
|
@ -67,9 +86,11 @@ class LinkText extends TIMStatelessWidget {
|
|||
/// text style for default words
|
||||
final TextStyle? style;
|
||||
|
||||
final bool isUseDefaultEmoji;
|
||||
final bool isUseQQPackage;
|
||||
|
||||
final List customEmojiStickerList;
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
final bool? isEnableTextSelection;
|
||||
|
||||
|
|
@ -79,7 +100,8 @@ class LinkText extends TIMStatelessWidget {
|
|||
this.onLinkTap,
|
||||
this.isEnableTextSelection,
|
||||
this.style,
|
||||
this.isUseDefaultEmoji = false,
|
||||
this.isUseQQPackage = false,
|
||||
this.isUseTencentCloudChatPackage = false,
|
||||
this.customEmojiStickerList = const []})
|
||||
: super(key: key);
|
||||
|
||||
|
|
@ -151,26 +173,10 @@ class LinkText extends TIMStatelessWidget {
|
|||
},
|
||||
style: style ?? const TextStyle(fontSize: 16.0),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseDefaultEmoji: isUseDefaultEmoji,
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
isUseTencentCloudChatPackage: isUseTencentCloudChatPackage,
|
||||
customEmojiStickerList: customEmojiStickerList,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,13 +324,15 @@ class TUIKitWidePopup {
|
|||
if (isLocalResource) {
|
||||
videoController = VideoPlayerController.file(File(mediaPath));
|
||||
} else {
|
||||
videoController = VideoPlayerController.networkUrl(Uri.parse(mediaPath));
|
||||
videoController =
|
||||
VideoPlayerController.networkUrl(Uri.parse(mediaPath));
|
||||
}
|
||||
|
||||
await videoController.initialize();
|
||||
aspectRatioFinal = videoController.value.aspectRatio;
|
||||
|
||||
chewieController = ChewieController(
|
||||
allowFullScreen: false,
|
||||
videoPlayerController: videoController,
|
||||
aspectRatio: aspectRatioFinal,
|
||||
autoPlay: true,
|
||||
|
|
@ -340,12 +342,9 @@ class TUIKitWidePopup {
|
|||
|
||||
mediaWidget = Chewie(controller: chewieController);
|
||||
} else {
|
||||
mediaWidget = FittedBox(
|
||||
fit: BoxFit.contain,
|
||||
child: isLocalResource
|
||||
? Image.file(File(mediaPath), fit: BoxFit.contain)
|
||||
: Image.network(mediaPath, fit: BoxFit.contain),
|
||||
);
|
||||
mediaWidget = isLocalResource
|
||||
? Image.file(File(mediaPath), fit: BoxFit.contain)
|
||||
: Image.network(mediaPath, fit: BoxFit.contain);
|
||||
}
|
||||
|
||||
showDialog(
|
||||
|
|
|
|||
34
pubspec.lock
34
pubspec.lock
|
|
@ -442,7 +442,7 @@ packages:
|
|||
source: hosted
|
||||
version: "11.0.2"
|
||||
fast_i18n:
|
||||
dependency: "direct dev"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fast_i18n
|
||||
sha256: f0039a3c1f5f3b7deafefdbb5222d7eb1ee9c2c2fe1222b648b285711b2c7570
|
||||
|
|
@ -811,6 +811,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1276,42 +1284,42 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: tencent_chat_i18n_tool
|
||||
sha256: ac8171d2574ed18babedd0cb67e937e255bf02fcb72f55408d033f74d4b11949
|
||||
sha256: "0ee982e814bedd0aea4751b972901c6cfcfb224cfeb8e13ae02e43c0b8a58bbc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3+2"
|
||||
version: "2.2.0"
|
||||
tencent_cloud_chat_sdk:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tencent_cloud_chat_sdk
|
||||
sha256: f98bdb55164051e2b196cac6e2e79e60248ed8351dc5a91d25568712ccb15839
|
||||
sha256: "013f8c9d96bbeed06d5fe971b7802d8ddf830c776332d6c6de6ccb9de8956d83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.7"
|
||||
version: "5.1.8"
|
||||
tencent_cloud_uikit_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: tencent_cloud_uikit_core
|
||||
sha256: "0a0f43e4c4241b25d12a9e9f0ee91922ac800a42229d97e3d21d16041ace3104"
|
||||
sha256: acb3bae877428457318b8c5604a6c263957b6df3454ed3e30e8b6f620c6b2cd9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
version: "1.1.0"
|
||||
tencent_im_base:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: tencent_im_base
|
||||
sha256: "0db83050452486571ee4ac07280a2fb4bbc07d297a54235b5cf12e46e79267d0"
|
||||
sha256: bc5eb080090038d21c879480c06d3ed7cb4b1dcc2cbe894189613eadf08cf7c5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "3.0.1"
|
||||
tencent_im_sdk_plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tencent_im_sdk_plugin_platform_interface
|
||||
sha256: "6a1f053567246148ad40667f2ab71d82bcee0d5d0c12e587340d2796c342b87e"
|
||||
sha256: "1f9814d654dc1ad0a4cb62936f0849defac058c3bdca471472efc8b64b63cc5e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.21"
|
||||
version: "0.3.22"
|
||||
tencent_keyboard_visibility:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1356,10 +1364,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: tim_ui_kit_sticker_plugin
|
||||
sha256: c9b0c1261bb51a5dee54bb50c6a106061b1bb10731b9c815fc5175afa60175e2
|
||||
sha256: db8143aea26eda5feec5ec2efc5a31b2c56928cd807537778829698f3c4efec5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "3.0.1+1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
10
pubspec.yaml
10
pubspec.yaml
|
|
@ -1,6 +1,6 @@
|
|||
name: tencent_cloud_chat_uikit
|
||||
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
|
||||
version: 2.1.3+1
|
||||
version: 2.2.0
|
||||
homepage: https://www.tencentcloud.com/products/im?from=pub
|
||||
repository: https://github.com/TencentCloud/tc-chat-uikit-flutter
|
||||
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
|
||||
|
|
@ -62,12 +62,12 @@ dependencies:
|
|||
uuid: ^3.0.6
|
||||
tencent_open_file: ^4.0.10
|
||||
tencent_keyboard_visibility: ^1.0.1
|
||||
tim_ui_kit_sticker_plugin: ^2.2.1
|
||||
tencent_im_base: ^2.0.1
|
||||
tim_ui_kit_sticker_plugin: ^3.0.1+1
|
||||
tencent_im_base: ^3.0.1
|
||||
fc_native_video_thumbnail: any
|
||||
audioplayers: ^3.0.1
|
||||
path: ^1.8.1
|
||||
tencent_cloud_uikit_core: ^1.0.7
|
||||
tencent_cloud_uikit_core: ^1.1.0
|
||||
pasteboard: ^0.2.0
|
||||
desktop_drop: ^0.4.1
|
||||
device_info_plus: any
|
||||
|
|
@ -75,11 +75,11 @@ dependencies:
|
|||
csslib: 0.17.2
|
||||
diff_match_patch: ^0.4.1
|
||||
markdown: ^7.1.0
|
||||
logger: ^2.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.0
|
||||
build_runner: any
|
||||
fast_i18n: ^5.12.2
|
||||
lints: ^1.0.1
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
|
|
|
|||
Loading…
Reference in New Issue