feat: Upgrade to UIKit 2.3.2

This commit is contained in:
anonymous 2023-09-27 23:21:40 +08:00
parent c28e1e9529
commit 038a3d483d
10 changed files with 187 additions and 447 deletions

View File

@ -1,3 +1,14 @@
# 2.3.2
## Improvements
* Enhanced message list performance.
## Bug Fixes
* Fixed an issue that prevented the group member addition/removal modal from closing.
* Addressed several other bugs.
# 2.3.1 # 2.3.1
## Bug Fixes ## Bug Fixes

View File

@ -706,7 +706,6 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
Future<void> onMessageDownloadProgressCallback( Future<void> onMessageDownloadProgressCallback(
V2TimMessageDownloadProgress messageProgress) async { V2TimMessageDownloadProgress messageProgress) async {
outputLogger.i(messageProgress.toJson());
final currentProgress = getMessageProgress(messageProgress.msgID); final currentProgress = getMessageProgress(messageProgress.msgID);
if (messageProgress.isFinish && currentProgress < 100) { if (messageProgress.isFinish && currentProgress < 100) {
@ -1120,9 +1119,7 @@ class TUIChatGlobalModel extends ChangeNotifier implements TIMUIKitClass {
listWithTimestamp.add(V2TimMessage.fromJson(item.toJson())); listWithTimestamp.add(V2TimMessage.fromJson(item.toJson()));
} }
} }
final returnValue = listWithTimestamp.reversed.toList(); return listWithTimestamp.reversed.toList();
outputLogger.i(returnValue.map((e) => e.toJson()).toList().toString());
return returnValue;
} }
HistoryMessagePosition getMessageListPosition(String? conversationID) { HistoryMessagePosition getMessageListPosition(String? conversationID) {

View File

@ -42,7 +42,7 @@ class TUIKitOutput extends LogOutput {
Future<String> getPlatformLogPath({String? path}) async { Future<String> getPlatformLogPath({String? path}) async {
if (TencentUtils.checkString(path) != null) { if (TencentUtils.checkString(path) != null) {
outputLogger.i("The path to local log: $path"); print("The path to local log: $path");
return path!; return path!;
} }
@ -54,7 +54,7 @@ class TUIKitOutput extends LogOutput {
"${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}"; "${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}";
final logPath = p.join(documentsDirectoryPath, "Documents", ".TencentCloudChat", final logPath = p.join(documentsDirectoryPath, "Documents", ".TencentCloudChat",
pkgName, "uikit_log", 'Flutter-TUIKit-$timeName.log'); pkgName, "uikit_log", 'Flutter-TUIKit-$timeName.log');
outputLogger.i("The path to local log: $logPath"); print("The path to local log: $logPath");
return logPath; return logPath;
} }

View File

@ -7,15 +7,17 @@ import 'package:provider/provider.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
// ignore: unused_import // ignore: unused_import
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_config.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_config.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/keepalive_wrapper.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/keepalive_wrapper.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart'; import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart';
import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart'; import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart';
@ -73,8 +75,7 @@ class TIMUIKitHistoryMessageList extends StatefulWidget {
final V2TimMessage? initFindingMsg; final V2TimMessage? initFindingMsg;
/// use for load more message /// use for load more message
final Future<void> Function(String?, LoadDirection direction, [int?]) final Future<void> Function(String?, LoadDirection direction, [int?]) onLoadMore;
onLoadMore;
/// configuration for list view /// configuration for list view
final TIMUIKitHistoryMessageListConfig? mainHistoryListConfig; final TIMUIKitHistoryMessageListConfig? mainHistoryListConfig;
@ -104,8 +105,7 @@ class TIMUIKitHistoryMessageList extends StatefulWidget {
State<StatefulWidget> createState() => _TIMUIKitHistoryMessageListState(); State<StatefulWidget> createState() => _TIMUIKitHistoryMessageListState();
} }
class _TIMUIKitHistoryMessageListState class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMessageList> {
extends TIMUIKitState<TIMUIKitHistoryMessageList> {
V2TimMessage? findingMsg; V2TimMessage? findingMsg;
String findingSeq = ""; String findingSeq = "";
late TIMUIKitHistoryMessageListController _controller; late TIMUIKitHistoryMessageListController _controller;
@ -116,8 +116,7 @@ class _TIMUIKitHistoryMessageListState
void initState() { void initState() {
super.initState(); super.initState();
_controller = widget.controller ?? TIMUIKitHistoryMessageListController(); _controller = widget.controller ?? TIMUIKitHistoryMessageListController();
_autoScrollController = _autoScrollController = _controller.scrollController ?? AutoScrollController();
_controller.scrollController ?? AutoScrollController();
_controller.addListener(_controllerListener); _controller.addListener(_controllerListener);
initFinding(); initFinding();
} }
@ -163,18 +162,14 @@ class _TIMUIKitHistoryMessageListState
findingMsg = null; findingMsg = null;
findingSeq = ""; findingSeq = "";
loadingPlace = LoadingPlace.none; loadingPlace = LoadingPlace.none;
onTIMCallback(TIMCallback( onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("无法定位到原消息"), infoCode: 6660401));
type: TIMCallbackType.INFO,
infoRecommendText: TIM_t("无法定位到原消息"),
infoCode: 6660401));
} }
_onScrollToIndex(V2TimMessage targetMsg) { _onScrollToIndex(V2TimMessage targetMsg) {
// This method called by @ messages or messages been searched, aims to jump to target message // This method called by @ messages or messages been searched, aims to jump to target message
loadingPlace = LoadingPlace.top; loadingPlace = LoadingPlace.top;
const int singleLoadAmount = kIsWeb ? 15 : 40; const int singleLoadAmount = kIsWeb ? 15 : 40;
final lastTimestamp = final lastTimestamp = widget.messageList[widget.messageList.length - 1]?.timestamp;
widget.messageList[widget.messageList.length - 1]?.timestamp;
final msgList = widget.messageList; final msgList = widget.messageList;
final targetTimeStamp = targetMsg.timestamp!; final targetTimeStamp = targetMsg.timestamp!;
@ -184,9 +179,7 @@ class _TIMUIKitHistoryMessageListState
int targetIndex = 1; int targetIndex = 1;
for (int i = msgList.length - 1; i >= 0; i--) { for (int i = msgList.length - 1; i >= 0; i--) {
final currentMsg = msgList[i]; final currentMsg = msgList[i];
if (currentMsg?.timestamp == targetTimeStamp && if (currentMsg?.timestamp == targetTimeStamp && currentMsg?.elemType != 11 && currentMsg!.msgID == targetMsg.msgID) {
currentMsg?.elemType != 11 &&
currentMsg!.msgID == targetMsg.msgID) {
// find the target index by timestamp and msgID // find the target index by timestamp and msgID
isFound = true; isFound = true;
targetIndex = -i; targetIndex = -i;
@ -202,10 +195,8 @@ class _TIMUIKitHistoryMessageListState
); );
// execute twice for accurate position, as the position located firstly can be wrong // execute twice for accurate position, as the position located firstly can be wrong
_autoScrollController.scrollToIndex(targetIndex, _autoScrollController.scrollToIndex(targetIndex, preferPosition: AutoScrollPosition.middle);
preferPosition: AutoScrollPosition.middle); _autoScrollController.scrollToIndex(targetIndex, preferPosition: AutoScrollPosition.middle);
_autoScrollController.scrollToIndex(targetIndex,
preferPosition: AutoScrollPosition.middle);
widget.model.jumpMsgID = targetMsg.msgID!; widget.model.jumpMsgID = targetMsg.msgID!;
loadingPlace = LoadingPlace.none; loadingPlace = LoadingPlace.none;
@ -258,8 +249,7 @@ class _TIMUIKitHistoryMessageListState
targetIndex, targetIndex,
preferPosition: AutoScrollPosition.middle, preferPosition: AutoScrollPosition.middle,
); );
_autoScrollController.scrollToIndex(targetIndex, _autoScrollController.scrollToIndex(targetIndex, preferPosition: AutoScrollPosition.middle);
preferPosition: AutoScrollPosition.middle);
if (targetMsgID != null && targetMsgID != "") { if (targetMsgID != null && targetMsgID != "") {
widget.model.jumpMsgID = targetMsgID; widget.model.jumpMsgID = targetMsgID;
} }
@ -270,8 +260,7 @@ class _TIMUIKitHistoryMessageListState
} else { } else {
if (widget.model.haveMoreData) { if (widget.model.haveMoreData) {
findingSeq = targetSeq; findingSeq = targetSeq;
widget.onLoadMore(_getMessageId(widget.messageList.length - 1), widget.onLoadMore(_getMessageId(widget.messageList.length - 1), LoadDirection.previous, singleLoadAmount);
LoadDirection.previous, singleLoadAmount);
} else { } else {
showCantFindMsg(); showCantFindMsg();
} }
@ -279,8 +268,7 @@ class _TIMUIKitHistoryMessageListState
} }
_onScrollToIndexBegin(V2TimMessage targetMsg) { _onScrollToIndexBegin(V2TimMessage targetMsg) {
final lastTimestamp = final lastTimestamp = widget.messageList[widget.messageList.length - 1]?.timestamp;
widget.messageList[widget.messageList.length - 1]?.timestamp;
final msgList = widget.messageList; final msgList = widget.messageList;
final int targetTimeStamp = targetMsg.timestamp!; final int targetTimeStamp = targetMsg.timestamp!;
@ -289,9 +277,7 @@ class _TIMUIKitHistoryMessageListState
int targetIndex = 1; int targetIndex = 1;
for (int i = msgList.length - 1; i >= 0; i--) { for (int i = msgList.length - 1; i >= 0; i--) {
final currentMsg = msgList[i]; final currentMsg = msgList[i];
if (currentMsg?.timestamp == targetTimeStamp && if (currentMsg?.timestamp == targetTimeStamp && currentMsg?.elemType != 11 && currentMsg!.msgID == targetMsg.msgID) {
currentMsg?.elemType != 11 &&
currentMsg!.msgID == targetMsg.msgID) {
isFound = true; isFound = true;
targetIndex = -i; targetIndex = -i;
break; break;
@ -310,11 +296,8 @@ class _TIMUIKitHistoryMessageListState
if (receivedMessageListCount == 0) { if (receivedMessageListCount == 0) {
return []; return [];
} }
final haveTimeStampMessage = final haveTimeStampMessage = widget.messageList[receivedMessageListCount]?.elemType == 11;
widget.messageList[receivedMessageListCount]?.elemType == 11; final endPoint = haveTimeStampMessage ? receivedMessageListCount + 1 : receivedMessageListCount;
final endPoint = haveTimeStampMessage
? receivedMessageListCount + 1
: receivedMessageListCount;
return widget.messageList.sublist(0, endPoint).reversed.toList(); return widget.messageList.sublist(0, endPoint).reversed.toList();
} }
@ -333,19 +316,14 @@ class _TIMUIKitHistoryMessageListState
final receivedNewMessageList = globalModel.receivedMessageListCount; final receivedNewMessageList = globalModel.receivedMessageListCount;
final shouldShowUnreadMessage = receivedNewMessageList > 0; final shouldShowUnreadMessage = receivedNewMessageList > 0;
final unreadMessageList = _getReceivedMessageList(receivedNewMessageList); final unreadMessageList = _getReceivedMessageList(receivedNewMessageList);
final readMessageList = messageList final readMessageList = messageList.sublist(unreadMessageList.length, messageList.length).toList();
.sublist(unreadMessageList.length, messageList.length)
.toList();
final throttleFunction = final throttleFunction = OptimizeUtils.multiThrottle((index, LoadDirection direction) async {
OptimizeUtils.multiThrottle((index, LoadDirection direction) async { final msgID = TIMUIKitChatUtils.getMessageIDWithinIndex(readMessageList, index);
final msgID =
TIMUIKitChatUtils.getMessageIDWithinIndex(readMessageList, index);
await widget.onLoadMore(msgID, direction); await widget.onLoadMore(msgID, direction);
}, 20); }, 20);
final throttleFunctionWithMsgID = final throttleFunctionWithMsgID = OptimizeUtils.multiThrottle((msgID, LoadDirection direction) async {
OptimizeUtils.multiThrottle((msgID, LoadDirection direction) async {
await widget.onLoadMore(msgID, direction); await widget.onLoadMore(msgID, direction);
}, 200); }, 200);
@ -368,63 +346,43 @@ class _TIMUIKitHistoryMessageListState
center: shouldShowUnreadMessage ? centerKey : null, center: shouldShowUnreadMessage ? centerKey : null,
key: widget.mainHistoryListConfig?.key, key: widget.mainHistoryListConfig?.key,
primary: widget.mainHistoryListConfig?.primary, primary: widget.mainHistoryListConfig?.primary,
physics: (widget.isAllowScroll == false) physics: (widget.isAllowScroll == false) ? const NeverScrollableScrollPhysics() : widget.mainHistoryListConfig?.physics,
? const NeverScrollableScrollPhysics()
: widget.mainHistoryListConfig?.physics,
// padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero, // padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
// itemExtent: widget.mainHistoryListConfig?.itemExtent, // itemExtent: widget.mainHistoryListConfig?.itemExtent,
// prototypeItem: widget.mainHistoryListConfig?.prototypeItem, // prototypeItem: widget.mainHistoryListConfig?.prototypeItem,
cacheExtent: widget.mainHistoryListConfig?.cacheExtent ?? 1500, cacheExtent: widget.mainHistoryListConfig?.cacheExtent ?? 1500,
semanticChildCount: semanticChildCount: widget.mainHistoryListConfig?.semanticChildCount,
widget.mainHistoryListConfig?.semanticChildCount, dragStartBehavior: widget.mainHistoryListConfig?.dragStartBehavior ?? DragStartBehavior.start,
dragStartBehavior: keyboardDismissBehavior: widget.mainHistoryListConfig?.keyboardDismissBehavior ?? ScrollViewKeyboardDismissBehavior.manual,
widget.mainHistoryListConfig?.dragStartBehavior ??
DragStartBehavior.start,
keyboardDismissBehavior:
widget.mainHistoryListConfig?.keyboardDismissBehavior ??
ScrollViewKeyboardDismissBehavior.manual,
restorationId: widget.mainHistoryListConfig?.restorationId, restorationId: widget.mainHistoryListConfig?.restorationId,
clipBehavior: clipBehavior: widget.mainHistoryListConfig?.clipBehavior ?? Clip.hardEdge,
widget.mainHistoryListConfig?.clipBehavior ?? Clip.hardEdge,
reverse: true, reverse: true,
shrinkWrap: !shouldShowUnreadMessage, shrinkWrap: !shouldShowUnreadMessage,
controller: _autoScrollController, controller: _autoScrollController,
slivers: [ slivers: [
SliverPadding( SliverPadding(
padding: padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
sliver: SliverList( sliver: SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) { (BuildContext context, int index) {
final messageItem = unreadMessageList[index]; final messageItem = unreadMessageList[index];
if (index == unreadMessageList.length - 1 && if (index == unreadMessageList.length - 1 && widget.model.haveMoreLatestData == true) {
widget.model.haveMoreLatestData == true) { throttleFunctionWithMsgID(messageItem?.msgID ?? "", LoadDirection.latest);
throttleFunctionWithMsgID(
messageItem?.msgID ?? "",
LoadDirection.latest);
} }
outputLogger.i("Rendering a unread message: ${getMessageIdentifier(messageItem, 0)}, message Type: ${messageItem?.elemType}");
return AutoScrollTag( return AutoScrollTag(
controller: _autoScrollController, controller: _autoScrollController,
index: -index, index: -index,
key: ValueKey( key: ValueKey(getMessageIdentifier(messageItem, index)),
getMessageIdentifier(messageItem, index)),
highlightColor: Colors.black.withOpacity(0.1), highlightColor: Colors.black.withOpacity(0.1),
child: KeepAliveWrapper( child: KeepAliveWrapper(keepAlive: messageItem?.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND, child: Container(child: _getMessageItemBuilder(messageItem))),
keepAlive: messageItem?.elemType ==
MessageElemType.V2TIM_ELEM_TYPE_SOUND,
child: Container(
child:
_getMessageItemBuilder(messageItem))),
); );
}, },
childCount: unreadMessageList.length, childCount: unreadMessageList.length,
findChildIndexCallback: (Key key) { findChildIndexCallback: (Key key) {
final ValueKey<String> valueKey = final ValueKey<String> valueKey = key as ValueKey<String>;
key as ValueKey<String>;
final String data = valueKey.value; final String data = valueKey.value;
final int index = unreadMessageList.indexWhere( final int index = unreadMessageList.indexWhere((element) => getMessageIdentifier(element, 0) == data);
(element) =>
getMessageIdentifier(element, 0) == data);
return index != -1 ? index : null; return index != -1 ? index : null;
})), })),
), ),
@ -433,8 +391,7 @@ class _TIMUIKitHistoryMessageListState
key: centerKey, key: centerKey,
), ),
SliverPadding( SliverPadding(
padding: padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
sliver: Selector<TUIChatSeparateViewModel, bool>( sliver: Selector<TUIChatSeparateViewModel, bool>(
selector: (context, model) { selector: (context, model) {
return model.haveMoreData; return model.haveMoreData;
@ -449,64 +406,41 @@ class _TIMUIKitHistoryMessageListState
final messageItem = readMessageList[index]; final messageItem = readMessageList[index];
if (index == readMessageList.length - 1) { if (index == readMessageList.length - 1) {
if (haveMoreData) { if (haveMoreData) {
throttleFunction( throttleFunction(index, LoadDirection.previous);
index, LoadDirection.previous);
return Column( return Column(
children: [ children: [
LoadingAnimationWidget LoadingAnimationWidget.staggeredDotsWave(
.staggeredDotsWave( color: theme.weakTextColor ?? Colors.grey,
color: theme.weakTextColor ??
Colors.grey,
size: 28, size: 28,
), ),
AutoScrollTag( AutoScrollTag(
controller: _autoScrollController, controller: _autoScrollController,
index: -index, index: -index,
key: ValueKey(getMessageIdentifier( key: ValueKey(getMessageIdentifier(messageItem, index)),
messageItem, index)), highlightColor: Colors.black.withOpacity(0.1),
highlightColor: child: KeepAliveWrapper(keepAlive: messageItem?.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND, child: Container(child: _getMessageItemBuilder(messageItem))),
Colors.black.withOpacity(0.1),
child: KeepAliveWrapper(
keepAlive: messageItem
?.elemType ==
MessageElemType
.V2TIM_ELEM_TYPE_SOUND,
child: Container(
child: _getMessageItemBuilder(
messageItem))),
), ),
], ],
); );
} }
} }
if (index == 0 && if (index == 0 && widget.model.haveMoreLatestData == true && globalModel.receivedMessageListCount < 10) {
widget.model.haveMoreLatestData == true &&
globalModel.receivedMessageListCount < 10) {
throttleFunction(index, LoadDirection.latest); throttleFunction(index, LoadDirection.latest);
} }
outputLogger.i("Rendering a read message: ${getMessageIdentifier(messageItem, 0)}, message Type: ${messageItem?.elemType}");
return AutoScrollTag( return AutoScrollTag(
controller: _autoScrollController, controller: _autoScrollController,
index: -index, index: -index,
key: ValueKey( key: ValueKey(getMessageIdentifier(messageItem, index)),
getMessageIdentifier(messageItem, index)),
highlightColor: Colors.black.withOpacity(0.1), highlightColor: Colors.black.withOpacity(0.1),
child: KeepAliveWrapper( child: KeepAliveWrapper(keepAlive: messageItem?.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND, child: Container(child: _getMessageItemBuilder(messageItem))),
keepAlive: messageItem?.elemType ==
MessageElemType.V2TIM_ELEM_TYPE_SOUND,
child: Container(
child: _getMessageItemBuilder(
messageItem))),
); );
}, },
childCount: readMessageList.length, childCount: readMessageList.length,
findChildIndexCallback: (Key key) { findChildIndexCallback: (Key key) {
final ValueKey<String> valueKey = final ValueKey<String> valueKey = key as ValueKey<String>;
key as ValueKey<String>;
final String data = valueKey.value; final String data = valueKey.value;
final int index = readMessageList.indexWhere( final int index = readMessageList.indexWhere((element) => getMessageIdentifier(element, 0) == data);
(element) =>
getMessageIdentifier(element, 0) ==
data);
return index > -1 ? index : null; return index > -1 ? index : null;
})); }));
}, },
@ -539,17 +473,14 @@ class TIMUIKitHistoryMessageListSelector extends TIMUIKitStatelessWidget {
final Widget Function(BuildContext, List<V2TimMessage?>, Widget?) builder; final Widget Function(BuildContext, List<V2TimMessage?>, Widget?) builder;
final String conversationID; final String conversationID;
TIMUIKitHistoryMessageListSelector( TIMUIKitHistoryMessageListSelector({Key? key, required this.builder, required this.conversationID}) : super(key: key);
{Key? key, required this.builder, required this.conversationID})
: super(key: key);
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
return Selector<TUIChatGlobalModel, List<V2TimMessage?>>( return Selector<TUIChatGlobalModel, List<V2TimMessage?>>(
builder: builder, builder: builder,
shouldRebuild: (previous, next) { shouldRebuild: (previous, next) {
final isEquals = final isEquals = const DeepCollectionEquality.unordered().equals(previous, next);
const DeepCollectionEquality.unordered().equals(previous, next);
return !isEquals; return !isEquals;
}, },
selector: (context, model) { selector: (context, model) {

View File

@ -1,26 +1,27 @@
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:collection/collection.dart';
import 'package:diff_match_patch/diff_match_patch.dart'; import 'package:diff_match_patch/diff_match_patch.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_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/emoji_text.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:collection/collection.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart'; import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart'; import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/special_text/emoji_text.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_at_text.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_at_text.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/narrow.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/wide.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_layout/wide.dart';
enum MuteStatus { none, me, all } enum MuteStatus { none, me, all }
@ -141,8 +142,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
double inputWidth = 900; double inputWidth = 900;
Map<String, V2TimGroupMemberFullInfo> mentionedMembersMap = {}; Map<String, V2TimGroupMemberFullInfo> mentionedMembersMap = {};
late TextEditingController textEditingController; late TextEditingController textEditingController;
final TUIConversationViewModel conversationModel = final TUIConversationViewModel conversationModel = serviceLocator<TUIConversationViewModel>();
serviceLocator<TUIConversationViewModel>();
final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>(); final TUISelfInfoViewModel selfModel = serviceLocator<TUISelfInfoViewModel>();
MuteStatus muteStatus = MuteStatus.none; MuteStatus muteStatus = MuteStatus.none;
bool _isComposingText = false; bool _isComposingText = false;
@ -154,56 +154,38 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
// Keep using original scheme. // Keep using original scheme.
return; return;
} }
final stickerConfig = final stickerConfig = widget.model.chatConfig.stickerPanelConfig ?? StickerPanelConfig();
widget.model.chatConfig.stickerPanelConfig ?? StickerPanelConfig();
if (stickerConfig.useTencentCloudChatStickerPackage) { if (stickerConfig.useTencentCloudChatStickerPackage) {
final tccEmojiSet = TUIKitStickerConstData.emojiList final tccEmojiSet = TUIKitStickerConstData.emojiList.firstWhere((element) => element.name == "tcc1");
.firstWhere((element) => element.name == "tcc1");
stickerPackageList.add(CustomStickerPackage( stickerPackageList.add(CustomStickerPackage(
name: tccEmojiSet.name, name: tccEmojiSet.name,
baseUrl: "assets/custom_face_resource/${tccEmojiSet.name}", baseUrl: "assets/custom_face_resource/${tccEmojiSet.name}",
isEmoji: tccEmojiSet.isEmoji, isEmoji: tccEmojiSet.isEmoji,
isDefaultEmoji: true, isDefaultEmoji: true,
stickerList: tccEmojiSet.list stickerList: tccEmojiSet.list.asMap().keys.map((idx) => CustomSticker(index: idx, name: tccEmojiSet.list[idx])).toList(),
.asMap()
.keys
.map((idx) =>
CustomSticker(index: idx, name: tccEmojiSet.list[idx]))
.toList(),
menuItem: CustomSticker( menuItem: CustomSticker(
index: 0, index: 0,
name: tccEmojiSet.icon, name: tccEmojiSet.icon,
))); )));
} }
if (stickerConfig.useQQStickerPackage) { if (stickerConfig.useQQStickerPackage) {
final qqEmojiSet = TUIKitStickerConstData.emojiList final qqEmojiSet = TUIKitStickerConstData.emojiList.firstWhere((element) => element.name == "4349");
.firstWhere((element) => element.name == "4349");
stickerPackageList.add(CustomStickerPackage( stickerPackageList.add(CustomStickerPackage(
name: qqEmojiSet.name, name: qqEmojiSet.name,
baseUrl: "assets/custom_face_resource/${qqEmojiSet.name}", baseUrl: "assets/custom_face_resource/${qqEmojiSet.name}",
isEmoji: qqEmojiSet.isEmoji, isEmoji: qqEmojiSet.isEmoji,
isDefaultEmoji: true, isDefaultEmoji: true,
stickerList: qqEmojiSet.list stickerList: qqEmojiSet.list.asMap().keys.map((idx) => CustomSticker(index: idx, name: qqEmojiSet.list[idx])).toList(),
.asMap()
.keys
.map((idx) =>
CustomSticker(index: idx, name: qqEmojiSet.list[idx]))
.toList(),
menuItem: CustomSticker( menuItem: CustomSticker(
index: 0, index: 0,
name: qqEmojiSet.icon, name: qqEmojiSet.icon,
))); )));
} }
if (stickerConfig.unicodeEmojiList.isNotEmpty) { if (stickerConfig.unicodeEmojiList.isNotEmpty) {
final defEmojiList = final defEmojiList = TUIKitStickerConstData.defaultUnicodeEmojiList.map((emojiItem) {
TUIKitStickerConstData.defaultUnicodeEmojiList.map((emojiItem) { return CustomSticker(index: 0, name: emojiItem.toString(), unicode: emojiItem);
return CustomSticker(
index: 0, name: emojiItem.toString(), unicode: emojiItem);
}).toList(); }).toList();
stickerPackageList.add(CustomStickerPackage( stickerPackageList.add(CustomStickerPackage(name: "defaultEmoji", stickerList: defEmojiList, menuItem: defEmojiList[0]));
name: "defaultEmoji",
stickerList: defEmojiList,
menuItem: defEmojiList[0]));
} }
stickerPackageList.addAll(stickerConfig.customStickerPackages); stickerPackageList.addAll(stickerConfig.customStickerPackages);
@ -253,9 +235,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
textEditingController.text = text; textEditingController.text = text;
if (TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop) { if (TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop) {
textEditingController.selection = TextSelection.fromPosition( textEditingController.selection = TextSelection.fromPosition(TextPosition(offset: currentCursor ?? textEditingController.text.length));
TextPosition(
offset: currentCursor ?? textEditingController.text.length));
focusNode.requestFocus(); focusNode.requestFocus();
} }
} }
@ -263,9 +243,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
void addStickerToText(String sticker) { void addStickerToText(String sticker) {
final currentText = textEditingController.text; final currentText = textEditingController.text;
if (currentCursor != null && if (currentCursor != null && currentCursor! > -1 && currentCursor! < currentText.length + 1) {
currentCursor! > -1 &&
currentCursor! < currentText.length + 1) {
final firstString = currentText.substring(0, currentCursor); final firstString = currentText.substring(0, currentCursor);
final secondString = currentText.substring(currentCursor!); final secondString = currentText.substring(currentCursor!);
currentCursor = currentCursor! + sticker.length; currentCursor = currentCursor! + sticker.length;
@ -276,8 +254,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} }
if (TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop) { if (TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop) {
textEditingController.selection = TextSelection.fromPosition(TextPosition( textEditingController.selection = TextSelection.fromPosition(TextPosition(offset: currentCursor ?? textEditingController.text.length));
offset: currentCursor ?? textEditingController.text.length));
focusNode.requestFocus(); focusNode.requestFocus();
} }
} }
@ -286,23 +263,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
return text.replaceAll(RegExp(r'\ufeff'), ""); return text.replaceAll(RegExp(r'\ufeff'), "");
} }
Future handleSetDraftText( Future handleSetDraftText({String? id, ConvType? convType, String? groupID}) async {
{String? id, ConvType? convType, String? groupID}) async {
String text = textEditingController.text; String text = textEditingController.text;
String convID = id ?? widget.conversationID; String convID = id ?? widget.conversationID;
final isTopic = convID.contains("@TOPIC#"); final isTopic = convID.contains("@TOPIC#");
String conversationID = isTopic String conversationID = isTopic ? convID : ((convType ?? widget.conversationType) == ConvType.c2c ? "c2c_$convID" : "group_$convID");
? convID
: ((convType ?? widget.conversationType) == ConvType.c2c
? "c2c_$convID"
: "group_$convID");
String draftText = _filterU200b(text); String draftText = _filterU200b(text);
return await conversationModel.setConversationDraft( return await conversationModel.setConversationDraft(groupID: groupID ?? widget.groupID, isTopic: isTopic, isAllowWeb: widget.model.chatConfig.isUseDraftOnWeb, conversationID: conversationID, draftText: draftText);
groupID: groupID ?? widget.groupID,
isTopic: isTopic,
isAllowWeb: widget.model.chatConfig.isUseDraftOnWeb,
conversationID: conversationID,
draftText: draftText);
} }
// onSubmitted一样 // onSubmitted一样
@ -350,21 +317,9 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} }
if (widget.model.repliedMessage != null) { if (widget.model.repliedMessage != null) {
MessageUtils.handleMessageError( MessageUtils.handleMessageError(widget.model.sendFaceMessage(index: index, data: data, convID: widget.conversationID, convType: convType), context);
widget.model.sendFaceMessage(
index: index,
data: data,
convID: widget.conversationID,
convType: convType),
context);
} else { } else {
MessageUtils.handleMessageError( MessageUtils.handleMessageError(widget.model.sendFaceMessage(index: index, data: data, convID: widget.conversationID, convType: convType), context);
widget.model.sendFaceMessage(
index: index,
data: data,
convID: widget.conversationID,
convType: convType),
context);
} }
} }
@ -384,24 +339,11 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
final convType = widget.conversationType; final convType = widget.conversationType;
if (text.isNotEmpty && text != zeroWidthSpace) { if (text.isNotEmpty && text != zeroWidthSpace) {
if (widget.model.repliedMessage != null) { if (widget.model.repliedMessage != null) {
MessageUtils.handleMessageError( MessageUtils.handleMessageError(widget.model.sendReplyMessage(text: text, convID: widget.conversationID, convType: convType, atUserIDList: getUserIdFromMemberInfoMap()), context);
widget.model.sendReplyMessage(
text: text,
convID: widget.conversationID,
convType: convType,
atUserIDList: getUserIdFromMemberInfoMap()),
context);
} else if (mentionedMembersMap.isNotEmpty) { } else if (mentionedMembersMap.isNotEmpty) {
widget.model.sendTextAtMessage( widget.model.sendTextAtMessage(text: text, convType: widget.conversationType, convID: widget.conversationID, atUserList: getUserIdFromMemberInfoMap());
text: text,
convType: widget.conversationType,
convID: widget.conversationID,
atUserList: getUserIdFromMemberInfoMap());
} else { } else {
MessageUtils.handleMessageError( MessageUtils.handleMessageError(widget.model.sendTextMessage(text: text, convID: widget.conversationID, convType: convType), context);
widget.model.sendTextMessage(
text: text, convID: widget.conversationID, convType: convType),
context);
} }
textEditingController.clear(); textEditingController.clear();
currentCursor = null; currentCursor = null;
@ -414,8 +356,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} }
void goDownBottom() { void goDownBottom() {
if (globalModel.getMessageListPosition(widget.conversationID) == if (globalModel.getMessageListPosition(widget.conversationID) == HistoryMessagePosition.notShowLatest) {
HistoryMessagePosition.notShowLatest) {
return; return;
} }
Future.delayed(const Duration(milliseconds: 50), () { Future.delayed(const Duration(milliseconds: 50), () {
@ -444,18 +385,14 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} }
String _getShowName(V2TimGroupMemberFullInfo? item) { String _getShowName(V2TimGroupMemberFullInfo? item) {
return TencentUtils.checkStringWithoutSpace(item?.nameCard) ?? return TencentUtils.checkStringWithoutSpace(item?.nameCard) ?? TencentUtils.checkStringWithoutSpace(item?.nickName) ?? TencentUtils.checkStringWithoutSpace(item?.userID) ?? "";
TencentUtils.checkStringWithoutSpace(item?.nickName) ??
TencentUtils.checkStringWithoutSpace(item?.userID) ??
"";
} }
mentionMemberInMessage(String? userID, String? nickName) { mentionMemberInMessage(String? userID, String? nickName) {
if (TencentUtils.checkString(userID) == null) { if (TencentUtils.checkString(userID) == null) {
focusNode.requestFocus(); focusNode.requestFocus();
} else { } else {
final memberInfo = widget.model.groupMemberList final memberInfo = widget.model.groupMemberList?.firstWhereOrNull((element) => element?.userID == userID) ??
?.firstWhereOrNull((element) => element?.userID == userID) ??
V2TimGroupMemberFullInfo( V2TimGroupMemberFullInfo(
userID: userID ?? "", userID: userID ?? "",
nickName: nickName, nickName: nickName,
@ -466,8 +403,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
//please do not delete space //please do not delete space
focusNode.requestFocus(); focusNode.requestFocus();
textEditingController.text = text; textEditingController.text = text;
textEditingController.selection = textEditingController.selection = TextSelection.fromPosition(TextPosition(offset: text.length));
TextSelection.fromPosition(TextPosition(offset: text.length));
lastText = text; lastText = text;
_isComposingText = false; _isComposingText = false;
narrowTextFieldKey.currentState?.showKeyboard = true; narrowTextFieldKey.currentState?.showKeyboard = true;
@ -495,16 +431,10 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
maxLines: null, maxLines: null,
); );
textPainter.layout(maxWidth: inputWidth); textPainter.layout(maxWidth: inputWidth);
final TextPosition lastLineOffset = textPainter final TextPosition lastLineOffset = textPainter.getPositionForOffset(Offset(textPainter.width, textPainter.height));
.getPositionForOffset(Offset(textPainter.width, textPainter.height)); final Offset caretPosition = textPainter.getOffsetForCaret(lastLineOffset, Rect.zero);
final Offset caretPosition =
textPainter.getOffsetForCaret(lastLineOffset, Rect.zero);
final dx = min(inputWidth - 180, caretPosition.dx + 16); final dx = min(inputWidth - 180, caretPosition.dx + 16);
final dy = max( final dy = max(24, 21 * widget.model.chatConfig.desktopMessageInputFieldLines - caretPosition.dy).toDouble();
24,
21 * widget.model.chatConfig.desktopMessageInputFieldLines -
caretPosition.dy)
.toDouble();
return Offset(dx, dy); return Offset(dx, dy);
} }
@ -541,19 +471,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
mentionedMembersMap = map; mentionedMembersMap = map;
} }
(int, String, bool)? findChangedCharacter( (int, String, bool)? findChangedCharacter(String originalString, String newString) {
String originalString, String newString) {
if (newString.length < originalString.length) { if (newString.length < originalString.length) {
final originalStringLength = originalString.length; final originalStringLength = originalString.length;
final newStringLength = newString.length; final newStringLength = newString.length;
for (int i = 0; i < newString.length; ++i) { for (int i = 0; i < newString.length; ++i) {
if (originalString[originalStringLength - i - 1] != if (originalString[originalStringLength - i - 1] != newString[newStringLength - i - 1]) {
newString[newStringLength - i - 1]) { return (newStringLength - i, originalString[originalStringLength - i - 1], false);
return (
newStringLength - i,
originalString[originalStringLength - i - 1],
false
);
} }
} }
return (newString.length, originalString[newString.length], false); return (newString.length, originalString[newString.length], false);
@ -572,11 +496,8 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
_handleAtText(String text, TUIChatSeparateViewModel model) async { _handleAtText(String text, TUIChatSeparateViewModel model) async {
final text = textEditingController.text; final text = textEditingController.text;
final String originalText = lastText; final String originalText = lastText;
String? groupID = widget.conversationType == ConvType.group String? groupID = widget.conversationType == ConvType.group ? widget.conversationID : null;
? widget.conversationID final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
: null;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
if (groupID == null) { if (groupID == null) {
lastText = text; lastText = text;
@ -596,11 +517,9 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
String atTag = originalText.substring(atIndex, spaceIndex); String atTag = originalText.substring(atIndex, spaceIndex);
String deletedChar = originalText[diffIndex]; String deletedChar = originalText[diffIndex];
if (shouldRemoveAtTag(atTag, deletedChar)) { if (shouldRemoveAtTag(atTag, deletedChar)) {
final newText = originalText.substring(0, atIndex) + final newText = originalText.substring(0, atIndex) + originalText.substring(spaceIndex + 1);
originalText.substring(spaceIndex + 1);
textEditingController.text = newText; textEditingController.text = newText;
textEditingController.selection = textEditingController.selection = TextSelection.collapsed(offset: atIndex);
TextSelection.collapsed(offset: atIndex);
lastText = newText; lastText = newText;
updateMentionedMap(); updateMentionedMap();
return; return;
@ -610,13 +529,10 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} }
final int selfRole = widget.model.selfMemberInfo?.role ?? 0; final int selfRole = widget.model.selfMemberInfo?.role ?? 0;
final bool canAtAll = final bool canAtAll = (selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN || selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER);
(selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN ||
selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER);
if (isDesktopScreen) { if (isDesktopScreen) {
(int, String, bool)? changedCharacterRecord = (int, String, bool)? changedCharacterRecord = findChangedCharacter(originalText, text);
findChangedCharacter(originalText, text);
int? changedTextPosition = changedCharacterRecord?.$1; int? changedTextPosition = changedCharacterRecord?.$1;
String? changedCharacter = changedCharacterRecord?.$2; String? changedCharacter = changedCharacterRecord?.$2;
bool isAdded = changedCharacterRecord?.$3 ?? false; bool isAdded = changedCharacterRecord?.$3 ?? false;
@ -625,13 +541,10 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
int? atPlace; int? atPlace;
if (changedTextPosition != null) { if (changedTextPosition != null) {
subText = isAdded == true subText = isAdded == true ? text.substring(0, changedTextPosition + 1) : text.substring(0, changedTextPosition);
? text.substring(0, changedTextPosition + 1)
: text.substring(0, changedTextPosition);
atPlace = subText.lastIndexOf('@'); atPlace = subText.lastIndexOf('@');
if (atPlace != -1) { if (atPlace != -1) {
keyword = text.substring( keyword = text.substring(atPlace + 1, changedTextPosition + (isAdded ? 1 : 0));
atPlace + 1, changedTextPosition + (isAdded ? 1 : 0));
} }
} else { } else {
atPlace = -1; atPlace = -1;
@ -644,28 +557,21 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
model.atPositionY = atPosition.dy; model.atPositionY = atPosition.dy;
isAddingAtSearchWords = true; isAddingAtSearchWords = true;
} }
List<V2TimGroupMemberFullInfo> showAtMemberList = (model List<V2TimGroupMemberFullInfo> showAtMemberList = (model.groupMemberList ?? [])
.groupMemberList ??
[])
.where((element) { .where((element) {
final showName = (TencentUtils.checkStringWithoutSpace( final showName = (TencentUtils.checkStringWithoutSpace(element?.friendRemark) ??
element?.friendRemark) ??
TencentUtils.checkStringWithoutSpace(element?.nameCard) ?? TencentUtils.checkStringWithoutSpace(element?.nameCard) ??
TencentUtils.checkStringWithoutSpace(element?.nickName) ?? TencentUtils.checkStringWithoutSpace(element?.nickName) ??
TencentUtils.checkStringWithoutSpace(element?.userID) ?? TencentUtils.checkStringWithoutSpace(element?.userID) ??
"") "")
.toLowerCase(); .toLowerCase();
keyword ??= ""; keyword ??= "";
return element != null && return element != null && showName.contains(keyword!.toLowerCase()) && TencentUtils.checkString(showName) != null && element.userID != widget.model.selfMemberInfo?.userID;
showName.contains(keyword!.toLowerCase()) &&
TencentUtils.checkString(showName) != null &&
element.userID != widget.model.selfMemberInfo?.userID;
}) })
.whereType<V2TimGroupMemberFullInfo>() .whereType<V2TimGroupMemberFullInfo>()
.toList(); .toList();
showAtMemberList.sort( showAtMemberList.sort((V2TimGroupMemberFullInfo userA, V2TimGroupMemberFullInfo userB) {
(V2TimGroupMemberFullInfo userA, V2TimGroupMemberFullInfo userB) {
final isUserAIsGroupAdmin = userA.role == 300; final isUserAIsGroupAdmin = userA.role == 300;
final isUserAIsGroupOwner = userA.role == 400; final isUserAIsGroupOwner = userA.role == 400;
@ -688,11 +594,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
keyword ??= ""; keyword ??= "";
if (canAtAll && showAtMemberList.isNotEmpty && keyword!.isEmpty) { if (canAtAll && showAtMemberList.isNotEmpty && keyword!.isEmpty) {
showAtMemberList = [ showAtMemberList = [V2TimGroupMemberFullInfo(userID: "__kImSDK_MesssageAtALL__", nickName: TIM_t("所有人")), ...showAtMemberList];
V2TimGroupMemberFullInfo(
userID: "__kImSDK_MesssageAtALL__", nickName: TIM_t("所有人")),
...showAtMemberList
];
} }
model.activeAtIndex = 0; model.activeAtIndex = 0;
@ -704,18 +606,11 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
model.showAtMemberList = []; model.showAtMemberList = [];
isAddingAtSearchWords = false; isAddingAtSearchWords = false;
} }
} else if (textLength > 0 && } else if (textLength > 0 && text[textLength - 1] == "@" && lastText.length < textLength) {
text[textLength - 1] == "@" &&
lastText.length < textLength) {
V2TimGroupMemberFullInfo? memberInfo = await Navigator.push( V2TimGroupMemberFullInfo? memberInfo = await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AtText( builder: (context) => AtText(groupMemberList: model.groupMemberList, groupInfo: model.groupInfo, groupID: groupID, canAtAll: canAtAll, groupType: widget.groupType),
groupMemberList: model.groupMemberList,
groupInfo: model.groupInfo,
groupID: groupID,
canAtAll: canAtAll,
groupType: widget.groupType),
), ),
); );
final showName = _getShowName(memberInfo); final showName = _getShowName(memberInfo);
@ -730,22 +625,17 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
void replaceAtTag(String selectedMember) { void replaceAtTag(String selectedMember) {
int cursorPosition = textEditingController.selection.baseOffset; int cursorPosition = textEditingController.selection.baseOffset;
int atIndex = int atIndex = textEditingController.text.lastIndexOf('@', cursorPosition - 1);
textEditingController.text.lastIndexOf('@', cursorPosition - 1);
if (atIndex >= 0) { if (atIndex >= 0) {
String beforeAt = textEditingController.text.substring(0, atIndex); String beforeAt = textEditingController.text.substring(0, atIndex);
String afterAt = textEditingController.text.substring(cursorPosition); String afterAt = textEditingController.text.substring(cursorPosition);
textEditingController.text = textEditingController.text = beforeAt + '@' + selectedMember + ' ' + afterAt;
beforeAt + '@' + selectedMember + ' ' + afterAt; textEditingController.selection = TextSelection.collapsed(offset: atIndex + selectedMember.length + 2);
textEditingController.selection =
TextSelection.collapsed(offset: atIndex + selectedMember.length + 2);
lastText = beforeAt + '@' + selectedMember + ' ' + afterAt; lastText = beforeAt + '@' + selectedMember + ' ' + afterAt;
} }
} }
void handleAtMember( void handleAtMember({V2TimGroupMemberFullInfo? memberInfo, bool? isAddToCursorPosition = false}) {
{V2TimGroupMemberFullInfo? memberInfo,
bool? isAddToCursorPosition = false}) {
if (memberInfo != null) { if (memberInfo != null) {
final String showName = _getShowName(memberInfo); final String showName = _getShowName(memberInfo);
mentionedMembersMap["@$showName"] = memberInfo; mentionedMembersMap["@$showName"] = memberInfo;
@ -759,24 +649,17 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
KeyEventResult handleDesktopKeyEvent(FocusNode node, RawKeyEvent event) { KeyEventResult handleDesktopKeyEvent(FocusNode node, RawKeyEvent event) {
final activeIndex = widget.model.activeAtIndex; final activeIndex = widget.model.activeAtIndex;
final showMemberList = widget.model.showAtMemberList; final showMemberList = widget.model.showAtMemberList;
final isPressEnter = (event.physicalKey == PhysicalKeyboardKey.enter) || final isPressEnter = (event.physicalKey == PhysicalKeyboardKey.enter) || (event.physicalKey == PhysicalKeyboardKey.numpadEnter);
(event.physicalKey == PhysicalKeyboardKey.numpadEnter);
if (event.runtimeType == RawKeyDownEvent) { if (event.runtimeType == RawKeyDownEvent) {
if (event.physicalKey == PhysicalKeyboardKey.backspace) { if (event.physicalKey == PhysicalKeyboardKey.backspace) {
if (textEditingController.text.isEmpty && lastText.isEmpty) { if (textEditingController.text.isEmpty && lastText.isEmpty) {
widget.model.repliedMessage = null; widget.model.repliedMessage = null;
return KeyEventResult.handled; return KeyEventResult.handled;
} }
} else if ((event.isShiftPressed || } else if ((event.isShiftPressed || event.isAltPressed || event.isControlPressed || event.isMetaPressed) && isPressEnter) {
event.isAltPressed ||
event.isControlPressed ||
event.isMetaPressed) &&
isPressEnter) {
final offset = textEditingController.selection.baseOffset; final offset = textEditingController.selection.baseOffset;
textEditingController.text = textEditingController.text = '${lastText.substring(0, offset)}\n${lastText.substring(offset)}';
'${lastText.substring(0, offset)}\n${lastText.substring(offset)}'; textEditingController.selection = TextSelection.fromPosition(TextPosition(offset: offset + 1));
textEditingController.selection =
TextSelection.fromPosition(TextPosition(offset: offset + 1));
lastText = textEditingController.text; lastText = textEditingController.text;
return KeyEventResult.handled; return KeyEventResult.handled;
@ -786,34 +669,26 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
onSubmitted(); onSubmitted();
} else { } else {
isAddingAtSearchWords = false; isAddingAtSearchWords = false;
final V2TimGroupMemberFullInfo? memberInfo = final V2TimGroupMemberFullInfo? memberInfo = showMemberList[activeIndex];
showMemberList[activeIndex];
if (memberInfo != null) { if (memberInfo != null) {
handleAtMember( handleAtMember(memberInfo: memberInfo, isAddToCursorPosition: true);
memberInfo: memberInfo, isAddToCursorPosition: true);
} }
} }
return KeyEventResult.handled; return KeyEventResult.handled;
} }
} }
if (event.isKeyPressed(LogicalKeyboardKey.arrowUp) && if (event.isKeyPressed(LogicalKeyboardKey.arrowUp) && isAddingAtSearchWords && showMemberList.isNotEmpty) {
isAddingAtSearchWords &&
showMemberList.isNotEmpty) {
final newIndex = max(activeIndex - 1, 0); final newIndex = max(activeIndex - 1, 0);
widget.model.activeAtIndex = newIndex; widget.model.activeAtIndex = newIndex;
widget.atMemberPanelScroll?.scrollToIndex(newIndex, widget.atMemberPanelScroll?.scrollToIndex(newIndex, preferPosition: AutoScrollPosition.middle);
preferPosition: AutoScrollPosition.middle);
return KeyEventResult.handled; return KeyEventResult.handled;
} }
if (event.isKeyPressed(LogicalKeyboardKey.arrowDown) && if (event.isKeyPressed(LogicalKeyboardKey.arrowDown) && isAddingAtSearchWords && showMemberList.isNotEmpty) {
isAddingAtSearchWords &&
showMemberList.isNotEmpty) {
final newIndex = min(activeIndex + 1, showMemberList.length - 1); final newIndex = min(activeIndex + 1, showMemberList.length - 1);
widget.model.activeAtIndex = newIndex; widget.model.activeAtIndex = newIndex;
widget.atMemberPanelScroll?.scrollToIndex(newIndex, widget.atMemberPanelScroll?.scrollToIndex(newIndex, preferPosition: AutoScrollPosition.middle);
preferPosition: AutoScrollPosition.middle);
return KeyEventResult.handled; return KeyEventResult.handled;
} }
} }
@ -831,8 +706,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} else { } else {
focusNode = FocusNode(); focusNode = FocusNode();
} }
textEditingController = textEditingController = widget.controller?.textEditingController ?? TextEditingController();
widget.controller?.textEditingController ?? TextEditingController();
if (widget.initText != null) { if (widget.initText != null) {
textEditingController.text = widget.initText!; textEditingController.text = widget.initText!;
} }
@ -840,10 +714,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
widget.controller?.addListener(controllerHandler); widget.controller?.addListener(controllerHandler);
} }
final AppLocale appLocale = I18nUtils.findDeviceLocale(null); final AppLocale appLocale = I18nUtils.findDeviceLocale(null);
languageType = languageType = (appLocale == AppLocale.zhHans || appLocale == AppLocale.zhHant) ? 'zh' : 'en';
(appLocale == AppLocale.zhHans || appLocale == AppLocale.zhHant)
? 'zh'
: 'en';
textEditingController.addListener(() { textEditingController.addListener(() {
_isComposingText = textEditingController.value.composing.start != -1; _isComposingText = textEditingController.value.composing.start != -1;
}); });
@ -853,13 +724,11 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
controllerHandler() { controllerHandler() {
final actionType = widget.controller?.actionType; final actionType = widget.controller?.actionType;
if (actionType == ActionType.longPressToAt) { if (actionType == ActionType.longPressToAt) {
mentionMemberInMessage( mentionMemberInMessage(widget.controller?.atUserID, widget.controller?.atUserName);
widget.controller?.atUserID, widget.controller?.atUserName);
} else if (actionType == ActionType.setTextField) { } else if (actionType == ActionType.setTextField) {
final newText = widget.controller?.inputText ?? ""; final newText = widget.controller?.inputText ?? "";
textEditingController.text = newText; textEditingController.text = newText;
textEditingController.selection = TextSelection.fromPosition( textEditingController.selection = TextSelection.fromPosition(TextPosition(offset: textEditingController.text.length));
TextPosition(offset: textEditingController.text.length));
lastText = textEditingController.text; lastText = textEditingController.text;
focusNode.requestFocus(); focusNode.requestFocus();
return; return;
@ -877,18 +746,14 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.conversationID != oldWidget.conversationID) { if (widget.conversationID != oldWidget.conversationID) {
mentionedMembersMap.clear(); mentionedMembersMap.clear();
handleSetDraftText( handleSetDraftText(id: oldWidget.conversationID, convType: oldWidget.conversationType, groupID: oldWidget.groupID);
id: oldWidget.conversationID,
convType: oldWidget.conversationType,
groupID: oldWidget.groupID);
if (oldWidget.initText != widget.initText) { if (oldWidget.initText != widget.initText) {
textEditingController.text = widget.initText ?? ""; textEditingController.text = widget.initText ?? "";
} else { } else {
textEditingController.clear(); textEditingController.clear();
} }
} }
if (widget.initText != oldWidget.initText && if (widget.initText != oldWidget.initText && TencentUtils.checkString(widget.initText) != null) {
TencentUtils.checkString(widget.initText) != null) {
textEditingController.text = widget.initText!; textEditingController.text = widget.initText!;
focusNode.requestFocus(); focusNode.requestFocus();
} }
@ -906,12 +771,8 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
Future<bool> getMemberMuteStatus(String userID) async { Future<bool> getMemberMuteStatus(String userID) async {
// Get the mute state of the members recursively // Get the mute state of the members recursively
if (widget.model.groupMemberList?.any((item) => (item?.userID == userID)) ?? if (widget.model.groupMemberList?.any((item) => (item?.userID == userID)) ?? false) {
false) { final int muteUntil = widget.model.groupMemberList?.firstWhere((item) => (item?.userID == userID))?.muteUntil ?? 0;
final int muteUntil = widget.model.groupMemberList
?.firstWhere((item) => (item?.userID == userID))
?.muteUntil ??
0;
return muteUntil * 1000 > DateTime.now().millisecondsSinceEpoch; return muteUntil * 1000 > DateTime.now().millisecondsSinceEpoch;
} else { } else {
return false; return false;
@ -924,42 +785,28 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} }
final int selfRole = widget.model.selfMemberInfo?.role ?? 0; final int selfRole = widget.model.selfMemberInfo?.role ?? 0;
final bool willNotBeenMuted = final bool willNotBeenMuted = (selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN || selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER);
(selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN ||
selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER);
if (widget.conversationType == ConvType.group && !willNotBeenMuted) { if (widget.conversationType == ConvType.group && !willNotBeenMuted) {
if ((model.groupInfo?.isAllMuted ?? false) && if ((model.groupInfo?.isAllMuted ?? false) && muteStatus != MuteStatus.all) {
muteStatus != MuteStatus.all) {
Future.delayed(const Duration(seconds: 0), () { Future.delayed(const Duration(seconds: 0), () {
setState(() { setState(() {
muteStatus = MuteStatus.all; muteStatus = MuteStatus.all;
}); });
}); });
} else if (selfModel.loginInfo?.userID != null && } else if (selfModel.loginInfo?.userID != null && await getMemberMuteStatus(selfModel.loginInfo!.userID!) && muteStatus != MuteStatus.me) {
await getMemberMuteStatus(selfModel.loginInfo!.userID!) &&
muteStatus != MuteStatus.me) {
Future.delayed(const Duration(seconds: 0), () { Future.delayed(const Duration(seconds: 0), () {
setState(() { setState(() {
muteStatus = MuteStatus.me; muteStatus = MuteStatus.me;
}); });
}); });
} else if (!(model.groupInfo?.isAllMuted ?? false) && } else if (!(model.groupInfo?.isAllMuted ?? false) && !(selfModel.loginInfo?.userID != null && await getMemberMuteStatus(selfModel.loginInfo!.userID!)) && muteStatus != MuteStatus.none) {
!(selfModel.loginInfo?.userID != null &&
await getMemberMuteStatus(selfModel.loginInfo!.userID!)) &&
muteStatus != MuteStatus.none) {
Future.delayed(const Duration(seconds: 0), () { Future.delayed(const Duration(seconds: 0), () {
setState(() { setState(() {
muteStatus = MuteStatus.none; muteStatus = MuteStatus.none;
}); });
}); });
} }
} else {
Future.delayed(const Duration(seconds: 0), () {
setState(() {
muteStatus = MuteStatus.none;
});
});
} }
} }
@ -982,8 +829,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme; final theme = value.theme;
final TUIChatSeparateViewModel model = final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context);
Provider.of<TUIChatSeparateViewModel>(context);
_getMuteType(model); _getMuteType(model);
@ -1003,8 +849,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
} }
final forbiddenText = getForbiddenText(); final forbiddenText = getForbiddenText();
return LayoutBuilder( return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
builder: (BuildContext context, BoxConstraints constraints) {
inputWidth = constraints.maxWidth; inputWidth = constraints.maxWidth;
return TUIKitScreenUtils.getDeviceWidget( return TUIKitScreenUtils.getDeviceWidget(
context: context, context: context,

View File

@ -1,18 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_group_profile_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_group_profile_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/contact_list.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/contact_list.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
GlobalKey<_AddGroupMemberPageState> addGroupMemberKey = GlobalKey(); GlobalKey<_AddGroupMemberPageState> addGroupMemberKey = GlobalKey();
class AddGroupMemberPage extends StatefulWidget { class AddGroupMemberPage extends StatefulWidget {
final TUIGroupProfileModel model; final TUIGroupProfileModel model;
final VoidCallback? onClose;
const AddGroupMemberPage({Key? key, required this.model}) : super(key: key); const AddGroupMemberPage({Key? key, required this.model, this.onClose}) : super(key: key);
@override @override
State<StatefulWidget> createState() => _AddGroupMemberPageState(); State<StatefulWidget> createState() => _AddGroupMemberPageState();
@ -25,7 +25,7 @@ class _AddGroupMemberPageState extends TIMUIKitState<AddGroupMemberPage> {
if (selectedContacts.isNotEmpty) { if (selectedContacts.isNotEmpty) {
final userIDs = selectedContacts.map((e) => e.userID).toList(); final userIDs = selectedContacts.map((e) => e.userID).toList();
await widget.model.inviteUserToGroup(userIDs); await widget.model.inviteUserToGroup(userIDs);
Navigator.pop(context); widget.onClose ?? Navigator.pop(context);
} }
} }
@ -68,8 +68,7 @@ class _AddGroupMemberPageState extends TIMUIKitState<AddGroupMemberPage> {
) )
], ],
shadowColor: theme.weakDividerColor, shadowColor: theme.weakDividerColor,
backgroundColor: theme.appbarBgColor ?? backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
theme.primaryColor,
iconTheme: IconThemeData( iconTheme: IconThemeData(
color: theme.appbarTextColor, color: theme.appbarTextColor,
)), )),

View File

@ -1,20 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_group_profile_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_group_profile_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/group_member_list.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/group_member_list.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
GlobalKey<_DeleteGroupMemberPageState> deleteGroupMemberKey = GlobalKey(); GlobalKey<_DeleteGroupMemberPageState> deleteGroupMemberKey = GlobalKey();
class DeleteGroupMemberPage extends StatefulWidget { class DeleteGroupMemberPage extends StatefulWidget {
final TUIGroupProfileModel model; final TUIGroupProfileModel model;
final VoidCallback? onClose;
const DeleteGroupMemberPage({Key? key, required this.model}) const DeleteGroupMemberPage({Key? key, required this.model, this.onClose}) : super(key: key);
: super(key: key);
@override @override
State<StatefulWidget> createState() => _DeleteGroupMemberPageState(); State<StatefulWidget> createState() => _DeleteGroupMemberPageState();
@ -30,11 +29,8 @@ class _DeleteGroupMemberPageState extends TIMUIKitState<DeleteGroupMemberPage> {
handleSearchGroupMembers(String searchText, context) async { handleSearchGroupMembers(String searchText, context) async {
searchText = searchText; searchText = searchText;
List<V2TimGroupMemberFullInfo?> currentGroupMember = List<V2TimGroupMemberFullInfo?> currentGroupMember = Provider.of<TUIGroupProfileModel>(context, listen: false).groupMemberList;
Provider.of<TUIGroupProfileModel>(context, listen: false) final res = await widget.model.searchGroupMember(V2TimGroupMemberSearchParam(
.groupMemberList;
final res =
await widget.model.searchGroupMember(V2TimGroupMemberSearchParam(
keywordList: [searchText], keywordList: [searchText],
groupIDList: [widget.model.groupInfo!.groupID], groupIDList: [widget.model.groupInfo!.groupID],
)); ));
@ -55,25 +51,19 @@ class _DeleteGroupMemberPageState extends TIMUIKitState<DeleteGroupMemberPage> {
currentGroupMember = []; currentGroupMember = [];
} }
setState(() { setState(() {
searchMemberList = searchMemberList = isSearchTextExist(searchText) ? currentGroupMember : null;
isSearchTextExist(searchText) ? currentGroupMember : null;
}); });
} }
handleRole(groupMemberList) { handleRole(groupMemberList) {
return groupMemberList return groupMemberList?.where((value) => value?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER).toList() ?? [];
?.where((value) =>
value?.role ==
GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER)
.toList() ??
[];
} }
void submitDelete() async { void submitDelete() async {
if (selectedGroupMember.isNotEmpty) { if (selectedGroupMember.isNotEmpty) {
final userIDs = selectedGroupMember.map((e) => e.userID).toList(); final userIDs = selectedGroupMember.map((e) => e.userID).toList();
widget.model.kickOffMember(userIDs); widget.model.kickOffMember(userIDs);
Navigator.pop(context); widget.onClose ?? Navigator.pop(context);
} }
} }
@ -86,8 +76,7 @@ class _DeleteGroupMemberPageState extends TIMUIKitState<DeleteGroupMemberPage> {
desktopWidget: Container( desktopWidget: Container(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: GroupProfileMemberList( child: GroupProfileMemberList(
memberList: memberList: handleRole(searchMemberList ?? widget.model.groupMemberList),
handleRole(searchMemberList ?? widget.model.groupMemberList),
canSelectMember: true, canSelectMember: true,
canSlideDelete: false, canSlideDelete: false,
onSelectedMemberChange: (selectedMember) { onSelectedMemberChange: (selectedMember) {
@ -115,14 +104,12 @@ class _DeleteGroupMemberPageState extends TIMUIKitState<DeleteGroupMemberPage> {
) )
], ],
shadowColor: theme.weakBackgroundColor, shadowColor: theme.weakBackgroundColor,
backgroundColor: theme.appbarBgColor ?? backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
theme.primaryColor,
iconTheme: IconThemeData( iconTheme: IconThemeData(
color: theme.appbarTextColor, color: theme.appbarTextColor,
)), )),
body: GroupProfileMemberList( body: GroupProfileMemberList(
memberList: memberList: handleRole(searchMemberList ?? widget.model.groupMemberList),
handleRole(searchMemberList ?? widget.model.groupMemberList),
canSelectMember: true, canSelectMember: true,
canSlideDelete: false, canSlideDelete: false,
onSelectedMemberChange: (selectedMember) { onSelectedMemberChange: (selectedMember) {

View File

@ -3,17 +3,15 @@
import 'package:dotted_border/dotted_border.dart'; import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart'; import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_group_profile_model.dart'; import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_group_profile_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart'; import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart'; import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/group_member/tui_add_group_member.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/group_member/tui_add_group_member.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/group_member/tui_delete_group_member.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/group_member/tui_delete_group_member.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/group_member/tui_group_member_list.dart'; import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/group_member/tui_group_member_list.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart'; import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
import 'package:tencent_im_base/tencent_im_base.dart'; import 'package:tencent_im_base/tencent_im_base.dart';
@ -38,8 +36,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
return friendRemark != "" ? friendRemark : showName; return friendRemark != "" ? friendRemark : showName;
} }
List<Widget> _groupMemberListBuilder(List memberList, TUITheme theme, List<Widget> _groupMemberListBuilder(List memberList, TUITheme theme, TUIGroupProfileModel model, int showRange) {
TUIGroupProfileModel model, int showRange) {
final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop; final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
return _getMemberList(memberList, showRange).map((element) { return _getMemberList(memberList, showRange).map((element) {
final faceUrl = element?.faceUrl ?? ""; final faceUrl = element?.faceUrl ?? "";
@ -74,10 +71,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
Text( Text(
showName, showName,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(overflow: TextOverflow.ellipsis, color: theme.weakTextColor, fontSize: 10),
overflow: TextOverflow.ellipsis,
color: theme.weakTextColor,
fontSize: 10),
) )
], ],
), ),
@ -86,21 +80,17 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
}).toList(); }).toList();
} }
List<Widget> _inviteMemberBuilder(bool isCanInviteMember, List<Widget> _inviteMemberBuilder(bool isCanInviteMember, bool isCanKickOffMember, theme, BuildContext context) {
bool isCanKickOffMember, theme, BuildContext context) {
return []; return [];
} }
void navigateToMemberList(BuildContext context, TUIGroupProfileModel model, void navigateToMemberList(BuildContext context, TUIGroupProfileModel model, List<V2TimGroupMemberFullInfo?> memberList) {
List<V2TimGroupMemberFullInfo?> memberList) { final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final isDesktopScreen =
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
if (!isDesktopScreen) { if (!isDesktopScreen) {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => GroupProfileMemberListPage( builder: (context) => GroupProfileMemberListPage(model: model, memberList: memberList),
model: model, memberList: memberList),
)); ));
} else { } else {
final option1 = memberList.length.toString(); final option1 = memberList.length.toString();
@ -109,18 +99,15 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
context: context, context: context,
width: MediaQuery.of(context).size.width * 0.5, width: MediaQuery.of(context).size.width * 0.5,
height: MediaQuery.of(context).size.height * 0.8, height: MediaQuery.of(context).size.height * 0.8,
title: TIM_t_para("群成员({{option1}}人)", "群成员($option1人)")( title: TIM_t_para("群成员({{option1}}人)", "群成员($option1人)")(option1: option1),
option1: option1), child: (onClose) => GroupProfileMemberListPage(model: model, memberList: memberList));
child: (onClose) =>
GroupProfileMemberListPage(model: model, memberList: memberList));
} }
} }
@override @override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) { Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme; final TUITheme theme = value.theme;
final isDesktopScreen = final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
final model = Provider.of<TUIGroupProfileModel>(context); final model = Provider.of<TUIGroupProfileModel>(context);
final memberAmount = model.groupInfo?.memberCount ?? 0; final memberAmount = model.groupInfo?.memberCount ?? 0;
final option1 = memberAmount.toString(); final option1 = memberAmount.toString();
@ -144,13 +131,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
children: [ children: [
Container( Container(
padding: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.only(bottom: 12),
decoration: isDesktopScreen decoration: isDesktopScreen ? null : BoxDecoration(border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))),
? null
: BoxDecoration(
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ??
CommonColor.weakDividerColor))),
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
navigateToMemberList(context, model, memberList); navigateToMemberList(context, model, memberList);
@ -158,18 +139,12 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(TIM_t("群成员"), Text(TIM_t("群成员"), style: TextStyle(color: theme.darkTextColor, fontSize: isDesktopScreen ? 14 : 16)),
style: TextStyle(
color: theme.darkTextColor,
fontSize: isDesktopScreen ? 14 : 16)),
Row( Row(
children: [ children: [
Text( Text(
TIM_t_para("{{option1}}人", "$option1人")( TIM_t_para("{{option1}}人", "$option1人")(option1: option1),
option1: option1), style: TextStyle(color: theme.darkTextColor, fontSize: isDesktopScreen ? 14 : 16),
style: TextStyle(
color: theme.darkTextColor,
fontSize: isDesktopScreen ? 14 : 16),
), ),
Icon( Icon(
Icons.keyboard_arrow_right, Icons.keyboard_arrow_right,
@ -188,15 +163,11 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(width: 1, color: theme.weakDividerColor ?? CommonColor.weakDividerColor),
width: 1,
color: theme.weakDividerColor ??
CommonColor.weakDividerColor),
borderRadius: const BorderRadius.all(Radius.circular(4)), borderRadius: const BorderRadius.all(Radius.circular(4)),
), ),
// height: 30, // height: 30,
padding: padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
@ -247,6 +218,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
}, },
child: (onClose) => AddGroupMemberPage( child: (onClose) => AddGroupMemberPage(
model: model, model: model,
onClose: onClose,
key: addGroupMemberKey, key: addGroupMemberKey,
)); ));
} else { } else {
@ -285,11 +257,11 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
title: TIM_t("删除群成员"), title: TIM_t("删除群成员"),
height: 460, height: 460,
onSubmit: () { onSubmit: () {
deleteGroupMemberKey.currentState deleteGroupMemberKey.currentState?.submitDelete();
?.submitDelete();
}, },
child: (onClose) => DeleteGroupMemberPage( child: (onClose) => DeleteGroupMemberPage(
model: model, model: model,
onClose: onClose,
key: deleteGroupMemberKey, key: deleteGroupMemberKey,
), ),
); );
@ -297,8 +269,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => DeleteGroupMemberPage(model: model),
DeleteGroupMemberPage(model: model),
)); ));
} }
}, },
@ -319,9 +290,7 @@ class GroupMemberTile extends TIMUIKitStatelessWidget {
margin: EdgeInsets.only(top: isDesktopScreen ? 12 : 16), margin: EdgeInsets.only(top: isDesktopScreen ? 12 : 16),
child: Text( child: Text(
TIM_t("查看更多群成员"), TIM_t("查看更多群成员"),
style: TextStyle( style: TextStyle(color: theme.weakTextColor, fontSize: isDesktopScreen ? 12 : 14),
color: theme.weakTextColor,
fontSize: isDesktopScreen ? 12 : 14),
), ),
), ),
onTap: () async { onTap: () async {

View File

@ -13,12 +13,13 @@ class KeepAliveWrapper extends StatefulWidget {
_KeepAliveWrapperState createState() => _KeepAliveWrapperState(); _KeepAliveWrapperState createState() => _KeepAliveWrapperState();
} }
class _KeepAliveWrapperState extends State<KeepAliveWrapper> class _KeepAliveWrapperState extends State<KeepAliveWrapper> with AutomaticKeepAliveClientMixin {
with AutomaticKeepAliveClientMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return widget.child; return RepaintBoundary(
child: widget.child,
);
} }
@override @override

View File

@ -1,6 +1,6 @@
name: tencent_cloud_chat_uikit name: tencent_cloud_chat_uikit
description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences. description: A powerful chat UI component library and business logic for Tencent Cloud Chat, creating seamless in-app chat modules for delightful user experiences.
version: 2.3.1 version: 2.3.2
homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn homepage: https://trtc.io/products/chat?utm_source=gfs&utm_medium=link&utm_campaign=%E6%B8%A0%E9%81%93&_channel_track_key=k6WgfCKn
repository: https://github.com/TencentCloud/chat-uikit-flutter repository: https://github.com/TencentCloud/chat-uikit-flutter
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html