Improve manual message dialog, improve message bar design

This commit is contained in:
ranfdev
2023-10-25 22:32:08 +02:00
parent 10ee058277
commit b212c99730
6 changed files with 221 additions and 58 deletions

36
Cargo.lock generated
View File

@ -1658,6 +1658,7 @@ dependencies = [
"relm4-macros", "relm4-macros",
"serde", "serde",
"serde_json", "serde_json",
"sourceview5",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"ureq", "ureq",
@ -2397,6 +2398,41 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "sourceview5"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88c5f976a113e947bc5ec67758b2960c0db4ca76f80fb410d7cd86cd456d9ee5"
dependencies = [
"futures-channel",
"futures-core",
"gdk-pixbuf",
"gdk4",
"gio",
"glib",
"gtk4",
"libc",
"pango",
"sourceview5-sys",
]
[[package]]
name = "sourceview5-sys"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29637cccd56075a37ba72c0cc8b8d599dbc1d857e30dadea97eaacbc29b7fd46"
dependencies = [
"gdk-pixbuf-sys",
"gdk4-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"gtk4-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.5.2" version = "0.5.2"

View File

@ -15,7 +15,8 @@ members = [
[dependencies] [dependencies]
ntfy-daemon = { path = "./ntfy-daemon" } ntfy-daemon = { path = "./ntfy-daemon" }
gettext-rs = { version = "0.7", features = ["gettext-system"] } gettext-rs = { version = "0.7", features = ["gettext-system"] }
gtk = { version = "0.7", package = "gtk4", features = ["v4_12", "blueprint"] } gtk = { version = "0.7", package = "gtk4", features = ["gnome_45"] }
gsv = { package = "sourceview5", version = "0.7" }
once_cell = "1.14" once_cell = "1.14"
tracing = "0.1.37" tracing = "0.1.37"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"

View File

@ -3,10 +3,17 @@
font-weight: bold; font-weight: bold;
} }
button.small {
padding: 6px 12px;
min-height: 0px;
min-width: 0px;
font-size: 14px;
}
.chip { .chip {
min-height: 16px; min-height: 16px;
min-width: 16px; min-width: 16px;
padding: 3px 5px; padding: 2px 5px;
color: @theme_fg_color; color: @theme_fg_color;
border-radius: 8px; border-radius: 8px;
} }
@ -29,3 +36,23 @@
.chip--small { .chip--small {
font-size: 0.8rem; font-size: 0.8rem;
} }
.sourceview {
padding: 4px 8px;
}
.code {
border-radius: 12px;
border: 1px solid @borders;
}
.message_bar {
padding: 2px 2px;
background-color: @sidebar_bg_color;
border-radius: 24px;
}
.message_bar entry {
background-color: @sidebar_bg_color;
border-radius: 12px;
}

View File

@ -127,16 +127,19 @@ template $NotifyWindow : Adw.ApplicationWindow {
}; };
[bottom] [bottom]
Adw.Bin { Adw.Bin {
margin-top: 8; margin-top: 4;
margin-bottom: 8; margin-bottom: 4;
margin-start: 8; margin-start: 4;
margin-end: 8; margin-end: 4;
Adw.Clamp { Adw.Clamp {
Gtk.Box { Gtk.Box {
spacing: 4; styles [
"message_bar"
]
Gtk.Button code_btn { Gtk.Button code_btn {
styles [ styles [
"circular" "circular",
"flat"
] ]
icon-name: "code-symbolic"; icon-name: "code-symbolic";
} }

View File

@ -14,6 +14,7 @@ base_id = 'com.ranfdev.Notify'
dependency('glib-2.0', version: '>= 2.66') dependency('glib-2.0', version: '>= 2.66')
dependency('gio-2.0', version: '>= 2.66') dependency('gio-2.0', version: '>= 2.66')
dependency('gtk4', version: '>= 4.0.0') dependency('gtk4', version: '>= 4.0.0')
dependency('gtksourceview-5', version: '>= 5.0.0')
glib_compile_resources = find_program('glib-compile-resources', required: true) glib_compile_resources = find_program('glib-compile-resources', required: true)
glib_compile_schemas = find_program('glib-compile-schemas', required: true) glib_compile_schemas = find_program('glib-compile-schemas', required: true)

View File

@ -4,6 +4,7 @@ use std::cell::OnceCell;
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use futures::prelude::*; use futures::prelude::*;
use gsv::prelude::*;
use gtk::{gio, glib}; use gtk::{gio, glib};
use ntfy_daemon::models; use ntfy_daemon::models;
use ntfy_daemon::ntfy_capnp::{system_notifier, Status}; use ntfy_daemon::ntfy_capnp::{system_notifier, Status};
@ -26,13 +27,17 @@ impl<W: glib::IsA<gtk::Widget>> SpawnWithToast for W {
&self, &self,
f: impl Future<Output = Result<T, R>> + 'static, f: impl Future<Output = Result<T, R>> + 'static,
) { ) {
let p: Option<NotifyWindow> = self.ancestor(NotifyWindow::static_type()).and_downcast(); let toast_overlay: Option<adw::ToastOverlay> = self
.ancestor(adw::ToastOverlay::static_type())
.and_downcast();
let win: Option<NotifyWindow> = self.ancestor(NotifyWindow::static_type()).and_downcast();
glib::MainContext::default().spawn_local(async move { glib::MainContext::default().spawn_local(async move {
if let Err(e) = f.await { if let Err(e) = f.await {
if let Some(p) = p { if let Some(o) = toast_overlay
p.imp() .as_ref()
.toast_overlay .or_else(|| win.as_ref().map(|win| win.imp().toast_overlay.as_ref()))
.add_toast(adw::Toast::builder().title(&e.to_string()).build()) {
o.add_toast(adw::Toast::builder().title(&e.to_string()).build())
} }
} }
}); });
@ -255,13 +260,6 @@ impl NotifyWindow {
let this = self.clone(); let this = self.clone();
let topic = self.selected_subscription().unwrap().topic(); let topic = self.selected_subscription().unwrap().topic();
let message = imp.entry.text(); let message = imp.entry.text();
let buffer = gtk::TextBuffer::new(None);
buffer.set_text(&format!(
r#"{{
"topic": "{topic}",
"message": "{message}"
}}"#
));
relm4_macros::view! { relm4_macros::view! {
window = adw::Window { window = adw::Window {
set_modal: true, set_modal: true,
@ -270,50 +268,127 @@ impl NotifyWindow {
set_content = &adw::ToolbarView { set_content = &adw::ToolbarView {
add_top_bar = &adw::HeaderBar {}, add_top_bar = &adw::HeaderBar {},
#[wrap(Some)] #[wrap(Some)]
set_content = &adw::Clamp { set_content: toast_overlay = &adw::ToastOverlay {
#[wrap(Some)] #[wrap(Some)]
set_child = &gtk::Box { set_child = &adw::Clamp {
set_margin_top: 8, #[wrap(Some)]
set_margin_bottom: 8, set_child = &gtk::Box {
set_margin_start: 8, set_margin_top: 8,
set_margin_end: 8, set_margin_bottom: 8,
set_spacing: 8, set_margin_start: 8,
set_orientation: gtk::Orientation::Vertical, set_margin_end: 8,
append = &gtk::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 = &gtk::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 = &gtk::Box {
set_halign: gtk::Align::Center,
set_spacing: 8, set_spacing: 8,
append = &gtk::Button { set_orientation: gtk::Orientation::Vertical,
add_css_class: "pill", append = &gtk::Label {
set_label: "Documentation", set_label: "Here you can manually build the JSON message you want to POST to this topic",
connect_clicked[this] => move |_| { set_natural_wrap_mode: gtk::NaturalWrapMode::None,
gtk::UriLauncher::new("https://docs.ntfy.sh/publish/#publish-as-json").launch( set_xalign: 0.0,
Some(&this), set_halign: gtk::Align::Start,
gio::Cancellable::NONE, set_wrap_mode: gtk::pango::WrapMode::WordChar,
|_| {} set_wrap: true,
); },
} append = &gtk::Label {
add_css_class: "heading",
set_label: "JSON",
set_xalign: 0.0,
set_halign: gtk::Align::Start,
},
append: 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 = &gtk::Label {
add_css_class: "heading",
set_label: "Snippets",
set_xalign: 0.0,
set_halign: gtk::Align::Start,
},
append = &gtk::Box {
set_spacing: 4,
append = &gtk::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 = &gtk::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 = &gtk::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 = &gtk::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#""action": [
{
"type": "view",
"label": "torvalds boosted your toot",
"url": "https://joinmastodon.org"
}
]"#)
}
},
append = &gtk::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#""action": [
{
"type": "http",
"label": "Turn off lights",
"method": "post",
"url": "https://api.example.com/lights",
"body": "OFF"
}
]"#)
}
},
append = &gtk::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 = &gtk::Button { append = &gtk::Button {
set_margin_top: 8,
set_margin_bottom: 8,
add_css_class: "suggested-action", add_css_class: "suggested-action",
add_css_class: "pill", add_css_class: "pill",
set_label: "Send", set_label: "Send",
connect_clicked[this, text_view] => move |_| { connect_clicked[this, toast_overlay, text_view] => move |_| {
let thisc = this.clone(); let thisc = this.clone();
let text_view = text_view.clone(); let text_view = text_view.clone();
let f = async move { let f = async move {
@ -327,7 +402,7 @@ impl NotifyWindow {
.unwrap() .unwrap()
.publish_msg(msg).await .publish_msg(msg).await
}; };
this.spawn_with_near_toast(f); toast_overlay.spawn_with_near_toast(f);
} }
} }
} }
@ -336,6 +411,26 @@ impl NotifyWindow {
} }
} }
} }
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(); window.present();
} }
fn show_subscription_info(&self) { fn show_subscription_info(&self) {