@@ -22,7 +22,7 @@ use tokio::net::UdpSocket;
2222use tokio:: sync:: RwLock ;
2323use tokio:: task:: JoinHandle ;
2424use tokio:: time:: { self , Duration } ;
25- use tracing:: info;
25+ use tracing:: { info, warn } ;
2626
2727use crate :: dto:: {
2828 CommunityBrowseView , CommunityParticipantView , CommunityView , CreateCommunityResult ,
@@ -49,6 +49,10 @@ struct RuntimeState {
4949 dht_republish_task : Option < JoinHandle < ( ) > > ,
5050 subscription_sync_task : Option < JoinHandle < ( ) > > ,
5151 last_public_shares : Vec < PublicShareView > ,
52+ /// Transport used for DHT replication (same instance as background loops).
53+ dht_transport : Option < Arc < dyn scp2p_core:: RequestTransport > > ,
54+ /// Bootstrap peers used for DHT replication.
55+ dht_bootstrap_peers : Vec < PeerAddr > ,
5256}
5357
5458const LAN_DISCOVERY_PORT : u16 = 46123 ;
@@ -188,10 +192,12 @@ impl DesktopAppState {
188192 DHT_LOOP_INTERVAL ,
189193 ) ) ;
190194 state. subscription_sync_task = Some ( handle. clone ( ) . start_subscription_sync_loop (
191- transport,
192- bootstrap,
195+ transport. clone ( ) ,
196+ bootstrap. clone ( ) ,
193197 DHT_LOOP_INTERVAL ,
194198 ) ) ;
199+ state. dht_transport = Some ( transport) ;
200+ state. dht_bootstrap_peers = bootstrap;
195201 }
196202
197203 state. node = Some ( handle) ;
@@ -711,6 +717,26 @@ impl DesktopAppState {
711717 . context ( "node is not running" )
712718 }
713719
720+ /// Fire-and-forget: run one DHT republish cycle so newly published
721+ /// share heads + manifests reach the relay immediately instead of
722+ /// waiting for the next background interval.
723+ async fn trigger_dht_republish ( & self ) {
724+ let ( node, transport, peers) = {
725+ let state = self . inner . read ( ) . await ;
726+ match ( & state. node , & state. dht_transport ) {
727+ ( Some ( n) , Some ( t) ) => {
728+ ( n. clone ( ) , t. clone ( ) , state. dht_bootstrap_peers . clone ( ) )
729+ }
730+ _ => return ,
731+ }
732+ } ;
733+ tokio:: spawn ( async move {
734+ if let Err ( e) = node. dht_republish_once ( transport. as_ref ( ) , & peers) . await {
735+ warn ! ( error = %e, "immediate DHT republish after publish failed" ) ;
736+ }
737+ } ) ;
738+ }
739+
714740 async fn sync_peer_targets ( & self , node : & NodeHandle ) -> anyhow:: Result < Vec < PeerAddr > > {
715741 let mut peers = node. configured_bootstrap_peers ( ) . await ?;
716742 for record in node. peer_records ( ) . await {
@@ -802,6 +828,11 @@ impl DesktopAppState {
802828 )
803829 . await ?;
804830
831+ // Immediately replicate share head + manifest to DHT peers so
832+ // subscribers can discover the share without waiting for the
833+ // next background republish cycle.
834+ self . trigger_dht_republish ( ) . await ;
835+
805836 Ok ( PublishResultView {
806837 share_id_hex : hex:: encode ( share. share_id ( ) . 0 ) ,
807838 share_pubkey_hex : hex:: encode ( share. verifying_key ( ) . to_bytes ( ) ) ,
@@ -858,6 +889,9 @@ impl DesktopAppState {
858889 )
859890 . await ?;
860891
892+ // Immediately replicate share head + manifest to DHT peers.
893+ self . trigger_dht_republish ( ) . await ;
894+
861895 Ok ( PublishResultView {
862896 share_id_hex : hex:: encode ( share. share_id ( ) . 0 ) ,
863897 share_pubkey_hex : hex:: encode ( share. verifying_key ( ) . to_bytes ( ) ) ,
0 commit comments