fix(watch-together): re-host an abandoned room code instead of joining it

A room whose peers have all left is a code nobody is using, but the relay
kept it bound to the creator's reconnect capability and rejected every
other create with room_exists. The app compounded it: enterRoom only
promoted to host on room_not_found, so tapping a recent code landed the
user in the retained room as a guest of a host that was never coming
back, until the cleanup sweep finally dropped the room.

Create now replaces a room with no connected peers, and enterRoom hosts
the code when its probe join finds an empty room. An occupied room still
rejects create, including from its previous owner, and a host that is
merely disconnected still reclaims its peer ID through join with the
matching token.
This commit is contained in:
edde746
2026-07-28 03:30:32 +02:00
parent 314fec5383
commit 1be982f43d
4 changed files with 144 additions and 32 deletions
@@ -438,8 +438,8 @@ class WatchTogetherProvider with ChangeNotifier {
}
}
/// Enter a room by code — joins any reserved room and creates only when the
/// relay reports that no room reservation exists.
/// Enter a room by code — joins a room that still has someone in it and
/// hosts the code otherwise.
///
/// Returns `true` if the user became the host.
Future<bool> enterRoom(
@@ -453,13 +453,19 @@ class WatchTogetherProvider with ChangeNotifier {
final probe = _peerServiceFactory(endpoint: relayEndpoint);
var shouldBeHost = false;
try {
var probeJoined = false;
try {
await probe.joinSession(sessionId);
probeJoined = true;
} on PeerError catch (error) {
if (error.serverCode != RelayProtocol.roomNotFoundCode) rethrow;
shouldBeHost = true;
}
if (!shouldBeHost) {
// A room the relay still holds but nobody is connected to is an
// abandoned code, not a session: its declared host is gone and the
// reservation only survives until the next cleanup sweep. Take the code
// over instead of waiting on a host that is never coming back.
shouldBeHost = !probeJoined || probe.connectedPeers.isEmpty;
if (probeJoined) {
await probe.releaseSession();
}
} finally {