修复自己刚上麦成功后,会立即触发一次 /live/mic/list 刷新。如果这次拉到的是旧麦位快照,本地 roomWheatMap 会把“自己在麦上”的状态清掉,isOnMai() 变成 false,底部栏麦克风就被隐藏了。之前只保护了 MIC_CHANGE 和换麦位场景,没有保护“自己初次上麦”的旧快照覆盖
This commit is contained in:
parent
10d5e821f7
commit
15eb93c810
@ -428,6 +428,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
final Map<num, _RecentMicChangeSeatSnapshot> _recentMicChangeSeatSnapshots =
|
||||
<num, _RecentMicChangeSeatSnapshot>{};
|
||||
num? _preferredSelfMicIndex;
|
||||
num? _pendingSelfMicGoUpIndex;
|
||||
int? _pendingSelfMicGoUpGuardUntilMs;
|
||||
num? _pendingSelfMicSourceIndex;
|
||||
int? _pendingSelfMicSwitchGuardUntilMs;
|
||||
num? _pendingSelfMicReleaseIndex;
|
||||
@ -1218,6 +1220,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
required num sourceIndex,
|
||||
required num targetIndex,
|
||||
}) {
|
||||
_clearSelfMicGoUpGuard();
|
||||
_clearSelfMicReleaseGuard();
|
||||
_preferredSelfMicIndex = targetIndex;
|
||||
_pendingSelfMicSourceIndex = sourceIndex;
|
||||
@ -1225,6 +1228,22 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
DateTime.now().add(_selfMicStateGracePeriod).millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
void _startSelfMicGoUpGuard({required num targetIndex}) {
|
||||
_clearSelfMicReleaseGuard();
|
||||
_preferredSelfMicIndex = targetIndex;
|
||||
_pendingSelfMicGoUpIndex = targetIndex;
|
||||
_pendingSelfMicGoUpGuardUntilMs =
|
||||
DateTime.now().add(_selfMicStateGracePeriod).millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
void _clearSelfMicGoUpGuard({bool clearPreferredIndex = false}) {
|
||||
_pendingSelfMicGoUpIndex = null;
|
||||
_pendingSelfMicGoUpGuardUntilMs = null;
|
||||
if (clearPreferredIndex) {
|
||||
_preferredSelfMicIndex = null;
|
||||
}
|
||||
}
|
||||
|
||||
void _clearSelfMicSwitchGuard({bool clearPreferredIndex = false}) {
|
||||
_pendingSelfMicSourceIndex = null;
|
||||
_pendingSelfMicSwitchGuardUntilMs = null;
|
||||
@ -1233,6 +1252,11 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
void debugStartSelfMicGoUpGuard(num targetIndex) {
|
||||
_startSelfMicGoUpGuard(targetIndex: targetIndex);
|
||||
}
|
||||
|
||||
void _startSelfMicReleaseGuard({required num sourceIndex}) {
|
||||
_pendingSelfMicReleaseIndex = sourceIndex;
|
||||
_pendingSelfMicReleaseGuardUntilMs =
|
||||
@ -1305,9 +1329,31 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
if (targetIndex != null &&
|
||||
nextMap[targetIndex]?.user?.id == currentUserId) {
|
||||
_clearSelfMicSwitchGuard();
|
||||
_clearSelfMicGoUpGuard();
|
||||
return nextMap;
|
||||
}
|
||||
|
||||
final goUpIndex = _pendingSelfMicGoUpIndex;
|
||||
final goUpGuardUntilMs = _pendingSelfMicGoUpGuardUntilMs ?? 0;
|
||||
final hasActiveGoUpGuard = goUpIndex != null && goUpGuardUntilMs > nowMs;
|
||||
if (!hasActiveGoUpGuard) {
|
||||
_clearSelfMicGoUpGuard();
|
||||
} else if (selfSeatIndices.isEmpty ||
|
||||
selfSeatIndices.every((seatIndex) => seatIndex == goUpIndex)) {
|
||||
final stabilizedMap = _stabilizeOptimisticSelfMicSeat(
|
||||
nextMap: nextMap,
|
||||
previousMap: previousMap,
|
||||
selfSeatIndices: selfSeatIndices,
|
||||
targetIndex: goUpIndex,
|
||||
currentUserId: currentUserId,
|
||||
);
|
||||
if (stabilizedMap != null) {
|
||||
return stabilizedMap;
|
||||
}
|
||||
} else {
|
||||
_clearSelfMicGoUpGuard();
|
||||
}
|
||||
|
||||
if (!hasActiveGuard) {
|
||||
_clearSelfMicSwitchGuard();
|
||||
} else {
|
||||
@ -1320,50 +1366,15 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
(seatIndex) => seatIndex == resolvedSourceIndex,
|
||||
);
|
||||
if (selfOnlyOnSource) {
|
||||
final optimisticTargetSeat = previousMap[resolvedTargetIndex];
|
||||
if (optimisticTargetSeat != null &&
|
||||
optimisticTargetSeat.user?.id == currentUserId) {
|
||||
final incomingTargetSeat = nextMap[resolvedTargetIndex];
|
||||
if (incomingTargetSeat?.user == null ||
|
||||
incomingTargetSeat?.user?.id == currentUserId) {
|
||||
final stabilizedMap = Map<num, MicRes>.from(nextMap);
|
||||
for (final seatIndex in selfSeatIndices) {
|
||||
if (seatIndex == resolvedTargetIndex) {
|
||||
continue;
|
||||
}
|
||||
final seat = stabilizedMap[seatIndex];
|
||||
if (seat == null) {
|
||||
continue;
|
||||
}
|
||||
stabilizedMap[seatIndex] = seat.copyWith(clearUser: true);
|
||||
}
|
||||
|
||||
final baseSeat = incomingTargetSeat ?? optimisticTargetSeat;
|
||||
stabilizedMap[resolvedTargetIndex] = baseSeat.copyWith(
|
||||
user: optimisticTargetSeat.user,
|
||||
micMute:
|
||||
incomingTargetSeat?.micMute ?? optimisticTargetSeat.micMute,
|
||||
micLock:
|
||||
incomingTargetSeat?.micLock ?? optimisticTargetSeat.micLock,
|
||||
roomToken:
|
||||
incomingTargetSeat?.roomToken ??
|
||||
optimisticTargetSeat.roomToken,
|
||||
emojiPath:
|
||||
(incomingTargetSeat?.emojiPath ?? "").isNotEmpty
|
||||
? incomingTargetSeat?.emojiPath
|
||||
: optimisticTargetSeat.emojiPath,
|
||||
type:
|
||||
(incomingTargetSeat?.type ?? "").isNotEmpty
|
||||
? incomingTargetSeat?.type
|
||||
: optimisticTargetSeat.type,
|
||||
number:
|
||||
(incomingTargetSeat?.number ?? "").isNotEmpty
|
||||
? incomingTargetSeat?.number
|
||||
: optimisticTargetSeat.number,
|
||||
);
|
||||
|
||||
return stabilizedMap;
|
||||
}
|
||||
final stabilizedMap = _stabilizeOptimisticSelfMicSeat(
|
||||
nextMap: nextMap,
|
||||
previousMap: previousMap,
|
||||
selfSeatIndices: selfSeatIndices,
|
||||
targetIndex: resolvedTargetIndex,
|
||||
currentUserId: currentUserId,
|
||||
);
|
||||
if (stabilizedMap != null) {
|
||||
return stabilizedMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1397,6 +1408,60 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
return stabilizedMap;
|
||||
}
|
||||
|
||||
Map<num, MicRes>? _stabilizeOptimisticSelfMicSeat({
|
||||
required Map<num, MicRes> nextMap,
|
||||
required Map<num, MicRes> previousMap,
|
||||
required Iterable<num> selfSeatIndices,
|
||||
required num targetIndex,
|
||||
required String currentUserId,
|
||||
}) {
|
||||
final optimisticTargetSeat = previousMap[targetIndex];
|
||||
if (optimisticTargetSeat == null ||
|
||||
optimisticTargetSeat.user?.id != currentUserId) {
|
||||
return null;
|
||||
}
|
||||
final incomingTargetSeat = nextMap[targetIndex];
|
||||
if (incomingTargetSeat?.user != null &&
|
||||
incomingTargetSeat?.user?.id != currentUserId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final stabilizedMap = Map<num, MicRes>.from(nextMap);
|
||||
for (final seatIndex in selfSeatIndices) {
|
||||
if (seatIndex == targetIndex) {
|
||||
continue;
|
||||
}
|
||||
final seat = stabilizedMap[seatIndex];
|
||||
if (seat == null) {
|
||||
continue;
|
||||
}
|
||||
stabilizedMap[seatIndex] = seat.copyWith(clearUser: true);
|
||||
}
|
||||
|
||||
final baseSeat = incomingTargetSeat ?? optimisticTargetSeat;
|
||||
stabilizedMap[targetIndex] = baseSeat.copyWith(
|
||||
user: optimisticTargetSeat.user,
|
||||
micMute: incomingTargetSeat?.micMute ?? optimisticTargetSeat.micMute,
|
||||
micLock: incomingTargetSeat?.micLock ?? optimisticTargetSeat.micLock,
|
||||
roomToken:
|
||||
incomingTargetSeat?.roomToken ?? optimisticTargetSeat.roomToken,
|
||||
emojiPath:
|
||||
(incomingTargetSeat?.emojiPath ?? "").isNotEmpty
|
||||
? incomingTargetSeat?.emojiPath
|
||||
: optimisticTargetSeat.emojiPath,
|
||||
type:
|
||||
(incomingTargetSeat?.type ?? "").isNotEmpty
|
||||
? incomingTargetSeat?.type
|
||||
: optimisticTargetSeat.type,
|
||||
number:
|
||||
(incomingTargetSeat?.number ?? "").isNotEmpty
|
||||
? incomingTargetSeat?.number
|
||||
: optimisticTargetSeat.number,
|
||||
);
|
||||
|
||||
return stabilizedMap;
|
||||
}
|
||||
|
||||
bool _shouldPreferDuplicateMicSeat({
|
||||
required String userId,
|
||||
required num existingIndex,
|
||||
@ -1477,6 +1542,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
|
||||
if (currentUserMic == null) {
|
||||
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
|
||||
_roomRtcRoleIsBroadcaster = false;
|
||||
_syncRoomHeartbeatState(
|
||||
roomId: roomId,
|
||||
@ -2443,6 +2509,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
final currentSeatIndex = userOnMaiInIndex(currentUserId);
|
||||
isMic = true;
|
||||
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
|
||||
if (currentSeatIndex > -1) {
|
||||
_startSelfMicReleaseGuard(sourceIndex: currentSeatIndex);
|
||||
} else {
|
||||
@ -4966,6 +5033,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
needUpDataUserInfo = false;
|
||||
SCRoomUtils.roomUsersMap.clear();
|
||||
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
|
||||
_clearSelfMicReleaseGuard();
|
||||
_seatResizeOccupantPreserveUntilMs = null;
|
||||
roomIsMute = false;
|
||||
@ -5215,6 +5283,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
);
|
||||
} catch (downError) {}
|
||||
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
|
||||
_clearUserFromSeats(myUser?.id);
|
||||
_syncSelfMicRuntimeState();
|
||||
notifyListeners();
|
||||
@ -5237,8 +5306,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
targetIndex: targetIndex,
|
||||
);
|
||||
} else {
|
||||
_preferredSelfMicIndex = targetIndex;
|
||||
_clearSelfMicSwitchGuard();
|
||||
_startSelfMicGoUpGuard(targetIndex: targetIndex);
|
||||
}
|
||||
_clearUserFromSeats(myUser.id, exceptIndex: targetIndex);
|
||||
final currentSeat = roomWheatMap[targetIndex];
|
||||
@ -5302,6 +5371,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
isMic = true;
|
||||
}
|
||||
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
|
||||
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
|
||||
if (currentUserSeatIndex > -1) {
|
||||
_startSelfMicReleaseGuard(sourceIndex: currentUserSeatIndex);
|
||||
} else {
|
||||
|
||||
33
test/rtc_self_mic_snapshot_test.dart
Normal file
33
test/rtc_self_mic_snapshot_test.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
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/data_sources/sources/local/user_manager.dart';
|
||||
|
||||
void main() {
|
||||
test('self go-up keeps optimistic mic seat over stale mic list data', () {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
AccountStorage().setCurrentUser(
|
||||
SocialChatLoginRes(
|
||||
userProfile: SocialChatUserProfile(id: 'user-1', userNickname: 'Me'),
|
||||
),
|
||||
);
|
||||
final rtcProvider =
|
||||
RealTimeCommunicationManager()
|
||||
..roomWheatMap = {
|
||||
3: MicRes(
|
||||
micIndex: 3,
|
||||
user: SocialChatUserProfile(id: 'user-1', userNickname: 'Me'),
|
||||
),
|
||||
};
|
||||
|
||||
rtcProvider.debugStartSelfMicGoUpGuard(3);
|
||||
rtcProvider.debugApplyMicListSnapshot([
|
||||
MicRes(micIndex: 3),
|
||||
], notifyIfUnchanged: false);
|
||||
|
||||
expect(rtcProvider.roomWheatMap[3]?.user?.id, 'user-1');
|
||||
expect(rtcProvider.isOnMai(), isTrue);
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user