use dioxus_signals::{ReadableExt as _, Signal}; use mime_guess::Mime; use mumble_web2_common::ProxyOverrides; use ordermap::OrderSet; use std::collections::{HashMap, HashSet}; use std::{fmt, sync::Arc}; pub type ChannelId = u32; pub type UserId = u32; #[derive(Debug)] pub enum ConnectionState { Disconnected, Connecting, Connected, Failed(String), } #[derive(Debug, Clone)] pub struct AudioSettings { pub denoise: bool, } #[derive(Debug)] pub enum Command { Connect { address: String, username: String, config: ProxyOverrides, }, SendChat { markdown: String, channels: Vec, }, SendFile { bytes: Vec, name: String, mime: Option, channels: Vec, }, SetMute { mute: bool, }, SetDeaf { deaf: bool, }, EnterChannel { channel: ChannelId, user: UserId, }, UpdateAudioSettings(AudioSettings), Disconnect, } #[derive(Default, Debug)] pub struct UserState { pub name: String, pub channel: ChannelId, pub deaf: bool, pub mute: bool, pub suppress: bool, pub self_deaf: bool, pub self_mute: bool, } #[derive(Debug)] pub struct Chat { pub raw: String, pub dangerous_html: String, pub sender: Option, } #[derive(Default, Debug)] pub struct ChannelState { pub name: String, pub children: OrderSet, pub users: OrderSet, pub parent: Option, pub position: i32, } impl ChannelState { pub fn update_from_channel_state( &mut self, channel_state: &mumble_protocol::control::msgs::ChannelState, ) { if channel_state.has_position() { self.position = channel_state.get_position(); } if channel_state.has_parent() { self.parent = Some(channel_state.get_parent()); } if channel_state.has_name() { self.name = channel_state.get_name().to_string(); } } } #[derive(Default, Debug)] pub struct ChannelsState { pub channels: HashMap, } impl ChannelsState { pub fn update_from_channel_state( &mut self, channel_state: &mumble_protocol::control::msgs::ChannelState, ) { self.channels .entry(channel_state.get_channel_id()) .or_default() .update_from_channel_state(channel_state); self.update_channel_parents(); } pub fn update_from_channel_remove( &mut self, channel_remove: &mumble_protocol::control::msgs::ChannelRemove, ) { self.channels.remove(&channel_remove.get_channel_id()); self.update_channel_parents(); } pub fn update_channel_parents(&mut self) { // Zero out existing children for state in self.channels.values_mut() { state.children.clear(); } let mut to_sort: Vec<(ChannelId, Option, i32, String)> = Vec::new(); for (id, state) in self.channels.iter() { // Handle channels with no parent (the root channel) let Some(parent_id) = state.parent else { to_sort.push((*id, None, 0, state.name.clone())); continue; }; // If a channel has a parent that we haven't gotten a channel // state packet for, ignore it if !self.channels.contains_key(&parent_id) { continue; } to_sort.push((*id, Some(parent_id), state.position, state.name.clone())); } let pos_name: HashMap = self .channels .iter() .map(|(&id, state)| (id, (state.position, state.name.clone()))) .collect(); let mut updated: HashSet = HashSet::new(); while updated.len() < to_sort.len() { for &(id, ref parent_id, position, ref name) in &to_sort { let Some(parent_id) = parent_id else { updated.insert(id); continue; }; if updated.contains(&id) || !updated.contains(&parent_id) { continue; } // Unwrap should never fail here since we pre filter let parent = self.channels.get_mut(&parent_id).unwrap(); let mut insert_index = parent.children.len(); for (i, &child) in parent.children.iter().enumerate() { let (p, ref n) = pos_name[&child]; if (position == p && name < n) || p > position { insert_index = i; break; } } parent.children.insert_before(insert_index, id); updated.insert(id); } } } } #[derive(Default, Debug)] pub struct ServerState { pub channels_state: ChannelsState, pub users: HashMap, pub chat: Vec, pub session: Option, } impl ServerState { pub fn this_user(&self) -> Option<&UserState> { self.users.get(&self.session?) } } pub struct State { pub status: Signal, pub server: Signal, pub audio: Signal, } impl fmt::Debug for State { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("State") .field("status", &self.status.read()) .field("server", &self.server.read()) .finish() } } pub type SharedState = Arc;