From 39d81c8b3cdfb8567a2feb03936daae75ce1771e Mon Sep 17 00:00:00 2001 From: Daniel Leech Date: Sun, 10 Nov 2024 17:34:51 +0000 Subject: [PATCH] Updatre README --- README.md | 67 +++++++++++++++++++++++++++++++++++++------ src/app.rs | 23 ++++++++------- src/expr/parser.rs | 2 -- src/store/activity.rs | 2 ++ 4 files changed, 72 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3e323e0..2802d8c 100644 --- a/README.md +++ b/README.md @@ -6,24 +6,73 @@ Strava TUI written in Rust! This is an experimental TUI for Strava. Features: - List activities in a comparable way -- Filter activites by name +- Filter activites by with expressions - Sort listed activities - Display the route - Show laps - Race predictions - Filter by route similarity ("anchoring") +## Screenshots + ### List activities -![image](https://github.com/dantleech/strava-rs/assets/530801/7187befb-65e2-4fbc-b5b4-8710510c5e1a) -*Numbers* +![image](https://github.com/user-attachments/assets/f13ed611-d764-4941-a3df-c95db8636ba7) + +### Acivity View + +![image](https://github.com/user-attachments/assets/88c9b34a-7cee-409d-9d01-39bd22ef8259) + +## Key Map + +- `q`: **Quit**: quit! +- `k`: **Up** - select previous activity +- `j`: **Down** - select next activity +- `n`: **Next** - (in activity view) next split +- `p`: **Previous** - (in activity view) previous split +- `o`: **ToggleSortOrder** - switch between ascending and descending order +- `u`: **ToggleUnitSystem** - switch between imperial and metric units +- `s`: **Sort** - show sort dialog +- `S`: **Rank** - choose ranking +- `f`: **Filter** - filter (see filter section below) +- `r`: **Refresh** - reload activities +- `a`: **Anchor** - show activities with similar routes +- `+`: **IncreaseTolerance** - incease the anchor tolerance +- `-`: **DecreaseTolerance** - descrease the ancor tolerance +- `0`: **ToggleLogView** - toggle log view + +## Filter + +Press `f` on the activity list view to open the filter input. + +### Examples + +Show all runs that are of a half marathon distance or more: + +``` +type = "Run" and distance > 21000 +``` + +Show all runs with "Park" in the title: + +``` +type = "Run" and title ~ "Park" +``` -### Filter activities +### Fields -![image](https://github.com/dantleech/strava-rs/assets/530801/42a5a2e2-0925-4d1f-a780-e1a5d11b0ab1) -*Chronological* +- `distance`: Distance (in meters) +- `type`: `Run`, `Ride` etc. +- `heartrate`: Heart rate in BPM. +- `title`: Activity title +- `elevation`: Elevation (in meters) +- `time`: Time (in seconds, 3600 = 1 hour) -### Details +### Operators -![image](https://github.com/dantleech/strava-rs/assets/530801/633ea4ff-12c8-4ead-817b-80db8efcf61a) -*Detailed Maps* +- `>`, `<`: Greater than, Less than (e.g. `distance > 21000`) +- `and`, `or`: Logical operators (e.g. `type = "Run" and time > 0`) +- `=`: Equal to +- `~`: String contains +- `!=`: Not equal to (e.g. `type != "Run"`) +- `!~`: String does not contain (e.g. `title ~ "Parkrun"`) diff --git a/src/app.rs b/src/app.rs index 1765791..5a8a8b7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -8,16 +8,18 @@ use log::info; use tokio::sync::mpsc::{Receiver, Sender}; use tui::{ backend::{Backend, CrosstermBackend}, - widgets::TableState, Terminal, + widgets::TableState, + Terminal, }; use tui_input::Input; use tui_logger::TuiWidgetState; use crate::{ - component::{activity_list, unit_formatter::UnitFormatter, log_view::LogView}, + component::{activity_list, log_view::LogView, unit_formatter::UnitFormatter}, event::keymap::KeyMap, + expr::evaluator::Evaluator, store::activity::Activity, - ui, expr::{self, evaluator::{Evaluator, Vars}}, + ui, }; use crate::{ component::{ @@ -138,7 +140,8 @@ impl App<'_> { pace_table_state: TableState::default(), selected_split: None, }, - log_view_state: TuiWidgetState::default().set_default_display_level(log::LevelFilter::Debug), + log_view_state: TuiWidgetState::default() + .set_default_display_level(log::LevelFilter::Debug), filters: ActivityFilters { sort_by: SortBy::Date, sort_order: SortOrder::Desc, @@ -176,8 +179,8 @@ impl App<'_> { let mut view: Box = match self.active_page { ActivePage::ActivityList => Box::new(ActivityList::new()), - ActivePage::Activity => Box::new(ActivityView{}), - ActivePage::LogView => Box::new(LogView::new()) + ActivePage::Activity => Box::new(ActivityView {}), + ActivePage::LogView => Box::new(LogView::new()), }; if let Some(message) = &self.info_message { @@ -194,9 +197,7 @@ impl App<'_> { while self.event_queue.len() > 1 { let event = self.event_queue.pop().unwrap(); info!("Sending event: {:?}", event); - self.event_sender - .send(event) - .await?; + self.event_sender.send(event).await?; } if let Some(event) = self.event_receiver.recv().await { @@ -233,7 +234,7 @@ impl App<'_> { Ok(expr) => activities.by_expr(&evaluator, &expr), Err(_) => activities.where_title_contains(self.filters.filter.as_str()), }; - + if let Some(activity_type) = self.activity_type.clone() { activities = activities.having_activity_type(activity_type); } @@ -299,7 +300,7 @@ impl App<'_> { fn render( &mut self, terminal: &mut Terminal>, - view: &mut dyn View + view: &mut dyn View, ) -> Result<(), anyhow::Error> { let area = terminal.size().expect("Could not determine terminal size'"); terminal.autoresize()?; diff --git a/src/expr/parser.rs b/src/expr/parser.rs index b490d94..2d450cf 100644 --- a/src/expr/parser.rs +++ b/src/expr/parser.rs @@ -1,5 +1,3 @@ -use std::ascii::AsciiExt; - use super::lexer::{Lexer, Token, TokenKind}; #[derive(PartialEq, Debug, Clone)] diff --git a/src/store/activity.rs b/src/store/activity.rs index 5c8ee1c..0b2a679 100644 --- a/src/store/activity.rs +++ b/src/store/activity.rs @@ -214,6 +214,8 @@ impl Activities { ("type".to_string(), Evalue::String(a.activity_type.to_string())), ("heartrate".to_string(), Evalue::Number(a.average_heartrate.unwrap_or(0.0))), ("title".to_string(), Evalue::String(a.title.clone())), + ("elevation".to_string(), Evalue::Number(a.total_elevation_gain)), + ("time".to_string(), Evalue::Number(a.moving_time as f64)), ])) { Ok(v) => v, Err(_) => false,