rewrite subscription complete

This commit is contained in:
ranfdev
2024-11-20 20:20:44 +01:00
parent 3afe79bc82
commit 0a083a6e2b
13 changed files with 1084 additions and 525 deletions

View File

@ -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 {