diff --git a/example/pubspec.lock b/example/pubspec.lock
index 72dea3f..41d9098 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -1139,7 +1139,7 @@ packages:
path: ".."
relative: true
source: path
- version: "2.1.0+1"
+ version: "2.1.0+2"
tencent_cloud_uikit_core:
dependency: transitive
description:
diff --git a/images/svg/send_face.png b/images/svg/send_face.png
new file mode 100644
index 0000000..11b1dbb
Binary files /dev/null and b/images/svg/send_face.png differ
diff --git a/images/svg/send_face.svg b/images/svg/send_face.svg
index 4f0d3c5..cc96225 100644
--- a/images/svg/send_face.svg
+++ b/images/svg/send_face.svg
@@ -1,5 +1,7 @@
diff --git a/lib/business_logic/separate_models/tui_chat_separate_view_model.dart b/lib/business_logic/separate_models/tui_chat_separate_view_model.dart
index a0984ec..bedb4af 100644
--- a/lib/business_logic/separate_models/tui_chat_separate_view_model.dart
+++ b/lib/business_logic/separate_models/tui_chat_separate_view_model.dart
@@ -20,6 +20,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
+import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart';
import 'package:uuid/uuid.dart';
enum LoadDirection { previous, latest }
@@ -53,7 +54,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
bool showC2cMessageEditStatus = true;
TIMUIKitChatConfig chatConfig = const TIMUIKitChatConfig();
ValueChanged? setInputField;
- String Function(V2TimMessage message)? abstractMessageBuilder;
+ String? Function(V2TimMessage message)? abstractMessageBuilder;
Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
V2TimGroupMemberFullInfo? _currentChatUserInfo;
V2TimGroupInfo? _groupInfo;
@@ -228,7 +229,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
globalModel.setChatConfig(chatConfig);
globalModel.clearRecivedNewMessageCount();
_isInit = true;
- Future.delayed(const Duration(milliseconds: 300), (){
+ Future.delayed(const Duration(milliseconds: 300), () {
markMessageAsRead();
});
}
@@ -809,7 +810,25 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
return null;
}
- String _getAbstractMessage(V2TimMessage message) {
+ String _getMessageAbstract(V2TimMessage message){
+ final messageAbstract = RepliedMessageAbstract(
+ summary: TIM_t(_getMessageSummary(message)),
+ elemType: message.elemType,
+ msgID: message.msgID,
+ timestamp: message.timestamp,
+ seq: message.seq
+ );
+ return jsonEncode(messageAbstract.toJson());
+ }
+
+ String _getMessageSummary(V2TimMessage message) {
+ final String? customAbstractMessage = abstractMessageBuilder != null
+ ? abstractMessageBuilder!(message)
+ : null;
+ if (customAbstractMessage != null) {
+ return customAbstractMessage;
+ }
+
final elemType = message.elemType;
switch (elemType) {
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
@@ -831,7 +850,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
return "[语音消息]";
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
- return "[文本消息]";
+ return message.textElem?.text ?? "[文本消息]";
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
return "[视频消息]";
default:
@@ -869,7 +888,7 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
final cloudCustomData = {
"messageReply": {
"messageID": _repliedMessage!.msgID,
- "messageAbstract": _getAbstractMessage(_repliedMessage!),
+ "messageAbstract": _getMessageAbstract(_repliedMessage!),
"messageSender": hasNickName
? _repliedMessage!.nickName
: _repliedMessage?.sender,
@@ -893,9 +912,10 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
];
globalModel.setMessageList(conversationID, currentHistoryMsgList);
- final sendMsgRes = await _messageService.sendReplyMessage(
+ _repliedMessage = null;
+ final sendMsgRes = await _messageService.sendMessage(
+ cloudCustomData: json.encode(cloudCustomData),
id: textMessageInfo.id as String,
- replyMessage: _repliedMessage!,
offlinePushInfo: tools.buildMessagePushInfo(
messageInfoWithSender, convID, convType),
needReadReceipt: chatConfig.isShowGroupReadingStatus &&
@@ -908,7 +928,6 @@ class TUIChatSeparateViewModel extends ChangeNotifier {
.contains(oldGroupType))),
groupID: groupID,
receiver: receiver);
- _repliedMessage = null;
notifyListeners();
globalModel.updateMessage(sendMsgRes, convID,
messageInfoWithSender.id ?? "", convType, groupType, setInputField);
diff --git a/lib/business_logic/view_models/tui_conversation_view_model.dart b/lib/business_logic/view_models/tui_conversation_view_model.dart
index 6d8985f..40b7175 100644
--- a/lib/business_logic/view_models/tui_conversation_view_model.dart
+++ b/lib/business_logic/view_models/tui_conversation_view_model.dart
@@ -2,6 +2,7 @@
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
+import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/conversation_life_cycle.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
@@ -42,6 +43,7 @@ class TUIConversationViewModel extends ChangeNotifier {
late V2TimConversationListener _conversationListener;
List _conversationList = [];
static V2TimConversation? _selectedConversation;
+ Map webDraftMap = {};
bool _haveMoreData = true;
int _totalUnReadCount = 0;
@@ -65,9 +67,8 @@ class TUIConversationViewModel extends ChangeNotifier {
.toList();
_conversationList.removeWhere((element) => element?.isPinned == true);
_conversationList = [...pinnedConversation, ..._conversationList];
- // ignore: empty_catches
- } catch (e) {
- }
+ // ignore: empty_catches
+ } catch (e) {}
} else {
_conversationList.sort((a, b) => b!.orderkey!.compareTo(a!.orderkey!));
}
@@ -259,10 +260,46 @@ class TUIConversationViewModel extends ChangeNotifier {
listener: _conversationListener);
}
- Future setConversationDraft(
- {required String conversationID, String? draftText}) async {
- return _conversationService.setConversationDraft(
- conversationID: conversationID, draftText: draftText);
+ Future setConversationDraft({
+ required String conversationID,
+ String? draftText,
+ bool isTopic = false,
+ String? groupID,
+ bool isAllowWeb = true,
+ }) async {
+ assert(!isTopic || (groupID != null && groupID.isNotEmpty),
+ "When 'isTopic' is true, 'groupID' must not be null or empty.");
+ if (PlatformUtils().isWeb && isAllowWeb) {
+ webDraftMap[conversationID] = draftText ?? "";
+ return V2TimCallback(code: 0, desc: "");
+ } else {
+ if (isTopic) {
+ final topicInfoList = await TencentImSDKPlugin.v2TIMManager
+ .getGroupManager()
+ .getTopicInfoList(groupID: groupID!, topicIDList: [conversationID]);
+ final topicInfo = topicInfoList.data?.first.topicInfo;
+ topicInfo?.draftText = draftText;
+ final res = await TencentImSDKPlugin.v2TIMManager
+ .getGroupManager()
+ .setTopicInfo(groupID: groupID, topicInfo: topicInfo!);
+ return res;
+ } else {
+ return _conversationService.setConversationDraft(
+ conversationID: conversationID, draftText: draftText);
+ }
+ }
+ }
+
+ clearWebDraft({
+ required String conversationID,
+ }) {
+ webDraftMap[conversationID] = "";
+ }
+
+ String? getWebDraft({
+ required String conversationID,
+ }) {
+ return TencentUtils.checkString(webDraftMap[conversationID]);
}
clearData() {
diff --git a/lib/data_services/core/core_services.dart b/lib/data_services/core/core_services.dart
index 5c68558..1faa4ab 100644
--- a/lib/data_services/core/core_services.dart
+++ b/lib/data_services/core/core_services.dart
@@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_config.dart';
@@ -92,4 +93,6 @@ abstract class CoreServices {
setDarkTheme();
setLightTheme();
+
+ setDeviceType(DeviceType deviceType);
}
diff --git a/lib/data_services/core/core_services_implements.dart b/lib/data_services/core/core_services_implements.dart
index 47fc81a..97fc281 100644
--- a/lib/data_services/core/core_services_implements.dart
+++ b/lib/data_services/core/core_services_implements.dart
@@ -2,6 +2,7 @@
import 'package:flutter/cupertino.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
@@ -74,22 +75,28 @@ class CoreServicesImpl implements CoreServices {
@override
Future init(
{
-
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
ValueChanged? onTUIKitCallbackListener,
required int sdkAppID,
required LogLevelEnum loglevel,
required V2TimSDKListener listener,
LanguageEnum? language,
- String? extraLanguage,
+ String? extraLanguage,
TIMUIKitConfig? config,
+
+ /// Specify the current device platform, mobile or desktop, based on your needs.
+ /// TUIKit will automatically determine the platform if no specification is provided. DeviceType? platform,
+ DeviceType? platform,
VoidCallback? onWebLoginSuccess}) async {
+ if (platform != null) {
+ TUIKitScreenUtils.deviceType = platform;
+ }
addIdentifier();
- if(extraLanguage != null){
+ if (extraLanguage != null) {
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, extraLanguage);
});
- }else if (language != null) {
+ } else if (language != null) {
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, languageEnumToString[language]);
});
@@ -139,11 +146,11 @@ class CoreServicesImpl implements CoreServices {
required String userId,
}) async {
_userID = userId;
- if(extraLanguage != null){
+ if (extraLanguage != null) {
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, extraLanguage);
});
- }else if (language != null) {
+ } else if (language != null) {
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, languageEnumToString[language]);
});
@@ -246,9 +253,8 @@ class CoreServicesImpl implements CoreServices {
}
tuiFriendShipViewModel.userStatusList = currentUserStatusList;
- // ignore: empty_catches
- } catch (e) {
- }
+ // ignore: empty_catches
+ } catch (e) {}
}
@override
@@ -398,4 +404,9 @@ class CoreServicesImpl implements CoreServices {
.doBackground(unreadCount: totalCount ?? 0);
}
}
+
+ @override
+ setDeviceType(DeviceType deviceType) {
+ TUIKitScreenUtils.deviceType = deviceType;
+ }
}
diff --git a/lib/ui/controller/tim_uikit_chat_controller.dart b/lib/ui/controller/tim_uikit_chat_controller.dart
index 054ca99..43cd351 100644
--- a/lib/ui/controller/tim_uikit_chat_controller.dart
+++ b/lib/ui/controller/tim_uikit_chat_controller.dart
@@ -138,8 +138,8 @@ class TIMUIKitChatController {
assert((groupID == null) != (userID == null));
assert(groupID != null || convType != ConvType.group);
assert(userID != null || convType != ConvType.c2c);
- if (isNavigateToMessageListBottom) {
- scrollController?.animateTo(
+ if (isNavigateToMessageListBottom && scrollController != null) {
+ scrollController!.animateTo(
scrollController!.position.minScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.ease,
@@ -159,7 +159,7 @@ class TIMUIKitChatController {
offlinePushInfo: offlinePushInfo);
} else if (model != null) {
/// Sends a message to the current conversation specified on `TIMUIKitChat`. 发送到 `TIMUIKitChat` 中指定的当前对话。
- if (isNavigateToMessageListBottom) {
+ if (isNavigateToMessageListBottom && scrollController != null) {
scrollController?.animateTo(
scrollController!.position.minScrollExtent,
duration: const Duration(milliseconds: 200),
diff --git a/lib/ui/utils/screen_utils.dart b/lib/ui/utils/screen_utils.dart
index 5416c25..b137c46 100644
--- a/lib/ui/utils/screen_utils.dart
+++ b/lib/ui/utils/screen_utils.dart
@@ -28,8 +28,9 @@ class TUIKitScreenUtils {
final diagonalInInches =
sqrt(pow(screenWidth, 2) + pow(screenHeight, 2)) / 96.0;
+ print("diagonalInInches $diagonalInInches");
- deviceType = diagonalInInches < 8.0 ? DeviceType.Mobile : DeviceType.Desktop;
+ deviceType = diagonalInInches < 11.0 ? DeviceType.Mobile : DeviceType.Desktop;
return deviceType ?? DeviceType.Mobile;
}else{
if(context != null){
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart
index 2410199..880c882 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart
@@ -38,33 +38,34 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import '../TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.dart';
typedef MessageRowBuilder = Widget? Function(
- /// current message
- V2TimMessage message,
- /// the message widget for current message, build by your custom builder or our default builder
- Widget messageWidget,
+ /// current message
+ V2TimMessage message,
- /// scroll to the specific message, it will shows in the screen center, and call isNeedShowJumpStatus if necessary
- Function onScrollToIndex,
+ /// the message widget for current message, build by your custom builder or our default builder
+ Widget messageWidget,
- /// if current message been called to jumped by other message
- bool isNeedShowJumpStatus,
+ /// scroll to the specific message, it will shows in the screen center, and call isNeedShowJumpStatus if necessary
+ Function onScrollToIndex,
- /// clear the been jumped status, recommend to execute after get 'isNeedShowJumpStatus'
- VoidCallback clearJumpStatus,
+ /// if current message been called to jumped by other message
+ bool isNeedShowJumpStatus,
- /// scroll to specific message, it will shows on the screen top, without the call isNeedShowJumpStatus
- Function onScrollToIndexBegin,
-);
+ /// clear the been jumped status, recommend to execute after get 'isNeedShowJumpStatus'
+ VoidCallback clearJumpStatus,
+
+ /// scroll to specific message, it will shows on the screen top, without the call isNeedShowJumpStatus
+ Function onScrollToIndexBegin,
+ );
typedef MessageNickNameBuilder = Widget Function(
BuildContext context, V2TimMessage message, TUIChatSeparateViewModel model);
typedef MessageItemContent = Widget? Function(
- V2TimMessage message,
- bool isShowJump,
- VoidCallback clearJump,
-);
+ V2TimMessage message,
+ bool isShowJump,
+ VoidCallback clearJump,
+ );
class MessageHoverControlItem {
String name;
@@ -140,11 +141,10 @@ class MessageToolTipItem {
final String iconImageAsset;
final VoidCallback onClick;
- MessageToolTipItem(
- {required this.label,
- required this.id,
- required this.iconImageAsset,
- required this.onClick});
+ MessageToolTipItem({required this.label,
+ required this.id,
+ required this.iconImageAsset,
+ required this.onClick});
}
class ToolTipsConfig {
@@ -177,18 +177,16 @@ class ToolTipsConfig {
List Function(
V2TimMessage message, Function() closeTooltip)? additionalMessageToolTips;
- ToolTipsConfig(
- {this.showDeleteMessage = true,
- this.showMultipleChoiceMessage = true,
- this.showRecallMessage = true,
- this.showReplyMessage = true,
- this.showTranslation = true,
- this.showCopyMessage = true,
- this.showForwardMessage = true,
- this.additionalMessageToolTips,
- @Deprecated(
- "Please use `additionalMessageToolTips` instead. You are now only expected to specify the data, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.")
- this.additionalItemBuilder});
+ ToolTipsConfig({this.showDeleteMessage = true,
+ this.showMultipleChoiceMessage = true,
+ this.showRecallMessage = true,
+ this.showReplyMessage = true,
+ this.showTranslation = true,
+ this.showCopyMessage = true,
+ this.showForwardMessage = true,
+ this.additionalMessageToolTips,
+ @Deprecated(
+ "Please use `additionalMessageToolTips` instead. You are now only expected to specify the data, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.") this.additionalItemBuilder});
}
class TIMUIKitHistoryMessageListItem extends StatefulWidget {
@@ -197,11 +195,11 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// tap remote user avatar callback function
final void Function(String userID, TapDownDetails tapDetails)?
- onTapForOthersPortrait;
+ onTapForOthersPortrait;
/// secondary tap remote user avatar callback function
final void Function(String userID, TapDownDetails tapDetails)?
- onSecondaryTapForOthersPortrait;
+ onSecondaryTapForOthersPortrait;
/// the function use for reply message, when click replied message can scroll to it.
final Function? onScrollToIndex;
@@ -211,7 +209,7 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// the callback for long press event, except myself avatar
final Function(String? userId, String? nickName)?
- onLongPressForOthersHeadPortrait;
+ onLongPressForOthersHeadPortrait;
/// message item builder, works for customize all message types and row layout.
final MessageItemBuilder? messageItemBuilder;
@@ -260,18 +258,18 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
/// avatar builder
final Widget Function(BuildContext context, V2TimMessage message)?
- userAvatarBuilder;
+ userAvatarBuilder;
/// theme info for message and avatar
final MessageThemeData? themeData;
/// builder for nick name row
final Widget Function(BuildContext context, V2TimMessage message)?
- topRowBuilder;
+ topRowBuilder;
/// builder for bottom raw which under message content
final Widget Function(BuildContext context, V2TimMessage message)?
- bottomRowBuilder;
+ bottomRowBuilder;
// open MessageReaction
final bool? isUseMessageReaction;
@@ -281,37 +279,35 @@ class TIMUIKitHistoryMessageListItem extends StatefulWidget {
final List customEmojiStickerList;
- const TIMUIKitHistoryMessageListItem(
- {Key? key,
- required this.message,
- @Deprecated(
- "Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
- this.showNickName = false,
- this.onScrollToIndex,
- this.onScrollToIndexBegin,
- this.onTapForOthersPortrait,
- this.messageItemBuilder,
- this.onLongPressForOthersHeadPortrait,
- this.showAvatar = true,
- this.showMessageSending = true,
- this.showMessageReadRecipt = true,
- this.allowLongPress = true,
- this.toolTipsConfig,
- this.onLongPress,
- this.showGroupMessageReadRecipt = false,
- this.allowAtUserWhenReply = true,
- this.allowAvatarTap = true,
- this.userAvatarBuilder,
- this.themeData,
- this.padding,
- this.textPadding,
- this.topRowBuilder,
- this.isUseMessageReaction,
- this.bottomRowBuilder,
- this.isUseDefaultEmoji = false,
- this.customEmojiStickerList = const [],
- this.textFieldController,
- this.onSecondaryTapForOthersPortrait})
+ const TIMUIKitHistoryMessageListItem({Key? key,
+ required this.message,
+ @Deprecated(
+ "Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead") this.showNickName = false,
+ this.onScrollToIndex,
+ this.onScrollToIndexBegin,
+ this.onTapForOthersPortrait,
+ this.messageItemBuilder,
+ this.onLongPressForOthersHeadPortrait,
+ this.showAvatar = true,
+ this.showMessageSending = true,
+ this.showMessageReadRecipt = true,
+ this.allowLongPress = true,
+ this.toolTipsConfig,
+ this.onLongPress,
+ this.showGroupMessageReadRecipt = false,
+ this.allowAtUserWhenReply = true,
+ this.allowAvatarTap = true,
+ this.userAvatarBuilder,
+ this.themeData,
+ this.padding,
+ this.textPadding,
+ this.topRowBuilder,
+ this.isUseMessageReaction,
+ this.bottomRowBuilder,
+ this.isUseDefaultEmoji = false,
+ this.customEmojiStickerList = const [],
+ this.textFieldController,
+ this.onSecondaryTapForOthersPortrait})
: super(key: key);
@override
@@ -361,7 +357,7 @@ class _TIMUIKItHistoryMessageListItemState
// ignore: unused_field
final MessageService _messageService = serviceLocator();
final TUISelfInfoViewModel selfInfoModel =
- serviceLocator();
+ serviceLocator();
final TUIThemeViewModel themeModel = serviceLocator();
// bool isChecked = false;
@@ -395,8 +391,8 @@ class _TIMUIKItHistoryMessageListItemState
return false;
}
- Widget _messageItemBuilder(
- V2TimMessage messageItem, TUIChatSeparateViewModel model) {
+ Widget _messageItemBuilder(V2TimMessage messageItem,
+ TUIChatSeparateViewModel model) {
final msgType = messageItem.elemType;
final isShowJump = (model.jumpMsgID == messageItem.msgID) &&
(messageItem.msgID?.isNotEmpty ?? false);
@@ -411,13 +407,13 @@ class _TIMUIKItHistoryMessageListItemState
switch (msgType) {
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
final customWidget =
- messageItemBuilder?.customMessageItemBuilder != null
- ? messageItemBuilder!.customMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
- : null;
+ messageItemBuilder?.customMessageItemBuilder != null
+ ? messageItemBuilder!.customMessageItemBuilder!(
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
+ : null;
return customWidget ??
TIMUIKitCustomElem(
message: messageItem,
@@ -432,10 +428,10 @@ class _TIMUIKItHistoryMessageListItemState
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
final customWidget = messageItemBuilder?.soundMessageItemBuilder != null
? messageItemBuilder!.soundMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
: null;
return customWidget ??
TIMUIKitSoundElem(
@@ -456,13 +452,13 @@ class _TIMUIKItHistoryMessageListItemState
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
if (isReplyMessage(messageItem)) {
final customWidget =
- messageItemBuilder?.textReplyMessageItemBuilder != null
- ? messageItemBuilder!.textReplyMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
- : null;
+ messageItemBuilder?.textReplyMessageItemBuilder != null
+ ? messageItemBuilder!.textReplyMessageItemBuilder!(
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
+ : null;
return customWidget ??
TIMUIKitReplyElem(
message: messageItem,
@@ -481,10 +477,10 @@ class _TIMUIKItHistoryMessageListItemState
}
final customWidget = messageItemBuilder?.textMessageItemBuilder != null
? messageItemBuilder!.textMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
: null;
return customWidget ??
TIMUIKitTextElem(
@@ -504,10 +500,10 @@ class _TIMUIKItHistoryMessageListItemState
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
final customWidget = messageItemBuilder?.faceMessageItemBuilder != null
? messageItemBuilder!.faceMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
: null;
return customWidget ??
TIMUIKitFaceElem(
@@ -521,10 +517,10 @@ class _TIMUIKItHistoryMessageListItemState
case MessageElemType.V2TIM_ELEM_TYPE_FILE:
final customWidget = messageItemBuilder?.fileMessageItemBuilder != null
? messageItemBuilder!.fileMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
: null;
return customWidget ??
TIMUIKitFileElem(
@@ -539,21 +535,21 @@ class _TIMUIKItHistoryMessageListItemState
);
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
final customWidget =
- messageItemBuilder?.groupTipsMessageItemBuilder != null
- ? messageItemBuilder!.groupTipsMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
- : null;
+ messageItemBuilder?.groupTipsMessageItemBuilder != null
+ ? messageItemBuilder!.groupTipsMessageItemBuilder!(
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
+ : null;
return customWidget ?? Text(TIM_t("[群系统消息]"));
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
final customWidget = messageItemBuilder?.imageMessageItemBuilder != null
? messageItemBuilder!.imageMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
: null;
return customWidget ??
TIMUIKitImageElem(
@@ -567,10 +563,10 @@ class _TIMUIKItHistoryMessageListItemState
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
final customWidget = messageItemBuilder?.videoMessageItemBuilder != null
? messageItemBuilder!.videoMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
: null;
return customWidget ??
TIMUIKitVideoElem(
@@ -582,23 +578,23 @@ class _TIMUIKItHistoryMessageListItemState
);
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
final customWidget =
- messageItemBuilder?.locationMessageItemBuilder != null
- ? messageItemBuilder!.locationMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
- : null;
+ messageItemBuilder?.locationMessageItemBuilder != null
+ ? messageItemBuilder!.locationMessageItemBuilder!(
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
+ : null;
return customWidget ?? Text(TIM_t("[位置]"));
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
final customWidget =
- messageItemBuilder?.mergerMessageItemBuilder != null
- ? messageItemBuilder!.mergerMessageItemBuilder!(
- messageItem,
- isShowJump,
- () => model.jumpMsgID = "",
- )
- : null;
+ messageItemBuilder?.mergerMessageItemBuilder != null
+ ? messageItemBuilder!.mergerMessageItemBuilder!(
+ messageItem,
+ isShowJump,
+ () => model.jumpMsgID = "",
+ )
+ : null;
return customWidget ??
TIMUIKitMergerElem(
messageItemBuilder: messageItemBuilder,
@@ -649,13 +645,14 @@ class _TIMUIKItHistoryMessageListItemState
margin: const EdgeInsets.symmetric(vertical: 20),
alignment: Alignment.center,
child: Text(
- TIM_t_para("{{option2}}撤回了一条消息", "$option2撤回了一条消息")(option2: option2),
+ TIM_t_para("{{option2}}撤回了一条消息", "$option2撤回了一条消息")(
+ option2: option2),
style: TextStyle(color: theme.weakTextColor, fontSize: 12),
));
}
- Widget _timeDividerBuilder(
- theme, int timeStamp, TUIChatSeparateViewModel model) {
+ Widget _timeDividerBuilder(theme, int timeStamp,
+ TUIChatSeparateViewModel model) {
return Container(
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(vertical: 20),
@@ -686,11 +683,11 @@ class _TIMUIKItHistoryMessageListItemState
width: 100,
child: Container(
decoration: BoxDecoration(
- gradient: LinearGradient(colors: [
- const Color(0x00C0E1FF),
- theme.primaryColor ?? CommonColor.lightPrimaryColor
- ]),
- )),
+ gradient: LinearGradient(colors: [
+ const Color(0x00C0E1FF),
+ theme.primaryColor ?? CommonColor.lightPrimaryColor
+ ]),
+ )),
),
),
Text(
@@ -709,11 +706,11 @@ class _TIMUIKItHistoryMessageListItemState
width: 100,
child: Container(
decoration: BoxDecoration(
- gradient: LinearGradient(colors: [
- theme.primaryColor ?? CommonColor.primaryColor,
- const Color(0x00C0E1FF),
- ]),
- )),
+ gradient: LinearGradient(colors: [
+ theme.primaryColor ?? CommonColor.primaryColor,
+ const Color(0x00C0E1FF),
+ ]),
+ )),
),
),
],
@@ -722,48 +719,63 @@ class _TIMUIKItHistoryMessageListItemState
}
bool isRevocable(int timestamp) =>
- (DateTime.now().millisecondsSinceEpoch / 1000).ceil() - timestamp < 120;
+ (DateTime
+ .now()
+ .millisecondsSinceEpoch / 1000).ceil() - timestamp < 120;
- _onOpenToolTip(
- c,
- V2TimMessage message,
- TUIChatSeparateViewModel model,
- TUITheme theme,
- TapDownDetails? details,
- bool? isFromWideTooltip,
- bool? isShowMoreSticker,
- ) {
+ _onOpenToolTip(c,
+ V2TimMessage message,
+ TUIChatSeparateViewModel model,
+ TUITheme theme,
+ TapDownDetails? details,
+ bool? isFromWideTooltip,
+ bool? isShowMoreSticker,) {
if (tooltip != null && tooltip!.isOpen) {
tooltip!.close();
return;
}
tooltip = null;
- final screenHeight = MediaQuery.of(context).size.height;
- final screenWidth = MediaQuery.of(context).size.width;
+ final screenHeight = MediaQuery
+ .of(context)
+ .size
+ .height;
+ final screenWidth = MediaQuery
+ .of(context)
+ .size
+ .width;
final isLongMessage =
context.size!.height + 350 > screenHeight && PlatformUtils().isMobile;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final tapDetails =
- (isDesktopScreen || isLongMessage) ? (details ?? _tapDetails) : details;
+ (isDesktopScreen || isLongMessage) ? (details ?? _tapDetails) : details;
final isSelf = message.isSelf ?? true;
final targetWidth =
- min(MediaQuery.of(context).size.width * 0.84, 350).toDouble();
+ min(MediaQuery
+ .of(context)
+ .size
+ .width * 0.84, 350).toDouble();
final double dx = !isSelf
? min(tapDetails?.globalPosition.dx ?? targetWidth,
- screenWidth - targetWidth)
+ screenWidth - targetWidth)
: max(tapDetails?.globalPosition.dx ?? targetWidth, targetWidth)
- .toDouble();
+ .toDouble();
final double dy = min(
- tapDetails?.globalPosition.dy ?? MediaQuery.of(context).size.height,
- MediaQuery.of(context).size.height - 320)
+ tapDetails?.globalPosition.dy ?? MediaQuery
+ .of(context)
+ .size
+ .height,
+ MediaQuery
+ .of(context)
+ .size
+ .height - 320)
.toDouble();
final finalTapDetail = tapDetails != null
? TapDownDetails(
- globalPosition: Offset(dx, dy),
- )
+ globalPosition: Offset(dx, dy),
+ )
: null;
initTools(
@@ -792,14 +804,13 @@ class _TIMUIKItHistoryMessageListItemState
});
}
- initTools(
- {BuildContext? context,
- bool isLongMessage = false,
- required TUIChatSeparateViewModel model,
- TUITheme? theme,
- bool? isShowMoreSticker,
- TapDownDetails? details,
- bool? isFromWideToolTip}) {
+ initTools({BuildContext? context,
+ bool isLongMessage = false,
+ required TUIChatSeparateViewModel model,
+ TUITheme? theme,
+ bool? isShowMoreSticker,
+ TapDownDetails? details,
+ bool? isFromWideToolTip}) {
final isUseMessageReaction = widget.message.elemType == 2
? false
: model.chatConfig.isUseMessageReaction;
@@ -818,7 +829,10 @@ class _TIMUIKItHistoryMessageListItemState
if (context != null) {
RenderBox? box = _key.currentContext?.findRenderObject() as RenderBox?;
if (details != null && box != null) {
- double screenWidth = MediaQuery.of(context).size.width;
+ double screenWidth = MediaQuery
+ .of(context)
+ .size
+ .width;
final mousePosition = details.globalPosition;
hasArrow = isDesktopScreen ? false : true;
arrowTipDistance = 0;
@@ -832,8 +846,14 @@ class _TIMUIKItHistoryMessageListItemState
}
} else {
if (box != null) {
- double screenWidth = MediaQuery.of(context).size.width;
- double viewInsetsBottom = MediaQuery.of(context).viewInsets.bottom;
+ double screenWidth = MediaQuery
+ .of(context)
+ .size
+ .width;
+ double viewInsetsBottom = MediaQuery
+ .of(context)
+ .viewInsets
+ .bottom;
Offset offset = box.localToGlobal(Offset.zero);
double boxWidth = box.size.width;
if (isSelf) {
@@ -880,7 +900,7 @@ class _TIMUIKItHistoryMessageListItemState
message: widget.message,
allowAtUserWhenReply: widget.allowAtUserWhenReply,
onLongPressForOthersHeadPortrait:
- widget.onLongPressForOthersHeadPortrait,
+ widget.onLongPressForOthersHeadPortrait,
selectEmojiPanelPosition: selectEmojiPanelPosition,
onCloseTooltip: () => tooltip?.close(),
onSelectSticker: (int value) {
@@ -966,7 +986,13 @@ class _TIMUIKItHistoryMessageListItemState
),
onClick: (details) {
_onOpenToolTip(
- context, widget.message, model, theme, details, true, true);
+ context,
+ widget.message,
+ model,
+ theme,
+ details,
+ true,
+ true);
},
),
if (widget.toolTipsConfig?.showReplyMessage ?? true)
@@ -1009,12 +1035,19 @@ class _TIMUIKItHistoryMessageListItemState
context: context,
title: TIM_t("转发"),
submitWidget: Text(TIM_t("发送")),
- width: MediaQuery.of(context).size.width * 0.5,
- height: MediaQuery.of(context).size.height * 0.8,
+ width: MediaQuery
+ .of(context)
+ .size
+ .width * 0.5,
+ height: MediaQuery
+ .of(context)
+ .size
+ .height * 0.8,
onSubmit: () {
forwardMessageScreenKey.currentState?.handleForwardMessage();
},
- child: (onClose) => Container(
+ child: (onClose) =>
+ Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: ForwardMessageScreen(
conversationType: ConvType.c2c,
@@ -1035,7 +1068,13 @@ class _TIMUIKItHistoryMessageListItemState
),
onClick: (details) {
_onOpenToolTip(
- context, widget.message, model, theme, details, true, false);
+ context,
+ widget.message,
+ model,
+ theme,
+ details,
+ true,
+ false);
},
),
...?model.chatConfig.additionalDesktopMessageHoverBarItem
@@ -1053,8 +1092,7 @@ class _TIMUIKItHistoryMessageListItemState
context);
}
- Widget renderHoverTipAndReadStatus(
- TUIChatSeparateViewModel model,
+ Widget renderHoverTipAndReadStatus(TUIChatSeparateViewModel model,
bool isSelf,
V2TimMessage message,
bool isPeerRead,
@@ -1066,7 +1104,7 @@ class _TIMUIKItHistoryMessageListItemState
? getMessageHoverControlBar(model, theme)
: [];
final lastItemName =
- wideHoverTipList.isNotEmpty ? wideHoverTipList.last.name : "";
+ wideHoverTipList.isNotEmpty ? wideHoverTipList.last.name : "";
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -1082,35 +1120,38 @@ class _TIMUIKItHistoryMessageListItemState
margin: const EdgeInsets.symmetric(horizontal: 4),
child: Row(
children: wideHoverTipList
- .map((e) => Tooltip(
- message: e.name,
- preferBelow: false,
- textStyle: TextStyle(fontSize: 12, color: theme.white),
- child: Row(
- children: [
- InkWell(
- onTapDown: e.onClick,
- child: SizedBox(
- width: 22,
- height: 22,
- child: e.icon,
- ),
- ),
- if (lastItemName != e.name)
- SizedBox(
- width: 1,
- height: 22,
- child: Container(
- color: theme.weakDividerColor,
- ),
- )
- ],
+ .map((e) =>
+ Tooltip(
+ message: e.name,
+ preferBelow: false,
+ textStyle: TextStyle(fontSize: 12, color: theme.white),
+ child: Row(
+ children: [
+ InkWell(
+ onTapDown: e.onClick,
+ child: SizedBox(
+ width: 22,
+ height: 22,
+ child: e.icon,
+ ),
),
- ))
+ if (lastItemName != e.name)
+ SizedBox(
+ width: 1,
+ height: 22,
+ child: Container(
+ color: theme.weakDividerColor,
+ ),
+ )
+ ],
+ ),
+ ))
.toList(),
),
),
- if (!isDesktopScreen || !isShowWideToolTip)
+ if (!isDesktopScreen ||
+ (model.chatConfig.isUseMessageHoverBarOnDesktop &&
+ !isShowWideToolTip))
const SizedBox(
height: 24,
),
@@ -1161,9 +1202,9 @@ class _TIMUIKItHistoryMessageListItemState
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUIChatSeparateViewModel model =
- Provider.of(context);
+ Provider.of(context);
final isDownloadWaiting = context.select(
- (value) => value.isWaiting(widget.message.msgID ?? ""));
+ (value) => value.isWaiting(widget.message.msgID ?? ""));
final TUITheme theme = value.theme;
final message = widget.message;
final msgType = message.elemType;
@@ -1199,7 +1240,7 @@ class _TIMUIKItHistoryMessageListItemState
if (isGroupTipsMsg) {
if (widget.messageItemBuilder?.groupTipsMessageItemBuilder != null) {
final groupTipsMessage =
- widget.messageItemBuilder!.groupTipsMessageItemBuilder!(
+ widget.messageItemBuilder!.groupTipsMessageItemBuilder!(
message,
(model.jumpMsgID == message.msgID),
clearJump,
@@ -1211,7 +1252,7 @@ class _TIMUIKItHistoryMessageListItemState
if (isRevokedMsg) {
final displayName =
- isSelf ? TIM_t("您") : message.nickName ?? message.sender;
+ isSelf ? TIM_t("您") : message.nickName ?? message.sender;
return isSelf && isRevokeEditable && isRevocable(message.timestamp!)
? _selfRevokeEditMessageBuilder(theme, model)
: _revokedMessageBuilder(theme, displayName ?? "");
@@ -1233,321 +1274,334 @@ class _TIMUIKItHistoryMessageListItemState
}
return LayoutBuilder(
- builder: (context, constraints) => Container(
- margin: widget.padding ?? const EdgeInsets.only(bottom: 20),
- child: Row(
- key: _key,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- if (model.isMultiSelect)
- Container(
- margin:
+ builder: (context, constraints) =>
+ Container(
+ margin: widget.padding ?? const EdgeInsets.only(bottom: 20),
+ child: Row(
+ key: _key,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (model.isMultiSelect)
+ Container(
+ margin:
EdgeInsets.only(right: 12, top: 10, left: isSelf ? 16 : 0),
- child: CheckBoxButton(
- isChecked: model.multiSelectedMessageList.contains(message),
- onChanged: (value) {
- if (value) {
- model.addToMultiSelectedMessageList(message);
- } else {
- model.removeFromMultiSelectedMessageList(message);
- }
- },
- ),
- ),
- Expanded(
- child: MouseRegion(
- onEnter: (_) {
- if (isDesktopScreen) {
- setState(() {
- isShowWideToolTip = true;
- });
- }
- },
- onExit: (_) {
- if (isDesktopScreen) {
- setState(() {
- isShowWideToolTip = false;
- });
- }
- },
- child: GestureDetector(
- 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);
+ child: CheckBoxButton(
+ isChecked: model.multiSelectedMessageList.contains(
+ message),
+ onChanged: (value) {
+ if (value) {
+ model.addToMultiSelectedMessageList(message);
+ } else {
+ model.removeFromMultiSelectedMessageList(message);
+ }
+ },
+ ),
+ ),
+ Expanded(
+ child: MouseRegion(
+ onEnter: (_) {
+ if (isDesktopScreen &&
+ model.chatConfig.isUseMessageHoverBarOnDesktop) {
+ setState(() {
+ isShowWideToolTip = true;
+ });
}
- }
- },
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment: isSelf
- ? MainAxisAlignment.end
- : MainAxisAlignment.start,
- children: [
- if (!isSelf && widget.showAvatar)
- InkWell(
- onLongPress: () {
- if (widget.onLongPressForOthersHeadPortrait !=
- null) {}
- if (model.chatConfig.isAllowLongPressAvatarToAt) {
- widget.onLongPressForOthersHeadPortrait!(
- message.sender, message.nickName);
- }
- },
- onTapDown: isDesktopScreen
- ? (details) {
- if (widget.onTapForOthersPortrait != null &&
- widget.allowAvatarTap) {
- widget.onTapForOthersPortrait!(
- message.sender ?? "", details);
- }
+ },
+ onExit: (_) {
+ if (isDesktopScreen &&
+ model.chatConfig.isUseMessageHoverBarOnDesktop) {
+ setState(() {
+ isShowWideToolTip = false;
+ });
+ }
+ },
+ child: GestureDetector(
+ 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);
+ }
+ } else {
+ return;
+ }
+ },
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: isSelf
+ ? MainAxisAlignment.end
+ : MainAxisAlignment.start,
+ children: [
+ if (!isSelf && widget.showAvatar)
+ GestureDetector(
+ onLongPress: () {
+ if (widget.onLongPressForOthersHeadPortrait !=
+ null) {}
+ if (model.chatConfig
+ .isAllowLongPressAvatarToAt) {
+ widget.onLongPressForOthersHeadPortrait!(
+ message.sender, message.nickName);
}
- : null,
- onTap: isDesktopScreen
- ? null
- : () {
- if (widget.onTapForOthersPortrait != null &&
- widget.allowAvatarTap) {
- widget.onTapForOthersPortrait!(
- message.sender ?? "", TapDownDetails());
- }
- },
- onSecondaryTap: isDesktopScreen
- ? null
- : () {
- if (widget.onSecondaryTapForOthersPortrait !=
- null &&
- widget.allowAvatarTap) {
- widget.onSecondaryTapForOthersPortrait!(
- message.sender ?? "", TapDownDetails());
- }
- },
- onSecondaryTapDown: isDesktopScreen
- ? (details) {
- if (widget.onSecondaryTapForOthersPortrait !=
- null &&
- widget.allowAvatarTap) {
- widget.onSecondaryTapForOthersPortrait!(
- message.sender ?? "", details);
- }
+ },
+ onTapDown: isDesktopScreen
+ ? (details) {
+ if (widget.onTapForOthersPortrait != null &&
+ widget.allowAvatarTap) {
+ widget.onTapForOthersPortrait!(
+ message.sender ?? "", details);
}
- : null,
- child: widget.userAvatarBuilder != null
- ? widget.userAvatarBuilder!(context, message)
- : Container(
- margin: (isSelf && isShowNickNameForSelf) ||
- (!isSelf && isShowNickNameForOthers)
- ? const EdgeInsets.only(top: 2)
- : null,
- child: SizedBox(
- width: 40,
- height: 40,
- child: Avatar(
- faceUrl: message.faceUrl ?? "",
- showName:
- MessageUtils.getDisplayName(message),
- ),
+ }
+ : null,
+ onTap: isDesktopScreen
+ ? null
+ : () {
+ if (widget.onTapForOthersPortrait != null &&
+ widget.allowAvatarTap) {
+ widget.onTapForOthersPortrait!(
+ message.sender ?? "", TapDownDetails());
+ }
+ },
+ onSecondaryTap: isDesktopScreen
+ ? null
+ : () {
+ if (widget.onSecondaryTapForOthersPortrait !=
+ null &&
+ widget.allowAvatarTap) {
+ widget.onSecondaryTapForOthersPortrait!(
+ message.sender ?? "", TapDownDetails());
+ }
+ },
+ onSecondaryTapDown: isDesktopScreen
+ ? (details) {
+ if (widget.onSecondaryTapForOthersPortrait !=
+ null &&
+ widget.allowAvatarTap) {
+ widget.onSecondaryTapForOthersPortrait!(
+ message.sender ?? "", details);
+ }
+ }
+ : null,
+ child: widget.userAvatarBuilder != null
+ ? widget.userAvatarBuilder!(context, message)
+ : Container(
+ margin: (isSelf && isShowNickNameForSelf) ||
+ (!isSelf && isShowNickNameForOthers)
+ ? const EdgeInsets.only(top: 2)
+ : null,
+ child: SizedBox(
+ width: 40,
+ height: 40,
+ child: Avatar(
+ faceUrl: message.faceUrl ?? "",
+ showName:
+ MessageUtils.getDisplayName(message),
),
),
- ),
- if (isSelf &&
- widget.message.elemType == 6 &&
- isDownloadWaiting)
- Container(
- margin: const EdgeInsets.only(top: 2),
- child: LoadingAnimationWidget.threeArchedCircle(
- color: theme.weakTextColor ?? Colors.grey,
- size: 20,
- ),
- ),
- Container(
- margin: widget.showAvatar
- ? (isSelf
+ ),
+ ),
+ if (isSelf &&
+ widget.message.elemType == 6 &&
+ isDownloadWaiting)
+ Container(
+ margin: const EdgeInsets.only(top: 2),
+ child: LoadingAnimationWidget.threeArchedCircle(
+ color: theme.weakTextColor ?? Colors.grey,
+ size: 20,
+ ),
+ ),
+ Container(
+ margin: widget.showAvatar
+ ? (isSelf
? const EdgeInsets.only(right: 13)
: const EdgeInsets.only(left: 13))
- : null,
- child: Column(
- crossAxisAlignment: isSelf
- ? CrossAxisAlignment.end
- : CrossAxisAlignment.start,
- children: [
- if ((isSelf && isShowNickNameForSelf) ||
- (!isSelf && isShowNickNameForOthers))
- widget.topRowBuilder != null
- ? widget.topRowBuilder!(context, message)
- : Container(
+ : null,
+ child: Column(
+ crossAxisAlignment: isSelf
+ ? CrossAxisAlignment.end
+ : CrossAxisAlignment.start,
+ children: [
+ if ((isSelf && isShowNickNameForSelf) ||
+ (!isSelf && isShowNickNameForOthers))
+ widget.topRowBuilder != null
+ ? widget.topRowBuilder!(context, message)
+ : Container(
margin: const EdgeInsets.only(bottom: 4),
child: ConstrainedBox(
constraints: BoxConstraints(
- maxWidth: MediaQuery.of(context)
- .size
- .width /
+ maxWidth: MediaQuery
+ .of(context)
+ .size
+ .width /
1.7),
child: Text(
MessageUtils.getDisplayName(message),
overflow: TextOverflow.ellipsis,
style: widget.themeData
- ?.nickNameTextStyle ??
+ ?.nickNameTextStyle ??
TextStyle(
fontSize: 12,
color: theme.weakTextColor),
),
)),
- Row(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- if (isSelf)
- renderHoverTipAndReadStatus(
- model,
- isSelf,
- message,
- isPeerRead,
- theme,
- isDownloadWaiting),
- Container(
- constraints: BoxConstraints(
- maxWidth: constraints.maxWidth * 0.77,
- ),
- child: Builder(builder: (context) {
- return Column(
- crossAxisAlignment:
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ if (isSelf)
+ renderHoverTipAndReadStatus(
+ model,
+ isSelf,
+ message,
+ isPeerRead,
+ theme,
+ isDownloadWaiting),
+ Container(
+ constraints: BoxConstraints(
+ maxWidth: constraints.maxWidth * 0.77,
+ ),
+ child: Builder(builder: (context) {
+ return Column(
+ crossAxisAlignment:
(message.isSelf ?? true)
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
- children: [
- GestureDetector(
- child: IgnorePointer(
- ignoring: model.isMultiSelect,
- child: _getMessageItemBuilder(
- message,
- message.status,
- model)),
- onSecondaryTapDown: (details) {
- if (widget.onLongPress != null) {
- widget.onLongPress!(
- context, message);
- return;
- }
- if (!PlatformUtils().isMobile) {
- if (widget.allowLongPress) {
- _onOpenToolTip(
- context,
- message,
- model,
- theme,
- details,
- false,
- false);
- }
- }
- },
- onLongPress: () {
- if (widget.onLongPress != null) {
- widget.onLongPress!(
- context, message);
- return;
- }
- if (widget.allowLongPress &&
- PlatformUtils().isMobile) {
- _onOpenToolTip(
- context,
- message,
- model,
- theme,
- null,
- false,
- false);
- }
- },
- onTapDown: (details) {
- _tapDetails = details;
- },
- ),
- TIMUIKitTextTranslationElem(
- message: message,
- isUseDefaultEmoji:
+ children: [
+ GestureDetector(
+ child: IgnorePointer(
+ ignoring: model.isMultiSelect,
+ child: _getMessageItemBuilder(
+ message,
+ message.status,
+ model)),
+ onSecondaryTapDown: (details) {
+ if (widget.onLongPress !=
+ null) {
+ widget.onLongPress!(
+ context, message);
+ return;
+ }
+ if (!PlatformUtils().isMobile) {
+ if (widget.allowLongPress) {
+ _onOpenToolTip(
+ context,
+ message,
+ model,
+ theme,
+ details,
+ false,
+ false);
+ }
+ }
+ },
+ onLongPress: () {
+ if (widget.onLongPress !=
+ null) {
+ widget.onLongPress!(
+ context, message);
+ return;
+ }
+ if (widget.allowLongPress &&
+ !isDesktopScreen) {
+ _onOpenToolTip(
+ context,
+ message,
+ model,
+ theme,
+ null,
+ false,
+ false);
+ }
+ },
+ onTapDown: (details) {
+ _tapDetails = details;
+ },
+ ),
+ TIMUIKitTextTranslationElem(
+ message: message,
+ isUseDefaultEmoji:
widget.isUseDefaultEmoji,
- customEmojiStickerList:
+ customEmojiStickerList:
widget.customEmojiStickerList,
- isFromSelf: message.isSelf ?? true,
- isShowJump: false,
- clearJump: () {},
- chatModel: model)
- ],
- );
- }),
+ isFromSelf: message.isSelf ??
+ true,
+ isShowJump: false,
+ clearJump: () {},
+ chatModel: model)
+ ],
+ );
+ }),
+ ),
+ if (!isSelf &&
+ message.elemType ==
+ MessageElemType
+ .V2TIM_ELEM_TYPE_SOUND &&
+ message.localCustomInt != null &&
+ message.localCustomInt !=
+ HistoryMessageDartConstant.read)
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 5, bottom: 12),
+ child: Icon(Icons.circle,
+ color: theme.cautionColor,
+ size: 10)),
+ if (!isSelf)
+ renderHoverTipAndReadStatus(
+ model,
+ isSelf,
+ message,
+ isPeerRead,
+ theme,
+ isDownloadWaiting),
+ ],
),
- if (!isSelf &&
- message.elemType ==
- MessageElemType.V2TIM_ELEM_TYPE_SOUND &&
- message.localCustomInt != null &&
- message.localCustomInt !=
- HistoryMessageDartConstant.read)
- Padding(
- padding: const EdgeInsets.only(
- left: 5, bottom: 12),
- child: Icon(Icons.circle,
- color: theme.cautionColor, size: 10)),
- if (!isSelf)
- renderHoverTipAndReadStatus(
- model,
- isSelf,
- message,
- isPeerRead,
- theme,
- isDownloadWaiting),
+ if (widget.bottomRowBuilder != null)
+ widget.bottomRowBuilder!(context, message)
],
),
- if (widget.bottomRowBuilder != null)
- widget.bottomRowBuilder!(context, message)
- ],
- ),
- ),
- if (!isSelf &&
- widget.message.elemType == 6 &&
- isDownloadWaiting)
- Container(
- margin: const EdgeInsets.only(top: 24, left: 6),
- child: LoadingAnimationWidget.threeArchedCircle(
- color: theme.weakTextColor ?? Colors.grey,
- size: 20,
),
- ),
- if (isSelf && widget.showAvatar)
- widget.userAvatarBuilder != null
- ? widget.userAvatarBuilder!(context, message)
- : SizedBox(
- width: 40,
- height: 40,
- child: InkWell(
- onTapDown: (details) {
- if (widget.onTapForOthersPortrait != null &&
- widget.allowAvatarTap) {
- widget.onTapForOthersPortrait!(
- message.sender ?? "", details);
- }
- },
- child: Avatar(
- faceUrl: message.faceUrl ?? "",
- showName:
- MessageUtils.getDisplayName(message)),
- ),
+ if (!isSelf &&
+ widget.message.elemType == 6 &&
+ isDownloadWaiting)
+ Container(
+ margin: const EdgeInsets.only(top: 24, left: 6),
+ child: LoadingAnimationWidget.threeArchedCircle(
+ color: theme.weakTextColor ?? Colors.grey,
+ size: 20,
),
- ],
+ ),
+ if (isSelf && widget.showAvatar)
+ widget.userAvatarBuilder != null
+ ? widget.userAvatarBuilder!(context, message)
+ : SizedBox(
+ width: 40,
+ height: 40,
+ child: InkWell(
+ onTapDown: (details) {
+ if (widget.onTapForOthersPortrait != null &&
+ widget.allowAvatarTap) {
+ widget.onTapForOthersPortrait!(
+ message.sender ?? "", details);
+ }
+ },
+ child: Avatar(
+ faceUrl: message.faceUrl ?? "",
+ showName:
+ MessageUtils.getDisplayName(message)),
+ ),
+ ),
+ ],
+ ),
+ ),
),
),
- ),
+ ],
),
- ],
- ),
- ),
+ ),
);
}
}
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart
index 48f0744..a21d8da 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_message_tooltip.dart
@@ -351,7 +351,9 @@ class TIMUIKitMessageTooltipState
children: [
Image.asset(
item.iconImageAsset,
- package: 'tencent_cloud_chat_uikit',
+ package: defaultTipsIds.contains(item.id)
+ ? 'tencent_cloud_chat_uikit'
+ : null,
width: 20,
height: 20,
),
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart
index e8e10b2..329069c 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitMessageItem/tim_uikit_chat_reply_elem.dart
@@ -3,6 +3,7 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
+import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_model_tools.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
@@ -57,7 +58,7 @@ class TIMUIKitReplyElem extends StatefulWidget {
}
class _TIMUIKitReplyElemState extends TIMUIKitState {
- MessageRepliedData? repliedMessage;
+ MessageRepliedData? repliedMessage = null;
V2TimMessage? rawMessage;
bool isShowJumpState = false;
bool isShining = false;
@@ -81,10 +82,32 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
}
_getMessageByMessageID() async {
- final cloudCustomData = _getRepliedMessage();
+ final MessageRepliedData? cloudCustomData = _getRepliedMessage();
if (cloudCustomData != null) {
+ if (mounted) {
+ setState(() {
+ repliedMessage = cloudCustomData;
+ });
+ }
+
final messageID = cloudCustomData.messageID;
- final message = await widget.chatModel.findMessage(messageID);
+ V2TimMessage? message = await widget.chatModel.findMessage(messageID);
+ if (message == null) {
+ try {
+ final RepliedMessageAbstract repliedMessageAbstract =
+ RepliedMessageAbstract.fromJson(
+ jsonDecode(cloudCustomData.messageAbstract));
+ if (repliedMessageAbstract.isNotEmpty) {
+ message = V2TimMessage(
+ elemType: 0,
+ seq: repliedMessageAbstract.seq,
+ timestamp: repliedMessageAbstract.timestamp,
+ msgID: repliedMessageAbstract.msgID);
+ }
+ } catch (e) {
+ print(e.toString());
+ }
+ }
if (message != null) {
if (mounted) {
setState(() {
@@ -93,36 +116,53 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
}
}
}
- if (mounted) {
- setState(() {
- repliedMessage = cloudCustomData;
- });
- }
}
Widget _defaultRawMessageText(String text, TUITheme? theme) {
return Text(text,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
color: theme?.weakTextColor,
fontWeight: FontWeight.w400));
}
+ _renderMessageSummary(TUITheme? theme) {
+ try {
+ final RepliedMessageAbstract repliedMessageAbstract =
+ RepliedMessageAbstract.fromJson(
+ jsonDecode(repliedMessage?.messageAbstract ?? ""));
+ if (TencentUtils.checkString(repliedMessageAbstract.summary) != null) {
+ return _defaultRawMessageText(repliedMessageAbstract.summary!, theme);
+ }
+ return _defaultRawMessageText(
+ repliedMessage?.messageAbstract ?? TIM_t("[未知消息]"), theme);
+ } catch (e) {
+ return _defaultRawMessageText(
+ repliedMessage?.messageAbstract ?? TIM_t("[未知消息]"), theme);
+ }
+ }
+
_rawMessageBuilder(V2TimMessage? message, TUITheme? theme) {
if (repliedMessage == null) {
return const SizedBox(width: 0, height: 12);
}
if (message == null) {
if (repliedMessage?.messageAbstract != null) {
- return _defaultRawMessageText(repliedMessage!.messageAbstract, theme);
+ _renderMessageSummary(theme);
}
return const SizedBox(width: 0, height: 12);
}
final messageType = message.elemType;
final isSelf = message.isSelf ?? true;
- if (widget.chatModel.abstractMessageBuilder != null) {
+ final customAbstractMessage =
+ widget.chatModel.abstractMessageBuilder != null
+ ? widget.chatModel.abstractMessageBuilder!(message)
+ : null;
+ if (customAbstractMessage != null) {
return _defaultRawMessageText(
- widget.chatModel.abstractMessageBuilder!(message),
+ customAbstractMessage,
theme,
);
}
@@ -173,7 +213,7 @@ class _TIMUIKitReplyElemState extends TIMUIKitState {
messageID: message.msgID ?? "",
isSelf: isSelf);
default:
- return _defaultRawMessageText(TIM_t("[未知消息]"), theme);
+ return _renderMessageSummary(theme);
}
}
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart
index b4bbc45..608c923 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart
@@ -93,6 +93,8 @@ class TIMUIKitInputTextField extends StatefulWidget {
final String? groupType;
+ final String? groupID;
+
const TIMUIKitInputTextField(
{Key? key,
required this.conversationID,
@@ -113,7 +115,8 @@ class TIMUIKitInputTextField extends StatefulWidget {
required this.model,
required this.currentConversation,
this.groupType,
- this.atMemberPanelScroll})
+ this.atMemberPanelScroll,
+ this.groupID})
: super(key: key);
@override
@@ -166,21 +169,22 @@ class _InputTextFieldState extends TIMUIKitState {
return text.replaceAll(RegExp(r'\ufeff'), "");
}
- handleSetDraftText([String? id, ConvType? convType]) async {
+ Future handleSetDraftText([String? id, ConvType? convType]) async {
+ String text = textEditingController.text;
String convID = id ?? widget.conversationID;
- String conversationID = convID.contains("@TOPIC#")
+ final isTopic = convID.contains("@TOPIC#");
+ String conversationID = isTopic
? convID
: ((convType ?? widget.conversationType) == ConvType.c2c
? "c2c_$convID"
: "group_$convID");
- String text = textEditingController.text;
- String? draftText = _filterU200b(text);
-
- if (draftText.isEmpty) {
- draftText = "";
- }
- await conversationModel.setConversationDraft(
- conversationID: conversationID, draftText: draftText);
+ String draftText = _filterU200b(text);
+ return await conversationModel.setConversationDraft(
+ groupID: widget.groupID,
+ isTopic: isTopic,
+ isAllowWeb: widget.model.chatConfig.isUseDraftOnWeb,
+ conversationID: conversationID,
+ draftText: draftText);
}
backSpaceText() {
@@ -189,11 +193,9 @@ class _InputTextFieldState extends TIMUIKitState {
if (originalText == zeroWidthSpace) {
_handleSoftKeyBoardDelete();
- // _addDeleteTag();
} else {
text = originalText.characters.skipLast(1);
textEditingController.text = text;
- // handleSetDraftText();
}
}
@@ -202,6 +204,7 @@ class _InputTextFieldState extends TIMUIKitState {
lastText = "";
final text = textEditingController.text.trim();
final convType = widget.conversationType;
+ conversationModel.clearWebDraft(conversationID: widget.conversationID);
if (text.isNotEmpty && text != zeroWidthSpace) {
if (widget.model.repliedMessage != null) {
MessageUtils.handleMessageError(
@@ -259,6 +262,7 @@ class _InputTextFieldState extends TIMUIKitState {
}
onSubmitted() async {
+ conversationModel.clearWebDraft(conversationID: widget.conversationID);
lastText = "";
final text = textEditingController.text.trim();
final convType = widget.conversationType;
@@ -417,6 +421,22 @@ class _InputTextFieldState extends TIMUIKitState {
mentionedMembersMap = map;
}
+ updateMentionedMap() {
+ Map map = {};
+ Iterable matches = atTextReg.allMatches(textEditingController.text);
+ List parseAtList = [];
+ for (final item in matches) {
+ final str = item.group(0);
+ parseAtList.add(str);
+ }
+ for (String? key in parseAtList) {
+ if (key != null && mentionedMembersMap[key] != null) {
+ map[key] = mentionedMembersMap[key]!;
+ }
+ }
+ mentionedMembersMap = map;
+ }
+
_handleAtText(String text, TUIChatSeparateViewModel model) async {
final text = textEditingController.text;
String? groupID = widget.conversationType == ConvType.group
@@ -449,22 +469,11 @@ class _InputTextFieldState extends TIMUIKitState {
textEditingController.selection =
TextSelection.collapsed(offset: atIndex);
lastText = newText;
- Map map = {};
- Iterable matches = atTextReg.allMatches(text);
- List parseAtList = [];
- for (final item in matches) {
- final str = item.group(0);
- parseAtList.add(str);
- }
- for (String? key in parseAtList) {
- if (key != null && mentionedMembersMap[key] != null) {
- map[key] = mentionedMembersMap[key]!;
- }
- }
- mentionedMembersMap = map;
+ updateMentionedMap();
return;
}
}
+ updateMentionedMap();
}
final int selfRole = widget.model.selfMemberInfo?.role ?? 0;
@@ -709,8 +718,8 @@ class _InputTextFieldState extends TIMUIKitState {
void didUpdateWidget(TIMUIKitInputTextField oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.conversationID != oldWidget.conversationID) {
- handleSetDraftText(oldWidget.conversationID, oldWidget.conversationType);
mentionedMembersMap.clear();
+ handleSetDraftText(oldWidget.conversationID, oldWidget.conversationType);
if (oldWidget.initText != widget.initText) {
textEditingController.text = widget.initText ?? "";
} else {
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart
index 3a427c1..8e95045 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart
@@ -309,12 +309,23 @@ class _TIMUIKitTextFieldLayoutNarrowState
});
};
}
+ String getAbstractMessage(V2TimMessage message) {
+ final String? customAbstractMessage = widget
+ .model.abstractMessageBuilder != null ? widget.model
+ .abstractMessageBuilder!(widget.model.repliedMessage!) : null;
+ return customAbstractMessage ?? MessageUtils
+ .getAbstractMessageAsync(
+ widget.model.repliedMessage!, widget.model.groupMemberList ?? []);
+ }
_buildRepliedMessage(V2TimMessage? repliedMessage) {
final haveRepliedMessage = repliedMessage != null;
if (haveRepliedMessage) {
final text =
- "${MessageUtils.getDisplayName(widget.model.repliedMessage!)}:${widget.model.abstractMessageBuilder != null ? widget.model.abstractMessageBuilder!(widget.model.repliedMessage!) : MessageUtils.getAbstractMessageAsync(widget.model.repliedMessage!, widget.model.groupMemberList ?? [])}";
+ "${MessageUtils.getDisplayName(
+ widget.model.repliedMessage!)}:${getAbstractMessage(
+ repliedMessage
+ )}";
return Container(
color: widget.backgroundColor ?? hexToColor("f5f5f6"),
alignment: Alignment.centerLeft,
diff --git a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/wide.dart b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/wide.dart
index c390f5b..4501ba9 100644
--- a/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/wide.dart
+++ b/lib/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/wide.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
+import 'dart:ui' as ui;
import 'package:fc_native_video_thumbnail/fc_native_video_thumbnail.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
@@ -32,6 +33,7 @@ import 'package:universal_html/html.dart' as html;
import 'package:url_launcher/url_launcher.dart';
import 'package:uuid/uuid.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
+import 'package:flutter_svg/flutter_svg.dart';
// ignore: unnecessary_import
import 'dart:typed_data';
@@ -262,36 +264,62 @@ class _TIMUIKitTextFieldLayoutWideState
};
}
+ String getAbstractMessage(V2TimMessage message) {
+ final String? customAbstractMessage =
+ widget.model.abstractMessageBuilder != null
+ ? widget.model.abstractMessageBuilder!(widget.model.repliedMessage!)
+ : null;
+ return customAbstractMessage ??
+ MessageUtils.getAbstractMessageAsync(
+ widget.model.repliedMessage!, widget.model.groupMemberList ?? []);
+ }
+
_buildRepliedMessage(V2TimMessage? repliedMessage) {
final haveRepliedMessage = repliedMessage != null;
if (haveRepliedMessage) {
- final text =
- "${MessageUtils.getDisplayName(widget.model.repliedMessage!)}:${widget.model.abstractMessageBuilder != null ? widget.model.abstractMessageBuilder!(widget.model.repliedMessage!) : MessageUtils.getAbstractMessageAsync(widget.model.repliedMessage!, widget.model.groupMemberList ?? [])}";
return Container(
color: widget.backgroundColor ?? hexToColor("f5f5f6"),
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ mainAxisAlignment: MainAxisAlignment.start,
children: [
+ Text(
+ TIM_t("回复 "),
+ style: TextStyle(
+ color: hexToColor("8f959e"), fontSize: 14),
+ ),
+ Text(
+ MessageUtils.getDisplayName(
+ widget.model.repliedMessage!),
+ softWrap: true,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: hexToColor("8f959e"),
+ fontSize: 14,
+ fontWeight: FontWeight.bold),
+ ),
Expanded(
child: Text(
- text,
- softWrap: true,
- maxLines: 3,
+ ": ${getAbstractMessage(repliedMessage)}",
+ maxLines: 1,
overflow: TextOverflow.ellipsis,
- style: TextStyle(color: hexToColor("8f959e"), fontSize: 14),
+ style: TextStyle(
+ fontSize: 14,
+ color: hexToColor("8f959e"),
+ ),
),
),
const SizedBox(
- width: 16,
+ width: 8,
),
InkWell(
onTap: () {
widget.model.repliedMessage = null;
},
- child: Icon(Icons.clear, color: hexToColor("8f959e"), size: 18),
+ child: Icon(Icons.cancel, color: hexToColor("8f959e"), size: 18),
)
],
),
@@ -314,8 +342,9 @@ class _TIMUIKitTextFieldLayoutWideState
entry = null;
}
},
- initOffset: offset ??
- Offset(MediaQuery.of(context).size.height * 0.5 + 20,
+ initOffset: offset != null
+ ? Offset(offset.dx, max(offset.dy, 16))
+ : Offset(MediaQuery.of(context).size.height * 0.5 + 20,
MediaQuery.of(context).size.height * 0.5 - 100),
child: Container(
decoration: BoxDecoration(
@@ -793,7 +822,7 @@ class _TIMUIKitTextFieldLayoutWideState
onClick: (offset) {
_sendEmoji(offset, widget.theme);
},
- svgPath: "images/svg/send_face.svg"),
+ imgPath: "images/svg/send_face.png"),
if (config.showScreenshotButton && PlatformUtils().isDesktop)
DesktopControlBarItem(
item: "screenShot",
@@ -950,6 +979,7 @@ class _TIMUIKitTextFieldLayoutWideState
color: widget.backgroundColor ?? theme.desktopChatMessageInputBgColor,
child: Column(
children: [
+ _buildRepliedMessage(widget.repliedMessage),
SizedBox(
height: 1,
child: Container(
@@ -963,7 +993,6 @@ class _TIMUIKitTextFieldLayoutWideState
children: generateControlBar(widget.model, theme),
),
),
- _buildRepliedMessage(widget.repliedMessage),
Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 6),
constraints: const BoxConstraints(minHeight: 50),
diff --git a/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart b/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart
index 1e2c089..32f3f41 100644
--- a/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart
+++ b/lib/ui/views/TIMUIKitChat/tim_uikit_chat.dart
@@ -11,12 +11,14 @@ import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/chat_life_cyc
import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
+import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_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/constants/history_message_constant.dart';
import 'package:tencent_cloud_chat_uikit/ui/controller/tim_uikit_chat_controller.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/frame.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
+import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/at_member_panel.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_multi_select_panel.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_send_file.dart';
@@ -50,7 +52,7 @@ class TIMUIKitChat extends StatefulWidget {
/// use for customize avatar
final Widget Function(BuildContext context, V2TimMessage message)?
- userAvatarBuilder;
+ userAvatarBuilder;
/// Use for show conversation name.
/// This field is not necessary to be provided, when `conversation` is provided, unless you want to cover this field manually.
@@ -61,7 +63,7 @@ class TIMUIKitChat extends StatefulWidget {
/// Avatar and name in message reaction secondary tap callback.
final void Function(String userID, TapDownDetails tapDetails)?
- onSecondaryTapAvatar;
+ onSecondaryTapAvatar;
@Deprecated(
"Nickname will not shows in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
@@ -108,18 +110,23 @@ class TIMUIKitChat extends StatefulWidget {
final TongueItemBuilder? tongueItemBuilder;
/// The `groupAtInfoList` from `V2TimConversation`.
- /// This field is not necessary to be provided, when `conversation` is provided, unless you want to cover this field manually.
+ /// This field is not necessary to be provided, when `conversation` is provided,
+ /// unless you want to cover this field manually.
final List? groupAtInfoList;
/// The configuration for the whole `TIMUIKitChat` widget.
final TIMUIKitChatConfig? config;
- /// The callback for jumping to the page for `TIMUIKitGroupApplicationList` or other pages to deal with enter group application for group administrator manually, in the case of [public group].
+ /// The callback for jumping to the page for `TIMUIKitGroupApplicationList`
+ /// or other pages to deal with enter group application for group administrator manually,
+ /// in the case of [public group].
/// The parameter here is `String groupID`
final ValueChanged? onDealWithGroupApplication;
- /// The builder for abstract messages, normally used in replied message and forward message.
- final String Function(V2TimMessage message)? abstractMessageBuilder;
+ /// The generator for the abstract summary preview of a message,
+ /// typically used in replied and forwarded messages.
+ /// Returns `null` to use the default message summary.
+ final String? Function(V2TimMessage message)? abstractMessageBuilder;
/// The configuration for tool tips panel, long press messages will show this panel.
final ToolTipsConfig? toolTipsConfig;
@@ -140,45 +147,45 @@ class TIMUIKitChat extends StatefulWidget {
/// Custom text field
final Widget Function(BuildContext context)? textFieldBuilder;
- TIMUIKitChat(
- {Key? key,
- this.groupID,
- required this.conversation,
- this.conversationID,
- this.conversationType,
- this.conversationShowName,
- this.abstractMessageBuilder,
- this.onTapAvatar,
- @Deprecated(
- "Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
- this.showNickName = false,
- this.showTotalUnReadCount = false,
- this.messageItemBuilder,
- @Deprecated("Please use [extraTipsActionItemBuilder] instead")
- this.exteraTipsActionItemBuilder,
- this.extraTipsActionItemBuilder,
- this.draftText,
- this.textFieldHintText,
- this.initFindingMsg,
- this.userAvatarBuilder,
- this.appBarConfig,
- this.controller,
- this.morePanelConfig,
- this.customStickerPanel,
- this.config = const TIMUIKitChatConfig(),
- this.tongueItemBuilder,
- this.groupAtInfoList,
- this.mainHistoryListConfig,
- this.onDealWithGroupApplication,
- this.toolTipsConfig,
- this.lifeCycle,
- this.topFixWidget = const SizedBox(),
- this.textFieldBuilder,
- this.customEmojiStickerList = const [],
- this.customAppBar,
- this.onSecondaryTapAvatar})
+ TIMUIKitChat({Key? key,
+ this.groupID,
+ required this.conversation,
+ this.conversationID,
+ this.conversationType,
+ this.conversationShowName,
+ this.abstractMessageBuilder,
+ this.onTapAvatar,
+ @Deprecated(
+ "Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead") this.showNickName = false,
+ this.showTotalUnReadCount = false,
+ this.messageItemBuilder,
+ @Deprecated(
+ "Please use [extraTipsActionItemBuilder] instead") this.exteraTipsActionItemBuilder,
+ this.extraTipsActionItemBuilder,
+ this.draftText,
+ this.textFieldHintText,
+ this.initFindingMsg,
+ this.userAvatarBuilder,
+ this.appBarConfig,
+ this.controller,
+ this.morePanelConfig,
+ this.customStickerPanel,
+ this.config = const TIMUIKitChatConfig(),
+ this.tongueItemBuilder,
+ this.groupAtInfoList,
+ this.mainHistoryListConfig,
+ this.onDealWithGroupApplication,
+ this.toolTipsConfig,
+ this.lifeCycle,
+ this.topFixWidget = const SizedBox(),
+ this.textFieldBuilder,
+ this.customEmojiStickerList = const [],
+ this.customAppBar,
+ this.onSecondaryTapAvatar})
: super(key: key) {
- startTime = DateTime.now().millisecondsSinceEpoch;
+ startTime = DateTime
+ .now()
+ .millisecondsSinceEpoch;
}
@override
@@ -188,11 +195,13 @@ class TIMUIKitChat extends StatefulWidget {
class _TUIChatState extends TIMUIKitState {
TUIChatSeparateViewModel model = TUIChatSeparateViewModel();
final TUIThemeViewModel themeViewModel = serviceLocator();
+ final TUIConversationViewModel conversationViewModel =
+ serviceLocator();
final TIMUIKitInputTextFieldController textFieldController =
- TIMUIKitInputTextFieldController();
+ TIMUIKitInputTextFieldController();
bool isInit = false;
final TUIChatGlobalModel chatGlobalModel =
- serviceLocator();
+ serviceLocator();
bool _dragging = false;
final GlobalKey alignKey = GlobalKey();
@@ -200,13 +209,19 @@ class _TUIChatState extends TIMUIKitState {
late AutoScrollController autoController = AutoScrollController(
viewportBoundaryGetter: () =>
- Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
+ Rect.fromLTRB(0, 0, 0, MediaQuery
+ .of(context)
+ .padding
+ .bottom),
axis: Axis.vertical,
);
late AutoScrollController atMemberPanelScroll = AutoScrollController(
viewportBoundaryGetter: () =>
- Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
+ Rect.fromLTRB(0, 0, 0, MediaQuery
+ .of(context)
+ .padding
+ .bottom),
axis: Axis.vertical,
);
@@ -220,11 +235,16 @@ class _TUIChatState extends TIMUIKitState {
model.onTapAvatar = widget.onTapAvatar;
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (kProfileMode) {
- widget.endTime = DateTime.now().millisecondsSinceEpoch;
+ widget.endTime = DateTime
+ .now()
+ .millisecondsSinceEpoch;
int timeSpend = widget.endTime - widget.startTime;
print("Page render time:$timeSpend ms");
}
});
+ Future.delayed(const Duration(milliseconds: 500), () {
+ updateDraft();
+ });
}
@override
@@ -245,8 +265,8 @@ class _TUIChatState extends TIMUIKitState {
model = TUIChatSeparateViewModel();
model.abstractMessageBuilder = widget.abstractMessageBuilder;
model.onTapAvatar = widget.onTapAvatar;
- textFieldController.requestFocus();
Future.delayed(const Duration(milliseconds: 50), () {
+ updateDraft();
textFieldController.requestFocus();
try {
autoController.jumpTo(
@@ -261,6 +281,22 @@ class _TUIChatState extends TIMUIKitState {
}
}
+ updateDraft() async {
+ final isTopic = widget.conversation.conversationID.contains("@TOPIC#");
+ if (isTopic) {
+ final topicInfoList = await TencentImSDKPlugin.v2TIMManager
+ .getGroupManager()
+ .getTopicInfoList(
+ groupID: widget.groupID!,
+ topicIDList: [widget.conversation.conversationID]);
+ final topicInfo = topicInfoList.data?.first.topicInfo;
+ final draftText = topicInfo?.draftText;
+ if (TencentUtils.checkString(draftText) != null) {
+ textFieldController.setTextField(draftText!);
+ }
+ }
+ }
+
Widget _renderJoinGroupApplication(int amount, TUITheme theme) {
String option1 = amount.toString();
return Container(
@@ -318,7 +354,7 @@ class _TUIChatState extends TIMUIKitState {
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme;
final closePanel =
- OptimizeUtils.throttle((_) => textFieldController.hideAllPanel(), 60);
+ OptimizeUtils.throttle((_) => textFieldController.hideAllPanel(), 60);
final isBuild = isInit;
isInit = true;
@@ -337,7 +373,7 @@ class _TUIChatState extends TIMUIKitState {
],
builder: (context, model, w) {
final TUIChatGlobalModel chatGlobalModel =
- Provider.of(context, listen: true);
+ Provider.of(context, listen: true);
widget.controller?.model = model;
widget.controller?.textFieldController = textFieldController;
@@ -347,13 +383,13 @@ class _TUIChatState extends TIMUIKitState {
widget.onDealWithGroupApplication != null) {
filteredApplicationList =
chatGlobalModel.groupApplicationList.where((item) {
- return (item.groupID == widget.conversationID) &&
- item.handleStatus == 0;
- }).toList();
+ return (item.groupID == widget.conversationID) &&
+ item.handleStatus == 0;
+ }).toList();
}
final TUIGroupListenerModel groupListenerModel =
- Provider.of(context, listen: true);
+ Provider.of(context, listen: true);
final NeedUpdate? needUpdate = groupListenerModel.needUpdate;
if (needUpdate != null &&
needUpdate.groupID == widget.conversationID) {
@@ -379,13 +415,13 @@ class _TUIChatState extends TIMUIKitState {
resizeToAvoidBottomInset: false,
appBar: (widget.customAppBar == null)
? TIMUIKitAppBar(
- showTotalUnReadCount: widget.showTotalUnReadCount,
- config: widget.appBarConfig,
- conversationShowName: _getTitle(),
- conversationID: _getConvID(),
- showC2cMessageEditStatus:
- widget.config?.showC2cMessageEditStatus ?? true,
- )
+ showTotalUnReadCount: widget.showTotalUnReadCount,
+ config: widget.appBarConfig,
+ conversationShowName: _getTitle(),
+ conversationID: _getConvID(),
+ showC2cMessageEditStatus:
+ widget.config?.showC2cMessageEditStatus ?? true,
+ )
: null,
body: DropTarget(
onDragDone: (detail) {
@@ -421,90 +457,100 @@ class _TUIChatState extends TIMUIKitState {
if (widget.topFixWidget != null) widget.topFixWidget!,
Expanded(
child: Container(
- color: theme.chatBgColor,
- child: Align(
- key: alignKey,
- alignment: Alignment.topCenter,
- child: Listener(
- onPointerMove: closePanel,
- child: TIMUIKitHistoryMessageListContainer(
- conversation: widget.conversation,
- textFieldController: textFieldController,
- customEmojiStickerList:
+ color: theme.chatBgColor,
+ child: Align(
+ key: alignKey,
+ alignment: Alignment.topCenter,
+ child: Listener(
+ onPointerMove: closePanel,
+ child: TIMUIKitHistoryMessageListContainer(
+ conversation: widget.conversation,
+ textFieldController: textFieldController,
+ customEmojiStickerList:
widget.customEmojiStickerList,
- isUseDefaultEmoji:
+ isUseDefaultEmoji:
widget.config!.isUseDefaultEmoji,
- key: listContainerKey,
- isAllowScroll: true,
- userAvatarBuilder: widget.userAvatarBuilder,
- toolTipsConfig: widget.toolTipsConfig,
- groupAtInfoList: widget.groupAtInfoList,
- tongueItemBuilder: widget.tongueItemBuilder,
- onLongPressForOthersHeadPortrait:
- (String? userId, String? nickName) {
- textFieldController.longPressToAt(
- nickName, userId);
- },
- mainHistoryListConfig:
+ key: listContainerKey,
+ isAllowScroll: true,
+ userAvatarBuilder: widget
+ .userAvatarBuilder,
+ toolTipsConfig: widget.toolTipsConfig,
+ groupAtInfoList: widget.groupAtInfoList,
+ tongueItemBuilder: widget
+ .tongueItemBuilder,
+ onLongPressForOthersHeadPortrait:
+ (String? userId, String? nickName) {
+ textFieldController.longPressToAt(
+ nickName, userId);
+ },
+ mainHistoryListConfig:
widget.mainHistoryListConfig,
- initFindingMsg: widget.initFindingMsg,
- extraTipsActionItemBuilder:
+ initFindingMsg: widget.initFindingMsg,
+ extraTipsActionItemBuilder:
widget.extraTipsActionItemBuilder ??
widget.exteraTipsActionItemBuilder,
- conversationType: _getConvType(),
- scrollController: autoController,
- onSecondaryTapAvatar:
+ conversationType: _getConvType(),
+ scrollController: autoController,
+ onSecondaryTapAvatar:
widget.onSecondaryTapAvatar,
- onTapAvatar: widget.onTapAvatar,
- // ignore: deprecated_member_use_from_same_package
- showNickName: widget.showNickName,
- messageItemBuilder:
+ onTapAvatar: widget.onTapAvatar,
+ // ignore: deprecated_member_use_from_same_package
+ showNickName: widget.showNickName,
+ messageItemBuilder:
widget.messageItemBuilder,
- conversationID: _getConvID(),
- ),
- )),
- )),
+ conversationID: _getConvID(),
+ ),
+ )),
+ )),
Selector(
builder: (context, value, child) {
return value
? MultiSelectPanel(
- conversationType: _getConvType(),
- )
+ conversationType: _getConvType(),
+ )
: (widget.textFieldBuilder != null
- ? widget.textFieldBuilder!(context)
- : TIMUIKitInputTextField(
- atMemberPanelScroll:
- atMemberPanelScroll,
- groupType:
- widget.conversation.groupType,
- currentConversation:
- widget.conversation,
- model: model,
- controller: textFieldController,
- customEmojiStickerList:
- widget.customEmojiStickerList,
- isUseDefaultEmoji:
- widget.config!.isUseDefaultEmoji,
- customStickerPanel:
- widget.customStickerPanel,
- morePanelConfig:
- widget.morePanelConfig,
- scrollController: autoController,
- conversationID: _getConvID(),
- conversationType: _getConvType(),
- initText: widget.draftText ??
- widget.conversation.draftText,
- hintText: widget.textFieldHintText,
- showMorePanel: widget.config
- ?.isAllowShowMorePanel ??
- true,
- showSendAudio: widget.config
- ?.isAllowSoundMessage ??
- true,
- showSendEmoji: widget
- .config?.isAllowEmojiPanel ??
- true,
- ));
+ ? widget.textFieldBuilder!(context)
+ : TIMUIKitInputTextField(
+ groupID: widget.groupID,
+ atMemberPanelScroll:
+ atMemberPanelScroll,
+ groupType:
+ widget.conversation.groupType,
+ currentConversation:
+ widget.conversation,
+ model: model,
+ controller: textFieldController,
+ customEmojiStickerList:
+ widget.customEmojiStickerList,
+ isUseDefaultEmoji:
+ widget.config!.isUseDefaultEmoji,
+ customStickerPanel:
+ widget.customStickerPanel,
+ morePanelConfig:
+ widget.morePanelConfig,
+ scrollController: autoController,
+ conversationID: _getConvID(),
+ conversationType: _getConvType(),
+ initText: TencentUtils.checkString(
+ widget.draftText) ?? (PlatformUtils().isWeb
+ ? TencentUtils.checkString(
+ conversationViewModel.getWebDraft(
+ conversationID: widget.conversation
+ .conversationID))
+ :
+ TencentUtils.checkString(widget
+ .conversation.draftText)),
+ hintText: widget.textFieldHintText,
+ showMorePanel: widget.config
+ ?.isAllowShowMorePanel ??
+ true,
+ showSendAudio: widget.config
+ ?.isAllowSoundMessage ??
+ true,
+ showSendEmoji: widget
+ .config?.isAllowEmojiPanel ??
+ true,
+ ));
},
selector: (c, model) {
return model.isMultiSelect;
@@ -533,13 +579,13 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
final TUIChatGlobalModel globalModel = serviceLocator();
TUIChatSeparateViewModel? model;
final TUIGroupListenerModel groupListenerModel =
- serviceLocator();
+ serviceLocator();
final TUIThemeViewModel themeViewModel = serviceLocator();
final Widget? child;
/// You could get the model from here, and transfer it to other widget from TUIKit.
final Widget Function(BuildContext, TUIChatSeparateViewModel, Widget?)
- builder;
+ builder;
final List? providers;
/// `TIMUIKitChatController` needs to be provided if you use it outside.
@@ -566,21 +612,20 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
final AutoScrollController? scrollController;
- TIMUIKitChatProviderScope(
- {Key? key,
- this.child,
- this.providers,
- this.textFieldController,
- required this.builder,
- this.model,
- this.groupID,
- this.isBuild,
- required this.conversationID,
- required this.conversationType,
- this.controller,
- this.config,
- this.lifeCycle,
- this.scrollController})
+ TIMUIKitChatProviderScope({Key? key,
+ this.child,
+ this.providers,
+ this.textFieldController,
+ required this.builder,
+ this.model,
+ this.groupID,
+ this.isBuild,
+ required this.conversationID,
+ required this.conversationType,
+ this.controller,
+ this.config,
+ this.lifeCycle,
+ this.scrollController})
: super(key: key) {
if (isBuild ?? false) {
return;
@@ -596,7 +641,7 @@ class TIMUIKitChatProviderScope extends StatelessWidget {
model?.initForEachConversation(
conversationType,
conversationID,
- (String value) {
+ (String value) {
textFieldController?.textEditingController?.text = value;
},
groupID: groupID,
diff --git a/lib/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart b/lib/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart
index eddebef..1b01cb7 100644
--- a/lib/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart
+++ b/lib/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart
@@ -185,6 +185,12 @@ class TIMUIKitChatConfig {
/// Define the lines in the text message input field on Desktop.
final int desktopMessageInputFieldLines;
+ /// Specifies whether to use the draft feature on the Web, as the Chat SDK does not support this functionality.
+ /// If enabled, draft data will be stored in TUIKit's memory.
+ /// Note that the draft text will be lost upon refreshing the website.
+ /// [Default]: true.
+ final bool isUseDraftOnWeb;
+
const TIMUIKitChatConfig(
{this.onTapLink,
this.timeDividerConfig,
@@ -203,7 +209,7 @@ class TIMUIKitChatConfig {
this.isShowSelfNameInGroup = false,
this.offlinePushInfo,
@Deprecated("Please use [isShowGroupReadingStatus] instead")
- this.isShowGroupMessageReadReceipt = true,
+ this.isShowGroupMessageReadReceipt = true,
this.upperRecallTime = 120,
this.isShowOthersNameInGroup = true,
this.urlPreviewType = UrlPreviewType.onlyHyperlink,
@@ -213,13 +219,14 @@ class TIMUIKitChatConfig {
this.notificationIOSSound = "",
this.isAllowSoundMessage = true,
@Deprecated("Please use [groupReadReceiptPermissionList] instead")
- this.groupReadReceiptPermisionList,
+ this.groupReadReceiptPermisionList,
this.groupReadReceiptPermissionList,
this.isAllowEmojiPanel = true,
this.isAllowShowMorePanel = true,
this.isShowReadingStatus = true,
this.desktopControlBarConfig,
this.isAllowLongPressMessage = true,
+ this.isUseDraftOnWeb = true,
this.isAllowClickAvatar = true,
this.isEnableTextSelection,
this.additionalDesktopMessageHoverBarItem,
diff --git a/lib/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart b/lib/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart
index b21315e..f63c1c9 100644
--- a/lib/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart
+++ b/lib/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart
@@ -1,3 +1,5 @@
+import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
+
class MessageRepliedData {
late String messageAbstract;
late String messageSender;
@@ -10,6 +12,42 @@ class MessageRepliedData {
}
}
+class RepliedMessageAbstract {
+ final int? elemType;
+ final String? msgID;
+ final int? timestamp;
+ final String? seq;
+ final String? summary;
+
+ RepliedMessageAbstract(
+ {this.elemType, this.msgID, this.timestamp, this.seq, this.summary});
+
+ // fromJson constructor
+ RepliedMessageAbstract.fromJson(Map json)
+ : elemType = json['elemType'],
+ msgID = json['msgID'],
+ timestamp = json['timestamp'],
+ seq = json['seq'],
+ summary = json['summary'];
+
+ // toJson function
+ Map toJson() {
+ return {
+ 'summary': summary,
+ 'elemType': elemType,
+ 'msgID': msgID,
+ 'timestamp': timestamp,
+ 'seq': seq,
+ };
+ }
+
+ // isNotEmpty method
+ bool get isNotEmpty =>
+ TencentUtils.checkString(msgID) != null &&
+ TencentUtils.checkString(timestamp.toString()) != null &&
+ TencentUtils.checkString(seq) != null;
+}
+
class CloudCustomData {
Map? messageReply;
Map? messageReaction = {};
diff --git a/lib/ui/widgets/link_preview/link_preview_entry.dart b/lib/ui/widgets/link_preview/link_preview_entry.dart
index ba0de41..b51a0ed 100644
--- a/lib/ui/widgets/link_preview/link_preview_entry.dart
+++ b/lib/ui/widgets/link_preview/link_preview_entry.dart
@@ -17,7 +17,8 @@ class LinkPreviewEntry {
return isMarkdown
? LinkTextMarkdown(
isEnableTextSelection: isEnableTextSelection,
- messageText: replaceSingleNewlineWithTwo(messageText),
+ messageText: addSpaceAfterLeftBracket(
+ addSpaceBeforeHttp(replaceSingleNewlineWithTwo(messageText))),
style: style,
onLinkTap: onLinkTap)
: LinkText(
@@ -30,10 +31,27 @@ class LinkPreviewEntry {
};
}
+ static String addSpaceAfterLeftBracket(String inputText) {
+ return inputText.splitMapJoin(
+ RegExp(r'<\w+[^<>]*>'),
+ onMatch: (match) {
+ return match.group(0)!.replaceFirst('<', '< ');
+ },
+ onNonMatch: (text) => text,
+ );
+ }
+
static String replaceSingleNewlineWithTwo(String inputText) {
- return inputText.replaceAllMapped(
- RegExp(r'(? '\n\n',
+ return inputText.split('\n').join('\n\n');
+ }
+
+ static String addSpaceBeforeHttp(String inputText) {
+ return inputText.splitMapJoin(
+ RegExp(r'http'),
+ onMatch: (match) {
+ return ' http';
+ },
+ onNonMatch: (text) => text,
);
}
diff --git a/lib/ui/widgets/link_preview/widgets/link_text.dart b/lib/ui/widgets/link_preview/widgets/link_text.dart
index 3460a3d..fc12d6f 100644
--- a/lib/ui/widgets/link_preview/widgets/link_text.dart
+++ b/lib/ui/widgets/link_preview/widgets/link_text.dart
@@ -8,6 +8,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:tencent_im_base/base_widgets/tim_stateless_widget.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/DefaultSpecialTextSpanBuilder.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/common/utils.dart';
+import 'package:markdown/markdown.dart' as md;
class LinkTextMarkdown extends TIMStatelessWidget {
/// Callback for when link is tapped
@@ -40,6 +41,7 @@ class LinkTextMarkdown extends TIMStatelessWidget {
.copyWith(
a: TextStyle(color: LinkUtils.hexToColor("015fff")),
),
+ extensionSet: md.ExtensionSet.gitHubWeb,
onTapLink: (
String link,
String? href,
@@ -155,3 +157,20 @@ class LinkText extends TIMStatelessWidget {
));
}
}
+
+class TextBuilder extends MarkdownElementBuilder {
+ @override
+ Widget? visitText(md.Text text, TextStyle? preferredStyle) {
+ return Text(text.textContent);
+ }
+}
+
+class RawHtmlSyntax extends md.InlineSyntax {
+ RawHtmlSyntax() : super(r'<.+?>');
+
+ @override
+ bool onMatch(md.InlineParser parser, Match match) {
+ parser.addNode(md.Text(match[0]!));
+ return true;
+ }
+}
diff --git a/lib/ui/widgets/wide_popup.dart b/lib/ui/widgets/wide_popup.dart
index 5b023ee..12628a8 100644
--- a/lib/ui/widgets/wide_popup.dart
+++ b/lib/ui/widgets/wide_popup.dart
@@ -7,6 +7,7 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/drag_widget.dart';
class TUIKitWidePopup {
static OverlayEntry? entry;
+ static bool isShow = false;
static showSecondaryConfirmDialog({
required TUIKitWideModalOperationKey operationKey,
@@ -17,7 +18,7 @@ class TUIKitWidePopup {
VoidCallback? onCancel,
}) {
return TUIKitWidePopup.showPopupWindow(
- operationKey: operationKey,
+ operationKey: operationKey,
context: context,
isDarkBackground: false,
onCancel: onCancel,
@@ -56,37 +57,205 @@ class TUIKitWidePopup {
VoidCallback? onConfirm,
VoidCallback? onCancel,
}) async {
+ if (isShow) {
+ return;
+ }
+ isShow = true;
final TUISelfInfoViewModel selfInfoViewModel =
- serviceLocator();
+ serviceLocator();
- if(selfInfoViewModel.globalConfig?.showDesktopModalFunc != null){
+ if (selfInfoViewModel.globalConfig?.showDesktopModalFunc != null) {
final res = await selfInfoViewModel.globalConfig!.showDesktopModalFunc!(
- operationKey,
- context,
- child,
- theme,
- width,
- height,
- offset,
- initText,
- borderRadius,
- isDarkBackground,
- title,
- onSubmit,
- submitWidget,
- onConfirm,
- onCancel
- );
+ operationKey,
+ context,
+ child,
+ theme,
+ width,
+ height,
+ offset,
+ initText,
+ borderRadius,
+ isDarkBackground,
+ title,
+ onSubmit,
+ submitWidget,
+ onConfirm,
+ onCancel);
- if(res == true){
+ if (res == true) {
return;
}
}
+ final isUseMaterialAlert = (offset == null);
+
+ final Widget contentWidget = Container(
+ width: width,
+ height: height,
+ decoration: BoxDecoration(
+ borderRadius:
+ borderRadius ?? const BorderRadius.all(Radius.circular(16)),
+ color: theme?.wideBackgroundColor ?? const Color(0xFFffffff),
+ border: isDarkBackground
+ ? Border.all(
+ width: 2,
+ color: theme?.weakBackgroundColor ?? const Color(0xFFbebebe),
+ )
+ : null,
+ boxShadow: (isDarkBackground || isUseMaterialAlert)
+ ? null
+ : const [
+ BoxShadow(
+ color: Color(0xFFbebebe),
+ offset: Offset(3, 3),
+ blurRadius: 10,
+ spreadRadius: 1,
+ ),
+ ],
+ ),
+ child: Column(
+ children: [
+ if (title != null)
+ Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ color: hexToColor("f5f6f7"),
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16)),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ mainAxisSize: MainAxisSize.max,
+ children: [
+ Text(
+ title,
+ style: TextStyle(
+ fontSize: 18,
+ color: theme?.darkTextColor ?? const Color(0xFF444444)),
+ ),
+ InkWell(
+ onTap: () {
+ if (onSubmit != null) {
+ onSubmit();
+ }
+ isShow = false;
+ if (offset == null) {
+ Navigator.pop(context);
+ } else {
+ entry?.remove();
+ entry = null;
+ }
+ },
+ child: onSubmit != null
+ ? (submitWidget ?? const Icon(Icons.check))
+ : const Icon(Icons.close),
+ )
+ ],
+ ),
+ ),
+ if (title != null)
+ SizedBox(
+ height: 1,
+ child: Container(
+ color: theme?.weakDividerColor ?? const Color(0xFFE5E6E9),
+ ),
+ ),
+ if (height != null && width != null)
+ Expanded(child: child(() {
+ isShow = false;
+ if (isUseMaterialAlert) {
+ Navigator.pop(context);
+ } else {
+ entry?.remove();
+ entry = null;
+ }
+ })),
+ if (height == null || width == null)
+ child(() {
+ isShow = false;
+ if (isUseMaterialAlert) {
+ Navigator.pop(context);
+ } else {
+ entry?.remove();
+ entry = null;
+ }
+ }),
+ if (onCancel != null || onConfirm != null)
+ Container(
+ padding: const EdgeInsets.only(bottom: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ if (onCancel != null)
+ Container(
+ margin: const EdgeInsets.only(right: 16),
+ child: OutlinedButton(
+ onPressed: () {
+ isShow = false;
+ if (isUseMaterialAlert) {
+ Navigator.pop(context);
+ } else {
+ entry?.remove();
+ entry = null;
+ }
+ onCancel();
+ },
+ child: Text(
+ TIM_t("取消"),
+ style: TextStyle(
+ color: theme?.weakTextColor ?? Colors.black),
+ )),
+ ),
+ if (onConfirm != null)
+ Container(
+ margin: const EdgeInsets.only(right: 16),
+ child: ElevatedButton(
+ onPressed: () {
+ isShow = false;
+ if (isUseMaterialAlert) {
+ Navigator.pop(context);
+ } else {
+ entry?.remove();
+ entry = null;
+ }
+ onConfirm();
+ },
+ child: Text(
+ TIM_t("确定"),
+ style: TextStyle(color: theme?.primaryColor),
+ )),
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ );
+
+ if (isUseMaterialAlert) {
+ return showDialog(
+ barrierDismissible: false,
+ context: context,
+ builder: (context) {
+ return WillPopScope(
+ child: AlertDialog(
+ backgroundColor: Colors.transparent,
+ titlePadding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
+ contentPadding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
+ content: contentWidget,
+ ),
+ onWillPop: () {
+ return Future.value(false);
+ });
+ });
+ }
+
if (entry != null) {
return;
}
+
entry = OverlayEntry(builder: (BuildContext context) {
return Material(
color: Colors.transparent,
@@ -100,133 +269,13 @@ class TUIKitWidePopup {
},
initOffset: offset ??
(width != null && height != null
- ? Offset(MediaQuery.of(context).size.width * 0.5 - width / 2,
- MediaQuery.of(context).size.height * 0.5 - height / 2)
+ ? Offset(
+ MediaQuery.of(context).size.width * 0.5 - width / 2,
+ MediaQuery.of(context).size.height * 0.5 - height / 2)
: null),
- child: Container(
- width: width,
- height: height,
- decoration: BoxDecoration(
- borderRadius:
- borderRadius ?? const BorderRadius.all(Radius.circular(16)),
- color: theme?.wideBackgroundColor ?? const Color(0xFFffffff),
- border: isDarkBackground
- ? Border.all(
- width: 2,
- color:
- theme?.weakBackgroundColor ?? const Color(0xFFbebebe),
- )
- : null,
- boxShadow: isDarkBackground
- ? null
- : const [
- BoxShadow(
- color: Color(0xFFbebebe),
- offset: Offset(3, 3),
- blurRadius: 10,
- spreadRadius: 1,
- ),
- ],
- ),
- child: Column(
- children: [
- if (title != null)
- Container(
- padding: const EdgeInsets.all(16),
- decoration: BoxDecoration(
- color: hexToColor("f5f6f7"),
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(16),
- topRight: Radius.circular(16)),
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- mainAxisSize: MainAxisSize.max,
- children: [
- Text(
- title,
- style: TextStyle(
- fontSize: 18,
- color: theme?.darkTextColor ??
- const Color(0xFF444444)),
- ),
- InkWell(
- onTap: () {
- if (onSubmit != null) {
- onSubmit();
- }
- entry?.remove();
- entry = null;
- },
- child: onSubmit != null
- ? (submitWidget ?? const Icon(Icons.check))
- : const Icon(Icons.close),
- )
- ],
- ),
- ),
- if (title != null)
- SizedBox(
- height: 1,
- child: Container(
- color: theme?.weakDividerColor ?? const Color(0xFFE5E6E9),
- ),
- ),
- if (height != null && width != null)
- Expanded(child: child(() {
- entry?.remove();
- entry = null;
- })),
- if (height == null || width == null)
- child(() {
- entry?.remove();
- entry = null;
- }),
- if (onCancel != null || onConfirm != null)
- Container(
- padding: const EdgeInsets.only(bottom: 16),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- if (onCancel != null)
- Container(
- margin: const EdgeInsets.only(right: 16),
- child: OutlinedButton(
- onPressed: () {
- entry?.remove();
- entry = null;
- onCancel();
- },
- child: Text(
- TIM_t("取消"),
- style: TextStyle(
- color:
- theme?.weakTextColor ?? Colors.black),
- )),
- ),
- if (onConfirm != null)
- Container(
- margin: const EdgeInsets.only(right: 16),
- child: ElevatedButton(
- onPressed: () {
- entry?.remove();
- entry = null;
- onConfirm();
- },
- child: Text(
- TIM_t("确定"),
- style: TextStyle(color: theme?.primaryColor),
- )),
- ),
- ],
- ),
- )
- ],
- ),
- )),
+ child: contentWidget),
);
});
Overlay.of(context).insert(entry!);
-
}
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 92a3e2b..337966e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: tencent_cloud_chat_uikit
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
-version: 2.1.0+2
+version: 2.1.1
homepage: https://www.tencentcloud.com/products/im?from=pub
repository: https://github.com/TencentCloud/tc-chat-uikit-flutter
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html