Add icon when self muted of deafen
authorGreg Burri <greg.burri@gmail.com>
Wed, 19 Jun 2024 13:33:06 +0000 (15:33 +0200)
committerGreg Burri <greg.burri@gmail.com>
Wed, 19 Jun 2024 13:33:06 +0000 (15:33 +0200)
Cargo.lock
Cargo.toml
README.md [new file with mode: 0644]
doc/mumble_web.service [new file with mode: 0644]
ice/ice.py
src/ice.rs
src/main.rs
static/deafened_self.svg [new file with mode: 0644]
static/muted_self.svg [new file with mode: 0644]
style.scss
templates/main.html

index 3eb5a8a..3a5ea2d 100644 (file)
@@ -82,19 +82,6 @@ dependencies = [
  "nom",
 ]
 
-[[package]]
-name = "async-compression"
-version = "0.4.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5"
-dependencies = [
- "flate2",
- "futures-core",
- "memchr",
- "pin-project-lite",
- "tokio",
-]
-
 [[package]]
 name = "async-trait"
 version = "0.1.80"
@@ -251,15 +238,6 @@ dependencies = [
  "unicode_categories",
 ]
 
-[[package]]
-name = "crc32fast"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
-dependencies = [
- "cfg-if",
-]
-
 [[package]]
 name = "deunicode"
 version = "1.6.0"
@@ -284,16 +262,6 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
-[[package]]
-name = "flate2"
-version = "1.0.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
-dependencies = [
- "crc32fast",
- "miniz_oxide",
-]
-
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -1081,10 +1049,8 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
 dependencies = [
- "async-compression",
  "bitflags",
  "bytes",
- "futures-core",
  "futures-util",
  "http",
  "http-body",
index 0cf7267..5d6e904 100644 (file)
@@ -7,7 +7,7 @@ edition = "2021"
 axum = { version = "0.7", features = ["http2"] }
 tokio = { version = "1", features = ["full"] }
 tower = { version = "0.4", features = ["util"] }
-tower-http = { version = "0.5", features = ["fs", "trace", "compression-gzip"] }
+tower-http = { version = "0.5", features = ["fs", "trace"] }
 
 tracing = "0.1"
 tracing-subscriber = { version = "0.3", features = ["env-filter"] }
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..edc79be
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+# Systemd
+
+"/doc/mumble_web.service" is a systemd user service file. On linux it must be placed
+in "~/.config/systemd/user".
+
+# Documentation
+
+Axum: https://docs.rs/axum/latest/axum/index.html
+Axum examples: https://github.com/tokio-rs/axum/blob/main/ECOSYSTEM.md
+Tower HTTP: https://docs.rs/tower-http/0.5.2/tower_http/index.html
+Askama: https://djc.github.io/askama/askama.html
diff --git a/doc/mumble_web.service b/doc/mumble_web.service
new file mode 100644 (file)
index 0000000..21e7b66
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=mumble_web
+
+[Service]
+WorkingDirectory=/var/www/mumble_web
+ExecStart=/var/www/mumble_web/mumble_web
+SyslogIdentifier=mumble_web
+Restart=always
+RestartSec=10
+KillSignal=SIGINT
+
+[Install]
+WantedBy=default.target
index f981bd8..34cba11 100644 (file)
@@ -4,23 +4,29 @@ initdata = Ice.InitializationData()
 initdata.properties = Ice.createProperties(None, initdata.properties)
 initdata.properties.setProperty('Ice.ImplicitContext', 'Shared')
 
-def getChannelPath(channels, channelId):
+def getChannelPath(channels, channelId, first=True):
     if channelId == 0:
-        return ""
+        return "/"
     else:
         channel = channels[channelId]
-        return getChannelPath(channels, channel.parent) + "/" + channel.name
+        return getChannelPath(channels, channel.parent, False) + channel.name + \
+            ("" if first  else "/")
 
 def getUsers(server):
-    return map(lambda u: (u.name, u.channel), server.getUsers().values())
+    return map(lambda u: (u.name, u.channel, u.selfMute, u.selfDeaf), server.getUsers().values())
 
 def getUsersAsJson(server):
     channels = server.getChannels()
     users = getUsers(server)
     return json.dumps(
         [
-            {'username': username, 'channel': getChannelPath(channels, channel)}
-            for (username, channel) in users
+            {
+                'username': username,
+                'channel': getChannelPath(channels, channel),
+                'selfMute': selfMute,
+                'selfDeaf': selfDeaf
+            }
+            for (username, channel, selfMute, selfDeaf) in users
         ]
     )
 
index f917f82..c772ddc 100644 (file)
@@ -22,6 +22,8 @@ const CACHED_VALUES_DURATION: Duration = Duration::from_secs(5);
 pub struct User {
     pub name: String,
     pub channel: String,
+    pub self_mute: bool,
+    pub self_deaf: bool, // ðŸ”‡
 }
 
 pub struct Ice {
@@ -137,6 +139,8 @@ impl Ice {
                 users.push(User {
                     name: user_json["username"].as_str().unwrap().to_string(),
                     channel: user_json["channel"].as_str().unwrap().to_string(),
+                    self_mute: user_json["selfMute"].as_bool().unwrap(),
+                    self_deaf: user_json["selfDeaf"].as_bool().unwrap(),
                 });
             }
             self.cached_users = users.clone();
@@ -148,10 +152,10 @@ impl Ice {
 
 impl Drop for Ice {
     fn drop(&mut self) {
-        println!("Dropping..",);
+        // println!("Ice: dropping...",);
         self.dropped.store(true, Ordering::Relaxed);
         self.kill_event.set();
         self.kill_event.wait();
-        println!("Dropped!",);
+        // println!("Ice: dropped!",);
     }
 }
index 6352630..2055ed5 100644 (file)
@@ -8,13 +8,25 @@ use askama::Template;
 use askama_axum::IntoResponse;
 use axum::{extract::State, routing::get, Router};
 use tokio::signal;
-use tower_http::{compression::CompressionLayer, services::ServeDir};
+use tower_http::services::ServeDir;
 
 mod ice;
 
 #[derive(Eq)]
 struct User {
     name: String,
+    self_mute: bool,
+    self_deaf: bool,
+}
+
+impl User {
+    fn from_ice_user(user: &ice::User) -> Self {
+        User {
+            name: user.name.clone(),
+            self_mute: user.self_mute,
+            self_deaf: user.self_deaf,
+        }
+    }
 }
 
 impl PartialEq for User {
@@ -57,8 +69,7 @@ async fn main() {
     let app = Router::new()
         .route("/", get(root))
         .nest_service("/static", ServeDir::new("static"))
-        .with_state(ice)
-        .layer(CompressionLayer::new());
+        .with_state(ice);
 
     let addr = SocketAddr::from(([127, 0, 0, 1], 8086));
     let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
@@ -76,17 +87,13 @@ async fn root(State(ice): State<Arc<Mutex<ice::Ice>>>) -> impl IntoResponse {
     'next_user: for u in &users {
         for c in &mut channels {
             if c.name == u.channel {
-                c.users.push(User {
-                    name: u.name.clone(),
-                });
+                c.users.push(User::from_ice_user(u));
                 continue 'next_user;
             }
         }
         let channel = Channel {
             name: u.channel.clone(),
-            users: vec![User {
-                name: u.name.clone(),
-            }],
+            users: vec![User::from_ice_user(u)],
         };
         channels.push(channel);
     }
diff --git a/static/deafened_self.svg b/static/deafened_self.svg
new file mode 100644 (file)
index 0000000..05037d4
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g opacity="0.85">
+       <path fill="#EA4335" d="M168.46,169.386c0-12.6-10.309-22.909-22.909-22.909h-10.19c-12.6,0-22.909,10.309-22.909,22.909v21.048
+               v53.296l56.008-28.068V169.386z"/>
+</g>
+<path opacity="0.85" fill="#EA4335" enable-background="new    " d="M391.982,103.627l-198.147,99.316l-0.001-33.063
+       c0-12.6,9.309-27.338,20.687-32.751l156.772-74.583c11.379-5.413,20.687,0.079,20.687,12.203L391.982,103.627z"/>
+<path opacity="0.85" fill="#EA4335" enable-background="new    " d="M193.835,297.054v44.058c0,12.6,9.309,27.338,20.687,32.751
+       l156.771,74.583c11.379,5.413,20.687-0.467,20.687-13.067l0.002-237.018l73.56-35.201l-19.709-39.323L46.46,324.009l19.709,39.323
+       l46.282-24.613v4.721c0,12.6,10.309,22.909,22.909,22.909h10.19c12.6,0,22.909-10.309,22.909-22.909v-32.799L193.835,297.054z"/>
+</svg>
diff --git a/static/muted_self.svg b/static/muted_self.svg
new file mode 100644 (file)
index 0000000..be80b95
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<path opacity="0.85" fill="#EA4335" enable-background="new    " d="M256,55.05c-41.43,0-75.02,33.59-75.02,75.02v100.89
+       l150.04-81.8v-19.09C331.02,88.64,297.43,55.05,256,55.05z"/>
+<path opacity="0.85" fill="#FF3E3E" enable-background="new    " d="M237.46,444.93L237.46,444.93z"/>
+<path opacity="0.85" fill="#FF3E3E" enable-background="new    " d="M274.54,444.93L274.54,444.93z"/>
+<path opacity="0.85" fill="#EA4335" enable-background="new    " d="M274.54,444.93c62.37-8.99,110.29-62.66,110.29-127.51
+       l-26.71,0.79c0,56.4-45.72,102.12-102.12,102.12c-44.12,0-81.71-27.98-95.97-67.17l-0.001-0.002l27.46-14.971l0.001,0.003
+       c11.71,26.19,37.98,44.43,68.51,44.43c41.43,0,75.02-33.59,75.02-75.02l0.001-47.667L481.5,177.89l-22.98-42.14L30.5,369.11
+       l22.98,42.14l83.149-45.333l0.001,0.003c16.91,41.62,54.96,72.39,100.83,79.01v27.24h-73.4c-10.41,0-18.85,8.44-18.85,18.85v-2.93
+       c0,10.42,8.44,18.86,18.85,18.86h183.88c10.41,0,18.85-8.44,18.85-18.86v2.93c0-10.41-8.44-18.85-18.85-18.85h-73.4V444.93z"/>
+</svg>
index b745f54..255e4da 100644 (file)
@@ -28,10 +28,17 @@ body {
   background-color: #252525;
 }
 
-#channel {
+.channel {
   color: #c5c500;
 }
 
+.icon {
+  height: 20px;
+  margin-left: 5px;
+  margin-right: 5px;
+  vertical-align: bottom;
+}
+
 h1 {
   font-size: x-large;
   margin: 20px 20px 16px 20px;
index 18fe2c5..5bc6fba 100644 (file)
@@ -4,7 +4,7 @@
 <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Euphoirik Mumble</title>
+    <title>Euphorik Mumble</title>
     <link rel="stylesheet" type="text/css" href="static/style.css" />
 </head>
 
         <p>No user connected</p>
         {% else %}
         {% for channel in channels %}
-        <h2 id="channel">{{ channel.name }}</h2>
+        <h2 class="channel">{{ channel.name }}</h2>
         <ul>
             {% for user in channel.users %}
-            <li>{{ user.name }}</li>
+            <li>{{ user.name }}
+                {% if user.self_mute %}<img class="icon" src="static/muted_self.svg" />{% endif %}<!--
+                -->{% if user.self_deaf %}<img class="icon" src="static/deafened_self.svg" />{% endif %}
+            </li>
             {% endfor %}
         </ul>
         {% endfor %}