Something went wrong. Try again.
A DnD character builder built on top of Iroh for peer to peer connections! sheets.renatillas.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144use std::{ collections::BTreeSet, sync::{Arc, Mutex},};
use anyhow::Result;use chat_shared::{ChatSender, ChatTicket, EndpointId, TopicId};use n0_future::StreamExt;use wasm_bindgen::{JsError, JsValue, prelude::wasm_bindgen};use wasm_streams::ReadableStream;
#[wasm_bindgen(start)]fn start() { console_error_panic_hook::set_once(); tracing_subscriber::fmt() .with_writer(tracing_subscriber_wasm::MakeConsoleWriter::default()) .without_time() .with_ansi(false) .init();}
#[wasm_bindgen]pub struct SheetNode(chat_shared::ChatNode);
#[wasm_bindgen]impl SheetNode { pub async fn spawn() -> Result<Self, JsError> { let node = chat_shared::ChatNode::spawn(None) .await .map_err(to_js_error)?; Ok(Self(node)) }
pub async fn create(&self, nickname: String) -> Result<SheetChannel, JsError> { self.join_inner(ChatTicket::new_random(), nickname).await }
pub async fn join(&self, ticket: String, nickname: String) -> Result<SheetChannel, JsError> { let ticket = ChatTicket::deserialize(&ticket).map_err(to_js_error)?; self.join_inner(ticket, nickname).await }
pub async fn shutdown(&self) { self.0.shutdown().await; } async fn join_inner( &self, mut ticket: ChatTicket, nickname: String, ) -> Result<SheetChannel, JsError> { let (sender, receiver) = self.0.join(&ticket, nickname).await.map_err(to_js_error)?; let neighbors = Arc::new(Mutex::new(BTreeSet::new())); let tracked_neighbors = neighbors.clone(); let receiver = receiver.map(move |event| { if let Ok(event) = &event { match event { chat_shared::Event::Joined { neighbors } => { tracked_neighbors .lock() .unwrap() .extend(neighbors.iter().copied()); } chat_shared::Event::NeighborUp { endpoint_id } => { tracked_neighbors.lock().unwrap().insert(*endpoint_id); } chat_shared::Event::NeighborDown { endpoint_id } => { tracked_neighbors.lock().unwrap().remove(endpoint_id); } _ => {} } } event .map_err(|error| JsValue::from_str(&error.to_string())) .map(|event| serde_wasm_bindgen::to_value(&event).unwrap()) }); let endpoint_id = self.0.endpoint_id(); ticket.bootstrap.insert(endpoint_id);
Ok(SheetChannel { topic_id: ticket.topic_id, endpoint_id, bootstrap: ticket.bootstrap, neighbors, sender: ChannelSender(sender), receiver: ReadableStream::from_stream(receiver).into_raw(), }) }}
type ChannelReceiver = wasm_streams::readable::sys::ReadableStream;
#[wasm_bindgen]pub struct SheetChannel { topic_id: TopicId, endpoint_id: EndpointId, bootstrap: BTreeSet<EndpointId>, neighbors: Arc<Mutex<BTreeSet<EndpointId>>>, sender: ChannelSender, receiver: ChannelReceiver,}
#[wasm_bindgen]impl SheetChannel { #[wasm_bindgen(getter)] pub fn sender(&self) -> ChannelSender { self.sender.clone() }
#[wasm_bindgen(getter)] pub fn receiver(&self) -> ChannelReceiver { self.receiver.clone() }
pub fn ticket(&self) -> String { let mut ticket = ChatTicket::new(self.topic_id); ticket.bootstrap.extend(self.bootstrap.iter().copied()); ticket .bootstrap .extend(self.neighbors.lock().unwrap().iter().copied()); ticket.bootstrap.insert(self.endpoint_id); ticket.serialize() }
pub fn peer_count(&self) -> usize { self.neighbors.lock().unwrap().len() }}
#[wasm_bindgen]#[derive(Clone)]pub struct ChannelSender(ChatSender);
#[wasm_bindgen]impl ChannelSender { pub async fn broadcast(&self, payload: String) -> Result<(), JsError> { self.0.send(payload).await.map_err(to_js_error) }}
fn to_js_error(error: impl Into<anyhow::Error>) -> JsError { let error: anyhow::Error = error.into(); JsError::new(&error.to_string())}