3a9bb60605
Reviewed-on: #30 Reviewed-by: restitux <restitux@ohea.xyz> Co-authored-by: Sam Sartor <me@samsartor.com> Co-committed-by: Sam Sartor <me@samsartor.com>
216 lines
5.5 KiB
Rust
216 lines
5.5 KiB
Rust
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<ChannelId>,
|
|
},
|
|
SendFile {
|
|
bytes: Vec<u8>,
|
|
name: String,
|
|
mime: Option<Mime>,
|
|
channels: Vec<ChannelId>,
|
|
},
|
|
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<UserId>,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct ChannelState {
|
|
pub name: String,
|
|
pub children: OrderSet<ChannelId>,
|
|
pub users: OrderSet<UserId>,
|
|
pub parent: Option<ChannelId>,
|
|
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<ChannelId, ChannelState>,
|
|
}
|
|
|
|
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<ChannelId>, 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<ChannelId, (i32, String)> = self
|
|
.channels
|
|
.iter()
|
|
.map(|(&id, state)| (id, (state.position, state.name.clone())))
|
|
.collect();
|
|
|
|
let mut updated: HashSet<ChannelId> = 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<UserId, UserState>,
|
|
pub chat: Vec<Chat>,
|
|
pub session: Option<UserId>,
|
|
}
|
|
|
|
impl ServerState {
|
|
pub fn this_user(&self) -> Option<&UserState> {
|
|
self.users.get(&self.session?)
|
|
}
|
|
}
|
|
|
|
pub struct State {
|
|
pub status: Signal<ConnectionState>,
|
|
pub server: Signal<ServerState>,
|
|
pub audio: Signal<AudioSettings>,
|
|
}
|
|
|
|
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<State>;
|