diff --git a/src/widgets/advanced_message_dialog.rs b/src/widgets/advanced_message_dialog.rs new file mode 100644 index 0000000..9271e77 --- /dev/null +++ b/src/widgets/advanced_message_dialog.rs @@ -0,0 +1,226 @@ +use std::cell::OnceCell; + +use adw::prelude::*; +use adw::subclass::prelude::*; +use gsv::prelude::*; +use gtk::{gio, glib}; + +use crate::subscription::Subscription; +use crate::widgets::*; + +mod imp { + use super::*; + + #[derive(Debug, Default)] + pub struct AdvancedMessageDialog { + pub subscription: OnceCell, + } + + #[glib::object_subclass] + impl ObjectSubclass for AdvancedMessageDialog { + const NAME: &'static str = "AdvancedMessageDialog"; + type Type = super::AdvancedMessageDialog; + type ParentType = adw::Window; + } + + impl ObjectImpl for AdvancedMessageDialog {} + impl WidgetImpl for AdvancedMessageDialog {} + impl WindowImpl for AdvancedMessageDialog {} + impl AdwWindowImpl for AdvancedMessageDialog {} +} + +glib::wrapper! { + pub struct AdvancedMessageDialog(ObjectSubclass) + @extends gtk::Widget, gtk::Window, adw::Window; +} + +impl AdvancedMessageDialog { + pub fn new( + parent: &impl IsA, + subscription: Subscription, + message: String, + ) -> Self { + let this: Self = glib::Object::new(); + this.set_transient_for(Some(parent)); + this.set_modal(true); + this.set_default_height(400); + this.imp().subscription.set(subscription).unwrap(); + this.build_ui( + this.imp().subscription.get().unwrap().topic().clone(), + message, + ); + this + } + fn build_ui(&self, topic: String, message: String) { + let this = self.clone(); + relm4_macros::view! { + content = &adw::ToolbarView { + add_top_bar = &adw::HeaderBar {}, + #[wrap(Some)] + set_content: toast_overlay = &adw::ToastOverlay { + #[wrap(Some)] + set_child = &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_natural_wrap_mode: gtk::NaturalWrapMode::None, + set_xalign: 0.0, + set_halign: gtk::Align::Start, + set_wrap_mode: gtk::pango::WrapMode::WordChar, + set_wrap: true, + }, + append = >k::Label { + add_css_class: "heading", + set_label: "JSON", + set_xalign: 0.0, + set_halign: gtk::Align::Start, + }, + append = >k::ScrolledWindow { + #[wrap(Some)] + set_child: text_view = &gsv::View { + add_css_class: "code", + set_tab_width: 4, + set_indent_width: 2, + set_auto_indent: true, + set_top_margin: 4, + set_bottom_margin: 4, + set_left_margin: 4, + set_right_margin: 4, + set_hexpand: true, + set_vexpand: true, + set_monospace: true, + set_background_pattern: gsv::BackgroundPatternType::Grid + }, + }, + append = >k::Label { + add_css_class: "heading", + set_label: "Snippets", + set_xalign: 0.0, + set_halign: gtk::Align::Start, + }, + append = >k::FlowBox { + set_column_spacing: 4, + set_row_spacing: 4, + append = >k::Button { + add_css_class: "pill", + add_css_class: "small", + set_label: "Title", + connect_clicked[text_view] => move |_| { + text_view.buffer().insert_at_cursor(r#""title": "Title of your message""#) + } + }, + append = >k::Button { + add_css_class: "pill", + add_css_class: "small", + set_label: "Tags", + connect_clicked[text_view] => move |_| { + text_view.buffer().insert_at_cursor(r#""tags": ["warning","cd"]"#) + } + }, + append = >k::Button { + add_css_class: "pill", + add_css_class: "small", + set_label: "Priority", + connect_clicked[text_view] => move |_| { + text_view.buffer().insert_at_cursor(r#""priority": 5"#) + } + }, + append = >k::Button { + add_css_class: "pill", + add_css_class: "small", + set_label: "View Action", + connect_clicked[text_view] => move |_| { + text_view.buffer().insert_at_cursor(r#""actions": [ + { + "action": "view", + "label": "torvalds boosted your toot", + "url": "https://joinmastodon.org" + } + ]"#) + } + }, + append = >k::Button { + add_css_class: "pill", + add_css_class: "small", + set_label: "HTTP Action", + connect_clicked[text_view] => move |_| { + text_view.buffer().insert_at_cursor(r#""actions": [ + { + "action": "http", + "label": "Turn off lights", + "method": "post", + "url": "https://api.example.com/lights", + "body": "OFF" + } + ]"#) + } + }, + append = >k::Button { + add_css_class: "circular", + add_css_class: "small", + set_label: "?", + connect_clicked[this] => move |_| { + gtk::UriLauncher::new("https://docs.ntfy.sh/publish/#publish-as-json").launch( + Some(&this), + gio::Cancellable::NONE, + |_| {} + ); + } + }, + }, + append = >k::Button { + set_margin_top: 8, + set_margin_bottom: 8, + add_css_class: "suggested-action", + add_css_class: "pill", + set_label: "Send", + connect_clicked[this, toast_overlay, 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.imp().subscription.get().unwrap() + .publish_msg(msg).await + }; + toast_overlay.spawn_with_near_toast(f); + } + } + } + } + } + } + } + + let lang = gsv::LanguageManager::default().language("json").unwrap(); + let buffer = gsv::Buffer::with_language(&lang); + buffer.set_text(&format!( + r#"{{ + "topic": "{topic}", + "message": "{message}" +}}"# + )); + text_view.set_buffer(Some(&buffer)); + + let manager = adw::StyleManager::default(); + let scheme_name = if manager.is_dark() { + "solarized-dark" + } else { + "solarized-light" + }; + let scheme = gsv::StyleSchemeManager::default().scheme(scheme_name); + buffer.set_style_scheme(scheme.as_ref()); + this.set_content(Some(&content)); + } +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index e243c2b..fbcf4f5 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,8 +1,10 @@ mod add_subscription_dialog; +mod advanced_message_dialog; mod message_row; mod subscription_info_dialog; mod window; pub use add_subscription_dialog::AddSubscriptionDialog; +pub use advanced_message_dialog::*; pub use message_row::*; pub use subscription_info_dialog::SubscriptionInfoDialog; pub use window::*; diff --git a/src/widgets/window.rs b/src/widgets/window.rs index 77013a4..3039a12 100644 --- a/src/widgets/window.rs +++ b/src/widgets/window.rs @@ -4,7 +4,6 @@ use std::cell::OnceCell; use adw::prelude::*; use adw::subclass::prelude::*; use futures::prelude::*; -use gsv::prelude::*; use gtk::{gio, glib}; use ntfy_daemon::models; use ntfy_daemon::ntfy_capnp::{system_notifier, Status}; @@ -260,192 +259,13 @@ impl NotifyWindow { let imp = self.imp(); let this = self.clone(); imp.code_btn.connect_clicked(move |_| { - this.show_docs_dialog(); + let this = this.clone(); + this.selected_subscription().map(move |sub| { + AdvancedMessageDialog::new(&this, sub, this.imp().entry.text().to_string()) + .present() + }); }); } - 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(); - relm4_macros::view! { - window = adw::Window { - set_default_height: 400, - set_modal: true, - set_transient_for: Some(self), - #[wrap(Some)] - set_content = &adw::ToolbarView { - add_top_bar = &adw::HeaderBar {}, - #[wrap(Some)] - set_content: toast_overlay = &adw::ToastOverlay { - #[wrap(Some)] - set_child = &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_natural_wrap_mode: gtk::NaturalWrapMode::None, - set_xalign: 0.0, - set_halign: gtk::Align::Start, - set_wrap_mode: gtk::pango::WrapMode::WordChar, - set_wrap: true, - }, - append = >k::Label { - add_css_class: "heading", - set_label: "JSON", - set_xalign: 0.0, - set_halign: gtk::Align::Start, - }, - append = >k::ScrolledWindow { - #[wrap(Some)] - set_child: text_view = &gsv::View { - add_css_class: "code", - set_tab_width: 4, - set_indent_width: 2, - set_auto_indent: true, - set_top_margin: 4, - set_bottom_margin: 4, - set_left_margin: 4, - set_right_margin: 4, - set_hexpand: true, - set_vexpand: true, - set_monospace: true, - set_background_pattern: gsv::BackgroundPatternType::Grid - }, - }, - append = >k::Label { - add_css_class: "heading", - set_label: "Snippets", - set_xalign: 0.0, - set_halign: gtk::Align::Start, - }, - append = >k::FlowBox { - set_column_spacing: 4, - set_row_spacing: 4, - append = >k::Button { - add_css_class: "pill", - add_css_class: "small", - set_label: "Title", - connect_clicked[text_view] => move |_| { - text_view.buffer().insert_at_cursor(r#""title": "Title of your message""#) - } - }, - append = >k::Button { - add_css_class: "pill", - add_css_class: "small", - set_label: "Tags", - connect_clicked[text_view] => move |_| { - text_view.buffer().insert_at_cursor(r#""tags": ["warning","cd"]"#) - } - }, - append = >k::Button { - add_css_class: "pill", - add_css_class: "small", - set_label: "Priority", - connect_clicked[text_view] => move |_| { - text_view.buffer().insert_at_cursor(r#""priority": 5"#) - } - }, - append = >k::Button { - add_css_class: "pill", - add_css_class: "small", - set_label: "View Action", - connect_clicked[text_view] => move |_| { - text_view.buffer().insert_at_cursor(r#""actions": [ - { - "action": "view", - "label": "torvalds boosted your toot", - "url": "https://joinmastodon.org" - } - ]"#) - } - }, - append = >k::Button { - add_css_class: "pill", - add_css_class: "small", - set_label: "HTTP Action", - connect_clicked[text_view] => move |_| { - text_view.buffer().insert_at_cursor(r#""actions": [ - { - "action": "http", - "label": "Turn off lights", - "method": "post", - "url": "https://api.example.com/lights", - "body": "OFF" - } - ]"#) - } - }, - append = >k::Button { - add_css_class: "circular", - add_css_class: "small", - set_label: "?", - connect_clicked[this] => move |_| { - gtk::UriLauncher::new("https://docs.ntfy.sh/publish/#publish-as-json").launch( - Some(&this), - gio::Cancellable::NONE, - |_| {} - ); - } - }, - }, - append = >k::Button { - set_margin_top: 8, - set_margin_bottom: 8, - add_css_class: "suggested-action", - add_css_class: "pill", - set_label: "Send", - connect_clicked[this, toast_overlay, 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 - }; - toast_overlay.spawn_with_near_toast(f); - } - } - } - } - } - } - } - } - - let lang = gsv::LanguageManager::default().language("json").unwrap(); - let buffer = gsv::Buffer::with_language(&lang); - buffer.set_text(&format!( - r#"{{ - "topic": "{topic}", - "message": "{message}" -}}"# - )); - text_view.set_buffer(Some(&buffer)); - - let manager = adw::StyleManager::default(); - let scheme_name = if manager.is_dark() { - "solarized-dark" - } else { - "solarized-light" - }; - let scheme = gsv::StyleSchemeManager::default().scheme(scheme_name); - buffer.set_style_scheme(scheme.as_ref()); - - window.present(); - } fn show_subscription_info(&self) { let sub = SubscriptionInfoDialog::new(self.selected_subscription().unwrap()); sub.set_transient_for(Some(self));