mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-20 05:39:52 -07:00
Support notifications via terminal bell (#227)
Co-authored-by: Benjamin Grosse <ste3ls@gmail.com>
This commit is contained in:
parent
db9cb92737
commit
99996e275b
6 changed files with 78 additions and 27 deletions
|
@ -1273,6 +1273,9 @@ pub struct ChatStore {
|
|||
|
||||
/// Last draw time, used to match with RoomInfo's draw_last.
|
||||
pub draw_curr: Option<Instant>,
|
||||
|
||||
/// Whether to ring the terminal bell on the next redraw.
|
||||
pub ring_bell: bool,
|
||||
}
|
||||
|
||||
impl ChatStore {
|
||||
|
@ -1294,6 +1297,7 @@ impl ChatStore {
|
|||
need_load: Default::default(),
|
||||
sync_info: Default::default(),
|
||||
draw_curr: None,
|
||||
ring_bell: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,10 @@ fn is_profile_char(c: char) -> bool {
|
|||
c.is_ascii_alphanumeric() || c == '.' || c == '-'
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn validate_profile_name(name: &str) -> bool {
|
||||
if name.is_empty() {
|
||||
return false;
|
||||
|
@ -391,10 +395,24 @@ pub enum UserDisplayStyle {
|
|||
DisplayName,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum NotifyVia {
|
||||
/// Deliver notifications via terminal bell.
|
||||
Bell,
|
||||
/// Deliver notifications via desktop mechanism.
|
||||
#[default]
|
||||
Desktop,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||
pub struct Notifications {
|
||||
#[serde(default)]
|
||||
pub enabled: bool,
|
||||
pub show_message: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub via: NotifyVia,
|
||||
#[serde(default = "default_true")]
|
||||
pub show_message: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::collections::VecDeque;
|
|||
use std::convert::TryFrom;
|
||||
use std::fmt::Display;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::{stdout, BufWriter, Stdout};
|
||||
use std::io::{stdout, BufWriter, Stdout, Write};
|
||||
use std::ops::DerefMut;
|
||||
use std::process;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
@ -293,6 +293,10 @@ impl Application {
|
|||
let sstate = &mut self.screen;
|
||||
let term = &mut self.terminal;
|
||||
|
||||
if store.application.ring_bell {
|
||||
store.application.ring_bell = term.backend_mut().write_all(&[7]).is_err();
|
||||
}
|
||||
|
||||
if full {
|
||||
term.clear()?;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||
|
||||
use crate::{
|
||||
base::{AsyncProgramStore, IambError, IambResult},
|
||||
config::ApplicationSettings,
|
||||
config::{ApplicationSettings, NotifyVia},
|
||||
};
|
||||
|
||||
pub async fn register_notifications(
|
||||
|
@ -26,6 +26,7 @@ pub async fn register_notifications(
|
|||
if !settings.tunables.notifications.enabled {
|
||||
return;
|
||||
}
|
||||
let notify_via = settings.tunables.notifications.via;
|
||||
let show_message = settings.tunables.notifications.show_message;
|
||||
let server_settings = client.notification_settings().await;
|
||||
let Some(startup_ts) = MilliSecondsSinceUnixEpoch::from_system_time(SystemTime::now()) else {
|
||||
|
@ -47,29 +48,19 @@ pub async fn register_notifications(
|
|||
return;
|
||||
}
|
||||
|
||||
match parse_notification(notification, room).await {
|
||||
match parse_notification(notification, room, show_message).await {
|
||||
Ok((summary, body, server_ts)) => {
|
||||
if server_ts < startup_ts {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut desktop_notification = notify_rust::Notification::new();
|
||||
desktop_notification
|
||||
.summary(&summary)
|
||||
.appname("iamb")
|
||||
.timeout(notify_rust::Timeout::Milliseconds(3000))
|
||||
.action("default", "default");
|
||||
|
||||
if is_missing_mention(&body, mode, &client) {
|
||||
return;
|
||||
}
|
||||
if show_message != Some(false) {
|
||||
if let Some(body) = body {
|
||||
desktop_notification.body(&body);
|
||||
}
|
||||
}
|
||||
if let Err(err) = desktop_notification.show() {
|
||||
tracing::error!("Failed to send notification: {err}")
|
||||
|
||||
match notify_via {
|
||||
NotifyVia::Desktop => send_notification_desktop(summary, body),
|
||||
NotifyVia::Bell => send_notification_bell(&store).await,
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
|
@ -81,6 +72,28 @@ pub async fn register_notifications(
|
|||
.await;
|
||||
}
|
||||
|
||||
async fn send_notification_bell(store: &AsyncProgramStore) {
|
||||
let mut locked = store.lock().await;
|
||||
locked.application.ring_bell = true;
|
||||
}
|
||||
|
||||
fn send_notification_desktop(summary: String, body: Option<String>) {
|
||||
let mut desktop_notification = notify_rust::Notification::new();
|
||||
desktop_notification
|
||||
.summary(&summary)
|
||||
.appname("iamb")
|
||||
.timeout(notify_rust::Timeout::Milliseconds(3000))
|
||||
.action("default", "default");
|
||||
|
||||
if let Some(body) = body {
|
||||
desktop_notification.body(&body);
|
||||
}
|
||||
|
||||
if let Err(err) = desktop_notification.show() {
|
||||
tracing::error!("Failed to send notification: {err}")
|
||||
}
|
||||
}
|
||||
|
||||
async fn global_or_room_mode(
|
||||
settings: &NotificationSettings,
|
||||
room: &MatrixRoom,
|
||||
|
@ -129,6 +142,7 @@ async fn is_open(store: &AsyncProgramStore, room_id: &RoomId) -> bool {
|
|||
pub async fn parse_notification(
|
||||
notification: Notification,
|
||||
room: MatrixRoom,
|
||||
show_body: bool,
|
||||
) -> IambResult<(String, Option<String>, MilliSecondsSinceUnixEpoch)> {
|
||||
let event = notification.event.deserialize().map_err(IambError::from)?;
|
||||
|
||||
|
@ -142,12 +156,17 @@ pub async fn parse_notification(
|
|||
.and_then(|m| m.display_name())
|
||||
.unwrap_or_else(|| sender_id.localpart());
|
||||
|
||||
let body = event_notification_body(
|
||||
&event,
|
||||
sender_name,
|
||||
room.is_direct().await.map_err(IambError::from)?,
|
||||
)
|
||||
.map(truncate);
|
||||
let body = if show_body {
|
||||
event_notification_body(
|
||||
&event,
|
||||
sender_name,
|
||||
room.is_direct().await.map_err(IambError::from)?,
|
||||
)
|
||||
.map(truncate)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
return Ok((sender_name.to_string(), body, server_ts));
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ use crate::{
|
|||
ApplicationSettings,
|
||||
DirectoryValues,
|
||||
Notifications,
|
||||
NotifyVia,
|
||||
ProfileConfig,
|
||||
SortOverrides,
|
||||
TunableValues,
|
||||
|
@ -187,7 +188,11 @@ pub fn mock_tunables() -> TunableValues {
|
|||
open_command: None,
|
||||
username_display: UserDisplayStyle::Username,
|
||||
message_user_color: false,
|
||||
notifications: Notifications { enabled: false, show_message: None },
|
||||
notifications: Notifications {
|
||||
enabled: false,
|
||||
via: NotifyVia::Desktop,
|
||||
show_message: true,
|
||||
},
|
||||
image_preview: None,
|
||||
user_gutter_width: 30,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue