aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Wortman <ixineeringeverywhere@gmail.com>2017-11-08 02:05:09 -0800
committerAndy Wortman <ixineeringeverywhere@gmail.com>2017-11-08 02:05:09 -0800
commitaa5f8ff4bce898907ffc0c0e2b7ea36d7f8c10b7 (patch)
tree532704985e9b9484e9ac78fee9f95a29fdab48d3
parent41f6535a480bb9b0ff085e0144852827633f2305 (diff)
first brush of a compose mode, support thread viewing again
-rw-r--r--src/commands/mod.rs16
-rw-r--r--src/commands/twete.rs122
-rw-r--r--src/display/mod.rs142
-rw-r--r--src/main.rs122
-rw-r--r--src/tw/mod.rs17
5 files changed, 327 insertions, 92 deletions
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 49b2cba..9ec6c4b 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -7,14 +7,14 @@ pub struct Command {
pub exec: fn(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer)
}
-mod show_cache;
-mod twete;
-mod look_up;
-mod view;
-mod quit;
-mod fav;
-mod follow;
-mod thread;
+pub mod show_cache;
+pub mod twete;
+pub mod look_up;
+pub mod view;
+pub mod quit;
+pub mod fav;
+pub mod follow;
+pub mod thread;
pub static COMMANDS: &[&Command] = &[
&show_cache::SHOW_CACHE,
diff --git a/src/commands/twete.rs b/src/commands/twete.rs
index f057e5f..4452df9 100644
--- a/src/commands/twete.rs
+++ b/src/commands/twete.rs
@@ -36,13 +36,23 @@ fn del(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
pub static TWETE: Command = Command {
keyword: "t",
- params: 1,
+ params: 0,
exec: twete
};
fn twete(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
- let text = line.trim();
- let substituted = ::url_encode(text);
+ // if there's text, send it.
+ // if it's just "t", enter compose mode.
+ let text = line.trim().to_owned();
+ if text.len() == 0 {
+ tweeter.display_info.mode = Some(::display::DisplayMode::Compose(text));
+ } else {
+ send_twete(text, tweeter, queryer);
+ }
+}
+
+pub fn send_twete(text: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
+ let substituted = ::url_encode(&text);
if text.len() <= 140 {
match queryer.do_api_post(&format!("{}?status={}", CREATE_TWEET_URL, substituted)) {
Ok(_) => (),
@@ -61,6 +71,8 @@ pub static THREAD: Command = Command {
exec: thread
};
+// the difference between threading and replying is not including
+// yourself in th @'s.
fn thread(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let mut text: String = line.trim().to_string();
if let Some(id_end_idx) = text.find(" ") {
@@ -75,11 +87,7 @@ fn thread(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let handle = &tweeter.retrieve_user(&twete.author_id).unwrap().handle.to_owned();
// TODO: definitely breaks if you change your handle right now
if handle == &tweeter.current_user.handle {
- let substituted = ::url_encode(reply);
- match queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id)) {
- Ok(_) => (),
- Err(e) => tweeter.display_info.status(e)
- }
+ send_reply(reply.to_owned(), twid, tweeter, queryer);
} else {
tweeter.display_info.status("you can only thread your own tweets".to_owned());
// ask if it should .@ instead?
@@ -100,58 +108,70 @@ fn thread(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
pub static REP: Command = Command {
keyword: "rep",
- params: 2,
+ params: 1,
exec: rep
};
fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let mut text: String = line.trim().to_string();
- if let Some(id_end_idx) = text.find(" ") {
- let reply_bare = text.split_off(id_end_idx + 1);
- let reply = reply_bare.trim();
- let id_str = text.trim();
- if reply.len() > 0 {
- let maybe_id = TweetId::parse(id_str.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
- // get handles to reply to...
- let author_handle = tweeter.retrieve_user(&twete.author_id).unwrap().handle.to_owned();
- let mut ats: Vec<String> = twete.get_mentions(); //std::collections::HashSet::new();
- /*
- for handle in twete.get_mentions() {
- ats.insert(handle);
- }
- */
- ats.remove_item(&author_handle);
- ats.insert(0, author_handle);
- if let Some(rt_tweet) = twete.rt_tweet.and_then(|id| tweeter.retrieve_tweet(&TweetId::Twitter(id))).map(|x| x.clone()) {
- let rt_author_handle = tweeter.retrieve_user(&rt_tweet.author_id).unwrap().handle.to_owned();
- ats.remove_item(&rt_author_handle);
- ats.insert(1, rt_author_handle);
- }
- //let ats_vec: Vec<&str> = ats.into_iter().collect();
- //let full_reply = format!("{} {}", ats_vec.join(" "), reply);
- let decorated_ats: Vec<String> = ats.into_iter().map(|x| format!("@{}", x)).collect();
- let full_reply = format!("{} {}", decorated_ats.join(" "), reply);
- let substituted = ::url_encode(&full_reply);
- match queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id)) {
- Ok(_) => (),
- Err(e) => tweeter.display_info.status(e)
- }
- } else {
- tweeter.display_info.status(format!("No tweet for id: {:?}", twid));
- }
- },
- Err(e) => {
- tweeter.display_info.status(format!("Cannot parse input: {:?} ({})", id_str, e));
+ let reply_bare = match text.find(" ") {
+ None => "".to_owned(),
+ Some(id_end_idx) => {
+ text.split_off(id_end_idx + 1)
+ }
+ };
+ let reply = reply_bare.trim();
+ let id_str = text.trim();
+ let maybe_id = TweetId::parse(id_str.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
+ // get handles to reply to...
+ let author_handle = tweeter.retrieve_user(&twete.author_id).unwrap().handle.to_owned();
+ let mut ats: Vec<String> = twete.get_mentions(); //std::collections::HashSet::new();
+ ats.remove_item(&author_handle);
+ ats.insert(0, author_handle);
+ if let Some(rt_tweet) = twete.rt_tweet.and_then(|id| tweeter.retrieve_tweet(&TweetId::Twitter(id))).map(|x| x.clone()) {
+ let rt_author_handle = tweeter.retrieve_user(&rt_tweet.author_id).unwrap().handle.to_owned();
+ ats.remove_item(&rt_author_handle);
+ ats.insert(1, rt_author_handle);
}
+
+ // if you're directly replying to yourself, i trust you know what you're doing and
+ // want to @ yourself again (this keeps self-replies from showing up on your
+ // profile as threaded tweets, f.ex)
+ if !(ats.len() > 0 && &ats[0] == &tweeter.current_user.handle) {
+ ats.remove_item(&tweeter.current_user.handle);
+ }
+ //let ats_vec: Vec<&str> = ats.into_iter().collect();
+ //let full_reply = format!("{} {}", ats_vec.join(" "), reply);
+ let decorated_ats: Vec<String> = ats.into_iter().map(|x| format!("@{}", x)).collect();
+ let full_reply = format!("{} {}", decorated_ats.join(" "), reply);
+
+ if reply.len() > 0 {
+ send_reply(full_reply, twid, tweeter, queryer);
+ } else {
+ tweeter.display_info.mode = Some(::display::DisplayMode::Reply(twid, full_reply));
+ }
+ } else {
+ tweeter.display_info.status(format!("No tweet for id: {:?}", twid));
}
- } else {
- tweeter.display_info.status("rep <id> your sik reply".to_owned());
+ },
+ Err(e) => {
+ tweeter.display_info.status(format!("Cannot parse input: {:?} ({})", id_str, e));
+ }
+ }
+}
+
+pub fn send_reply(text: String, twid: TweetId, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
+ if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self
+ let substituted = ::url_encode(&text);
+ match queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
}
} else {
- tweeter.display_info.status("rep <id> your sik reply".to_owned());
+ tweeter.display_info.status(format!("Tweet stopped existing: {:?}", twid));
}
}
diff --git a/src/display/mod.rs b/src/display/mod.rs
index 73a1e09..4480855 100644
--- a/src/display/mod.rs
+++ b/src/display/mod.rs
@@ -13,6 +13,12 @@ use ::tw::TweetId;
use std;
#[derive(Clone)]
+pub enum DisplayMode {
+ Compose(String),
+ Reply(TweetId, String)
+}
+
+#[derive(Clone)]
pub enum Infos {
Tweet(TweetId),
TweetWithContext(TweetId, String),
@@ -22,20 +28,29 @@ pub enum Infos {
User(tw::user::User)
}
+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 infos: Vec<Infos>,
+ pub input_buf: Vec<char>
}
impl Default for DisplayInfo {
fn default() -> Self {
DisplayInfo {
+ log_height: 4,
+ prompt_height: 3,
+ mode: None,
log_seek: 0,
infos_seek: 0,
log: Vec::new(),
- infos: Vec::new()
+ infos: Vec::new(),
+ input_buf: Vec::new()
}
}
}
@@ -48,15 +63,34 @@ impl DisplayInfo {
pub fn recv(&mut self, info: Infos) {
self.infos.push(info);
}
+
+ pub fn ui_height(&self) -> u16 {
+ self.log_height + self.prompt_height
+ }
+}
+
+/*
+ * wraps x so each line is indentation or fewer characters, after splitting by \n.
+ */
+fn into_display_lines(x: Vec<String>, width: u16) -> Vec<String> {
+ 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
}
pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
match termion::terminal_size() {
- Ok((_width, height)) => {
+ Ok((width, height)) => {
// draw input prompt
- print!("{}{}", cursor::Goto(1, height - 6), clear::CurrentLine);
- print!("{}{}>", cursor::Goto(1, height - 5), clear::CurrentLine);
- print!("{}{}", cursor::Goto(1, height - 4), clear::CurrentLine);
let mut i = 0;
let log_size = 4;
let last_elem = tweeter.display_info.log.len().saturating_sub(log_size);
@@ -76,18 +110,68 @@ pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
let last_twevent = tweeter.display_info.infos.len().saturating_sub(height as usize - 4).saturating_sub(tweeter.display_info.infos_seek as usize);
let last_few_twevent: Vec<Infos> = tweeter.display_info.infos[last_twevent..].iter().map(|x| x.clone()).rev().collect::<Vec<Infos>>();
- let mut h = 7;
+ let mut h = tweeter.display_info.ui_height();
+
+ /*
+ * draw in whatever based on mode...
+ */
+ match tweeter.display_info.mode.clone() {
+ None => {
+ print!("{}{}", cursor::Goto(1, height - 6), clear::CurrentLine);
+ print!("{}{}@{}>{}", cursor::Goto(1, height - 5), clear::CurrentLine, tweeter.current_user.handle, tweeter.display_info.input_buf.clone().into_iter().collect::<String>());
+ print!("{}{}", cursor::Goto(1, height - 4), clear::CurrentLine);
+ }
+ Some(DisplayMode::Compose(x)) => {
+ let mut lines: Vec<String> = into_display_lines(x.split("\n").map(|x| x.to_owned()).collect(), width - 2);
+ if lines.len() == 0 {
+ lines.push("".to_owned());
+ }
+ // TODO: properly probe tweet length lim
+ lines.push(format!("{}/{}", x.len(), 140));
+ lines.insert(0, "".to_owned());
+ let mut lines_drawn: u16 = 0;
+ for line in lines.into_iter().rev() {
+ print!("{}{} {}{}{}{}",
+ cursor::Goto(1, height - 4 - lines_drawn), clear::CurrentLine,
+ color::Bg(color::Blue), line, std::iter::repeat(" ").take((width as usize).saturating_sub(line.len() + 2)).collect::<String>(), termion::style::Reset
+ );
+ lines_drawn += 1;
+ }
+ h += (lines_drawn - 3);
+ }
+ Some(DisplayMode::Reply(twid, msg)) => {
+ let mut lines = into_display_lines(render_twete(&twid, tweeter), width - 2);
+ lines.push(" -------- ".to_owned());
+ lines.extend(into_display_lines(msg.split("\n").map(|x| x.to_owned()).collect(), width - 2));
+ if lines.len() == 0 {
+ lines.push("".to_owned());
+ }
+ // TODO: properly probe tweet length lim
+ lines.push(format!("{}/{}", msg.len(), 140));
+ lines.insert(0, "".to_owned());
+ let mut lines_drawn: u16 = 0;
+ for line in lines.into_iter().rev() {
+ print!("{}{} {}{}{}{}",
+ cursor::Goto(1, height - 4 - lines_drawn), clear::CurrentLine,
+ color::Bg(color::Blue), line, std::iter::repeat(" ").take((width as usize).saturating_sub(line.len() + 2)).collect::<String>(), termion::style::Reset
+ );
+ lines_drawn += 1;
+ }
+ h += (lines_drawn - 3);
+ }
+ Some(_) => { }
+ }
+
for info in last_few_twevent {
let to_draw: Vec<String> = match info {
Infos::Tweet(id) => {
let pre_split: Vec<String> = render_twete(&id, tweeter);
- let split_on_newline: Vec<String> = pre_split.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();
+ let total_length: usize = pre_split.iter().map(|x| x.len()).sum();
+ let wrapped = if total_length <= 1024 {
+ into_display_lines(pre_split, width)
+ } else {
+ vec!["This tweet discarded for your convenience".to_owned()]
+ };
wrapped.into_iter().rev().collect()
}
Infos::TweetWithContext(id, context) => {
@@ -95,14 +179,34 @@ pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
lines.push(context);
lines
}
- Infos::Thread(_ids) => {
- let mut lines = vec![format!("{}{}I'd show a thread if I knew how", cursor::Goto(1, height - h), clear::CurrentLine)];
+ Infos::Thread(ids) => {
+ let mut tweets: Vec<Vec<String>> = ids.iter().rev().map(|x| into_display_lines(render_twete(x, tweeter), width)).collect();
+ let last = tweets.pop();
+ let mut lines = tweets.into_iter().fold(Vec::new(), |mut sum, lines| {
+ sum.extend(lines);
+ sum.extend(vec![
+ " ^".to_owned(),
+ " |".to_owned()
+ ]);
+ sum
+ });
+ if let Some(last_lines) = last {
+ lines.extend(last_lines);
+ }
+ //let mut lines = vec![format!("{}{}I'd show a thread if I knew how", cursor::Goto(1, height - h), clear::CurrentLine)];
lines.push("".to_owned());
// lines.push(format!("link: https://twitter.com/i/web/status/{}", id));
- lines
+ lines.into_iter().rev().collect()
},
Infos::Event(e) => {
- e.clone().render(tweeter).into_iter().rev().collect()
+ let pre_split = e.clone().render(tweeter);
+ let total_length: usize = pre_split.iter().map(|x| x.len()).sum();
+ let wrapped = if total_length <= 1024 {
+ into_display_lines(pre_split, width)
+ } else {
+ vec!["This tweet discarded for your convenience".to_owned()]
+ };
+ wrapped.into_iter().rev().collect()
},
Infos::DM(msg) => {
vec![format!("{}{}DM: {}", cursor::Goto(1, height - h), clear::CurrentLine, msg)]
@@ -132,7 +236,7 @@ pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
print!("{}{}", cursor::Goto(1, height - h), clear::CurrentLine);
h = h + 1;
}
- print!("{}", cursor::Goto(2, height - 5));
+ print!("{}", cursor::Goto(2 + 1 + tweeter.current_user.handle.len() as u16 + tweeter.display_info.input_buf.len() as u16, height - 5));
stdout().flush()?;
},
Err(e) => {
diff --git a/src/main.rs b/src/main.rs
index c46fec1..f8276ed 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,17 @@
#![feature(vec_remove_item)]
extern crate serde_json;
+extern crate termion;
+extern crate termios;
+
+use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
+
+use termion::input::TermRead;
+use termion::event::{Event, Key};
+
use std::str;
//use std::io::BufRead;
+use std::io::stdin;
#[macro_use] extern crate chan;
@@ -174,15 +183,13 @@ fn main() {
// let url = "https://stream.twitter.com/1.1/statuses/filter.json";
// let url = "https://stream.twitter.com/1.1/statuses/sample.json";
- let (ui_tx, mut ui_rx) = chan::sync::<Vec<u8>>(0);
+ let (ui_tx, mut ui_rx) = chan::sync::<Result<termion::event::Event, std::io::Error>>(0);
let mut twete_rx = connect_twitter_stream();
std::thread::spawn(move || {
- loop {
- let mut line = String::new();
- std::io::stdin().read_line(&mut line).unwrap();
- ui_tx.send(line.into_bytes());
+ for input in stdin().events() {
+ ui_tx.send(input);
}
});
@@ -211,6 +218,13 @@ fn main() {
core: c2
};
+ let termios = Termios::from_fd(0).unwrap();
+ let mut new_termios = termios.clone();
+
+ // fix terminal to not echo, thanks
+ new_termios.c_lflag &= !(ICANON | ECHO);
+
+ tcsetattr(0, TCSANOW, &new_termios).unwrap();
loop {
match do_ui(ui_rx, twete_rx, &mut tweeter, &mut queryer) {
Some((new_ui_rx, new_twete_rx)) => {
@@ -222,9 +236,82 @@ fn main() {
}
}
}
+ tcsetattr(0, TCSANOW, &termios);
}
-fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>, mut tweeter: &mut tw::TwitterCache, mut queryer: &mut ::Queryer) -> Option<(chan::Receiver<Vec<u8>>, chan::Receiver<Vec<u8>>)> {
+fn handle_input(event: termion::event::Event, tweeter: &mut tw::TwitterCache, queryer: &mut ::Queryer) {
+ match event {
+ Event::Key(Key::Backspace) => {
+ match tweeter.display_info.mode.clone() {
+ None => { tweeter.display_info.input_buf.pop(); },
+ Some(display::DisplayMode::Compose(msg)) => {
+ let mut newstr = msg.clone();
+ newstr.pop();
+ tweeter.display_info.mode = Some(display::DisplayMode::Compose(newstr));
+ },
+ Some(display::DisplayMode::Reply(twid, msg)) => {
+ let mut newstr = msg.clone();
+ newstr.pop();
+ tweeter.display_info.mode = Some(display::DisplayMode::Reply(twid, newstr));
+ }
+ }
+ }
+ // would Shift('\n') but.. that doesn't exist.
+ // would Ctrl('\n') but.. that doesn't work.
+ Event::Key(Key::Ctrl('n')) => {
+ match tweeter.display_info.mode.clone() {
+ Some(display::DisplayMode::Compose(msg)) => {
+ tweeter.display_info.mode = Some(display::DisplayMode::Compose(format!("{}{}", msg, "\n")));
+ }
+ _ => {}
+ }
+ }
+ Event::Key(Key::Char(x)) => {
+ match tweeter.display_info.mode.clone() {
+ None => {
+ if x == '\n' {
+ let line = tweeter.display_info.input_buf.drain(..).collect::<String>();
+ tweeter.handle_user_input(line.into_bytes(), queryer);
+ } else {
+ tweeter.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);
+ tweeter.display_info.mode = None;
+ } else {
+ tweeter.display_info.mode = Some(display::DisplayMode::Compose(format!("{}{}", msg, x)));
+ }
+ }
+ Some(display::DisplayMode::Reply(twid, msg)) => {
+ if x == '\n' {
+ // TODO: move this somewhere better.
+ ::commands::twete::send_reply(msg, twid, tweeter, queryer);
+ tweeter.display_info.mode = None;
+ } else {
+ tweeter.display_info.mode = Some(display::DisplayMode::Reply(twid, format!("{}{}", msg, x)));
+ }
+ }
+ }
+ },
+ Event::Key(Key::PageUp) => {
+ tweeter.display_info.infos_seek += 1;
+ }
+ Event::Key(Key::PageDown) => {
+ tweeter.display_info.infos_seek = tweeter.display_info.infos_seek.saturating_sub(1);
+ }
+ Event::Key(Key::Esc) => {
+ tweeter.display_info.mode = None;
+ }
+ Event::Key(_) => { }
+ Event::Mouse(_) => { }
+ Event::Unsupported(_) => {}
+ }
+}
+
+fn do_ui(ui_rx_orig: chan::Receiver<Result<termion::event::Event, std::io::Error>>, twete_rx: chan::Receiver<Vec<u8>>, mut tweeter: &mut tw::TwitterCache, mut queryer: &mut ::Queryer) -> Option<(chan::Receiver<Result<termion::event::Event, std::io::Error>>, chan::Receiver<Vec<u8>>)> {
loop {
let ui_rx_a = &ui_rx_orig;
let ui_rx_b = &ui_rx_orig;
@@ -243,27 +330,38 @@ fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>,
tweeter.display_info.status("Twitter stream hung up...".to_owned());
chan_select! {
ui_rx_b.recv() -> input => match input {
- Some(line) => {
- if line == "reconnect\n".as_bytes() {
- return Some((ui_rx_orig.clone(), connect_twitter_stream()));
+ Some(maybe_event) => {
+ if let Ok(event) = maybe_event {
+ handle_input(event, tweeter, queryer);
} else {
- tweeter.handle_user_input(line, &mut queryer);
+ // stdin closed?
}
}
+ // twitter stream closed, ui thread closed, uhh..
None => std::process::exit(0)
}
}
}
},
ui_rx_a.recv() -> user_input => match user_input {
- Some(line) => {
- tweeter.handle_user_input(line, &mut queryer);
+ Some(maybe_event) => {
+ if let Ok(event) = maybe_event {
+ handle_input(event, tweeter, queryer); // eventually DisplayInfo too, as a separate piece of data...
+ } else {
+ // dunno how we'd reach this... stdin closed?
+ }
},
None => tweeter.display_info.status("UI thread hung up...".to_owned())
}
// and then we can introduce a channel that just sends a message every 100 ms or so
// that acts as a clock!
}
+
+ match tweeter.state {
+ tw::AppState::Reconnect => return Some((ui_rx_orig.clone(), connect_twitter_stream())),
+ _ => ()
+ };
+
// one day display_info should be distinct
match display::paint(tweeter) {
Ok(_) => (),
diff --git a/src/tw/mod.rs b/src/tw/mod.rs
index 82dfe10..b1bc9c2 100644
--- a/src/tw/mod.rs
+++ b/src/tw/mod.rs
@@ -22,6 +22,16 @@ use self::tweet::Tweet;
pub mod user;
use self::user::User;
+pub enum AppState {
+ Reconnect,
+ Compose,
+ View
+}
+
+impl Default for AppState {
+ fn default() -> AppState { AppState::View }
+}
+
pub fn full_twete_text(twete: &serde_json::map::Map<String, serde_json::Value>) -> String {
if twete.contains_key("retweeted_status") {
return full_twete_text(twete["retweeted_status"].as_object().unwrap())
@@ -103,7 +113,9 @@ pub struct TwitterCache {
#[serde(skip)]
id_conversions: IdConversions,
#[serde(skip)]
- pub display_info: display::DisplayInfo
+ pub display_info: display::DisplayInfo,
+ #[serde(skip)]
+ pub state: AppState
}
// Internally, a monotonically increasin i64 is always the id used.
@@ -259,7 +271,8 @@ impl TwitterCache {
current_user: User::default(),
threads: HashMap::new(),
id_conversions: IdConversions::default(),
- display_info: display::DisplayInfo::default()
+ display_info: display::DisplayInfo::default(),
+ state: AppState::View
}
}