Skip to content

Commit

Permalink
feat: implement topic search
Browse files Browse the repository at this point in the history
  • Loading branch information
EdJoPaTo committed Nov 24, 2023
1 parent ef0cc5b commit 18f5920
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 2 deletions.
12 changes: 11 additions & 1 deletion src/interactive/footer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Footer {
}
}

pub fn draw(&self, f: &mut Frame, area: Rect, focus: &ElementInFocus) {
pub fn draw(&self, f: &mut Frame, area: Rect, focus: &ElementInFocus, topic_search: &str) {
const STYLE: Style = Style::new()
.fg(Color::Black)
.bg(Color::White)
Expand All @@ -33,9 +33,19 @@ impl Footer {
Span::raw(" Quit "),
Span::styled("Tab", STYLE),
Span::raw(" Switch to JSON Payload "),
Span::styled("/", STYLE),
Span::raw(" Search "),
Span::styled("Del", STYLE),
Span::raw(" Clean retained "),
],
ElementInFocus::TopicSearch => vec![
Span::styled("Enter", STYLE),
Span::raw(" Next "),
Span::styled("Esc", STYLE),
Span::raw(" Clear "),
Span::raw("Search: "),
Span::raw(topic_search),
],
ElementInFocus::JsonPayload => vec![
Span::styled("q", STYLE),
Span::raw(" Quit "),
Expand Down
74 changes: 73 additions & 1 deletion src/interactive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ impl App {
}
Refresh::Update
}
KeyCode::Char('/') => {
self.focus = ElementInFocus::TopicSearch;
Refresh::Update
}
KeyCode::Enter | KeyCode::Char(' ') => {
self.topic_overview.state.toggle_selected();
Refresh::Update
Expand Down Expand Up @@ -276,6 +280,69 @@ impl App {
}
_ => Refresh::Skip,
},
ElementInFocus::TopicSearch => match key.code {
KeyCode::Char(char) => {
self.topic_overview.search += &char.to_lowercase().to_string();
Refresh::Update
}
KeyCode::Backspace => {
self.topic_overview.search.pop();
Refresh::Update
}
KeyCode::Esc => {
self.topic_overview.search = String::new();
self.focus = ElementInFocus::TopicOverview;
Refresh::Update
}
KeyCode::Tab => {
self.focus = ElementInFocus::TopicOverview;
Refresh::Update
}
KeyCode::Enter => {
let selection = self.topic_overview.get_selected();
let history = self.mqtt_thread.get_history();
let mut topics = history
.get_all_topics()
.into_iter()
.enumerate()
.collect::<Vec<_>>();

let begin_index = selection
.and_then(|selection| {
topics
.iter()
.find(|(_, topic)| *topic == &selection)
.map(|(index, _)| *index)
})
.unwrap_or(0);

// Filter out topics not matching the search
topics.retain(|(_, topic)| {
topic.to_lowercase().contains(&self.topic_overview.search)
});

let select = topics
.iter()
.find(|(index, _)| *index > begin_index)
.or_else(|| topics.first())
.map(|(_, topic)| topic)
.map_or(vec![], |o| {
o.split('/')
.map(std::borrow::ToOwned::to_owned)
.collect::<Vec<_>>()
});
drop(history);

for i in 0..select.len() {
self.topic_overview.state.open(select[0..i].to_vec());
}

self.topic_overview.state.select(select);

Refresh::Update
}
_ => Refresh::Skip,
},
ElementInFocus::JsonPayload => match key.code {
KeyCode::Char('q') => Refresh::Quit,
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
Expand Down Expand Up @@ -328,12 +395,14 @@ impl App {
Ok(refresh)
}

/// Handle mouse and keyboard up movement
fn on_up(&mut self) -> Refresh {
match self.focus {
ElementInFocus::TopicOverview => {
let items = self.get_topic_tree_items();
self.topic_overview.state.key_up(&items);
}
ElementInFocus::TopicSearch => {}
ElementInFocus::JsonPayload => {
let json = self
.get_json_of_current_topic()
Expand All @@ -346,12 +415,14 @@ impl App {
Refresh::Update
}

/// Handle mouse and keyboard down movement
fn on_down(&mut self) -> Refresh {
match self.focus {
ElementInFocus::TopicOverview => {
let items = self.get_topic_tree_items();
self.topic_overview.state.key_down(&items);
}
ElementInFocus::TopicSearch => {}
ElementInFocus::JsonPayload => {
let json = self
.get_json_of_current_topic()
Expand Down Expand Up @@ -435,7 +506,8 @@ impl App {
f.render_widget(paragraph.alignment(Alignment::Center), header_area);
}

self.footer.draw(f, footer_area, &self.focus);
self.footer
.draw(f, footer_area, &self.focus, &self.topic_overview.search);
if let Some(connection_error) = connection_error {
mqtt_error_widget::draw(f, error_area, "MQTT Connection Error", &connection_error);
}
Expand Down
6 changes: 6 additions & 0 deletions src/interactive/mqtt_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ impl MqttHistory {
.and_then(|node| node.value().history.last())
}

pub fn get_all_topics(&self) -> Vec<&String> {
let mut topics = self.ids.keys().collect::<Vec<_>>();
topics.sort();
topics
}

pub fn get_topics_below(&self, topic: &str) -> Vec<String> {
fn build_recursive(prefix: &[&str], node: NodeRef<Topic>) -> Vec<String> {
let mut topic = prefix.to_vec();
Expand Down
1 change: 1 addition & 0 deletions src/interactive/topic_overview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::interactive::ui::{focus_color, get_row_inside};
#[derive(Default)]
pub struct TopicOverview {
pub last_area: Rect,
pub search: String,
pub state: TreeState<String>,
}

Expand Down
1 change: 1 addition & 0 deletions src/interactive/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub const STYLE_BOLD: Style = Style::new().add_modifier(Modifier::BOLD);

pub enum ElementInFocus {
TopicOverview,
TopicSearch,
JsonPayload,
CleanRetainedPopup(String),
}
Expand Down

0 comments on commit 18f5920

Please sign in to comment.