rewrite subscription complete
This commit is contained in:
@ -14,6 +14,7 @@ use gio::UnixSocketAddress;
|
||||
use gtk::{gdk, gio, glib};
|
||||
use ntfy_daemon::models;
|
||||
use ntfy_daemon::ntfy_capnp::system_notifier;
|
||||
use ntfy_daemon::NtfyHandle;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::config::{APP_ID, PKGDATADIR, PROFILE, VERSION};
|
||||
@ -23,7 +24,6 @@ mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::WeakRef;
|
||||
use ntfy_daemon::Ntfy;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use super::*;
|
||||
@ -33,7 +33,7 @@ mod imp {
|
||||
pub window: RefCell<WeakRef<NotifyWindow>>,
|
||||
pub socket_path: RefCell<PathBuf>,
|
||||
pub hold_guard: OnceCell<gio::ApplicationHoldGuard>,
|
||||
pub ntfy: OnceCell<Ntfy>,
|
||||
pub ntfy: OnceCell<NtfyHandle>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -319,7 +319,7 @@ impl NotifyApplication {
|
||||
}
|
||||
}
|
||||
let proxies = std::sync::Arc::new(Proxies { notification: s });
|
||||
let ntfy = ntfy_daemon::Ntfy::start(
|
||||
let ntfy = ntfy_daemon::start(
|
||||
socket_path.to_owned(),
|
||||
dbpath.to_str().unwrap(),
|
||||
proxies.clone(),
|
||||
|
||||
@ -4,53 +4,37 @@ use std::rc::Rc;
|
||||
use adw::prelude::*;
|
||||
use capnp::capability::Promise;
|
||||
use capnp_rpc::pry;
|
||||
use futures::join;
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::Properties;
|
||||
use gtk::glib::MainContext;
|
||||
use gtk::{gio, glib};
|
||||
use ntfy_daemon::models;
|
||||
use ntfy_daemon::ntfy_capnp::{output_channel, subscription, watch_handle, Status};
|
||||
use ntfy_daemon::ntfy_capnp::{output_channel, subscription, watch_handle};
|
||||
use ntfy_daemon::{models, ConnectionState, ListenerEvent};
|
||||
use tracing::{debug, error, instrument};
|
||||
|
||||
struct TopicWatcher {
|
||||
sub: glib::WeakRef<Subscription>,
|
||||
#[repr(u16)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Status {
|
||||
Down = 0,
|
||||
Degraded = 1,
|
||||
Up = 2,
|
||||
}
|
||||
impl output_channel::Server for TopicWatcher {
|
||||
fn send_message(
|
||||
&mut self,
|
||||
params: output_channel::SendMessageParams,
|
||||
_results: output_channel::SendMessageResults,
|
||||
) -> capnp::capability::Promise<(), capnp::Error> {
|
||||
if let Some(sub) = self.sub.upgrade() {
|
||||
let request = pry!(params.get());
|
||||
let message = pry!(pry!(request.get_message()).to_str());
|
||||
|
||||
let msg: models::Message = serde_json::from_str(message).unwrap();
|
||||
sub.imp().messages.append(&glib::BoxedAnyObject::new(msg));
|
||||
sub.update_unread_count();
|
||||
Promise::ok(())
|
||||
} else {
|
||||
Promise::err(capnp::Error::failed("dead channel".to_string()))
|
||||
}
|
||||
}
|
||||
fn send_status(
|
||||
&mut self,
|
||||
params: output_channel::SendStatusParams,
|
||||
_: output_channel::SendStatusResults,
|
||||
) -> capnp::capability::Promise<(), capnp::Error> {
|
||||
if let Some(sub) = self.sub.upgrade() {
|
||||
let status = pry!(pry!(params.get()).get_status());
|
||||
sub.imp().status.set(status);
|
||||
sub.notify_status();
|
||||
Promise::ok(())
|
||||
} else {
|
||||
Promise::err(capnp::Error::failed("dead channel".to_string()))
|
||||
impl From<u16> for Status {
|
||||
fn from(value: u16) -> Self {
|
||||
match value {
|
||||
0 => Status::Down,
|
||||
1 => Status::Degraded,
|
||||
2 => Status::Up,
|
||||
_ => panic!("Invalid value for Status"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TopicWatcher {
|
||||
fn drop(&mut self) {
|
||||
debug!("Dropped topic watcher");
|
||||
impl From<Status> for u16 {
|
||||
fn from(status: Status) -> Self {
|
||||
status as u16
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +60,7 @@ mod imp {
|
||||
pub unread_count: Cell<u32>,
|
||||
pub read_until: Cell<u64>,
|
||||
pub messages: gio::ListStore,
|
||||
pub client: OnceCell<subscription::Client>,
|
||||
pub client: OnceCell<ntfy_daemon::SubscriptionHandle>,
|
||||
pub remote_handle: RefCell<Option<watch_handle::Client>>,
|
||||
}
|
||||
|
||||
@ -120,7 +104,7 @@ glib::wrapper! {
|
||||
}
|
||||
|
||||
impl Subscription {
|
||||
pub fn new(client: subscription::Client) -> Self {
|
||||
pub fn new(client: ntfy_daemon::SubscriptionHandle) -> Self {
|
||||
let this: Self = glib::Object::builder().build();
|
||||
let imp = this.imp();
|
||||
if let Err(_) = imp.client.set(client) {
|
||||
@ -161,34 +145,56 @@ impl Subscription {
|
||||
|
||||
fn load(&self) -> Promise<(), capnp::Error> {
|
||||
let imp = self.imp();
|
||||
let req_info = imp.client.get().unwrap().get_info_request();
|
||||
let req_messages = {
|
||||
let mut req = imp.client.get().unwrap().watch_request();
|
||||
req.get().set_watcher(capnp_rpc::new_client(TopicWatcher {
|
||||
sub: self.downgrade(),
|
||||
}));
|
||||
req
|
||||
};
|
||||
|
||||
let this = self.clone();
|
||||
Promise::from_future(async move {
|
||||
let info = req_info.send().promise.await?;
|
||||
let info = info.get()?;
|
||||
let remote_subscription = this.imp().client.get().unwrap();
|
||||
let model = remote_subscription.model().await;
|
||||
|
||||
this.init_info(
|
||||
info.get_topic()?.to_str()?,
|
||||
info.get_server()?.to_str()?,
|
||||
info.get_muted(),
|
||||
info.get_read_until(),
|
||||
info.get_display_name()?.to_str()?,
|
||||
&model.topic,
|
||||
&model.server,
|
||||
model.muted,
|
||||
model.read_until,
|
||||
&model.display_name,
|
||||
);
|
||||
|
||||
let message_stream = req_messages.send().promise.await?;
|
||||
let handle = message_stream.get()?.get_handle()?;
|
||||
this.imp().remote_handle.replace(Some(handle));
|
||||
let (prev_msgs, mut rx) = remote_subscription.attach().await;
|
||||
|
||||
for msg in prev_msgs {
|
||||
this.handle_event(msg);
|
||||
}
|
||||
|
||||
while let Ok(ev) = rx.recv().await {
|
||||
this.handle_event(ev);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_event(&self, ev: ListenerEvent) {
|
||||
match dbg!(ev) {
|
||||
ListenerEvent::Message(msg) => {
|
||||
self.imp().messages.append(&glib::BoxedAnyObject::new(msg));
|
||||
self.update_unread_count();
|
||||
}
|
||||
ListenerEvent::ConnectionStateChanged(connection_state) => {
|
||||
self.set_connection_state(connection_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_connection_state(&self, state: ConnectionState) {
|
||||
let status = match state {
|
||||
ConnectionState::Unitialized => Status::Degraded,
|
||||
ConnectionState::Connected => Status::Up,
|
||||
ConnectionState::Reconnecting { .. } => Status::Degraded,
|
||||
};
|
||||
self.imp().status.set(status);
|
||||
dbg!(status);
|
||||
self.notify_status();
|
||||
}
|
||||
|
||||
fn _set_display_name(&self, value: String) {
|
||||
let imp = self.imp();
|
||||
let value = if value.is_empty() {
|
||||
@ -209,18 +215,15 @@ impl Subscription {
|
||||
})
|
||||
}
|
||||
|
||||
fn send_updated_info(&self) -> Promise<(), anyhow::Error> {
|
||||
async fn send_updated_info(&self) -> anyhow::Result<()> {
|
||||
let imp = self.imp();
|
||||
let mut req = imp.client.get().unwrap().update_info_request();
|
||||
let mut val = pry!(req.get().get_value());
|
||||
val.set_muted(imp.muted.get());
|
||||
val.set_display_name(imp.display_name.borrow().as_str().into());
|
||||
val.set_read_until(imp.read_until.get());
|
||||
Promise::from_future(async move {
|
||||
debug!("sending update_info");
|
||||
req.send().promise.await?;
|
||||
Ok(())
|
||||
})
|
||||
imp.client.get().unwrap().update_info(
|
||||
models::Subscription::builder(self.topic())
|
||||
.display_name((imp.display_name.borrow().to_string()))
|
||||
.muted(imp.muted.get())
|
||||
.build().map_err(|e| anyhow::anyhow!("invalid subscription data"))?
|
||||
).await?;
|
||||
Ok(())
|
||||
}
|
||||
fn last_message(list: &gio::ListStore) -> Option<models::Message> {
|
||||
let n = list.n_items();
|
||||
@ -249,51 +252,38 @@ impl Subscription {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
pub fn flag_all_as_read(&self) -> Promise<(), anyhow::Error> {
|
||||
pub async fn flag_all_as_read(&self) -> anyhow::Result<()> {
|
||||
let imp = self.imp();
|
||||
let Some(value) = Self::last_message(&imp.messages)
|
||||
.map(|last| last.time)
|
||||
.filter(|time| *time > self.imp().read_until.get())
|
||||
else {
|
||||
return Promise::ok(());
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let this = self.clone();
|
||||
Promise::from_future(async move {
|
||||
let mut req = this.imp().client.get().unwrap().update_read_until_request();
|
||||
req.get().set_value(value);
|
||||
req.send().promise.await?;
|
||||
this.imp().read_until.set(value);
|
||||
this.update_unread_count();
|
||||
Ok(())
|
||||
})
|
||||
this.imp().client.get().unwrap().update_read_until(value).await?;
|
||||
this.imp().read_until.set(value);
|
||||
this.update_unread_count();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn publish_msg(&self, mut msg: models::Message) -> Promise<(), anyhow::Error> {
|
||||
pub async fn publish_msg(&self, mut msg: models::Message) -> anyhow::Result<()> {
|
||||
let imp = self.imp();
|
||||
let json = {
|
||||
msg.topic = self.topic();
|
||||
serde_json::to_string(&msg)
|
||||
serde_json::to_string(&msg)?
|
||||
};
|
||||
let mut req = imp.client.get().unwrap().publish_request();
|
||||
req.get().set_message(pry!(json).as_str().into());
|
||||
|
||||
Promise::from_future(async move {
|
||||
debug!("sending publish");
|
||||
req.send().promise.await?;
|
||||
Ok(())
|
||||
})
|
||||
imp.client.get().unwrap().publish(json).await?;
|
||||
Ok(())
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub fn clear_notifications(&self) -> Promise<(), anyhow::Error> {
|
||||
pub async fn clear_notifications(&self) -> anyhow::Result<()> {
|
||||
let imp = self.imp();
|
||||
let req = imp.client.get().unwrap().clear_notifications_request();
|
||||
let this = self.clone();
|
||||
Promise::from_future(async move {
|
||||
debug!("sending clear_notifications");
|
||||
req.send().promise.await?;
|
||||
this.imp().messages.remove_all();
|
||||
Ok(())
|
||||
})
|
||||
imp.client.get().unwrap().clear_notifications().await?;
|
||||
self.imp().messages.remove_all();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn nice_status(&self) -> Status {
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
use std::cell::Cell;
|
||||
use std::cell::OnceCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use adw::prelude::*;
|
||||
use adw::subclass::prelude::*;
|
||||
use futures::prelude::*;
|
||||
use gtk::{gio, glib};
|
||||
use ntfy_daemon::models;
|
||||
use ntfy_daemon::ntfy_capnp::{system_notifier, Status};
|
||||
use ntfy_daemon::Ntfy;
|
||||
use ntfy_daemon::ntfy_capnp::system_notifier;
|
||||
use ntfy_daemon::NtfyHandle;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::application::NotifyApplication;
|
||||
use crate::config::{APP_ID, PROFILE};
|
||||
use crate::error::*;
|
||||
use crate::subscription::Status;
|
||||
use crate::subscription::Subscription;
|
||||
use crate::widgets::*;
|
||||
|
||||
@ -53,7 +55,7 @@ mod imp {
|
||||
pub send_btn: TemplateChild<gtk::Button>,
|
||||
#[template_child]
|
||||
pub code_btn: TemplateChild<gtk::Button>,
|
||||
pub notifier: OnceCell<Ntfy>,
|
||||
pub notifier: OnceCell<NtfyHandle>,
|
||||
pub conn: OnceCell<gio::SocketConnection>,
|
||||
pub settings: gio::Settings,
|
||||
pub banner_binding: Cell<Option<(Subscription, glib::SignalHandlerId)>>,
|
||||
@ -139,7 +141,7 @@ mod imp {
|
||||
});
|
||||
klass.install_action("win.clear-notifications", None, |this, _, _| {
|
||||
this.selected_subscription().map(|sub| {
|
||||
this.error_boundary().spawn(sub.clear_notifications());
|
||||
this.error_boundary().spawn(async move {sub.clear_notifications().await});
|
||||
});
|
||||
});
|
||||
//klass.bind_template_instance_callbacks();
|
||||
@ -191,7 +193,7 @@ glib::wrapper! {
|
||||
}
|
||||
|
||||
impl NotifyWindow {
|
||||
pub fn new(app: &NotifyApplication, notifier: Ntfy) -> Self {
|
||||
pub fn new(app: &NotifyApplication, notifier: NtfyHandle) -> Self {
|
||||
let obj: Self = glib::Object::builder().property("application", app).build();
|
||||
|
||||
if let Err(_) = obj.imp().notifier.set(notifier) {
|
||||
@ -212,24 +214,25 @@ impl NotifyWindow {
|
||||
fn connect_entry_and_send_btn(&self) {
|
||||
let imp = self.imp();
|
||||
let this = self.clone();
|
||||
let entry = imp.entry.clone();
|
||||
let publish = move || {
|
||||
let p = this
|
||||
.selected_subscription()
|
||||
|
||||
imp.entry.connect_activate(move |_| this.publish_msg());
|
||||
let this = self.clone();
|
||||
imp.send_btn.connect_clicked(move |_| this.publish_msg());
|
||||
}
|
||||
fn publish_msg(&self) {
|
||||
let entry = self.imp().entry.clone();
|
||||
let this = self.clone();
|
||||
|
||||
entry.error_boundary().spawn(async move {
|
||||
this.selected_subscription()
|
||||
.unwrap()
|
||||
.publish_msg(models::Message {
|
||||
message: Some(entry.text().as_str().to_string()),
|
||||
..models::Message::default()
|
||||
});
|
||||
|
||||
entry.error_boundary().spawn(async move {
|
||||
p.await?;
|
||||
Ok(())
|
||||
});
|
||||
};
|
||||
let publishc = publish.clone();
|
||||
imp.entry.connect_activate(move |_| publishc());
|
||||
imp.send_btn.connect_clicked(move |_| publish());
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
fn connect_code_btn(&self) {
|
||||
let imp = self.imp();
|
||||
@ -261,26 +264,27 @@ impl NotifyWindow {
|
||||
}
|
||||
|
||||
fn add_subscription(&self, sub: models::Subscription) {
|
||||
// let mut req = self.notifier().subscribe_request();
|
||||
let this = self.clone();
|
||||
self.error_boundary().spawn(async move {
|
||||
let sub = this
|
||||
.notifier()
|
||||
.subscribe(&sub.server, &sub.topic)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
anyhow::anyhow!(err.into_iter().map(|x| x.to_string()).collect::<String>())
|
||||
})?;
|
||||
let imp = this.imp();
|
||||
|
||||
// req.get().set_server(sub.server.as_str().into());
|
||||
// req.get().set_topic(sub.topic.as_str().into());
|
||||
// let res = req.send();
|
||||
// let this = self.clone();
|
||||
// self.error_boundary().spawn(async move {
|
||||
// let imp = this.imp();
|
||||
// Subscription::new will use the pipelined client to retrieve info about the subscription
|
||||
let subscription = Subscription::new(sub);
|
||||
// We want to still check if there were any errors adding the subscription.
|
||||
|
||||
// // Subscription::new will use the pipelined client to retrieve info about the subscription
|
||||
// let subscription = Subscription::new(res.pipeline.get_subscription());
|
||||
// // We want to still check if there were any errors adding the subscription.
|
||||
// res.promise.await?;
|
||||
|
||||
// imp.subscription_list_model.append(&subscription);
|
||||
// let i = imp.subscription_list_model.n_items() - 1;
|
||||
// let row = imp.subscription_list.row_at_index(i as i32);
|
||||
// imp.subscription_list.select_row(row.as_ref());
|
||||
// Ok(())
|
||||
// });
|
||||
imp.subscription_list_model.append(&subscription);
|
||||
let i = imp.subscription_list_model.n_items() - 1;
|
||||
let row = imp.subscription_list.row_at_index(i as i32);
|
||||
imp.subscription_list.select_row(row.as_ref());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn unsubscribe(&self) {
|
||||
@ -303,7 +307,7 @@ impl NotifyWindow {
|
||||
// Ok(())
|
||||
// });
|
||||
}
|
||||
fn notifier(&self) -> &Ntfy {
|
||||
fn notifier(&self) -> &NtfyHandle {
|
||||
self.imp().notifier.get().unwrap()
|
||||
}
|
||||
fn selected_subscription(&self) -> Option<Subscription> {
|
||||
@ -314,32 +318,31 @@ impl NotifyWindow {
|
||||
.and_downcast::<Subscription>()
|
||||
}
|
||||
fn bind_message_list(&self) {
|
||||
// let imp = self.imp();
|
||||
let imp = self.imp();
|
||||
|
||||
// imp.subscription_list
|
||||
// .bind_model(Some(&imp.subscription_list_model), |obj| {
|
||||
// let sub = obj.downcast_ref::<Subscription>().unwrap();
|
||||
imp.subscription_list
|
||||
.bind_model(Some(&imp.subscription_list_model), |obj| {
|
||||
let sub = obj.downcast_ref::<Subscription>().unwrap();
|
||||
|
||||
// Self::build_subscription_row(&sub).upcast()
|
||||
// });
|
||||
Self::build_subscription_row(&sub).upcast()
|
||||
});
|
||||
|
||||
// let this = self.clone();
|
||||
// imp.subscription_list.connect_row_selected(move |_, _row| {
|
||||
// this.selected_subscription_changed(this.selected_subscription().as_ref());
|
||||
// });
|
||||
let this = self.clone();
|
||||
imp.subscription_list.connect_row_selected(move |_, _row| {
|
||||
this.selected_subscription_changed(this.selected_subscription().as_ref());
|
||||
});
|
||||
|
||||
// let this = self.clone();
|
||||
// let req = self.notifier().list_subscriptions_request();
|
||||
// let res = req.send();
|
||||
// self.error_boundary().spawn(async move {
|
||||
// let list = res.promise.await?;
|
||||
// let list = list.get()?.get_list()?;
|
||||
// let imp = this.imp();
|
||||
// for sub in list {
|
||||
// imp.subscription_list_model.append(&Subscription::new(sub?));
|
||||
// }
|
||||
// Ok(())
|
||||
// });
|
||||
let this = self.clone();
|
||||
self.error_boundary().spawn(async move {
|
||||
glib::timeout_future_seconds(1).await;
|
||||
let list = this.notifier().list_subscriptions().await?;
|
||||
for sub in list {
|
||||
this.imp()
|
||||
.subscription_list_model
|
||||
.append(&Subscription::new(sub));
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
fn update_banner(&self, sub: Option<&Subscription>) {
|
||||
let imp = self.imp();
|
||||
@ -403,7 +406,7 @@ impl NotifyWindow {
|
||||
{
|
||||
self.selected_subscription().map(|sub| {
|
||||
self.error_boundary()
|
||||
.spawn(sub.flag_all_as_read().map_err(|e| e.into()));
|
||||
.spawn(async move {sub.flag_all_as_read().await});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user