feat: Upgrade to 3.1.0
This commit is contained in:
parent
157f48e653
commit
8ad7673b7b
|
|
@ -1,3 +1,9 @@
|
|||
# 3.1.0
|
||||
## Bug Fixes
|
||||
* The interface for deleting messages is changed to the interface for deleting cloud messages.
|
||||
* C2C messages support read receipts
|
||||
* Fix and optimize some issues
|
||||
|
||||
# 3.0.0
|
||||
## Breaking Changes
|
||||
* Migrated to Flutter 3.24.0
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
#
|
||||
It is recommended to download the source code from [pub.dev](https://pub.dev/packages/tencent_cloud_chat_uikit/versions)
|
||||
|
||||
## Product Introduction
|
||||
You only need to integrate Chat SDK to easily gain chat, conversation, group capabilities, and you can also communicate with other products such as whiteboards through signaling messages. Chat can cover various business scenarios, support the access and use of various platforms, and fully meet the communication needs.
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,2 +0,0 @@
|
|||
<EFBFBD>
|
||||
<EFBFBD>
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_im_base/tencent_im_base.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/services_locatar.dart';
|
||||
|
|
@ -15,6 +14,8 @@ class NeedUpdate {
|
|||
final String groupID;
|
||||
final UpdateType updateType;
|
||||
final String extraData;
|
||||
int? groupInfoSubType;
|
||||
String? ownerID;
|
||||
|
||||
NeedUpdate(this.groupID, this.updateType, this.extraData);
|
||||
}
|
||||
|
|
@ -44,10 +45,11 @@ class TUIGroupListenerModel extends ChangeNotifier {
|
|||
onMemberKicked: (groupID, opUser, memberList) async {
|
||||
if (_isLoginUserKickedFromGroup(groupID, memberList)) {
|
||||
_deleteGroupConversation(groupID);
|
||||
}
|
||||
|
||||
final groupName = await _getGroupName(groupID);
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.kickedFromGroup, groupName);
|
||||
notifyListeners();
|
||||
}
|
||||
},
|
||||
onMemberEnter: (String groupID, List<V2TimGroupMemberInfo> memberList) {
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.memberList, "");
|
||||
|
|
@ -59,6 +61,12 @@ class TUIGroupListenerModel extends ChangeNotifier {
|
|||
},
|
||||
onGroupInfoChanged: (groupID, changeInfos) {
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.groupInfo, "");
|
||||
for (V2TimGroupChangeInfo info in changeInfos) {
|
||||
if (info.type == GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER) {
|
||||
_needUpdate!.groupInfoSubType = GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER;
|
||||
_needUpdate!.ownerID = info.value;
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
},
|
||||
onReceiveJoinApplication:
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ import 'dart:math';
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
// ignore: unnecessary_import
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/chat_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_model_tools.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_conversation_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/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
|
||||
|
|
@ -33,6 +32,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
||||
final TUIChatModelTools tools = serviceLocator<TUIChatModelTools>();
|
||||
final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
final TUIConversationViewModel conversationViewModel = serviceLocator<TUIConversationViewModel>();
|
||||
final _uuid = const Uuid();
|
||||
|
||||
ChatLifeCycle? lifeCycle;
|
||||
|
|
@ -45,7 +45,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
bool haveMoreLatestData = false;
|
||||
String _currentPlayedMsgId = "";
|
||||
GroupReceiptAllowType? _groupType;
|
||||
List<V2TimMessage> _multiSelectedMessageList = [];
|
||||
// List<V2TimMessage> _multiSelectedMessageList = [];
|
||||
Map<String, bool> _selectedPositions = {};
|
||||
V2TimMessage? _repliedMessage;
|
||||
String _jumpMsgID = "";
|
||||
bool _isGroupExist = true;
|
||||
|
|
@ -68,7 +69,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
String? _groupID;
|
||||
|
||||
Map<String, String> get groupUserShowName => _groupUserShowName;
|
||||
final List<String> _sendingMessageIDList = [];
|
||||
// value 的 bool 值表示是否已经延迟显示过发送进度
|
||||
final Map<String, bool> _sendingMessageIDMap = {};
|
||||
Map<String, V2TimMessage> _readReceiptMap = {};
|
||||
|
||||
set groupUserShowName(Map<String, String> value) {
|
||||
_groupUserShowName = value;
|
||||
|
|
@ -124,11 +127,35 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
_notify();
|
||||
}
|
||||
|
||||
List<V2TimMessage> get multiSelectedMessageList => _multiSelectedMessageList;
|
||||
List<V2TimMessage> getSelectedMessageList() {
|
||||
List<V2TimMessage> selectList = [];
|
||||
if (_selectedPositions.isEmpty) {
|
||||
return selectList;
|
||||
}
|
||||
|
||||
set multiSelectedMessageList(List<V2TimMessage> value) {
|
||||
_multiSelectedMessageList = value;
|
||||
_notify();
|
||||
List<V2TimMessage> currentHistoryMsgList = getOriginMessageList();
|
||||
for (var v2TimMessage in currentHistoryMsgList) {
|
||||
if (_selectedPositions.containsKey(v2TimMessage.msgID) && _selectedPositions[v2TimMessage.msgID]!) {
|
||||
selectList.add(v2TimMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return selectList.reversed.toList();
|
||||
}
|
||||
|
||||
List<String> getSelectedMessageIDList() {
|
||||
List<String> selectList = [];
|
||||
if (_selectedPositions.isEmpty) {
|
||||
return selectList;
|
||||
}
|
||||
|
||||
for (String msgID in _selectedPositions.keys) {
|
||||
if (_selectedPositions[msgID]!) {
|
||||
selectList.add(msgID);
|
||||
}
|
||||
}
|
||||
|
||||
return selectList;
|
||||
}
|
||||
|
||||
V2TimMessage? get repliedMessage => _repliedMessage;
|
||||
|
|
@ -233,7 +260,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
globalModel.setMessageListPosition(
|
||||
conversationID, HistoryMessagePosition.bottom);
|
||||
globalModel.setChatConfig(chatConfig);
|
||||
globalModel.clearRecivedNewMessageCount();
|
||||
globalModel.clearReceivedNewMessageCount();
|
||||
|
||||
if (conversationType == ConvType.group) {
|
||||
_groupID = groupID;
|
||||
|
|
@ -290,7 +317,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
required int seq,
|
||||
}) async {
|
||||
List<V2TimMessage> msgList = [];
|
||||
haveMoreData = false;
|
||||
bool tempHaveMoreData = false;
|
||||
|
||||
final previousResponse =
|
||||
await _messageService.getHistoryMessageListWithComplete(
|
||||
|
|
@ -300,7 +327,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
groupID: conversationType == ConvType.group ? conversationID : null,
|
||||
lastMsgSeq: max(seq, 0));
|
||||
msgList = previousResponse?.messageList ?? [];
|
||||
haveMoreData = !(previousResponse?.isFinished ?? false);
|
||||
tempHaveMoreData = !(previousResponse?.isFinished ?? false);
|
||||
haveMoreLatestData = true;
|
||||
globalModel.setMessageListPosition(
|
||||
conversationID, HistoryMessagePosition.notShowLatest);
|
||||
|
|
@ -318,15 +345,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
globalModel.setMessageList(conversationID, msgList,
|
||||
needResetNewMessageCount: false);
|
||||
|
||||
if (chatConfig.isShowGroupReadingStatus &&
|
||||
conversationType == ConvType.group) {
|
||||
if (chatConfig.isShowReadingStatus) {
|
||||
_getMsgReadReceipt(msgList);
|
||||
}
|
||||
if (chatConfig.isReportGroupReadingStatus &&
|
||||
conversationType == ConvType.group) {
|
||||
_setMsgReadReceipt(msgList);
|
||||
}
|
||||
|
||||
haveMoreData = tempHaveMoreData;
|
||||
return haveMoreData;
|
||||
}
|
||||
|
||||
|
|
@ -339,10 +362,12 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
LoadDirection direction = LoadDirection.previous,
|
||||
}) async {
|
||||
try {
|
||||
bool tempHaveMoreData = false;
|
||||
// 根据加载方向设置是否还能继续加载更多消息
|
||||
direction == LoadDirection.latest
|
||||
? haveMoreLatestData = false
|
||||
: haveMoreData = false;
|
||||
: tempHaveMoreData = false;
|
||||
|
||||
|
||||
// 获取当前聊天对话的历史消息列表
|
||||
final currentRecordList = globalModel.messageListMap[conversationID];
|
||||
|
|
@ -368,8 +393,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
if (direction == LoadDirection.latest) {
|
||||
haveMoreLatestData = !response.isFinished;
|
||||
} else {
|
||||
haveMoreData = !response.isFinished;
|
||||
tempHaveMoreData = !response.isFinished;
|
||||
}
|
||||
|
||||
_notify();
|
||||
|
||||
// 根据lastMsgID判断是否为分页加载
|
||||
|
|
@ -380,7 +406,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
// 根据加载方向拼接消息列表
|
||||
if (direction == LoadDirection.latest) {
|
||||
globalModel.receivedNewMessageCount =
|
||||
globalModel.receivedMessageListCount + messageList.length;
|
||||
globalModel.receivedNewMessageCount + messageList.length;
|
||||
messageList = messageList.reversed.toList();
|
||||
newList = _combineMessageList(messageList, currentRecordList);
|
||||
} else {
|
||||
|
|
@ -413,16 +439,9 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
}
|
||||
|
||||
// 获取已读未读状态
|
||||
if (chatConfig.isShowGroupReadingStatus &&
|
||||
conversationType == ConvType.group &&
|
||||
response.messageList.isNotEmpty) {
|
||||
if (chatConfig.isShowReadingStatus && response.messageList.isNotEmpty) {
|
||||
_getMsgReadReceipt(response.messageList);
|
||||
}
|
||||
if (chatConfig.isReportGroupReadingStatus &&
|
||||
conversationType == ConvType.group &&
|
||||
response.messageList.isNotEmpty) {
|
||||
_setMsgReadReceipt(response.messageList);
|
||||
}
|
||||
|
||||
// 根据加载方向更新是否还能继续加载更多消息
|
||||
if (direction == LoadDirection.latest && !haveMoreLatestData) {
|
||||
|
|
@ -431,6 +450,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
}
|
||||
_notify();
|
||||
|
||||
haveMoreData = tempHaveMoreData;
|
||||
return haveMoreData;
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
|
|
@ -439,7 +459,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
// 拼接聊天记录
|
||||
// 拼接聊天记录
|
||||
List<V2TimMessage> _combineMessageList(
|
||||
List<V2TimMessage> first, List<V2TimMessage> second) {
|
||||
return [...first, ...second];
|
||||
|
|
@ -458,7 +478,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
|
||||
_getMsgReadReceipt(List<V2TimMessage> message) async {
|
||||
final msgID = message
|
||||
.where((e) => (e.isSelf ?? true) && (e.needReadReceipt ?? false))
|
||||
.where((e) => (e.isSelf ?? true) && (e.needReadReceipt ?? false) && (e.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC))
|
||||
.map((e) => e.msgID ?? '')
|
||||
.toList();
|
||||
if (msgID.isNotEmpty) {
|
||||
|
|
@ -491,9 +511,21 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
msgID: message.msgID!, localCustomData: message.localCustomData ?? "");
|
||||
}
|
||||
|
||||
_setMsgReadReceipt(List<V2TimMessage> message) async {
|
||||
addToMessageReadReceiptList(V2TimMessage message) {
|
||||
if (chatConfig.isShowReadingStatus) {
|
||||
if (message.msgID != null) {
|
||||
_readReceiptMap[message.msgID!] = message;
|
||||
}
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
_setMsgReadReceipt(_readReceiptMap.values.toList());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_setMsgReadReceipt(List<V2TimMessage> messageList) async {
|
||||
final msgIDList = List<String>.empty(growable: true);
|
||||
for (var item in message) {
|
||||
for (var item in messageList) {
|
||||
final isSelf = item.isSelf ?? true;
|
||||
final needReadReceipt = item.needReadReceipt ?? false;
|
||||
final isRead = item.isRead ?? false;
|
||||
|
|
@ -514,7 +546,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
}
|
||||
|
||||
markMessageAsRead() async {
|
||||
globalModel.unreadCountForConversation = 0;
|
||||
if (conversationType == ConvType.c2c) {
|
||||
return _messageService.markC2CMessageAsRead(userID: conversationID);
|
||||
}
|
||||
|
|
@ -655,9 +686,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
if (convType == ConvType.group && _groupType == null) {
|
||||
await loadGroupInfo(groupID);
|
||||
}
|
||||
final oldGroupType = _groupType != null
|
||||
? GroupReceptAllowType.values[_groupType!.index]
|
||||
: null;
|
||||
if (messageInfo != null) {
|
||||
setLoadingMessageMap(convID, messageInfo);
|
||||
}
|
||||
|
|
@ -667,15 +695,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
isExcludedFromUnreadCount: isExcludedFromUnreadCount ?? false,
|
||||
id: id,
|
||||
receiver: receiver,
|
||||
needReadReceipt: needReadReceipt ??
|
||||
chatConfig.isShowGroupReadingStatus &&
|
||||
convType == ConvType.group &&
|
||||
((chatConfig.groupReadReceiptPermissionList != null &&
|
||||
chatConfig.groupReadReceiptPermissionList!
|
||||
.contains(_groupType)) ||
|
||||
(chatConfig.groupReadReceiptPermisionList != null &&
|
||||
chatConfig.groupReadReceiptPermisionList!
|
||||
.contains(oldGroupType))),
|
||||
needReadReceipt: needReadReceipt ?? chatConfig.isShowReadingStatus,
|
||||
groupID: groupID,
|
||||
offlinePushInfo: offlinePushInfo,
|
||||
onlineUserOnly: onlineUserOnly ?? false,
|
||||
|
|
@ -708,7 +728,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
}
|
||||
|
||||
int getConversationUnreadCount() {
|
||||
return globalModel.unreadCountForConversation;
|
||||
return globalModel.unreadCountForTongue;
|
||||
}
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessage>?> sendTextAtMessage(
|
||||
|
|
@ -871,9 +891,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
final V2TimMessage? messageInfo = textMessageInfo!.messageInfo;
|
||||
final receiver = convType == ConvType.c2c ? convID : '';
|
||||
final groupID = convType == ConvType.group ? convID : '';
|
||||
final oldGroupType = _groupType != null
|
||||
? GroupReceptAllowType.values[_groupType!.index]
|
||||
: null;
|
||||
if (messageInfo != null) {
|
||||
V2TimMessage messageInfoWithSender =
|
||||
tools.setUserInfoForMessage(messageInfo, textMessageInfo.id!);
|
||||
|
|
@ -909,14 +926,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
id: textMessageInfo.id as String,
|
||||
offlinePushInfo: tools.buildMessagePushInfo(
|
||||
messageInfoWithSender, convID, convType),
|
||||
needReadReceipt: chatConfig.isShowGroupReadingStatus &&
|
||||
convType == ConvType.group &&
|
||||
((chatConfig.groupReadReceiptPermissionList != null &&
|
||||
chatConfig.groupReadReceiptPermissionList!
|
||||
.contains(_groupType)) ||
|
||||
(chatConfig.groupReadReceiptPermisionList != null &&
|
||||
chatConfig.groupReadReceiptPermisionList!
|
||||
.contains(oldGroupType))),
|
||||
needReadReceipt: chatConfig.isShowReadingStatus,
|
||||
groupID: groupID,
|
||||
receiver: receiver);
|
||||
_notify();
|
||||
|
|
@ -1135,15 +1145,11 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
sendForwardMessage({
|
||||
required List<V2TimConversation> conversationList,
|
||||
}) async {
|
||||
final selectedMessages = List.from(_multiSelectedMessageList);
|
||||
if (conversationType == ConvType.c2c) {
|
||||
selectedMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp));
|
||||
} else if (conversationType == ConvType.group) {
|
||||
selectedMessages.sort((a, b) => a.seq.compareTo(b.seq));
|
||||
}
|
||||
final selectedMessages = getSelectedMessageList();
|
||||
for (var conversation in conversationList) {
|
||||
final convID = conversation.groupID ?? conversation.userID ?? "";
|
||||
final convType = conversation.type;
|
||||
List<V2TimMessage> currentHistoryMsgList = globalModel.messageListMap[conversationID] ?? [];
|
||||
for (var message in selectedMessages) {
|
||||
final forwardMessageInfo = await _messageService.createForwardMessage(msgID: message.msgID!);
|
||||
final messageInfo = forwardMessageInfo!.messageInfo;
|
||||
|
|
@ -1151,6 +1157,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
tools.setUserInfoForMessage(messageInfo, forwardMessageInfo.id);
|
||||
messageInfo.status = MessageStatus.V2TIM_MSG_STATUS_SENDING;
|
||||
addSendingMessageID(messageInfo.id);
|
||||
// 如果转发的会话是当前会话,则直接添加到当前会话的消息列表中
|
||||
if (convID == conversationID) {
|
||||
if (globalModel.getMessageListPosition(convID) != HistoryMessagePosition.notShowLatest) {
|
||||
currentHistoryMsgList = [
|
||||
messageInfo,
|
||||
...currentHistoryMsgList
|
||||
];
|
||||
globalModel.setMessageList(conversationID, currentHistoryMsgList);
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
await Future.delayed(Duration(milliseconds: 100), () {
|
||||
_sendMessage(
|
||||
id: forwardMessageInfo.id!,
|
||||
|
|
@ -1174,13 +1191,14 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
required List<String> abstractList,
|
||||
required BuildContext context,
|
||||
}) async {
|
||||
final List<String> msgIDList = _multiSelectedMessageList
|
||||
final List<String> msgIDList = getSelectedMessageList()
|
||||
.map((e) => e.msgID ?? "")
|
||||
.where((element) => element != "")
|
||||
.toList();
|
||||
for (var conversation in conversationList) {
|
||||
final convID = conversation.groupID ?? conversation.userID ?? "";
|
||||
final convType = conversation.type;
|
||||
List<V2TimMessage> currentHistoryMsgList = globalModel.messageListMap[conversationID] ?? [];
|
||||
final mergerMessageInfo = await _messageService.createMergerMessage(
|
||||
msgIDList: msgIDList,
|
||||
title: title,
|
||||
|
|
@ -1191,6 +1209,17 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
tools.setUserInfoForMessage(messageInfo, mergerMessageInfo.id);
|
||||
messageInfo.status = MessageStatus.V2TIM_MSG_STATUS_SENDING;
|
||||
addSendingMessageID(messageInfo.id);
|
||||
// 如果转发的会话是当前会话,则直接添加到当前会话的消息列表中
|
||||
if (convID == conversationID) {
|
||||
if (globalModel.getMessageListPosition(convID) != HistoryMessagePosition.notShowLatest) {
|
||||
currentHistoryMsgList = [
|
||||
messageInfo,
|
||||
...currentHistoryMsgList
|
||||
];
|
||||
globalModel.setMessageList(conversationID, currentHistoryMsgList);
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
_sendMessage(
|
||||
id: mergerMessageInfo.id!,
|
||||
convID: convID,
|
||||
|
|
@ -1215,24 +1244,34 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
return null;
|
||||
}
|
||||
|
||||
int messageIndex = currentHistoryMsgList.indexWhere((element) => element.msgID == message.msgID);
|
||||
if (messageIndex != -1) {
|
||||
// 取出界面的消息列表,设置转发的这条消息状态为 sending
|
||||
currentHistoryMsgList[messageIndex].status = MessageStatus.V2TIM_MSG_STATUS_SENDING;
|
||||
currentHistoryMsgList.removeWhere((element) => element.msgID == message.msgID);
|
||||
message.status = MessageStatus.V2TIM_MSG_STATUS_SENDING;
|
||||
addSendingMessageID(message.msgID);
|
||||
globalModel.setMessageList(convID, currentHistoryMsgList);
|
||||
if (globalModel.getMessageListPosition(conversationID) !=
|
||||
HistoryMessagePosition.notShowLatest) {
|
||||
currentHistoryMsgList = [
|
||||
message,
|
||||
...currentHistoryMsgList
|
||||
];
|
||||
globalModel.setMessageList(conversationID, currentHistoryMsgList);
|
||||
_notify();
|
||||
}
|
||||
|
||||
// 重发该消息
|
||||
final res = await _messageService.reSendMessage(
|
||||
msgID: message.msgID ?? "", onlineUserOnly: false);
|
||||
removeSendingMessageID(message.msgID ?? "");
|
||||
final messageInfo = res.data;
|
||||
// 重发完成后,更新消息列表中的这条消息
|
||||
currentHistoryMsgList[messageIndex] = messageInfo!;
|
||||
globalModel.setMessageList(convID, currentHistoryMsgList);
|
||||
return res;
|
||||
if (globalModel.getMessageListPosition(conversationID) !=
|
||||
HistoryMessagePosition.notShowLatest) {
|
||||
globalModel.updateMessage(
|
||||
res, convID, message.msgID!, convType, groupType, setInputField);
|
||||
}
|
||||
|
||||
return null;
|
||||
if (lifeCycle?.messageDidSend != null) {
|
||||
lifeCycle!.messageDidSend(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessage>?> sendTextMessage(
|
||||
|
|
@ -1322,8 +1361,8 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
return;
|
||||
}
|
||||
final messageList = getOriginMessageList();
|
||||
final res = await _messageService.deleteMessageFromLocalStorage(
|
||||
msgID: msgID, webMessageInstance: webMessageInstance);
|
||||
final res = await _messageService.deleteMessages(
|
||||
msgIDs: [msgID], webMessageInstanceList: [webMessageInstance]);
|
||||
if (res.code == 0) {
|
||||
messageList.removeWhere((element) {
|
||||
return element.msgID == msgID || (id != null && element.id == id);
|
||||
|
|
@ -1368,26 +1407,18 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
return res;
|
||||
}
|
||||
|
||||
addToMultiSelectedMessageList(V2TimMessage message) {
|
||||
_multiSelectedMessageList.add(message);
|
||||
_notify();
|
||||
setMessageItemChecked(V2TimMessage message, bool isChecked) {
|
||||
if (message.msgID != null) {
|
||||
_selectedPositions[message.msgID!] = isChecked;
|
||||
}
|
||||
|
||||
removeFromMultiSelectedMessageList(V2TimMessage message) {
|
||||
_multiSelectedMessageList.remove(message);
|
||||
_notify();
|
||||
}
|
||||
|
||||
deleteSelectedMsg() async {
|
||||
List<V2TimMessage> messageList = getOriginMessageList();
|
||||
final msgIDs = _multiSelectedMessageList
|
||||
.map((e) => e.msgID ?? "")
|
||||
.where((element) => element != "")
|
||||
.toList();
|
||||
final webMessageInstanceList = _multiSelectedMessageList
|
||||
.map((e) => e.messageFromWeb)
|
||||
.where((element) => element != null)
|
||||
.toList();
|
||||
final msgIDs = getSelectedMessageIDList();
|
||||
final webMessageInstanceList = getSelectedMessageIDList();
|
||||
|
||||
final res = await _messageService.deleteMessages(
|
||||
msgIDs: msgIDs, webMessageInstanceList: webMessageInstanceList);
|
||||
|
|
@ -1395,14 +1426,14 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
for (var msgID in msgIDs) {
|
||||
messageList.removeWhere((element) => element.msgID == msgID);
|
||||
}
|
||||
globalModel.setMessageList(conversationID, messageList);
|
||||
globalModel.setMessageList(conversationID, messageList, isDeleteMsg: true);
|
||||
}
|
||||
}
|
||||
|
||||
updateMultiSelectStatus(bool isSelect) {
|
||||
_isMultiSelect = isSelect;
|
||||
if (!isSelect) {
|
||||
_multiSelectedMessageList.clear();
|
||||
_selectedPositions.clear();
|
||||
}
|
||||
_notify();
|
||||
}
|
||||
|
|
@ -1440,6 +1471,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
}
|
||||
|
||||
showLatestUnread() {
|
||||
globalModel.unreadCountForTongue = 0;
|
||||
markMessageAsRead();
|
||||
globalModel.setMessageListPosition(
|
||||
conversationID, HistoryMessagePosition.bottom);
|
||||
|
|
@ -1448,25 +1480,56 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
|
|||
// 添加发送中的消息的 id 或者 msgID(id 不存在时使用 msgID)
|
||||
void addSendingMessageID(String? id) {
|
||||
if (id?.isNotEmpty == true) {
|
||||
_sendingMessageIDList.add(id!);
|
||||
_sendingMessageIDMap[id!] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 移除发送中的消息的 id 或者 msgID(id 不存在时使用 msgID)
|
||||
void removeSendingMessageID(String id) {
|
||||
bool hasID = _sendingMessageIDList.contains(id);
|
||||
if (hasID) {
|
||||
_sendingMessageIDList.remove(id);
|
||||
_sendingMessageIDMap.remove(id);
|
||||
}
|
||||
|
||||
// 是否已经延迟渲染
|
||||
bool? hasDelayedRenderSendingStatus(String id) {
|
||||
if (_sendingMessageIDMap.containsKey(id)) {
|
||||
return _sendingMessageIDMap[id];
|
||||
}
|
||||
|
||||
print("sending test, hasDelayedRenderSendingStatus false:${id}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 设置已经延迟渲染过的消息
|
||||
void setDelayedRenderSendingStatus(String id) {
|
||||
if (_sendingMessageIDMap.containsKey(id)) {
|
||||
_sendingMessageIDMap[id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 内存中有发送中的消息的 id 或者 msgID(id 不存在时使用 msgID)
|
||||
bool hasSendingMessageID(String id) {
|
||||
return _sendingMessageIDList.contains(id);
|
||||
bool isVoteMessage(V2TimMessage message) {
|
||||
bool isVote = false;
|
||||
V2TimCustomElem? custom = message.customElem;
|
||||
|
||||
if (custom != null) {
|
||||
String? data = custom.data;
|
||||
if (data != null && data.isNotEmpty) {
|
||||
try {
|
||||
Map<String, dynamic> mapData = json.decode(data);
|
||||
if (mapData["businessID"] == "group_poll") {
|
||||
isVote = true;
|
||||
}
|
||||
} catch (err) {
|
||||
// err
|
||||
}
|
||||
}
|
||||
}
|
||||
return isVote;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
markMessageAsRead();
|
||||
globalModel.unreadCountForTongue = 0;
|
||||
globalModel.clearCurrentConversation();
|
||||
_isInit = false;
|
||||
super.dispose();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class TUIGroupProfileModel extends ChangeNotifier {
|
|||
List<V2TimGroupMemberFullInfo?>? _groupMemberList;
|
||||
String _groupMemberListSeq = "0";
|
||||
V2TimGroupInfo? _groupInfo;
|
||||
Function(String userID, TapDownDetails? tapDetails)? onClickUser;
|
||||
Function(V2TimGroupMemberFullInfo groupMemberFullInfo, TapDownDetails? tapDetails)? onClickUser;
|
||||
|
||||
GroupProfileLifeCycle? get lifeCycle => _lifeCycle;
|
||||
|
||||
|
|
@ -233,6 +233,33 @@ class TUIGroupProfileModel extends ChangeNotifier {
|
|||
return res;
|
||||
}
|
||||
|
||||
void onOwnerChanged(String? userID) {
|
||||
if (userID == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 把之前的群主更新为普通成员
|
||||
final preOwnerIndex = _groupMemberList!.indexWhere((e) => e!.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER);
|
||||
if (preOwnerIndex != -1) {
|
||||
final preOwnerElem = _groupMemberList![preOwnerIndex];
|
||||
preOwnerElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
|
||||
|
||||
print("preOwnerUserID: ${preOwnerElem?.userID}");
|
||||
}
|
||||
|
||||
// 设置新的群主
|
||||
final targetIndex = _groupMemberList!.indexWhere((e) => e!.userID == userID);
|
||||
if (targetIndex != -1) {
|
||||
final targetElem = _groupMemberList![targetIndex];
|
||||
targetElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER;
|
||||
_groupMemberList![targetIndex] = targetElem;
|
||||
|
||||
print("newOwnerUserID: ${targetElem?.userID}");
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool canInviteMember() {
|
||||
final groupType = _groupInfo?.groupType;
|
||||
return groupType == GroupType.Work || groupType == "Private";
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ class TUIProfileViewModel extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
Future<V2TimFriendOperationResult?> deleteFriend(String userID) async {
|
||||
Future<V2TimFriendOperationResult?> deleteFriend(String userID, {bool needUpdateData = true}) async {
|
||||
if (_lifeCycle?.shouldDeleteFriend != null &&
|
||||
await _lifeCycle!.shouldDeleteFriend(userID) == false) {
|
||||
return null;
|
||||
|
|
@ -152,9 +152,13 @@ class TUIProfileViewModel extends ChangeNotifier {
|
|||
userIDList: [userID],
|
||||
deleteType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH);
|
||||
if (res != null) {
|
||||
_conversationService.deleteConversation(conversationID: "c2c_$userID");
|
||||
if (needUpdateData) {
|
||||
loadData(userID: userID);
|
||||
}
|
||||
return res.first;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -171,49 +175,6 @@ class TUIProfileViewModel extends ChangeNotifier {
|
|||
return res;
|
||||
}
|
||||
|
||||
// 1:男 女:2
|
||||
Future<V2TimCallback> updateGender(int gender) async {
|
||||
final res = await _coreServices.setSelfInfo(
|
||||
userFullInfo: V2TimUserFullInfo.fromJson(
|
||||
{"gender": gender},
|
||||
),
|
||||
);
|
||||
if (res.code == 0) {
|
||||
_userProfile?.friendInfo!.userProfile!.gender = gender;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateNickName(String nickName) async {
|
||||
final res = await _coreServices.setSelfInfo(
|
||||
userFullInfo: V2TimUserFullInfo.fromJson(
|
||||
{"nickName": nickName},
|
||||
),
|
||||
);
|
||||
|
||||
if (res.code == 0) {
|
||||
_userProfile?.friendInfo!.userProfile!.nickName = nickName;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateSelfSignature(String selfSignature) async {
|
||||
final res = await _coreServices.setSelfInfo(
|
||||
userFullInfo: V2TimUserFullInfo.fromJson(
|
||||
{"selfSignature": selfSignature},
|
||||
),
|
||||
);
|
||||
if (res.code == 0) {
|
||||
_userProfile?.friendInfo!.userProfile!.selfSignature = selfSignature;
|
||||
notifyListeners();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimFriendOperationResult?> addFriend(String userID) async {
|
||||
if (_lifeCycle?.shouldAddFriend != null &&
|
||||
await _lifeCycle!.shouldAddFriend(userID) == false) {
|
||||
|
|
@ -225,6 +186,7 @@ class TUIProfileViewModel extends ChangeNotifier {
|
|||
loadData(userID: userID);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -291,13 +253,14 @@ class TUIProfileViewModel extends ChangeNotifier {
|
|||
newSelfInfo,
|
||||
),
|
||||
);
|
||||
|
||||
if (res.code == 0) {
|
||||
newSelfInfo.forEach((key, value) {
|
||||
updateUserInfo(key, value);
|
||||
});
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,12 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
String localMsgIDListKey = "TUIKit_conversation_list";
|
||||
|
||||
late V2TimAdvancedMsgListener advancedMsgListener;
|
||||
int _unreadCountForConversation = 0;
|
||||
int _unreadCountForTongue = 0;
|
||||
|
||||
// use for generate a new sliver list to show received message list
|
||||
int _receivedNewMessageCount = 0;
|
||||
final List<V2TimMessage> _receivedUnreadMessageList = [];
|
||||
|
||||
TIMUIKitChatConfig chatConfig = const TIMUIKitChatConfig();
|
||||
List<V2TimGroupApplication>? _groupApplicationList;
|
||||
String Function(V2TimMessage message)? _abstractMessageBuilder;
|
||||
|
|
@ -170,15 +172,23 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
return _totalUnreadCount;
|
||||
}
|
||||
|
||||
int get receivedMessageListCount {
|
||||
return _receivedNewMessageCount;
|
||||
set totalUnReadCount(int newValue) {
|
||||
_totalUnreadCount = newValue;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
int get receivedNewMessageCount => _receivedNewMessageCount;
|
||||
|
||||
set receivedNewMessageCount(int value) {
|
||||
_receivedNewMessageCount = value;
|
||||
}
|
||||
|
||||
int get unreadCountForConversation => _unreadCountForConversation;
|
||||
int get unreadCountForTongue => _unreadCountForTongue;
|
||||
|
||||
set unreadCountForTongue(int value) {
|
||||
_unreadCountForTongue = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<V2TimGroupApplication> get groupApplicationList => _groupApplicationList ?? [];
|
||||
|
||||
|
|
@ -201,9 +211,15 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
if (_currentConversationList.isNotEmpty) {
|
||||
_currentConversationList.removeLast();
|
||||
}
|
||||
|
||||
_receivedUnreadMessageList.clear();
|
||||
// notifyListeners();
|
||||
}
|
||||
|
||||
void removeMessageList(String conversationID) {
|
||||
_messageListMap.remove(conversationID);
|
||||
}
|
||||
|
||||
V2TimMessageReceipt? getMessageReadReceipt(String msgID) {
|
||||
return messageReadReceiptMap[msgID];
|
||||
}
|
||||
|
|
@ -250,16 +266,6 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
_groupApplicationList = value;
|
||||
}
|
||||
|
||||
set unreadCountForConversation(int value) {
|
||||
_unreadCountForConversation = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
set totalUnReadCount(int newValue) {
|
||||
_totalUnreadCount = newValue;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setChatConfig(TIMUIKitChatConfig config) {
|
||||
chatConfig = config;
|
||||
}
|
||||
|
|
@ -324,7 +330,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
clearRecivedNewMessageCount() {
|
||||
clearReceivedNewMessageCount() {
|
||||
_receivedNewMessageCount = 0;
|
||||
}
|
||||
|
||||
|
|
@ -523,12 +529,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
_checkFromUserisActive(msgComing);
|
||||
final convType = TencentUtils.checkString(newMsg.groupID) != null ? ConvType.group : ConvType.c2c;
|
||||
if (convID != null && convID == currentSelectedConv) {
|
||||
final position = getMessageListPosition(convID);
|
||||
if (position == HistoryMessagePosition.notShowLatest) {
|
||||
return;
|
||||
}
|
||||
if (position == HistoryMessagePosition.bottom && unreadCountForConversation == 0) {
|
||||
_unreadCountForConversation = 0;
|
||||
// when receive new message in the current chat page, we need to mark the message as read.
|
||||
if (chatConfig.isAutoReportRead) {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
markMessageAsRead(
|
||||
|
|
@ -537,22 +538,22 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
final position = getMessageListPosition(convID);
|
||||
if (position == HistoryMessagePosition.notShowLatest) {
|
||||
return;
|
||||
}
|
||||
if (position == HistoryMessagePosition.bottom && unreadCountForTongue == 0) {
|
||||
_unreadCountForTongue = 0;
|
||||
_receivedNewMessageCount = 0;
|
||||
final tempCurrentMsgList = _messageListMap[convID] ?? [];
|
||||
_messageListMap[convID] = [newMsg, ...tempCurrentMsgList];
|
||||
notifyListeners();
|
||||
final messageID = newMsg.msgID;
|
||||
final needReadReceipt = newMsg.needReadReceipt ?? false;
|
||||
if (needReadReceipt && messageID != null && msgComing.groupID != null && chatConfig.isReportGroupReadingStatus && chatConfig.isAutoReportRead) {
|
||||
// only group message send message read receipt
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
sendMessageReadReceipts([messageID]);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (convID == currentSelectedConv) {
|
||||
unreadCountForConversation++;
|
||||
unreadCountForTongue++;
|
||||
_receivedNewMessageCount++;
|
||||
_receivedUnreadMessageList.add(newMsg);
|
||||
final currentMsg = _messageListMap[convID] ?? [];
|
||||
_messageListMap[convID] = [newMsg, ...currentMsg];
|
||||
notifyListeners();
|
||||
|
|
@ -565,11 +566,6 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
}
|
||||
}
|
||||
|
||||
sendMessageReadReceipts(List<String> messageIDList) async {
|
||||
final res = await _messageService.sendMessageReadReceipts(messageIDList: messageIDList);
|
||||
return res;
|
||||
}
|
||||
|
||||
onMessageRevoked(String msgID, [String? convID]) {
|
||||
final activeMessageList = _messageListMap[convID ?? currentSelectedConv];
|
||||
if (activeMessageList != null) {
|
||||
|
|
@ -581,8 +577,23 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
targetItem.status = MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED;
|
||||
targetItem.id = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
activeMessageList[findeIndex] = targetItem;
|
||||
|
||||
bool isUnreadMessage = _receivedUnreadMessageList.any((element) => element.msgID == msgID);
|
||||
if (!(targetItem.isSelf ?? true) && isUnreadMessage) {
|
||||
if (_unreadCountForTongue > 0) {
|
||||
if (_unreadCountForTongue > 0) {
|
||||
_unreadCountForTongue--;
|
||||
}
|
||||
if (_receivedNewMessageCount > 0) {
|
||||
_receivedNewMessageCount--;
|
||||
}
|
||||
|
||||
_receivedUnreadMessageList.removeWhere((element) => element.msgID == targetItem.msgID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_messageListMap[convID ?? currentSelectedConv] = activeMessageList;
|
||||
notifyListeners();
|
||||
}
|
||||
|
|
@ -884,15 +895,10 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
}) async {
|
||||
String receiver = convType == ConvType.c2c ? convID : '';
|
||||
String groupID = convType == ConvType.group ? convID : '';
|
||||
final oldGroupType = groupType != null ? GroupReceptAllowType.values[groupType.index] : null;
|
||||
final sendMsgRes = await _messageService.sendMessage(
|
||||
id: id,
|
||||
receiver: receiver,
|
||||
needReadReceipt: needReadReceipt ??
|
||||
chatConfig.isShowGroupReadingStatus &&
|
||||
convType == ConvType.group &&
|
||||
((chatConfig.groupReadReceiptPermissionList != null && chatConfig.groupReadReceiptPermissionList!.contains(groupType)) ||
|
||||
(chatConfig.groupReadReceiptPermisionList != null && chatConfig.groupReadReceiptPermisionList!.contains(oldGroupType))),
|
||||
needReadReceipt: needReadReceipt ?? chatConfig.isShowReadingStatus,
|
||||
groupID: groupID,
|
||||
priority: priority,
|
||||
localCustomData: localCustomData,
|
||||
|
|
@ -916,21 +922,25 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
return sendMsgRes;
|
||||
}
|
||||
|
||||
void setMessageList(String conversationID, List<V2TimMessage> messageList, {bool needResetNewMessageCount = true}) {
|
||||
void setMessageList(String conversationID, List<V2TimMessage> messageList, {bool needResetNewMessageCount = true, bool isDeleteMsg = false}) {
|
||||
_messageListMap[conversationID] = messageList;
|
||||
if (needResetNewMessageCount) {
|
||||
_receivedNewMessageCount = 0;
|
||||
}
|
||||
|
||||
if (isDeleteMsg) {
|
||||
HistoryMessagePosition position = getMessageListPosition(conversationID);
|
||||
if (position == HistoryMessagePosition.awayTwoScreen) {
|
||||
_historyMessagePositionMap[conversationID] = HistoryMessagePosition.notShowLatest;
|
||||
}
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
updateMessage(V2TimValueCallback<V2TimMessage> sendMsgRes, String convID, String id, ConvType convType, GroupReceiptAllowType? groupType, ValueChanged<String>? setInputField) {
|
||||
List<V2TimMessage> currentHistoryMsgList = _messageListMap[convID] ?? [];
|
||||
final V2TimMessage sendMsgResData = sendMsgRes.data as V2TimMessage;
|
||||
if ([80001, 80002].contains(sendMsgRes.code) && sendMsgRes.data?.textElem?.text != null && setInputField != null) {
|
||||
setInputField(sendMsgRes.data!.textElem!.text ?? "");
|
||||
}
|
||||
|
||||
final findIdIndex = currentHistoryMsgList.indexWhere((element) => element.id == id);
|
||||
final targetIndex = findIdIndex == -1 ? currentHistoryMsgList.indexWhere((element) => element.msgID == sendMsgResData.msgID) : findIdIndex;
|
||||
if (targetIndex != -1) {
|
||||
|
|
@ -941,12 +951,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
|
|||
if (loadingMessage[convID] != null && loadingMessage[convID]!.isNotEmpty) {
|
||||
loadingMessage[convID]!.removeWhere((element) => element.id == id);
|
||||
}
|
||||
final oldGroupType = groupType != null ? GroupReceptAllowType.values[groupType.index] : null;
|
||||
if (chatConfig.isShowGroupReadingStatus &&
|
||||
convType == ConvType.group &&
|
||||
sendMsgRes.data?.msgID != null &&
|
||||
((chatConfig.groupReadReceiptPermissionList != null && chatConfig.groupReadReceiptPermissionList!.contains(groupType)) ||
|
||||
(chatConfig.groupReadReceiptPermisionList != null && chatConfig.groupReadReceiptPermisionList!.contains(oldGroupType)))) {
|
||||
if (chatConfig.isShowReadingStatus && sendMsgRes.data?.msgID != null) {
|
||||
_messageReadReceiptMap[sendMsgRes.data!.msgID!] = V2TimMessageReceipt(timestamp: 0, userID: "", readCount: 0);
|
||||
}
|
||||
_messageListMap[convID] = currentHistoryMsgList;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ List<T> removeDuplicates<T>(List<T> list, bool Function(T first, T second) isEqu
|
|||
}
|
||||
|
||||
class TUIConversationViewModel extends ChangeNotifier {
|
||||
static const String conversationC2CPrefix = "c2c_";
|
||||
static const String conversationGroupPrefix = "group_";
|
||||
|
||||
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
final ConversationService _conversationService = serviceLocator<ConversationService>();
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
|
|
@ -65,6 +68,10 @@ class TUIConversationViewModel extends ChangeNotifier {
|
|||
return _conversationList;
|
||||
}
|
||||
|
||||
V2TimConversation? getConversation(String conversationID) {
|
||||
return _conversationList.firstWhere((element) => element?.conversationID == conversationID);
|
||||
}
|
||||
|
||||
String? get scrollToConversation => _scrollToConversation;
|
||||
|
||||
set scrollToConversation(String? value) {
|
||||
|
|
@ -124,6 +131,18 @@ class TUIConversationViewModel extends ChangeNotifier {
|
|||
}
|
||||
}, onConversationDeleted:(List<String> conversationIDList) {
|
||||
_onConversationDeleted(conversationIDList);
|
||||
for (var conversationID in conversationIDList) {
|
||||
String resultID = "";
|
||||
if (conversationID.startsWith(conversationC2CPrefix)) {
|
||||
resultID = conversationID.replaceFirst(conversationC2CPrefix, "");
|
||||
} else if (conversationID.startsWith(conversationGroupPrefix)) {
|
||||
resultID = conversationID.replaceFirst(conversationGroupPrefix, "");
|
||||
}
|
||||
|
||||
if (resultID != "") {
|
||||
_chatGlobalModel.removeMessageList(resultID);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,6 +171,17 @@ class TUIFriendShipViewModel extends ChangeNotifier {
|
|||
return;
|
||||
}
|
||||
|
||||
Future<bool> isFriend(String userID) async {
|
||||
final List<V2TimFriendInfo> res = await _friendshipServices.getFriendList() ?? [];
|
||||
for (V2TimFriendInfo info in res) {
|
||||
if (info.userID == userID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> loadBlockListData() async {
|
||||
final blockListRes = await _friendshipServices.getBlackList();
|
||||
_blockList = blockListRes ?? [];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/error_message_converter.dart';
|
||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||
|
||||
class FriendshipServicesImpl implements FriendshipServices {
|
||||
|
|
@ -77,17 +78,34 @@ class FriendshipServicesImpl implements FriendshipServices {
|
|||
friendGroup: friendGroup,
|
||||
addSource: addSource,
|
||||
);
|
||||
if (result.code != 0 ||
|
||||
(result.code == 0 &&
|
||||
result.data?.resultCode != 0 &&
|
||||
result.data?.resultCode != 30539 &&
|
||||
result.data?.resultCode != 30515)) {
|
||||
if (result.code != 0) {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code,
|
||||
infoRecommendText: TIM_t("好友添加失败"),
|
||||
));
|
||||
} else if (result.code == 0 && result.data?.resultCode != 0) {
|
||||
String recommendText = "";
|
||||
if (result.data != null && result.data!.resultCode != null) {
|
||||
recommendText = ErrorMessageConverter.getErrorMessage(result.data!.resultCode!);
|
||||
}
|
||||
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.code == 0 ? result.data?.resultInfo : result.desc,
|
||||
errorCode: result.code == 0 ? result.data?.resultCode : result.code,
|
||||
infoRecommendText: recommendText,
|
||||
));
|
||||
} else {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code,
|
||||
infoRecommendText: TIM_t("好友添加成功"),
|
||||
));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -118,12 +136,18 @@ class FriendshipServicesImpl implements FriendshipServices {
|
|||
.getFriendshipManager()
|
||||
.deleteFromFriendList(userIDList: userIDList, deleteType: deleteType);
|
||||
if (res.code == 0) {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: res.desc,
|
||||
errorCode: res.code,
|
||||
infoRecommendText: TIM_t("好友删除成功")));
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: res.desc,
|
||||
errorCode: res.code));
|
||||
errorCode: res.code,
|
||||
infoRecommendText: TIM_t("好友删除失败")));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.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/ui/utils/error_message_converter.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
|
||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||
|
||||
|
|
@ -249,10 +250,12 @@ class GroupServicesImpl extends GroupServices {
|
|||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.joinGroup(groupID: groupID, message: message);
|
||||
if (result.code != 0) {
|
||||
String recommendText = ErrorMessageConverter.getErrorMessage(result.code);
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code));
|
||||
errorCode: result.code,
|
||||
infoRecommendText: recommendText));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/error_message_converter.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||
|
||||
|
|
@ -271,10 +273,12 @@ class MessageServiceImpl extends MessageService {
|
|||
.getMessageManager()
|
||||
.reSendMessage(msgID: msgID, onlineUserOnly: onlineUserOnly ?? false);
|
||||
if (res.code != 0) {
|
||||
String recommendText = ErrorMessageConverter.getErrorMessage(res.code);
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: res.desc,
|
||||
errorCode: res.code));
|
||||
errorCode: res.code,
|
||||
infoRecommendText: recommendText));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
@ -359,10 +363,12 @@ class MessageServiceImpl extends MessageService {
|
|||
cloudCustomData: cloudCustomData,
|
||||
);
|
||||
if (result.code != 0) {
|
||||
String recommendText = ErrorMessageConverter.getErrorMessage(result.code);
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code));
|
||||
errorCode: result.code,
|
||||
infoRecommendText: recommendText));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -471,7 +477,7 @@ class MessageServiceImpl extends MessageService {
|
|||
return TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.cleanConversationUnreadMessageCount(
|
||||
conversationID: "c2c_$userID",
|
||||
conversationID: "${TUIConversationViewModel.conversationC2CPrefix}$userID",
|
||||
cleanTimestamp: 0,
|
||||
cleanSequence: 0,
|
||||
);
|
||||
|
|
@ -486,7 +492,7 @@ class MessageServiceImpl extends MessageService {
|
|||
return TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.cleanConversationUnreadMessageCount(
|
||||
conversationID: "group_$groupID",
|
||||
conversationID: "${TUIConversationViewModel.conversationGroupPrefix}$groupID",
|
||||
cleanTimestamp: 0,
|
||||
cleanSequence: 0,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -81,14 +81,28 @@ class TIMUIKitProfileController {
|
|||
}
|
||||
|
||||
Future<V2TimCallback> updateSelfSignature(String selfSignature) {
|
||||
return model.updateSelfSignature(selfSignature);
|
||||
Map<String, dynamic> infoMap = {"selfSignature": selfSignature};
|
||||
return model.updateSelfInfo(infoMap);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateNickName(String nickName) {
|
||||
return model.updateNickName(nickName);
|
||||
Map<String, dynamic> infoMap = {"nickName": nickName};
|
||||
return model.updateSelfInfo(infoMap);
|
||||
}
|
||||
|
||||
/// 1:男 2:女
|
||||
Future<V2TimCallback> updateGender(int gender) {
|
||||
return model.updateGender(gender);
|
||||
Map<String, dynamic> infoMap = {"gender": gender};
|
||||
return model.updateSelfInfo(infoMap);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateBirthday(int birthday) {
|
||||
Map<String, dynamic> infoMap = {"birthday": birthday};
|
||||
return model.updateSelfInfo(infoMap);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateAvatar(String url) {
|
||||
Map<String, dynamic> infoMap = {"faceUrl" : url};
|
||||
return model.updateSelfInfo(infoMap);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class ErrorMessageConverter {
|
||||
static Map<int, String> errorCodeMap = {
|
||||
10007: TIM_t("操作权限不足"),
|
||||
20007: TIM_t("发送单聊消息,被对方拉黑,禁止发送。"),
|
||||
30010: TIM_t("您的好友数已达系统上限"),
|
||||
30014: TIM_t("对方的好友数已达系统上限"),
|
||||
30015: TIM_t("对方已是您的好友"),
|
||||
30515: TIM_t("被加好友在自己的黑名单中"),
|
||||
30516: TIM_t("对方已禁止加好友"),
|
||||
30525: TIM_t("您已被被对方设置为黑名单"),
|
||||
30539: TIM_t("等待好友审核同意"),
|
||||
};
|
||||
|
||||
static String getErrorMessage(int code) {
|
||||
return errorCodeMap[code] ?? "";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
// import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter_plugin_record_plus/const/play_state.dart';
|
||||
import 'package:flutter_plugin_record_plus/const/response.dart';
|
||||
import 'package:flutter_plugin_record_plus/index.dart';
|
||||
|
|
|
|||
|
|
@ -27,6 +27,16 @@ class TimeAgo {
|
|||
return (month.length == 1 ? '0' : '') + month + '/' + (date.length == 1 ? '0' : '') + date;
|
||||
}
|
||||
|
||||
static String getMonth(DateTime dateTime) {
|
||||
String month = dateTime.month.toString();
|
||||
return (month.length == 1 ? '0' : '') + month;
|
||||
}
|
||||
|
||||
static String getDay(DateTime dateTime) {
|
||||
String day = dateTime.day.toString();
|
||||
return (day.length == 1 ? '0' : '') + day;
|
||||
}
|
||||
|
||||
String? getTimeStringForChat(int timeStamp) {
|
||||
final DateTime date = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
|
||||
final DateTime epochLimit = DateTime.utc(1971);
|
||||
|
|
|
|||
|
|
@ -71,10 +71,6 @@ class _TIMUIKitAddFriendState extends TIMUIKitState<TIMUIKitAddFriend> {
|
|||
if (checkFriend != null) {
|
||||
final res = checkFriend.first;
|
||||
if (res.resultCode == 0 && res.resultType != 0) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("该用户已是好友"),
|
||||
infoCode: 6660102));
|
||||
widget.onTapAlreadyFriendsItem(userID);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,29 +203,12 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
|||
return;
|
||||
}
|
||||
|
||||
final res = await _friendshipServices.addFriend(
|
||||
_friendshipServices.addFriend(
|
||||
userID: userID,
|
||||
addType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH,
|
||||
remark: remark,
|
||||
addWording: addWording,
|
||||
friendGroup: friendGroup);
|
||||
|
||||
if (res.code == 0 && res.data?.resultCode == 0) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("好友添加成功"),
|
||||
infoCode: 6661202));
|
||||
} else if (res.code == 0 && res.data?.resultCode == 30539) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("好友申请已发出"),
|
||||
infoCode: 6661203));
|
||||
} else if (res.code == 0 && res.data?.resultCode == 30515) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("当前用户在黑名单"),
|
||||
infoCode: 6661204));
|
||||
}
|
||||
},
|
||||
child: Text(TIM_t("发送"))),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -84,11 +84,11 @@ class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState<TIMU
|
|||
}
|
||||
}
|
||||
|
||||
if ((widget.conversation.unreadCount ?? 0) > 20 && !isClickShowPrevious) {
|
||||
return MessageListTongueType.showPrevious;
|
||||
}
|
||||
// if ((widget.conversation.unreadCount ?? 0) > 20 && !isClickShowPrevious) {
|
||||
// return MessageListTongueType.showPrevious;
|
||||
// }
|
||||
|
||||
if (globalModel.unreadCountForConversation > 0) {
|
||||
if (globalModel.unreadCountForTongue > 0) {
|
||||
return MessageListTongueType.showUnread;
|
||||
}
|
||||
|
||||
|
|
@ -116,12 +116,12 @@ class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState<TIMU
|
|||
child: TIMUIKitHistoryMessageListTongue(
|
||||
previousCount: widget.conversation.unreadCount ?? 0,
|
||||
tongueItemBuilder: widget.tongueItemBuilder,
|
||||
unreadCount: globalModel.unreadCountForConversation,
|
||||
unreadCount: value.item2,
|
||||
onClick: () async {
|
||||
if (groupAtInfoList != null && groupAtInfoList!.isNotEmpty) {
|
||||
if (groupAtInfoList?.length == 1) {
|
||||
widget.scrollToIndexBySeq(groupAtInfoList![0]!.seq);
|
||||
widget.model.markMessageAsRead();
|
||||
|
||||
setState(() {
|
||||
groupAtInfoList = [];
|
||||
isFinishJumpToAt = true;
|
||||
|
|
@ -146,7 +146,7 @@ class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState<TIMU
|
|||
// TODO: 这里后续加个弹窗提示客户,找消息失败了
|
||||
}
|
||||
// widget.model.loadListForSpecificMessage(seq: count);
|
||||
} else if (value.item1 == HistoryMessagePosition.awayTwoScreen || globalModel.unreadCountForConversation > 0) {
|
||||
} else if (value.item1 == HistoryMessagePosition.awayTwoScreen || value.item2 > 0) {
|
||||
widget.model.showLatestUnread();
|
||||
widget.scrollController.animateTo(
|
||||
widget.scrollController.position.minScrollExtent,
|
||||
|
|
@ -162,9 +162,9 @@ class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState<TIMU
|
|||
);
|
||||
},
|
||||
selector: (c, model) {
|
||||
final mesageListPosition = model.getMessageListPosition(widget.model.conversationID);
|
||||
final unreadCountForConversation = model.unreadCountForConversation;
|
||||
return Tuple2(mesageListPosition, unreadCountForConversation);
|
||||
final messageListPosition = model.getMessageListPosition(widget.model.conversationID);
|
||||
final unreadCountForConversation = model.unreadCountForTongue;
|
||||
return Tuple2(messageListPosition, unreadCountForConversation);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ class TIMUIKitTongueItem extends TIMUIKitStatelessWidget {
|
|||
Map<MessageListTongueType, String> textType(BuildContext context) {
|
||||
final option1 = unreadCount.toString();
|
||||
final option2 = atNum.toString();
|
||||
final option3 = previousCount.toString();
|
||||
// final option3 = previousCount.toString();
|
||||
final String atMeString = option2 != ""
|
||||
? TIM_t_para("有{{option2}}条@我消息", "有$option2条@我消息")(option2: option2)
|
||||
: TIM_t("有人@我");
|
||||
|
||||
return {
|
||||
MessageListTongueType.showPrevious:
|
||||
TIM_t_para("{{option3}}条未读消息", "$option3条未读消息")(option3: option3),
|
||||
// MessageListTongueType.showPrevious:
|
||||
// TIM_t_para("{{option3}}条未读消息", "$option3条未读消息")(option3: option3),
|
||||
MessageListTongueType.toLatest: TIM_t("回到最新位置"),
|
||||
MessageListTongueType.showUnread:
|
||||
TIM_t_para("{{option1}}条新消息", "$option1条新消息")(option1: option1),
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class TIMUIKitHistoryMessageList extends StatefulWidget {
|
|||
final V2TimMessage? initFindingMsg;
|
||||
|
||||
/// use for load more message
|
||||
final Future<void> Function(String?, LoadDirection direction, [int?]) onLoadMore;
|
||||
final Future<bool> Function(String?, LoadDirection direction, [int?, int?]) onLoadMore;
|
||||
|
||||
/// configuration for list view
|
||||
final TIMUIKitHistoryMessageListConfig? mainHistoryListConfig;
|
||||
|
|
@ -111,6 +111,7 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMess
|
|||
late TIMUIKitHistoryMessageListController _controller;
|
||||
late AutoScrollController _autoScrollController;
|
||||
LoadingPlace loadingPlace = LoadingPlace.none;
|
||||
bool maybeHaveMoreMessageForFind = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -165,7 +166,7 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMess
|
|||
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("无法定位到原消息"), infoCode: 6660401));
|
||||
}
|
||||
|
||||
_onScrollToIndex(V2TimMessage targetMsg) {
|
||||
_onScrollToIndex(V2TimMessage targetMsg) async {
|
||||
// This method called by @ messages or messages been searched, aims to jump to target message
|
||||
loadingPlace = LoadingPlace.top;
|
||||
const int singleLoadAmount = kIsWeb ? 15 : 40;
|
||||
|
|
@ -204,21 +205,21 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMess
|
|||
showCantFindMsg();
|
||||
}
|
||||
} else {
|
||||
if (widget.model.haveMoreData) {
|
||||
if (maybeHaveMoreMessageForFind) {
|
||||
// if the target message not in current message list, load more
|
||||
findingMsg = targetMsg;
|
||||
final lastMsgId = _getMessageId(widget.messageList.length - 1);
|
||||
widget.onLoadMore(lastMsgId, LoadDirection.previous, singleLoadAmount);
|
||||
maybeHaveMoreMessageForFind = await widget.onLoadMore(lastMsgId, LoadDirection.previous, singleLoadAmount);
|
||||
} else {
|
||||
showCantFindMsg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onScrollToIndexBySeq(String targetSeq) {
|
||||
_onScrollToIndexBySeq(String targetSeq) async {
|
||||
// This method called by tongue request jumping to target @ message
|
||||
loadingPlace = LoadingPlace.top;
|
||||
const int singleLoadAmount = 40;
|
||||
// const int singleLoadAmount = 40;
|
||||
final msgList = widget.messageList;
|
||||
String lastSeq = "";
|
||||
for (int i = msgList.length - 1; i >= 0; i--) {
|
||||
|
|
@ -258,9 +259,10 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMess
|
|||
showCantFindMsg();
|
||||
}
|
||||
} else {
|
||||
if (widget.model.haveMoreData) {
|
||||
if (maybeHaveMoreMessageForFind) {
|
||||
findingSeq = targetSeq;
|
||||
widget.onLoadMore(_getMessageId(widget.messageList.length - 1), LoadDirection.previous, singleLoadAmount);
|
||||
int requestCount = int.parse(lastSeq) - int.parse(targetSeq);
|
||||
maybeHaveMoreMessageForFind = await widget.onLoadMore(_getMessageId(widget.messageList.length - 1), LoadDirection.previous, requestCount, int.parse(lastSeq));
|
||||
} else {
|
||||
showCantFindMsg();
|
||||
}
|
||||
|
|
@ -313,9 +315,9 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMess
|
|||
|
||||
final messageList = widget.messageList;
|
||||
final globalModel = context.read<TUIChatGlobalModel>();
|
||||
final receivedNewMessageList = globalModel.receivedMessageListCount;
|
||||
final shouldShowUnreadMessage = receivedNewMessageList > 0;
|
||||
final unreadMessageList = _getReceivedMessageList(receivedNewMessageList);
|
||||
final receivedNewMessageCount = globalModel.receivedNewMessageCount;
|
||||
final shouldShowUnreadMessage = receivedNewMessageCount > 0;
|
||||
final unreadMessageList = _getReceivedMessageList(receivedNewMessageCount);
|
||||
final readMessageList = messageList.sublist(unreadMessageList.length, messageList.length).toList();
|
||||
|
||||
final throttleFunction = OptimizeUtils.multiThrottle((index, LoadDirection direction) async {
|
||||
|
|
@ -429,7 +431,7 @@ class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMess
|
|||
);
|
||||
}
|
||||
}
|
||||
if (index == 0 && widget.model.haveMoreLatestData == true && globalModel.receivedMessageListCount < 10) {
|
||||
if (index == 0 && widget.model.haveMoreLatestData == true && globalModel.receivedNewMessageCount < 10) {
|
||||
throttleFunction(index, LoadDirection.latest);
|
||||
}
|
||||
outputLogger.i("Rendering a read message: ${getMessageIdentifier(messageItem, 0)}, message Type: ${messageItem?.elemType}");
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/forward_message_screen.dart'
|
|||
import 'package:tencent_cloud_chat_uikit/ui/widgets/radio_button.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
|
||||
import 'package:tencent_super_tooltip/tencent_super_tooltip.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
import '../TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart';
|
||||
|
||||
|
|
@ -143,25 +144,25 @@ class MessageToolTipItem {
|
|||
|
||||
class ToolTipsConfig {
|
||||
/// Whether to show the reply to a message option.
|
||||
final bool showReplyMessage;
|
||||
bool showReplyMessage;
|
||||
|
||||
/// Whether to show the multiple-choice option for messages.
|
||||
final bool showMultipleChoiceMessage;
|
||||
bool showMultipleChoiceMessage;
|
||||
|
||||
/// Whether to show the option to delete a message.
|
||||
final bool showDeleteMessage;
|
||||
bool showDeleteMessage;
|
||||
|
||||
/// Whether to show the option to recall a message.
|
||||
final bool showRecallMessage;
|
||||
bool showRecallMessage;
|
||||
|
||||
/// Whether to show the option to copy a message.
|
||||
final bool showCopyMessage;
|
||||
bool showCopyMessage;
|
||||
|
||||
/// Whether to show the option to forward a message.
|
||||
final bool showForwardMessage;
|
||||
bool showForwardMessage;
|
||||
|
||||
/// Whether to show the option to translate a text message. This module is not available by default. Please contact your Tencent Cloud sales representative or customer service team to enable this feature.
|
||||
final bool showTranslation;
|
||||
bool showTranslation;
|
||||
|
||||
/// A builder for additional custom items. We recommend using `additionalMessageToolTips` instead of this field since version 2.0, as you only need to provide the data rather than the whole widget. This makes usage easier and you don't need to worry about the UI display.
|
||||
final Widget? Function(V2TimMessage message, Function() closeTooltip, [Key? key, BuildContext? context])? additionalItemBuilder;
|
||||
|
|
@ -208,10 +209,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
|||
final bool showAvatar;
|
||||
|
||||
/// message is read status
|
||||
final bool showMessageReadRecipt;
|
||||
|
||||
/// message read status in group
|
||||
final bool showGroupMessageReadRecipt;
|
||||
final bool showMessageReadReceipt;
|
||||
|
||||
/// allow message can long press
|
||||
final bool allowLongPress;
|
||||
|
|
@ -280,11 +278,10 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
|
|||
this.messageItemBuilder,
|
||||
this.onLongPressForOthersHeadPortrait,
|
||||
this.showAvatar = true,
|
||||
this.showMessageReadRecipt = true,
|
||||
this.showMessageReadReceipt = true,
|
||||
this.allowLongPress = true,
|
||||
this.toolTipsConfig,
|
||||
this.onLongPress,
|
||||
this.showGroupMessageReadRecipt = false,
|
||||
this.allowAtUserWhenReply = true,
|
||||
this.allowAvatarTap = true,
|
||||
this.userAvatarBuilder,
|
||||
|
|
@ -809,6 +806,18 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
}
|
||||
}
|
||||
|
||||
if (widget.message.status != MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) {
|
||||
widget.toolTipsConfig?.showReplyMessage = true;
|
||||
} else {
|
||||
widget.toolTipsConfig?.showReplyMessage = false;
|
||||
}
|
||||
|
||||
if (widget.message.status != MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL && !(widget.message.hasRiskContent ?? false)) {
|
||||
widget.toolTipsConfig?.showForwardMessage = true;
|
||||
} else {
|
||||
widget.toolTipsConfig?.showForwardMessage = false;
|
||||
}
|
||||
|
||||
tooltip = SuperTooltip(
|
||||
popupDirection: popupDirection,
|
||||
minimumOutSidePadding: 0,
|
||||
|
|
@ -887,27 +896,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
}
|
||||
}
|
||||
|
||||
bool isVoteMessage(V2TimMessage message) {
|
||||
bool isvote = false;
|
||||
V2TimCustomElem? custom = message.customElem;
|
||||
|
||||
if (custom != null) {
|
||||
String? data = custom.data;
|
||||
if (data != null && data.isNotEmpty) {
|
||||
try {
|
||||
Map<String, dynamic> mapData = json.decode(data);
|
||||
if (mapData["businessID"] == "group_poll") {
|
||||
isvote = true;
|
||||
}
|
||||
} catch (err) {
|
||||
// err
|
||||
}
|
||||
}
|
||||
}
|
||||
return isvote;
|
||||
}
|
||||
|
||||
List<MessageHoverControlItem> getMessageHoverControlBar(TUIChatSeparateViewModel model, TUITheme theme) {
|
||||
List<MessageHoverControlItem> getWideMessageHoverControlBar(TUIChatSeparateViewModel model, TUITheme theme) {
|
||||
return [
|
||||
if (widget.isUseMessageReaction ?? false)
|
||||
MessageHoverControlItem(
|
||||
|
|
@ -939,7 +928,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
widget.onLongPressForOthersHeadPortrait!(!isAtWhenReply ? null : widget.message.sender, !isAtWhenReply ? null : widget.message.nickName);
|
||||
},
|
||||
),
|
||||
if ((widget.toolTipsConfig?.showForwardMessage ?? true) && !isVoteMessage(widget.message))
|
||||
if ((widget.toolTipsConfig?.showForwardMessage ?? true) && !model.isVoteMessage(widget.message))
|
||||
MessageHoverControlItem(
|
||||
name: TIM_t("转发"),
|
||||
icon: Icon(
|
||||
|
|
@ -948,7 +937,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
color: hexToColor("8f959e"),
|
||||
),
|
||||
onClick: (_) {
|
||||
model.addToMultiSelectedMessageList(widget.message);
|
||||
model.setMessageItemChecked(widget.message, true);
|
||||
TUIKitWidePopup.showPopupWindow(
|
||||
operationKey: TUIKitWideModalOperationKey.forward,
|
||||
context: context,
|
||||
|
|
@ -995,10 +984,16 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
Widget renderHoverTipAndReadStatus(TUIChatSeparateViewModel model, bool isSelf, V2TimMessage message, bool isPeerRead, TUITheme theme, bool isDownloadWaiting) {
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
final customHoverBar = widget.customMessageHoverBarOnDesktop != null ? widget.customMessageHoverBarOnDesktop!(message) : null;
|
||||
|
||||
final wideHoverTipList = (model.chatConfig.isUseMessageHoverBarOnDesktop && customHoverBar == null) ? getMessageHoverControlBar(model, theme) : [];
|
||||
|
||||
final wideHoverTipList = (model.chatConfig.isUseMessageHoverBarOnDesktop && customHoverBar == null) ? getWideMessageHoverControlBar(model, theme) : [];
|
||||
final lastItemName = wideHoverTipList.isNotEmpty ? wideHoverTipList.last.name : "";
|
||||
|
||||
Future<void> _conditionalDelay() async {
|
||||
if (!(model.hasDelayedRenderSendingStatus(message.id ?? message.msgID!) ?? true)) {
|
||||
model.setDelayedRenderSendingStatus(message.id ?? message.msgID!);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
|
@ -1041,7 +1036,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
if (isDesktopScreen && isShowWideToolTip && customHoverBar != null) customHoverBar,
|
||||
if (!isDesktopScreen || (model.chatConfig.isUseMessageHoverBarOnDesktop && customHoverBar == null && !isShowWideToolTip))
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
height: 20,
|
||||
),
|
||||
if (isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL)
|
||||
Container(
|
||||
|
|
@ -1056,18 +1051,17 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
},
|
||||
child: Icon(Icons.error, color: theme.cautionColor, size: 18),
|
||||
)),
|
||||
if (isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING && model.hasSendingMessageID(message.id ?? message.msgID!))
|
||||
if (isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SENDING)
|
||||
FutureBuilder(
|
||||
future: Future.delayed(const Duration(seconds: 1)),
|
||||
future: _conditionalDelay(),
|
||||
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done && model.hasSendingMessageID(message.id ?? message.msgID!)) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 3),
|
||||
margin: const EdgeInsets.only(right: 6),
|
||||
child: RotationTransition(
|
||||
turns: Tween(begin: 0.0, end: 1.0).animate(_animationController),
|
||||
child: Icon(Icons.rotate_right, color: theme.cautionColor, size: 18),
|
||||
),
|
||||
width: 12.0,
|
||||
height: 15.0,
|
||||
child: CircularProgressIndicator(strokeWidth: 1.0),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
|
|
@ -1075,21 +1069,9 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
},
|
||||
),
|
||||
if (model.chatConfig.isShowReadingStatus &&
|
||||
widget.showMessageReadRecipt &&
|
||||
model.conversationType == ConvType.c2c &&
|
||||
isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC)
|
||||
Container(
|
||||
padding: const EdgeInsets.only(bottom: 3),
|
||||
margin: const EdgeInsets.only(right: 6),
|
||||
child: Text(
|
||||
isPeerRead ? TIM_t("已读") : TIM_t("未读"),
|
||||
style: TextStyle(color: theme.chatMessageItemUnreadStatusTextColor, fontSize: 12),
|
||||
),
|
||||
),
|
||||
if (model.chatConfig.isShowGroupReadingStatus &&
|
||||
model.chatConfig.isShowGroupMessageReadReceipt &&
|
||||
model.conversationType == ConvType.group &&
|
||||
isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC)
|
||||
isSelf && message.status == MessageStatus.V2TIM_MSG_STATUS_SEND_SUCC &&
|
||||
(message.needReadReceipt ?? false) &&
|
||||
!model.isVoteMessage(widget.message))
|
||||
TIMUIKitMessageReadReceipt(
|
||||
messageItem: widget.message,
|
||||
onTapAvatar: widget.onTapForOthersPortrait,
|
||||
|
|
@ -1164,7 +1146,16 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
}
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
return VisibilityDetector(
|
||||
key: Key(message.id ?? message.msgID!),
|
||||
// 判断消息是否可见
|
||||
onVisibilityChanged: (visibilityInfo) {
|
||||
var visiblePercentage = visibilityInfo.visibleFraction * 100;
|
||||
if (visiblePercentage > 50) {
|
||||
model.addToMessageReadReceiptList(message);
|
||||
}
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) => Container(
|
||||
padding: EdgeInsets.only(left: isSelf ? 0 : 16, right: isSelf ? 16 : 0),
|
||||
margin: widget.padding ?? const EdgeInsets.only(bottom: 20),
|
||||
|
|
@ -1177,13 +1168,9 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
Container(
|
||||
margin: EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0),
|
||||
child: CheckBoxButton(
|
||||
isChecked: model.multiSelectedMessageList.contains(message),
|
||||
isChecked: model.getSelectedMessageList().contains(message),
|
||||
onChanged: (value) {
|
||||
if (value) {
|
||||
model.addToMultiSelectedMessageList(message);
|
||||
} else {
|
||||
model.removeFromMultiSelectedMessageList(message);
|
||||
}
|
||||
model.setMessageItemChecked(message, value);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -1210,12 +1197,8 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
behavior: model.isMultiSelect ? HitTestBehavior.translucent : null,
|
||||
onTap: () {
|
||||
if (model.isMultiSelect) {
|
||||
final checked = model.multiSelectedMessageList.contains(message);
|
||||
if (checked) {
|
||||
model.removeFromMultiSelectedMessageList(message);
|
||||
} else {
|
||||
model.addToMultiSelectedMessageList(message);
|
||||
}
|
||||
final checked = model.getSelectedMessageList().contains(message);
|
||||
model.setMessageItemChecked(message, !checked);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1291,7 +1274,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
widget.topRowBuilder != null
|
||||
? widget.topRowBuilder!(context, message)
|
||||
: Container(
|
||||
margin: const EdgeInsets.only(bottom: 4),
|
||||
// margin: const EdgeInsets.only(bottom: 4),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width / 1.7),
|
||||
child: Text(
|
||||
|
|
@ -1309,10 +1292,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
maxWidth: constraints.maxWidth * 0.77,
|
||||
),
|
||||
child: Builder(builder: (context) {
|
||||
return Column(
|
||||
crossAxisAlignment: (message.isSelf ?? true) ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
return GestureDetector(
|
||||
child: IgnorePointer(ignoring: model.isMultiSelect, child: _getMessageItemBuilder(message, message.status, model)),
|
||||
onSecondaryTapDown: (details) {
|
||||
if (widget.onLongPress != null) {
|
||||
|
|
@ -1337,16 +1317,6 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
onTapDown: (details) {
|
||||
_tapDetails = details;
|
||||
},
|
||||
),
|
||||
TIMUIKitTextTranslationElem(
|
||||
message: message,
|
||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
isFromSelf: message.isSelf ?? true,
|
||||
isShowJump: false,
|
||||
clearJump: () {},
|
||||
chatModel: model)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
|
@ -1355,6 +1325,14 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
if (!isSelf) renderHoverTipAndReadStatus(model, isSelf, message, isPeerRead, theme, isDownloadWaiting),
|
||||
],
|
||||
),
|
||||
TIMUIKitTextTranslationElem(
|
||||
message: message,
|
||||
isUseDefaultEmoji: widget.isUseDefaultEmoji,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
isFromSelf: message.isSelf ?? true,
|
||||
isShowJump: false,
|
||||
clearJump: () {},
|
||||
chatModel: model),
|
||||
if (widget.bottomRowBuilder != null) widget.bottomRowBuilder!(context, message)
|
||||
],
|
||||
),
|
||||
|
|
@ -1390,6 +1368,7 @@ class _TIMUIKItHistoryMessageListItemState extends TIMUIKitState<TIMUIKitHistory
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:open_file/open_file.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart';
|
||||
|
|
@ -162,26 +163,6 @@ class TIMUIKitMessageTooltipState
|
|||
);
|
||||
}
|
||||
|
||||
bool isVoteMessage(V2TimMessage message) {
|
||||
bool isvote = false;
|
||||
V2TimCustomElem? custom = message.customElem;
|
||||
|
||||
if (custom != null) {
|
||||
String? data = custom.data;
|
||||
if (data != null && data.isNotEmpty) {
|
||||
try {
|
||||
Map<String, dynamic> mapData = json.decode(data);
|
||||
if (mapData["businessID"] == "group_poll") {
|
||||
isvote = true;
|
||||
}
|
||||
} catch (err) {
|
||||
// err
|
||||
}
|
||||
}
|
||||
}
|
||||
return isvote;
|
||||
}
|
||||
|
||||
bool isAdminCanRecall() {
|
||||
if (widget.model.chatConfig.isGroupAdminRecallEnabled) {
|
||||
final selfMemberInfo =
|
||||
|
|
@ -212,6 +193,15 @@ class TIMUIKitMessageTooltipState
|
|||
(isDesktopScreen &&
|
||||
widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE &&
|
||||
fileBeenDownloaded);
|
||||
bool showTranslation = true;
|
||||
if (widget.message.localCustomData != null) {
|
||||
final LocalCustomDataModel localCustomData = LocalCustomDataModel.fromMap(
|
||||
json.decode(TencentUtils.checkString(widget.message.localCustomData) ?? "{}"));
|
||||
if (localCustomData.translatedText != null && localCustomData.translatedText != "") {
|
||||
showTranslation = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final dynamicQuote =
|
||||
model.chatConfig.isAtWhenReplyDynamic?.call(widget.message);
|
||||
|
|
@ -235,7 +225,7 @@ class TIMUIKitMessageTooltipState
|
|||
id: "copyMessage",
|
||||
iconImageAsset: "images/copy_message.png",
|
||||
onClick: () => _onTap("copyMessage", model)),
|
||||
if (shouldShowForwardAction && !isVoteMessage(widget.message))
|
||||
if (shouldShowForwardAction && !model.isVoteMessage(widget.message))
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("转发"),
|
||||
id: "forwardMessage",
|
||||
|
|
@ -253,16 +243,17 @@ class TIMUIKitMessageTooltipState
|
|||
id: "multiSelect",
|
||||
iconImageAsset: "images/multi_message.png",
|
||||
onClick: () => _onTap("multiSelect", model)),
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("翻译"),
|
||||
id: "translate",
|
||||
iconImageAsset: "images/translate.png",
|
||||
onClick: () => _onTap("translate", model)),
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("删除"),
|
||||
id: "delete",
|
||||
iconImageAsset: "images/delete_message.png",
|
||||
onClick: () => _onTap("delete", model)),
|
||||
if (showTranslation)
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("翻译"),
|
||||
id: "translate",
|
||||
iconImageAsset: "images/translate.png",
|
||||
onClick: () => _onTap("translate", model)),
|
||||
if (shouldShowRevokeAction)
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("撤回"),
|
||||
|
|
@ -480,10 +471,10 @@ class TIMUIKitMessageTooltipState
|
|||
break;
|
||||
case "multiSelect":
|
||||
model.updateMultiSelectStatus(true);
|
||||
model.addToMultiSelectedMessageList(widget.message);
|
||||
model.setMessageItemChecked(widget.message, true);
|
||||
break;
|
||||
case "forwardMessage":
|
||||
model.addToMultiSelectedMessageList(widget.message);
|
||||
model.setMessageItemChecked(widget.message, true);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
|
|
|
|||
|
|
@ -125,15 +125,18 @@ class _TIMUIKitHistoryMessageListContainerState
|
|||
|
||||
List<V2TimMessage?> historyMessageList = [];
|
||||
|
||||
Future<void> requestForData(String? lastMsgID, LoadDirection direction,
|
||||
Future<bool> requestForData(String? lastMsgID, LoadDirection direction,
|
||||
TUIChatSeparateViewModel model,
|
||||
[int? count]) async {
|
||||
if ((direction == LoadDirection.previous && model.haveMoreData) ||
|
||||
[int? count, int? lastSeq]) async {
|
||||
if ((direction == LoadDirection.previous) ||
|
||||
(direction == LoadDirection.latest && model.haveMoreLatestData)) {
|
||||
await model.loadChatRecord(
|
||||
return await model.loadChatRecord(
|
||||
direction: direction,
|
||||
count: count ?? (kIsWeb ? 15 : HistoryMessageDartConstant.getCount),
|
||||
lastMsgID: lastMsgID);
|
||||
lastMsgID: lastMsgID,
|
||||
lastMsgSeq: lastSeq ?? -1,);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,8 +206,8 @@ class _TIMUIKitHistoryMessageListContainerState
|
|||
tongueItemBuilder: widget.tongueItemBuilder,
|
||||
initFindingMsg: widget.initFindingMsg,
|
||||
messageList: messageList,
|
||||
onLoadMore: (String? a, LoadDirection direction, [int? b]) async {
|
||||
return await requestForData(a, direction, model, b);
|
||||
onLoadMore: (String? a, LoadDirection direction, [int? b, int? lastSeq]) async {
|
||||
return await requestForData(a, direction, model, b, lastSeq);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,15 +21,25 @@ class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget {
|
|||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
final TUIChatSeparateViewModel model =
|
||||
Provider.of<TUIChatSeparateViewModel>(context, listen: false);
|
||||
final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context, listen: false);
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
|
||||
return Selector<TUIChatGlobalModel, V2TimMessageReceipt?>(
|
||||
builder: (context, value, child) {
|
||||
// if (value == null || value.unreadCount == 0 && value.readCount == 0) {
|
||||
// return Container();
|
||||
// }
|
||||
if (model.conversationType == ConvType.c2c) {
|
||||
bool isPeerRead = false;
|
||||
if (value != null) {
|
||||
isPeerRead = value.isPeerRead ?? false;
|
||||
}
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 3),
|
||||
margin: const EdgeInsets.only(right: 6),
|
||||
child: Text(
|
||||
isPeerRead ? TIM_t("已读") : TIM_t("未读"),
|
||||
style: TextStyle(color: theme.chatMessageItemUnreadStatusTextColor, fontSize: 12),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
|
|
@ -48,7 +58,7 @@ class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget {
|
|||
unreadCount: value?.unreadCount ?? 0,
|
||||
readCount: value?.readCount ?? 0)
|
||||
);
|
||||
}else{
|
||||
} else {
|
||||
if (value?.unreadCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -95,6 +105,7 @@ class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget {
|
|||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
selector: (c, model) {
|
||||
return model.getMessageReadReceipt(messageItem.msgID ?? "");
|
||||
|
|
|
|||
|
|
@ -143,8 +143,10 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
|||
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
||||
widget.message.fileElem?.path ??
|
||||
'';
|
||||
|
||||
File f = File(savePath);
|
||||
if (f.existsSync() && widget.messageID != null) {
|
||||
if (widget.messageID != null) {
|
||||
if (f.existsSync()) {
|
||||
filePath = savePath;
|
||||
if (downloadProgress != 100) {
|
||||
setState(() {
|
||||
|
|
@ -161,7 +163,11 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
|||
advancedMsgListener = null;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
model.setMessageProgress(widget.messageID!, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -409,7 +415,7 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
|
|||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 72),
|
||||
child: Container(
|
||||
width: 237,
|
||||
width: 170,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: theme.weakDividerColor ??
|
||||
|
|
|
|||
|
|
@ -289,13 +289,12 @@ class _TIMUIKitReplyElemState extends TIMUIKitState<TIMUIKitReplyElem> {
|
|||
}
|
||||
|
||||
void _jumpToRawMsg() {
|
||||
if (rawMessage?.timestamp != null) {
|
||||
if (rawMessage?.status != MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED && rawMessage?.timestamp != null) {
|
||||
widget.scrollToIndex(rawMessage);
|
||||
} else {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("无法定位到原消息"),
|
||||
infoCode: 6660401));
|
||||
infoRecommendText: TIM_t("无法定位到原消息")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
// import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
|
|
@ -76,6 +75,10 @@ class _TIMUIKitSoundElemState extends TIMUIKitState<TIMUIKitSoundElem> {
|
|||
} else {
|
||||
SoundPlayer.play(url: stateElement.url!);
|
||||
widget.chatModel.currentPlayedMsgId = widget.msgID;
|
||||
|
||||
setState(() {
|
||||
isPlaying = widget.chatModel.currentPlayedMsgId != '' && widget.chatModel.currentPlayedMsgId == widget.msgID;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,14 +101,6 @@ class _TIMUIKitSoundElemState extends TIMUIKitState<TIMUIKitSoundElem> {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
setState(() {
|
||||
isPlaying = widget.chatModel.currentPlayedMsgId != '' && widget.chatModel.currentPlayedMsgId == widget.msgID;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -113,6 +108,9 @@ class _TIMUIKitSoundElemState extends TIMUIKitState<TIMUIKitSoundElem> {
|
|||
subscription = SoundPlayer.playStateListener(listener: (PlayerState state) {
|
||||
if (state.processingState == ProcessingState.completed) {
|
||||
widget.chatModel.currentPlayedMsgId = "";
|
||||
setState(() {
|
||||
isPlaying = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ class _TIMUIKitTextElemState extends TIMUIKitState<TIMUIKitTextElem> {
|
|||
true,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
checkHttpLink: true,
|
||||
)),
|
||||
// If the link preview info is available, render the preview card.
|
||||
if (_renderPreviewWidget() != null &&
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
|
|||
this.isUseTencentCloudChatPackage = false,
|
||||
this.customEmojiStickerList = const [],
|
||||
this.showAtBackground = false,
|
||||
this.checkHttpLink = true,
|
||||
});
|
||||
|
||||
/// whether show background for @somebody
|
||||
|
|
@ -22,6 +23,8 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
|
|||
|
||||
final bool isUseTencentCloudChatPackage;
|
||||
|
||||
final bool checkHttpLink;
|
||||
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
@override
|
||||
|
|
@ -40,7 +43,7 @@ class DefaultSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
|
|||
start: index! - (EmojiText.flag.length - 1),
|
||||
isUseQQPackage: isUseQQPackage,
|
||||
customEmojiStickerList: customEmojiStickerList);
|
||||
} else if (isStart(flag, HttpText.flag)) {
|
||||
} else if (isStart(flag, HttpText.flag) && checkHttpLink) {
|
||||
return HttpText(textStyle, onTap,
|
||||
start: index! - (HttpText.flag.length - 1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class HttpText extends SpecialText {
|
|||
HttpText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
|
||||
{this.start})
|
||||
: super(flag, flag, textStyle, onTap: onTap);
|
||||
static const String flag = '\$';
|
||||
static const String flag = '!@TURL#*&\$';
|
||||
final int? start;
|
||||
@override
|
||||
InlineSpan finishText() {
|
||||
|
|
@ -30,16 +30,3 @@ class HttpText extends SpecialText {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<String> dollarList = <String>[
|
||||
'\$Dota2\$',
|
||||
'\$Dota2 Ti9\$',
|
||||
'\$CN dota best dota\$',
|
||||
'\$Flutter\$',
|
||||
'\$CN dev best dev\$',
|
||||
'\$UWP\$',
|
||||
'\$Nevermore\$',
|
||||
'\$FlutterCandies\$',
|
||||
'\$ExtendedImage\$',
|
||||
'\$ExtendedText\$',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.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_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
|
||||
|
|
@ -17,9 +18,7 @@ class AtText extends StatefulWidget {
|
|||
final V2TimGroupInfo? groupInfo;
|
||||
final List<V2TimGroupMemberFullInfo?>? groupMemberList;
|
||||
final VoidCallback? closeFunc;
|
||||
final Function(
|
||||
V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails)?
|
||||
onChooseMember;
|
||||
final Function(List<V2TimGroupMemberFullInfo> memberInfo)? onChooseMember;
|
||||
final bool canAtAll;
|
||||
|
||||
// some Group type cant @all
|
||||
|
|
@ -42,10 +41,13 @@ class AtText extends StatefulWidget {
|
|||
|
||||
class _AtTextState extends TIMUIKitState<AtText> {
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
final TUISelfInfoViewModel _selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
|
||||
List<V2TimGroupMemberFullInfo?>? groupMemberList;
|
||||
List<V2TimGroupMemberFullInfo?>? searchMemberList;
|
||||
|
||||
List<V2TimGroupMemberFullInfo> selectedGroupMemberList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
groupMemberList = widget.groupMemberList;
|
||||
|
|
@ -58,16 +60,15 @@ class _AtTextState extends TIMUIKitState<AtText> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
_onTapMemberItem(
|
||||
V2TimGroupMemberFullInfo memberInfo, TapDownDetails? tapDetails) {
|
||||
void _submitAtMemberList() {
|
||||
if (widget.closeFunc != null) {
|
||||
widget.closeFunc!();
|
||||
}
|
||||
|
||||
if (widget.onChooseMember != null) {
|
||||
widget.onChooseMember!(memberInfo, tapDetails);
|
||||
widget.onChooseMember!(selectedGroupMemberList);
|
||||
} else {
|
||||
Navigator.pop(context, memberInfo);
|
||||
Navigator.pop(context, selectedGroupMemberList);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,13 +114,30 @@ class _AtTextState extends TIMUIKitState<AtText> {
|
|||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
|
||||
V2TimUserFullInfo? loginUserInfo = _selfInfoViewModel.loginInfo;
|
||||
if (loginUserInfo != null) {
|
||||
searchMemberList?.removeWhere((memberInfo) {
|
||||
return memberInfo?.userID == loginUserInfo.userID;
|
||||
});
|
||||
}
|
||||
|
||||
Widget mentionedMembersBody() {
|
||||
return GroupProfileMemberList(
|
||||
groupType: widget.groupType ?? "",
|
||||
memberList: searchMemberList ?? [],
|
||||
onTapMemberItem: _onTapMemberItem,
|
||||
canAtAll: widget.canAtAll,
|
||||
canSelectMember: true,
|
||||
canSlideDelete: false,
|
||||
onSelectedMemberChange: (selectedMemberList) {
|
||||
selectedGroupMemberList = selectedMemberList;
|
||||
bool isAtAllSelected = selectedGroupMemberList.where((element) {
|
||||
return element.userID == GroupProfileMemberList.AT_ALL_USER_ID;
|
||||
}).isNotEmpty;
|
||||
|
||||
if (isAtAllSelected) {
|
||||
_submitAtMemberList();
|
||||
}
|
||||
},
|
||||
touchBottomCallBack: () {
|
||||
// Get all by once, unnecessary to load more
|
||||
},
|
||||
|
|
@ -168,6 +186,20 @@ class _AtTextState extends TIMUIKitState<AtText> {
|
|||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_submitAtMemberList();
|
||||
},
|
||||
child: Text(
|
||||
TIM_t("确定"),
|
||||
style: TextStyle(
|
||||
color: theme.appbarTextColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: mentionedMembersBody()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ import 'package:universal_html/html.dart' as html;
|
|||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class MorePanelConfig {
|
||||
static final int FILE_MAX_SIZE = 100 * 1024 * 1024;
|
||||
static final int VIDEO_MAX_SIZE = 100 * 1024 * 1024;
|
||||
static final int IMAGE_MAX_SIZE = 28 * 1024 * 1024;
|
||||
|
||||
final bool showGalleryPickAction;
|
||||
final bool showCameraAction;
|
||||
final bool showFilePickAction;
|
||||
|
|
@ -316,20 +320,19 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
}).toList();
|
||||
}
|
||||
|
||||
_sendVideoMessage(AssetEntity asset, TUIChatSeparateViewModel model) async {
|
||||
final plugin = FcNativeVideoThumbnail();
|
||||
final originFile = await asset.originFile;
|
||||
final size = await originFile!.length();
|
||||
if (size >= 104857600) {
|
||||
_sendVideoMessage(AssetEntity asset, int size, TUIChatSeparateViewModel model) async {
|
||||
if (size >= MorePanelConfig.VIDEO_MAX_SIZE) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("发送失败,视频不能大于100MB"),
|
||||
infoCode: 6660405));
|
||||
infoRecommendText: TIM_t("文件大小超出了限制")));
|
||||
return;
|
||||
}
|
||||
|
||||
final plugin = FcNativeVideoThumbnail();
|
||||
final originFile = await asset.originFile;
|
||||
|
||||
final duration = asset.videoDuration.inSeconds;
|
||||
final filePath = originFile.path;
|
||||
final filePath = originFile!.path;
|
||||
final convID = widget.conversationID;
|
||||
final convType = widget.conversationType;
|
||||
|
||||
|
|
@ -341,9 +344,9 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
srcFile: originFile.path,
|
||||
destFile: tempPath,
|
||||
format: 'jpeg',
|
||||
width: 128,
|
||||
width: 1280,
|
||||
quality: 100,
|
||||
height: 128,
|
||||
height: 1280,
|
||||
);
|
||||
MessageUtils.handleMessageError(
|
||||
model.sendVideoMessage(
|
||||
|
|
@ -407,8 +410,16 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
final originFile = await asset.originFile;
|
||||
final filePath = originFile?.path;
|
||||
final type = asset.type;
|
||||
final size = await originFile!.length();
|
||||
if (filePath != null) {
|
||||
if (type == AssetType.image) {
|
||||
if (size >= MorePanelConfig.IMAGE_MAX_SIZE) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("文件大小超出了限制")));
|
||||
return;
|
||||
}
|
||||
|
||||
MessageUtils.handleMessageError(
|
||||
model.sendImageMessage(
|
||||
imagePath: filePath,
|
||||
|
|
@ -418,7 +429,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
}
|
||||
|
||||
if (type == AssetType.video) {
|
||||
_sendVideoMessage(asset, model);
|
||||
_sendVideoMessage(asset, size, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -480,7 +491,15 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
final originFile = await pickedFile?.originFile;
|
||||
if (originFile != null) {
|
||||
final type = pickedFile!.type;
|
||||
final size = await originFile!.length();
|
||||
if (type == AssetType.image) {
|
||||
if (size >= MorePanelConfig.IMAGE_MAX_SIZE) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("文件大小超出了限制")));
|
||||
return;
|
||||
}
|
||||
|
||||
MessageUtils.handleMessageError(
|
||||
model.sendImageMessage(
|
||||
imagePath: originFile.path,
|
||||
|
|
@ -489,7 +508,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
context);
|
||||
}
|
||||
if (type == AssetType.video) {
|
||||
_sendVideoMessage(pickedFile, model);
|
||||
_sendVideoMessage(pickedFile, size, model);
|
||||
}
|
||||
} else {
|
||||
// Toast.showToast(ToastType.fail, TIM_t("图片不能为空"), context);
|
||||
|
|
@ -591,6 +610,13 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
|
||||
File file = File(result.files.single.path!);
|
||||
final int size = file.lengthSync();
|
||||
if (size >= MorePanelConfig.FILE_MAX_SIZE) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("文件大小超出了限制")));
|
||||
return;
|
||||
}
|
||||
|
||||
final String savePath = file.path;
|
||||
|
||||
MessageUtils.handleMessageError(
|
||||
|
|
@ -643,14 +669,22 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
|
|||
|
||||
_goToVideoUI(String type) async {
|
||||
if (!PlatformUtils().isWeb) {
|
||||
final hasCameraPermission = type == TYPE_VIDEO
|
||||
? await Permissions.checkPermission(context, Permission.camera.value)
|
||||
: true;
|
||||
final hasMicphonePermission = await Permissions.checkPermission(
|
||||
bool hasCameraPermission = false;
|
||||
bool hasMicrophonePermission = false;
|
||||
if (type == TYPE_VIDEO) {
|
||||
hasCameraPermission = await Permissions.checkPermission(context, Permission.camera.value);
|
||||
hasMicrophonePermission = await Permissions.checkPermission(
|
||||
context, Permission.microphone.value);
|
||||
if (!hasCameraPermission || !hasMicphonePermission) {
|
||||
if (!hasCameraPermission || !hasMicrophonePermission) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
hasMicrophonePermission = await Permissions.checkPermission(
|
||||
context, Permission.microphone.value);
|
||||
if (!hasMicrophonePermission) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final isGroup = widget.conversationType == ConvType.group;
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
return stickerPackageList;
|
||||
}
|
||||
|
||||
setCurrentCursor(int? value) {
|
||||
_setCurrentCursor(int? value) {
|
||||
currentCursor = value;
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
return emojiRegex().hasMatch(input);
|
||||
}
|
||||
|
||||
void deleteStickerFromText() {
|
||||
void _deleteStickerFromText() {
|
||||
String originalText = textEditingController.text;
|
||||
|
||||
if (originalText == zeroWidthSpace) {
|
||||
|
|
@ -239,9 +239,21 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
focusNode.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
if (originalText.isEmpty && textEditingController.text.isEmpty) {
|
||||
widget.model.repliedMessage = null;
|
||||
}
|
||||
}
|
||||
|
||||
void addStickerToText(String sticker) {
|
||||
void _onDeleteText(String oldText) {
|
||||
if (oldText.isEmpty) {
|
||||
if (widget.model.repliedMessage != null) {
|
||||
widget.model.repliedMessage = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _addStickerToText(String sticker) {
|
||||
final currentText = textEditingController.text;
|
||||
if (currentCursor != null && currentCursor! > -1 && currentCursor! < currentText.length + 1) {
|
||||
final firstString = currentText.substring(0, currentCursor);
|
||||
|
|
@ -267,13 +279,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
String text = textEditingController.text;
|
||||
String convID = id ?? widget.conversationID;
|
||||
final isTopic = convID.contains("@TOPIC#");
|
||||
String conversationID = isTopic ? convID : ((convType ?? widget.conversationType) == ConvType.c2c ? "c2c_$convID" : "group_$convID");
|
||||
String conversationID = isTopic ? convID : ((convType ?? widget.conversationType) == ConvType.c2c ? "${TUIConversationViewModel.conversationC2CPrefix}$convID" : "${TUIConversationViewModel.conversationGroupPrefix}$convID");
|
||||
String draftText = _filterU200b(text);
|
||||
return await conversationModel.setConversationDraft(groupID: groupID ?? widget.groupID, isTopic: isTopic, isAllowWeb: widget.model.chatConfig.isUseDraftOnWeb, conversationID: conversationID, draftText: draftText);
|
||||
}
|
||||
|
||||
// 和onSubmitted一样,只是保持焦点的不同
|
||||
onEmojiSubmitted() {
|
||||
// 和onSubmitted一样,只是保持焦点的不同
|
||||
_onEmojiSubmitted() {
|
||||
lastText = "";
|
||||
final text = textEditingController.text.trim();
|
||||
final convType = widget.conversationType;
|
||||
|
|
@ -303,8 +315,8 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
currentCursor = null;
|
||||
}
|
||||
|
||||
// index为emoji的index,data为baseurl+name
|
||||
onCustomEmojiFaceSubmitted(int index, String data) {
|
||||
// index为emoji的index,data为baseurl+name
|
||||
_onCustomEmojiFaceSubmitted(int index, String data) {
|
||||
final convType = widget.conversationType;
|
||||
|
||||
// This part of the code is written to adapt to the Native side requirements.
|
||||
|
|
@ -610,19 +622,25 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
isAddingAtSearchWords = false;
|
||||
}
|
||||
} else if (textLength > 0 && text[textLength - 1] == "@" && lastText.length < textLength) {
|
||||
V2TimGroupMemberFullInfo? memberInfo = await Navigator.push(
|
||||
List<V2TimGroupMemberFullInfo> selectedAtMemberList = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AtText(groupMemberList: model.groupMemberList, groupInfo: model.groupInfo, groupID: groupID, canAtAll: canAtAll, groupType: widget.groupType),
|
||||
),
|
||||
);
|
||||
|
||||
for (int i = 0; i < selectedAtMemberList.length; ++i) {
|
||||
V2TimGroupMemberFullInfo memberInfo = selectedAtMemberList[i];
|
||||
final showName = _getShowName(memberInfo);
|
||||
if (memberInfo != null) {
|
||||
mentionedMembersMap["@$showName"] = memberInfo;
|
||||
textEditingController.text = "$text$showName ";
|
||||
lastText = "$text$showName ";
|
||||
String addAtCharacter = i == 0 ? "" : "@";
|
||||
textEditingController.text = "${textEditingController.text}$addAtCharacter$showName ";
|
||||
lastText = "${textEditingController.text}$addAtCharacter$showName ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastText = textEditingController.text;
|
||||
}
|
||||
|
||||
|
|
@ -858,12 +876,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
context: context,
|
||||
defaultWidget: TIMUIKitTextFieldLayoutNarrow(
|
||||
stickerPackageList: stickerPackageList,
|
||||
onEmojiSubmitted: onEmojiSubmitted,
|
||||
onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted,
|
||||
backSpaceText: deleteStickerFromText,
|
||||
addStickerToText: addStickerToText,
|
||||
onEmojiSubmitted: _onEmojiSubmitted,
|
||||
onCustomEmojiFaceSubmitted: _onCustomEmojiFaceSubmitted,
|
||||
backSpaceText: _deleteStickerFromText,
|
||||
addStickerToText: _addStickerToText,
|
||||
customStickerPanel: widget.customStickerPanel,
|
||||
onChanged: widget.onChanged,
|
||||
onDeleteText: _onDeleteText,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
morePanelConfig: widget.morePanelConfig,
|
||||
repliedMessage: value,
|
||||
|
|
@ -876,7 +895,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
conversationType: widget.conversationType,
|
||||
focusNode: focusNode,
|
||||
controller: widget.controller,
|
||||
setCurrentCursor: setCurrentCursor,
|
||||
setCurrentCursor: _setCurrentCursor,
|
||||
onCursorChange: _onCursorChange,
|
||||
model: model,
|
||||
handleSendEditStatus: _handleSendEditStatus,
|
||||
|
|
@ -895,10 +914,10 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
chatConfig: widget.chatConfig ?? widget.model.chatConfig,
|
||||
theme: theme,
|
||||
currentConversation: widget.currentConversation,
|
||||
onEmojiSubmitted: onEmojiSubmitted,
|
||||
onCustomEmojiFaceSubmitted: onCustomEmojiFaceSubmitted,
|
||||
backSpaceText: deleteStickerFromText,
|
||||
addStickerToText: addStickerToText,
|
||||
onEmojiSubmitted: _onEmojiSubmitted,
|
||||
onCustomEmojiFaceSubmitted: _onCustomEmojiFaceSubmitted,
|
||||
backSpaceText: _deleteStickerFromText,
|
||||
addStickerToText: _addStickerToText,
|
||||
customStickerPanel: widget.customStickerPanel,
|
||||
onChanged: widget.onChanged,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
|
|
@ -913,7 +932,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
|
|||
conversationType: widget.conversationType,
|
||||
focusNode: focusNode,
|
||||
controller: widget.controller,
|
||||
setCurrentCursor: setCurrentCursor,
|
||||
setCurrentCursor: _setCurrentCursor,
|
||||
onCursorChange: _onCursorChange,
|
||||
model: model,
|
||||
handleSendEditStatus: _handleSendEditStatus,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:extended_text_field/extended_text_field.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.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';
|
||||
|
|
@ -81,6 +82,8 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget {
|
|||
|
||||
final V2TimMessage? repliedMessage;
|
||||
|
||||
final void Function(String)? onDeleteText;
|
||||
|
||||
/// show send emoji icon
|
||||
final bool showSendEmoji;
|
||||
|
||||
|
|
@ -112,6 +115,7 @@ class TIMUIKitTextFieldLayoutNarrow extends StatefulWidget {
|
|||
required this.model,
|
||||
this.backgroundColor,
|
||||
this.onChanged,
|
||||
this.onDeleteText,
|
||||
required this.handleSendEditStatus,
|
||||
required this.handleAtText,
|
||||
required this.handleSoftKeyBoardDelete,
|
||||
|
|
@ -444,7 +448,10 @@ class _TIMUIKitTextFieldLayoutNarrowState extends TIMUIKitState<TIMUIKitTextFiel
|
|||
Expanded(
|
||||
child: showSendSoundText
|
||||
? SendSoundMessage(onDownBottom: widget.goDownBottom, conversationID: widget.conversationID, conversationType: widget.conversationType)
|
||||
: KeyboardVisibility(
|
||||
: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: KeyboardVisibility(
|
||||
child: ExtendedTextField(
|
||||
maxLines: 4,
|
||||
minLines: 1,
|
||||
|
|
@ -490,6 +497,7 @@ class _TIMUIKitTextFieldLayoutNarrowState extends TIMUIKitState<TIMUIKitTextFiel
|
|||
isUseTencentCloudChatPackage: widget.model.chatConfig.stickerPanelConfig?.useTencentCloudChatStickerPackage ?? true,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
showAtBackground: true,
|
||||
checkHttpLink: false,
|
||||
)),
|
||||
onChanged: (bool visibility) {
|
||||
if (showKeyboard != visibility) {
|
||||
|
|
@ -499,6 +507,20 @@ class _TIMUIKitTextFieldLayoutNarrowState extends TIMUIKitState<TIMUIKitTextFiel
|
|||
}
|
||||
}),
|
||||
),
|
||||
RawKeyboardListener(
|
||||
autofocus: true,
|
||||
focusNode: FocusNode(),
|
||||
onKey: (key) {
|
||||
if (key is RawKeyDownEvent && key.logicalKey == LogicalKeyboardKey.backspace) {
|
||||
if (widget.onDeleteText != null) {
|
||||
widget.onDeleteText!(widget.textEditingController.text);
|
||||
}
|
||||
}
|
||||
}, child: Container(),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -66,10 +66,12 @@ class TIMUIKitChatConfig {
|
|||
|
||||
/// Control if allowed to show reading status for group.
|
||||
/// [Default]: true.
|
||||
/// [Deprecated: ] Please use [isShowReadingStatus] instead.
|
||||
final bool isShowGroupReadingStatus;
|
||||
|
||||
/// Control if allowed to report reading status for group.
|
||||
/// [Default]: true.
|
||||
/// [Deprecated: ] Please use [isShowReadingStatus] instead.
|
||||
final bool isReportGroupReadingStatus;
|
||||
|
||||
/// Control if allowed to show the message operation menu after long pressing message.
|
||||
|
|
@ -101,12 +103,14 @@ class TIMUIKitChatConfig {
|
|||
final bool Function(V2TimMessage message)? isAtWhenReplyDynamic;
|
||||
|
||||
/// The main switch of the group read receipt.
|
||||
/// [Deprecated: ] Please use [isShowReadingStatus] instead.
|
||||
final bool isShowGroupMessageReadReceipt;
|
||||
|
||||
/// [Deprecated: ] Please use [groupReadReceiptPermissionList] instead.
|
||||
/// [Deprecated: ] not support.
|
||||
final List<GroupReceptAllowType>? groupReadReceiptPermisionList;
|
||||
|
||||
/// Control which group can send message read receipt.
|
||||
/// [Deprecated: ] not support.
|
||||
final List<GroupReceiptAllowType>? groupReadReceiptPermissionList;
|
||||
|
||||
/// Control if show self name in group chat.
|
||||
|
|
@ -271,7 +275,7 @@ class TIMUIKitChatConfig {
|
|||
this.isShowSelfNameInGroup = false,
|
||||
this.isAtWhenReplyDynamic,
|
||||
this.offlinePushInfo,
|
||||
@Deprecated("Please use [isShowGroupReadingStatus] instead")
|
||||
@Deprecated("Please use [isShowReadingStatus] instead")
|
||||
this.isShowGroupMessageReadReceipt = true,
|
||||
this.upperRecallTime = 120,
|
||||
this.isShowOthersNameInGroup = true,
|
||||
|
|
@ -281,8 +285,9 @@ class TIMUIKitChatConfig {
|
|||
this.notificationTitle = "",
|
||||
this.notificationIOSSound = "",
|
||||
this.isAllowSoundMessage = true,
|
||||
@Deprecated("Please use [groupReadReceiptPermissionList] instead")
|
||||
@Deprecated("not support")
|
||||
this.groupReadReceiptPermisionList,
|
||||
@Deprecated("not support")
|
||||
this.groupReadReceiptPermissionList,
|
||||
this.isAllowEmojiPanel = true,
|
||||
this.isAllowShowMorePanel = true,
|
||||
|
|
@ -294,6 +299,7 @@ class TIMUIKitChatConfig {
|
|||
this.isEnableTextSelection,
|
||||
this.additionalDesktopMessageHoverBarItem,
|
||||
this.isShowGroupReadingStatus = true,
|
||||
@Deprecated("Please use [isShowReadingStatus] instead")
|
||||
this.isReportGroupReadingStatus = true,
|
||||
this.showC2cMessageEditStatus = true,
|
||||
this.additionalDesktopControlBarItems,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import 'package:tencent_im_base/tencent_im_base.dart';
|
|||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
|
||||
class MultiSelectPanel extends TIMUIKitStatelessWidget {
|
||||
final int forwardMsgNumLimit = 30;
|
||||
|
||||
final ConvType conversationType;
|
||||
|
||||
MultiSelectPanel({Key? key, required this.conversationType})
|
||||
|
|
@ -21,6 +23,39 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget {
|
|||
|
||||
_handleForwardMessage(BuildContext context, bool isMergerForward,
|
||||
TUIChatSeparateViewModel model) {
|
||||
|
||||
// 是否有选中消息
|
||||
if (model.getSelectedMessageList().isEmpty) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("请选择要操作的消息!")));
|
||||
return;
|
||||
}
|
||||
|
||||
for (var v2TimMessage in model.getSelectedMessageList()) {
|
||||
// 失败消息不支持转发
|
||||
if (v2TimMessage.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("发送失败消息不支持转发!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// 投票消息不支持转发
|
||||
if (model.isVoteMessage(v2TimMessage)) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("投票消息不支持转发!")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 逐条转发限制在 30 条以内
|
||||
if (!isMergerForward && model.getSelectedMessageList().length > forwardMsgNumLimit) {
|
||||
_showForwardLimitDialog(context);
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
|
|
@ -31,6 +66,33 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget {
|
|||
)));
|
||||
}
|
||||
|
||||
// 弹出逐条转发超限的对话框
|
||||
Future<bool?> _showForwardLimitDialog(BuildContext context) {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return CupertinoAlertDialog(
|
||||
title: Text(TIM_t("转发消息过多,暂不支持逐条转发")),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: Text(TIM_t("确定")),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: Text(TIM_t("取消")),
|
||||
isDestructiveAction: true,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_handleForwardMessageWide(BuildContext context, bool isMergerForward,
|
||||
TUIChatSeparateViewModel model) {
|
||||
TUIKitWidePopup.showPopupWindow(
|
||||
|
|
|
|||
|
|
@ -362,7 +362,6 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
|
|||
child: GestureDetector(
|
||||
child: TIMUIKitConversationItem(
|
||||
isCurrent: isCurrent,
|
||||
isShowDraft: widget.isShowDraft,
|
||||
lastMessageBuilder: widget.lastMessageBuilder,
|
||||
faceUrl: conversationItem.faceUrl ?? "",
|
||||
nickName: conversationItem.showName ?? "",
|
||||
|
|
|
|||
|
|
@ -31,14 +31,8 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
|
|||
final int? convType;
|
||||
final bool isCurrent;
|
||||
|
||||
/// Control if shows the identifier that the conversation has a draft text, inputted in previous.
|
||||
/// Also, you'd better specifying the `draftText` field for `TIMUIKitChat`, from the `draftText` in `V2TimConversation`,
|
||||
/// to meet the identifier shows here.
|
||||
final bool isShowDraft;
|
||||
|
||||
TIMUIKitConversationItem({
|
||||
Key? key,
|
||||
required this.isShowDraft,
|
||||
required this.faceUrl,
|
||||
required this.nickName,
|
||||
required this.lastMsg,
|
||||
|
|
@ -56,22 +50,20 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
|
|||
|
||||
Widget _getShowMsgWidget(BuildContext context) {
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
if (isShowDraft && draftText != null && draftText != "") {
|
||||
return TIMUIKitDraftText(
|
||||
context: context,
|
||||
draftText: draftText ?? "",
|
||||
fontSize: isDesktopScreen ? 12 : 14,
|
||||
);
|
||||
} else if (lastMsg != null) {
|
||||
if (lastMessageBuilder != null &&
|
||||
if (lastMsg != null && lastMessageBuilder != null &&
|
||||
lastMessageBuilder!(lastMsg, groupAtInfoList) != null) {
|
||||
return lastMessageBuilder!(lastMsg, groupAtInfoList)!;
|
||||
}
|
||||
|
||||
if (lastMsg != null || (draftText != null && draftText != "")) {
|
||||
return TIMUIKitLastMsg(
|
||||
fontSize: isDesktopScreen ? 12 : 14,
|
||||
groupAtInfoList: groupAtInfoList,
|
||||
lastMsg: lastMsg,
|
||||
isDisturb: isDisturb,
|
||||
unreadCount: unreadCount,
|
||||
context: context,
|
||||
draftText: draftText ?? "",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -81,8 +73,7 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
|
|||
}
|
||||
|
||||
bool isHaveSecondLine() {
|
||||
return (isShowDraft && draftText != null && draftText != "") ||
|
||||
(lastMsg != null);
|
||||
return (draftText != null && draftText != "") || (lastMsg != null);
|
||||
}
|
||||
|
||||
Widget _getTimeStringForChatWidget(BuildContext context, TUITheme theme) {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:extended_text/extended_text.dart';
|
||||
import 'package:flutter/material.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/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/message.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/TIMUIKitConversation/tim_uikit_conversation_draft_text.dart';
|
||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||
|
||||
class TIMUIKitLastMsg extends StatefulWidget {
|
||||
|
|
@ -15,8 +19,20 @@ class TIMUIKitLastMsg extends StatefulWidget {
|
|||
final List<V2TimGroupAtInfo?> groupAtInfoList;
|
||||
final BuildContext context;
|
||||
final double fontSize;
|
||||
bool isDisturb;
|
||||
int unreadCount;
|
||||
String draftText;
|
||||
|
||||
const TIMUIKitLastMsg({Key? key, this.lastMsg, required this.groupAtInfoList, required this.context, this.fontSize = 14.0}) : super(key: key);
|
||||
TIMUIKitLastMsg(
|
||||
{Key? key,
|
||||
this.lastMsg,
|
||||
required this.groupAtInfoList,
|
||||
this.isDisturb = false,
|
||||
this.unreadCount = 0,
|
||||
required this.draftText,
|
||||
required this.context,
|
||||
this.fontSize = 14.0})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<TIMUIKitLastMsg> createState() => _TIMUIKitLastMsgState();
|
||||
|
|
@ -34,7 +50,10 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
|
|||
@override
|
||||
void didUpdateWidget(covariant TIMUIKitLastMsg oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if ((oldWidget.lastMsg?.msgID != widget.lastMsg?.msgID) || (oldWidget.lastMsg?.id != widget.lastMsg?.id) || (oldWidget.lastMsg?.status != widget.lastMsg?.status)) {
|
||||
if ((oldWidget.lastMsg?.msgID != widget.lastMsg?.msgID) ||
|
||||
(oldWidget.lastMsg?.id != widget.lastMsg?.id) ||
|
||||
(oldWidget.lastMsg?.status != widget.lastMsg?.status) ||
|
||||
(oldWidget.unreadCount != widget.unreadCount)) {
|
||||
_getMsgElem();
|
||||
}
|
||||
}
|
||||
|
|
@ -63,22 +82,33 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
|
|||
final isAdminRevoke = revokeStatus.$2;
|
||||
if (isRevokedMessage) {
|
||||
final isSelf = widget.lastMsg!.isSelf ?? true;
|
||||
final option1 = isAdminRevoke ? TIM_t("管理员") : (isSelf ? TIM_t("您") : widget.lastMsg!.nickName ?? widget.lastMsg?.sender);
|
||||
final option1 =
|
||||
isAdminRevoke ? TIM_t("管理员") : (isSelf ? TIM_t("您") : widget.lastMsg!.nickName ?? widget.lastMsg?.sender);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
groupTipsAbstractText = TIM_t_para("{{option1}}撤回了一条消息", "$option1撤回了一条消息")(option1: option1);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
final newText = await _getLastMsgShowText(widget.lastMsg, widget.context) ?? "";
|
||||
String msgShowText = await _getLastMsgShowText(widget.lastMsg, widget.context) ?? "";
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
groupTipsAbstractText = newText;
|
||||
groupTipsAbstractText = msgShowText;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _getDisturbUnreadCountInfo() {
|
||||
if (widget.isDisturb && widget.unreadCount > 0) {
|
||||
final option1 = widget.unreadCount.toString();
|
||||
String unreadCountText = TIM_t_para("[{{option1}} 条]", "[$option1 条]")(option1: option1);
|
||||
return unreadCountText;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Future<String?> _getLastMsgShowText(V2TimMessage? message, BuildContext context) async {
|
||||
final msgType = message!.elemType;
|
||||
switch (msgType) {
|
||||
|
|
@ -121,37 +151,87 @@ class _TIMUIKitLastMsgState extends TIMUIKitState<TIMUIKitLastMsg> {
|
|||
}
|
||||
|
||||
String _getAtMessage() {
|
||||
bool atMe = false;
|
||||
bool atAll = false;
|
||||
String msg = "";
|
||||
for (var item in widget.groupAtInfoList) {
|
||||
if (item!.atType == 1) {
|
||||
msg = TIM_t("[有人@我] ");
|
||||
} else {
|
||||
msg = TIM_t("[@所有人] ");
|
||||
atMe = true;
|
||||
continue;
|
||||
} else if (item!.atType == 2) {
|
||||
atAll = true;
|
||||
continue;
|
||||
} else if (item!.atType == 3) {
|
||||
atMe = true;
|
||||
atAll = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (atAll && atMe) {
|
||||
msg = TIM_t("[@所有人][有人@我]");
|
||||
} else if (atAll) {
|
||||
msg = TIM_t("[@所有人]");
|
||||
} else if (atMe) {
|
||||
msg = TIM_t("[有人@我]");
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
String _getDraftShowText() {
|
||||
final draftShowText = TIM_t("草稿");
|
||||
return '[$draftShowText]';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
final TUITheme theme = value.theme;
|
||||
final icon = _getIconByMsgStatus(context);
|
||||
String disturbUnreadCountInfo = _getDisturbUnreadCountInfo();
|
||||
return Row(children: [
|
||||
if (icon != null)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 2),
|
||||
child: icon,
|
||||
),
|
||||
if (widget.groupAtInfoList.isNotEmpty) Text(_getAtMessage(), style: TextStyle(color: theme.cautionColor, fontSize: widget.fontSize)),
|
||||
if (TencentUtils.checkString(groupTipsAbstractText) != null)
|
||||
if (widget.groupAtInfoList.isNotEmpty)
|
||||
Text(_getAtMessage(), style: TextStyle(color: theme.cautionColor, fontSize: widget.fontSize)),
|
||||
if (widget.draftText != null && widget.draftText != "")
|
||||
Text(_getDraftShowText(), style: TextStyle(color: theme.conversationItemDraftTextColor, fontSize: widget.fontSize)),
|
||||
if (disturbUnreadCountInfo != "")
|
||||
Text(disturbUnreadCountInfo, style: TextStyle(color: theme.weakTextColor, fontSize: widget.fontSize)),
|
||||
if (widget.draftText != null && widget.draftText != "")
|
||||
Expanded(
|
||||
child: Text(
|
||||
child: ExtendedText(
|
||||
groupTipsAbstractText,
|
||||
softWrap: true,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(height: 1, color: theme.weakTextColor, fontSize: widget.fontSize),
|
||||
)),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseQQPackage: true,
|
||||
isUseTencentCloudChatPackage: true,
|
||||
showAtBackground: true,
|
||||
)
|
||||
),
|
||||
),
|
||||
if (widget.draftText == null || widget.draftText == "" && TencentUtils.checkString(groupTipsAbstractText) != null)
|
||||
Expanded(
|
||||
child: ExtendedText(
|
||||
groupTipsAbstractText,
|
||||
softWrap: true,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(height: 1, color: theme.weakTextColor, fontSize: widget.fontSize),
|
||||
specialTextSpanBuilder: DefaultSpecialTextSpanBuilder(
|
||||
isUseQQPackage: true,
|
||||
isUseTencentCloudChatPackage: true,
|
||||
showAtBackground: true,
|
||||
)
|
||||
),
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,9 +104,9 @@ class GroupProfileMemberListPageState
|
|||
memberList: searchMemberList ?? groupProfileModel.groupMemberList,
|
||||
removeMember: _kickedOffMember,
|
||||
touchBottomCallBack: () {},
|
||||
onTapMemberItem: (friendInfo, details) {
|
||||
onTapMemberItem: (memberInfo, details) {
|
||||
if (widget.model.onClickUser != null) {
|
||||
widget.model.onClickUser!(friendInfo.userID, details);
|
||||
widget.model.onClickUser!(memberInfo, details);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -134,9 +134,9 @@ class GroupProfileMemberListPageState
|
|||
memberList: searchMemberList ?? groupProfileModel.groupMemberList,
|
||||
removeMember: _kickedOffMember,
|
||||
touchBottomCallBack: () {},
|
||||
onTapMemberItem: (friendInfo, details) {
|
||||
onTapMemberItem: (memberInfo, details) {
|
||||
if (widget.model.onClickUser != null) {
|
||||
widget.model.onClickUser!(friendInfo.userID, details);
|
||||
widget.model.onClickUser!(memberInfo, details);
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class TIMUIKitGroupProfile extends StatefulWidget {
|
|||
|
||||
/// The callback after user clicking a user,
|
||||
/// you may navigating to the specific profile page, or anywhere you want.
|
||||
final Function(String userID, TapDownDetails? tapDetails)? onClickUser;
|
||||
final Function(V2TimGroupMemberFullInfo groupMemberFullInfo, TapDownDetails? tapDetails)? onClickUser;
|
||||
|
||||
const TIMUIKitGroupProfile(
|
||||
{Key? key,
|
||||
|
|
@ -154,6 +154,10 @@ class _TIMUIKitGroupProfileState extends TIMUIKitState<TIMUIKitGroupProfile> {
|
|||
groupListenerModel.needUpdate = null;
|
||||
switch (needUpdate.updateType) {
|
||||
case UpdateType.groupInfo:
|
||||
if (needUpdate.groupInfoSubType == GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER) {
|
||||
model.onOwnerChanged(needUpdate.ownerID);
|
||||
}
|
||||
|
||||
model.loadGroupInfo(widget.groupID);
|
||||
break;
|
||||
case UpdateType.memberList:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/t
|
|||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_add_opt.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_detail_card.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_manage.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_tile.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_member_title.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_message_disturb.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_name_card.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/widgets/tim_uikit_group_notification.dart';
|
||||
|
|
@ -27,7 +27,7 @@ class TIMUIKitGroupProfileWidget {
|
|||
}
|
||||
|
||||
static Widget memberTile() {
|
||||
return GroupMemberTile();
|
||||
return GroupMemberTitle();
|
||||
}
|
||||
|
||||
static Widget groupNotification({
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
|
|||
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
|
||||
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||
|
||||
class GroupMemberTile extends TIMUIKitStatelessWidget {
|
||||
GroupMemberTile({
|
||||
class GroupMemberTitle extends TIMUIKitStatelessWidget {
|
||||
GroupMemberTitle({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
|
|||
return InkWell(
|
||||
onTapDown: (details) {
|
||||
if (model.onClickUser != null && element?.userID != null) {
|
||||
model.onClickUser!(element!.userID, details);
|
||||
model.onClickUser!(element!, details);
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
|
|
@ -45,26 +45,22 @@ class GroupProfileType extends TIMUIKitStatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: isDesktopScreen
|
||||
? null
|
||||
: Border(
|
||||
border: isDesktopScreen ? null : Border(
|
||||
bottom: BorderSide(
|
||||
color: theme.weakDividerColor ??
|
||||
CommonColor.weakDividerColor))),
|
||||
color:
|
||||
theme.weakDividerColor ?? CommonColor.weakDividerColor))),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
TIM_t("群类型"),
|
||||
style: TextStyle(
|
||||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
color: theme.darkTextColor),
|
||||
fontSize: isDesktopScreen ? 14 : 16, color: theme.darkTextColor),
|
||||
),
|
||||
Text(
|
||||
groupType,
|
||||
style: TextStyle(
|
||||
fontSize: isDesktopScreen ? 14 : 16,
|
||||
color: theme.weakTextColor),
|
||||
fontSize: isDesktopScreen ? 14 : 16, color: theme.weakTextColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -240,51 +240,14 @@ class _TIMUIKitProfileState extends TIMUIKitState<TIMUIKitProfile> {
|
|||
}
|
||||
|
||||
void handleAddFriend() async {
|
||||
model.addFriend(userInfo.userID).then((res) {
|
||||
if (res == null) {
|
||||
throw Error();
|
||||
}
|
||||
if (res.resultCode == 0) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("好友添加成功"),
|
||||
infoCode: 6661202));
|
||||
} else if (res.resultCode == 30539) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("好友申请已发出"),
|
||||
infoCode: 6661203));
|
||||
} else if (res.resultCode == 30515) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("当前用户在黑名单"),
|
||||
infoCode: 6661204));
|
||||
}
|
||||
}).catchError((error) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("好友添加失败"),
|
||||
infoCode: 6661205));
|
||||
});
|
||||
model.addFriend(userInfo.userID);
|
||||
}
|
||||
|
||||
void handleDeleteFriend() {
|
||||
model.deleteFriend(userInfo.userID).then((res) {
|
||||
if (res == null) {
|
||||
throw Error();
|
||||
Future<void> handleDeleteFriend() async {
|
||||
var result = await model.deleteFriend(userInfo.userID, needUpdateData: false);
|
||||
if (result != null) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
if (res.resultCode != 0 && res.resultCode != null) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("好友删除失败"),
|
||||
infoCode: 6661207));
|
||||
} else {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO,
|
||||
infoRecommendText: TIM_t("好友删除成功"),
|
||||
infoCode: 6661206));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _renderWidgetsWithOrder(List<ProfileWidgetEnum> order) {
|
||||
|
|
|
|||
|
|
@ -263,7 +263,6 @@ class TIMUIKitProfileWidget extends TIMUIKitClass {
|
|||
return TIMUIKitOperationItem(
|
||||
smallCardMode: smallCardMode,
|
||||
isEmpty: false,
|
||||
showAllowEditStatus: false,
|
||||
operationName: TIM_t("生日"),
|
||||
operationRightWidget: Text(formatter.format(date),
|
||||
textAlign: isDesktopScreen ? null : TextAlign.end),
|
||||
|
|
@ -272,7 +271,6 @@ class TIMUIKitProfileWidget extends TIMUIKitClass {
|
|||
return TIMUIKitOperationItem(
|
||||
smallCardMode: smallCardMode,
|
||||
isEmpty: false,
|
||||
showAllowEditStatus: false,
|
||||
operationName: TIM_t("生日"),
|
||||
operationRightWidget:
|
||||
Text(TIM_t("未填写"), textAlign: isDesktopScreen ? null : TextAlign.end),
|
||||
|
|
|
|||
|
|
@ -66,16 +66,24 @@ class TIMUIKitSearchFriendState extends TIMUIKitState<TIMUIKitSearchFriend> {
|
|||
int convIndex = _conversationList
|
||||
.indexWhere((item) => conv.friendInfo?.userID == item?.userID);
|
||||
V2TimConversation conversation = _conversationList[convIndex]!;
|
||||
late String? showNickName;
|
||||
if (conv.friendInfo?.friendRemark != null && conv.friendInfo?.friendRemark != "") {
|
||||
showNickName = conv.friendInfo?.friendRemark;
|
||||
} else if (conv.friendInfo?.userProfile?.nickName != null && conv.friendInfo?.userProfile?.nickName != "") {
|
||||
showNickName = conv.friendInfo?.userProfile?.nickName;
|
||||
} else {
|
||||
showNickName = conv.friendInfo?.userID;
|
||||
}
|
||||
|
||||
return TIMUIKitSearchItem(
|
||||
onClick: () {
|
||||
widget.onTapConversation(conversation, null);
|
||||
},
|
||||
faceUrl: conv.friendInfo?.userProfile?.faceUrl ?? "",
|
||||
showName: "",
|
||||
lineOne: conversation.showName ??
|
||||
conversation.userID ??
|
||||
conv.friendInfo?.userID ??
|
||||
"",
|
||||
|
||||
lineOne: conversation.userID!,
|
||||
lineTwo: TIM_t("昵称") + ":" + showNickName!,
|
||||
);
|
||||
}).toList(),
|
||||
_renderShowALl(filteredFriendResultList.length),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.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';
|
||||
|
|
@ -53,7 +54,7 @@ class _ForwardMessageScreenState extends TIMUIKitState<ForwardMessageScreen> {
|
|||
}
|
||||
|
||||
List<String> _getAbstractList() {
|
||||
return widget.model.multiSelectedMessageList.map((e) {
|
||||
return widget.model.getSelectedMessageList().map((e) {
|
||||
final sender = (e.nickName != null && e.nickName!.isNotEmpty)
|
||||
? e.nickName
|
||||
: e.sender;
|
||||
|
|
@ -62,6 +63,11 @@ class _ForwardMessageScreenState extends TIMUIKitState<ForwardMessageScreen> {
|
|||
}
|
||||
|
||||
handleForwardMessage() async {
|
||||
var confirmResult = await _showConfirmForwardDialog(context);
|
||||
if (confirmResult == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.isMergerForward) {
|
||||
await widget.model.sendMergerMessage(
|
||||
conversationList: _conversationList,
|
||||
|
|
@ -82,6 +88,33 @@ class _ForwardMessageScreenState extends TIMUIKitState<ForwardMessageScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
// 弹出转发确认对话框
|
||||
Future<bool?> _showConfirmForwardDialog(BuildContext context) {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return CupertinoAlertDialog(
|
||||
title: Text(TIM_t("您确定进行转发吗?")),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: Text(TIM_t("确定")),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: Text(TIM_t("取消")),
|
||||
isDestructiveAction: true,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ class GesturedImageState extends ExtendedImageGestureState {
|
|||
gestureDetails = GestureDetails(
|
||||
totalScale: _gestureConfig!.initialScale,
|
||||
offset: Offset.zero,
|
||||
)..initialAlignment = _gestureConfig!.initialAlignment;
|
||||
initialAlignment: _gestureConfig!.initialAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -433,7 +434,8 @@ class GesturedImageState extends ExtendedImageGestureState {
|
|||
_gestureDetails = GestureDetails(
|
||||
totalScale: _gestureConfig!.initialScale,
|
||||
offset: Offset.zero,
|
||||
)..initialAlignment = _gestureConfig!.initialAlignment;
|
||||
initialAlignment: _gestureConfig!.initialAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
if (_gestureConfig!.cacheGesture) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/radio_button.dart';
|
|||
import 'package:tencent_im_base/tencent_im_base.dart';
|
||||
|
||||
class GroupProfileMemberList extends StatefulWidget {
|
||||
static String AT_ALL_USER_ID = "__kImSDK_MesssageAtALL__";
|
||||
final List<V2TimGroupMemberFullInfo?> memberList;
|
||||
final Function(String userID)? removeMember;
|
||||
final bool canSlideDelete;
|
||||
|
|
@ -53,7 +54,7 @@ class GroupProfileMemberList extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList> {
|
||||
List<V2TimGroupMemberFullInfo> selectedMember = [];
|
||||
List<V2TimGroupMemberFullInfo> selectedMemberList = [];
|
||||
|
||||
_getShowName(V2TimGroupMemberFullInfo? item) {
|
||||
final friendRemark = item?.friendRemark ?? "";
|
||||
|
|
@ -93,7 +94,7 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
|
|||
if (widget.canAtAll) {
|
||||
final canAtGroupType = ["Work", "Public", "Meeting"];
|
||||
if (canAtGroupType.contains(widget.groupType)) {
|
||||
showList.insert(0, ISuspensionBeanImpl(memberInfo: V2TimGroupMemberFullInfo(userID: "__kImSDK_MesssageAtALL__", nickName: TIM_t("所有人")), tagIndex: ""));
|
||||
showList.insert(0, ISuspensionBeanImpl(memberInfo: V2TimGroupMemberFullInfo(userID: GroupProfileMemberList.AT_ALL_USER_ID, nickName: TIM_t("所有人")), tagIndex: ""));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,19 +134,21 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
|
|||
child: CheckBoxButton(
|
||||
onChanged: (isChecked) {
|
||||
if (isChecked) {
|
||||
if (widget.maxSelectNum != null && selectedMember.length >= widget.maxSelectNum!) {
|
||||
if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) {
|
||||
return;
|
||||
}
|
||||
selectedMember.add(memberInfo);
|
||||
selectedMemberList.add(memberInfo);
|
||||
} else {
|
||||
selectedMember.remove(memberInfo);
|
||||
selectedMemberList.removeWhere((element) => element.userID == memberInfo.userID);
|
||||
}
|
||||
|
||||
if (widget.onSelectedMemberChange != null) {
|
||||
widget.onSelectedMemberChange!(selectedMember);
|
||||
widget.onSelectedMemberChange!(selectedMemberList);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
isChecked: selectedMember.contains(memberInfo)),
|
||||
isChecked: selectedMemberList.where((element) => element.userID == memberInfo.userID).toList().isNotEmpty
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: isDesktopScreen ? 30 : 36,
|
||||
|
|
@ -194,17 +197,17 @@ class _GroupProfileMemberListState extends TIMUIKitState<GroupProfileMemberList>
|
|||
widget.onTapMemberItem!(memberInfo, null);
|
||||
}
|
||||
if (widget.canSelectMember) {
|
||||
final isChecked = selectedMember.contains(memberInfo);
|
||||
final isChecked = selectedMemberList.contains(memberInfo);
|
||||
if (isChecked) {
|
||||
selectedMember.remove(memberInfo);
|
||||
selectedMemberList.remove(memberInfo);
|
||||
} else {
|
||||
if (widget.maxSelectNum != null && selectedMember.length >= widget.maxSelectNum!) {
|
||||
if (widget.maxSelectNum != null && selectedMemberList.length >= widget.maxSelectNum!) {
|
||||
return;
|
||||
}
|
||||
selectedMember.add(memberInfo);
|
||||
selectedMemberList.add(memberInfo);
|
||||
}
|
||||
if (widget.onSelectedMemberChange != null) {
|
||||
widget.onSelectedMemberChange!(selectedMember);
|
||||
widget.onSelectedMemberChange!(selectedMemberList);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,13 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
|
|||
double initialScale = 1.0;
|
||||
bool isLoading = false;
|
||||
|
||||
GlobalKey<ExtendedImageSlidePageState> slidePagekey =
|
||||
GlobalKey<ExtendedImageSlidePageState> slidePageKey =
|
||||
GlobalKey<ExtendedImageSlidePageState>();
|
||||
GlobalKey<ExtendedImageGestureState> extendedImageGestureKey =
|
||||
GlobalKey<ExtendedImageGestureState>();
|
||||
|
||||
void close() {
|
||||
slidePagekey.currentState!.popPage();
|
||||
slidePageKey.currentState!.popPage();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +78,10 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
|
|||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
return OrientationBuilder(builder: ((context, orientation) {
|
||||
if (extendedImageGestureKey.currentState != null) {
|
||||
extendedImageGestureKey.currentState!.reset();
|
||||
}
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
|
|
@ -94,7 +98,7 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
|
|||
bottom: 0,
|
||||
right: 0,
|
||||
child: ExtendedImageSlidePage(
|
||||
key: slidePagekey,
|
||||
key: slidePageKey,
|
||||
slideAxis: SlideAxis.both,
|
||||
slidePageBackgroundHandler: (Offset offset, Size size) {
|
||||
if (orientation == Orientation.landscape) {
|
||||
|
|
@ -123,7 +127,7 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
|
|||
onTap: close,
|
||||
child: HeroWidget(
|
||||
tag: widget.heroTag,
|
||||
slidePagekey: slidePagekey,
|
||||
slidePagekey: slidePageKey,
|
||||
child: ExtendedImage(
|
||||
image: widget.imageProvider,
|
||||
extendedImageGestureKey:
|
||||
|
|
@ -179,6 +183,7 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
|
|||
1 / fitWidthScale; // fittedHeight
|
||||
doubleTapScales[1] = 1 / fitWidthScale;
|
||||
}
|
||||
|
||||
return GesturedImage(state,
|
||||
key: extendedImageGestureKey);
|
||||
case LoadState.failed:
|
||||
|
|
@ -188,7 +193,7 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
|
|||
},
|
||||
onDoubleTap: (ExtendedImageGestureState state) {
|
||||
///you can use define pointerDownPosition as you can,
|
||||
///default value is double tap pointer down postion.
|
||||
///default value is double tap pointer down position.
|
||||
final Offset? pointerDownPosition =
|
||||
state.pointerDownPosition;
|
||||
final double? begin =
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:extended_text/extended_text.dart';
|
|||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/http_text.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/compiler/md_text.dart';
|
||||
import 'package:tencent_im_base/base_widgets/tim_stateless_widget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
|
||||
|
|
@ -125,7 +126,7 @@ class LinkText extends TIMStatelessWidget {
|
|||
}
|
||||
|
||||
if (LinkUtils.urlReg.hasMatch(c)) {
|
||||
contentData += '\$' + c + '\$';
|
||||
contentData += HttpText.flag + c + HttpText.flag;
|
||||
_contentList.add(TextSpan(
|
||||
text: c,
|
||||
style: TextStyle(color: LinkUtils.hexToColor("015fff")),
|
||||
|
|
@ -160,12 +161,12 @@ class LinkText extends TIMStatelessWidget {
|
|||
Widget timBuild(BuildContext context) {
|
||||
return ExtendedText(_getContentSpan(messageText, context), softWrap: true,
|
||||
onSpecialTextTap: (dynamic parameter) {
|
||||
if (parameter.toString().startsWith('\$')) {
|
||||
if (parameter.toString().startsWith(HttpText.flag)) {
|
||||
if (onLinkTap != null) {
|
||||
onLinkTap!((parameter.toString()).replaceAll('\$', ''));
|
||||
onLinkTap!((parameter.toString()).replaceAll(HttpText.flag, ''));
|
||||
} else {
|
||||
LinkUtils.launchURL(
|
||||
context, (parameter.toString()).replaceAll('\$', ''));
|
||||
context, (parameter.toString()).replaceAll(HttpText.flag, ''));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -243,15 +243,14 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
|
|||
));
|
||||
await player.initialize();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
double w = getVideoWidth();
|
||||
double h = getVideoHeight();
|
||||
double aspectRatio = player.value.aspectRatio;
|
||||
ChewieController controller = ChewieController(
|
||||
videoPlayerController: player,
|
||||
autoPlay: true,
|
||||
looping: false,
|
||||
showControlsOnInitialize: false,
|
||||
allowPlaybackSpeedChanging: false,
|
||||
aspectRatio: w == 0 || h == 0 ? null : w / h,
|
||||
aspectRatio: aspectRatio,
|
||||
customControls: VideoCustomControls(downloadFn: () async {
|
||||
return await _saveVideo();
|
||||
}));
|
||||
|
|
|
|||
25
pubspec.yaml
25
pubspec.yaml
|
|
@ -1,6 +1,6 @@
|
|||
name: tencent_cloud_chat_uikit
|
||||
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
|
||||
version: 3.0.0
|
||||
version: 3.1.0
|
||||
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
|
||||
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
|
||||
|
|
@ -21,17 +21,17 @@ dependencies:
|
|||
sdk: flutter
|
||||
adaptive_action_sheet: ^2.0.1
|
||||
provider: ^6.0.1
|
||||
intl: any
|
||||
intl: ^0.19.0
|
||||
get_it: ^7.2.0
|
||||
dotted_border: ^2.0.0+2
|
||||
flutter_svg: ^2.0.6
|
||||
image_picker: ^0.8.5+3
|
||||
file_picker: ^5.3.0
|
||||
tencent_super_tooltip: ^0.0.1
|
||||
video_player: ^2.7.0
|
||||
chewie: ^1.7.5
|
||||
video_player: ^2.9.0
|
||||
chewie: ^1.8.5
|
||||
flutter_slidable: ^3.0.1
|
||||
flutter_plugin_record_plus: ^0.0.16
|
||||
flutter_plugin_record_plus: ^0.0.17
|
||||
azlistview_all_platforms: ^2.1.2
|
||||
lpinyin: ^2.0.3
|
||||
transparent_image: ^2.0.0
|
||||
|
|
@ -40,13 +40,13 @@ dependencies:
|
|||
cached_network_image: ^3.3.0
|
||||
shared_preferences: ^2.0.13
|
||||
scroll_to_index: ^2.1.1
|
||||
wechat_assets_picker: ^8.9.0-dev.1
|
||||
wechat_camera_picker: ^4.2.0-dev.2
|
||||
wechat_assets_picker: ^9.3.3
|
||||
wechat_camera_picker: ^4.3.4
|
||||
flutter_easyrefresh: ^2.2.1
|
||||
extended_image: '>=8.2.0 <=8.2.4'
|
||||
extended_image: ^9.0.0
|
||||
extended_text_field: ^16.0.0
|
||||
extended_text: ^14.0.0
|
||||
package_info_plus: ^4.0.1
|
||||
package_info_plus: ^8.0.0
|
||||
loading_animation_widget: ^1.1.0+3
|
||||
permission_handler: ^10.2.0
|
||||
tuple: ^2.0.0
|
||||
|
|
@ -63,19 +63,20 @@ dependencies:
|
|||
tencent_keyboard_visibility: ^1.0.1
|
||||
tim_ui_kit_sticker_plugin: ^3.2.0
|
||||
tencent_im_base: ^8.0.0
|
||||
fc_native_video_thumbnail: ^0.11.1
|
||||
fc_native_video_thumbnail: ^0.16.0
|
||||
path: ^1.8.1
|
||||
tencent_cloud_uikit_core: ^1.6.0
|
||||
pasteboard: ^0.2.0
|
||||
desktop_drop: ^0.4.4
|
||||
device_info_plus: any
|
||||
device_info_plus: ^10.1.2
|
||||
cross_file: ^0.3.3+4
|
||||
csslib: 0.17.2
|
||||
csslib: ^0.17.2
|
||||
diff_match_patch: ^0.4.1
|
||||
just_audio: ^0.9.34
|
||||
markdown: ^7.1.0
|
||||
logger: ^2.0.1
|
||||
image_clipboard: ^1.0.0+2
|
||||
visibility_detector: ^0.4.0+2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue