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