feat: Upgrade to 2.5.1+4

This commit is contained in:
anonymous 2024-04-25 16:12:14 +08:00
parent da84ca9bc6
commit 68dc760e32
28 changed files with 1660 additions and 946 deletions

View File

@ -3,6 +3,7 @@
## Improvements ## Improvements
* Improved memory usage, enhancing performance. * Improved memory usage, enhancing performance.
* Improved the logger storage.
# 2.5.0 # 2.5.0

View File

@ -20,6 +20,7 @@ import shared_preferences_foundation
import sqflite import sqflite
import tencent_cloud_chat_sdk import tencent_cloud_chat_sdk
import url_launcher_macos import url_launcher_macos
import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
@ -37,4 +38,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
TencentCloudChatSdkPlugin.register(with: registry.registrar(forPlugin: "TencentCloudChatSdkPlugin")) TencentCloudChatSdkPlugin.register(with: registry.registrar(forPlugin: "TencentCloudChatSdkPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
} }

View File

@ -161,14 +161,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.3.1"
chewie_for_us: chewie:
dependency: transitive dependency: transitive
description: description:
name: chewie_for_us name: chewie
sha256: "0307723e811508d361fffa6f8bbd9040b1bfea5536544e4d655e10c27de002ec" sha256: "8bc4ac4cf3f316e50a25958c0f5eb9bb12cf7e8308bb1d74a43b230da2cfc144"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "1.7.5"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -241,6 +241,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.10" version: "0.2.10"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.dev"
source: hosted
version: "0.7.10"
desktop_drop: desktop_drop:
dependency: transitive dependency: transitive
description: description:
@ -1233,33 +1241,33 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: tencent_cloud_chat_sdk name: tencent_cloud_chat_sdk
sha256: a78f1f20dc9ebe40aee1bbb47da097780028434d77e97774fbe733debb21e18e sha256: "358e79b51aba5457418d3bb87e0bbd0f088a1eaf4c4463d09bdda93d1d655aa3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.7.5296" version: "7.9.5672"
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.5.1" version: "2.5.1+2"
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: "7ddb2c034e5f832261ba268957e282b7c2e738acb1d21aa40c62dad4eaa433ea" sha256: "61a5400b3fe75c00252272469f332e7ec07f6d1932ee636a3f2b919cf9805cb8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.2" version: "1.6.0"
tencent_im_base: tencent_im_base:
dependency: transitive dependency: transitive
description: description:
name: tencent_im_base name: tencent_im_base
sha256: "035d97d24bebb87654700d4afc8227de8721a259ef5d0195f3207cb0eb0cdc7a" sha256: daee1faac70fdf5fa4a53576db4fb7268ba5d897cc036353d3114a31abb76fb1
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.775296" version: "3.3.775297"
tencent_im_sdk_plugin_desktop: tencent_im_sdk_plugin_desktop:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1516,22 +1524,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "13.0.0" version: "13.0.0"
wakelock_for_us: wakelock_plus:
dependency: transitive dependency: transitive
description: description:
name: wakelock_for_us name: wakelock_plus
sha256: a5bdd445e51a617f7c24be8165230391447301f622aacd050038cee7b41989b4 sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.3+1" version: "1.1.4"
wakelock_platform_interface: wakelock_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: wakelock_platform_interface name: wakelock_plus_platform_interface
sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0" version: "1.1.0"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:

View File

@ -36,6 +36,10 @@ abstract class DefaultLifeCycle {
return message; return message;
} }
static Future<V2TimMessage> defaultTwoMessagesSolution(V2TimMessage message, [V2TimMessage? repliedMessage]) async {
return message;
}
static Future<List<V2TimMessage>> defaultMessageListSolution(List<V2TimMessage> list) async { static Future<List<V2TimMessage>> defaultMessageListSolution(List<V2TimMessage> list) async {
return list; return list;
} }

View File

@ -11,7 +11,7 @@ class ChatLifeCycle {
/// Before a new message will be sent. /// Before a new message will be sent.
/// Returns null can block the message from sending. /// Returns null can block the message from sending.
MessageFunction messageWillSend; Future<V2TimMessage?> Function(V2TimMessage message, [V2TimMessage? repliedMessage]) messageWillSend;
/// After a new message been sent. /// After a new message been sent.
MessageFunctionNullCallback messageDidSend; MessageFunctionNullCallback messageDidSend;
@ -43,7 +43,7 @@ class ChatLifeCycle {
this.shouldDeleteMessage = DefaultLifeCycle.defaultAsyncBooleanSolution, this.shouldDeleteMessage = DefaultLifeCycle.defaultAsyncBooleanSolution,
this.messageDidSend = DefaultLifeCycle.defaultNullCallbackSolution, this.messageDidSend = DefaultLifeCycle.defaultNullCallbackSolution,
this.didGetHistoricalMessageList = DefaultLifeCycle.defaultMessageListSolution, this.didGetHistoricalMessageList = DefaultLifeCycle.defaultMessageListSolution,
this.messageWillSend = DefaultLifeCycle.defaultMessageSolution, this.messageWillSend = DefaultLifeCycle.defaultTwoMessagesSolution,
this.modifiedMessageWillMount = DefaultLifeCycle.defaultMessageSolution, this.modifiedMessageWillMount = DefaultLifeCycle.defaultMessageSolution,
this.newMessageWillMount = DefaultLifeCycle.defaultMessageSolution, this.newMessageWillMount = DefaultLifeCycle.defaultMessageSolution,
this.messageShouldMount = DefaultLifeCycle.defaultBooleanSolution, this.messageShouldMount = DefaultLifeCycle.defaultBooleanSolution,

View File

@ -17,7 +17,6 @@ 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/logger.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/logger.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/platform.dart';
enum ConvType { none, c2c, group } enum ConvType { none, c2c, group }
@ -504,6 +503,11 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
} }
_onReceiveNewMsg(V2TimMessage msgComing) async { _onReceiveNewMsg(V2TimMessage msgComing) async {
final convID = TencentUtils.checkString(msgComing.userID) ?? msgComing.groupID;
if(convID != currentSelectedConv){
return;
}
final V2TimMessage? newMsg = _lifeCycle?.newMessageWillMount != null ? await _lifeCycle?.newMessageWillMount(msgComing) : msgComing; final V2TimMessage? newMsg = _lifeCycle?.newMessageWillMount != null ? await _lifeCycle?.newMessageWillMount(msgComing) : msgComing;
if (newMsg == null) { if (newMsg == null) {
return; return;
@ -511,13 +515,12 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
// check the message is editing status msg. and flutter is only support the latest version // check the message is editing status msg. and flutter is only support the latest version
bool isEditMessage = _editStatusCheck(msgComing); bool isEditMessage = _editStatusCheck(msgComing);
// if the message is edit status message dont up to screen // if the message is edit status message don't up to screen
if (isEditMessage) { if (isEditMessage) {
return; return;
} }
_checkFromUserisActive(msgComing); _checkFromUserisActive(msgComing);
final convID = TencentUtils.checkString(newMsg.userID) ?? newMsg.groupID;
final convType = TencentUtils.checkString(newMsg.groupID) != null ? ConvType.group : ConvType.c2c; final convType = TencentUtils.checkString(newMsg.groupID) != null ? ConvType.group : ConvType.c2c;
if (convID != null && convID == currentSelectedConv) { if (convID != null && convID == currentSelectedConv) {
final position = getMessageListPosition(convID); final position = getMessageListPosition(convID);
@ -557,7 +560,8 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
} }
} }
} else if (convID != null) { } else if (convID != null) {
final currentMsg = _messageListMap[convID] ?? []; final tempCurrentMsgList = _messageListMap[convID] ?? [];
final currentMsg = tempCurrentMsgList..sublist(max(0, (tempCurrentMsgList.length - 20)));
_messageListMap[convID] = [newMsg, ...currentMsg]; _messageListMap[convID] = [newMsg, ...currentMsg];
notifyListeners(); notifyListeners();
} }
@ -641,6 +645,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
Future<void> onMessageDownloadProgressCallback(V2TimMessageDownloadProgress messageProgress) async { Future<void> onMessageDownloadProgressCallback(V2TimMessageDownloadProgress messageProgress) async {
final currentProgress = getMessageProgress(messageProgress.msgID); final currentProgress = getMessageProgress(messageProgress.msgID);
print("onMessageDownloadProgressCallback, ${messageProgress.type} - ${messageProgress.isFinish} - ${messageProgress.currentSize} - $currentProgress - ");
if (messageProgress.isError || messageProgress.errorCode != 0) { if (messageProgress.isError || messageProgress.errorCode != 0) {
V2TimMessage? message = await _findAndRetrieveMessage(messageProgress.msgID); V2TimMessage? message = await _findAndRetrieveMessage(messageProgress.msgID);
@ -666,7 +671,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
if (message != null) { if (message != null) {
bool isImageType = message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE; bool isImageType = message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE;
bool isVideoType = message.elemType == MessageElemType.V2TIM_ELEM_TYPE_VIDEO; bool isVideoType = message.elemType == MessageElemType.V2TIM_ELEM_TYPE_VIDEO;
final originalImageType = PlatformUtils().isIOS ? 1 : 0; const originalImageType = 0;
if (!isImageType && !isVideoType) { if (!isImageType && !isVideoType) {
_updateMessageLocationAndDownloadFile(messageProgress); _updateMessageLocationAndDownloadFile(messageProgress);
} else if ((isImageType && messageProgress.type == originalImageType) || (isVideoType && !messageProgress.isSnapshot)) { } else if ((isImageType && messageProgress.type == originalImageType) || (isVideoType && !messageProgress.isSnapshot)) {

View File

@ -119,9 +119,9 @@ class TUIConversationViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
}, onSyncServerFinish: () { }, onSyncServerFinish: () {
// Remove the process to load such a many of conversations after launching // Remove the process to load such a many of conversations after launching
// if (!PlatformUtils().isWeb) { if (!PlatformUtils().isWeb) {
// loadInitConversation(); loadInitConversation();
// } }
}); });
} }

View File

@ -95,9 +95,6 @@ class CoreServicesImpl implements CoreServices {
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), () {

View File

@ -16,7 +16,8 @@ class MessageServiceImpl extends MessageService {
@override @override
Future<MessageListResponse> getHistoryMessageListV2({ Future<MessageListResponse> getHistoryMessageListV2({
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG, HistoryMsgGetTypeEnum getType =
HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
String? userID, String? userID,
String? groupID, String? groupID,
int lastMsgSeq = -1, int lastMsgSeq = -1,
@ -25,8 +26,16 @@ class MessageServiceImpl extends MessageService {
List<int>? messageTypeList, List<int>? messageTypeList,
}) async { }) async {
bool haveMoreData = true; bool haveMoreData = true;
final res = final res = await TencentImSDKPlugin.v2TIMManager
await TencentImSDKPlugin.v2TIMManager.getMessageManager().getHistoryMessageList(count: count, getType: getType, userID: userID, groupID: groupID, lastMsgID: lastMsgID, lastMsgSeq: lastMsgSeq, messageTypeList: messageTypeList); .getMessageManager()
.getHistoryMessageList(
count: count,
getType: getType,
userID: userID,
groupID: groupID,
lastMsgID: lastMsgID,
lastMsgSeq: lastMsgSeq,
messageTypeList: messageTypeList);
final List<V2TimMessage> responseMessageList = res.data ?? []; final List<V2TimMessage> responseMessageList = res.data ?? [];
final conversationID = userID ?? groupID; final conversationID = userID ?? groupID;
final cachedMessageList = messageListMap[conversationID]; final cachedMessageList = messageListMap[conversationID];
@ -36,29 +45,41 @@ class MessageServiceImpl extends MessageService {
combinedMessageList = [...cachedMessageList, ...responseMessageList]; combinedMessageList = [...cachedMessageList, ...responseMessageList];
// //
} else { } else {
final bool existSendingMessage = sendingMessage[conversationID] != null && sendingMessage[conversationID]!.isNotEmpty; final bool existSendingMessage = sendingMessage[conversationID] != null &&
sendingMessage[conversationID]!.isNotEmpty;
// //
if (existSendingMessage) { if (existSendingMessage) {
combinedMessageList = [...sendingMessage[conversationID]!, ...responseMessageList]; combinedMessageList = [
...sendingMessage[conversationID]!,
...responseMessageList
];
} else { } else {
sendingMessage.remove(conversationID); sendingMessage.remove(conversationID);
combinedMessageList = responseMessageList; combinedMessageList = responseMessageList;
} }
} }
if (res.code != 0) { if (res.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
} }
if (responseMessageList.isEmpty || (!PlatformUtils().isWeb && responseMessageList.length < count) || (PlatformUtils().isWeb && responseMessageList.length < min(count, 20))) { if (responseMessageList.isEmpty ||
(!PlatformUtils().isWeb && responseMessageList.length < count) ||
(PlatformUtils().isWeb &&
responseMessageList.length < min(count, 20))) {
haveMoreData = false; haveMoreData = false;
} else { } else {
haveMoreData = true; haveMoreData = true;
} }
return MessageListResponse(haveMoreData: haveMoreData, data: combinedMessageList); return MessageListResponse(
haveMoreData: haveMoreData, data: combinedMessageList);
} }
@override @override
Future<List<V2TimMessage>> getHistoryMessageList({ Future<List<V2TimMessage>> getHistoryMessageList({
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG, HistoryMsgGetTypeEnum getType =
HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
String? userID, String? userID,
String? groupID, String? groupID,
int lastMsgSeq = -1, int lastMsgSeq = -1,
@ -66,30 +87,53 @@ class MessageServiceImpl extends MessageService {
String? lastMsgID, String? lastMsgID,
List<int>? messageTypeList, List<int>? messageTypeList,
}) async { }) async {
final res = final res = await TencentImSDKPlugin.v2TIMManager
await TencentImSDKPlugin.v2TIMManager.getMessageManager().getHistoryMessageList(count: count, getType: getType, userID: userID, groupID: groupID, lastMsgID: lastMsgID, lastMsgSeq: lastMsgSeq, messageTypeList: messageTypeList); .getMessageManager()
.getHistoryMessageList(
count: count,
getType: getType,
userID: userID,
groupID: groupID,
lastMsgID: lastMsgID,
lastMsgSeq: lastMsgSeq,
messageTypeList: messageTypeList);
final reponseMessageList = res.data ?? []; final reponseMessageList = res.data ?? [];
if (res.code != 0) { if (res.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
} }
return reponseMessageList; return reponseMessageList;
} }
@override @override
Future<V2TimMessageListResult?> getHistoryMessageListWithComplete({ Future<V2TimMessageListResult?> getHistoryMessageListWithComplete({
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG, HistoryMsgGetTypeEnum getType =
HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
String? userID, String? userID,
String? groupID, String? groupID,
int lastMsgSeq = -1, int lastMsgSeq = 0,
required int count, required int count,
String? lastMsgID, String? lastMsgID,
List<int>? messageTypeList, List<int>? messageTypeList,
}) async { }) async {
final res = final res = await TencentImSDKPlugin.v2TIMManager
await TencentImSDKPlugin.v2TIMManager.getMessageManager().getHistoryMessageListV2(count: count, getType: getType, userID: userID, groupID: groupID, lastMsgID: lastMsgID, lastMsgSeq: lastMsgSeq, messageTypeList: messageTypeList); .getMessageManager()
.getHistoryMessageListV2(
count: count,
getType: getType,
userID: userID,
groupID: groupID,
lastMsgID: lastMsgID,
lastMsgSeq: lastMsgSeq,
messageTypeList: messageTypeList);
final responseMessageList = res.data; final responseMessageList = res.data;
if (res.code != 0) { if (res.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
} }
return responseMessageList; return responseMessageList;
} }
@ -98,31 +142,45 @@ class MessageServiceImpl extends MessageService {
Future addSimpleMsgListener({ Future addSimpleMsgListener({
required V2TimSimpleMsgListener listener, required V2TimSimpleMsgListener listener,
}) async { }) async {
return TencentImSDKPlugin.v2TIMManager.addSimpleMsgListener(listener: listener); return TencentImSDKPlugin.v2TIMManager
.addSimpleMsgListener(listener: listener);
} }
@override @override
Future<void> removeSimpleMsgListener({V2TimSimpleMsgListener? listener}) { Future<void> removeSimpleMsgListener({V2TimSimpleMsgListener? listener}) {
return TencentImSDKPlugin.v2TIMManager.removeSimpleMsgListener(listener: listener); return TencentImSDKPlugin.v2TIMManager
.removeSimpleMsgListener(listener: listener);
} }
@override @override
Future<void> addAdvancedMsgListener({ Future<void> addAdvancedMsgListener({
required V2TimAdvancedMsgListener listener, required V2TimAdvancedMsgListener listener,
}) { }) {
return TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(listener: listener); return TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.addAdvancedMsgListener(listener: listener);
} }
@override @override
Future<V2TimValueCallback<V2TimGroupMessageReadMemberList>> getGroupMessageReadMemberList({ Future<V2TimValueCallback<V2TimGroupMessageReadMemberList>>
getGroupMessageReadMemberList({
required String messageID, required String messageID,
required GetGroupMessageReadMemberListFilter filter, required GetGroupMessageReadMemberListFilter filter,
int nextSeq = 0, int nextSeq = 0,
int count = 100, int count = 100,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().getGroupMessageReadMemberList(messageID: messageID, filter: filter, nextSeq: nextSeq, count: count); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.getGroupMessageReadMemberList(
messageID: messageID,
filter: filter,
nextSeq: nextSeq,
count: count);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -131,9 +189,14 @@ class MessageServiceImpl extends MessageService {
Future<V2TimValueCallback<List<V2TimMessageReceipt>>> getMessageReadReceipts({ Future<V2TimValueCallback<List<V2TimMessageReceipt>>> getMessageReadReceipts({
required List<String> messageIDList, required List<String> messageIDList,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().getMessageReadReceipts(messageIDList: messageIDList); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.getMessageReadReceipts(messageIDList: messageIDList);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -142,43 +205,61 @@ class MessageServiceImpl extends MessageService {
Future<V2TimCallback> sendMessageReadReceipts({ Future<V2TimCallback> sendMessageReadReceipts({
required List<String> messageIDList, required List<String> messageIDList,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessageReadReceipts(messageIDList: messageIDList); return _retryMarkMessageAsRead(action: (){
if (result.code != 0) { return TencentImSDKPlugin.v2TIMManager
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); .getMessageManager()
} .sendMessageReadReceipts(messageIDList: messageIDList);
return result; });
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createTextMessage({required String text}) async { Future<V2TimMsgCreateInfoResult?> createTextMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createTextMessage(text: text); {required String text}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createTextMessage(text: text);
if (res.code == 0) { if (res.code == 0) {
final messageResult = res.data; final messageResult = res.data;
return messageResult; return messageResult;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createCustomMessage({required String data}) async { Future<V2TimMsgCreateInfoResult?> createCustomMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createCustomMessage(data: data); {required String data}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createCustomMessage(data: data);
if (res.code == 0) { if (res.code == 0) {
final messageResult = res.data; final messageResult = res.data;
return messageResult; return messageResult;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createFaceMessage({required int index, required String data}) async { Future<V2TimMsgCreateInfoResult?> createFaceMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createFaceMessage(index: index, data: data); {required int index, required String data}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createFaceMessage(index: index, data: data);
if (res.code == 0) { if (res.code == 0) {
final messageResult = res.data; final messageResult = res.data;
return messageResult; return messageResult;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@ -186,31 +267,51 @@ class MessageServiceImpl extends MessageService {
Future<V2TimValueCallback<V2TimMessage>> reSendMessage( Future<V2TimValueCallback<V2TimMessage>> reSendMessage(
{required String msgID, // ID {required String msgID, // ID
bool? onlineUserOnly}) async { bool? onlineUserOnly}) async {
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().reSendMessage(msgID: msgID, onlineUserOnly: onlineUserOnly ?? false); final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.reSendMessage(msgID: msgID, onlineUserOnly: onlineUserOnly ?? false);
if (res.code != 0) { if (res.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
} }
return res; return res;
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createTextAtMessage({required String text, required List<String> atUserList}) async { Future<V2TimMsgCreateInfoResult?> createTextAtMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createTextAtMessage(text: text, atUserList: atUserList); {required String text, required List<String> atUserList}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createTextAtMessage(text: text, atUserList: atUserList);
if (res.code == 0) { if (res.code == 0) {
final messageResult = res.data; final messageResult = res.data;
return messageResult; return messageResult;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createImageMessage({String? imageName, String? imagePath, dynamic inputElement}) async { Future<V2TimMsgCreateInfoResult?> createImageMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createImageMessage(imageName: imageName, imagePath: imagePath ?? "", inputElement: inputElement); {String? imageName, String? imagePath, dynamic inputElement}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createImageMessage(
imageName: imageName,
imagePath: imagePath ?? "",
inputElement: inputElement);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@ -219,11 +320,16 @@ class MessageServiceImpl extends MessageService {
required String soundPath, required String soundPath,
required int duration, required int duration,
}) async { }) async {
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createSoundMessage(soundPath: soundPath, duration: duration); final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createSoundMessage(soundPath: soundPath, duration: duration);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@ -240,19 +346,23 @@ class MessageServiceImpl extends MessageService {
String? cloudCustomData, String? cloudCustomData,
String? localCustomData, String? localCustomData,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessage( final result =
id: id, await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessage(
receiver: receiver, id: id,
groupID: groupID, receiver: receiver,
priority: priority, groupID: groupID,
onlineUserOnly: onlineUserOnly, priority: priority,
offlinePushInfo: offlinePushInfo, onlineUserOnly: onlineUserOnly,
needReadReceipt: needReadReceipt, offlinePushInfo: offlinePushInfo,
localCustomData: localCustomData, needReadReceipt: needReadReceipt,
cloudCustomData: cloudCustomData, localCustomData: localCustomData,
); cloudCustomData: cloudCustomData,
);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -264,22 +374,36 @@ class MessageServiceImpl extends MessageService {
}) async { }) async {
V2TimCallback result; V2TimCallback result;
if (kIsWeb) { if (kIsWeb) {
result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().deleteMessages(msgIDs: [], webMessageInstanceList: [webMessageInstance]); result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.deleteMessages(
msgIDs: [], webMessageInstanceList: [webMessageInstance]);
} else { } else {
result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().deleteMessageFromLocalStorage(msgID: msgID); result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.deleteMessageFromLocalStorage(msgID: msgID);
} }
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<V2TimCallback> revokeMessage({required String msgID, Object? webMessageInstance}) async { Future<V2TimCallback> revokeMessage(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().revokeMessage(msgID: msgID, webMessageInstatnce: webMessageInstance); {required String msgID, Object? webMessageInstance}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.revokeMessage(msgID: msgID, webMessageInstatnce: webMessageInstance);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -288,9 +412,14 @@ class MessageServiceImpl extends MessageService {
Future<V2TimCallback> clearC2CHistoryMessage({ Future<V2TimCallback> clearC2CHistoryMessage({
required String userID, required String userID,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().clearC2CHistoryMessage(userID: userID); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.clearC2CHistoryMessage(userID: userID);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -299,38 +428,77 @@ class MessageServiceImpl extends MessageService {
Future<V2TimCallback> clearGroupHistoryMessage({ Future<V2TimCallback> clearGroupHistoryMessage({
required String groupID, required String groupID,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().clearGroupHistoryMessage(groupID: groupID); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.clearGroupHistoryMessage(groupID: groupID);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
Future<V2TimCallback> _retryMarkMessageAsRead({
required Future<V2TimCallback> Function() action,
int retries = 3,
}) async {
V2TimCallback result;
int attempts = 0;
do {
result = await action();
if (result.code == 0) {
return result;
}
attempts++;
await Future.delayed(const Duration(milliseconds: 500));
} while (attempts < retries);
_coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
return result;
}
@override @override
Future<V2TimCallback> markC2CMessageAsRead({ Future<V2TimCallback> markC2CMessageAsRead({
required String userID, required String userID,
}) async { }) {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().markC2CMessageAsRead(userID: userID); return _retryMarkMessageAsRead(action: () {
if (result.code != 0) { return TencentImSDKPlugin.v2TIMManager
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); .getConversationManager()
} .cleanConversationUnreadMessageCount(
return result; conversationID: "c2c_$userID",
cleanTimestamp: 0,
cleanSequence: 0,
);
});
} }
@override @override
Future<V2TimCallback> markGroupMessageAsRead({ Future<V2TimCallback> markGroupMessageAsRead({
required String groupID, required String groupID,
}) async { }) {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().markGroupMessageAsRead(groupID: groupID); return _retryMarkMessageAsRead(action: () {
if (result.code != 0) { return TencentImSDKPlugin.v2TIMManager
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); .getConversationManager()
} .cleanConversationUnreadMessageCount(
return result; conversationID: "group_$groupID",
cleanTimestamp: 0,
cleanSequence: 0,
);
});
} }
@override @override
Future<void> removeAdvancedMsgListener({V2TimAdvancedMsgListener? listener}) async { Future<void> removeAdvancedMsgListener(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().removeAdvancedMsgListener(listener: listener); {V2TimAdvancedMsgListener? listener}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.removeAdvancedMsgListener(listener: listener);
return result; return result;
} }
@ -338,11 +506,16 @@ class MessageServiceImpl extends MessageService {
Future<List<V2TimMessage>?> downloadMergerMessage({ Future<List<V2TimMessage>?> downloadMergerMessage({
required String msgID, required String msgID,
}) async { }) async {
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().downloadMergerMessage(msgID: msgID); final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.downloadMergerMessage(msgID: msgID);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@ -350,11 +523,16 @@ class MessageServiceImpl extends MessageService {
Future<V2TimMsgCreateInfoResult?> createForwardMessage({ Future<V2TimMsgCreateInfoResult?> createForwardMessage({
required String msgID, required String msgID,
}) async { }) async {
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createForwardMessage(msgID: msgID); final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createForwardMessage(msgID: msgID);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@ -365,30 +543,62 @@ class MessageServiceImpl extends MessageService {
required List<String> abstractList, required List<String> abstractList,
required String compatibleText, required String compatibleText,
}) async { }) async {
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createMergerMessage(msgIDList: msgIDList, title: title, abstractList: abstractList, compatibleText: compatibleText); final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createMergerMessage(
msgIDList: msgIDList,
title: title,
abstractList: abstractList,
compatibleText: compatibleText);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@override @override
Future<V2TimCallback> deleteMessages({required List<String> msgIDs, List<dynamic>? webMessageInstanceList}) async { Future<V2TimCallback> deleteMessages(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().deleteMessages(msgIDs: msgIDs, webMessageInstanceList: webMessageInstanceList); {required List<String> msgIDs,
List<dynamic>? webMessageInstanceList}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.deleteMessages(
msgIDs: msgIDs, webMessageInstanceList: webMessageInstanceList);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createVideoMessage({String? videoPath, String? type, int? duration, String? snapshotPath, dynamic inputElement}) async { Future<V2TimMsgCreateInfoResult?> createVideoMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createVideoMessage(videoFilePath: videoPath ?? "", type: type ?? "", duration: duration ?? 1, snapshotPath: snapshotPath ?? "", inputElement: inputElement); {String? videoPath,
String? type,
int? duration,
String? snapshotPath,
dynamic inputElement}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createVideoMessage(
videoFilePath: videoPath ?? "",
type: type ?? "",
duration: duration ?? 1,
snapshotPath: snapshotPath ?? "",
inputElement: inputElement);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@ -401,38 +611,75 @@ class MessageServiceImpl extends MessageService {
bool needReadReceipt = false, bool needReadReceipt = false,
required V2TimMessage replyMessage, // required V2TimMessage replyMessage, //
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendReplyMessage(id: id, receiver: receiver, offlinePushInfo: offlinePushInfo, groupID: groupID, needReadReceipt: needReadReceipt, replyMessage: replyMessage); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.sendReplyMessage(
id: id,
receiver: receiver,
offlinePushInfo: offlinePushInfo,
groupID: groupID,
needReadReceipt: needReadReceipt,
replyMessage: replyMessage);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createFileMessage({String? filePath, required String fileName, dynamic inputElement}) async { Future<V2TimMsgCreateInfoResult?> createFileMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createFileMessage(filePath: filePath ?? "", fileName: fileName, inputElement: inputElement); {String? filePath,
required String fileName,
dynamic inputElement}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createFileMessage(
filePath: filePath ?? "",
fileName: fileName,
inputElement: inputElement);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@override @override
Future<V2TimMsgCreateInfoResult?> createLocationMessage({required String desc, required double longitude, required double latitude}) async { Future<V2TimMsgCreateInfoResult?> createLocationMessage(
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createLocationMessage(desc: desc, longitude: longitude, latitude: latitude); {required String desc,
required double longitude,
required double latitude}) async {
final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createLocationMessage(
desc: desc, longitude: longitude, latitude: latitude);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@override @override
Future<V2TimValueCallback<V2TimMessageSearchResult>> searchLocalMessages({required V2TimMessageSearchParam searchParam}) async { Future<V2TimValueCallback<V2TimMessageSearchResult>> searchLocalMessages(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().searchLocalMessages(searchParam: searchParam); {required V2TimMessageSearchParam searchParam}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.searchLocalMessages(searchParam: searchParam);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -441,19 +688,30 @@ class MessageServiceImpl extends MessageService {
Future<List<V2TimMessage>?> findMessages({ Future<List<V2TimMessage>?> findMessages({
required List<String> messageIDList, required List<String> messageIDList,
}) async { }) async {
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().findMessages(messageIDList: messageIDList); final res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.findMessages(messageIDList: messageIDList);
if (res.code == 0) { if (res.code == 0) {
return res.data; return res.data;
} }
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: res.desc,
errorCode: res.code));
return null; return null;
} }
@override @override
Future<V2TimCallback> setLocalCustomInt({required String msgID, required int localCustomInt}) async { Future<V2TimCallback> setLocalCustomInt(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().setLocalCustomInt(msgID: msgID, localCustomInt: localCustomInt); {required String msgID, required int localCustomInt}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.setLocalCustomInt(msgID: msgID, localCustomInt: localCustomInt);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -463,9 +721,14 @@ class MessageServiceImpl extends MessageService {
required List<String> userIDList, required List<String> userIDList,
required ReceiveMsgOptEnum opt, required ReceiveMsgOptEnum opt,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().setC2CReceiveMessageOpt(userIDList: userIDList, opt: opt); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.setC2CReceiveMessageOpt(userIDList: userIDList, opt: opt);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@ -475,55 +738,96 @@ class MessageServiceImpl extends MessageService {
required String groupID, required String groupID,
required ReceiveMsgOptEnum opt, required ReceiveMsgOptEnum opt,
}) async { }) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().setGroupReceiveMessageOpt(groupID: groupID, opt: opt); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.setGroupReceiveMessageOpt(groupID: groupID, opt: opt);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<V2TimValueCallback<V2TimMessageChangeInfo>> modifyMessage({required V2TimMessage message}) async { Future<V2TimValueCallback<V2TimMessageChangeInfo>> modifyMessage(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().modifyMessage(message: message); {required V2TimMessage message}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.modifyMessage(message: message);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<V2TimCallback> setLocalCustomData({required String msgID, required String localCustomData}) async { Future<V2TimCallback> setLocalCustomData(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().setLocalCustomData(msgID: msgID, localCustomData: localCustomData); {required String msgID, required String localCustomData}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.setLocalCustomData(msgID: msgID, localCustomData: localCustomData);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<V2TimValueCallback<V2TimMessageOnlineUrl>> getMessageOnlineUrl({required String msgID}) async { Future<V2TimValueCallback<V2TimMessageOnlineUrl>> getMessageOnlineUrl(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().getMessageOnlineUrl(msgID: msgID); {required String msgID}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.getMessageOnlineUrl(msgID: msgID);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<V2TimCallback> downloadMessage({required String msgID, required int messageType, required int imageType, required bool isSnapshot}) async { Future<V2TimCallback> downloadMessage(
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().downloadMessage(msgID: msgID, messageType: messageType, imageType: imageType, isSnapshot: isSnapshot); {required String msgID,
required int messageType,
required int imageType,
required bool isSnapshot}) async {
final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.downloadMessage(
msgID: msgID,
messageType: messageType,
imageType: imageType,
isSnapshot: isSnapshot);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result; return result;
} }
@override @override
Future<String> translateText(String text, String target) async { Future<String> translateText(String text, String target) async {
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().translateText(texts: [text], targetLanguage: target); final result = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.translateText(texts: [text], targetLanguage: target);
if (result.code != 0) { if (result.code != 0) {
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code)); _coreService.callOnCallback(TIMCallback(
type: TIMCallbackType.API_ERROR,
errorMsg: result.desc,
errorCode: result.code));
} }
return result.data?[text] ?? ""; return result.data?[text] ?? "";
} }

View File

@ -1,107 +1,15 @@
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/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
final outputLogger = Logger( final outputLogger = TencentCloudChatLog();
output: _outputLogger ?? logOutputGenerator(null),
);
TUIKitOutput? _outputLogger; class TencentCloudChatLog{
void i(String text){
TUIKitOutput logOutputGenerator(String? path) { if(!PlatformUtils().isWeb){
_outputLogger = TUIKitOutput(path); TencentImSDKPlugin.v2TIMManager
return _outputLogger!; .uikitTrace(trace: text);
}
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) {
print("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');
print("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();
}
});
}
}
}
}

View File

@ -32,11 +32,13 @@ class SoundPlayer {
_soundInterruptListener!(); _soundInterruptListener!();
} }
await _audioPlayer.setUrl(url); await _audioPlayer.setUrl(url);
await _audioPlayer.play(); await _audioPlayer.play();
} }
static stop() { static stop() {
_audioPlayer.stop(); _audioPlayer.stop();
} }
static dispose() { static dispose() {

View File

@ -1,19 +1,18 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_im_base/tencent_im_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/business_logic/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
class TIMUIKitHistoryMessageListTongueContainer extends StatefulWidget { class TIMUIKitHistoryMessageListTongueContainer extends StatefulWidget {
final Widget Function(void Function(), MessageListTongueType, int)? final Widget Function(void Function(), MessageListTongueType, int)? tongueItemBuilder;
tongueItemBuilder;
final List<V2TimGroupAtInfo?>? groupAtInfoList; final List<V2TimGroupAtInfo?>? groupAtInfoList;
final Function(String targetSeq) scrollToIndexBySeq; final Function(String targetSeq) scrollToIndexBySeq;
final AutoScrollController scrollController; final AutoScrollController scrollController;
@ -31,12 +30,10 @@ class TIMUIKitHistoryMessageListTongueContainer extends StatefulWidget {
}) : super(key: key); }) : super(key: key);
@override @override
State<StatefulWidget> createState() => State<StatefulWidget> createState() => _TIMUIKitHistoryMessageListTongueContainerState();
_TIMUIKitHistoryMessageListTongueContainerState();
} }
class _TIMUIKitHistoryMessageListTongueContainerState class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState<TIMUIKitHistoryMessageListTongueContainer> {
extends TIMUIKitState<TIMUIKitHistoryMessageListTongueContainer> {
bool isFinishJumpToAt = false; bool isFinishJumpToAt = false;
List<V2TimGroupAtInfo?>? groupAtInfoList = []; List<V2TimGroupAtInfo?>? groupAtInfoList = [];
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
@ -50,10 +47,8 @@ class _TIMUIKitHistoryMessageListTongueContainerState
} }
void changePositionState(HistoryMessagePosition newPosition) { void changePositionState(HistoryMessagePosition newPosition) {
if (globalModel.getMessageListPosition(widget.model.conversationID) != if (globalModel.getMessageListPosition(widget.model.conversationID) != newPosition) {
newPosition) { globalModel.setMessageListPosition(widget.model.conversationID, newPosition);
globalModel.setMessageListPosition(
widget.model.conversationID, newPosition);
} }
} }
@ -64,19 +59,11 @@ class _TIMUIKitHistoryMessageListTongueContainerState
if (offset <= 0.0 && conversationUnreadCount != 0) { if (offset <= 0.0 && conversationUnreadCount != 0) {
widget.model.showLatestUnread(); widget.model.showLatestUnread();
} }
if (widget.scrollController.offset <= if (widget.scrollController.offset <= widget.scrollController.position.minScrollExtent && !widget.scrollController.position.outOfRange && !widget.model.haveMoreLatestData) {
widget.scrollController.position.minScrollExtent &&
!widget.scrollController.position.outOfRange &&
!widget.model.haveMoreLatestData) {
changePositionState(HistoryMessagePosition.bottom); changePositionState(HistoryMessagePosition.bottom);
} else if (widget.scrollController.offset <= screenHeight * 1.6 && } else if (widget.scrollController.offset <= screenHeight * 1.6 && widget.scrollController.offset > 0 && !widget.scrollController.position.outOfRange && !widget.model.haveMoreLatestData) {
widget.scrollController.offset > 0 &&
!widget.scrollController.position.outOfRange &&
!widget.model.haveMoreLatestData) {
changePositionState(HistoryMessagePosition.inTwoScreen); changePositionState(HistoryMessagePosition.inTwoScreen);
} else if (widget.scrollController.offset > screenHeight * 1.6 && } else if (widget.scrollController.offset > screenHeight * 1.6 && !widget.scrollController.position.outOfRange && !widget.model.haveMoreLatestData) {
!widget.scrollController.position.outOfRange &&
!widget.model.haveMoreLatestData) {
changePositionState(HistoryMessagePosition.awayTwoScreen); changePositionState(HistoryMessagePosition.awayTwoScreen);
} }
} }
@ -85,15 +72,11 @@ class _TIMUIKitHistoryMessageListTongueContainerState
widget.scrollController.addListener(scrollHandler); widget.scrollController.addListener(scrollHandler);
} }
MessageListTongueType _getTongueValueType( MessageListTongueType _getTongueValueType(List<V2TimGroupAtInfo?>? groupAtInfoList) {
List<V2TimGroupAtInfo?>? groupAtInfoList) { if (globalModel.getMessageListPosition(widget.model.conversationID) == HistoryMessagePosition.notShowLatest) {
if (globalModel.getMessageListPosition(widget.model.conversationID) ==
HistoryMessagePosition.notShowLatest) {
return MessageListTongueType.none; return MessageListTongueType.none;
} }
if (groupAtInfoList != null && if (groupAtInfoList != null && groupAtInfoList.isNotEmpty && !isFinishJumpToAt) {
groupAtInfoList.isNotEmpty &&
!isFinishJumpToAt) {
if (groupAtInfoList[0]!.atType == 1) { if (groupAtInfoList[0]!.atType == 1) {
return MessageListTongueType.atMe; return MessageListTongueType.atMe;
} else { } else {
@ -109,8 +92,7 @@ class _TIMUIKitHistoryMessageListTongueContainerState
return MessageListTongueType.showUnread; return MessageListTongueType.showUnread;
} }
if (globalModel.getMessageListPosition(widget.model.conversationID) == if (globalModel.getMessageListPosition(widget.model.conversationID) == HistoryMessagePosition.awayTwoScreen) {
HistoryMessagePosition.awayTwoScreen) {
return MessageListTongueType.toLatest; return MessageListTongueType.toLatest;
} }
@ -128,14 +110,8 @@ class _TIMUIKitHistoryMessageListTongueContainerState
return Selector<TUIChatGlobalModel, Tuple2<HistoryMessagePosition, int>>( return Selector<TUIChatGlobalModel, Tuple2<HistoryMessagePosition, int>>(
builder: (context, value, child) { builder: (context, value, child) {
return Positioned( return Positioned(
bottom: _getTongueValueType(groupAtInfoList) != bottom: _getTongueValueType(groupAtInfoList) != MessageListTongueType.showPrevious ? 16 : null,
MessageListTongueType.showPrevious top: _getTongueValueType(groupAtInfoList) == MessageListTongueType.showPrevious ? 16 : null,
? 16
: null,
top: _getTongueValueType(groupAtInfoList) ==
MessageListTongueType.showPrevious
? 16
: null,
right: 16, right: 16,
child: TIMUIKitHistoryMessageListTongue( child: TIMUIKitHistoryMessageListTongue(
previousCount: widget.conversation.unreadCount ?? 0, previousCount: widget.conversation.unreadCount ?? 0,
@ -153,29 +129,24 @@ class _TIMUIKitHistoryMessageListTongueContainerState
} else { } else {
widget.scrollToIndexBySeq(groupAtInfoList!.removeAt(0)!.seq); widget.scrollToIndexBySeq(groupAtInfoList!.removeAt(0)!.seq);
} }
} else if ((widget.conversation.unreadCount ?? 0) > 20 && } else if ((widget.conversation.unreadCount ?? 0) > 20 && !isClickShowPrevious) {
!isClickShowPrevious) {
try { try {
isClickShowPrevious = true; isClickShowPrevious = true;
final String? lastSeqString = final String? lastSeqString = widget.conversation.lastMessage?.seq;
widget.conversation.lastMessage?.seq; final int? lastSeq = TencentUtils.checkString(lastSeqString) != null ? int.parse(lastSeqString!) : null;
final int? lastSeq =
TencentUtils.checkString(lastSeqString) != null
? int.parse(lastSeqString!)
: null;
final int? previousCount = widget.conversation.unreadCount; final int? previousCount = widget.conversation.unreadCount;
if (lastSeq != null && previousCount != null) { if (lastSeq != null && previousCount != null) {
final targetSeq = lastSeq - previousCount; final targetSeq = lastSeq - previousCount;
await widget.model await widget.model.loadListForSpecificMessage(seq: targetSeq);
.loadListForSpecificMessage(seq: targetSeq); // Future.delayed(const Duration(milliseconds: 100), () {
// widget.scrollToIndexBySeq((targetSeq + 1).toString()); // widget.scrollToIndexBySeq((targetSeq).toString());
// });
} }
} catch (e) { } catch (e) {
// TODO: // TODO:
} }
// widget.model.loadListForSpecificMessage(seq: count); // widget.model.loadListForSpecificMessage(seq: count);
} else if (value.item1 == HistoryMessagePosition.awayTwoScreen || } else if (value.item1 == HistoryMessagePosition.awayTwoScreen || globalModel.unreadCountForConversation > 0) {
globalModel.unreadCountForConversation > 0) {
widget.model.showLatestUnread(); widget.model.showLatestUnread();
widget.scrollController.animateTo( widget.scrollController.animateTo(
widget.scrollController.position.minScrollExtent, widget.scrollController.position.minScrollExtent,
@ -191,8 +162,7 @@ class _TIMUIKitHistoryMessageListTongueContainerState
); );
}, },
selector: (c, model) { selector: (c, model) {
final mesageListPosition = final mesageListPosition = model.getMessageListPosition(widget.model.conversationID);
model.getMessageListPosition(widget.model.conversationID);
final unreadCountForConversation = model.unreadCountForConversation; final unreadCountForConversation = model.unreadCountForConversation;
return Tuple2(mesageListPosition, unreadCountForConversation); return Tuple2(mesageListPosition, unreadCountForConversation);
}, },

View File

@ -646,7 +646,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
alignment: Alignment.center, alignment: Alignment.center,
margin: const EdgeInsets.symmetric(vertical: 20), margin: const EdgeInsets.symmetric(vertical: 20),
child: Text( child: Text(
model.chatConfig.timeDividerConfig?.timestampParser != null ? (model.chatConfig.timeDividerConfig?.timestampParser!(timeStamp))! : TimeAgo().getTimeForMessage(1709740800), model.chatConfig.timeDividerConfig?.timestampParser != null ? (model.chatConfig.timeDividerConfig?.timestampParser!(timeStamp))! : TimeAgo().getTimeForMessage(timeStamp),
style: widget.themeData?.timelineTextStyle ?? style: widget.themeData?.timelineTextStyle ??
TextStyle( TextStyle(
fontSize: 12, fontSize: 12,

View File

@ -213,6 +213,9 @@ class TIMUIKitMessageTooltipState
widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE && widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE &&
fileBeenDownloaded); fileBeenDownloaded);
final dynamicQuote =
model.chatConfig.isAtWhenReplyDynamic?.call(widget.message);
final List<MessageToolTipItem> defaultTipsList = [ final List<MessageToolTipItem> defaultTipsList = [
if (fileBeenDownloaded) if (fileBeenDownloaded)
MessageToolTipItem( MessageToolTipItem(
@ -240,7 +243,8 @@ class TIMUIKitMessageTooltipState
onClick: () => _onTap("forwardMessage", model)), onClick: () => _onTap("forwardMessage", model)),
if (shouldShowReplyAction) if (shouldShowReplyAction)
MessageToolTipItem( MessageToolTipItem(
label: TIM_t(model.chatConfig.isAtWhenReply ? "回复" : "引用"), label: TIM_t(
(dynamicQuote ?? model.chatConfig.isAtWhenReply) ? "回复" : "引用"),
id: "replyMessage", id: "replyMessage",
iconImageAsset: "images/reply_message.png", iconImageAsset: "images/reply_message.png",
onClick: () => _onTap("replyMessage", model)), onClick: () => _onTap("replyMessage", model)),
@ -502,7 +506,7 @@ class TIMUIKitMessageTooltipState
} else if (widget.message.elemType == } else if (widget.message.elemType ==
MessageElemType.V2TIM_ELEM_TYPE_IMAGE) { MessageElemType.V2TIM_ELEM_TYPE_IMAGE) {
final savePath = (TencentUtils.checkString( final savePath = (TencentUtils.checkString(
widget.message.imageElem!.imageList?[0]?.localUrl) ?? widget.message.imageElem!.imageList?[0]?.localUrl) ??
TencentUtils.checkString(widget.message.imageElem?.path) ?? TencentUtils.checkString(widget.message.imageElem?.path) ??
""); "");
copyImageToClipboard(savePath); copyImageToClipboard(savePath);
@ -510,12 +514,14 @@ class TIMUIKitMessageTooltipState
break; break;
case "replyMessage": case "replyMessage":
model.repliedMessage = widget.message; model.repliedMessage = widget.message;
final dynamicQuote =
model.chatConfig.isAtWhenReplyDynamic?.call(widget.message);
final isSelf = widget.message.isSelf ?? true; final isSelf = widget.message.isSelf ?? true;
final isGroup = final isGroup =
TencentUtils.checkString(widget.message.groupID) != null; TencentUtils.checkString(widget.message.groupID) != null;
final isAtWhenReply = !isSelf && final isAtWhenReply = !isSelf &&
isGroup && isGroup &&
widget.allowAtUserWhenReply && (dynamicQuote ?? widget.allowAtUserWhenReply) &&
widget.onLongPressForOthersHeadPortrait != null; widget.onLongPressForOthersHeadPortrait != null;
/// If replying to a self message, do not add a at tag, only requestFocus. /// If replying to a self message, do not add a at tag, only requestFocus.

View File

@ -32,7 +32,16 @@ class TIMUIKitFileElem extends StatefulWidget {
final bool? isShowMessageReaction; final bool? isShowMessageReaction;
final TUIChatSeparateViewModel chatModel; final TUIChatSeparateViewModel chatModel;
const TIMUIKitFileElem({Key? key, required this.chatModel, required this.messageID, required this.fileElem, required this.isSelf, required this.isShowJump, this.clearJump, required this.message, this.isShowMessageReaction}) const TIMUIKitFileElem(
{Key? key,
required this.chatModel,
required this.messageID,
required this.fileElem,
required this.isSelf,
required this.isShowJump,
this.clearJump,
required this.message,
this.isShowMessageReaction})
: super(key: key); : super(key: key);
@override @override
@ -44,14 +53,19 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
bool isWebDownloading = false; bool isWebDownloading = false;
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
int downloadProgress = 0; int downloadProgress = 0;
late V2TimAdvancedMsgListener advancedMsgListener; V2TimAdvancedMsgListener? advancedMsgListener;
final GlobalKey containerKey = GlobalKey(); final GlobalKey containerKey = GlobalKey();
double? containerHeight; double? containerHeight;
bool? _downloadFailed = false; bool? _downloadFailed = false;
@override @override
void dispose() { void dispose() {
TencentImSDKPlugin.v2TIMManager.getMessageManager().removeAdvancedMsgListener(listener: advancedMsgListener); if (advancedMsgListener != null) {
TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.removeAdvancedMsgListener(listener: advancedMsgListener);
advancedMsgListener = null;
}
super.dispose(); super.dispose();
} }
@ -63,8 +77,15 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
hasFile(); hasFile();
}); });
} }
}
Future<bool> addAdvancedMsgListenerForDownload() async {
if(advancedMsgListener != null){
return false;
}
advancedMsgListener = V2TimAdvancedMsgListener( advancedMsgListener = V2TimAdvancedMsgListener(
onMessageDownloadProgressCallback: (V2TimMessageDownloadProgress messageProgress) async { onMessageDownloadProgressCallback:
(V2TimMessageDownloadProgress messageProgress) async {
if (messageProgress.msgID == widget.message.msgID) { if (messageProgress.msgID == widget.message.msgID) {
if (messageProgress.isError || messageProgress.errorCode != 0) { if (messageProgress.isError || messageProgress.errorCode != 0) {
setState(() { setState(() {
@ -79,12 +100,17 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
downloadProgress = 100; downloadProgress = 100;
}); });
TencentImSDKPlugin.v2TIMManager.getMessageManager().removeAdvancedMsgListener( if (advancedMsgListener != null) {
listener: advancedMsgListener, TencentImSDKPlugin.v2TIMManager
); .getMessageManager()
.removeAdvancedMsgListener(listener: advancedMsgListener);
advancedMsgListener = null;
}
} }
} else { } else {
final currentProgress = (messageProgress.currentSize / messageProgress.totalSize * 100).floor(); final currentProgress =
(messageProgress.currentSize / messageProgress.totalSize * 100)
.floor();
if (mounted && currentProgress > downloadProgress) { if (mounted && currentProgress > downloadProgress) {
setState(() { setState(() {
downloadProgress = currentProgress; downloadProgress = currentProgress;
@ -94,11 +120,17 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
} }
}, },
); );
TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(listener: advancedMsgListener); await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.addAdvancedMsgListener(listener: advancedMsgListener!);
return true;
} }
Future<String> getSavePath() async { Future<String> getSavePath() async {
String savePathWithAppPath = '/storage/emulated/0/Android/data/com.tencent.flutter.tuikit/cache/' + (widget.message.msgID ?? "") + widget.fileElem!.fileName!; String savePathWithAppPath =
'/storage/emulated/0/Android/data/com.tencent.flutter.tuikit/cache/' +
(widget.message.msgID ?? "") +
widget.fileElem!.fileName!;
return savePathWithAppPath; return savePathWithAppPath;
} }
@ -106,7 +138,11 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
if (PlatformUtils().isWeb) { if (PlatformUtils().isWeb) {
return true; return true;
} }
String savePath = TencentUtils.checkString(model.getFileMessageLocation(widget.messageID)) ?? TencentUtils.checkString(widget.message.fileElem!.localUrl) ?? widget.message.fileElem?.path ?? ''; String savePath = TencentUtils.checkString(
model.getFileMessageLocation(widget.messageID)) ??
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
widget.message.fileElem?.path ??
'';
File f = File(savePath); File f = File(savePath);
if (f.existsSync() && widget.messageID != null) { if (f.existsSync() && widget.messageID != null) {
filePath = savePath; filePath = savePath;
@ -118,6 +154,12 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
if (model.getMessageProgress(widget.messageID) != 100) { if (model.getMessageProgress(widget.messageID) != 100) {
model.setMessageProgress(widget.messageID!, 100); model.setMessageProgress(widget.messageID!, 100);
} }
if (advancedMsgListener != null) {
TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.removeAdvancedMsgListener(listener: advancedMsgListener);
advancedMsgListener = null;
}
return true; return true;
} }
return false; return false;
@ -159,7 +201,8 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
downloadFile(TUITheme theme) async { downloadFile(TUITheme theme) async {
if (PlatformUtils().isMobile) { if (PlatformUtils().isMobile) {
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (!await Permissions.checkPermission(context, Permission.photosAddOnly.value, theme, false)) { if (!await Permissions.checkPermission(
context, Permission.photosAddOnly.value, theme, false)) {
return; return;
} }
} else { } else {
@ -191,13 +234,18 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
} }
tryOpenFile(context, theme) async { tryOpenFile(context, theme) async {
if (!PlatformUtils().isWeb && (await hasZeroSize(filePath) || widget.message.status == 3)) { if (!PlatformUtils().isWeb &&
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: "不支持 0KB 文件的传输", infoCode: 6660417)); (await hasZeroSize(filePath) || widget.message.status == 3)) {
onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: "不支持 0KB 文件的传输",
infoCode: 6660417));
return; return;
} }
if (PlatformUtils().isMobile) { if (PlatformUtils().isMobile) {
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (!await Permissions.checkPermission(context, Permission.photosAddOnly.value, theme!, false)) { if (!await Permissions.checkPermission(
context, Permission.photosAddOnly.value, theme!, false)) {
return; return;
} }
} else { } else {
@ -241,7 +289,8 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
headers: {'Content-Type': 'application/x-www-form-urlencoded'}, headers: {'Content-Type': 'application/x-www-form-urlencoded'},
); );
final html.AnchorElement downloadAnchor = html.document.createElement('a') as html.AnchorElement; final html.AnchorElement downloadAnchor =
html.document.createElement('a') as html.AnchorElement;
final html.Blob blob = html.Blob([response.bodyBytes]); final html.Blob blob = html.Blob([response.bodyBytes]);
@ -253,7 +302,8 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
html.AnchorElement( html.AnchorElement(
href: widget.fileElem?.path ?? "", href: widget.fileElem?.path ?? "",
) )
..setAttribute("download", widget.message.fileElem?.fileName ?? fileName) ..setAttribute(
"download", widget.message.fileElem?.fileName ?? fileName)
..setAttribute("target", '_blank') ..setAttribute("target", '_blank')
..style.display = "none" ..style.display = "none"
..click(); ..click();
@ -272,14 +322,24 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
final fileName = widget.fileElem!.fileName ?? ""; final fileName = widget.fileElem!.fileName ?? "";
final fileSize = widget.fileElem!.fileSize; final fileSize = widget.fileElem!.fileSize;
final borderRadius = widget.isSelf final borderRadius = widget.isSelf
? const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(2), bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)) ? const BorderRadius.only(
: const BorderRadius.only(topLeft: Radius.circular(2), topRight: Radius.circular(10), bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)); topLeft: Radius.circular(10),
topRight: Radius.circular(2),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10))
: const BorderRadius.only(
topLeft: Radius.circular(2),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10));
String? fileFormat; String? fileFormat;
if (widget.fileElem?.fileName != null && widget.fileElem!.fileName!.isNotEmpty) { if (widget.fileElem?.fileName != null &&
widget.fileElem!.fileName!.isNotEmpty) {
final String fileName = widget.fileElem!.fileName!; final String fileName = widget.fileElem!.fileName!;
fileFormat = fileName.split(".")[max(fileName.split(".").length - 1, 0)]; fileFormat = fileName.split(".")[max(fileName.split(".").length - 1, 0)];
} }
final RenderBox? containerRenderBox = containerKey.currentContext?.findRenderObject() as RenderBox?; final RenderBox? containerRenderBox =
containerKey.currentContext?.findRenderObject() as RenderBox?;
if (containerRenderBox != null) { if (containerRenderBox != null) {
containerHeight = containerRenderBox.size.height; containerHeight = containerRenderBox.size.height;
} }
@ -312,6 +372,8 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
} }
return; return;
} }
await addAdvancedMsgListenerForDownload();
if (await hasFile()) { if (await hasFile()) {
if (received == 100) { if (received == 100) {
tryOpenFile(context, theme); tryOpenFile(context, theme);
@ -328,14 +390,20 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
} }
if (checkIsWaiting()) { if (checkIsWaiting()) {
onTIMCallback( onTIMCallback(
TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("已加入待下载队列,其他文件下载中"), infoCode: 6660413), TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("已加入待下载队列,其他文件下载中"),
infoCode: 6660413),
); );
return; return;
} else { } else {
await addUrlToWaitingPath(theme); await addUrlToWaitingPath(theme);
} }
} catch (e) { } catch (e) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: "文件处理异常", infoCode: 6660416)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: "文件处理异常",
infoCode: 6660416));
} }
}, },
child: ConstrainedBox( child: ConstrainedBox(
@ -344,7 +412,8 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
width: 237, width: 237,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: theme.weakDividerColor ?? CommonColor.weakDividerColor, color: theme.weakDividerColor ??
CommonColor.weakDividerColor,
), ),
borderRadius: borderRadius), borderRadius: borderRadius),
child: Stack(children: [ child: Stack(children: [
@ -353,44 +422,55 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
child: LinearProgressIndicator( child: LinearProgressIndicator(
minHeight: ((containerHeight) ?? 72) - 6, minHeight: ((containerHeight) ?? 72) - 6,
value: (received == 100 ? 0 : received) / 100, value: (received == 100 ? 0 : received) / 100,
backgroundColor: received == 100 ? theme.weakBackgroundColor : Colors.white, backgroundColor: received == 100
valueColor: AlwaysStoppedAnimation(theme.lightPrimaryMaterialColor.shade50), ? theme.weakBackgroundColor
: Colors.white,
valueColor: AlwaysStoppedAnimation(
theme.lightPrimaryMaterialColor.shade50),
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), padding: const EdgeInsets.symmetric(
child: Row(mainAxisAlignment: widget.isSelf ? MainAxisAlignment.end : MainAxisAlignment.start, children: [ vertical: 8, horizontal: 12),
Expanded( child: Row(
child: Column( mainAxisAlignment: widget.isSelf
crossAxisAlignment: CrossAxisAlignment.start, ? MainAxisAlignment.end
: MainAxisAlignment.start,
children: [ children: [
Container( Expanded(
constraints: const BoxConstraints(maxWidth: 160), child: Column(
child: LayoutBuilder( crossAxisAlignment: CrossAxisAlignment.start,
builder: (buildContext, boxConstraints) { children: [
return CustomText( Container(
fileName, constraints:
width: boxConstraints.maxWidth, const BoxConstraints(maxWidth: 160),
maxLines: 1, child: LayoutBuilder(
builder: (buildContext, boxConstraints) {
return CustomText(
fileName,
width: boxConstraints.maxWidth,
maxLines: 1,
style: TextStyle(
color: theme.darkTextColor,
fontSize: 16,
),
);
},
),
),
if (fileSize != null)
Text(
showFileSize(fileSize),
style: TextStyle( style: TextStyle(
color: theme.darkTextColor, fontSize: 14,
fontSize: 16, color: theme.weakTextColor),
), )
); ],
}, )),
), TIMUIKitFileIcon(
fileFormat: fileFormat,
), ),
if (fileSize != null) ])),
Text(
showFileSize(fileSize),
style: TextStyle(fontSize: 14, color: theme.weakTextColor),
)
],
)),
TIMUIKitFileIcon(
fileFormat: fileFormat,
),
])),
]), ]),
), ),
), ),

View File

@ -45,7 +45,15 @@ class TIMUIKitImageElem extends StatefulWidget {
final bool? isShowMessageReaction; final bool? isShowMessageReaction;
final TUIChatSeparateViewModel chatModel; final TUIChatSeparateViewModel chatModel;
const TIMUIKitImageElem({required this.message, this.isShowJump = false, required this.chatModel, this.clearJump, this.isFrom, Key? key, this.isShowMessageReaction}) : super(key: key); const TIMUIKitImageElem(
{required this.message,
this.isShowJump = false,
required this.chatModel,
this.clearJump,
this.isFrom,
Key? key,
this.isShowMessageReaction})
: super(key: key);
@override @override
State<StatefulWidget> createState() => _TIMUIKitImageElem(); State<StatefulWidget> createState() => _TIMUIKitImageElem();
@ -65,7 +73,9 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
String getOriginImgURL() { String getOriginImgURL() {
// //
V2TimImage? img = MessageUtils.getImageFromImgList(widget.message.imageElem!.imageList, HistoryMessageDartConstant.oriImgPrior); V2TimImage? img = MessageUtils.getImageFromImgList(
widget.message.imageElem!.imageList,
HistoryMessageDartConstant.oriImgPrior);
return img == null ? widget.message.imageElem!.path! : img.url!; return img == null ? widget.message.imageElem!.path! : img.url!;
} }
@ -114,7 +124,8 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
final http.Response r = await http.get(Uri.parse(imageUrl)); final http.Response r = await http.get(Uri.parse(imageUrl));
final data = r.bodyBytes; final data = r.bodyBytes;
final base64data = base64Encode(data); final base64data = base64Encode(data);
final a = html.AnchorElement(href: 'data:image/jpeg;base64,$base64data'); final a =
html.AnchorElement(href: 'data:image/jpeg;base64,$base64data');
a.download = md5.convert(utf8.encode(imageUrl)).toString(); a.download = md5.convert(utf8.encode(imageUrl)).toString();
a.click(); a.click();
a.remove(); a.remove();
@ -125,7 +136,8 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
} }
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (!await Permissions.checkPermission(context, Permission.photosAddOnly.value, theme!, false)) { if (!await Permissions.checkPermission(
context, Permission.photosAddOnly.value, theme!, false)) {
return; return;
} }
} else { } else {
@ -160,7 +172,8 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (model.getMessageProgress(widget.message.msgID) == 100) { if (model.getMessageProgress(widget.message.msgID) == 100) {
String savePath; String savePath;
if (widget.message.imageElem!.path != null && widget.message.imageElem!.path != '') { if (widget.message.imageElem!.path != null &&
widget.message.imageElem!.path != '') {
savePath = widget.message.imageElem!.path!; savePath = widget.message.imageElem!.path!;
} else { } else {
savePath = model.getFileMessageLocation(widget.message.msgID); savePath = model.getFileMessageLocation(widget.message.msgID);
@ -171,21 +184,36 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (result['isSuccess']) { if (result['isSuccess']) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存成功"), infoCode: 6660406)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存成功"),
infoCode: 6660406));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存失败"), infoCode: 6660407)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存失败"),
infoCode: 6660407));
} }
} else { } else {
if (result != null) { if (result != null) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存成功"), infoCode: 6660406)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存成功"),
infoCode: 6660406));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存失败"), infoCode: 6660407)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存失败"),
infoCode: 6660407));
} }
} }
return; return;
} }
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("the message is downloading"), infoCode: -1)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("the message is downloading"),
infoCode: -1));
} }
return; return;
} }
@ -194,15 +222,27 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (result['isSuccess']) { if (result['isSuccess']) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存成功"), infoCode: 6660406)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存成功"),
infoCode: 6660406));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存失败"), infoCode: 6660407)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存失败"),
infoCode: 6660407));
} }
} else { } else {
if (result != null) { if (result != null) {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存成功"), infoCode: 6660406)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存成功"),
infoCode: 6660406));
} else { } else {
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("图片保存失败"), infoCode: 6660407)); onTIMCallback(TIMCallback(
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("图片保存失败"),
infoCode: 6660407));
} }
} }
return; return;
@ -223,7 +263,8 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (!isWeb && filePath != null && File(filePath).existsSync()) { if (!isWeb && filePath != null && File(filePath).existsSync()) {
imageUrl = filePath; imageUrl = filePath;
isAssetBool = true; isAssetBool = true;
} else if (localUrl != null && (!isWeb && File(localUrl).existsSync())) { } else if (localUrl != null &&
(!isWeb && File(localUrl).existsSync())) {
imageUrl = localUrl; imageUrl = localUrl;
isAssetBool = true; isAssetBool = true;
} else { } else {
@ -241,13 +282,19 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
); );
} }
} catch (e) { } catch (e) {
onTIMCallback(TIMCallback(infoCode: 6660414, infoRecommendText: TIM_t("正在下载中"), type: TIMCallbackType.INFO)); onTIMCallback(TIMCallback(
infoCode: 6660414,
infoRecommendText: TIM_t("正在下载中"),
type: TIMCallbackType.INFO));
return; return;
} }
} }
V2TimImage? getImageFromList(V2TimImageTypesEnum imgType) { V2TimImage? getImageFromList(V2TimImageTypesEnum imgType) {
V2TimImage? img = MessageUtils.getImageFromImgList(widget.message.imageElem!.imageList, HistoryMessageDartConstant.imgPriorMap[imgType] ?? HistoryMessageDartConstant.oriImgPrior); V2TimImage? img = MessageUtils.getImageFromImgList(
widget.message.imageElem!.imageList,
HistoryMessageDartConstant.imgPriorMap[imgType] ??
HistoryMessageDartConstant.oriImgPrior);
return img; return img;
} }
@ -271,18 +318,26 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
)); ));
bool checkIfDownloadSuccess() { bool checkIfDownloadSuccess() {
final localUrl = TencentUtils.checkString(model.getFileMessageLocation(widget.message.msgID)) ?? widget.message.imageElem!.imageList![0]!.localUrl; final localUrl = TencentUtils.checkString(
return TencentUtils.checkString(localUrl) != null && File(localUrl!).existsSync(); model.getFileMessageLocation(widget.message.msgID)) ??
widget.message.imageElem!.imageList![0]!.localUrl;
return TencentUtils.checkString(localUrl) != null &&
File(localUrl!).existsSync();
} }
_onClickOpenImageInNewWindow() { _onClickOpenImageInNewWindow() {
final localUrl = TencentUtils.checkString(model.getFileMessageLocation(widget.message.msgID)) ?? widget.message.imageElem!.imageList![0]!.localUrl; final localUrl = TencentUtils.checkString(
model.getFileMessageLocation(widget.message.msgID)) ??
widget.message.imageElem!.imageList![0]!.localUrl;
Future.delayed(const Duration(milliseconds: 0), () async { Future.delayed(const Duration(milliseconds: 0), () async {
final isDownloaded = checkIfDownloadSuccess(); final isDownloaded = checkIfDownloadSuccess();
if (isDownloaded) { if (isDownloaded) {
launchDesktopFile(localUrl ?? ""); launchDesktopFile(localUrl ?? "");
} else { } else {
onTIMCallback(TIMCallback(infoCode: 6660414, infoRecommendText: TIM_t("正在下载原始资源,请稍候..."), type: TIMCallbackType.INFO)); onTIMCallback(TIMCallback(
infoCode: 6660414,
infoRecommendText: TIM_t("正在下载原始资源,请稍候..."),
type: TIMCallbackType.INFO));
} }
}); });
} }
@ -291,14 +346,27 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
double? positionRadio, double? positionRadio,
String? originImgUrl, String? originImgUrl,
}) { }) {
final localUrl = TencentUtils.checkString(model.getFileMessageLocation(widget.message.msgID)) ?? widget.message.imageElem!.imageList![0]!.localUrl; final localUrl = TencentUtils.checkString(
model.getFileMessageLocation(widget.message.msgID)) ??
widget.message.imageElem!.imageList![0]!.localUrl;
if (checkIfDownloadSuccess()) { if (checkIfDownloadSuccess()) {
TUIKitWidePopup.showMedia(aspectRatio: positionRadio, context: context, mediaLocalPath: localUrl ?? "", onClickOrigin: () => _onClickOpenImageInNewWindow()); TUIKitWidePopup.showMedia(
aspectRatio: positionRadio,
context: context,
mediaLocalPath: localUrl ?? "",
onClickOrigin: () => _onClickOpenImageInNewWindow());
} else { } else {
if (TencentUtils.checkString(originImgUrl) != null) { if (TencentUtils.checkString(originImgUrl) != null) {
TUIKitWidePopup.showMedia(aspectRatio: positionRadio, context: context, mediaURL: originImgUrl, onClickOrigin: () => _onClickOpenImageInNewWindow()); TUIKitWidePopup.showMedia(
aspectRatio: positionRadio,
context: context,
mediaURL: originImgUrl,
onClickOrigin: () => _onClickOpenImageInNewWindow());
} else { } else {
onTIMCallback(TIMCallback(infoCode: 6660414, infoRecommendText: TIM_t("正在下载中"), type: TIMCallbackType.INFO)); onTIMCallback(TIMCallback(
infoCode: 6660414,
infoRecommendText: TIM_t("正在下载中"),
type: TIMCallbackType.INFO));
} }
} }
} }
@ -365,7 +433,10 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
} }
} else { } else {
if (PlatformUtils().isDesktop) { if (PlatformUtils().isDesktop) {
TUIKitWidePopup.showMedia(mediaLocalPath: imgPath, context: context, onClickOrigin: () => launchDesktopFile(imgPath ?? "")); TUIKitWidePopup.showMedia(
mediaLocalPath: imgPath,
context: context,
onClickOrigin: () => launchDesktopFile(imgPath ?? ""));
} else { } else {
Navigator.of(context).push( Navigator.of(context).push(
PageRouteBuilder( PageRouteBuilder(
@ -383,56 +454,134 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
} }
} }
Widget _renderAllImage({dynamic heroTag, required TUITheme theme, bool isNetworkImage = false, String? webPath, V2TimImage? originalImg, V2TimImage? smallImg, String? smallLocalPath, String? originLocalPath}) { Widget _renderAllImage(
{dynamic heroTag,
double? positionRadio,
required TUITheme theme,
bool isNetworkImage = false,
String? webPath,
V2TimImage? originalImg,
V2TimImage? smallImg,
String? smallLocalPath,
String? originLocalPath}) {
Widget getImageWidget() { Widget getImageWidget() {
if (isNetworkImage) { if (isNetworkImage) {
return Hero( return Hero(
tag: heroTag, tag: heroTag,
child: PlatformUtils().isWeb child: PlatformUtils().isWeb
? Image.network(webPath ?? smallImg?.url ?? originalImg!.url!, fit: BoxFit.contain) ? Image.network(webPath ?? smallImg?.url ?? originalImg!.url!,
fit: BoxFit.contain)
: CachedNetworkImage( : CachedNetworkImage(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
imageUrl: webPath ?? smallImg?.url ?? originalImg!.url!, imageUrl: webPath ?? smallImg?.url ?? originalImg!.url!,
errorWidget: (context, error, stackTrace) => errorPage(theme), errorWidget: (context, error, stackTrace) =>
errorPage(theme),
fit: BoxFit.contain, fit: BoxFit.contain,
cacheKey: smallImg?.uuid ?? originalImg!.uuid, cacheKey: smallImg?.uuid ?? originalImg!.uuid,
placeholder: (context, url) => Image(image: MemoryImage(kTransparentImage)), placeholder: (context, url) =>
Image(image: MemoryImage(kTransparentImage)),
fadeInDuration: const Duration(milliseconds: 0), fadeInDuration: const Duration(milliseconds: 0),
)); ));
} else { } else {
final imgPath = (TencentUtils.checkString(smallLocalPath) != null ? smallLocalPath : originLocalPath)!; final imgPath = (TencentUtils.checkString(smallLocalPath) != null
return Hero(tag: heroTag, child: Image.file(File(imgPath), fit: BoxFit.contain)); ? smallLocalPath
: originLocalPath)!;
return Hero(
tag: heroTag,
child: Image.file(File(imgPath), fit: BoxFit.contain));
} }
} }
double? currentPositionRadio;
// File imgF = File((TencentUtils.checkString(originLocalPath) != null
// ? originLocalPath
// : smallLocalPath) ??
// "");
// bool isExist = imgF.existsSync();
//
// if (!isExist) {
// return errorDisplay(context, theme);
// }
// Image image = Image.file(imgF);
//
// image.image
// .resolve(const ImageConfiguration())
// .addListener(ImageStreamListener((image, synchronousCall) {
// if (image.image.width != 0 && image.image.height != 0) {
// currentPositionRadio = image.image.width / image.image.height;
// }
// }));
return GestureDetector( return GestureDetector(
onTap: () => onClickImage( onTap: () => onClickImage(
theme: theme, heroTag: heroTag, isNetworkImage: isNetworkImage, imgUrl: webPath ?? smallImg?.url ?? originalImg?.url ?? "", imgPath: (TencentUtils.checkString(originLocalPath) != null ? originLocalPath : smallLocalPath) ?? ""), theme: theme,
child: getImageWidget(), heroTag: heroTag,
isNetworkImage: isNetworkImage,
imgUrl: webPath ?? smallImg?.url ?? originalImg?.url ?? "",
imgPath: (TencentUtils.checkString(originLocalPath) != null
? originLocalPath
: smallLocalPath) ??
""),
child: Stack(
children: [
if (currentPositionRadio != null || positionRadio != null)
AspectRatio(
aspectRatio: (currentPositionRadio ?? positionRadio)!,
child: Container(
decoration: const BoxDecoration(color: Colors.transparent),
),
),
getImageWidget(),
],
),
); );
} }
void initImages() async { void initImages() async {
final zeroImageLocal = TencentUtils.checkString(widget.message.imageElem?.imageList?.firstWhereOrNull((element) => element?.type == 0)?.localUrl); final zeroImageLocal = TencentUtils.checkString(widget
final oneImageLocal = TencentUtils.checkString(widget.message.imageElem?.imageList?.firstWhereOrNull((element) => element?.type == 1)?.localUrl); .message.imageElem?.imageList
final twoImageLocal = TencentUtils.checkString(widget.message.imageElem?.imageList?.firstWhereOrNull((element) => element?.type == 2)?.localUrl); ?.firstWhereOrNull((element) => element?.type == 0)
?.localUrl);
final oneImageLocal = TencentUtils.checkString(widget
.message.imageElem?.imageList
?.firstWhereOrNull((element) => element?.type == 1)
?.localUrl);
final twoImageLocal = TencentUtils.checkString(widget
.message.imageElem?.imageList
?.firstWhereOrNull((element) => element?.type == 2)
?.localUrl);
if (!PlatformUtils().isWeb && TencentUtils.checkString(widget.message.msgID) != null) { if (!PlatformUtils().isWeb &&
if ((widget.message.imageElem?.imageList) == null || widget.message.imageElem!.imageList!.isEmpty) { TencentUtils.checkString(widget.message.msgID) != null) {
final response = await _messageService.getMessageOnlineUrl(msgID: widget.message.msgID!); if ((widget.message.imageElem?.imageList) == null ||
widget.message.imageElem!.imageList!.isEmpty) {
final response = await _messageService.getMessageOnlineUrl(
msgID: widget.message.msgID!);
final elem = response.data; final elem = response.data;
if (elem != null && elem.imageElem != null) { if (elem != null && elem.imageElem != null) {
widget.message.imageElem = elem.imageElem; widget.message.imageElem = elem.imageElem;
} }
} }
if (oneImageLocal == null || !File(oneImageLocal).existsSync()) { if (oneImageLocal == null || !File(oneImageLocal).existsSync()) {
_messageService.downloadMessage(msgID: widget.message.msgID!, messageType: 3, imageType: 1, isSnapshot: false); _messageService.downloadMessage(
msgID: widget.message.msgID!,
messageType: 3,
imageType: 1,
isSnapshot: false);
} }
if (twoImageLocal == null || !File(twoImageLocal).existsSync()) { if (twoImageLocal == null || !File(twoImageLocal).existsSync()) {
_messageService.downloadMessage(msgID: widget.message.msgID!, messageType: 3, imageType: 2, isSnapshot: false); _messageService.downloadMessage(
msgID: widget.message.msgID!,
messageType: 3,
imageType: 2,
isSnapshot: false);
} }
if (zeroImageLocal == null || !File(zeroImageLocal).existsSync()) { if (zeroImageLocal == null || !File(zeroImageLocal).existsSync()) {
_messageService.downloadMessage(msgID: widget.message.msgID!, messageType: 3, imageType: 0, isSnapshot: false); _messageService.downloadMessage(
msgID: widget.message.msgID!,
messageType: 3,
imageType: 0,
isSnapshot: false);
} }
} }
} }
@ -446,36 +595,83 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
bool isNeedShowLocalPath() { bool isNeedShowLocalPath() {
final current = (DateTime.now().millisecondsSinceEpoch / 1000).ceil(); final current = (DateTime.now().millisecondsSinceEpoch / 1000).ceil();
final timeStamp = widget.message.timestamp ?? current; final timeStamp = widget.message.timestamp ?? current;
return (widget.message.isSelf ?? true) && (isSent || current - timeStamp < 300); return (widget.message.isSelf ?? true) &&
(isSent || current - timeStamp < 300);
} }
Widget? _renderImage(dynamic heroTag, TUITheme theme, {V2TimImage? originalImg, V2TimImage? smallImg}) { Widget? _renderImage(dynamic heroTag, TUITheme theme,
{V2TimImage? originalImg, V2TimImage? smallImg}) {
double positionRadio = 1.0;
if (smallImg?.width != null &&
smallImg?.height != null &&
smallImg?.width != 0 &&
smallImg?.height != 0) {
positionRadio = (smallImg!.width! / smallImg.height!);
}
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 _renderAllImage(heroTag: heroTag, theme: theme, isNetworkImage: true, smallImg: smallImg, originalImg: originalImg, webPath: widget.message.imageElem!.path); return _renderAllImage(
heroTag: heroTag,
theme: theme,
isNetworkImage: true,
smallImg: smallImg,
originalImg: originalImg,
positionRadio: positionRadio,
webPath: widget.message.imageElem!.path);
} }
try { try {
if ((isNeedShowLocalPath() && widget.message.imageElem!.path != null && widget.message.imageElem!.path!.isNotEmpty && File(widget.message.imageElem!.path!).existsSync())) { if ((isNeedShowLocalPath() &&
return _renderAllImage(smallLocalPath: widget.message.imageElem!.path!, heroTag: heroTag, theme: theme, originLocalPath: widget.message.imageElem!.path!); widget.message.imageElem!.path != null &&
widget.message.imageElem!.path!.isNotEmpty &&
File(widget.message.imageElem!.path!).existsSync())) {
return _renderAllImage(
smallLocalPath: widget.message.imageElem!.path!,
heroTag: heroTag,
theme: theme,
positionRadio: positionRadio,
originLocalPath: widget.message.imageElem!.path!);
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
outputLogger.i(e); outputLogger.i(e.toString());
} }
try { try {
if ((TencentUtils.checkString(smallImg?.localUrl) != null && File((smallImg?.localUrl!)!).existsSync()) || (TencentUtils.checkString(originalImg?.localUrl) != null && File((originalImg?.localUrl!)!).existsSync())) { if ((TencentUtils.checkString(smallImg?.localUrl) != null &&
return _renderAllImage(smallLocalPath: smallImg?.localUrl ?? "", heroTag: heroTag, theme: theme, originLocalPath: originalImg?.localUrl); File((smallImg?.localUrl!)!).existsSync()) ||
(TencentUtils.checkString(originalImg?.localUrl) != null &&
File((originalImg?.localUrl!)!).existsSync())) {
return _renderAllImage(
smallLocalPath: smallImg?.localUrl ?? "",
heroTag: heroTag,
theme: theme,
positionRadio: positionRadio,
originLocalPath: originalImg?.localUrl);
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
outputLogger.i(e); outputLogger.i(e.toString());
return _renderAllImage(heroTag: heroTag, theme: theme, isNetworkImage: true, smallImg: smallImg, originalImg: originalImg); return _renderAllImage(
heroTag: heroTag,
theme: theme,
isNetworkImage: true,
smallImg: smallImg,
positionRadio: positionRadio,
originalImg: originalImg);
} }
if ((smallImg?.url ?? originalImg?.url) != null && (smallImg?.url ?? originalImg?.url)!.isNotEmpty) { if ((smallImg?.url ?? originalImg?.url) != null &&
return _renderAllImage(heroTag: heroTag, theme: theme, isNetworkImage: true, smallImg: smallImg, originalImg: originalImg); (smallImg?.url ?? originalImg?.url)!.isNotEmpty) {
return _renderAllImage(
heroTag: heroTag,
theme: theme,
isNetworkImage: true,
positionRadio: positionRadio,
smallImg: smallImg,
originalImg: originalImg);
} }
return errorDisplay(context, theme); return errorDisplay(context, theme);
@ -487,8 +683,10 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING) { if (widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING) {
isSent = true; isSent = true;
} }
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop; final isDesktopScreen =
final heroTag = "${widget.message.msgID ?? widget.message.id ?? widget.message.timestamp ?? DateTime.now().millisecondsSinceEpoch}${widget.isFrom}"; TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final heroTag =
"${widget.message.msgID ?? widget.message.id ?? widget.message.timestamp ?? DateTime.now().millisecondsSinceEpoch}${widget.isFrom}";
V2TimImage? originalImg = getImageFromList(V2TimImageTypesEnum.original); V2TimImage? originalImg = getImageFromList(V2TimImageTypesEnum.original);
V2TimImage? smallImg = getImageFromList(V2TimImageTypesEnum.small); V2TimImage? smallImg = getImageFromList(V2TimImageTypesEnum.small);
@ -499,14 +697,16 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
isFromSelf: widget.message.isSelf ?? true, isFromSelf: widget.message.isSelf ?? true,
isShowMessageReaction: widget.isShowMessageReaction ?? true, isShowMessageReaction: widget.isShowMessageReaction ?? true,
message: widget.message, message: widget.message,
child: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return ConstrainedBox( return ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: constraints.maxWidth * (isDesktopScreen ? 0.4 : 0.5), maxWidth: constraints.maxWidth * (isDesktopScreen ? 0.4 : 0.5),
minWidth: 64, minWidth: 64,
maxHeight: 256, maxHeight: 256,
), ),
child: _renderImage(heroTag, theme, originalImg: originalImg, smallImg: smallImg), child: _renderImage(heroTag, theme,
originalImg: originalImg, smallImg: smallImg),
); );
})); }));
} }
@ -515,7 +715,9 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
class ImageClipper extends CustomClipper<RRect> { class ImageClipper extends CustomClipper<RRect> {
@override @override
RRect getClip(Size size) { RRect getClip(Size size) {
return RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, size.width, min(size.height, 256)), const Radius.circular(5)); return RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, min(size.height, 256)),
const Radius.circular(5));
} }
@override @override

View File

@ -5,6 +5,7 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_model_tools.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_model_tools.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/platform.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_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:extended_text/extended_text.dart'; import 'package:extended_text/extended_text.dart';
@ -93,6 +94,9 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
} }
final messageID = cloudCustomData.messageID; final messageID = cloudCustomData.messageID;
if(PlatformUtils().isWeb){
return;
}
V2TimMessage? message = await widget.chatModel.findMessage(messageID); V2TimMessage? message = await widget.chatModel.findMessage(messageID);
if (message == null) { if (message == null) {
try { try {
@ -168,7 +172,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
} }
if (message == null) { if (message == null) {
if (repliedMessage?.messageAbstract != null) { if (repliedMessage?.messageAbstract != null) {
_renderMessageSummary(theme); return _renderMessageSummary(theme);
} }
return const SizedBox(width: 0, height: 12); return const SizedBox(width: 0, height: 12);
} }

View File

@ -261,7 +261,7 @@ class _SendSoundMessageState extends TIMUIKitState<SendSoundMessage> {
isRecording = true; isRecording = true;
}); });
} else { } else {
outputLogger.i(status); outputLogger.i(status.toString());
} }
}); });
final amplitudesResponseSubscription = final amplitudesResponseSubscription =

View File

@ -222,7 +222,7 @@ class _TIMUIKitTextFieldLayoutWideState extends TIMUIKitState<TIMUIKitTextFieldL
} }
} catch (e) { } catch (e) {
// ignore: avoid_print // ignore: avoid_print
outputLogger.i(e); outputLogger.i(e.toString());
} }
generateDefaultControlBarItems(); generateDefaultControlBarItems();
} }

View File

@ -230,6 +230,8 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
axis: Axis.vertical, axis: Axis.vertical,
); );
Widget? joinInGroupCallWidget;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -346,12 +348,21 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
return widget.conversation.type == 1 ? ConvType.c2c : ConvType.group; return widget.conversation.type == 1 ? ConvType.c2c : ConvType.group;
} }
_updateJoinInGroupCallWidget() async {
if (_getConvType() != ConvType.group) {
return;
}
joinInGroupCallWidget = await TUICore.instance.raiseExtension(TUIExtensionID.joinInGroup, {GROUP_ID: widget.conversationID!});
setState(() {});
}
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme; final TUITheme theme = value.theme;
final closePanel = OptimizeUtils.throttle((_) => textFieldController.hideAllPanel(), 60); final closePanel = OptimizeUtils.throttle((_) => textFieldController.hideAllPanel(), 60);
final isBuild = isInit; final isBuild = isInit;
isInit = true; isInit = true;
_updateJoinInGroupCallWidget();
return TIMUIKitChatProviderScope( return TIMUIKitChatProviderScope(
model: model, model: model,
@ -450,6 +461,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
if (widget.customAppBar != null) widget.customAppBar!, if (widget.customAppBar != null) widget.customAppBar!,
if (filteredApplicationList.isNotEmpty) _renderJoinGroupApplication(filteredApplicationList.length, theme), if (filteredApplicationList.isNotEmpty) _renderJoinGroupApplication(filteredApplicationList.length, theme),
if (widget.topFixWidget != null) widget.topFixWidget!, if (widget.topFixWidget != null) widget.topFixWidget!,
if (joinInGroupCallWidget != null) Center(child: joinInGroupCallWidget!),
Expanded( Expanded(
child: Container( child: Container(
color: theme.chatBgColor, color: theme.chatBgColor,

View File

@ -96,6 +96,10 @@ class TIMUIKitChatConfig {
/// [Default]: true. /// [Default]: true.
final bool isAtWhenReply; final bool isAtWhenReply;
/// Control if allowed to at when reply automatically.
/// [Default]: true.
final bool Function(V2TimMessage message)? isAtWhenReplyDynamic;
/// The main switch of the group read receipt. /// The main switch of the group read receipt.
final bool isShowGroupMessageReadReceipt; final bool isShowGroupMessageReadReceipt;
@ -258,6 +262,7 @@ class TIMUIKitChatConfig {
this.isUseMessageReaction = true, this.isUseMessageReaction = true,
this.isShowAvatar = true, this.isShowAvatar = true,
this.isShowSelfNameInGroup = false, this.isShowSelfNameInGroup = false,
this.isAtWhenReplyDynamic,
this.offlinePushInfo, this.offlinePushInfo,
@Deprecated("Please use [isShowGroupReadingStatus] instead") @Deprecated("Please use [isShowGroupReadingStatus] instead")
this.isShowGroupMessageReadReceipt = true, this.isShowGroupMessageReadReceipt = true,

View File

@ -1,21 +1,15 @@
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_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/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart'; import 'package:tencent_cloud_chat_uikit/data_services/group/group_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/utils/screen_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_im_base/tencent_im_base.dart'; import 'package:tencent_im_base/tencent_im_base.dart';
typedef GroupApplicationItemBuilder = Widget Function( typedef GroupApplicationItemBuilder = Widget Function(BuildContext context, V2TimGroupApplication applicationInfo, int index);
BuildContext context, V2TimGroupApplication applicationInfo, int index);
enum ApplicationStatus { enum ApplicationStatus {
none, none,
@ -30,16 +24,13 @@ class TIMUIKitGroupApplicationList extends StatefulWidget {
/// group ID /// group ID
final String groupID; final String groupID;
const TIMUIKitGroupApplicationList( const TIMUIKitGroupApplicationList({Key? key, this.itemBuilder, required this.groupID}) : super(key: key);
{Key? key, this.itemBuilder, required this.groupID})
: super(key: key);
@override @override
State<StatefulWidget> createState() => TIMUIKitGroupApplicationListState(); State<StatefulWidget> createState() => TIMUIKitGroupApplicationListState();
} }
class TIMUIKitGroupApplicationListState class TIMUIKitGroupApplicationListState extends TIMUIKitState<TIMUIKitGroupApplicationList> {
extends TIMUIKitState<TIMUIKitGroupApplicationList> {
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
final GroupServices _groupServices = serviceLocator<GroupServices>(); final GroupServices _groupServices = serviceLocator<GroupServices>();
List<V2TimGroupApplication> groupApplicationList = []; List<V2TimGroupApplication> groupApplicationList = [];
@ -48,26 +39,20 @@ class TIMUIKitGroupApplicationListState
@override @override
void initState() { void initState() {
super.initState(); super.initState();
groupApplicationList = model.groupApplicationList groupApplicationList = model.groupApplicationList.where((item) => (item.groupID == widget.groupID)).toList();
.where((item) => (item.groupID == widget.groupID)) applicationStatusList = groupApplicationList.map((item) => ApplicationStatus.none).toList();
.toList();
applicationStatusList =
groupApplicationList.map((item) => ApplicationStatus.none).toList();
} }
GroupApplicationItemBuilder _getItemBuilder() { GroupApplicationItemBuilder _getItemBuilder() {
return widget.itemBuilder ?? _defaultItemBuilder; return widget.itemBuilder ?? _defaultItemBuilder;
} }
Widget _defaultItemBuilder( Widget _defaultItemBuilder(BuildContext context, V2TimGroupApplication applicationInfo, int index) {
BuildContext context, V2TimGroupApplication applicationInfo, int index) {
final theme = Provider.of<TUIThemeViewModel>(context).theme; final theme = Provider.of<TUIThemeViewModel>(context).theme;
final ApplicationStatus currentStatus = applicationStatusList[index]; final ApplicationStatus currentStatus = applicationStatusList[index];
String _getUserName() { String _getUserName() {
if (applicationInfo.fromUserNickName != null && if (applicationInfo.fromUserNickName != null && applicationInfo.fromUserNickName!.isNotEmpty && applicationInfo.fromUserNickName != applicationInfo.fromUser) {
applicationInfo.fromUserNickName!.isNotEmpty &&
applicationInfo.fromUserNickName != applicationInfo.fromUser) {
return "${applicationInfo.fromUserNickName} (${applicationInfo.fromUser})"; return "${applicationInfo.fromUserNickName} (${applicationInfo.fromUser})";
} else { } else {
return "${applicationInfo.fromUser}"; return "${applicationInfo.fromUser}";
@ -76,17 +61,14 @@ class TIMUIKitGroupApplicationListState
String _getRequestMessage() { String _getRequestMessage() {
String option2 = applicationInfo.requestMsg ?? ""; String option2 = applicationInfo.requestMsg ?? "";
return TIM_t_para("验证消息: {{option2}}", "验证消息: $option2")( return TIM_t_para("验证消息: {{option2}}", "验证消息: $option2")(option2: option2);
option2: option2);
} }
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
border: Border( border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? const Color(0xFFDBDBDB))),
bottom: BorderSide(
color: theme.weakDividerColor ?? const Color(0xFFDBDBDB))),
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -96,11 +78,7 @@ class TIMUIKitGroupApplicationListState
child: SizedBox( child: SizedBox(
height: 40, height: 40,
width: 40, width: 40,
child: Avatar( child: Avatar(faceUrl: applicationInfo.fromUserFaceUrl ?? "", showName: applicationInfo.fromUserNickName ?? applicationInfo.fromUser ?? ""),
faceUrl: applicationInfo.fromUserFaceUrl ?? "",
showName: applicationInfo.fromUserNickName ??
applicationInfo.fromUser ??
""),
), ),
), ),
Expanded( Expanded(
@ -109,10 +87,7 @@ class TIMUIKitGroupApplicationListState
children: [ children: [
Text( Text(
_getUserName(), _getUserName(),
style: TextStyle( style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: theme.darkTextColor),
fontSize: 16,
fontWeight: FontWeight.w600,
color: theme.darkTextColor),
), ),
Text( Text(
_getRequestMessage(), _getRequestMessage(),
@ -120,21 +95,13 @@ class TIMUIKitGroupApplicationListState
), ),
], ],
)), )),
if (currentStatus == ApplicationStatus.none && if (currentStatus == ApplicationStatus.none && applicationInfo.handleStatus == 0)
applicationInfo.handleStatus == 0)
Container( Container(
margin: const EdgeInsets.only(left: 8, right: 8), margin: const EdgeInsets.only(left: 8, right: 8),
child: InkWell( child: InkWell(
child: Container( child: Container(
padding: padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration(borderRadius: BorderRadius.circular(4), color: theme.primaryColor, border: Border.all(width: 1, color: theme.weakTextColor ?? CommonColor.weakTextColor)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: theme.primaryColor,
border: Border.all(
width: 1,
color: theme.weakTextColor ??
CommonColor.weakTextColor)),
child: Text( child: Text(
TIM_t("同意"), // agree TIM_t("同意"), // agree
style: const TextStyle( style: const TextStyle(
@ -160,19 +127,11 @@ class TIMUIKitGroupApplicationListState
} }
}), }),
), ),
if (currentStatus == ApplicationStatus.none && if (currentStatus == ApplicationStatus.none && applicationInfo.handleStatus == 0)
applicationInfo.handleStatus == 0)
InkWell( InkWell(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(borderRadius: BorderRadius.circular(4), color: Colors.white, border: Border.all(width: 1, color: theme.weakTextColor ?? CommonColor.weakTextColor)),
borderRadius: BorderRadius.circular(4), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
color: Colors.white,
border: Border.all(
width: 1,
color:
theme.weakTextColor ?? CommonColor.weakTextColor)),
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: Text( child: Text(
TIM_t("拒绝"), // reject TIM_t("拒绝"), // reject
style: TextStyle( style: TextStyle(
@ -182,12 +141,7 @@ class TIMUIKitGroupApplicationListState
), ),
onTap: () async { onTap: () async {
final res = await _groupServices.refuseGroupApplication( final res = await _groupServices.refuseGroupApplication(
addTime: applicationInfo.addTime!, addTime: applicationInfo.addTime!, groupID: applicationInfo.groupID, fromUser: applicationInfo.fromUser!, toUser: applicationInfo.toUser!, type: GroupApplicationTypeEnum.values[applicationInfo.type]);
groupID: applicationInfo.groupID,
fromUser: applicationInfo.fromUser!,
toUser: applicationInfo.toUser!,
type:
GroupApplicationTypeEnum.values[applicationInfo.type]);
if (res.code == 0) { if (res.code == 0) {
setState(() { setState(() {
applicationStatusList[index] = ApplicationStatus.reject; applicationStatusList[index] = ApplicationStatus.reject;
@ -198,8 +152,7 @@ class TIMUIKitGroupApplicationListState
} }
}, },
), ),
if (currentStatus == ApplicationStatus.accept || if (currentStatus == ApplicationStatus.accept || applicationInfo.handleResult == 1)
applicationInfo.handleStatus == 1)
Container( Container(
margin: const EdgeInsets.only(left: 8), margin: const EdgeInsets.only(left: 8),
child: Text( child: Text(
@ -207,8 +160,7 @@ class TIMUIKitGroupApplicationListState
style: TextStyle(fontSize: 15, color: theme.weakTextColor), style: TextStyle(fontSize: 15, color: theme.weakTextColor),
), ),
), ),
if (currentStatus == ApplicationStatus.reject || if (currentStatus == ApplicationStatus.reject || applicationInfo.handleResult == 2)
applicationInfo.handleStatus == 2)
Container( Container(
margin: const EdgeInsets.only(left: 8), margin: const EdgeInsets.only(left: 8),
child: Text( child: Text(
@ -228,8 +180,7 @@ class TIMUIKitGroupApplicationListState
return MultiProvider( return MultiProvider(
providers: [ChangeNotifierProvider.value(value: model)], providers: [ChangeNotifierProvider.value(value: model)],
builder: (context, w) { builder: (context, w) {
final isDesktopScreen = final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
return Container( return Container(
decoration: isDesktopScreen ? null : BoxDecoration(color: theme.weakBackgroundColor), decoration: isDesktopScreen ? null : BoxDecoration(color: theme.weakBackgroundColor),
child: ListView.builder( child: ListView.builder(

View File

@ -2,10 +2,10 @@
import 'dart:async'; import 'dart:async';
import 'package:chewie_for_us/chewie_for_us.dart'; import 'package:chewie/chewie.dart';
import 'package:chewie_for_us/src/animated_play_pause.dart'; import 'package:chewie/src/animated_play_pause.dart';
import 'package:chewie_for_us/src/helpers/utils.dart'; import 'package:chewie/src/helpers/utils.dart';
import 'package:chewie_for_us/src/material/material_progress_bar.dart'; import 'package:chewie/src/material/material_progress_bar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:loading_animation_widget/loading_animation_widget.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';
@ -380,8 +380,7 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls> with
_startHideTimer(); _startHideTimer();
}, },
colors: chewieController.materialProgressColors ?? colors: chewieController.materialProgressColors ?? ChewieProgressColors(playedColor: Colors.white, handleColor: Colors.white, bufferedColor: Colors.white38, backgroundColor: Colors.white24),
ChewieProgressColors(playedColor: Colors.white, handleColor: Colors.white, bufferedColor: Colors.white38, backgroundColor: Colors.white24),
), ),
), ),
); );

View File

@ -1,30 +1,27 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:chewie/chewie.dart';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:extended_image/extended_image.dart'; import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:universal_html/html.dart' as html; import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:chewie_for_us/chewie_for_us.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.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/widgets/video_custom_control.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/video_custom_control.dart';
import 'package:universal_html/html.dart' as html;
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
class VideoScreen extends StatefulWidget { class VideoScreen extends StatefulWidget {
const VideoScreen({required this.message, const VideoScreen({required this.message, required this.heroTag, required this.videoElement, Key? key}) : super(key: key);
required this.heroTag,
required this.videoElement,
Key? key})
: super(key: key);
final V2TimMessage message; final V2TimMessage message;
final dynamic heroTag; final dynamic heroTag;
@ -37,8 +34,7 @@ class VideoScreen extends StatefulWidget {
class _VideoScreenState extends TIMUIKitState<VideoScreen> { class _VideoScreenState extends TIMUIKitState<VideoScreen> {
late VideoPlayerController videoPlayerController; late VideoPlayerController videoPlayerController;
late ChewieController chewieController; late ChewieController chewieController;
GlobalKey<ExtendedImageSlidePageState> slidePagekey = GlobalKey<ExtendedImageSlidePageState> slidePagekey = GlobalKey<ExtendedImageSlidePageState>();
GlobalKey<ExtendedImageSlidePageState>();
final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>(); final TUIChatGlobalModel model = serviceLocator<TUIChatGlobalModel>();
bool isInit = false; bool isInit = false;
@ -56,22 +52,19 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
} }
// //
Future<void> _saveNetworkVideo(context, Future<void> _saveNetworkVideo(
String videoUrl, { context,
bool isAsset = true, String videoUrl, {
}) async { bool isAsset = true,
}) async {
if (PlatformUtils().isWeb) { if (PlatformUtils().isWeb) {
RegExp exp = RegExp(r"((\.){1}[^?]{2,4})"); RegExp exp = RegExp(r"((\.){1}[^?]{2,4})");
String? suffix = exp String? suffix = exp.allMatches(videoUrl).last.group(0);
.allMatches(videoUrl)
.last
.group(0);
var xhr = html.HttpRequest(); var xhr = html.HttpRequest();
xhr.open('get', videoUrl); xhr.open('get', videoUrl);
xhr.responseType = 'arraybuffer'; xhr.responseType = 'arraybuffer';
xhr.onLoad.listen((event) { xhr.onLoad.listen((event) {
final a = html.AnchorElement( final a = html.AnchorElement(href: html.Url.createObjectUrl(html.Blob([xhr.response])));
href: html.Url.createObjectUrl(html.Blob([xhr.response])));
a.download = '${md5.convert(utf8.encode(videoUrl)).toString()}$suffix'; a.download = '${md5.convert(utf8.encode(videoUrl)).toString()}$suffix';
a.click(); a.click();
a.remove(); a.remove();
@ -92,7 +85,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
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,
); );
if (!videos) { if (!videos) {
@ -100,7 +94,8 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
} }
} else { } else {
final storage = await Permissions.checkPermission( final storage = await Permissions.checkPermission(
context, Permission.storage.value, context,
Permission.storage.value,
); );
if (!storage) { if (!storage) {
return; return;
@ -115,8 +110,7 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
} }
if (model.getMessageProgress(widget.message.msgID) == 100) { if (model.getMessageProgress(widget.message.msgID) == 100) {
String savePath; String savePath;
if (widget.message.videoElem!.localVideoUrl != null && if (widget.message.videoElem!.localVideoUrl != null && widget.message.videoElem!.localVideoUrl != '') {
widget.message.videoElem!.localVideoUrl != '') {
savePath = widget.message.videoElem!.localVideoUrl!; savePath = widget.message.videoElem!.localVideoUrl!;
} else { } else {
savePath = model.getFileMessageLocation(widget.message.msgID); savePath = model.getFileMessageLocation(widget.message.msgID);
@ -126,62 +120,35 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
var result = await ImageGallerySaver.saveFile(savePath); var result = await ImageGallerySaver.saveFile(savePath);
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (result['isSuccess']) { if (result['isSuccess']) {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} else { } else {
if (result != null) { if (result != null) {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} }
} }
} else { } else {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("the message is downloading"), infoCode: -1));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("the message is downloading"),
infoCode: -1));
} }
return; return;
} }
var result = await ImageGallerySaver.saveFile(savePath); var result = await ImageGallerySaver.saveFile(savePath);
if (PlatformUtils().isIOS) { if (PlatformUtils().isIOS) {
if (result['isSuccess']) { if (result['isSuccess']) {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} else { } else {
if (result != null) { if (result != null) {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存成功"), infoCode: 6660402));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存成功"),
infoCode: 6660402));
} else { } else {
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("视频保存失败"), infoCode: 6660403));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("视频保存失败"),
infoCode: 6660403));
} }
} }
return; return;
@ -195,8 +162,7 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
isAsset: true, isAsset: true,
); );
} }
if (widget.videoElement.videoPath != '' && if (widget.videoElement.videoPath != '' && widget.videoElement.videoPath != null) {
widget.videoElement.videoPath != null) {
File f = File(widget.videoElement.videoPath!); File f = File(widget.videoElement.videoPath!);
if (f.existsSync()) { if (f.existsSync()) {
return await _saveNetworkVideo( return await _saveNetworkVideo(
@ -206,8 +172,7 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
); );
} }
} }
if (widget.videoElement.localVideoUrl != '' && if (widget.videoElement.localVideoUrl != '' && widget.videoElement.localVideoUrl != null) {
widget.videoElement.localVideoUrl != null) {
File f = File(widget.videoElement.localVideoUrl!); File f = File(widget.videoElement.localVideoUrl!);
if (f.existsSync()) { if (f.existsSync()) {
return await _saveNetworkVideo( return await _saveNetworkVideo(
@ -246,8 +211,7 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
setVideoPlayerController() async { setVideoPlayerController() async {
if (!PlatformUtils().isWeb) { if (!PlatformUtils().isWeb) {
if (TencentUtils.checkString(widget.message.msgID) != null && if (TencentUtils.checkString(widget.message.msgID) != null && widget.videoElement.localVideoUrl == null) {
widget.videoElement.localVideoUrl == null) {
String savePath = model.getFileMessageLocation(widget.message.msgID); String savePath = model.getFileMessageLocation(widget.message.msgID);
File f = File(savePath); File f = File(savePath);
if (f.existsSync()) { if (f.existsSync()) {
@ -257,28 +221,26 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
} }
VideoPlayerController player = PlatformUtils().isWeb VideoPlayerController player = PlatformUtils().isWeb
? ((TencentUtils.checkString(widget.videoElement.videoPath) != null) || ? ((TencentUtils.checkString(widget.videoElement.videoPath) != null) || widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING
widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING ? VideoPlayerController.networkUrl(
? VideoPlayerController.networkUrl( Uri.parse(widget.videoElement.videoPath!),
Uri.parse(widget.videoElement.videoPath!), )
) : (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null)
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null) ? VideoPlayerController.networkUrl(
? VideoPlayerController.networkUrl( Uri.parse(widget.videoElement.videoUrl!),
Uri.parse(widget.videoElement.videoUrl!), )
) : VideoPlayerController.networkUrl(
: VideoPlayerController.networkUrl( Uri.parse(widget.videoElement.localVideoUrl!),
Uri.parse(widget.videoElement.localVideoUrl!), ))
)) : (TencentUtils.checkString(widget.videoElement.videoPath) != null || widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING)
: (TencentUtils.checkString(widget.videoElement.videoPath) != null || ? VideoPlayerController.file(File(widget.videoElement.videoPath!))
widget.message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING) : (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null)
? VideoPlayerController.file(File(widget.videoElement.videoPath!)) ? VideoPlayerController.networkUrl(
: (TencentUtils.checkString(widget.videoElement.localVideoUrl) == null) Uri.parse(widget.videoElement.videoUrl!),
? VideoPlayerController.networkUrl( )
Uri.parse(widget.videoElement.videoUrl!), : VideoPlayerController.file(File(
) widget.videoElement.localVideoUrl!,
: VideoPlayerController.file(File( ));
widget.videoElement.localVideoUrl!,
));
await player.initialize(); await player.initialize();
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
double w = getVideoWidth(); double w = getVideoWidth();
@ -303,8 +265,7 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
@override @override
didUpdateWidget(oldWidget) { didUpdateWidget(oldWidget) {
if (oldWidget.videoElement.videoUrl != widget.videoElement.videoUrl || if (oldWidget.videoElement.videoUrl != widget.videoElement.videoUrl || oldWidget.videoElement.videoPath != widget.videoElement.videoPath) {
oldWidget.videoElement.videoPath != widget.videoElement.videoPath) {
setVideoPlayerController(); setVideoPlayerController();
} }
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
@ -330,10 +291,7 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
child: Container( child: Container(
color: Colors.transparent, color: Colors.transparent,
constraints: BoxConstraints.expand( constraints: BoxConstraints.expand(
height: MediaQuery height: MediaQuery.of(context).size.height,
.of(context)
.size
.height,
), ),
child: ExtendedImageSlidePage( child: ExtendedImageSlidePage(
key: slidePagekey, key: slidePagekey,
@ -342,13 +300,12 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
return Colors.black; return Colors.black;
} }
double opacity = 0.0; double opacity = 0.0;
opacity = offset.distance / opacity = offset.distance / (Offset(size.width, size.height).distance / 2.0);
(Offset(size.width, size.height).distance / 2.0); return Colors.black.withOpacity(min(1.0, max(1.0 - opacity, 0.0)));
return Colors.black
.withOpacity(min(1.0, max(1.0 - opacity, 0.0)));
}, },
slideType: SlideType.onlyImage, slideType: SlideType.onlyImage,
slideEndHandler: (Offset offset, { slideEndHandler: (
Offset offset, {
ExtendedImageSlidePageState? state, ExtendedImageSlidePageState? state,
ScaleEndDetails? details, ScaleEndDetails? details,
}) { }) {
@ -364,24 +321,15 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
color: Colors.black, color: Colors.black,
child: isInit child: isInit
? Chewie( ? Chewie(
controller: chewieController, controller: chewieController,
) )
: const Center( : const Center(child: CircularProgressIndicator(color: Colors.white))),
child:
CircularProgressIndicator(color: Colors.white))),
heroBuilderForSlidingPage: (Widget result) { heroBuilderForSlidingPage: (Widget result) {
return Hero( return Hero(
tag: widget.heroTag, tag: widget.heroTag,
child: result, child: result,
flightShuttleBuilder: (BuildContext flightContext, flightShuttleBuilder: (BuildContext flightContext, Animation<double> animation, HeroFlightDirection flightDirection, BuildContext fromHeroContext, BuildContext toHeroContext) {
Animation<double> animation, final Hero hero = (flightDirection == HeroFlightDirection.pop ? fromHeroContext.widget : toHeroContext.widget) as Hero;
HeroFlightDirection flightDirection,
BuildContext fromHeroContext,
BuildContext toHeroContext) {
final Hero hero =
(flightDirection == HeroFlightDirection.pop
? fromHeroContext.widget
: toHeroContext.widget) as Hero;
return hero.child; return hero.child;
}, },

View File

@ -2,15 +2,15 @@
import 'dart:io'; import 'dart:io';
import 'package:chewie_for_us/chewie_for_us.dart'; import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart'; import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.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/tencent_cloud_chat_uikit.dart'; import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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/widgets/drag_widget.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/drag_widget.dart';
import 'package:path/path.dart' as p;
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
class TUIKitWidePopup { class TUIKitWidePopup {
@ -70,26 +70,10 @@ class TUIKitWidePopup {
} }
isShow = true; isShow = true;
final TUISelfInfoViewModel selfInfoViewModel = final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
serviceLocator<TUISelfInfoViewModel>();
if (selfInfoViewModel.globalConfig?.showDesktopModalFunc != null) { if (selfInfoViewModel.globalConfig?.showDesktopModalFunc != null) {
final res = await selfInfoViewModel.globalConfig!.showDesktopModalFunc!( final res = await selfInfoViewModel.globalConfig!.showDesktopModalFunc!(operationKey, context, child, theme, width, height, offset, initText, borderRadius, isDarkBackground, title, onSubmit, submitWidget, onConfirm, onCancel);
operationKey,
context,
child,
theme,
width,
height,
offset,
initText,
borderRadius,
isDarkBackground,
title,
onSubmit,
submitWidget,
onConfirm,
onCancel);
if (res == true) { if (res == true) {
return; return;
@ -102,8 +86,7 @@ class TUIKitWidePopup {
width: width, width: width,
height: height, height: height,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius: borderRadius ?? const BorderRadius.all(Radius.circular(16)),
borderRadius ?? const BorderRadius.all(Radius.circular(16)),
color: theme?.wideBackgroundColor ?? const Color(0xFFffffff), color: theme?.wideBackgroundColor ?? const Color(0xFFffffff),
border: isDarkBackground border: isDarkBackground
? Border.all( ? Border.all(
@ -129,9 +112,7 @@ class TUIKitWidePopup {
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: hexToColor("f5f6f7"), color: hexToColor("f5f6f7"),
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16)),
topLeft: Radius.circular(16),
topRight: Radius.circular(16)),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -139,9 +120,7 @@ class TUIKitWidePopup {
children: [ children: [
Text( Text(
title, title,
style: TextStyle( style: TextStyle(fontSize: 18, color: theme?.darkTextColor ?? const Color(0xFF444444)),
fontSize: 18,
color: theme?.darkTextColor ?? const Color(0xFF444444)),
), ),
InkWell( InkWell(
onTap: () { onTap: () {
@ -156,9 +135,7 @@ class TUIKitWidePopup {
entry = null; entry = null;
} }
}, },
child: onSubmit != null child: onSubmit != null ? (submitWidget ?? const Icon(Icons.check)) : const Icon(Icons.close),
? (submitWidget ?? const Icon(Icons.check))
: const Icon(Icons.close),
) )
], ],
), ),
@ -212,8 +189,7 @@ class TUIKitWidePopup {
}, },
child: Text( child: Text(
TIM_t("取消"), TIM_t("取消"),
style: TextStyle( style: TextStyle(color: theme?.weakTextColor ?? Colors.black),
color: theme?.weakTextColor ?? Colors.black),
)), )),
), ),
if (onConfirm != null) if (onConfirm != null)
@ -293,8 +269,7 @@ class TUIKitWidePopup {
required VoidCallback onClickOrigin, required VoidCallback onClickOrigin,
double? aspectRatio, double? aspectRatio,
}) async { }) async {
assert((mediaLocalPath != null) || (mediaURL != null), assert((mediaLocalPath != null) || (mediaURL != null), "At least one of mediaLocalPath or mediaURL must be provided.");
"At least one of mediaLocalPath or mediaURL must be provided.");
String _removeQueryString(String urlString) { String _removeQueryString(String urlString) {
Uri uri = Uri.parse(urlString); Uri uri = Uri.parse(urlString);
@ -310,10 +285,8 @@ class TUIKitWidePopup {
final String mediaPath = mediaLocalPath ?? mediaURL ?? ""; final String mediaPath = mediaLocalPath ?? mediaURL ?? "";
final isLocalResource = mediaLocalPath != null; final isLocalResource = mediaLocalPath != null;
String fileExtension = p String fileExtension = p.extension(isLocalResource ? mediaPath : _removeQueryString(mediaPath));
.extension(isLocalResource ? mediaPath : _removeQueryString(mediaPath)); bool isVideo = ['.mp4', '.avi', '.mov', '.flv', '.wmv'].contains(fileExtension);
bool isVideo =
['.mp4', '.avi', '.mov', '.flv', '.wmv'].contains(fileExtension);
VideoPlayerController? videoController; VideoPlayerController? videoController;
ChewieController? chewieController; ChewieController? chewieController;
@ -324,8 +297,7 @@ class TUIKitWidePopup {
if (isLocalResource) { if (isLocalResource) {
videoController = VideoPlayerController.file(File(mediaPath)); videoController = VideoPlayerController.file(File(mediaPath));
} else { } else {
videoController = videoController = VideoPlayerController.networkUrl(Uri.parse(mediaPath));
VideoPlayerController.networkUrl(Uri.parse(mediaPath));
} }
await videoController.initialize(); await videoController.initialize();
@ -342,9 +314,7 @@ class TUIKitWidePopup {
mediaWidget = Chewie(controller: chewieController); mediaWidget = Chewie(controller: chewieController);
} else { } else {
mediaWidget = isLocalResource mediaWidget = isLocalResource ? Image.file(File(mediaPath), fit: BoxFit.contain) : Image.network(mediaPath, fit: BoxFit.contain);
? Image.file(File(mediaPath), fit: BoxFit.contain)
: Image.network(mediaPath, fit: BoxFit.contain);
} }
showDialog( showDialog(
@ -373,11 +343,7 @@ class TUIKitWidePopup {
maxWidth: MediaQuery.of(context).size.width * 0.85, maxWidth: MediaQuery.of(context).size.width * 0.85,
maxHeight: MediaQuery.of(context).size.height * 0.82, maxHeight: MediaQuery.of(context).size.height * 0.82,
), ),
child: aspectRatioFinal != null child: aspectRatioFinal != null ? AspectRatio(aspectRatio: aspectRatioFinal, child: mediaWidget) : mediaWidget,
? AspectRatio(
aspectRatio: aspectRatioFinal,
child: mediaWidget)
: mediaWidget,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
InkWell( InkWell(

View File

@ -217,14 +217,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.3" version: "2.0.3"
chewie_for_us: chewie:
dependency: "direct main" dependency: "direct main"
description: description:
name: chewie_for_us name: chewie
sha256: "0307723e811508d361fffa6f8bbd9040b1bfea5536544e4d655e10c27de002ec" sha256: "8bc4ac4cf3f316e50a25958c0f5eb9bb12cf7e8308bb1d74a43b230da2cfc144"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "1.7.5"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -313,6 +313,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.2"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.dev"
source: hosted
version: "0.7.10"
desktop_drop: desktop_drop:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1308,26 +1316,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: tencent_cloud_chat_sdk name: tencent_cloud_chat_sdk
sha256: a78f1f20dc9ebe40aee1bbb47da097780028434d77e97774fbe733debb21e18e sha256: "358e79b51aba5457418d3bb87e0bbd0f088a1eaf4c4463d09bdda93d1d655aa3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.7.5296" version: "7.9.5672"
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: "7ddb2c034e5f832261ba268957e282b7c2e738acb1d21aa40c62dad4eaa433ea" sha256: "61a5400b3fe75c00252272469f332e7ec07f6d1932ee636a3f2b919cf9805cb8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.2" version: "1.6.0"
tencent_im_base: tencent_im_base:
dependency: "direct main" dependency: "direct main"
description: description:
name: tencent_im_base name: tencent_im_base
sha256: "035d97d24bebb87654700d4afc8227de8721a259ef5d0195f3207cb0eb0cdc7a" sha256: daee1faac70fdf5fa4a53576db4fb7268ba5d897cc036353d3114a31abb76fb1
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.775296" version: "3.3.775297"
tencent_keyboard_visibility: tencent_keyboard_visibility:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1560,22 +1568,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.16" version: "2.0.16"
wakelock_for_us: wakelock_plus:
dependency: transitive dependency: transitive
description: description:
name: wakelock_for_us name: wakelock_plus
sha256: a5bdd445e51a617f7c24be8165230391447301f622aacd050038cee7b41989b4 sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.3+1" version: "1.1.4"
wakelock_platform_interface: wakelock_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: wakelock_platform_interface name: wakelock_plus_platform_interface
sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0" version: "1.1.0"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:

View File

@ -1,6 +1,6 @@
name: tencent_cloud_chat_uikit name: tencent_cloud_chat_uikit
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences. description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
version: 2.5.1 version: 2.5.1+4
homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn
repository: https://github.com/TencentCloud/chat-uikit-flutter repository: https://github.com/TencentCloud/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
@ -29,7 +29,7 @@ dependencies:
file_picker: ^5.3.0 file_picker: ^5.3.0
tencent_super_tooltip: ^0.0.1 tencent_super_tooltip: ^0.0.1
video_player: ^2.7.0 video_player: ^2.7.0
chewie_for_us: ^1.5.0 chewie: ^1.7.5
flutter_slidable: ^3.0.1 flutter_slidable: ^3.0.1
flutter_plugin_record_plus: ^0.0.16 flutter_plugin_record_plus: ^0.0.16
azlistview_all_platforms: ^2.1.2 azlistview_all_platforms: ^2.1.2
@ -62,10 +62,10 @@ dependencies:
open_file: ^3.3.2 open_file: ^3.3.2
tencent_keyboard_visibility: ^1.0.1 tencent_keyboard_visibility: ^1.0.1
tim_ui_kit_sticker_plugin: ^3.1.0 tim_ui_kit_sticker_plugin: ^3.1.0
tencent_im_base: ^3.3.775296 tencent_im_base: ^3.3.775297
fc_native_video_thumbnail: any fc_native_video_thumbnail: any
path: ^1.8.1 path: ^1.8.1
tencent_cloud_uikit_core: ^1.2.1 tencent_cloud_uikit_core: ^1.6.0
pasteboard: ^0.2.0 pasteboard: ^0.2.0
desktop_drop: ^0.4.4 desktop_drop: ^0.4.4
device_info_plus: any device_info_plus: any
@ -82,6 +82,11 @@ dev_dependencies:
build_runner: any build_runner: any
lints: ^1.0.1 lints: ^1.0.1
dependency_overrides:
# tencent_chat_i18n_tool:
# path: D:\Project\tencent-chat-i18n-tool
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec