@@ -894,7 +894,7 @@ class WatchTogetherSyncManager {
|
||||
onSessionConfigReceived?.call(message.controlMode!);
|
||||
}
|
||||
|
||||
await _runGuardedRemoteAction(
|
||||
final applied = await _runGuardedRemoteAction(
|
||||
actionName: 'session config',
|
||||
expectedAttachmentGeneration: expectedAttachmentGeneration,
|
||||
action: (player, attachmentGeneration) async {
|
||||
@@ -956,6 +956,10 @@ class WatchTogetherSyncManager {
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
if (applied) {
|
||||
_reannounceReady(reason: 'session config');
|
||||
}
|
||||
}
|
||||
|
||||
/// Set syncing state and notify listeners
|
||||
@@ -984,6 +988,14 @@ class WatchTogetherSyncManager {
|
||||
_backgrounded = value;
|
||||
}
|
||||
|
||||
void _reannounceReady({required String reason}) {
|
||||
if (_hasAnnouncedReady && _peerService.myPeerId != null) {
|
||||
_peerReady[_peerService.myPeerId!] = true;
|
||||
_peerService.broadcast(SyncMessage.playerReady(peerId: _peerService.myPeerId!, ready: true));
|
||||
appLogger.d('WatchTogether: Re-announced player ready after $reason');
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-announce player readiness after reconnect.
|
||||
///
|
||||
/// During reconnect the host resets our _peerReady entry to false via
|
||||
@@ -991,11 +1003,7 @@ class WatchTogetherSyncManager {
|
||||
/// reset because the player stays attached). Re-broadcast so the host
|
||||
/// doesn't stay stuck in the deferred-play gate.
|
||||
void reannounceReadyIfNeeded() {
|
||||
if (_hasAnnouncedReady && _peerService.myPeerId != null) {
|
||||
_peerReady[_peerService.myPeerId!] = true;
|
||||
_peerService.broadcast(SyncMessage.playerReady(peerId: _peerService.myPeerId!, ready: true));
|
||||
appLogger.d('WatchTogether: Re-announced player ready after reconnect');
|
||||
}
|
||||
_reannounceReady(reason: 'reconnect');
|
||||
}
|
||||
|
||||
/// Send join announcement to all peers
|
||||
|
||||
@@ -58,6 +58,67 @@ void main() {
|
||||
await peerService.close();
|
||||
});
|
||||
|
||||
test('ready guest re-announces readiness after receiving session config', () async {
|
||||
final peerService = _FakeWatchTogetherPeerService(peerId: 'guest');
|
||||
final player = _FakePlayer(playing: false, position: const Duration(seconds: 10));
|
||||
final manager = _guestManager(peerService, controlMode: ControlMode.anyone);
|
||||
|
||||
manager.initializeParticipants(['guest', 'host']);
|
||||
manager.attachPlayer(player);
|
||||
peerService.broadcasts.clear();
|
||||
|
||||
peerService.emit(
|
||||
SyncMessage.sessionConfig(
|
||||
controlMode: ControlMode.anyone,
|
||||
currentPosition: const Duration(seconds: 20),
|
||||
isPlaying: false,
|
||||
playbackRate: 1.0,
|
||||
peerId: 'host',
|
||||
),
|
||||
);
|
||||
await _settle();
|
||||
|
||||
expect(
|
||||
peerService.broadcasts.where(
|
||||
(message) =>
|
||||
message.type == SyncMessageType.playerReady &&
|
||||
message.peerId == 'guest' &&
|
||||
message.bufferingState == true,
|
||||
),
|
||||
isNotEmpty,
|
||||
);
|
||||
expect(player.state.position, const Duration(seconds: 20));
|
||||
expect(player.state.playing, isFalse);
|
||||
|
||||
manager.dispose();
|
||||
await player.dispose();
|
||||
await peerService.close();
|
||||
});
|
||||
|
||||
test('host local play is not deferred after guest readiness is restored', () async {
|
||||
final peerService = _FakeWatchTogetherPeerService(peerId: 'host');
|
||||
final player = _FakePlayer(playing: false, position: const Duration(minutes: 3));
|
||||
final manager = _hostManager(peerService);
|
||||
final deferredStates = <bool>[];
|
||||
manager.onDeferredPlayChanged = deferredStates.add;
|
||||
|
||||
manager.initializeParticipants(['host', 'guest']);
|
||||
manager.attachPlayer(player);
|
||||
peerService.emit(SyncMessage.playerReady(peerId: 'guest', ready: true));
|
||||
await _settle();
|
||||
peerService.broadcasts.clear();
|
||||
|
||||
await player.emitPlaying(true);
|
||||
|
||||
expect(deferredStates, isNot(contains(true)));
|
||||
expect(player.state.playing, isTrue);
|
||||
expect(peerService.broadcasts.where((m) => m.type == SyncMessageType.play), isNotEmpty);
|
||||
|
||||
manager.dispose();
|
||||
await player.dispose();
|
||||
await peerService.close();
|
||||
});
|
||||
|
||||
test('removing a disconnected not-ready peer resumes deferred play', () async {
|
||||
final peerService = _FakeWatchTogetherPeerService(peerId: 'host');
|
||||
final player = _FakePlayer(playing: false, position: const Duration(minutes: 5));
|
||||
|
||||
Reference in New Issue
Block a user