Repository: tarkah/tickrs (language: rust)

Issue:
Don't redraw unless new data received over API
@daniel-j-h I've already merged the user update interval feature and as I mentioned, this doesn't affect redrawing, only when to fetch API data.

Right now I have the app re-draw on a fixed 1 second cycle. In addition, user input causing changes will trigger the app to re-draw. If I can add a mechanism that tells the app to redraw whenever new API data is received, I can ditch the fixed update interval and the entire app will be reactive to when changes occur. Which will be awesome because it'll even further reduce CPU usage, especially when a long interval is specified by the user.

I have an idea on how to implement this, I'll get to it sometime later today!


Discussion / hints:
Awesome idea!

Fix patch:
diff --git a/src/draw.rs b/src/draw.rs
index 5b77a0c..e9e69fe 100644
--- a/src/draw.rs
+++ b/src/draw.rs
@@ -11,6 +11,10 @@ use tui::widgets::{Block, Borders, Paragraph, Tabs, Text};
 use tui::{Frame, Terminal};
 
 pub fn draw<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) {
+    if app.debug.enabled {
+        app.debug.dimensions = terminal.size().map_or((0, 0), |r| (r.width, r.height));
+    }
+
     terminal
         .draw(|mut frame| {
             if app.debug.enabled && app.mode == Mode::AddStock {
diff --git a/src/main.rs b/src/main.rs
index 3068439..20dde0e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
 extern crate tickrs_api as api;
 
-use crossbeam_channel::{select, tick, unbounded, Receiver};
+use crossbeam_channel::{select, tick, unbounded, Receiver, Sender};
 
 use crossterm::cursor;
 use crossterm::event::{Event, MouseEvent};
@@ -14,6 +14,7 @@ use tui::Terminal;
 
 use std::io::{self, Write};
 use std::panic;
+use std::thread;
 use std::time::Duration;
 
 mod app;
@@ -32,6 +33,7 @@ lazy_static! {
     pub static ref OPTS: cli::Opt = cli::get_opts();
     pub static ref UPDATE_INTERVAL: u64 = OPTS.update_interval;
     pub static ref TIME_FRAME: TimeFrame = OPTS.time_frame;
+    pub static ref REDRAW_REQUEST: (Sender<()>, Receiver<()>) = unbounded();
 }
 
 fn main() {
@@ -45,7 +47,8 @@ fn main() {
     setup_panic_hook();
     setup_terminal();
 
-    let ticker = tick(Duration::from_millis(1000));
+    let redraw_request = REDRAW_REQUEST.1.clone();
+    let ticker = tick(Duration::from_secs(*UPDATE_INTERVAL));
     let ui_events = setup_ui_events();
 
     let starting_stocks: Vec<_> = opts
@@ -104,8 +107,22 @@ fn main() {
                     }
                 }
 
-                if app.debug.enabled {
-                    app.debug.dimensions = crossterm::terminal::size().unwrap_or((0,0));
+                draw::draw(&mut terminal, &mut app);
+            }
+            recv(redraw_request) -> _ => {
+                // Wait some ms and collect all redraw requests so we don't
+                // redraw on each one. This is mostly important when the app is first
+                // launched with more than one symbol supplied, since each will initially
+                // request a redraw when the API data is first received.
+                thread::sleep(Duration::from_millis(200));
+                let _ = redraw_request.try_iter().collect::<Vec<_>>();
+
+                for stock in app.stocks.iter_mut() {
+                    stock.update();
+
+                    if let Some(options) = stock.options.as_mut() {
+                        options.update();
+                    }
                 }
 
                 draw::draw(&mut terminal, &mut app);
@@ -146,6 +163,8 @@ fn main() {
                         }
                         draw::draw(&mut terminal, &mut app);
                     }
+                } else if let Ok(Event::Resize(_,_)) = message {
+                    draw::draw(&mut terminal, &mut app);
                 }
             }
         }
diff --git a/src/task.rs b/src/task.rs
index f8bcbb4..0b560bb 100644
--- a/src/task.rs
+++ b/src/task.rs
@@ -1,4 +1,4 @@
-use crate::UPDATE_INTERVAL;
+use crate::{REDRAW_REQUEST, UPDATE_INTERVAL};
 
 use async_std::sync::Arc;
 use async_std::task;
@@ -40,6 +40,7 @@ pub trait AsyncTask: 'static {
     fn connect(&self) -> AsyncTaskHandle<Self::Response> {
         let (drop_sender, drop_receiver) = bounded::<()>(1);
         let (response_sender, response_receiver) = unbounded::<Self::Response>();
+        let redraw_request = REDRAW_REQUEST.0.clone();
 
         let update_interval = self.update_interval();
         let input = Arc::new(self.input());
@@ -47,9 +48,10 @@ pub trait AsyncTask: 'static {
         task::spawn(async move {
             let mut last_updated = Instant::now();
 
-            // Execute the task initially
+            // Execute the task initially and request a redraw to display this data
             if let Some(response) = <Self as AsyncTask>::task(input.clone()).await {
                 let _ = response_sender.send(response);
+                let _ = redraw_request.send(());
             }
 
             // If no update interval is defined, exit task


Test patch: