diff --git a/core/archipelago/src/api/rpc/federation/handlers.rs b/core/archipelago/src/api/rpc/federation/handlers.rs index 90d38102..d80690f0 100644 --- a/core/archipelago/src/api/rpc/federation/handlers.rs +++ b/core/archipelago/src/api/rpc/federation/handlers.rs @@ -30,6 +30,25 @@ impl RpcHandler { mesh::upsert_federation_peer(&svc.shared_state(), pubkey_hex, did, name).await; } } + + /// Re-seed every federation node from disk into the mesh peer table so the + /// chat list reflects what the latest federation sync learned — display + /// names (landed in `nodes.json` by `update_node_state` when a peer + /// announces its name) and transitively-discovered peers (merged by + /// `merge_transitive_peers`) — WITHOUT waiting for a mesh restart. + /// + /// Without this, a peer accepted via invite (seeded with `name = None`) + /// stays "Archipelago " in chat until the next restart even after + /// sync has learned its real name, and transitive peers never appear as + /// chat contacts at all. `seed_federation_peers_into_mesh` is idempotent + /// and dedups by onion, so calling it after each sync is safe. + /// Best-effort: silently no-ops when mesh is off. + pub(crate) async fn refresh_federation_mesh_peers(&self) { + let svc = self.mesh_service.read().await; + if let Some(svc) = svc.as_ref() { + mesh::seed_federation_peers_into_mesh(&svc.shared_state(), &self.config.data_dir).await; + } + } } impl RpcHandler { @@ -341,6 +360,10 @@ impl RpcHandler { } } + // Push any names/roster the sync just learned into the live mesh peer + // table so the chat list updates without a restart (#42). + self.refresh_federation_mesh_peers().await; + Ok(serde_json::json!({ "synced": synced, "failed": failed, diff --git a/core/archipelago/src/server.rs b/core/archipelago/src/server.rs index 113eb891..9978be03 100644 --- a/core/archipelago/src/server.rs +++ b/core/archipelago/src/server.rs @@ -508,6 +508,7 @@ impl Server { { let data_dir = config.data_dir.clone(); let state = state_manager.clone(); + let rpc = api_handler.rpc_handler().clone(); tokio::spawn(async move { // First run 60s after boot to let onboarding settle. tokio::time::sleep(Duration::from_secs(60)).await; @@ -558,6 +559,10 @@ impl Server { } tokio::time::sleep(Duration::from_secs(5)).await; } + // After syncing every peer, push the names/roster just + // learned (into nodes.json) into the live mesh peer table + // so chat contacts refresh without a restart (#42). + rpc.refresh_federation_mesh_peers().await; } }); }