antenna-client-web 0.1.1

Web-over-WASM platform implementation for the antenna P2P mesh protocol.
Documentation
use std::{cell::RefCell, collections::HashSet, rc::Rc};

use crate::{Driver, JsEventCallback, Storage};
use antenna_client_shared::{Event, IceServerConfig, RtcCallbacks, STORAGE_IDENTITY_KEY};
use antenna_protocol::{PeerID, UserMsgPayload};
use anyhow::Result;
use wasm_bindgen::closure::Closure;

/// User-facing handle to a mesh peer running in the browser.
pub struct Peer<Msg>
where
    Msg: UserMsgPayload + 'static,
{
    driver: Rc<RefCell<Driver<Msg>>>,
    callbacks: Rc<RefCell<RtcCallbacks<Msg>>>,
    _callback_buffer: Vec<JsEventCallback>,
}

impl<Msg> Drop for Peer<Msg>
where
    Msg: UserMsgPayload + 'static,
{
    fn drop(&mut self) {
        self.leave();
    }
}

impl<Msg> Default for Peer<Msg>
where
    Msg: UserMsgPayload + 'static,
{
    fn default() -> Self {
        Self::new()
    }
}

impl<Msg> Peer<Msg>
where
    Msg: UserMsgPayload + 'static,
{
    pub fn new() -> Self {
        Self::with_ice_servers(IceServerConfig::default_stun())
    }

    pub fn with_ice_servers(ice_servers: Vec<IceServerConfig>) -> Self {
        Self::with_storage(ice_servers, Storage::new(STORAGE_IDENTITY_KEY))
    }

    pub fn with_storage(ice_servers: Vec<IceServerConfig>, storage: Storage) -> Self {
        let callbacks = Rc::new(RefCell::new(RtcCallbacks::new()));
        let driver = Rc::new(RefCell::new(Driver::new(
            ice_servers,
            callbacks.clone(),
            storage,
        )));

        let window = web_sys::window().expect("no global window");
        let cb = Closure::<dyn FnMut()>::new({
            let driver = driver.clone();
            move || {
                Driver::leave(driver.clone());
            }
        });
        let _callback_buffer = vec![JsEventCallback::new(window.into(), "beforeunload", cb)];

        Self {
            driver,
            callbacks,
            _callback_buffer,
        }
    }

    pub fn my_id(&self) -> PeerID {
        *self.driver.borrow().id()
    }

    pub fn subscribe(&self, subscription: Event<Msg>) -> u64 {
        self.callbacks.borrow_mut().subscribe(subscription)
    }

    pub fn unsubscribe(&self, id: u64) -> bool {
        self.callbacks.borrow_mut().unsubscribe(id)
    }

    pub async fn start(&self) -> Result<String> {
        Driver::start(self.driver.clone()).await
    }

    pub async fn receive_offer(&self, offer: &str) -> Result<String> {
        Driver::receive_offer(self.driver.clone(), offer).await
    }

    pub async fn receive_answer(&self, answer: &str) -> Result<()> {
        Driver::receive_answer(self.driver.clone(), answer).await
    }

    pub fn send(&self, peer_id: PeerID, data: Msg) {
        Driver::send(self.driver.clone(), peer_id, data);
    }

    pub fn broadcast(&self, data: Msg) {
        Driver::broadcast(self.driver.clone(), data);
    }

    pub fn leave(&self) {
        Driver::leave(self.driver.clone());
    }

    pub fn is_connected(&self, peer_id: PeerID) -> bool {
        self.driver.borrow().is_connected(&peer_id)
    }

    pub fn connected_peers(&self) -> HashSet<String> {
        self.driver
            .borrow()
            .connected_peers()
            .iter()
            .map(|p| p.to_string())
            .collect()
    }
}