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)); } }