diff --git a/lib/app/constants/sc_global_config.dart b/lib/app/constants/sc_global_config.dart index 36935da..4dbfff4 100644 --- a/lib/app/constants/sc_global_config.dart +++ b/lib/app/constants/sc_global_config.dart @@ -78,10 +78,7 @@ class SCGlobalConfig { static Set get systemConversationIds { return { - "administrator", - ...systemUserIds - .where((element) => element != "administrator") - .map((element) => "c2c_$element"), + for (final userId in systemUserIds) ...{userId, "c2c_$userId"}, }; } @@ -93,7 +90,7 @@ class SCGlobalConfig { if (conversationId == null || conversationId.isEmpty) { return false; } - return systemConversationIds.contains(conversationId); + return systemUserIds.contains(_normalizeConversationUserId(conversationId)); } static bool isSystemUserId(String? userId) { diff --git a/lib/modules/chat/message_chat_page.dart b/lib/modules/chat/message_chat_page.dart index 9608ee2..bf23fe8 100644 --- a/lib/modules/chat/message_chat_page.dart +++ b/lib/modules/chat/message_chat_page.dart @@ -24,6 +24,7 @@ import 'package:marquee/marquee.dart'; import 'package:provider/provider.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart'; +import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart'; import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart'; import 'package:tencent_cloud_chat_sdk/enum/message_status.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'; @@ -51,7 +52,6 @@ import 'package:yumi/shared/tools/sc_keybord_util.dart'; import 'package:yumi/shared/tools/sc_message_notifier.dart'; import 'package:yumi/shared/tools/sc_path_utils.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; -import 'package:yumi/shared/business_logic/usecases/custom_tab_selector.dart'; import 'package:yumi/services/auth/user_profile_manager.dart'; import 'package:yumi/modules/index/main_route.dart'; @@ -104,6 +104,34 @@ class _SCMessageChatPageState extends State { List coinsTitles = ["100", "1000", "10000", "50000"]; String selecteCoins = "100"; + bool get _isSystemConversation => + SCGlobalConfig.isSystemConversationId( + currentConversation?.conversationID, + ) || + SCGlobalConfig.isSystemUserId(currentConversation?.userID); + + bool get _isC2CConversation => + currentConversation?.type == ConversationType.V2TIM_C2C; + + bool get _isGroupConversation => + currentConversation?.type == ConversationType.V2TIM_GROUP; + + bool get _canOpenFriendProfile => _isC2CConversation && friend != null; + + bool get _canSendConversationMessage => + _isC2CConversation && !_isSystemConversation; + + String get _conversationTitle { + final friendNickname = friend?.userNickname ?? ""; + if (friendNickname.isNotEmpty) { + return friendNickname; + } + return currentConversation?.showName ?? + currentConversation?.userID ?? + currentConversation?.groupID ?? + ""; + } + @override void initState() { super.initState(); @@ -119,7 +147,7 @@ class _SCMessageChatPageState extends State { rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage; loadMsg(); - if (friend == null) { + if (_isC2CConversation && friend == null) { loadFriend(); } @@ -220,36 +248,35 @@ class _SCMessageChatPageState extends State { child: Container( alignment: AlignmentDirectional.center, child: socialchatNickNameText( - friend?.userNickname ?? "", + _conversationTitle, fontSize: 14.sp, fontWeight: FontWeight.w500, textColor: Colors.white, type: "", needScroll: - (friend?.userNickname?.characters.length ?? - 0) > - 22, + _conversationTitle.characters.length > 22, ), ), ), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - SCNavigatorUtils.push( - context, - replace: true, - "${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}", - ); - }, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 10.w), - child: Icon( - Icons.more_vert, - size: 22.w, - color: Colors.white, + if (_canOpenFriendProfile) + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + SCNavigatorUtils.push( + context, + replace: true, + "${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}", + ); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Icon( + Icons.more_vert, + size: 22.w, + color: Colors.white, + ), ), ), - ), ], ), Expanded(child: _msgList()), @@ -262,10 +289,7 @@ class _SCMessageChatPageState extends State { top: false, child: Column( children: [ - if (!SCGlobalConfig.isSystemConversationId( - currentConversation?.conversationID, - )) - _input(), + if (_canSendConversationMessage) _input(), _tools(provider), _emoji(), // _fahongbao(), @@ -285,18 +309,18 @@ class _SCMessageChatPageState extends State { } void loadFriend() { - if (SCGlobalConfig.isSystemConversationId( - currentConversation?.conversationID, - ) || - SCGlobalConfig.isSystemUserId(currentConversation?.userID)) { + final userId = currentConversation?.userID; + if (!_isC2CConversation || + _isSystemConversation || + SCGlobalConfig.isSystemUserId(userId) || + userId == null || + userId.isEmpty) { return; } SCLoadingManager.show(); Future.wait([ - SCAccountRepository().loadUserInfo("${currentConversation?.userID}"), - SCAccountRepository().friendRelationCheck( - "${currentConversation?.userID}", - ), + SCAccountRepository().loadUserInfo(userId), + SCAccountRepository().friendRelationCheck(userId), ]) .then((result) { SCLoadingManager.hide(); @@ -318,19 +342,10 @@ class _SCMessageChatPageState extends State { onLoading: () async { print('onLoading'); if (currentConversationMessageList.isNotEmpty) { - // 拉取单聊历史消息 - // 首次拉取,lastMsgID 设置为 null - // 再次拉取时,lastMsgID 可以使用返回的消息列表中的最后一条消息的id - V2TimValueCallback> v2timValueCallback = - await TencentImSDKPlugin.v2TIMManager - .getMessageManager() - .getC2CHistoryMessageList( - userID: currentConversation!.userID ?? "", - count: 30, - lastMsgID: currentConversationMessageList.last.msgID, - ); - List messages = - v2timValueCallback.data as List; + final messages = await _loadHistoryMessages( + count: 30, + lastMsgID: currentConversationMessageList.last.msgID, + ); print('加载前:${currentConversationMessageList.length}'); currentConversationMessageList.addAll(messages); print('加载后:${currentConversationMessageList.length}'); @@ -386,6 +401,54 @@ class _SCMessageChatPageState extends State { ); } + Future> _loadHistoryMessages({ + required int count, + String? lastMsgID, + }) async { + final messageManager = TencentImSDKPlugin.v2TIMManager.getMessageManager(); + if (_isGroupConversation) { + final groupId = currentConversation?.groupID; + if (groupId == null || groupId.isEmpty) { + return []; + } + final result = await messageManager.getGroupHistoryMessageList( + groupID: groupId, + count: count, + lastMsgID: lastMsgID, + ); + return result.data ?? []; + } + + final userId = currentConversation?.userID; + if (userId == null || userId.isEmpty) { + return []; + } + final result = await messageManager.getC2CHistoryMessageList( + userID: userId, + count: count, + lastMsgID: lastMsgID, + ); + return result.data ?? []; + } + + Future _markCurrentConversationAsRead() async { + final messageManager = TencentImSDKPlugin.v2TIMManager.getMessageManager(); + if (_isGroupConversation) { + final groupId = currentConversation?.groupID; + if (groupId == null || groupId.isEmpty) { + return; + } + await messageManager.markGroupMessageAsRead(groupID: groupId); + return; + } + + final userId = currentConversation?.userID; + if (userId == null || userId.isEmpty) { + return; + } + await messageManager.markC2CMessageAsRead(userID: userId); + } + Future loadMsg() async { // 1. 在初始化时,添加高级消息监听器 TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener( @@ -404,19 +467,9 @@ class _SCMessageChatPageState extends State { }, ), ); - V2TimValueCallback> v2timValueCallback = - await TencentImSDKPlugin.v2TIMManager - .getMessageManager() - .getC2CHistoryMessageList( - userID: currentConversation!.userID!, - count: 100, - lastMsgID: null, - ); - List messages = v2timValueCallback.data!; - // List messages = await FTIM.getMessageManager().getMessages(conversation: currentConversation); - print("messages : ${messages?.length ?? 0}"); - currentConversationMessageList ??= []; - currentConversationMessageList?.clear(); + final messages = await _loadHistoryMessages(count: 100); + print("messages : ${messages.length}"); + currentConversationMessageList.clear(); for (var msg in messages) { if (!msg.isSelf! && msg.isPeerRead!) { @@ -425,7 +478,8 @@ class _SCMessageChatPageState extends State { .sendMessageReadReceipts(messageIDList: [msg.msgID!]); } } - currentConversationMessageList.insertAll(0, messages ?? []); + currentConversationMessageList.insertAll(0, messages); + await _markCurrentConversationAsRead(); setState(() {}); } @@ -767,7 +821,11 @@ class _SCMessageChatPageState extends State { if (message == null) { return; } - if (message.userID != currentConversation?.userID) return; + if (_isGroupConversation) { + if (message.groupID != currentConversation?.groupID) return; + } else if (message.userID != currentConversation?.userID) { + return; + } int? index; for (var element in currentConversationMessageList) { @@ -788,14 +846,12 @@ class _SCMessageChatPageState extends State { return; } if (index != null) { - currentConversationMessageList.removeAt(index!); - currentConversationMessageList.insert(index!, message); + currentConversationMessageList.removeAt(index); + currentConversationMessageList.insert(index, message); } else { currentConversationMessageList.insert(0, message); } - await TencentImSDKPlugin.v2TIMManager - .getMessageManager() - .markC2CMessageAsRead(userID: currentConversation!.userID!); + await _markCurrentConversationAsRead(); //发送已读回执 await TencentImSDKPlugin.v2TIMManager .getMessageManager() @@ -849,7 +905,7 @@ class _MessageItem extends StatelessWidget { } String time = SCMDateUtils.formatMessageTime( context, - DateTime.fromMillisecondsSinceEpoch(timestamp ?? 0), + DateTime.fromMillisecondsSinceEpoch(timestamp), ); if (message.status == MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) { return Container( diff --git a/lib/services/audio/rtc_manager.dart b/lib/services/audio/rtc_manager.dart index 450224e..e7309dc 100644 --- a/lib/services/audio/rtc_manager.dart +++ b/lib/services/audio/rtc_manager.dart @@ -572,6 +572,26 @@ class RealTimeCommunicationManager extends ChangeNotifier { } } + Future _bootstrapRoomHeartbeatState() async { + final String roomId = + (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim(); + if (roomId.isEmpty) { + _resetHeartbeatTracking(); + return; + } + await SCHeartbeatUtils.scheduleHeartbeat( + SCHeartbeatStatus.VOICE_LIVE.name, + false, + roomId: roomId, + ); + _lastScheduledVoiceLiveRoomId = roomId; + _lastScheduledVoiceLiveOnMic = false; + if (_lastScheduledAnchorRoomId != null) { + SCHeartbeatUtils.cancelAnchorTimer(); + _lastScheduledAnchorRoomId = null; + } + } + void _applyLocalAudioRuntimeState({ required ClientRoleType clientRole, required bool muted, @@ -1058,6 +1078,8 @@ class RealTimeCommunicationManager extends ChangeNotifier { ), ); + await _bootstrapRoomHeartbeatState(); + ///获取麦位 retrieveMicrophoneList(); fetchOnlineUsersList(); diff --git a/lib/services/audio/rtm_manager.dart b/lib/services/audio/rtm_manager.dart index f6cacd8..5b83a97 100644 --- a/lib/services/audio/rtm_manager.dart +++ b/lib/services/audio/rtm_manager.dart @@ -133,22 +133,27 @@ class RealTimeMessagingManager extends ChangeNotifier { ///客服 SocialChatUserProfile? customerInfo; + bool _isSystemConversation(V2TimConversation? conversation) { + if (conversation == null) { + return false; + } + return SCGlobalConfig.isSystemConversationId(conversation.conversationID) || + SCGlobalConfig.isSystemUserId(conversation.userID); + } + int _systemUnreadCount() { int count = 0; - for (final conversationId in SCGlobalConfig.systemConversationIds) { - count += conversationMap[conversationId]?.unreadCount ?? 0; + for (final conversation in conversationMap.values) { + if (_isSystemConversation(conversation)) { + count += conversation.unreadCount ?? 0; + } } return count; } V2TimConversation getPreferredSystemConversation() { final systemConversations = - conversationMap.values - .where( - (element) => - SCGlobalConfig.isSystemConversationId(element.conversationID), - ) - .toList() + conversationMap.values.where(_isSystemConversation).toList() ..sort((e1, e2) { final time1 = e1.lastMessage?.timestamp ?? 0; final time2 = e2.lastMessage?.timestamp ?? 0; @@ -169,6 +174,9 @@ class RealTimeMessagingManager extends ChangeNotifier { void getConversationList() { List list = conversationMap.values.toList(); list.removeWhere((element) { + if (_isSystemConversation(element)) { + return true; + } if (element.conversationID == "c2c_${customerInfo?.id}") { return true; } @@ -501,7 +509,7 @@ class RealTimeMessagingManager extends ChangeNotifier { _onNew1v1Message(V2TimMessage? message, {String? msgId}) async { for (var element in conversationList) { - if (message?.userID == element?.userID) { + if (message?.userID == element.userID) { element.lastMessage = message; if (onNewMessageCurrentConversationListener == null) { element.unreadCount = element.unreadCount! + 1; @@ -1740,7 +1748,14 @@ class RealTimeMessagingManager extends ChangeNotifier { } Future quitGroup(String groupID) async { - await TencentImSDKPlugin.v2TIMManager.quitGroup(groupID: groupID); + if (groupID.isEmpty) { + return; + } + try { + await TencentImSDKPlugin.v2TIMManager.quitGroup(groupID: groupID); + } finally { + await deleteConversationById("group_$groupID"); + } } ///清屏 @@ -1857,8 +1872,10 @@ class RealTimeMessagingManager extends ChangeNotifier { } void updateSystemCount(int count) { - for (final conversationId in SCGlobalConfig.systemConversationIds) { - conversationMap[conversationId]?.unreadCount = 0; + for (final conversation in conversationMap.values) { + if (_isSystemConversation(conversation)) { + conversation.unreadCount = 0; + } } systemUnReadCount = 0; notifyListeners(); @@ -1870,21 +1887,23 @@ class RealTimeMessagingManager extends ChangeNotifier { notifyListeners(); } - void clearC2CHistoryMessage(String conversationID, bool needShowToast) async { - // 清空单聊本地及云端的消息(不删除会话) - - V2TimCallback clearC2CHistoryMessageRes = await TencentImSDKPlugin - .v2TIMManager - .getConversationManager() - .deleteConversation(conversationID: conversationID); // 需要清空记录的用户id - if (clearC2CHistoryMessageRes.code == 0) { - // 清除成功 - if (needShowToast) { - SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful); - } - initConversation(); - } else { - // 清除失败,可以查看 clearC2CHistoryMessageRes.desc 获取错误描述 + Future deleteConversationById( + String conversationID, { + bool needShowToast = false, + }) async { + if (conversationID.isEmpty) { + return; } + V2TimCallback deleteConversationRes = await TencentImSDKPlugin.v2TIMManager + .getConversationManager() + .deleteConversation(conversationID: conversationID); + if (deleteConversationRes.code == 0 && needShowToast && context != null) { + SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful); + } + await initConversation(); + } + + void clearC2CHistoryMessage(String conversationID, bool needShowToast) async { + await deleteConversationById(conversationID, needShowToast: needShowToast); } } diff --git a/lib/shared/tools/sc_heartbeat_utils.dart b/lib/shared/tools/sc_heartbeat_utils.dart index 5451f84..f0d0e2e 100644 --- a/lib/shared/tools/sc_heartbeat_utils.dart +++ b/lib/shared/tools/sc_heartbeat_utils.dart @@ -4,16 +4,16 @@ import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import '../../shared/data_sources/models/enum/sc_heartbeat_status.dart'; - -class SCHeartbeatUtils { - static Timer? _h; - static Timer? _a; - static String _c = SCHeartbeatStatus.ONLINE.name; - static String? _r; - static bool _u = false; - - ///定时发送心跳 - static void scheduleHeartbeat( + +class SCHeartbeatUtils { + static Timer? _h; + static Timer? _a; + static String _c = SCHeartbeatStatus.ONLINE.name; + static String? _r; + static bool _u = false; + + ///定时发送心跳 + static Future scheduleHeartbeat( String status, bool upMick, { String? roomId, @@ -42,14 +42,14 @@ class SCHeartbeatUtils { } catch (_) {} } } - - static void cancelTimer() { - _h?.cancel(); - _h = null; - _r = null; - cancelAnchorTimer(); - } - + + static void cancelTimer() { + _h?.cancel(); + _h = null; + _r = null; + cancelAnchorTimer(); + } + ///上麦主播发送心跳 static void scheduleAnchorHeartbeat(String roomId) async { cancelAnchorTimer(); @@ -60,10 +60,10 @@ class SCHeartbeatUtils { }); }); } - - static void cancelAnchorTimer() { - _a?.cancel(); - _a = null; - _u = false; - } -} + + static void cancelAnchorTimer() { + _a?.cancel(); + _a = null; + _u = false; + } +} diff --git a/lib/ui_kit/widgets/msg/message_conversation_list_page.dart b/lib/ui_kit/widgets/msg/message_conversation_list_page.dart index 280d7cc..ad81013 100644 --- a/lib/ui_kit/widgets/msg/message_conversation_list_page.dart +++ b/lib/ui_kit/widgets/msg/message_conversation_list_page.dart @@ -1,543 +1,572 @@ -import 'dart:convert'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; -import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; -import 'package:provider/provider.dart'; -import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart'; -import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart'; -import 'package:tencent_cloud_chat_sdk/enum/message_status.dart'; -import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; -import 'package:tencent_cloud_chat_sdk/models/v2_tim_custom_elem.dart'; -import 'package:tencent_cloud_chat_sdk/models/v2_tim_text_elem.dart'; -import 'package:yumi/app_localizations.dart'; -import 'package:yumi/ui_kit/components/dialog/dialog_base.dart'; -import 'package:yumi/ui_kit/components/sc_compontent.dart'; -import 'package:yumi/ui_kit/components/text/sc_text.dart'; -import 'package:yumi/app/constants/sc_global_config.dart'; -import 'package:yumi/app/routes/sc_fluro_navigator.dart'; -import 'package:yumi/shared/tools/sc_date_utils.dart'; -import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; -import 'package:yumi/shared/business_logic/models/res/login_res.dart'; -import 'package:yumi/services/audio/rtm_manager.dart'; - -import '../../../modules/chat/chat_route.dart'; - -class MessageConversationListPage extends StatefulWidget { - bool isRoom; - - MessageConversationListPage(this.isRoom, {super.key}); - - @override - _MessageConversationListPageState createState() => - _MessageConversationListPageState(); -} - -class _MessageConversationListPageState - extends State { - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (_, provider, __) { - return Column( - children: [ - Expanded( - child: CustomScrollView( - shrinkWrap: true, - slivers: [ - SliverToBoxAdapter( - child: - provider.customerInfo != null && - provider.customerInfo?.id != - AccountStorage() - .getCurrentUser() - ?.userProfile - ?.id - ? Stack( - children: [ - Container( - margin: EdgeInsets.symmetric( - horizontal: 15.w, - ), - child: Row( - children: [ - Image.asset( - "sc_images/general/sc_icon_logo.png", - height: 48.w, - ), - SizedBox(width: 10.w), - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - text( - SCAppLocalizations.of( - context, - )!.contactUs, - fontWeight: FontWeight.w600, - textColor: - widget.isRoom - ? Colors.white - : Colors.black, - fontSize: 15.sp, - ), - Row( - children: [ - Expanded( - child: Text( - "[${SCAppLocalizations.of(context)!.newMessage}]", - maxLines: 1, - overflow: - TextOverflow.ellipsis, - style: TextStyle( - fontSize: ScreenUtil() - .setSp(13), - color: - widget.isRoom - ? Colors.white54 - : Color( - 0xFF666666, - ), - ), - ), - ), - SizedBox(width: 5.w), - ], - ), - ], - ), - ), - SCDebounceWidget( - child: Container( - alignment: Alignment.center, - width: 88.w, - height: 26.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - 18.w, - ), - gradient: LinearGradient( - colors: [ - Color(0xff963EFB), - Color(0xffDCAEF6), - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - ), - child: text( - SCAppLocalizations.of( - context, - )!.contactUs, - fontWeight: FontWeight.w600, - textColor: Colors.white, - fontSize: 13.sp, - ), - ), - onTap: () async { - var conversation = V2TimConversation( - type: ConversationType.V2TIM_C2C, - userID: provider.customerInfo?.id, - conversationID: '', - ); - provider.updateCustomerCount(0); - var bool = - await Provider.of( - context, - listen: false, - ).startConversation(conversation); - if (!bool) return; - var json = jsonEncode( - conversation.toJson(), - ); - SCNavigatorUtils.push( - context, - "${SCChatRouter.chat}?conversation=${Uri.encodeComponent(json)}", - ); - }, - ), - ], - ), - ), - // 未读数量 - PositionedDirectional( - bottom: 0, - end: 10.w, - child: Visibility( - visible: provider.customerUnReadCount > 0, - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 6.w, - ), - alignment: Alignment.center, - height: 16.w, - decoration: BoxDecoration( - //gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]), - color: Colors.red, - shape: BoxShape.circle, - //borderRadius: BorderRadius.all(Radius.circular(7.w)), - ), - child: Text( - "${provider.customerUnReadCount > 99 ? '99+' : provider.customerUnReadCount}", - style: TextStyle( - fontSize: 10.sp, - color: Color(0xffffffff), - fontWeight: FontWeight.w400, - decoration: TextDecoration.none, - ), - ), - ), - ), - ), - ], - ) - : Container(), - ), - SliverToBoxAdapter( - child: _msgListView( - provider.conversationList, - isRoom: widget.isRoom, - ), - ), - ], - ), - ), - ], - ); - }, - ); - } - - Widget _msgListView( - List chatList, { - EdgeInsetsGeometry? margin, - bool isTop = false, - bool isRoom = false, - }) { - if (chatList.isEmpty) { - if (isTop) return Container(); - return widget.isRoom - ? mainEmpty(msg: '') - : mainEmpty(msg: '', image: const SizedBox.shrink()); - } - return Container( - margin: margin ?? EdgeInsets.only(bottom: 56.w), - child: ListView.separated( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.only( - bottom: isTop ? 0 : ScreenUtil().bottomBarHeight, - ), - itemBuilder: - (_, index) => _item( - chatList[index], - isTop: isTop, - index: index, - isRoom: isRoom, - ), - separatorBuilder: - (_, index) => Divider( - color: Colors.black12, - indent: 65.w, - endIndent: 15.w, - height: 0.5.w, - thickness: 0.5.w, - ), - itemCount: chatList.length, - ), - ); - } - - Widget _item( - V2TimConversation conversation, { - bool isTop = false, - bool isRoom = false, - required int index, - }) { - return ConversationItem( - conversation: conversation, - index: index, - isRoom: isRoom, - key: Key(conversation.conversationID), - ); - } -} - -class ConversationItem extends StatefulWidget { - final V2TimConversation conversation; - final int index; - final bool isRoom; - - const ConversationItem({ - Key? key, - required this.conversation, - required this.index, - required this.isRoom, - }) : super(key: key); - - @override - _ConversationItemState createState() => _ConversationItemState(); -} - -class _ConversationItemState extends State { - V2TimConversation get conversation => widget.conversation; - SocialChatUserProfile? user; - - @override - void initState() { - loadUserInfo(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - String content = ""; - if (conversation.lastMessage == null) return Container(); - // if (conversation.lastMessage.elemList.length == 0) return Container(); - // if (conversation.lastMessage != null && conversation.type != FTIMConversationType.System) { - if (conversation.lastMessage != null) { - if (conversation.lastMessage?.status == - MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) { - content = "[${SCAppLocalizations.of(context)!.messageHasBeenRecalled}]"; - } else { - if (conversation.lastMessage!.elemType == - MessageElemType.V2TIM_ELEM_TYPE_TEXT) { - V2TimTextElem textElem = conversation.lastMessage!.textElem!; - content = textElem.text!; - } else if (conversation.lastMessage!.elemType == - MessageElemType.V2TIM_ELEM_TYPE_IMAGE) { - content = SCAppLocalizations.of(context)!.image; - } else if (conversation.lastMessage!.elemType == - MessageElemType.V2TIM_ELEM_TYPE_VIDEO) { - content = SCAppLocalizations.of(context)!.video; - } else if (conversation.lastMessage!.elemType == - MessageElemType.V2TIM_ELEM_TYPE_SOUND) { - content = SCAppLocalizations.of(context)!.sound; - } else if (conversation.lastMessage!.elemType == - MessageElemType.V2TIM_ELEM_TYPE_CUSTOM) { - V2TimCustomElem customElem = conversation.lastMessage!.customElem!; - content = customElem.desc ?? ""; - /*if (customElem.extension == 'sendGift') { - content = "[礼物]"; - } else { - content = "[收到一条消息]"; - }*/ - content = SCAppLocalizations.of(context)!.receivedAMessage; - } - } - } - /*if (conversation.type == ConversationType.V2TIM_C2C) { - userProfile = UserProfileCacheManager.getData(conversation.id); - if (userProfile == null) { - UserProfileCacheManager.checkCacheAndAdd([conversation.id]).then((value) { - setState(() {}); - }); - return Container(); - } - }*/ - // print('time :${Platform.isAndroid ? (conversation.lastMsg?.timestamp ?? 0) * 1000 : conversation.lastMsg?.timestamp}'); - return GestureDetector( - onTap: () async { - if (conversation != null) { - conversation.unreadCount = 0; - var bool = await Provider.of( - context, - listen: false, - ).startConversation(conversation); - if (!bool) return; - var json = jsonEncode(widget.conversation.toJson()); - final route = - SCGlobalConfig.isSystemConversationId(conversation.conversationID) - ? SCChatRouter.systemChat - : SCChatRouter.chat; - SCNavigatorUtils.push( - context, - "$route?conversation=${Uri.encodeComponent(json)}", - ); - } - }, - onLongPress: () { - showDeleteConfirm(); - }, - child: Container( - decoration: BoxDecoration( - //color: Colors.white, - ), - child: Column( - children: [ - Container( - width: ScreenUtil().screenWidth, - padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.w), - decoration: BoxDecoration( - //color: Colors.white, - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - head(url: user?.userAvatar ?? "", width: 55.w), - SizedBox(width: 6.w), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: Row( - children: [ - socialchatNickNameText( - maxWidth: 152.w, - user?.userNickname ?? "", - fontSize: 14.sp, - fontWeight: FontWeight.w500, - textColor: - widget.isRoom - ? Colors.black - : Colors.white, - type: user?.getVIP()?.name ?? "", - needScroll: - (user - ?.userNickname - ?.characters - .length ?? - 0) > - 12, - ), - SizedBox(width: 5.w), - xb( - user?.userSex ?? 0, - color: - widget.isRoom - ? Colors.white54 - : Colors.black26, - ), - ], - ), - ), - SizedBox(width: 6.w), - Text( - SCMDateUtils.formatMessageTime( - context, - DateTime.fromMillisecondsSinceEpoch( - (conversation.lastMessage!.timestamp ?? 0) * - 1000, - ), - ), - style: TextStyle( - fontSize: 13.sp, - color: - widget.isRoom - ? Colors.white54 - : Color(0xff8d8d8d), - fontWeight: FontWeight.w400, - decoration: TextDecoration.none, - height: 1, - ), - ), - ], - ), - SizedBox(height: 3.w), - Row( - children: [ - Expanded( - child: Text( - content, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: ScreenUtil().setSp(13), - color: - widget.isRoom - ? Colors.white54 - : Color(0xFF666666), - ), - ), - ), - SizedBox(width: 5.w), - // 未读数量 - Visibility( - visible: conversation.unreadCount! > 0, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 6.w), - alignment: Alignment.center, - height: 16.w, - decoration: BoxDecoration( - //gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]), - color: Colors.red, - shape: BoxShape.circle, - //borderRadius: BorderRadius.all(Radius.circular(7.w)), - ), - child: Text( - "${conversation.unreadCount! > 99 ? '99+' : conversation.unreadCount}", - style: TextStyle( - fontSize: 10.sp, - color: Color(0xffffffff), - fontWeight: FontWeight.w400, - decoration: TextDecoration.none, - ), - ), - ), - ), - ], - ), - ], - ), - ), - ], - ), - ), - ], - ), - ), - ); - } - - void showDeleteConfirm() { - SmartDialog.show( - tag: "showConfirmDialog", - alignment: Alignment.center, - debounce: true, - animationType: SmartAnimationType.fade, - builder: (_) { - return MsgDialog( - title: SCAppLocalizations.of(context)!.tips, - msg: SCAppLocalizations.of(context)!.deleteConversationTips, - btnText: SCAppLocalizations.of(context)!.confirm, - onEnsure: () async { - Provider.of( - context, - listen: false, - ).clearC2CHistoryMessage(conversation.conversationID ?? "", true); - }, - ); - }, - ); - } - - void loadUserInfo() { - if (!SCGlobalConfig.isSystemConversationId(conversation.conversationID) && - conversation.conversationID != "customer" && - conversation.conversationID != "article") { - SCAccountRepository().loadUserInfo("${conversation.userID}").then(( - value, - ) { - user = value; - setState(() {}); - }); - } - } -} +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; +import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; +import 'package:provider/provider.dart'; +import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart'; +import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart'; +import 'package:tencent_cloud_chat_sdk/enum/message_status.dart'; +import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; +import 'package:tencent_cloud_chat_sdk/models/v2_tim_custom_elem.dart'; +import 'package:tencent_cloud_chat_sdk/models/v2_tim_text_elem.dart'; +import 'package:yumi/app_localizations.dart'; +import 'package:yumi/ui_kit/components/dialog/dialog_base.dart'; +import 'package:yumi/ui_kit/components/sc_compontent.dart'; +import 'package:yumi/ui_kit/components/text/sc_text.dart'; +import 'package:yumi/app/constants/sc_global_config.dart'; +import 'package:yumi/app/routes/sc_fluro_navigator.dart'; +import 'package:yumi/shared/tools/sc_date_utils.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; +import 'package:yumi/shared/business_logic/models/res/login_res.dart'; +import 'package:yumi/services/audio/rtm_manager.dart'; + +import '../../../modules/chat/chat_route.dart'; + +class MessageConversationListPage extends StatefulWidget { + bool isRoom; + + MessageConversationListPage(this.isRoom, {super.key}); + + @override + _MessageConversationListPageState createState() => + _MessageConversationListPageState(); +} + +class _MessageConversationListPageState + extends State { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (_, provider, __) { + return Column( + children: [ + Expanded( + child: CustomScrollView( + shrinkWrap: true, + slivers: [ + SliverToBoxAdapter( + child: + provider.customerInfo != null && + provider.customerInfo?.id != + AccountStorage() + .getCurrentUser() + ?.userProfile + ?.id + ? Stack( + children: [ + Container( + margin: EdgeInsets.symmetric( + horizontal: 15.w, + ), + child: Row( + children: [ + Image.asset( + "sc_images/general/sc_icon_logo.png", + height: 48.w, + ), + SizedBox(width: 10.w), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + text( + SCAppLocalizations.of( + context, + )!.contactUs, + fontWeight: FontWeight.w600, + textColor: + widget.isRoom + ? Colors.white + : Colors.black, + fontSize: 15.sp, + ), + Row( + children: [ + Expanded( + child: Text( + "[${SCAppLocalizations.of(context)!.newMessage}]", + maxLines: 1, + overflow: + TextOverflow.ellipsis, + style: TextStyle( + fontSize: ScreenUtil() + .setSp(13), + color: + widget.isRoom + ? Colors.white54 + : Color( + 0xFF666666, + ), + ), + ), + ), + SizedBox(width: 5.w), + ], + ), + ], + ), + ), + SCDebounceWidget( + child: Container( + alignment: Alignment.center, + width: 88.w, + height: 26.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + 18.w, + ), + gradient: LinearGradient( + colors: [ + Color(0xff963EFB), + Color(0xffDCAEF6), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + child: text( + SCAppLocalizations.of( + context, + )!.contactUs, + fontWeight: FontWeight.w600, + textColor: Colors.white, + fontSize: 13.sp, + ), + ), + onTap: () async { + var conversation = V2TimConversation( + type: ConversationType.V2TIM_C2C, + userID: provider.customerInfo?.id, + conversationID: '', + ); + provider.updateCustomerCount(0); + var bool = + await Provider.of( + context, + listen: false, + ).startConversation(conversation); + if (!bool) return; + var json = jsonEncode( + conversation.toJson(), + ); + SCNavigatorUtils.push( + context, + "${SCChatRouter.chat}?conversation=${Uri.encodeComponent(json)}", + ); + }, + ), + ], + ), + ), + // 未读数量 + PositionedDirectional( + bottom: 0, + end: 10.w, + child: Visibility( + visible: provider.customerUnReadCount > 0, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 6.w, + ), + alignment: Alignment.center, + height: 16.w, + decoration: BoxDecoration( + //gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]), + color: Colors.red, + shape: BoxShape.circle, + //borderRadius: BorderRadius.all(Radius.circular(7.w)), + ), + child: Text( + "${provider.customerUnReadCount > 99 ? '99+' : provider.customerUnReadCount}", + style: TextStyle( + fontSize: 10.sp, + color: Color(0xffffffff), + fontWeight: FontWeight.w400, + decoration: TextDecoration.none, + ), + ), + ), + ), + ), + ], + ) + : Container(), + ), + SliverToBoxAdapter( + child: _msgListView( + provider.conversationList, + isRoom: widget.isRoom, + ), + ), + ], + ), + ), + ], + ); + }, + ); + } + + Widget _msgListView( + List chatList, { + EdgeInsetsGeometry? margin, + bool isTop = false, + bool isRoom = false, + }) { + if (chatList.isEmpty) { + if (isTop) return Container(); + return widget.isRoom + ? mainEmpty(msg: '') + : mainEmpty(msg: '', image: const SizedBox.shrink()); + } + return Container( + margin: margin ?? EdgeInsets.only(bottom: 56.w), + child: ListView.separated( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.only( + bottom: isTop ? 0 : ScreenUtil().bottomBarHeight, + ), + itemBuilder: + (_, index) => _item( + chatList[index], + isTop: isTop, + index: index, + isRoom: isRoom, + ), + separatorBuilder: + (_, index) => Divider( + color: Colors.black12, + indent: 65.w, + endIndent: 15.w, + height: 0.5.w, + thickness: 0.5.w, + ), + itemCount: chatList.length, + ), + ); + } + + Widget _item( + V2TimConversation conversation, { + bool isTop = false, + bool isRoom = false, + required int index, + }) { + return ConversationItem( + conversation: conversation, + index: index, + isRoom: isRoom, + key: Key(conversation.conversationID), + ); + } +} + +class ConversationItem extends StatefulWidget { + final V2TimConversation conversation; + final int index; + final bool isRoom; + + const ConversationItem({ + Key? key, + required this.conversation, + required this.index, + required this.isRoom, + }) : super(key: key); + + @override + _ConversationItemState createState() => _ConversationItemState(); +} + +class _ConversationItemState extends State { + V2TimConversation get conversation => widget.conversation; + SocialChatUserProfile? user; + + bool get _isSystemConversation => + SCGlobalConfig.isSystemConversationId(conversation.conversationID) || + SCGlobalConfig.isSystemUserId(conversation.userID); + + bool get _isC2CConversation => + conversation.type == ConversationType.V2TIM_C2C; + + bool get _shouldLoadUserInfo => + _isC2CConversation && + !_isSystemConversation && + conversation.conversationID != "customer" && + conversation.conversationID != "article" && + (conversation.userID?.isNotEmpty ?? false); + + String get _displayAvatar { + final userAvatar = user?.userAvatar ?? ""; + if (userAvatar.isNotEmpty) { + return userAvatar; + } + return conversation.faceUrl ?? ""; + } + + String get _displayName { + final nickname = user?.userNickname ?? ""; + if (nickname.isNotEmpty) { + return nickname; + } + return conversation.showName ?? + conversation.userID ?? + conversation.groupID ?? + ""; + } + + @override + void initState() { + loadUserInfo(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + String content = ""; + if (conversation.lastMessage == null) return Container(); + // if (conversation.lastMessage.elemList.length == 0) return Container(); + // if (conversation.lastMessage != null && conversation.type != FTIMConversationType.System) { + if (conversation.lastMessage != null) { + if (conversation.lastMessage?.status == + MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) { + content = "[${SCAppLocalizations.of(context)!.messageHasBeenRecalled}]"; + } else { + if (conversation.lastMessage!.elemType == + MessageElemType.V2TIM_ELEM_TYPE_TEXT) { + V2TimTextElem textElem = conversation.lastMessage!.textElem!; + content = textElem.text!; + } else if (conversation.lastMessage!.elemType == + MessageElemType.V2TIM_ELEM_TYPE_IMAGE) { + content = SCAppLocalizations.of(context)!.image; + } else if (conversation.lastMessage!.elemType == + MessageElemType.V2TIM_ELEM_TYPE_VIDEO) { + content = SCAppLocalizations.of(context)!.video; + } else if (conversation.lastMessage!.elemType == + MessageElemType.V2TIM_ELEM_TYPE_SOUND) { + content = SCAppLocalizations.of(context)!.sound; + } else if (conversation.lastMessage!.elemType == + MessageElemType.V2TIM_ELEM_TYPE_CUSTOM) { + V2TimCustomElem customElem = conversation.lastMessage!.customElem!; + content = customElem.desc ?? ""; + /*if (customElem.extension == 'sendGift') { + content = "[礼物]"; + } else { + content = "[收到一条消息]"; + }*/ + content = SCAppLocalizations.of(context)!.receivedAMessage; + } + } + } + /*if (conversation.type == ConversationType.V2TIM_C2C) { + userProfile = UserProfileCacheManager.getData(conversation.id); + if (userProfile == null) { + UserProfileCacheManager.checkCacheAndAdd([conversation.id]).then((value) { + setState(() {}); + }); + return Container(); + } + }*/ + // print('time :${Platform.isAndroid ? (conversation.lastMsg?.timestamp ?? 0) * 1000 : conversation.lastMsg?.timestamp}'); + return GestureDetector( + onTap: () async { + if (_isC2CConversation && (conversation.userID?.isEmpty ?? true)) { + return; + } + if (conversation.type == ConversationType.V2TIM_GROUP && + (conversation.groupID?.isEmpty ?? true)) { + return; + } + conversation.unreadCount = 0; + var bool = await Provider.of( + context, + listen: false, + ).startConversation(conversation); + if (!bool) return; + var json = jsonEncode(widget.conversation.toJson()); + final route = + _isSystemConversation ? SCChatRouter.systemChat : SCChatRouter.chat; + SCNavigatorUtils.push( + context, + "$route?conversation=${Uri.encodeComponent(json)}", + ); + }, + onLongPress: () { + showDeleteConfirm(); + }, + child: Container( + decoration: BoxDecoration( + //color: Colors.white, + ), + child: Column( + children: [ + Container( + width: ScreenUtil().screenWidth, + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.w), + decoration: BoxDecoration( + //color: Colors.white, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + head(url: _displayAvatar, width: 55.w), + SizedBox(width: 6.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: Row( + children: [ + socialchatNickNameText( + maxWidth: 152.w, + _displayName, + fontSize: 14.sp, + fontWeight: FontWeight.w500, + textColor: + widget.isRoom + ? Colors.black + : Colors.white, + type: + _isC2CConversation + ? user?.getVIP()?.name ?? "" + : "", + needScroll: + _displayName.characters.length > 12, + ), + SizedBox(width: 5.w), + if (_isC2CConversation && user != null) + xb( + user?.userSex ?? 0, + color: + widget.isRoom + ? Colors.white54 + : Colors.black26, + ), + ], + ), + ), + SizedBox(width: 6.w), + Text( + SCMDateUtils.formatMessageTime( + context, + DateTime.fromMillisecondsSinceEpoch( + (conversation.lastMessage!.timestamp ?? 0) * + 1000, + ), + ), + style: TextStyle( + fontSize: 13.sp, + color: + widget.isRoom + ? Colors.white54 + : Color(0xff8d8d8d), + fontWeight: FontWeight.w400, + decoration: TextDecoration.none, + height: 1, + ), + ), + ], + ), + SizedBox(height: 3.w), + Row( + children: [ + Expanded( + child: Text( + content, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: ScreenUtil().setSp(13), + color: + widget.isRoom + ? Colors.white54 + : Color(0xFF666666), + ), + ), + ), + SizedBox(width: 5.w), + // 未读数量 + Visibility( + visible: conversation.unreadCount! > 0, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 6.w), + alignment: Alignment.center, + height: 16.w, + decoration: BoxDecoration( + //gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]), + color: Colors.red, + shape: BoxShape.circle, + //borderRadius: BorderRadius.all(Radius.circular(7.w)), + ), + child: Text( + "${conversation.unreadCount! > 99 ? '99+' : conversation.unreadCount}", + style: TextStyle( + fontSize: 10.sp, + color: Color(0xffffffff), + fontWeight: FontWeight.w400, + decoration: TextDecoration.none, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + } + + void showDeleteConfirm() { + SmartDialog.show( + tag: "showConfirmDialog", + alignment: Alignment.center, + debounce: true, + animationType: SmartAnimationType.fade, + builder: (_) { + return MsgDialog( + title: SCAppLocalizations.of(context)!.tips, + msg: SCAppLocalizations.of(context)!.deleteConversationTips, + btnText: SCAppLocalizations.of(context)!.confirm, + onEnsure: () async { + Provider.of( + context, + listen: false, + ).clearC2CHistoryMessage(conversation.conversationID, true); + }, + ); + }, + ); + } + + void loadUserInfo() { + if (!_shouldLoadUserInfo) return; + SCAccountRepository().loadUserInfo(conversation.userID!).then((value) { + user = value; + setState(() {}); + }); + } +} diff --git a/lib/ui_kit/widgets/room/room_msg_item.dart b/lib/ui_kit/widgets/room/room_msg_item.dart index 4827517..f4c1b80 100644 --- a/lib/ui_kit/widgets/room/room_msg_item.dart +++ b/lib/ui_kit/widgets/room/room_msg_item.dart @@ -1118,12 +1118,16 @@ class _MsgItemState extends State { } _buildUserInfoLine(BuildContext context) { + final userId = widget.msg.user?.id; if ((widget.msg.needUpDataUserInfo) ?? false) { - SCRoomUtils.roomUsersMap.remove(widget.msg.user?.id ?? ""); + SCRoomUtils.roomUsersMap.remove(userId ?? ""); } - return SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""] == null + if (userId == null || userId.isEmpty) { + return _buildPendingUserInfoLine(context); + } + return SCRoomUtils.roomUsersMap[userId] == null ? FutureBuilder( - future: SCAccountRepository().loadUserInfo(widget.msg.user?.id ?? ""), + future: SCAccountRepository().loadUserInfo(userId), builder: (ct, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasData) { @@ -1132,96 +1136,92 @@ class _MsgItemState extends State { return _buildNewUserInfoLine(context, user); } } - return Row( - children: [ - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - widget.onClick(widget.msg.user); - }, - child: Stack( - alignment: AlignmentDirectional.bottomEnd, - children: [ - head( - url: widget.msg.user?.userAvatar ?? "", - width: 48.w, - // headdress: msg.user?.getHeaddress()?.sourceUrl, - ), - Positioned( - child: msgRoleTag(widget.msg.role ?? "", width: 14.w), - bottom: 2.w, - right: 2.w, - ), - ], - ), - ), - SizedBox(width: 3.w), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - getWealthLevel( - widget.msg.user?.wealthLevel ?? 0, - width: 48.w, - height: 23.w, - fontSize: 10.sp, - ), - getUserLevel( - widget.msg.user?.charmLevel ?? 0, - width: 48.w, - height: 23.w, - fontSize: 10.sp, - ), - SizedBox(width: 3.w), - socialchatNickNameText( - maxWidth: 120.w, - fontWeight: FontWeight.w500, - widget.msg.user?.userNickname ?? "", - fontSize: 13.sp, - type: widget.msg.user?.getVIP()?.name ?? "", - needScroll: - (widget - .msg - .user - ?.userNickname - ?.characters - .length ?? - 0) > - 10, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - widget.msg.user?.getVIP() != null - ? netImage( - url: widget.msg.user?.getVIP()?.cover ?? "", - width: 25.w, - height: 25.w, - ) - : Container(), - _buildMedals( - (widget.msg.user?.wearBadge?.where((item) { - return item.use ?? false; - }).toList() ?? - []), - ), - ], - ), - ], - ), - ), - ], - ); + return _buildPendingUserInfoLine(context); }, ) - : _buildNewUserInfoLine( - context, - SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""]!, - ); + : _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!); + } + + Widget _buildPendingUserInfoLine(BuildContext context) { + return Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + widget.onClick(widget.msg.user); + }, + child: Stack( + alignment: AlignmentDirectional.bottomEnd, + children: [ + head( + url: widget.msg.user?.userAvatar ?? "", + width: 48.w, + // headdress: msg.user?.getHeaddress()?.sourceUrl, + ), + Positioned( + child: msgRoleTag(widget.msg.role ?? "", width: 14.w), + bottom: 2.w, + right: 2.w, + ), + ], + ), + ), + SizedBox(width: 3.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + getWealthLevel( + widget.msg.user?.wealthLevel ?? 0, + width: 48.w, + height: 23.w, + fontSize: 10.sp, + ), + getUserLevel( + widget.msg.user?.charmLevel ?? 0, + width: 48.w, + height: 23.w, + fontSize: 10.sp, + ), + SizedBox(width: 3.w), + socialchatNickNameText( + maxWidth: 120.w, + fontWeight: FontWeight.w500, + widget.msg.user?.userNickname ?? "", + fontSize: 13.sp, + type: widget.msg.user?.getVIP()?.name ?? "", + needScroll: + (widget.msg.user?.userNickname?.characters.length ?? + 0) > + 10, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + widget.msg.user?.getVIP() != null + ? netImage( + url: widget.msg.user?.getVIP()?.cover ?? "", + width: 25.w, + height: 25.w, + ) + : Container(), + _buildMedals( + (widget.msg.user?.wearBadge?.where((item) { + return item.use ?? false; + }).toList() ?? + []), + ), + ], + ), + ], + ), + ), + ], + ); } _buildNewUserInfoLine(BuildContext context, SocialChatUserProfile user) { diff --git a/lib/ui_kit/widgets/room/seat/room_seat_widget.dart b/lib/ui_kit/widgets/room/seat/room_seat_widget.dart index 17efcaa..3d65a13 100644 --- a/lib/ui_kit/widgets/room/seat/room_seat_widget.dart +++ b/lib/ui_kit/widgets/room/seat/room_seat_widget.dart @@ -6,15 +6,34 @@ import 'package:yumi/services/audio/rtc_manager.dart'; import '../../../../modules/room/seat/sc_seat_item.dart'; class RoomSeatWidget extends StatefulWidget { + const RoomSeatWidget({super.key}); + @override - _RoomSeatWidgetState createState() => _RoomSeatWidgetState(); + State createState() => _RoomSeatWidgetState(); } class _RoomSeatWidgetState extends State { int _lastSeatCount = 0; + int _normalizeSeatCount(int? seatCount) { + switch (seatCount) { + case 5: + case 10: + case 15: + case 20: + return seatCount ?? 0; + default: + return 0; + } + } + int _resolvedSeatCount(_RoomSeatLayoutSnapshot snapshot) { - final int seatCount = snapshot.seatCount; + final int liveSeatCount = _normalizeSeatCount(snapshot.seatCount); + final int configuredSeatCount = _normalizeSeatCount( + snapshot.configuredSeatCount, + ); + final int seatCount = + liveSeatCount > 0 ? liveSeatCount : configuredSeatCount; if (!snapshot.isExitingCurrentVoiceRoomSession && seatCount > 0) { _lastSeatCount = seatCount; } @@ -30,6 +49,10 @@ class _RoomSeatWidgetState extends State { selector: (context, provider) => _RoomSeatLayoutSnapshot( seatCount: provider.roomWheatMap.length, + configuredSeatCount: + provider.currenRoom?.roomProfile?.roomSetting?.mikeSize + ?.toInt() ?? + 0, isExitingCurrentVoiceRoomSession: provider.isExitingCurrentVoiceRoomSession, ), @@ -187,10 +210,12 @@ class _RoomSeatWidgetState extends State { class _RoomSeatLayoutSnapshot { const _RoomSeatLayoutSnapshot({ required this.seatCount, + required this.configuredSeatCount, required this.isExitingCurrentVoiceRoomSession, }); final int seatCount; + final int configuredSeatCount; final bool isExitingCurrentVoiceRoomSession; @override @@ -200,10 +225,15 @@ class _RoomSeatLayoutSnapshot { } return other is _RoomSeatLayoutSnapshot && other.seatCount == seatCount && + other.configuredSeatCount == configuredSeatCount && other.isExitingCurrentVoiceRoomSession == isExitingCurrentVoiceRoomSession; } @override - int get hashCode => Object.hash(seatCount, isExitingCurrentVoiceRoomSession); + int get hashCode => Object.hash( + seatCount, + configuredSeatCount, + isExitingCurrentVoiceRoomSession, + ); } diff --git a/test/room_seat_widget_test.dart b/test/room_seat_widget_test.dart new file mode 100644 index 0000000..19fe25b --- /dev/null +++ b/test/room_seat_widget_test.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; +import 'package:yumi/modules/room/seat/sc_seat_item.dart'; +import 'package:yumi/services/audio/rtc_manager.dart'; +import 'package:yumi/shared/business_logic/models/res/join_room_res.dart'; +import 'package:yumi/shared/business_logic/models/res/room_res.dart'; +import 'package:yumi/ui_kit/widgets/room/seat/room_seat_widget.dart'; + +void main() { + testWidgets( + 'RoomSeatWidget falls back to configured mikeSize when mic list is empty', + (tester) async { + await tester.binding.setSurfaceSize(const Size(1600, 1200)); + addTearDown(() async { + await tester.binding.setSurfaceSize(null); + }); + + final rtcProvider = + RealTimeCommunicationManager() + ..currenRoom = JoinRoomRes( + roomProfile: RoomProfile(roomSetting: RoomSetting(mikeSize: 10)), + ); + + await tester.pumpWidget( + ScreenUtilInit( + designSize: const Size(375, 812), + builder: + (_, __) => + ChangeNotifierProvider.value( + value: rtcProvider, + child: MaterialApp(home: Scaffold(body: RoomSeatWidget())), + ), + ), + ); + + expect(find.byType(RoomSeatWidget), findsOneWidget); + expect(find.byType(SCSeatItem), findsNWidgets(10)); + }, + ); +}