Rewild Your Web
18
fork

Configure Feed

Select the types of activity you want to include in your feed.

pairing: persist the enabled state

Signed-off-by: webbeef <me@webbeef.org>

webbeef 4e197c72 e2a76456

+115 -67
+1 -1
patches/components/constellation/constellation.rs.patch
··· 566 566 + } 567 567 + }, 568 568 + ScriptToConstellationMessage::PairingStart(callback) => { 569 - + self.pairing.start(callback); 569 + + self.pairing.start(Some(callback)); 570 570 + }, 571 571 + ScriptToConstellationMessage::PairingStop(callback) => { 572 572 + self.pairing.stop(callback);
+101 -65
patches/components/constellation/pairing.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -0,0 +1,871 @@ 3 + @@ -0,0 +1,907 @@ 4 4 +// SPDX-License-Identifier: AGPL-3.0-or-later 5 5 + 6 6 +//! P2P pairing service integration with the constellation. ··· 113 113 + /// Peers that have confirmed bidirectional communication 114 114 + /// (we received at least one message from them). 115 115 + confirmed_peers: HashSet<String>, 116 + + config: Arc<Mutex<P2pConfig>>, 117 + + config_dir: Option<PathBuf>, 116 118 +} 117 119 + 118 120 +impl PairingService { 119 121 + pub(crate) fn new() -> Self { 120 - + Self { 122 + + let config_dir = servo_config::opts::get().config_dir.clone(); 123 + + let config = net::async_runtime::spawn_blocking_task::<_, P2pConfig>(async { 124 + + P2pConfig::load(config_dir.as_ref()).await 125 + + }); 126 + + let should_start = config.enabled; 127 + + let mut instance = Self { 121 128 + manager: Default::default(), 122 129 + local_info: Default::default(), 123 130 + event_receiver: None, 124 131 + remote_channels: Default::default(), 125 132 + confirmed_peers: Default::default(), 133 + + config: Arc::new(Mutex::new(config)), 134 + + config_dir, 135 + + }; 136 + + 137 + + if should_start { 138 + + instance.start(None); 126 139 + } 140 + + 141 + + instance 127 142 + } 128 143 + 129 144 + /// Get the local device name synchronously (best effort). ··· 151 166 + /// Start the pairing service. Creates the PairingManager on the tokio runtime 152 167 + /// and sets up a bridge task to forward PeerEvents to a crossbeam channel. 153 168 + /// The secret key and display name are persisted to the config directory. 154 - + pub(crate) fn start(&mut self, callback: GenericCallback<Result<(), String>>) { 169 + + pub(crate) fn start(&mut self, callback: Option<GenericCallback<Result<(), String>>>) { 155 170 + let manager = self.manager.clone(); 156 171 + let local_info = self.local_info.clone(); 157 172 + let (cb_sender, cb_receiver) = crossbeam_channel::unbounded(); 158 173 + self.event_receiver = Some(cb_receiver); 159 174 + 175 + + let config = Arc::clone(&self.config); 176 + + let config_dir = self.config_dir.clone(); 160 177 + net::async_runtime::spawn_task(async move { 161 178 + let mut guard = manager.lock().await; 162 179 + if guard.is_some() { 163 - + let _ = callback.send(Ok(())); 180 + + if let Some(callback) = callback { 181 + + let _ = callback.send(Ok(())); 182 + + } 164 183 + return; 165 184 + } 166 185 + 167 - + let config_dir = servo_config::opts::get().config_dir.clone(); 168 - + 169 186 + // Load or generate the persistent secret key. 170 187 + let key = load_or_create_key(config_dir.as_ref()).await; 171 188 + 172 189 + // Load config (display name + paired peers). 173 - + let mut config = load_config(config_dir.as_ref()).await; 174 - + if config.name.is_empty() { 175 - + config.name = generate_name(); 176 - + save_config(config_dir.as_ref(), &config).await; 177 - + } 178 - + let name = config.name.clone(); 190 + + let name = { 191 + + let mut config = config.lock().await; 192 + + if config.name.is_empty() { 193 + + config.name = generate_name(); 194 + + config.save(config_dir.as_ref()).await; 195 + + } 196 + + config.enabled = true; 197 + + config.save(config_dir.as_ref()).await; 198 + + config.name.clone() 199 + + }; 179 200 + info!("Pairing service starting as \"{name}\""); 180 201 + 181 202 + // Store local endpoint info (id is derived from the secret key). ··· 188 209 + let new_manager = PairingManager::create(event_sender, &name, key).await; 189 210 + 190 211 + // Load and register previously paired peers. 191 - + for peer in &config.paired_peers { 212 + + for peer in &config.lock().await.paired_peers { 192 213 + if let Ok(endpoint_id) = peer.id.parse() { 193 214 + new_manager 194 215 + .register_paired_peer(&endpoint_id, &peer.name) ··· 212 233 + } 213 234 + }); 214 235 + 215 - + let _ = callback.send(Ok(())); 236 + + if let Some(callback) = callback { 237 + + let _ = callback.send(Ok(())); 238 + + } 216 239 + }); 217 240 + } 218 241 + ··· 225 248 + 226 249 + let manager = self.manager.clone(); 227 250 + let local_info = self.local_info.clone(); 251 + + let config = Arc::clone(&self.config); 252 + + let config_dir = self.config_dir.clone(); 228 253 + net::async_runtime::spawn_task(async move { 229 254 + *local_info.lock().await = None; 230 255 + let mut guard = manager.lock().await; 231 256 + if let Some(mut mgr) = guard.take() { 232 257 + mgr.stop().await; 233 258 + } 259 + + let mut config = config.lock().await; 260 + + config.enabled = false; 261 + + config.save(config_dir.as_ref()).await; 234 262 + let _ = callback.send(Ok(())); 235 263 + }); 236 264 + } ··· 285 313 + pub(crate) fn set_name(&self, name: &str, callback: GenericCallback<Result<(), String>>) { 286 314 + let local_info = self.local_info.clone(); 287 315 + let name = name.to_owned(); 316 + + let config = Arc::clone(&self.config); 317 + + let config_dir = self.config_dir.clone(); 288 318 + net::async_runtime::spawn_task(async move { 289 - + let config_dir = servo_config::opts::get().config_dir.clone(); 290 - + let mut config = load_config(config_dir.as_ref()).await; 319 + + let mut config = config.lock().await; 291 320 + config.name = name.clone(); 292 - + save_config(config_dir.as_ref(), &config).await; 321 + + config.save(config_dir.as_ref()).await; 293 322 + 294 323 + // Update the cached local info if the service is running. 295 324 + if let Some(info) = local_info.lock().await.as_mut() { ··· 315 344 + 316 345 + let id = id.to_owned(); 317 346 + let manager = self.manager.clone(); 347 + + let config = Arc::clone(&self.config); 348 + + let config_dir = self.config_dir.clone(); 318 349 + net::async_runtime::spawn_task(async move { 319 350 + let mgr = { 320 351 + let guard = manager.lock().await; ··· 336 367 + .find(|p| p.id == endpoint_id) 337 368 + .map(|p| p.name) 338 369 + .unwrap_or_default(); 339 - + let config_dir = servo_config::opts::get().config_dir.clone(); 340 - + let mut config = load_config(config_dir.as_ref()).await; 341 - + add_paired_peer(&mut config, &id, &peer_name); 342 - + save_config(config_dir.as_ref(), &config).await; 370 + + let mut config = config.lock().await; 371 + + config.add_paired_peer(&id, &peer_name); 372 + + config.save(config_dir.as_ref()).await; 343 373 + let _ = callback.send(Ok(true)); 344 374 + }, 345 375 + Ok(beaver_p2p::PairingResult::Rejected) => { ··· 363 393 + }; 364 394 + let manager = self.manager.clone(); 365 395 + let id = id.to_owned(); 396 + + let config = Arc::clone(&self.config); 397 + + let config_dir = self.config_dir.clone(); 366 398 + net::async_runtime::spawn_task(async move { 367 399 + let mgr = { 368 400 + let guard = manager.lock().await; ··· 384 416 + .find(|p| p.id == endpoint_id) 385 417 + .map(|p| p.name) 386 418 + .unwrap_or_default(); 387 - + let config_dir = servo_config::opts::get().config_dir.clone(); 388 - + let mut config = load_config(config_dir.as_ref()).await; 389 - + add_paired_peer(&mut config, &id, &peer_name); 390 - + save_config(config_dir.as_ref(), &config).await; 419 + + let mut config = config.lock().await; 420 + + config.add_paired_peer(&id, &peer_name); 421 + + config.save(config_dir.as_ref()).await; 391 422 + let _ = callback.send(Ok(())); 392 423 + }, 393 424 + Err(e) => { ··· 441 472 + let id = id.to_owned(); 442 473 + let manager = self.manager.clone(); 443 474 + let remote_channels = self.remote_channels.clone(); 475 + + let config = Arc::clone(&self.config); 476 + + let config_dir = self.config_dir.clone(); 444 477 + net::async_runtime::spawn_task(async move { 445 478 + // Remove from beaver_p2p runtime state. 446 479 + { ··· 451 484 + } 452 485 + 453 486 + // Remove from persisted config. 454 - + let config_dir = servo_config::opts::get().config_dir.clone(); 455 - + let mut config = load_config(config_dir.as_ref()).await; 487 + + let mut config = config.lock().await; 456 488 + config.paired_peers.retain(|p| p.id != id); 457 - + save_config(config_dir.as_ref(), &config).await; 489 + + config.save(config_dir.as_ref()).await; 458 490 + 459 491 + // Clean up remote channel state. 460 492 + remote_channels.lock().await.remove(&id); ··· 774 806 + /// The list of paired peers. 775 807 + #[serde(default)] 776 808 + paired_peers: Vec<PairedPeer>, 809 + + #[serde(default)] 810 + + enabled: bool, 777 811 +} 778 812 + 779 813 +/// A persisted paired peer entry. ··· 783 817 + name: String, 784 818 +} 785 819 + 786 - +fn config_path(config_dir: Option<&PathBuf>) -> Option<PathBuf> { 787 - + config_dir.map(|dir| dir.join("p2p-config.json")) 788 - +} 789 - + 790 - +async fn load_config(config_dir: Option<&PathBuf>) -> P2pConfig { 791 - + let Some(path) = config_path(config_dir) else { 792 - + return P2pConfig::default(); 793 - + }; 794 - + match tokio::fs::read_to_string(&path).await { 795 - + Ok(content) => serde_json::from_str(&content).unwrap_or_default(), 796 - + Err(_) => P2pConfig::default(), 820 + +impl P2pConfig { 821 + + fn config_path(config_dir: Option<&PathBuf>) -> Option<PathBuf> { 822 + + config_dir.map(|dir| dir.join("p2p-config.json")) 797 823 + } 798 - +} 799 824 + 800 - +async fn save_config(config_dir: Option<&PathBuf>, config: &P2pConfig) { 801 - + let Some(path) = config_path(config_dir) else { 802 - + return; 803 - + }; 804 - + if let Some(dir) = config_dir { 805 - + if let Err(e) = tokio::fs::create_dir_all(dir).await { 806 - + warn!("Failed to create config dir: {e}"); 807 - + return; 825 + + async fn load(config_dir: Option<&PathBuf>) -> P2pConfig { 826 + + let Some(path) = Self::config_path(config_dir) else { 827 + + return P2pConfig::default(); 828 + + }; 829 + + match tokio::fs::read_to_string(&path).await { 830 + + Ok(content) => serde_json::from_str(&content).unwrap_or_default(), 831 + + Err(_) => P2pConfig::default(), 808 832 + } 809 833 + } 810 - + match serde_json::to_string_pretty(config) { 811 - + Ok(json) => { 812 - + if let Err(e) = tokio::fs::write(&path, json).await { 813 - + warn!("Failed to write p2p config: {e}"); 834 + + 835 + + async fn save(&self, config_dir: Option<&PathBuf>) { 836 + + let Some(path) = Self::config_path(config_dir) else { 837 + + return; 838 + + }; 839 + + if let Some(dir) = config_dir { 840 + + if let Err(e) = tokio::fs::create_dir_all(dir).await { 841 + + warn!("Failed to create config dir: {e}"); 842 + + return; 814 843 + } 815 - + }, 816 - + Err(e) => warn!("Failed to serialize p2p config: {e}"), 844 + + } 845 + + match serde_json::to_string_pretty(self) { 846 + + Ok(json) => { 847 + + if let Err(e) = tokio::fs::write(&path, json).await { 848 + + warn!("Failed to write p2p config: {e}"); 849 + + } 850 + + }, 851 + + Err(e) => warn!("Failed to serialize p2p config: {e}"), 852 + + } 817 853 + } 818 - +} 819 854 + 820 - +/// Add a paired peer to the config (deduplicating by id). 821 - +fn add_paired_peer(config: &mut P2pConfig, id: &str, name: &str) { 822 - + if let Some(existing) = config.paired_peers.iter_mut().find(|p| p.id == id) { 823 - + if !name.is_empty() { 824 - + existing.name = name.to_owned(); 855 + + /// Add a paired peer to the config (deduplicating by id). 856 + + fn add_paired_peer(&mut self, id: &str, name: &str) { 857 + + if let Some(existing) = self.paired_peers.iter_mut().find(|p| p.id == id) { 858 + + if !name.is_empty() { 859 + + existing.name = name.to_owned(); 860 + + } 861 + + } else { 862 + + self.paired_peers.push(PairedPeer { 863 + + id: id.to_owned(), 864 + + name: name.to_owned(), 865 + + }); 825 866 + } 826 - + } else { 827 - + config.paired_peers.push(PairedPeer { 828 - + id: id.to_owned(), 829 - + name: name.to_owned(), 830 - + }); 831 867 + } 832 868 +} 833 869 +
+13 -1
ui/settings/index.js
··· 245 245 ); 246 246 247 247 // P2P Pairing settings 248 - function setupP2P() { 248 + async function setupP2P() { 249 249 const toggle = document.getElementById("p2p-toggle"); 250 250 const localInfo = document.getElementById("p2p-local-info"); 251 251 const peersSection = document.getElementById("p2p-peers"); ··· 343 343 } catch (e) { 344 344 console.error("Failed to get peers:", e); 345 345 } 346 + } 347 + 348 + // Update the initial state based on the availability of the 349 + try { 350 + await pairing.local(); 351 + toggle.checked = true; 352 + await updateLocalInfo(); 353 + await refreshPeers(); 354 + setRunning(true); 355 + } catch (e) { 356 + toggle.checked = false; 357 + setRunning(false); 346 358 } 347 359 348 360 function setRunning(running) {