feat: notification actions

This commit is contained in:
Ashley Wulber 2024-12-10 14:04:06 -05:00 committed by Michael Murphy
parent 104a608cf1
commit ad656296e7
4 changed files with 269 additions and 159 deletions

View file

@ -2,15 +2,20 @@
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{
iced::{futures, stream},
iced::{
futures::{self, FutureExt},
stream,
},
iced_futures::Subscription,
};
use cosmic_notifications_util::Notification;
use std::{
collections::HashMap,
future::pending,
os::unix::io::{FromRawFd, RawFd},
pin::pin,
};
use tokio::sync::mpsc;
use tracing::{error, trace};
use zbus::{
connection::Builder,
@ -20,54 +25,95 @@ use zbus::{
#[derive(Debug)]
pub enum State {
WaitingForNotificationEvent(u8),
WaitingForNotificationEvent,
Finished,
}
pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<Notification> {
#[derive(Debug, Clone)]
pub enum Input {
Activated(u32, String),
}
#[derive(Debug, Clone)]
pub enum Output {
Ready(mpsc::Sender<Input>),
Notification(Notification),
}
pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<Output> {
struct SomeWorker;
Subscription::run_with_id(
std::any::TypeId::of::<SomeWorker>(),
stream::channel(50, |mut output| async move {
let mut state = State::WaitingForNotificationEvent(0);
let mut state = State::WaitingForNotificationEvent;
let (sender, mut receiver) = mpsc::channel(10);
_ = output.send(Output::Ready(sender)).await;
let mut signal;
let mut fail_count: u8 = 0;
loop {
match proxy.receive_notify().await {
Ok(s) => {
signal = s;
break;
}
Err(err) => {
error!(
"failed to get a stream of signals for notifications. {}",
err
);
fail_count = fail_count.saturating_add(1);
if fail_count > 5 {
error!("Failed to receive notification events");
_ = pending::<()>();
} else {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
};
continue;
}
}
}
loop {
match &mut state {
State::WaitingForNotificationEvent(mut fail_count) => {
State::WaitingForNotificationEvent => {
trace!("Waiting for notification events...");
let mut signal = match proxy.receive_notify().await {
Ok(s) => s,
Err(err) => {
error!(
"failed to get a stream of signals for notifications. {}",
err
);
fail_count = fail_count.saturating_add(1);
if fail_count > 5 {
error!("Failed to receive notification events");
state = State::Finished;
let mut next_signal = signal.next();
let mut next_input = pin!(receiver.recv().fuse());
cosmic::iced::futures::select! {
v = next_signal => {
if let Some(msg) = v {
let Some(args) = msg.args().into_iter().next() else {
break;
};
let notification = Notification::new(
args.app_name,
args.id,
args.app_icon,
args.summary,
args.body,
args.actions,
args.hints,
args.expire_timeout,
);
_ = output.send(Output::Notification(notification)).await;
} else {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
};
continue;
tracing::error!("Signal stream closed, ending notifications subscription");
state = State::Finished;
}
}
v = next_input => {
if let Some(Input::Activated(id, action)) = v {
if let Err(err) = proxy.invoke_action(id, action.clone()).await {
tracing::error!("Failed to invoke action {id} {action}");
} else {
tracing::error!("Invoked {action} for {id}")
}
} else {
tracing::error!("Channel closed, ending notifications subscription");
state = State::Finished;
}
}
};
while let Some(msg) = signal.next().await {
let Some(args) = msg.args().into_iter().next() else {
break;
};
let notification = Notification::new(
args.app_name,
args.id,
args.app_icon,
args.summary,
args.body,
args.actions,
args.hints,
args.expire_timeout,
);
_ = output.send(notification).await;
}
}
State::Finished => {
@ -97,6 +143,8 @@ trait NotificationsApplet {
hints: HashMap<&str, zbus::zvariant::Value<'_>>,
expire_timeout: i32,
) -> zbus::Result<()>;
fn invoke_action(&self, id: u32, action: String) -> zbus::Result<()>;
}
pub async fn get_proxy() -> anyhow::Result<NotificationsAppletProxy<'static>> {