From efab04c07524a22fc3743274c3282afc4774d05f Mon Sep 17 00:00:00 2001 From: dmackdev Date: Tue, 10 Oct 2023 20:18:58 +0100 Subject: [PATCH 1/7] Showing side panel with full message contents when message ID in table is clicked. --- pubsubman/src/app.rs | 104 ++++++++++++++++++++++++++++++ pubsubman/src/column_settings.rs | 12 ---- pubsubman/src/ui/messages_view.rs | 56 ++++------------ 3 files changed, 118 insertions(+), 54 deletions(-) diff --git a/pubsubman/src/app.rs b/pubsubman/src/app.rs index 555dfed..2f1cab3 100644 --- a/pubsubman/src/app.rs +++ b/pubsubman/src/app.rs @@ -1,10 +1,13 @@ use std::collections::{HashMap, HashSet}; +use chrono::{DateTime, Local}; +use egui_json_tree::{DefaultExpand, JsonTree}; use pubsubman_backend::{ message::{BackendMessage, FrontendMessage}, model::{PubsubMessage, SubscriptionName, TopicName}, Backend, }; +use serde_json::{Map, Value}; use tokio::sync::mpsc::{Receiver, Sender}; use crate::{ @@ -35,6 +38,7 @@ pub struct App { front_tx: Sender, back_rx: Receiver, notifications: Notifications, + selected_message: Option<(TopicName, usize)>, } impl App { @@ -66,6 +70,7 @@ impl App { front_tx, back_rx, notifications: Notifications::default(), + selected_message: None, } } @@ -165,6 +170,8 @@ impl App { cancel_token.cancel(); } + self.selected_message.take(); + self.selected_topic = Some(topic_name.clone()); if !self.memory.subscriptions.contains_key(topic_name) { @@ -181,6 +188,100 @@ impl App { fn render_central_panel(&mut self, ctx: &egui::Context) { match &self.selected_topic { Some(selected_topic) => { + let selected_message = + &self + .selected_message + .as_ref() + .and_then(|(topic_name, idx)| { + self.memory + .messages + .get(topic_name) + .and_then(|messages| messages.get(*idx)) + }); + + egui::SidePanel::right("selected_message") + .frame(egui::Frame::none()) + .resizable(true) + .show_animated(ctx, selected_message.is_some(), |ui| { + if let Some(message) = selected_message { + egui::TopBottomPanel::top("selected_message_top_panel") + .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) + .show_inside(ui, |ui| { + ui.with_layout( + egui::Layout::left_to_right(egui::Align::Center), + |ui| { + ui.heading(format!("Message {}", &message.id)); + ui.with_layout( + egui::Layout::right_to_left(egui::Align::Center), + |ui| { + if ui.button("✖").clicked() { + self.selected_message.take(); + } + }, + ); + }, + ); + }); + + egui::CentralPanel::default().show_inside(ui, |ui| { + egui::ScrollArea::vertical().show(ui, |ui| { + let publish_time = + if let Some(publish_time) = message.publish_time { + let local_publish_time: DateTime = + publish_time.into(); + local_publish_time.format("%d/%m/%Y %H:%M").to_string() + } else { + "".to_string() + }; + + ui.horizontal(|ui| { + ui.label("Publish Time: "); + ui.monospace(publish_time); + }); + + egui::CollapsingHeader::new("Data") + .id_source("selected_message_data_collapsing_header") + .default_open(false) + .show(ui, |ui| { + JsonTree::new( + format!( + "selected_message_data_json_{}", + &message.id + ), + &message.data_json, + ) + .default_expand(DefaultExpand::All) + .show(ui); + }); + + egui::CollapsingHeader::new("Attributes") + .id_source("selected_message_attributes_collapsing_header") + .default_open(false) + .show(ui, |ui| { + if message.attributes.is_empty() { + ui.monospace(""); + } else { + JsonTree::new( + format!( + "selected_message_attributes_json_{}", + &message.id + ), + &Value::Object(Map::from_iter( + message.attributes.iter().map(|(k, v)| { + (k.to_owned(), Value::String(v.clone())) + }), + )), + ) + .default_expand(egui_json_tree::DefaultExpand::All) + .show(ui); + } + }); + ui.allocate_space(ui.available_size()); + }); + }); + } + }); + egui::TopBottomPanel::top("topic_view_top_panel") .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) .show(ctx, |ui| { @@ -229,6 +330,9 @@ impl App { .entry(selected_topic.clone()) .or_default(), self.memory.messages.get(selected_topic).unwrap_or(&vec![]), + |idx| { + self.selected_message = Some((selected_topic.clone(), idx)) + }, ); } None => { diff --git a/pubsubman/src/column_settings.rs b/pubsubman/src/column_settings.rs index 6601212..dae110a 100644 --- a/pubsubman/src/column_settings.rs +++ b/pubsubman/src/column_settings.rs @@ -1,16 +1,12 @@ #[derive(Clone, serde::Deserialize, serde::Serialize)] pub struct ColumnSettings { - pub show_id: bool, pub show_published_at: bool, - pub show_attributes: bool, } impl Default for ColumnSettings { fn default() -> Self { Self { - show_id: true, show_published_at: true, - show_attributes: true, } } } @@ -19,17 +15,9 @@ impl ColumnSettings { pub fn show(&mut self, ui: &mut egui::Ui) { ui.visuals_mut().widgets.inactive.weak_bg_fill = egui::Color32::from_gray(32); ui.menu_button("Columns ⏷", |ui| { - ui.horizontal(|ui| { - ui.checkbox(&mut self.show_id, " ID"); - }); - ui.horizontal(|ui| { ui.checkbox(&mut self.show_published_at, " Published at"); }); - - ui.horizontal(|ui| { - ui.checkbox(&mut self.show_attributes, " Attributes"); - }); }); } } diff --git a/pubsubman/src/ui/messages_view.rs b/pubsubman/src/ui/messages_view.rs index 3dd3c16..1923cec 100644 --- a/pubsubman/src/ui/messages_view.rs +++ b/pubsubman/src/ui/messages_view.rs @@ -1,6 +1,3 @@ -use std::collections::HashMap; -use std::fmt::Write; - use chrono::{DateTime, Local}; use egui_json_tree::{DefaultExpand, JsonTree}; use pubsubman_backend::{ @@ -24,6 +21,7 @@ pub struct MessagesView { } impl MessagesView { + #[allow(clippy::too_many_arguments)] pub fn show( &mut self, ui: &mut egui::Ui, @@ -32,6 +30,7 @@ impl MessagesView { sub_name: &SubscriptionName, column_settings: &mut ColumnSettings, messages: &[PubsubMessage], + on_message_id_click: impl FnMut(usize), ) { let search_query = self.search_query.to_ascii_lowercase(); let filtered_messages = messages @@ -155,6 +154,7 @@ impl MessagesView { filtered_messages, &search_query, search_query_changed, + on_message_id_click, ); }); }); @@ -170,37 +170,29 @@ fn render_messages_table<'a, I>( messages: I, search_term: &str, search_query_changed: bool, + mut on_message_id_click: impl FnMut(usize), ) where I: Iterator, { - let ColumnSettings { - show_id, - show_published_at, - show_attributes, - } = *column_settings; + let ColumnSettings { show_published_at } = *column_settings; + + let mut num_columns = 2; // ID and Data columns will always be shown. - let num_columns = [show_id, show_published_at, show_attributes].iter().fold( - 1, // Data column will always be present - |acc, col_enabled| if *col_enabled { acc + 1 } else { acc }, - ); + if show_published_at { + num_columns += 1; + } egui::Grid::new(&selected_topic.0) .striped(true) .num_columns(num_columns) .spacing((25.0, 8.0)) .show(ui, |ui| { - if show_id { - ui.label("ID"); - } + ui.label("ID"); if show_published_at { ui.label("Published at"); } - if show_attributes { - ui.label("Attributes"); - } - // Let Data column take up all remaining space. ui.with_layout( egui::Layout::left_to_right(egui::Align::Center) @@ -213,9 +205,9 @@ fn render_messages_table<'a, I>( ui.end_row(); - for message in messages { - if show_id { - ui.label(&message.id); + for (idx, message) in messages.enumerate() { + if ui.link(&message.id).clicked() { + on_message_id_click(idx); } if show_published_at { @@ -226,10 +218,6 @@ fn render_messages_table<'a, I>( } } - if show_attributes { - ui.label(format_attributes(&message.attributes)); - } - let response = JsonTree::new(&message.id, &message.data_json) .default_expand(DefaultExpand::SearchResults(search_term)) .response_callback(|response, pointer| { @@ -276,19 +264,3 @@ fn show_context_menu(ui: &mut egui::Ui, pointer: &String, value: &Value) { } }); } - -fn format_attributes(attributes: &HashMap) -> String { - attributes - .iter() - .enumerate() - .fold(String::new(), |mut acc, (i, (k, v))| { - let _ = write!( - acc, - "{}:{}{}", - k, - v, - (if i == attributes.len() - 1 { "" } else { ", " }) - ); - acc - }) -} From 8924755d8fd847d71f7171b7c1c731cfe11f45f9 Mon Sep 17 00:00:00 2001 From: dmackdev Date: Sat, 14 Oct 2023 19:56:17 +0100 Subject: [PATCH 2/7] Extracted json context menu rendering. --- pubsubman/src/app.rs | 12 +++++++++- pubsubman/src/ui/json_ui.rs | 38 +++++++++++++++++++++++++++++++ pubsubman/src/ui/messages_view.rs | 38 +++---------------------------- pubsubman/src/ui/mod.rs | 2 ++ 4 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 pubsubman/src/ui/json_ui.rs diff --git a/pubsubman/src/app.rs b/pubsubman/src/app.rs index 2f1cab3..72e0f90 100644 --- a/pubsubman/src/app.rs +++ b/pubsubman/src/app.rs @@ -16,7 +16,7 @@ use crate::{ exit_state::{ExitState, SubscriptionCleanupState}, notifications::Notifications, settings::Settings, - ui::{render_topic_name, MessagesView, PublishView}, + ui::{render_topic_name, show_json_context_menu, MessagesView, PublishView}, }; #[derive(Default, serde::Deserialize, serde::Serialize)] @@ -251,6 +251,9 @@ impl App { &message.data_json, ) .default_expand(DefaultExpand::All) + .response_callback(show_json_context_menu( + &message.data_json, + )) .show(ui); }); @@ -273,6 +276,13 @@ impl App { )), ) .default_expand(egui_json_tree::DefaultExpand::All) + .response_callback(show_json_context_menu( + &Value::Object(Map::from_iter( + message.attributes.iter().map(|(k, v)| { + (k.to_owned(), Value::String(v.clone())) + }), + )), + )) .show(ui); } }); diff --git a/pubsubman/src/ui/json_ui.rs b/pubsubman/src/ui/json_ui.rs new file mode 100644 index 0000000..a497121 --- /dev/null +++ b/pubsubman/src/ui/json_ui.rs @@ -0,0 +1,38 @@ +use serde_json::Value; + +pub fn show_json_context_menu(value: &Value) -> impl FnMut(egui::Response, &String) + '_ { + |response, pointer| { + response + .on_hover_cursor(egui::CursorIcon::ContextMenu) + .context_menu(|ui| { + show_json_context_menu_impl(ui, pointer, value); + }); + } +} + +fn show_json_context_menu_impl(ui: &mut egui::Ui, pointer: &String, value: &Value) { + ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| { + ui.set_width(150.0); + + if !pointer.is_empty() + && ui + .add(egui::Button::new("Copy property path").frame(false)) + .clicked() + { + ui.output_mut(|o| o.copied_text = pointer.clone()); + ui.close_menu(); + } + + if ui + .add(egui::Button::new("Copy contents").frame(false)) + .clicked() + { + if let Some(val) = value.pointer(pointer) { + if let Ok(pretty_str) = serde_json::to_string_pretty(val) { + ui.output_mut(|o| o.copied_text = pretty_str); + } + } + ui.close_menu(); + } + }); +} diff --git a/pubsubman/src/ui/messages_view.rs b/pubsubman/src/ui/messages_view.rs index 1923cec..60c05f7 100644 --- a/pubsubman/src/ui/messages_view.rs +++ b/pubsubman/src/ui/messages_view.rs @@ -4,7 +4,6 @@ use pubsubman_backend::{ message::FrontendMessage, model::{PubsubMessage, SubscriptionName, TopicName}, }; -use serde_json::Value; use tokio::sync::mpsc::Sender; use tokio_util::sync::CancellationToken; @@ -13,6 +12,8 @@ use crate::{ column_settings::ColumnSettings, }; +use super::show_json_context_menu; + #[derive(Default)] pub struct MessagesView { pub stream_messages_enabled: bool, @@ -220,13 +221,7 @@ fn render_messages_table<'a, I>( let response = JsonTree::new(&message.id, &message.data_json) .default_expand(DefaultExpand::SearchResults(search_term)) - .response_callback(|response, pointer| { - response - .on_hover_cursor(egui::CursorIcon::ContextMenu) - .context_menu(|ui| { - show_context_menu(ui, pointer, &message.data_json); - }); - }) + .response_callback(show_json_context_menu(&message.data_json)) .show(ui); if search_query_changed { @@ -237,30 +232,3 @@ fn render_messages_table<'a, I>( } }); } - -fn show_context_menu(ui: &mut egui::Ui, pointer: &String, value: &Value) { - ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| { - ui.set_width(150.0); - - if !pointer.is_empty() - && ui - .add(egui::Button::new("Copy property path").frame(false)) - .clicked() - { - ui.output_mut(|o| o.copied_text = pointer.clone()); - ui.close_menu(); - } - - if ui - .add(egui::Button::new("Copy contents").frame(false)) - .clicked() - { - if let Some(val) = value.pointer(pointer) { - if let Ok(pretty_str) = serde_json::to_string_pretty(val) { - ui.output_mut(|o| o.copied_text = pretty_str); - } - } - ui.close_menu(); - } - }); -} diff --git a/pubsubman/src/ui/mod.rs b/pubsubman/src/ui/mod.rs index bcf54bd..41f8afd 100644 --- a/pubsubman/src/ui/mod.rs +++ b/pubsubman/src/ui/mod.rs @@ -1,9 +1,11 @@ +mod json_ui; mod messages_view; mod modal; mod publish_view; mod topic_name; mod validity_frame; +pub use json_ui::show_json_context_menu; pub use messages_view::MessagesView; pub use modal::Modal; pub use publish_view::PublishView; From 5ae994ef3de00dda37e59efb6478f9425ef69446 Mon Sep 17 00:00:00 2001 From: dmackdev Date: Sat, 14 Oct 2023 20:05:39 +0100 Subject: [PATCH 3/7] Added attributes json value. --- pubsubman/src/app.rs | 13 ++----------- pubsubman_backend/src/model/pubsub_message.rs | 12 +++++++++++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/pubsubman/src/app.rs b/pubsubman/src/app.rs index 72e0f90..386bc0c 100644 --- a/pubsubman/src/app.rs +++ b/pubsubman/src/app.rs @@ -7,7 +7,6 @@ use pubsubman_backend::{ model::{PubsubMessage, SubscriptionName, TopicName}, Backend, }; -use serde_json::{Map, Value}; use tokio::sync::mpsc::{Receiver, Sender}; use crate::{ @@ -269,19 +268,11 @@ impl App { "selected_message_attributes_json_{}", &message.id ), - &Value::Object(Map::from_iter( - message.attributes.iter().map(|(k, v)| { - (k.to_owned(), Value::String(v.clone())) - }), - )), + &message.attributes_json, ) .default_expand(egui_json_tree::DefaultExpand::All) .response_callback(show_json_context_menu( - &Value::Object(Map::from_iter( - message.attributes.iter().map(|(k, v)| { - (k.to_owned(), Value::String(v.clone())) - }), - )), + &message.attributes_json, )) .show(ui); } diff --git a/pubsubman_backend/src/model/pubsub_message.rs b/pubsubman_backend/src/model/pubsub_message.rs index f53c6a7..f036d26 100644 --- a/pubsubman_backend/src/model/pubsub_message.rs +++ b/pubsubman_backend/src/model/pubsub_message.rs @@ -1,6 +1,6 @@ use chrono::{DateTime, TimeZone, Utc}; use google_cloud_pubsub::subscriber::ReceivedMessage; -use serde_json::Value; +use serde_json::{Map, Value}; use std::{collections::HashMap, str}; #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] @@ -10,6 +10,7 @@ pub struct PubsubMessage { pub data: String, pub data_json: Value, pub attributes: HashMap, + pub attributes_json: Value, } impl From for PubsubMessage { @@ -30,12 +31,21 @@ impl From for PubsubMessage { Err(_) => Value::String(data.clone()), }; + let attributes_json = Value::Object(Map::from_iter( + value + .message + .attributes + .iter() + .map(|(k, v)| (k.to_owned(), Value::String(v.to_owned()))), + )); + Self { id: value.message.message_id, publish_time, data, data_json, attributes: value.message.attributes, + attributes_json, } } } From 14231d7fc03f64cef2a6b530ae2f84a6d3ee4470 Mon Sep 17 00:00:00 2001 From: dmackdev Date: Sun, 15 Oct 2023 11:24:16 +0100 Subject: [PATCH 4/7] Added republish button on selected message view. --- pubsubman/src/app.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pubsubman/src/app.rs b/pubsubman/src/app.rs index 386bc0c..4bc8504 100644 --- a/pubsubman/src/app.rs +++ b/pubsubman/src/app.rs @@ -4,13 +4,13 @@ use chrono::{DateTime, Local}; use egui_json_tree::{DefaultExpand, JsonTree}; use pubsubman_backend::{ message::{BackendMessage, FrontendMessage}, - model::{PubsubMessage, SubscriptionName, TopicName}, + model::{PubsubMessage, PubsubMessageToPublish, SubscriptionName, TopicName}, Backend, }; use tokio::sync::mpsc::{Receiver, Sender}; use crate::{ - actions::{create_subscription, delete_subscriptions, refresh_topics}, + actions::{create_subscription, delete_subscriptions, publish_message, refresh_topics}, column_settings::ColumnSettings, exit_state::{ExitState, SubscriptionCleanupState}, notifications::Notifications, @@ -222,6 +222,22 @@ impl App { ); }); + egui::TopBottomPanel::bottom("selected_message_bottom_panel") + .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) + .show_inside(ui, |ui| { + if ui.button("Republish Message").clicked() { + let message_to_publish = PubsubMessageToPublish::new( + message.data.clone(), + message.attributes.clone(), + ); + publish_message( + &self.front_tx, + selected_topic, + message_to_publish, + ) + } + }); + egui::CentralPanel::default().show_inside(ui, |ui| { egui::ScrollArea::vertical().show(ui, |ui| { let publish_time = From ff49bea3cb7a3c398fa5d8764ef11f79f822411f Mon Sep 17 00:00:00 2001 From: dmackdev Date: Tue, 17 Oct 2023 21:09:53 +0100 Subject: [PATCH 5/7] Rendering selected topic top panel above all. --- pubsubman/src/app.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pubsubman/src/app.rs b/pubsubman/src/app.rs index 4bc8504..cb2ffa1 100644 --- a/pubsubman/src/app.rs +++ b/pubsubman/src/app.rs @@ -198,6 +198,14 @@ impl App { .and_then(|messages| messages.get(*idx)) }); + egui::TopBottomPanel::top("topic_view_top_panel") + .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + ui.heading(&selected_topic.0); + }); + }); + egui::SidePanel::right("selected_message") .frame(egui::Frame::none()) .resizable(true) @@ -299,14 +307,6 @@ impl App { } }); - egui::TopBottomPanel::top("topic_view_top_panel") - .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) - .show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.heading(&selected_topic.0); - }); - }); - egui::TopBottomPanel::bottom("topic_view_bottom_panel") .resizable(true) .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) From 633d3f76f5a94f4648057ae21b447fdca1ce3182 Mon Sep 17 00:00:00 2001 From: dmackdev Date: Tue, 17 Oct 2023 21:11:40 +0100 Subject: [PATCH 6/7] Removed uneeded ref. --- pubsubman/src/app.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pubsubman/src/app.rs b/pubsubman/src/app.rs index cb2ffa1..b132b76 100644 --- a/pubsubman/src/app.rs +++ b/pubsubman/src/app.rs @@ -188,8 +188,7 @@ impl App { match &self.selected_topic { Some(selected_topic) => { let selected_message = - &self - .selected_message + self.selected_message .as_ref() .and_then(|(topic_name, idx)| { self.memory From c4f2b4d3a7c8ea3abf98d3bd2524bd82c7eb58a8 Mon Sep 17 00:00:00 2001 From: dmackdev Date: Tue, 17 Oct 2023 21:18:07 +0100 Subject: [PATCH 7/7] Extracted render_selected_message function. --- pubsubman/src/app.rs | 111 ++++----------------------- pubsubman/src/ui/mod.rs | 2 + pubsubman/src/ui/selected_message.rs | 90 ++++++++++++++++++++++ 3 files changed, 105 insertions(+), 98 deletions(-) create mode 100644 pubsubman/src/ui/selected_message.rs diff --git a/pubsubman/src/app.rs b/pubsubman/src/app.rs index b132b76..2166fb2 100644 --- a/pubsubman/src/app.rs +++ b/pubsubman/src/app.rs @@ -1,21 +1,19 @@ use std::collections::{HashMap, HashSet}; -use chrono::{DateTime, Local}; -use egui_json_tree::{DefaultExpand, JsonTree}; use pubsubman_backend::{ message::{BackendMessage, FrontendMessage}, - model::{PubsubMessage, PubsubMessageToPublish, SubscriptionName, TopicName}, + model::{PubsubMessage, SubscriptionName, TopicName}, Backend, }; use tokio::sync::mpsc::{Receiver, Sender}; use crate::{ - actions::{create_subscription, delete_subscriptions, publish_message, refresh_topics}, + actions::{create_subscription, delete_subscriptions, refresh_topics}, column_settings::ColumnSettings, exit_state::{ExitState, SubscriptionCleanupState}, notifications::Notifications, settings::Settings, - ui::{render_topic_name, show_json_context_menu, MessagesView, PublishView}, + ui::{render_selected_message, render_topic_name, MessagesView, PublishView}, }; #[derive(Default, serde::Deserialize, serde::Serialize)] @@ -210,99 +208,16 @@ impl App { .resizable(true) .show_animated(ctx, selected_message.is_some(), |ui| { if let Some(message) = selected_message { - egui::TopBottomPanel::top("selected_message_top_panel") - .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) - .show_inside(ui, |ui| { - ui.with_layout( - egui::Layout::left_to_right(egui::Align::Center), - |ui| { - ui.heading(format!("Message {}", &message.id)); - ui.with_layout( - egui::Layout::right_to_left(egui::Align::Center), - |ui| { - if ui.button("✖").clicked() { - self.selected_message.take(); - } - }, - ); - }, - ); - }); - - egui::TopBottomPanel::bottom("selected_message_bottom_panel") - .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) - .show_inside(ui, |ui| { - if ui.button("Republish Message").clicked() { - let message_to_publish = PubsubMessageToPublish::new( - message.data.clone(), - message.attributes.clone(), - ); - publish_message( - &self.front_tx, - selected_topic, - message_to_publish, - ) - } - }); - - egui::CentralPanel::default().show_inside(ui, |ui| { - egui::ScrollArea::vertical().show(ui, |ui| { - let publish_time = - if let Some(publish_time) = message.publish_time { - let local_publish_time: DateTime = - publish_time.into(); - local_publish_time.format("%d/%m/%Y %H:%M").to_string() - } else { - "".to_string() - }; - - ui.horizontal(|ui| { - ui.label("Publish Time: "); - ui.monospace(publish_time); - }); - - egui::CollapsingHeader::new("Data") - .id_source("selected_message_data_collapsing_header") - .default_open(false) - .show(ui, |ui| { - JsonTree::new( - format!( - "selected_message_data_json_{}", - &message.id - ), - &message.data_json, - ) - .default_expand(DefaultExpand::All) - .response_callback(show_json_context_menu( - &message.data_json, - )) - .show(ui); - }); - - egui::CollapsingHeader::new("Attributes") - .id_source("selected_message_attributes_collapsing_header") - .default_open(false) - .show(ui, |ui| { - if message.attributes.is_empty() { - ui.monospace(""); - } else { - JsonTree::new( - format!( - "selected_message_attributes_json_{}", - &message.id - ), - &message.attributes_json, - ) - .default_expand(egui_json_tree::DefaultExpand::All) - .response_callback(show_json_context_menu( - &message.attributes_json, - )) - .show(ui); - } - }); - ui.allocate_space(ui.available_size()); - }); - }); + render_selected_message( + ctx, + ui, + &self.front_tx, + message, + selected_topic, + || { + self.selected_message.take(); + }, + ); } }); diff --git a/pubsubman/src/ui/mod.rs b/pubsubman/src/ui/mod.rs index 41f8afd..ac9212d 100644 --- a/pubsubman/src/ui/mod.rs +++ b/pubsubman/src/ui/mod.rs @@ -2,6 +2,7 @@ mod json_ui; mod messages_view; mod modal; mod publish_view; +mod selected_message; mod topic_name; mod validity_frame; @@ -9,4 +10,5 @@ pub use json_ui::show_json_context_menu; pub use messages_view::MessagesView; pub use modal::Modal; pub use publish_view::PublishView; +pub use selected_message::render_selected_message; pub use topic_name::render_topic_name; diff --git a/pubsubman/src/ui/selected_message.rs b/pubsubman/src/ui/selected_message.rs new file mode 100644 index 0000000..f29b294 --- /dev/null +++ b/pubsubman/src/ui/selected_message.rs @@ -0,0 +1,90 @@ +use chrono::{DateTime, Local}; +use egui_json_tree::{DefaultExpand, JsonTree}; +use pubsubman_backend::{ + message::FrontendMessage, + model::{PubsubMessage, PubsubMessageToPublish, TopicName}, +}; +use tokio::sync::mpsc::Sender; + +use crate::actions::publish_message; + +use super::show_json_context_menu; + +pub fn render_selected_message( + ctx: &egui::Context, + ui: &mut egui::Ui, + front_tx: &Sender, + message: &PubsubMessage, + selected_topic: &TopicName, + mut on_close: impl FnMut(), +) { + egui::TopBottomPanel::top("selected_message_top_panel") + .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) + .show_inside(ui, |ui| { + ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| { + ui.heading(format!("Message {}", &message.id)); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + if ui.button("✖").clicked() { + on_close(); + } + }); + }); + }); + + egui::TopBottomPanel::bottom("selected_message_bottom_panel") + .frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(8.0)) + .show_inside(ui, |ui| { + if ui.button("Republish Message").clicked() { + let message_to_publish = + PubsubMessageToPublish::new(message.data.clone(), message.attributes.clone()); + publish_message(front_tx, selected_topic, message_to_publish) + } + }); + + egui::CentralPanel::default().show_inside(ui, |ui| { + egui::ScrollArea::vertical().show(ui, |ui| { + let publish_time = if let Some(publish_time) = message.publish_time { + let local_publish_time: DateTime = publish_time.into(); + local_publish_time.format("%d/%m/%Y %H:%M").to_string() + } else { + "".to_string() + }; + + ui.horizontal(|ui| { + ui.label("Publish Time: "); + ui.monospace(publish_time); + }); + + egui::CollapsingHeader::new("Data") + .id_source("selected_message_data_collapsing_header") + .default_open(false) + .show(ui, |ui| { + JsonTree::new( + format!("selected_message_data_json_{}", &message.id), + &message.data_json, + ) + .default_expand(DefaultExpand::All) + .response_callback(show_json_context_menu(&message.data_json)) + .show(ui); + }); + + egui::CollapsingHeader::new("Attributes") + .id_source("selected_message_attributes_collapsing_header") + .default_open(false) + .show(ui, |ui| { + if message.attributes.is_empty() { + ui.monospace(""); + } else { + JsonTree::new( + format!("selected_message_attributes_json_{}", &message.id), + &message.attributes_json, + ) + .default_expand(egui_json_tree::DefaultExpand::All) + .response_callback(show_json_context_menu(&message.attributes_json)) + .show(ui); + } + }); + ui.allocate_space(ui.available_size()); + }); + }); +}