#![cfg(feature = "client")]
use std::sync::Arc;
use std::sync::Mutex;
use terrazzo::autoclone;
use terrazzo::envelope;
use terrazzo::prelude::XSignal;
use terrazzo::prelude::diagnostics;
use wasm_bindgen_futures::spawn_local;
use self::diagnostics::warn;
use super::schema::PortForward;
use super::sync_state::Fields;
use super::sync_state::SyncState;
use crate::api::client::remotes_api;
use crate::api::client_address::ClientAddress;
use crate::frontend::remotes::Remote;
use crate::tiles::signals::TilePtr;
#[envelope]
pub struct ManagerImpl {
tile: TilePtr,
port_forwards_signal: XSignal<Arc<Vec<PortForward>>>,
port_forwards: Mutex<Arc<Vec<PortForward>>>,
remotes: XSignal<Vec<ClientAddress>>,
}
pub use ManagerImplPtr as Manager;
impl Manager {
#[autoclone]
pub fn new(tile: TilePtr) -> Self {
let manager = Self::from(ManagerImpl {
tile,
port_forwards_signal: XSignal::new("port-forwards", Default::default()),
port_forwards: Mutex::default(),
remotes: XSignal::new("remotes", vec![]),
});
spawn_local(async move {
autoclone!(manager);
let Ok(remotes) = remotes_api::remotes().await else {
return;
};
manager.remotes.set(remotes);
});
return manager;
}
pub fn tile(&self) -> TilePtr {
self.tile.clone()
}
pub fn remotes(&self) -> XSignal<Vec<ClientAddress>> {
self.remotes.clone()
}
pub fn port_forwards(&self) -> &XSignal<Arc<Vec<PortForward>>> {
&self.port_forwards_signal
}
pub fn port_forwards_lock(&self) -> std::sync::MutexGuard<'_, Arc<Vec<PortForward>>> {
self.port_forwards.lock().expect("port_forwards lock")
}
pub fn load_port_forwards(&self, remote: Remote) {
let manager = self.clone();
spawn_local(async move {
let Ok(port_forwards) = super::state::load_port_forwards(remote).await else {
return;
};
let port_forwards: Arc<Vec<PortForward>> = Arc::from(port_forwards);
*manager.port_forwards_lock() = port_forwards.clone();
manager.port_forwards().set(port_forwards);
});
}
pub fn set(
&self,
remote: &Remote,
sync_state: XSignal<SyncState>,
id: i32,
field: Fields,
update_fn: impl FnOnce(&PortForward) -> Option<PortForward>,
) {
let mut update_fn = Some(update_fn);
self.update(remote, sync_state, field, move |port_forwards| {
port_forwards
.iter()
.filter_map(|port_forward| {
if port_forward.id == id {
let update_fn = update_fn.take().unwrap();
update_fn(port_forward)
} else {
Some(port_forward.clone())
}
})
.collect::<Vec<_>>()
.into()
});
}
#[autoclone]
pub fn update(
&self,
remote: &Remote,
sync_state: XSignal<SyncState>,
field: Fields,
update_fn: impl FnOnce(&Arc<Vec<PortForward>>) -> Arc<Vec<PortForward>>,
) {
let loading = SyncState::incr_loading(sync_state, field);
let mut port_forwards_lock = self.port_forwards_lock();
let new = update_fn(&port_forwards_lock);
*port_forwards_lock = new.clone();
drop(port_forwards_lock);
let this = self.clone();
spawn_local(async move {
autoclone!(remote);
let Ok(()) = super::state::store_port_forwards(remote.clone(), new.clone())
.await
.inspect_err(|error| warn!("Failed to save port forwards: {error}"))
else {
return;
};
let new = match super::state::load_port_forwards(remote).await {
Ok(new) => new.into(),
Err(error) => {
warn!("Failed to load port forwards: {error}");
new
}
};
this.port_forwards().set(new);
drop(loading);
})
}
}