From 67baeb15f35357b42110ea7b3892152048f0fcbc Mon Sep 17 00:00:00 2001 From: roxy Date: Wed, 3 Jun 2026 14:28:43 +0800 Subject: [PATCH] =?UTF-8?q?=E9=BA=A6=E5=85=8B=E9=A3=8E=E5=BC=80=E5=85=B3?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/services/audio/rtc_manager.dart | 151 +++++++++++++++++++++++---- test/rtc_self_mic_snapshot_test.dart | 28 +++++ 2 files changed, 161 insertions(+), 18 deletions(-) diff --git a/lib/services/audio/rtc_manager.dart b/lib/services/audio/rtc_manager.dart index 5b381a7..d2415c7 100644 --- a/lib/services/audio/rtc_manager.dart +++ b/lib/services/audio/rtc_manager.dart @@ -436,6 +436,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { int? _pendingSelfMicSwitchGuardUntilMs; num? _pendingSelfMicReleaseIndex; int? _pendingSelfMicReleaseGuardUntilMs; + final Set _confirmedSelfMicIdentityKeys = {}; int? _seatResizeOccupantPreserveUntilMs; _RoomRtcLocalAudioRole? _lastAppliedLocalAudioRole; bool? _lastAppliedLocalAudioMuted; @@ -529,6 +530,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { currentUser?.id?.trim() ?? "", currentUser?.account?.trim() ?? "", currentUser?.getID().trim() ?? "", + ..._confirmedSelfMicIdentityKeys, }..removeWhere((item) => item.isEmpty); } @@ -550,6 +552,16 @@ class RealTimeCommunicationManager extends ChangeNotifier { return _userProfileIdentityKeys(user).any(identityKeys.contains); } + void _rememberConfirmedSelfMicIdentity(SocialChatUserProfile? user) { + _confirmedSelfMicIdentityKeys + ..clear() + ..addAll(_userProfileIdentityKeys(user)); + } + + void _clearConfirmedSelfMicIdentity() { + _confirmedSelfMicIdentityKeys.clear(); + } + ///房间红包列表 List redPacketList = []; @@ -2522,7 +2534,10 @@ class RealTimeCommunicationManager extends ChangeNotifier { notifyListeners(); final currentUserId = (AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim(); - final seatIndex = userOnMaiInIndex(currentUserId); + final seatIndex = + currentUserId.isNotEmpty + ? userOnMaiInIndex(currentUserId) + : (_currentUserMicSeat()?.micIndex ?? -1); if (seatIndex < 0) { notifyListeners(); return; @@ -2545,14 +2560,18 @@ class RealTimeCommunicationManager extends ChangeNotifier { Future handleSelfMicRemovedByRemote({ bool refreshMicList = true, }) async { + final currentUserKeys = _currentUserIdentityKeys(); final currentUserId = (AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim(); - if (currentUserId.isEmpty) { + if (currentUserKeys.isEmpty) { await _switchRoomRtcToAudience(); return; } - final currentSeatIndex = userOnMaiInIndex(currentUserId); + final currentSeatIndex = + currentUserId.isNotEmpty + ? userOnMaiInIndex(currentUserId) + : (_currentUserMicSeat()?.micIndex ?? -1); isMic = true; _clearSelfMicSwitchGuard(clearPreferredIndex: true); _clearSelfMicGoUpGuard(clearPreferredIndex: true); @@ -2561,9 +2580,10 @@ class RealTimeCommunicationManager extends ChangeNotifier { } else { _clearSelfMicReleaseGuard(); } - _clearUserFromSeats(currentUserId); + _clearCurrentUserFromSeats(); _roomRtcRoleIsBroadcaster = false; _syncSelfMicRuntimeState(); + _clearConfirmedSelfMicIdentity(); notifyListeners(); await _switchRoomRtcToAudience(); @@ -5049,6 +5069,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { _clearSelfMicSwitchGuard(clearPreferredIndex: true); _clearSelfMicGoUpGuard(clearPreferredIndex: true); _clearSelfMicReleaseGuard(); + _clearConfirmedSelfMicIdentity(); _seatResizeOccupantPreserveUntilMs = null; roomIsMute = false; rtmProvider?.roomAllMsgList.clear(); @@ -5207,16 +5228,21 @@ class RealTimeCommunicationManager extends ChangeNotifier { ).catchError((_) {}); } - void _clearUserFromSeats(String? userId, {num? exceptIndex}) { - final normalizedUserId = (userId ?? "").trim(); - if (normalizedUserId.isEmpty) { + void _clearCurrentUserFromSeats({num? exceptIndex}) { + _clearUsersFromSeatsByIdentityKeys( + _currentUserIdentityKeys(), + exceptIndex: exceptIndex, + ); + } + + void _clearUsersFromSeatsByIdentityKeys( + Set lookupKeys, { + num? exceptIndex, + }) { + lookupKeys.removeWhere((item) => item.trim().isEmpty); + if (lookupKeys.isEmpty) { return; } - final lookupKeys = {normalizedUserId}; - final currentUserKeys = _currentUserIdentityKeys(); - if (currentUserKeys.contains(normalizedUserId)) { - lookupKeys.addAll(currentUserKeys); - } roomWheatMap.forEach((seatIndex, seat) { final seatUserKeys = _userProfileIdentityKeys(seat.user); if (!seatUserKeys.any(lookupKeys.contains) || seatIndex == exceptIndex) { @@ -5232,6 +5258,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { required SCMicGoUpRes micGoUpRes, required bool selfMicMuted, }) { + _rememberConfirmedSelfMicIdentity(myUser); final previousSelfSeatIndex = userOnMaiInIndex(myUser.id ?? ""); final isSeatSwitching = previousSelfSeatIndex > -1 && previousSelfSeatIndex != targetIndex; @@ -5246,7 +5273,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { _clearSelfMicSwitchGuard(); _startSelfMicGoUpGuard(targetIndex: targetIndex); } - _clearUserFromSeats(myUser.id, exceptIndex: targetIndex); + _clearCurrentUserFromSeats(exceptIndex: targetIndex); final currentSeat = roomWheatMap[targetIndex]; final currentUser = myUser.copyWith(roles: currenRoom?.entrants?.roles); @@ -5269,6 +5296,82 @@ class RealTimeCommunicationManager extends ChangeNotifier { _syncSelfMicRuntimeState(); } + SocialChatUserProfile? _resolveSelfMicGoUpUser({ + required SocialChatUserProfile? localUser, + required SCMicGoUpRes micGoUpRes, + }) { + final responseUser = _profileFromMicGoUpUser(micGoUpRes.user); + if (localUser == null) { + return responseUser; + } + if (responseUser == null) { + return localUser.copyWith(roles: currenRoom?.entrants?.roles); + } + return localUser.copyWith( + id: _firstNonBlankText([localUser.id, responseUser.id]), + account: _firstNonBlankText([localUser.account, responseUser.account]), + userAvatar: _firstNonBlankText([ + localUser.userAvatar, + responseUser.userAvatar, + ]), + userNickname: _firstNonBlankText([ + localUser.userNickname, + responseUser.userNickname, + ]), + roles: _firstNonBlankText([ + localUser.roles, + responseUser.roles, + currenRoom?.entrants?.roles, + ]), + charmLevel: localUser.charmLevel ?? responseUser.charmLevel, + heartbeatVal: localUser.heartbeatVal ?? responseUser.heartbeatVal, + userSex: localUser.userSex ?? responseUser.userSex, + ); + } + + SocialChatUserProfile? _profileFromMicGoUpUser(User? user) { + if (user == null) { + return null; + } + final id = _firstNonBlankText([user.id]); + final account = _firstNonBlankText([user.account]); + if (id == null && account == null) { + return null; + } + return SocialChatUserProfile( + id: id, + account: account, + charmLevel: user.charmLevel, + heartbeatVal: user.heartbeatVal, + roles: _firstNonBlankText([user.roles, currenRoom?.entrants?.roles]), + userAvatar: _firstNonBlankText([user.userAvatar]), + userNickname: _firstNonBlankText([user.userNickname, user.account]), + userSex: user.userSex, + ); + } + + @visibleForTesting + void debugApplySelfMicGoUpResponse({ + SocialChatUserProfile? localUser, + required num targetIndex, + required SCMicGoUpRes micGoUpRes, + }) { + final selfUser = _resolveSelfMicGoUpUser( + localUser: localUser, + micGoUpRes: micGoUpRes, + ); + if (selfUser == null) { + return; + } + _applySelfMicGoUpState( + myUser: selfUser, + targetIndex: targetIndex, + micGoUpRes: micGoUpRes, + selfMicMuted: micGoUpRes.micMute ?? false, + ); + notifyListeners(); + } + void _openRoomUserInfoCard(String? userId) { final normalizedUserId = (userId ?? '').trim(); if (normalizedUserId.isEmpty) { @@ -5332,9 +5435,13 @@ class RealTimeCommunicationManager extends ChangeNotifier { final targetIndex = micGoUpRes.micIndex ?? index; final selfMicMuted = micGoUpRes.micMute ?? false; - if (myUser != null) { + final selfUser = _resolveSelfMicGoUpUser( + localUser: myUser, + micGoUpRes: micGoUpRes, + ); + if (selfUser != null) { _applySelfMicGoUpState( - myUser: myUser, + myUser: selfUser, targetIndex: targetIndex, micGoUpRes: micGoUpRes, selfMicMuted: selfMicMuted, @@ -5392,8 +5499,15 @@ class RealTimeCommunicationManager extends ChangeNotifier { final currentUserId = AccountStorage().getCurrentUser()?.userProfile?.id ?? ""; - final currentUserSeatIndex = userOnMaiInIndex(currentUserId); - if (roomWheatMap[index]?.user?.id == currentUserId) { + final currentUserKeys = _currentUserIdentityKeys(); + final currentUserSeatIndex = + currentUserId.trim().isNotEmpty + ? userOnMaiInIndex(currentUserId) + : (_currentUserMicSeat()?.micIndex ?? -1); + if (_userProfileMatchesAnyIdentity( + roomWheatMap[index]?.user, + currentUserKeys, + )) { isMic = true; } _clearSelfMicSwitchGuard(clearPreferredIndex: true); @@ -5406,8 +5520,9 @@ class RealTimeCommunicationManager extends ChangeNotifier { SCHeartbeatUtils.cancelAnchorTimer(); await _switchRoomRtcToAudience(); - _clearUserFromSeats(currentUserId); + _clearCurrentUserFromSeats(); _syncSelfMicRuntimeState(); + _clearConfirmedSelfMicIdentity(); notifyListeners(); requestMicrophoneListRefresh( notifyIfUnchanged: false, diff --git a/test/rtc_self_mic_snapshot_test.dart b/test/rtc_self_mic_snapshot_test.dart index b715b90..e49f2d2 100644 --- a/test/rtc_self_mic_snapshot_test.dart +++ b/test/rtc_self_mic_snapshot_test.dart @@ -3,6 +3,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:yumi/services/audio/rtc_manager.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; import 'package:yumi/shared/business_logic/models/res/mic_res.dart'; +import 'package:yumi/shared/business_logic/models/res/sc_mic_go_up_res.dart' + as mic_go_up; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; void main() { @@ -55,4 +57,30 @@ void main() { expect(rtcProvider.isOnMaiInIndex(2), isTrue); expect(rtcProvider.userOnMaiInIndex('user-1'), 2); }); + + test('self go-up uses response user when local profile is unavailable', () { + SharedPreferences.setMockInitialValues({}); + final rtcProvider = + RealTimeCommunicationManager()..roomWheatMap = {4: MicRes(micIndex: 4)}; + + rtcProvider.debugApplySelfMicGoUpResponse( + targetIndex: 4, + micGoUpRes: mic_go_up.SCMicGoUpRes( + micIndex: 4, + micMute: false, + micLock: false, + roomToken: 'token', + user: mic_go_up.User( + id: 'user-android', + account: 'android-account', + userNickname: 'Android User', + ), + ), + ); + + expect(rtcProvider.isOnMai(), isTrue); + expect(rtcProvider.isOnMaiInIndex(4), isTrue); + expect(rtcProvider.roomWheatMap[4]?.user?.id, 'user-android'); + expect(rtcProvider.roomWheatMap[4]?.user?.account, 'android-account'); + }); }