fix(app): don't wipe early-arriving peers on RoomJoined
Field-test regression from the F-06 fix (a17b930): joining a room via the
friends-list / Recents Join button sometimes landed in an empty roster even
though the peer was fully connected at the gossip layer.
Cause: `PeerJoined` (gossip event task) and `RoomJoined` (core command loop)
ride the same UI channel from different senders. The core emits `RoomJoined`
only after audio + echo-cancel setup, so `PeerJoined` for the new room
routinely arrives first. F-06 had added `reset_room_state()` to the
`RoomJoined` handler, which then cleared the peer that had already announced.
Echo cancellation widened the window and made it reliable; the roster
"self-healed" only on the peer's next periodic re-announce (`PeerUpdated`).
Fix: reset room-scoped UI state at join *initiation* (JoinPressed,
CreatePressed, JoinFriendRoom, JoinRecent) instead of on `RoomJoined`. From
Home that's a no-op (already cleared on leave), so nothing leaks, and an
early `PeerJoined` for the new room now survives. The in-call switch path
F-06 targeted is unreachable from the current UI (friends list + Recents
render only on the Home screen), so this fully covers the reachable case.
Field-verified on a 2-machine desktop<->dopedart call. 407 lib tests pass,
clippy --all-targets clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+16
-1
@@ -1006,6 +1006,10 @@ fn update(state: &mut AppState, message: AppMessage) -> Task<AppMessage> {
|
||||
let output_device = state.selected_output.as_ref().map(|d| d.name.clone());
|
||||
if !state.ticket_input.is_empty() {
|
||||
state.status_message = "Joining room...".to_string();
|
||||
// Clear any prior room's UI state now, at initiation, so an early
|
||||
// `PeerJoined` for the new room (which can beat `RoomJoined`) isn't
|
||||
// wiped. From Home this is a no-op (already cleared on leave).
|
||||
state.reset_room_state();
|
||||
// Remember this nickname for next launch.
|
||||
state.config.username = state.name.clone();
|
||||
state.config.save();
|
||||
@@ -1026,6 +1030,8 @@ fn update(state: &mut AppState, message: AppMessage) -> Task<AppMessage> {
|
||||
let input_device = state.selected_input.as_ref().map(|d| d.name.clone());
|
||||
let output_device = state.selected_output.as_ref().map(|d| d.name.clone());
|
||||
state.status_message = "Creating room...".to_string();
|
||||
// Clear any prior room's UI state at initiation (see JoinPressed).
|
||||
state.reset_room_state();
|
||||
// Remember this nickname for next launch.
|
||||
state.config.username = state.name.clone();
|
||||
state.config.save();
|
||||
@@ -1065,7 +1071,12 @@ fn update(state: &mut AppState, message: AppMessage) -> Task<AppMessage> {
|
||||
AppMessage::UiEventReceived(event) => {
|
||||
match event {
|
||||
UiEvent::RoomJoined { ticket, self_id } => {
|
||||
state.reset_room_state();
|
||||
// NB: do NOT clear peers here. `PeerJoined` rides a separate
|
||||
// channel sender (the gossip event task) and routinely arrives
|
||||
// BEFORE this `RoomJoined` (which the core emits only after audio
|
||||
// + echo-cancel setup), so clearing here would wipe a peer that
|
||||
// already announced → an empty roster. Room-scoped state is reset
|
||||
// at join *initiation* instead (see the Join* handlers).
|
||||
// Remember this gathering for one-click rejoin (W7 P5). The
|
||||
// emitted ticket is the canonical room door (topic + member
|
||||
// addr + label); push_recent de-dupes by topic and persists.
|
||||
@@ -1423,6 +1434,8 @@ fn update(state: &mut AppState, message: AppMessage) -> Task<AppMessage> {
|
||||
let input_device = state.selected_input.as_ref().map(|d| d.name.clone());
|
||||
let output_device = state.selected_output.as_ref().map(|d| d.name.clone());
|
||||
state.status_message = "Joining your friend's room...".to_string();
|
||||
// Clear any prior room's UI state at initiation (see JoinPressed).
|
||||
state.reset_room_state();
|
||||
state.config.username = state.name.clone();
|
||||
state.config.save();
|
||||
state.mic_test_active = false;
|
||||
@@ -1442,6 +1455,8 @@ fn update(state: &mut AppState, message: AppMessage) -> Task<AppMessage> {
|
||||
let input_device = state.selected_input.as_ref().map(|d| d.name.clone());
|
||||
let output_device = state.selected_output.as_ref().map(|d| d.name.clone());
|
||||
state.status_message = "Rejoining a recent room...".to_string();
|
||||
// Clear any prior room's UI state at initiation (see JoinPressed).
|
||||
state.reset_room_state();
|
||||
state.config.username = state.name.clone();
|
||||
state.config.save();
|
||||
state.mic_test_active = false;
|
||||
|
||||
Reference in New Issue
Block a user