From 722b1659c8e9aa6f8fb8c63e13b3bf7d0da08764 Mon Sep 17 00:00:00 2001 From: Tuan Nguyen <38831897+justmangoou@users.noreply.github.com> Date: Fri, 26 Jun 2026 17:50:31 +0700 Subject: [PATCH] inital commit --- Cargo.toml | 1 + src/actions/notifications.rs | 50 +++++++++++++++++++++++++++++++----- src/rpc_events.rs | 2 +- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f626906..7a45564 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] openaction = "2.6" discord-ipc-rust = { git = "https://github.com/nekename/discord-ipc-rust.git" } +base64 = "0.22" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1.52", features = ["rt-multi-thread", "macros"] } diff --git a/src/actions/notifications.rs b/src/actions/notifications.rs index cab4172..b78e836 100644 --- a/src/actions/notifications.rs +++ b/src/actions/notifications.rs @@ -5,15 +5,51 @@ use discord_ipc_rust::models::send::commands::{SelectTextChannelArgs, SentComman use openaction::{Action, ActionUuid, Instance, OpenActionResult, async_trait}; use serde::{Deserialize, Serialize}; -pub async fn update_title(instance: &Instance) -> OpenActionResult<()> { +const NOTIFICATION_SVG: &str = include_str!("../../assets/actions/notifications.svg"); + +fn generate_badge_xml(count: usize) -> String { + if count == 0 { + return String::new(); + } + let title = format!("{}", count); + + if count < 100 { + format!( + r#" + {}"#, + title + ) + } else { + let digit_count = title.len() as i32; + let width = 20 + (digit_count * 9); + let rect_x = 105 - (width / 2); + format!( + r#" + {}"#, + rect_x, width, title + ) + } +} + +pub async fn update_image(instance: &Instance) -> OpenActionResult<()> { + use base64::{Engine, prelude::BASE64_STANDARD}; + let cache = notification_cache().read().await; - let title = format!("{}", cache.len()); + let count = cache.len(); + let final_svg = if count > 0 { + let badge_xml = generate_badge_xml(count); + NOTIFICATION_SVG.replace("", &format!("{}\n", badge_xml)) + } else { + NOTIFICATION_SVG.to_string() + }; - if let Err(e) = instance.set_title(Some(title), None).await { + let b64_encoded = BASE64_STANDARD.encode(final_svg.as_bytes()); + let data_url = format!("data:image/svg+xml;base64,{}", b64_encoded); + + if let Err(e) = instance.set_image(Some(data_url), None).await { log::error!("Failed to update notifications action title: {}", e); instance.show_alert().await?; } - Ok(()) } @@ -44,7 +80,7 @@ impl Action for NotificationsAction { instance: &Instance, _settings: &Self::Settings, ) -> OpenActionResult<()> { - update_title(instance).await + update_image(instance).await } async fn key_down( @@ -56,7 +92,7 @@ impl Action for NotificationsAction { NotificationsActionType::DoNothing => return Ok(()), NotificationsActionType::Clear => { notification_cache().write().await.clear(); - update_title(instance).await?; + update_image(instance).await?; return Ok(()); } NotificationsActionType::OpenAndClear => { @@ -78,7 +114,7 @@ impl Action for NotificationsAction { return Ok(()); }; - update_title(instance).await?; + update_image(instance).await?; let mut client_lock = discord_client().write().await; let Some(client) = client_lock.as_mut() else { diff --git a/src/rpc_events.rs b/src/rpc_events.rs index b0efed5..c9a0c1b 100644 --- a/src/rpc_events.rs +++ b/src/rpc_events.rs @@ -132,7 +132,7 @@ async fn handle_select_voice_channel(channel_id: Option) { async fn handle_notification(notification: NotificationCreateData) { crate::cache::add_notification_to_cache(notification).await; for instance in visible_instances(crate::actions::NotificationsAction::UUID).await { - let _ = crate::actions::notifications::update_title(&instance).await; + let _ = crate::actions::notifications::update_image(&instance).await; } }