退出房间删除rtc, 消息列表判断

This commit is contained in:
hy 2026-04-22 18:09:41 +08:00
parent 15ac93c737
commit a5bc029113
9 changed files with 957 additions and 762 deletions

View File

@ -78,10 +78,7 @@ class SCGlobalConfig {
static Set<String> get systemConversationIds { static Set<String> get systemConversationIds {
return { return {
"administrator", for (final userId in systemUserIds) ...{userId, "c2c_$userId"},
...systemUserIds
.where((element) => element != "administrator")
.map((element) => "c2c_$element"),
}; };
} }
@ -93,7 +90,7 @@ class SCGlobalConfig {
if (conversationId == null || conversationId.isEmpty) { if (conversationId == null || conversationId.isEmpty) {
return false; return false;
} }
return systemConversationIds.contains(conversationId); return systemUserIds.contains(_normalizeConversationUserId(conversationId));
} }
static bool isSystemUserId(String? userId) { static bool isSystemUserId(String? userId) {

View File

@ -24,6 +24,7 @@ import 'package:marquee/marquee.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.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/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_elem_type.dart';
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart'; import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.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_message_notifier.dart';
import 'package:yumi/shared/tools/sc_path_utils.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/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/services/auth/user_profile_manager.dart';
import 'package:yumi/modules/index/main_route.dart'; import 'package:yumi/modules/index/main_route.dart';
@ -104,6 +104,34 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
List<String> coinsTitles = ["100", "1000", "10000", "50000"]; List<String> coinsTitles = ["100", "1000", "10000", "50000"];
String selecteCoins = "100"; 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 @override
void initState() { void initState() {
super.initState(); super.initState();
@ -119,7 +147,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage; rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage;
loadMsg(); loadMsg();
if (friend == null) { if (_isC2CConversation && friend == null) {
loadFriend(); loadFriend();
} }
@ -220,36 +248,35 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
child: Container( child: Container(
alignment: AlignmentDirectional.center, alignment: AlignmentDirectional.center,
child: socialchatNickNameText( child: socialchatNickNameText(
friend?.userNickname ?? "", _conversationTitle,
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
textColor: Colors.white, textColor: Colors.white,
type: "", type: "",
needScroll: needScroll:
(friend?.userNickname?.characters.length ?? _conversationTitle.characters.length > 22,
0) >
22,
), ),
), ),
), ),
GestureDetector( if (_canOpenFriendProfile)
behavior: HitTestBehavior.opaque, GestureDetector(
onTap: () { behavior: HitTestBehavior.opaque,
SCNavigatorUtils.push( onTap: () {
context, SCNavigatorUtils.push(
replace: true, context,
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}", replace: true,
); "${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}",
}, );
child: Container( },
padding: EdgeInsets.symmetric(horizontal: 10.w), child: Container(
child: Icon( padding: EdgeInsets.symmetric(horizontal: 10.w),
Icons.more_vert, child: Icon(
size: 22.w, Icons.more_vert,
color: Colors.white, size: 22.w,
color: Colors.white,
),
), ),
), ),
),
], ],
), ),
Expanded(child: _msgList()), Expanded(child: _msgList()),
@ -262,10 +289,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
top: false, top: false,
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
if (!SCGlobalConfig.isSystemConversationId( if (_canSendConversationMessage) _input(),
currentConversation?.conversationID,
))
_input(),
_tools(provider), _tools(provider),
_emoji(), _emoji(),
// _fahongbao(), // _fahongbao(),
@ -285,18 +309,18 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
} }
void loadFriend() { void loadFriend() {
if (SCGlobalConfig.isSystemConversationId( final userId = currentConversation?.userID;
currentConversation?.conversationID, if (!_isC2CConversation ||
) || _isSystemConversation ||
SCGlobalConfig.isSystemUserId(currentConversation?.userID)) { SCGlobalConfig.isSystemUserId(userId) ||
userId == null ||
userId.isEmpty) {
return; return;
} }
SCLoadingManager.show(); SCLoadingManager.show();
Future.wait([ Future.wait([
SCAccountRepository().loadUserInfo("${currentConversation?.userID}"), SCAccountRepository().loadUserInfo(userId),
SCAccountRepository().friendRelationCheck( SCAccountRepository().friendRelationCheck(userId),
"${currentConversation?.userID}",
),
]) ])
.then((result) { .then((result) {
SCLoadingManager.hide(); SCLoadingManager.hide();
@ -318,19 +342,10 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
onLoading: () async { onLoading: () async {
print('onLoading'); print('onLoading');
if (currentConversationMessageList.isNotEmpty) { if (currentConversationMessageList.isNotEmpty) {
// final messages = await _loadHistoryMessages(
// lastMsgID null count: 30,
// lastMsgID 使id lastMsgID: currentConversationMessageList.last.msgID,
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback = );
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.getC2CHistoryMessageList(
userID: currentConversation!.userID ?? "",
count: 30,
lastMsgID: currentConversationMessageList.last.msgID,
);
List<V2TimMessage> messages =
v2timValueCallback.data as List<V2TimMessage>;
print('加载前:${currentConversationMessageList.length}'); print('加载前:${currentConversationMessageList.length}');
currentConversationMessageList.addAll(messages); currentConversationMessageList.addAll(messages);
print('加载后:${currentConversationMessageList.length}'); print('加载后:${currentConversationMessageList.length}');
@ -386,6 +401,54 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
); );
} }
Future<List<V2TimMessage>> _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<void> _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<void> loadMsg() async { Future<void> loadMsg() async {
// 1. // 1.
TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener( TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(
@ -404,19 +467,9 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
}, },
), ),
); );
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback = final messages = await _loadHistoryMessages(count: 100);
await TencentImSDKPlugin.v2TIMManager print("messages : ${messages.length}");
.getMessageManager() currentConversationMessageList.clear();
.getC2CHistoryMessageList(
userID: currentConversation!.userID!,
count: 100,
lastMsgID: null,
);
List<V2TimMessage> messages = v2timValueCallback.data!;
// List<V2TimMessage> messages = await FTIM.getMessageManager().getMessages(conversation: currentConversation);
print("messages : ${messages?.length ?? 0}");
currentConversationMessageList ??= [];
currentConversationMessageList?.clear();
for (var msg in messages) { for (var msg in messages) {
if (!msg.isSelf! && msg.isPeerRead!) { if (!msg.isSelf! && msg.isPeerRead!) {
@ -425,7 +478,8 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
.sendMessageReadReceipts(messageIDList: [msg.msgID!]); .sendMessageReadReceipts(messageIDList: [msg.msgID!]);
} }
} }
currentConversationMessageList.insertAll(0, messages ?? []); currentConversationMessageList.insertAll(0, messages);
await _markCurrentConversationAsRead();
setState(() {}); setState(() {});
} }
@ -767,7 +821,11 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
if (message == null) { if (message == null) {
return; return;
} }
if (message.userID != currentConversation?.userID) return; if (_isGroupConversation) {
if (message.groupID != currentConversation?.groupID) return;
} else if (message.userID != currentConversation?.userID) {
return;
}
int? index; int? index;
for (var element in currentConversationMessageList) { for (var element in currentConversationMessageList) {
@ -788,14 +846,12 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
return; return;
} }
if (index != null) { if (index != null) {
currentConversationMessageList.removeAt(index!); currentConversationMessageList.removeAt(index);
currentConversationMessageList.insert(index!, message); currentConversationMessageList.insert(index, message);
} else { } else {
currentConversationMessageList.insert(0, message); currentConversationMessageList.insert(0, message);
} }
await TencentImSDKPlugin.v2TIMManager await _markCurrentConversationAsRead();
.getMessageManager()
.markC2CMessageAsRead(userID: currentConversation!.userID!);
// //
await TencentImSDKPlugin.v2TIMManager await TencentImSDKPlugin.v2TIMManager
.getMessageManager() .getMessageManager()
@ -849,7 +905,7 @@ class _MessageItem extends StatelessWidget {
} }
String time = SCMDateUtils.formatMessageTime( String time = SCMDateUtils.formatMessageTime(
context, context,
DateTime.fromMillisecondsSinceEpoch(timestamp ?? 0), DateTime.fromMillisecondsSinceEpoch(timestamp),
); );
if (message.status == MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) { if (message.status == MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) {
return Container( return Container(

View File

@ -572,6 +572,26 @@ class RealTimeCommunicationManager extends ChangeNotifier {
} }
} }
Future<void> _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({ void _applyLocalAudioRuntimeState({
required ClientRoleType clientRole, required ClientRoleType clientRole,
required bool muted, required bool muted,
@ -1058,6 +1078,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
), ),
); );
await _bootstrapRoomHeartbeatState();
/// ///
retrieveMicrophoneList(); retrieveMicrophoneList();
fetchOnlineUsersList(); fetchOnlineUsersList();

View File

@ -133,22 +133,27 @@ class RealTimeMessagingManager extends ChangeNotifier {
/// ///
SocialChatUserProfile? customerInfo; SocialChatUserProfile? customerInfo;
bool _isSystemConversation(V2TimConversation? conversation) {
if (conversation == null) {
return false;
}
return SCGlobalConfig.isSystemConversationId(conversation.conversationID) ||
SCGlobalConfig.isSystemUserId(conversation.userID);
}
int _systemUnreadCount() { int _systemUnreadCount() {
int count = 0; int count = 0;
for (final conversationId in SCGlobalConfig.systemConversationIds) { for (final conversation in conversationMap.values) {
count += conversationMap[conversationId]?.unreadCount ?? 0; if (_isSystemConversation(conversation)) {
count += conversation.unreadCount ?? 0;
}
} }
return count; return count;
} }
V2TimConversation getPreferredSystemConversation() { V2TimConversation getPreferredSystemConversation() {
final systemConversations = final systemConversations =
conversationMap.values conversationMap.values.where(_isSystemConversation).toList()
.where(
(element) =>
SCGlobalConfig.isSystemConversationId(element.conversationID),
)
.toList()
..sort((e1, e2) { ..sort((e1, e2) {
final time1 = e1.lastMessage?.timestamp ?? 0; final time1 = e1.lastMessage?.timestamp ?? 0;
final time2 = e2.lastMessage?.timestamp ?? 0; final time2 = e2.lastMessage?.timestamp ?? 0;
@ -169,6 +174,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
void getConversationList() { void getConversationList() {
List<V2TimConversation> list = conversationMap.values.toList(); List<V2TimConversation> list = conversationMap.values.toList();
list.removeWhere((element) { list.removeWhere((element) {
if (_isSystemConversation(element)) {
return true;
}
if (element.conversationID == "c2c_${customerInfo?.id}") { if (element.conversationID == "c2c_${customerInfo?.id}") {
return true; return true;
} }
@ -501,7 +509,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
_onNew1v1Message(V2TimMessage? message, {String? msgId}) async { _onNew1v1Message(V2TimMessage? message, {String? msgId}) async {
for (var element in conversationList) { for (var element in conversationList) {
if (message?.userID == element?.userID) { if (message?.userID == element.userID) {
element.lastMessage = message; element.lastMessage = message;
if (onNewMessageCurrentConversationListener == null) { if (onNewMessageCurrentConversationListener == null) {
element.unreadCount = element.unreadCount! + 1; element.unreadCount = element.unreadCount! + 1;
@ -1740,7 +1748,14 @@ class RealTimeMessagingManager extends ChangeNotifier {
} }
Future quitGroup(String groupID) async { 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) { void updateSystemCount(int count) {
for (final conversationId in SCGlobalConfig.systemConversationIds) { for (final conversation in conversationMap.values) {
conversationMap[conversationId]?.unreadCount = 0; if (_isSystemConversation(conversation)) {
conversation.unreadCount = 0;
}
} }
systemUnReadCount = 0; systemUnReadCount = 0;
notifyListeners(); notifyListeners();
@ -1870,21 +1887,23 @@ class RealTimeMessagingManager extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void clearC2CHistoryMessage(String conversationID, bool needShowToast) async { Future<void> deleteConversationById(
// String conversationID, {
bool needShowToast = false,
V2TimCallback clearC2CHistoryMessageRes = await TencentImSDKPlugin }) async {
.v2TIMManager if (conversationID.isEmpty) {
.getConversationManager() return;
.deleteConversation(conversationID: conversationID); // id
if (clearC2CHistoryMessageRes.code == 0) {
//
if (needShowToast) {
SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful);
}
initConversation();
} else {
// clearC2CHistoryMessageRes.desc
} }
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);
} }
} }

View File

@ -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 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
import '../../shared/data_sources/models/enum/sc_heartbeat_status.dart'; import '../../shared/data_sources/models/enum/sc_heartbeat_status.dart';
class SCHeartbeatUtils { class SCHeartbeatUtils {
static Timer? _h; static Timer? _h;
static Timer? _a; static Timer? _a;
static String _c = SCHeartbeatStatus.ONLINE.name; static String _c = SCHeartbeatStatus.ONLINE.name;
static String? _r; static String? _r;
static bool _u = false; static bool _u = false;
/// ///
static void scheduleHeartbeat( static Future<void> scheduleHeartbeat(
String status, String status,
bool upMick, { bool upMick, {
String? roomId, String? roomId,
@ -42,14 +42,14 @@ class SCHeartbeatUtils {
} catch (_) {} } catch (_) {}
} }
} }
static void cancelTimer() { static void cancelTimer() {
_h?.cancel(); _h?.cancel();
_h = null; _h = null;
_r = null; _r = null;
cancelAnchorTimer(); cancelAnchorTimer();
} }
/// ///
static void scheduleAnchorHeartbeat(String roomId) async { static void scheduleAnchorHeartbeat(String roomId) async {
cancelAnchorTimer(); cancelAnchorTimer();
@ -60,10 +60,10 @@ class SCHeartbeatUtils {
}); });
}); });
} }
static void cancelAnchorTimer() { static void cancelAnchorTimer() {
_a?.cancel(); _a?.cancel();
_a = null; _a = null;
_u = false; _u = false;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1118,12 +1118,16 @@ class _MsgItemState extends State<MsgItem> {
} }
_buildUserInfoLine(BuildContext context) { _buildUserInfoLine(BuildContext context) {
final userId = widget.msg.user?.id;
if ((widget.msg.needUpDataUserInfo) ?? false) { 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( ? FutureBuilder(
future: SCAccountRepository().loadUserInfo(widget.msg.user?.id ?? ""), future: SCAccountRepository().loadUserInfo(userId),
builder: (ct, AsyncSnapshot<SocialChatUserProfile> snapshot) { builder: (ct, AsyncSnapshot<SocialChatUserProfile> snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) { if (snapshot.hasData) {
@ -1132,96 +1136,92 @@ class _MsgItemState extends State<MsgItem> {
return _buildNewUserInfoLine(context, user); return _buildNewUserInfoLine(context, user);
} }
} }
return Row( return _buildPendingUserInfoLine(context);
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( : _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!);
context, }
SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""]!,
); 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) { _buildNewUserInfoLine(BuildContext context, SocialChatUserProfile user) {

View File

@ -6,15 +6,34 @@ import 'package:yumi/services/audio/rtc_manager.dart';
import '../../../../modules/room/seat/sc_seat_item.dart'; import '../../../../modules/room/seat/sc_seat_item.dart';
class RoomSeatWidget extends StatefulWidget { class RoomSeatWidget extends StatefulWidget {
const RoomSeatWidget({super.key});
@override @override
_RoomSeatWidgetState createState() => _RoomSeatWidgetState(); State<RoomSeatWidget> createState() => _RoomSeatWidgetState();
} }
class _RoomSeatWidgetState extends State<RoomSeatWidget> { class _RoomSeatWidgetState extends State<RoomSeatWidget> {
int _lastSeatCount = 0; 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) { 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) { if (!snapshot.isExitingCurrentVoiceRoomSession && seatCount > 0) {
_lastSeatCount = seatCount; _lastSeatCount = seatCount;
} }
@ -30,6 +49,10 @@ class _RoomSeatWidgetState extends State<RoomSeatWidget> {
selector: selector:
(context, provider) => _RoomSeatLayoutSnapshot( (context, provider) => _RoomSeatLayoutSnapshot(
seatCount: provider.roomWheatMap.length, seatCount: provider.roomWheatMap.length,
configuredSeatCount:
provider.currenRoom?.roomProfile?.roomSetting?.mikeSize
?.toInt() ??
0,
isExitingCurrentVoiceRoomSession: isExitingCurrentVoiceRoomSession:
provider.isExitingCurrentVoiceRoomSession, provider.isExitingCurrentVoiceRoomSession,
), ),
@ -187,10 +210,12 @@ class _RoomSeatWidgetState extends State<RoomSeatWidget> {
class _RoomSeatLayoutSnapshot { class _RoomSeatLayoutSnapshot {
const _RoomSeatLayoutSnapshot({ const _RoomSeatLayoutSnapshot({
required this.seatCount, required this.seatCount,
required this.configuredSeatCount,
required this.isExitingCurrentVoiceRoomSession, required this.isExitingCurrentVoiceRoomSession,
}); });
final int seatCount; final int seatCount;
final int configuredSeatCount;
final bool isExitingCurrentVoiceRoomSession; final bool isExitingCurrentVoiceRoomSession;
@override @override
@ -200,10 +225,15 @@ class _RoomSeatLayoutSnapshot {
} }
return other is _RoomSeatLayoutSnapshot && return other is _RoomSeatLayoutSnapshot &&
other.seatCount == seatCount && other.seatCount == seatCount &&
other.configuredSeatCount == configuredSeatCount &&
other.isExitingCurrentVoiceRoomSession == other.isExitingCurrentVoiceRoomSession ==
isExitingCurrentVoiceRoomSession; isExitingCurrentVoiceRoomSession;
} }
@override @override
int get hashCode => Object.hash(seatCount, isExitingCurrentVoiceRoomSession); int get hashCode => Object.hash(
seatCount,
configuredSeatCount,
isExitingCurrentVoiceRoomSession,
);
} }

View File

@ -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<RealTimeCommunicationManager>.value(
value: rtcProvider,
child: MaterialApp(home: Scaffold(body: RoomSeatWidget())),
),
),
);
expect(find.byType(RoomSeatWidget), findsOneWidget);
expect(find.byType(SCSeatItem), findsNWidgets(10));
},
);
}