gui: fix channel ordering
Build Mumble Web 2 / linux_build (push) Successful in 1m25s
Build Mumble Web 2 / windows_build (push) Successful in 2m41s
Build Mumble Web 2 / android_build (push) Successful in 5m58s

This commit is contained in:
2026-01-25 01:29:28 -07:00
parent a30082eebe
commit 53a0ad0125
2 changed files with 125 additions and 47 deletions
+120 -13
View File
@@ -4,7 +4,7 @@ use dioxus::prelude::*;
use mime_guess::Mime; use mime_guess::Mime;
use mumble_web2_common::{ClientConfig, ServerStatus}; use mumble_web2_common::{ClientConfig, ServerStatus};
use ordermap::OrderSet; use ordermap::OrderSet;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use crate::imp; use crate::imp;
@@ -54,14 +54,6 @@ pub enum Command {
use Command::*; use Command::*;
use ConnectionState::*; use ConnectionState::*;
#[derive(Default)]
pub struct ChannelState {
pub name: String,
pub children: OrderSet<ChannelId>,
pub users: OrderSet<UserId>,
pub parent: Option<ChannelId>,
}
#[derive(Default)] #[derive(Default)]
pub struct UserState { pub struct UserState {
pub name: String, pub name: String,
@@ -94,8 +86,123 @@ pub struct Chat {
} }
#[derive(Default)] #[derive(Default)]
pub struct ServerState { 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_ChannelState(
&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)]
pub struct ChannelsState {
pub channels: HashMap<ChannelId, ChannelState>, pub channels: HashMap<ChannelId, ChannelState>,
}
impl ChannelsState {
pub fn update_from_ChannelState(
&mut self,
channel_state: &mumble_protocol::control::msgs::ChannelState,
) {
self.channels
.entry(channel_state.get_channel_id())
.or_default()
.update_from_ChannelState(channel_state);
self.update_channel_parents();
}
pub fn update_from_ChannelRemove(
&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 (id, state) in self.channels.iter_mut() {
state.children = OrderSet::new();
}
let mut channels_updated: HashSet<ChannelId> = HashSet::new();
channels_updated.insert(0); // insert root channel
let mut channels_to_parse: Vec<(ChannelId, ChannelId, i32)> = Vec::new();
for (id, state) in self.channels.iter() {
// Skip root channel
if *id == 0 {
continue;
}
// Ignore channels with no parent
let Some(parent_id) = state.parent else {
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;
}
channels_to_parse.push((*id, parent_id, state.position));
}
let mut channel_positions = HashMap::new();
for (id, state) in self.channels.iter() {
channel_positions.insert(*id, state.position);
}
while channels_updated.len() < channels_to_parse.len() {
for (id, parent_id, position) in &channels_to_parse {
if channels_updated.contains(&id) {
continue;
}
// Skip channels whose parent we haven't seen yet
if !channels_updated.contains(&parent_id) {
continue;
}
let parent = self.channels.get_mut(&parent_id).unwrap();
let mut after_index = 0;
for (index, child_id) in parent.children.iter().enumerate() {
after_index = index;
if channel_positions[child_id] > *position {
continue;
}
}
parent.children.insert_before(after_index, *id);
channels_updated.insert(*id);
}
}
}
}
#[derive(Default)]
pub struct ServerState {
pub channels_state: ChannelsState,
pub users: HashMap<UserId, UserState>, pub users: HashMap<UserId, UserState>,
pub chat: Vec<Chat>, pub chat: Vec<Chat>,
pub session: Option<UserId>, pub session: Option<UserId>,
@@ -182,7 +289,7 @@ pub fn Channel(id: ChannelId) -> Element {
let net: Coroutine<Command> = use_coroutine_handle(); let net: Coroutine<Command> = use_coroutine_handle();
let server = STATE.server.read(); let server = STATE.server.read();
let user = server.session.unwrap(); let user = server.session.unwrap();
let Some(state) = server.channels.get(&id) else { let Some(state) = server.channels_state.channels.get(&id) else {
return rsx!("missing channel {id}"); return rsx!("missing channel {id}");
}; };
@@ -336,7 +443,7 @@ pub fn ControlView(config: Resource<ClientConfig>) -> Element {
return rsx!(); return rsx!();
}; };
let current_channel_name = server.channels[&channel].name.clone(); let current_channel_name = server.channels_state.channels[&channel].name.clone();
let proxy_url = config let proxy_url = config
.read_unchecked() .read_unchecked()
@@ -528,7 +635,7 @@ pub fn ServerView(config: Resource<ClientConfig>) -> Element {
class: "server_grid", class: "server_grid",
div { div {
class: "server_channel_box", class: "server_channel_box",
for (id, state) in server.channels.iter() { for (id, state) in server.channels_state.channels.iter() {
if state.parent.is_none() { if state.parent.is_none() {
Channel { id: *id } Channel { id: *id }
} }
+5 -34
View File
@@ -335,41 +335,11 @@ fn accept_packet(
} }
ControlPacket::ChannelState(u) => { ControlPacket::ChannelState(u) => {
let mut server = STATE.server.write(); let mut server = STATE.server.write();
let id = u.get_channel_id(); server.channels_state.update_from_ChannelState(&u);
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) => { ControlPacket::ChannelRemove(u) => {
let mut server = STATE.server.write(); let mut server = STATE.server.write();
let id = u.get_channel_id(); server.channels_state.update_from_ChannelRemove(&u);
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) => { ControlPacket::UserState(u) => {
let mut server = STATE.server.write(); let mut server = STATE.server.write();
@@ -381,12 +351,13 @@ fn accept_packet(
let state = state_entry.or_default(); let state = state_entry.or_default();
// the server might now send a channel_id if the user is in channel=0 // the server might now send a channel_id if the user is in channel=0
if u.has_channel_id() || new { if u.has_channel_id() || new {
if let Some(parent) = server.channels.get_mut(&state.channel) { if let Some(parent) = server.channels_state.channels.get_mut(&state.channel) {
parent.users.remove(&id); parent.users.remove(&id);
} }
let channel_id = u.get_channel_id(); let channel_id = u.get_channel_id();
server server
.channels_state
.channels .channels
.entry(channel_id) .entry(channel_id)
.or_default() .or_default()
@@ -418,7 +389,7 @@ fn accept_packet(
let mut server = STATE.server.write(); let mut server = STATE.server.write();
let id = u.get_session(); let id = u.get_session();
if let Some(state) = server.users.remove(&id) { if let Some(state) = server.users.remove(&id) {
if let Some(parent) = server.channels.get_mut(&state.channel) { if let Some(parent) = server.channels_state.channels.get_mut(&state.channel) {
parent.users.remove(&id); parent.users.remove(&id);
} }
} }