diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index dff7bdd..f4e7a8c 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -11,5 +11,6 @@
../icons/dice3-symbolic.svg
../icons/paper-plane-symbolic.svg
+ ../icons/code-symbolic.svg
diff --git a/data/resources/ui/window.blp b/data/resources/ui/window.blp
index a0cd37d..5cce952 100644
--- a/data/resources/ui/window.blp
+++ b/data/resources/ui/window.blp
@@ -133,7 +133,13 @@ template $NotifyWindow : Adw.ApplicationWindow {
margin-end: 8;
Adw.Clamp {
Gtk.Box {
- spacing: 8;
+ spacing: 4;
+ Gtk.Button code_btn {
+ styles [
+ "circular"
+ ]
+ icon-name: "code-symbolic";
+ }
Entry entry {
placeholder-text: "Message...";
hexpand: true;
diff --git a/ntfy-daemon/src/models.rs b/ntfy-daemon/src/models.rs
index 5b90b4c..df17d9c 100644
--- a/ntfy-daemon/src/models.rs
+++ b/ntfy-daemon/src/models.rs
@@ -27,6 +27,7 @@ pub fn validate_topic(topic: &str) -> Result<&str, Error> {
pub struct Message {
pub topic: String,
pub message: Option,
+ #[serde(default = "Default::default")]
pub time: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option,
diff --git a/src/subscription.rs b/src/subscription.rs
index 2d1e66f..60cd901 100644
--- a/src/subscription.rs
+++ b/src/subscription.rs
@@ -274,17 +274,12 @@ impl Subscription {
Ok(())
})
}
- pub fn publish(&self, message: &str) -> Promise<(), capnp::Error> {
+ pub fn publish_msg(&self, mut msg: models::Message) -> Promise<(), capnp::Error> {
let imp = self.imp();
let mut req = imp.client.get().unwrap().publish_request();
- let msg = serde_json::to_string(&models::Message {
- topic: self.topic(),
- message: Some(message.to_string()),
- ..models::Message::default()
- })
- .map_err(|e| capnp::Error::failed(e.to_string()));
-
- req.get().set_message(&pry!(msg));
+ msg.topic = self.topic();
+ let json = serde_json::to_string(&msg).map_err(|e| capnp::Error::failed(e.to_string()));
+ req.get().set_message(&pry!(json));
Promise::from_future(async move {
let _span = debug_span!("publish").entered();
diff --git a/src/widgets/window.rs b/src/widgets/window.rs
index c10c089..e729f67 100644
--- a/src/widgets/window.rs
+++ b/src/widgets/window.rs
@@ -74,6 +74,8 @@ mod imp {
pub banner: TemplateChild,
#[template_child]
pub send_btn: TemplateChild,
+ #[template_child]
+ pub code_btn: TemplateChild,
pub notifier: OnceCell,
pub conn: OnceCell,
pub settings: gio::Settings,
@@ -102,6 +104,7 @@ mod imp {
conn: Default::default(),
banner_binding: Default::default(),
send_btn: Default::default(),
+ code_btn: Default::default(),
};
this
@@ -214,6 +217,7 @@ impl NotifyWindow {
obj.load_window_size();
obj.bind_message_list();
obj.connect_entry_and_send_btn();
+ obj.connect_code_btn();
obj.connect_items_changed();
obj.selected_subscription_changed(None);
obj.bind_flag_read();
@@ -228,13 +232,112 @@ impl NotifyWindow {
let p = this
.selected_subscription()
.unwrap()
- .publish(entry.text().as_str());
+ .publish_msg(models::Message {
+ message: Some(entry.text().as_str().to_string()),
+ ..models::Message::default()
+ });
+
entry.spawn_with_near_toast(async move { p.await });
};
let publishc = publish.clone();
imp.entry.connect_activate(move |_| publishc());
imp.send_btn.connect_clicked(move |_| publish());
}
+ fn connect_code_btn(&self) {
+ let imp = self.imp();
+ let this = self.clone();
+ imp.code_btn.connect_clicked(move |_| {
+ this.show_docs_dialog();
+ });
+ }
+ fn show_docs_dialog(&self) {
+ let imp = self.imp();
+ let this = self.clone();
+ let topic = self.selected_subscription().unwrap().topic();
+ let message = imp.entry.text();
+ let buffer = gtk::TextBuffer::new(None);
+ buffer.set_text(&format!(
+ r#"{{
+"topic": "{topic}",
+"message": "{message}"
+}}"#
+ ));
+ relm4_macros::view! {
+ window = adw::Window {
+ set_modal: true,
+ set_transient_for: Some(self),
+ #[wrap(Some)]
+ set_content = &adw::ToolbarView {
+ add_top_bar = &adw::HeaderBar {},
+ #[wrap(Some)]
+ set_content = &adw::Clamp {
+ #[wrap(Some)]
+ set_child = >k::Box {
+ set_margin_top: 8,
+ set_margin_bottom: 8,
+ set_margin_start: 8,
+ set_margin_end: 8,
+ set_spacing: 8,
+ set_orientation: gtk::Orientation::Vertical,
+ append = >k::Label {
+ set_label: "Here you can manually build the JSON message you want to POST to this topic",
+ set_xalign: 0.0,
+ set_halign: gtk::Align::Start,
+ set_wrap_mode: gtk::pango::WrapMode::WordChar,
+ set_wrap: true,
+ },
+ append: text_view = >k::TextView {
+ set_top_margin: 8,
+ set_bottom_margin: 8,
+ set_left_margin: 8,
+ set_right_margin: 8,
+ set_hexpand: true,
+ set_vexpand: true,
+ set_buffer: Some(&buffer),
+ },
+ append = >k::Box {
+ set_halign: gtk::Align::Center,
+ set_spacing: 8,
+ append = >k::Button {
+ add_css_class: "pill",
+ set_label: "Documentation",
+ connect_clicked[this] => move |_| {
+ gtk::UriLauncher::new("https://docs.ntfy.sh/publish/#publish-as-json").launch(
+ Some(&this),
+ gio::Cancellable::NONE,
+ |_| {}
+ );
+ }
+ },
+ append = >k::Button {
+ add_css_class: "suggested-action",
+ add_css_class: "pill",
+ set_label: "Send",
+ connect_clicked[this, text_view] => move |_| {
+ let thisc = this.clone();
+ let text_view = text_view.clone();
+ let f = async move {
+ let buffer = text_view.buffer();
+ let msg = serde_json::from_str(&buffer.text(
+ &mut buffer.start_iter(),
+ &mut buffer.end_iter(),
+ true,
+ )).map_err(|e| capnp::Error::failed(e.to_string()))?;
+ thisc.selected_subscription()
+ .unwrap()
+ .publish_msg(msg).await
+ };
+ this.spawn_with_near_toast(f);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ window.present();
+ }
fn show_subscription_info(&self) {
let sub = SubscriptionInfoDialog::new(self.selected_subscription().unwrap());
sub.set_transient_for(Some(self));
@@ -362,7 +465,9 @@ impl NotifyWindow {
MessageRow::new(msg.clone()).upcast()
});
- imp.subscription_menu_btn.set_visible(true);
+ imp.subscription_menu_btn.set_sensitive(true);
+ imp.send_btn.set_sensitive(true);
+ imp.code_btn.set_sensitive(true);
imp.entry.set_sensitive(true);
let this = self.clone();
@@ -380,7 +485,9 @@ impl NotifyWindow {
} else {
imp.message_list
.bind_model(gio::ListModel::NONE, |_| adw::Bin::new().into());
- imp.subscription_menu_btn.set_visible(false);
+ imp.subscription_menu_btn.set_sensitive(false);
+ imp.code_btn.set_sensitive(false);
+ imp.send_btn.set_sensitive(false);
imp.entry.set_sensitive(false);
}
}