Skip to content

Commit

Permalink
feat: extend the timeout property to set per urgency (#62)
Browse files Browse the repository at this point in the history
* feat: extend the timeout property to set per urgency

* docs: describe the extended timeout config property
  • Loading branch information
JarKz authored Dec 14, 2024
1 parent b81ad94 commit 359fa75
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 7 deletions.
6 changes: 5 additions & 1 deletion crates/backend/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,11 @@ impl Window {
}
notification::Timeout::Never => None,
notification::Timeout::Configurable => {
let timeout = config.display_by_app(&rect.notification().app_name).timeout;
let notification = rect.notification();
let timeout = config
.display_by_app(&notification.app_name)
.timeout
.by_urgency(&notification.hints.urgency);
if timeout != 0 && rect.created_at().elapsed().as_millis() > timeout as u128 {
Some(rect.notification().id)
} else {
Expand Down
129 changes: 125 additions & 4 deletions crates/config/src/display.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::path::PathBuf;
use std::{collections::HashMap, marker::PhantomData, path::PathBuf};

use dbus::notification::Urgency;
use macros::{ConfigProperty, GenericBuilder};
use serde::Deserialize;
use serde::{de::Visitor, Deserialize};
use shared::{error::ConversionError, value::TryDowncast};

use crate::{
Expand Down Expand Up @@ -37,8 +38,8 @@ public! {
#[cfg_prop(default(true))]
markup: Option<bool>,

#[cfg_prop(default(0))]
timeout: Option<u16>,
#[cfg_prop(default(Timeout::new(0)))]
timeout: Option<Timeout>,
}
}

Expand Down Expand Up @@ -188,3 +189,123 @@ impl TryFrom<shared::value::Value> for Border {
}
}
}

#[derive(Debug, Default, Clone)]
pub struct Timeout {
default: Option<u16>,
low: Option<u16>,
normal: Option<u16>,
critical: Option<u16>,
}

impl Timeout {
const DEFAULT: u16 = 0;

fn new(default_value: u16) -> Self {
Self {
default: default_value.into(),
..Default::default()
}
}

pub fn by_urgency(&self, urgency: &Urgency) -> u16 {
match urgency {
Urgency::Low => self.low,
Urgency::Normal => self.normal,
Urgency::Critical => self.critical,
}
.or(self.default)
.unwrap_or(Self::DEFAULT)
}
}

impl From<u16> for Timeout {
fn from(value: u16) -> Self {
Timeout::new(value)
}
}

impl From<HashMap<String, u16>> for Timeout {
fn from(value: HashMap<String, u16>) -> Self {
Timeout {
default: value.get("default").copied(),
low: value.get("low").copied(),
normal: value.get("normal").copied(),
critical: value.get("critical").copied(),
}
}
}

struct TimeoutVisitor<T>(PhantomData<fn() -> T>);

impl<'de> Deserialize<'de> for Timeout {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_any(TimeoutVisitor(PhantomData))
}
}

impl<'de, T> Visitor<'de> for TimeoutVisitor<T>
where
T: Deserialize<'de> + From<u16> + From<HashMap<String, u16>>,
{
type Value = T;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
formatter,
r#"Either u16 or Table value.
Example:
# In milliseconds
display.timeout = 2000
# or
[display.timeout]
low = 2000
normal = 4000
critical = 5000
# or
[display.timeout]
default = 3000 # for low and normal this value will be set
critical = 0 # but for critical the default value will be overriden
"#
)
}

fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v.into())
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut local_map = HashMap::new();

while let Some((key, value)) = map.next_entry::<String, u16>()? {
match key.as_str() {
"default" | "low" | "normal" | "critical" => {
local_map.insert(key, value);
}
_ => {
return Err(serde::de::Error::unknown_variant(
&key,
&["default", "low", "normal", "critical"],
))
}
}
}

Ok(local_map.into())
}
}
16 changes: 14 additions & 2 deletions docs/ConfigProperties.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ The currently possible properties of `display` table:
| [title](#text) | Title text properties | `Text` | - |
| [body](#text) | Body text properties | `Text` | - |
| [markup](#markup) | Enables HTML style markup | `bool` | true |
| [timeout](#timeout) | Sets the timeout of banner | `u16` | 0 |
| [timeout](#timeout) | Sets the timeout of banner | `u16` or `Timeout` | 0 |

The [layout](./Filetype.md) property should have or `"default"` value or path to file in which
describes layout for banner. You can pass path with environment variables like
Expand Down Expand Up @@ -347,7 +347,19 @@ You can turn off the `markup` property by setting `false` value.
The time in milliseconds when the notification banner should be closed by expiration
since creation.

The value `0` means will never expired.
There is also extended timeout configuration - per urgency of banners by `Timeout` table.

The `Timeout` table:

| Key | Short description | Type |
| :------- | :---------------------------------------------------- | :---- |
| default | Set default timeout for all urgency | `u16` |
| low | Override timeout value for 'low' urgency banners | `u16` |
| normal | Override timeout value for 'normal' urgency banners | `u16` |
| critical | Override timeout value for 'critical' urgency banners | `u16` |

> [!NOTE]
> The value `0` means will never expired.
---

Expand Down

0 comments on commit 359fa75

Please sign in to comment.