diff --git a/Cargo.lock b/Cargo.lock index 062cfc6..d29a8b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1446,9 +1446,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -1748,6 +1748,7 @@ dependencies = [ "mumble-protocol-2x", "ogg", "once_cell", + "ordermap", "serde-wasm-bindgen 0.6.5", "serde_json", "tokio-util", @@ -1870,6 +1871,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordermap" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f2bd7b03bf2c767e1bb7b91505dbe022833776e60480275e6f2fb0db0c7503" +dependencies = [ + "indexmap", +] + [[package]] name = "overload" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 08f0843..1c76ee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ async-std = "1.12.0" anyhow = "1.0.86" byteorder = "1.5.0" ogg = "0.9.1" +ordermap = "0.5.3" diff --git a/src/app.rs b/src/app.rs index 577330f..a3e7c47 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,19 +1,87 @@ #![allow(non_snake_case)] +use std::collections::{BTreeMap, BTreeSet, HashMap}; + use dioxus::prelude::*; +use ordermap::OrderSet; use super::Command; use super::Command::*; use super::ConnectionState::{self, *}; +pub type ChannelId = u32; +pub type UserId = u32; + +#[derive(Default)] +pub struct ChannelState { + pub name: String, + pub children: OrderSet, + pub users: OrderSet, + pub parent: Option, +} + +#[derive(Default)] +pub struct UserState { + pub name: String, + pub channel: ChannelId, +} + +#[derive(Default)] +pub struct ServerState { + pub channels: HashMap, + pub users: HashMap, +} + pub struct State { pub status: GlobalSignal, + pub server: GlobalSignal, } pub static STATE: State = State { status: Signal::global(|| Disconnected), + server: Signal::global(|| Default::default()), }; +#[component] +pub fn User(id: UserId) -> Element { + let server = STATE.server.read(); + let state = server.users.get(&id)?; + rsx!( + span { + style: "border: solid black 1px; border-radius: 4px;", + "{state.name}" + } + ) +} + +#[component] +pub fn Channel(id: ChannelId) -> Element { + let server = STATE.server.read(); + let state = server.channels.get(&id)?; + rsx!( + "{state.name}" + div { + style: "border-left: solid black 1px; padding-left: 8px;", + for child in state.children.iter() { + Channel { id: *child } + } + for id in state.users.iter() { + User { id: *id } + } + } + ) +} + +#[component] +pub fn ServerView() -> Element { + let server = STATE.server.read(); + rsx!(for (id, state) in server.channels.iter() { + if state.parent.is_none() { + Channel { id: *id } + } + }) +} + pub fn app() -> Element { let net = use_coroutine(|rx: UnboundedReceiver| super::network_entrypoint(rx)); let mut username = use_signal(|| "".to_string()); @@ -52,6 +120,8 @@ pub fn app() -> Element { onclick: move |event| net.send(Disconnect), "Disconnect" } + br {} + ServerView {} } } br {} diff --git a/src/lib.rs b/src/lib.rs index 80dd684..4ff4b4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ pub mod app; +use app::ChannelState; +use app::UserState; use dioxus::prelude::*; use anyhow::Error; @@ -28,7 +30,6 @@ use web_sys::window; use web_sys::AudioContext; use web_sys::AudioContextOptions; use web_sys::AudioData; -use web_sys::AudioDataInit; use web_sys::AudioDecoder; use web_sys::AudioDecoderConfig; use web_sys::AudioDecoderInit; @@ -368,6 +369,7 @@ pub async fn network_entrypoint(mut event_rx: UnboundedReceiver) { panic!("Did not receive connect command") }; + *STATE.server.write() = Default::default(); *STATE.status.write() = ConnectionState::Connecting; console::log_1(&"Rust via WASM!".into()); @@ -540,6 +542,9 @@ fn accept_packet( audio_context: &AudioContext, decoder_map: &mut HashMap, ) { + if !matches!(msg, ControlPacket::UDPTunnel(_)) { + console::log_1(&format!("{:#?}", msg).into()); + } match msg { ControlPacket::UDPTunnel(u) => { match *u.clone() { @@ -579,8 +584,78 @@ fn accept_packet( } } } - _ => { - console::log_1(&format!("{:#?}", msg).into()); + ControlPacket::ChannelState(u) => { + let mut server = STATE.server.write(); + let id = u.get_channel_id(); + + let state = server.channels.entry(id).or_default(); + let new_parent = if u.has_parent() { + if let Some(parent) = state.parent.and_then(|p| server.channels.get_mut(&p)) { + parent.children.remove(&id); + } + + let parent_id = u.get_parent(); + let parent = server.channels.entry(parent_id).or_default(); + if u.has_position() && u.get_position() as usize <= parent.children.len() { + // TODO: what if positions are received out of order? we need to sort afterwards? + parent.children.insert_before(u.get_position() as usize, id); + } else { + parent.children.insert(id); + } + Some(parent_id) + } else { + None + }; + + let state = server.channels.entry(id).or_default(); + state.parent = new_parent; + if u.has_name() { + state.name = u.get_name().to_string(); + } } + ControlPacket::ChannelRemove(u) => { + let mut server = STATE.server.write(); + let id = u.get_channel_id(); + if let Some(channel) = server.channels.remove(&id) { + if let Some(parent) = channel.parent.and_then(|p| server.channels.get_mut(&p)) { + parent.children.remove(&id); + } + } + } + ControlPacket::UserState(u) => { + let mut server = STATE.server.write(); + let server = &mut *server; + let id = u.get_session(); + + let state = server.users.entry(id).or_default(); + if u.has_channel_id() { + if let Some(parent) = server.channels.get_mut(&state.channel) { + parent.users.remove(&id); + } + + let channel_id = u.get_channel_id(); + server + .channels + .entry(channel_id) + .or_default() + .users + .insert(id); + state.channel = channel_id; + } + + if u.has_name() { + state.name = u.get_name().to_string(); + } + } + ControlPacket::UserRemove(u) => { + let mut server = STATE.server.write(); + let id = u.get_session(); + if let Some(state) = server.users.remove(&id) { + if let Some(parent) = server.channels.get_mut(&state.channel) { + parent.users.remove(&id); + } + } + } + _ => {} } }