diff options
| author | iximeow <me@iximeow.net> | 2017-12-02 21:40:41 -0800 | 
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2017-12-02 21:40:41 -0800 | 
| commit | 461aa38a7abbae783ed130db27b71c026df971f7 (patch) | |
| tree | cea89b2fbcaa44872a7d458a12981080d558daa2 /src | |
| parent | bd78dc9a7156bca51935ff284c8747634031cedb (diff) | |
track dirty bit to know if we should redraw display
also clean up a bunch of TODOs
Diffstat (limited to 'src')
| -rw-r--r-- | src/commands/fav.rs | 4 | ||||
| -rw-r--r-- | src/commands/profile.rs | 3 | ||||
| -rw-r--r-- | src/commands/twete.rs | 4 | ||||
| -rw-r--r-- | src/display/mod.rs | 125 | ||||
| -rw-r--r-- | src/main.rs | 74 | ||||
| -rw-r--r-- | src/tw/mod.rs | 56 | 
6 files changed, 166 insertions, 100 deletions
| diff --git a/src/commands/fav.rs b/src/commands/fav.rs index 5e5f2a2..02ec7dd 100644 --- a/src/commands/fav.rs +++ b/src/commands/fav.rs @@ -21,7 +21,7 @@ fn unfav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer, di      let maybe_id = TweetId::parse(line.to_owned());      match maybe_id {          Ok(twid) => { -            if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self +            if let Some(twete) = tweeter.retrieve_tweet(&twid) {                  let result = match tweeter.current_profile() {                      Some(user_profile) => queryer.do_api_post(&format!("{}?id={}", UNFAV_TWEET_URL, twete.id), &tweeter.app_key, &user_profile.creds),                      None => Err("No logged in user to unfav from".to_owned()) @@ -53,7 +53,7 @@ fn fav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer, disp      match maybe_id {          Ok(twid) => {              // tweeter.to_twitter_tweet_id(twid)... -            if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self +            if let Some(twete) = tweeter.retrieve_tweet(&twid) {                  let result = match tweeter.current_profile() {                      Some(user_profile) => queryer.do_api_post(&format!("{}?id={}", FAV_TWEET_URL, twete.id), &tweeter.app_key, &user_profile.creds),                      None => Err("No logged in user to fav from".to_owned()) diff --git a/src/commands/profile.rs b/src/commands/profile.rs index e20859b..f3bfd8d 100644 --- a/src/commands/profile.rs +++ b/src/commands/profile.rs @@ -1,8 +1,5 @@  use display::DisplayInfo;  use tw; -use std; -use std::collections::HashMap; -use hyper;  use ::Queryer;  use commands::Command; diff --git a/src/commands/twete.rs b/src/commands/twete.rs index ebf3c3d..d727b9e 100644 --- a/src/commands/twete.rs +++ b/src/commands/twete.rs @@ -54,7 +54,7 @@ fn twete(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer, di      // if it's just "t", enter compose mode.      let text = line.trim().to_owned();      if text.len() == 0 { -        display_info.mode = Some(::display::DisplayMode::Compose(text)); +        display_info.set_mode(Some(::display::DisplayMode::Compose(text)));      } else {          send_twete(text, tweeter, queryer, display_info);      } @@ -178,7 +178,7 @@ fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer, disp                  if reply.len() > 0 {                      send_reply(full_reply, twid, tweeter, queryer, user_profile.creds, display_info);                  } else { -                    display_info.mode = Some(::display::DisplayMode::Reply(twid, full_reply)); +                    display_info.set_mode(Some(::display::DisplayMode::Reply(twid, full_reply)));                  }              } else {                  display_info.status(format!("No tweet for id: {:?}", twid)); diff --git a/src/display/mod.rs b/src/display/mod.rs index c3c4692..04b200f 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -35,14 +35,15 @@ pub enum Infos {  const COMPOSE_HEIGHT: u16 = 5;  pub struct DisplayInfo { -    pub log_height: u16, -    pub prompt_height: u16, -    pub mode: Option<DisplayMode>, -    pub log_seek: u32, -    pub infos_seek: u32, -    pub log: Vec<String>, -    pub infos: Vec<Infos>, -    pub input_buf: Vec<char> +    log_height: u16, +    prompt_height: u16, +    mode: Option<DisplayMode>, +    log_seek: u32, +    infos_seek: u32, +    log: Vec<String>, +    infos: Vec<Infos>, +    input_buf: Vec<char>, +    dirty: bool  }  impl Default for DisplayInfo { @@ -55,18 +56,91 @@ impl Default for DisplayInfo {              infos_seek: 0,              log: Vec::new(),              infos: Vec::new(), -            input_buf: Vec::new() +            input_buf: Vec::new(), +            dirty: false          }      }  }  impl DisplayInfo { +    pub fn set_mode(&mut self, new_mode: Option<DisplayMode>) { +        self.mode = new_mode; +        self.dirty = true; +    } + +    pub fn get_mode(&self) -> &Option<DisplayMode> { +        &self.mode +    } + +    pub fn adjust_infos_seek(&mut self, seek_adjust: Option<i32>) { +        self.infos_seek = match seek_adjust { +            Some(adjust) => { +                /* +                 * So this might be weird to read. +                 * +                 * I don't know how to add an i32 in a saturating manner to a u32. +                 * +                 * That's what this does: +                 *   if adjust is negative, negate to positive and saturating_sub it +                 *   if adjust is positive, we can just saturating_add it +                 */ +                if adjust < 0 { +                    self.infos_seek.saturating_sub(-adjust as u32) +                } else { +                    self.infos_seek.saturating_add(adjust as u32) +                } +            }, +            None => 0 +        }; +        self.dirty = true; +    } + +    pub fn adjust_log_seek(&mut self, seek_adjust: Option<i32>) { +        self.log_seek = match seek_adjust { +            Some(adjust) => { +                /* +                 * So this might be weird to read. +                 * +                 * I don't know how to add an i32 in a saturating manner to a u32. +                 * +                 * That's what this does: +                 *   if adjust is negative, negate to positive and saturating_sub it +                 *   if adjust is positive, we can just saturating_add it +                 */ +                if adjust < 0 { +                    self.log_seek.saturating_sub(-adjust as u32) +                } else { +                    self.log_seek.saturating_add(adjust as u32) +                } +            }, +            None => 0 +        }; +        self.dirty = true; +    } +      pub fn status(&mut self, stat: String) {          self.log.push(stat); +        self.dirty = true;      }      pub fn recv(&mut self, info: Infos) {          self.infos.push(info); +        self.dirty = true; +    } + +    pub fn input_buf_push(&mut self, c: char) { +        self.input_buf.push(c); +        self.dirty = true; +    } + +    pub fn input_buf_pop(&mut self) { +        self.input_buf.pop(); +        self.dirty = true; +    } + +    pub fn input_buf_drain(&mut self) -> String { +        self.dirty = true; +        self.input_buf.drain(..).collect()      }      pub fn ui_height(&self) -> u16 { @@ -79,20 +153,6 @@ impl DisplayInfo {   */  fn into_display_lines(x: Vec<String>, width: u16) -> Vec<String> {      ansi_aware_into_display_lines(x, width) -    /* -    let split_on_newline: Vec<String> = x.into_iter() -        .flat_map(|x| x.split("\n") -            .map(|x| x.to_owned()) -            .collect::<Vec<String>>() -        ).collect(); -    let wrapped: Vec<String> = split_on_newline.iter() -        .map(|x| x.chars().collect::<Vec<char>>()) -        .flat_map(|x| x.chunks(width as usize) -            .map(|x| x.into_iter().collect::<String>()) -            .collect::<Vec<String>>()) -        .collect(); -    wrapped -     */  }  #[derive(Clone)] @@ -211,7 +271,6 @@ fn ansi_aware_into_display_lines(x: Vec<String>, width: u16) -> Vec<String> {                              "".to_owned()                          },                          Some(AnsiInfo::Esc) => { -                            //  TODO: flush                              ansi_code = None;                              format!("{}{}", AnsiInfo::Esc, c)                          }, @@ -342,6 +401,9 @@ fn ansi_aware_into_display_lines(x: Vec<String>, width: u16) -> Vec<String> {  pub fn paint(tweeter: &::tw::TwitterCache, display_info: &mut DisplayInfo) -> Result<(), std::io::Error> {      match termion::terminal_size() {          Ok((width, height)) => { +            if !display_info.dirty { +                return Ok(()); +            }              // draw input prompt              let mut i = 0;              let log_size = 4; @@ -405,7 +467,7 @@ pub fn paint(tweeter: &::tw::TwitterCache, display_info: &mut DisplayInfo) -> Re                          lines_drawn += 1;                      }                      h += lines_drawn - 3; -                    (cursor_idx as u16 + 3, height as u16 - 5) // TODO: panic on underflow +                    (cursor_idx as u16 + 3, height as u16 - 5) // TODO: this panics on underflow                  }                  Some(DisplayMode::Reply(twid, msg)) => {                      let mut lines: Vec<String> = vec![]; @@ -432,7 +494,7 @@ pub fn paint(tweeter: &::tw::TwitterCache, display_info: &mut DisplayInfo) -> Re                          lines_drawn += 1;                      }                      h += lines_drawn - 3; -                    (cursor_idx as u16 + 3, height as u16 - 5) // TODO: panic on underflow +                    (cursor_idx as u16 + 3, height as u16 - 5) // TODO: this panics on underflow                  }              }; @@ -522,6 +584,7 @@ pub fn paint(tweeter: &::tw::TwitterCache, display_info: &mut DisplayInfo) -> Re              println!("Can't get term dimensions: {}", e);          }      } +    display_info.dirty = false;      Ok(())  } @@ -778,6 +841,14 @@ pub fn render_twete_no_recurse(twete_id: &TweetId, tweeter: &tw::TwitterCache, d                              }                          }                      } +                    /* +                    for elem in tweet.media.iter() { +                        if line.contains(elem.0) { +                            result = result.replace(elem.0, &format!("[{}]", urls_to_include.len())); +                            urls_to_include.push(elem.0); +                        } +                    } +                    */                      result                  })                  .collect(); @@ -792,8 +863,6 @@ pub fn render_twete_no_recurse(twete_id: &TweetId, tweeter: &tw::TwitterCache, d                          if expanded.len() < (width - 9) as usize { // "[XX]: " is 6 + some padding space?                              text.push(format!("[{}]: {}", i, expanded));                          } else { -                            // TODO: try to just show domain, THEN fall back to just a link if the -                            // domain is too long                              text.push(format!("[{}]: {}", i, short_url));                          }                      } diff --git a/src/main.rs b/src/main.rs index 7595ea0..e5c3910 100644 --- a/src/main.rs +++ b/src/main.rs @@ -260,60 +260,68 @@ fn main() {  fn handle_input(event: termion::event::Event, tweeter: &mut tw::TwitterCache, queryer: &mut ::Queryer, display_info: &mut display::DisplayInfo) {      match event {          Event::Key(Key::Backspace) => { -            match display_info.mode.clone() { -                None => { display_info.input_buf.pop(); }, +            let new_mode = match display_info.get_mode().clone() { +                None => { display_info.input_buf_pop(); None },                  Some(display::DisplayMode::Compose(msg)) => {                      let mut newstr = msg.clone();                      newstr.pop(); -                    display_info.mode = Some(display::DisplayMode::Compose(newstr)); +                    Some(display::DisplayMode::Compose(newstr))                  },                  Some(display::DisplayMode::Reply(twid, msg)) => {                      let mut newstr = msg.clone();                      newstr.pop(); -                    display_info.mode = Some(display::DisplayMode::Reply(twid, newstr)); +                    Some(display::DisplayMode::Reply(twid, newstr))                  } -            } +            }; +            display_info.set_mode(new_mode);          }          // would Shift('\n') but.. that doesn't exist.          // would Ctrl('\n') but.. that doesn't work.          Event::Key(Key::Ctrl('u')) => { -             match display_info.mode.clone() { -                None => display_info.input_buf = vec![], +            let new_mode = match display_info.get_mode().clone() { +                None => { display_info.input_buf_drain(); None},                  Some(display::DisplayMode::Compose(msg)) => { -                    // TODO: clear only one line? -                    display_info.mode = Some(display::DisplayMode::Compose("".to_owned())); +                    Some(display::DisplayMode::Compose("".to_owned()))                  }                  Some(display::DisplayMode::Reply(twid, msg)) => { -                    display_info.mode = Some(display::DisplayMode::Reply(twid, "".to_owned())); +                    Some(display::DisplayMode::Reply(twid, "".to_owned()))                  } -            } +            }; +            display_info.set_mode(new_mode);          }          Event::Key(Key::Ctrl('n')) => { -            match display_info.mode.clone() { +            let new_mode = match display_info.get_mode().clone() {                  Some(display::DisplayMode::Compose(msg)) => { -                    display_info.mode = Some(display::DisplayMode::Compose(format!("{}{}", msg, "\n"))); +                    Some(display::DisplayMode::Compose(format!("{}{}", msg, "\n")))                  } -                _ => {} -            } +                mode @ _ => mode +            }; +            display_info.set_mode(new_mode);          } -        // TODO: ctrl+u, ctrl+w +        // TODO: ctrl+w          Event::Key(Key::Char(x)) => { -            match display_info.mode.clone() { +            // Unlike other cases where we care about DisplayMode here, +            // we can't just set the display mode in this function.. +            // +            // commands can change display mode, but might not, so just +            // let them do their thing and only explicitly set display +            // mode when we know we ought to +            match display_info.get_mode().clone() {                  None => {                      if x == '\n' { -                        let line = display_info.input_buf.drain(..).collect::<String>(); +                        let line = display_info.input_buf_drain();                          tweeter.handle_user_input(line.into_bytes(), queryer, display_info);                      } else { -                        display_info.input_buf.push(x); +                        display_info.input_buf_push(x);                      }                  }                  Some(display::DisplayMode::Compose(msg)) => {                      if x == '\n' {                          // TODO: move this somewhere better.                          ::commands::twete::send_twete(msg, tweeter, queryer, display_info); -                        display_info.mode = None; +                        display_info.set_mode(None)                      } else { -                        display_info.mode = Some(display::DisplayMode::Compose(format!("{}{}", msg, x))); +                        display_info.set_mode(Some(display::DisplayMode::Compose(format!("{}{}", msg, x))))                      }                  }                  Some(display::DisplayMode::Reply(twid, msg)) => { @@ -327,27 +335,27 @@ fn handle_input(event: termion::event::Event, tweeter: &mut tw::TwitterCache, qu                                  display_info.status("Cannot reply when not logged in".to_owned());                              }                          } -                        display_info.mode = None; +                        display_info.set_mode(None)                      } else { -                        display_info.mode = Some(display::DisplayMode::Reply(twid, format!("{}{}", msg, x))); +                        display_info.set_mode(Some(display::DisplayMode::Reply(twid, format!("{}{}", msg, x))))                      }                  } -            } +            };          },          Event::Key(Key::PageUp) => { -            display_info.infos_seek = display_info.infos_seek.saturating_add(1); +            display_info.adjust_infos_seek(Some(1));          }          Event::Key(Key::PageDown) => { -            display_info.infos_seek = display_info.infos_seek.saturating_sub(1); +            display_info.adjust_infos_seek(Some(-1));          }          Event::Key(Key::Home) => { -            display_info.log_seek = display_info.log_seek.saturating_add(1); +            display_info.adjust_log_seek(Some(1));          }          Event::Key(Key::End) => { -            display_info.log_seek = display_info.log_seek.saturating_sub(1); +            display_info.adjust_log_seek(Some(-1));          }          Event::Key(Key::Esc) => { -            display_info.mode = None; +            display_info.set_mode(None);          }          Event::Key(_) => { }          Event::Mouse(_) => { } @@ -356,9 +364,7 @@ fn handle_input(event: termion::event::Event, tweeter: &mut tw::TwitterCache, qu  }  fn handle_twitter_line(conn_id: u8, line: Vec<u8>, mut tweeter: &mut tw::TwitterCache, mut queryer: &mut ::Queryer, display_info: &mut display::DisplayInfo) { -    let jsonstr = std::str::from_utf8(&line).unwrap().trim(); -    /* TODO: replace from_str with from_slice? */ -    match serde_json::from_str(&jsonstr) { +    match serde_json::from_slice(&line) {          Ok(json) => {              tw::handle_message(conn_id, json, &mut tweeter, display_info, &mut queryer);              if tweeter.needs_save && tweeter.caching_permitted { @@ -366,7 +372,7 @@ fn handle_twitter_line(conn_id: u8, line: Vec<u8>, mut tweeter: &mut tw::Twitter              }          },          Err(e) => -            display_info.status(format!("Error reading twitter line: {}", jsonstr)) +            display_info.status(format!("Error reading twitter line: {:?}", std::str::from_utf8(&line)))      }  } @@ -445,7 +451,7 @@ fn do_ui(                  for command in commands::COMMANDS {                      help_lines.push(format!("{}{: <width$} {}", command.keyword, command.param_str, command.help_str, width=(35 - command.keyword.len())));                  } -                display_info.infos.push(display::Infos::Text(help_lines)); +                display_info.recv(display::Infos::Text(help_lines));                  display::paint(tweeter, display_info).unwrap();                  tweeter.state = tw::AppState::View;              } diff --git a/src/tw/mod.rs b/src/tw/mod.rs index 6dbc259..ffa2a56 100644 --- a/src/tw/mod.rs +++ b/src/tw/mod.rs @@ -252,7 +252,6 @@ mod tests {  impl TweetId {      pub fn parse(id_str: String) -> Result<TweetId, String> { -        // TODO: figure out how to return a Result<TweetId, <.. the result types ..>>          if id_str.starts_with("twitter:") {              Ok(TweetId::Twitter(id_str.chars().skip("twitter:".len()).collect()))          } else if id_str.starts_with(":") { @@ -429,49 +428,37 @@ impl TwitterProfile {      }      /*       * Returns: "did this change?" -     * -     * but currently pessimistically always returns true right now -     * TODO: check if this should return false! (that's probably a cache bug though.)       */      pub fn add_following(&mut self, user_id: &String) -> bool { -        self.following.insert(user_id.to_owned()); -        self.following_history.insert(user_id.to_owned(), ("following".to_string(), Utc::now().timestamp())); -        true +        let mut changed = self.following.insert(user_id.to_owned()); +        changed |= self.following_history.insert(user_id.to_owned(), ("following".to_string(), Utc::now().timestamp())).is_none(); +        changed      }      /*       * Returns: "did this change?" -     * -     * but currently pessimistically always returns true right now -     * TODO: check if this should return false! (that's probably a cache bug though.)       */      pub fn remove_following(&mut self, user_id: &String) -> bool { -        self.following.remove(user_id); -        self.following_history.insert(user_id.to_owned(), ("unfollowing".to_string(), Utc::now().timestamp())); -        true +        let mut changed = self.following.remove(user_id); +        changed |= self.following_history.insert(user_id.to_owned(), ("unfollowing".to_string(), Utc::now().timestamp())).is_some(); +        changed      }      /*       * Returns: "did this change?" -     * -     * but currently pessimistically always returns true right now -     * TODO: check if this should return false! (that's probably a cache bug though.)       */      pub fn add_follower(&mut self, user_id: &String) -> bool { -        self.followers.insert(user_id.to_owned()); -        self.lost_followers.remove(user_id); -        self.follower_history.insert(user_id.to_owned(), ("follow".to_string(), Utc::now().timestamp())); -        true +        let mut changed = self.followers.insert(user_id.to_owned()); +        changed |= self.lost_followers.remove(user_id); +        changed |= self.follower_history.insert(user_id.to_owned(), ("follow".to_string(), Utc::now().timestamp())).is_none(); +        changed      }      /*       * Returns: "did this change?" -     * -     * but currently pessimistically always returns true right now -     * TODO: check if this should return false! (that's probably a cache bug though.)       */      pub fn remove_follower(&mut self, user_id: &String) -> bool { -        self.followers.remove(user_id); -        self.lost_followers.insert(user_id.to_owned()); -        self.follower_history.insert(user_id.to_owned(), ("unfollow".to_string(), Utc::now().timestamp())); -        true +        let mut changed = self.followers.remove(user_id); +        changed |= self.lost_followers.insert(user_id.to_owned()); +        changed |= self.follower_history.insert(user_id.to_owned(), ("unfollow".to_string(), Utc::now().timestamp())).is_some(); +        changed      }  } @@ -851,7 +838,9 @@ fn handle_twitter_event(      tweeter.cache_api_event(conn_id, structure.clone(), queryer, display_info);      match events::Event::from_json(structure) {          Ok(event) => { -            display_info.recv(display::Infos::Event(event)); +            if !tweeter.event_muted(&event) { +                display_info.recv(display::Infos::Event(event)); +            }          },          Err(e) => {              display_info.status(format!("Unknown twitter json: {:?}", e)); @@ -882,10 +871,15 @@ fn handle_twitter_twete(      display_info: &mut DisplayInfo,      _queryer: &mut ::Queryer) {      //display_info.recv(display::Infos::Text(vec![format!("{:?}", structure)])); -    let twete_id = structure["id_str"].as_str().unwrap().to_string(); +    let twete_id = TweetId::Twitter( +        structure["id_str"].as_str().unwrap().to_string() +    );      tweeter.cache_api_tweet(serde_json::Value::Object(structure)); -    display_info.recv(display::Infos::Tweet(TweetId::Twitter(twete_id))); -    // display::render_twete(&twete_id, tweeter); +    if let Some(twete) = tweeter.retrieve_tweet(&twete_id) { +        if !tweeter.tweet_muted(twete) { +            display_info.recv(display::Infos::Tweet(twete_id)); +        } +    }  }  fn handle_twitter_dm( | 
