User(String, Boosts, Replies),
}
+#[derive(Debug)]
pub struct Feed {
pub ids: VecDeque<String>, // ids, whether of statuses, accounts or what
pub origin: isize,
permit_write: bool,
}
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ClientError {
InternalError(String), // message
UrlParseError(String, String), // url, message
Ok(st)
}
+ // Ok(bool) tells you whether any new items were in fact retrieved
pub fn fetch_feed(&mut self, id: &FeedId, ext: FeedExtend) ->
- Result<(), ClientError>
+ Result<bool, ClientError>
{
if ext == FeedExtend::Initial {
if self.feeds.contains_key(id) {
- // No need to fetch the initial contents - we already have some
- return Ok(());
+ // No need to fetch the initial contents - we already
+ // have some.
+ //
+ // In this situation the boolean return value is not
+ // expected to very useful: its main purpose is when
+ // finding out if extending an _existing_ feed did
+ // anything. But we might as well return false in the
+ // case where nothing changed, and true in the case
+ // below where we did end up retrieving something.
+ return Ok(false);
}
} else {
assert!(self.feeds.contains_key(id),
Err(ClientError::UrlError(url.clone(), e.to_string()))
},
}?;
+ let any_new = !sts.is_empty();
for st in &sts {
self.cache_status(st);
}
let feed = self.feeds.get_mut(id).unwrap();
for id in ids.iter().rev() {
feed.ids.push_front(id.to_string());
- feed.origin += 1;
+ feed.origin -= 1;
}
},
}
- Ok(())
+ Ok(any_new)
}
pub fn borrow_feed(&self, id: &FeedId) -> &Feed {
struct FeedFileContents {
id: FeedId,
header: FileHeader,
+ extender: Option<ExtendableIndicator>,
origin: isize,
items: Vec<Box<dyn TextFragment>>,
}
impl FeedFileContents {
fn update_items(&mut self, client: &mut Client) {
- // FIXME: deal with origin, and with the feed having been
- // extended since we last looked
+ // FIXME: deal with the feed having been extended
let feed = client.borrow_feed(&self.id);
let ids: Vec<_> = feed.ids.iter().map(|x| x.clone()).collect();
}
}
- fn first_index(&self) -> isize { self.origin - 1 }
+ fn first_index(&self) -> isize {
+ let extcount = if self.extender.is_some() { 1 } else { 0 };
+ self.origin - 1 - extcount
+ }
+ fn extender_index(&self) -> Option<isize> {
+ if self.extender.is_some() { Some(self.origin - 1) } else { None }
+ }
fn index_limit(&self) -> isize {
self.origin.checked_add_unsigned(self.items.len())
.expect("Out-of-range index")
}
fn get(&self, index: isize) -> &dyn TextFragment {
- if index == self.origin - 1 {
+ if index == self.first_index() {
&self.header
+ } else if Some(index) == self.extender_index() {
+ match self.extender {
+ Some(ref ext) => ext,
+ _ => panic!("But it must be there, extender_index exists"),
+ }
} else {
let index = self.phys_index(index);
&*self.items[index]
let mut contents = FeedFileContents {
id: id,
header: FileHeader::new(desc),
+ extender: Some(ExtendableIndicator::new()),
origin: 0,
items: Vec::new(),
};
self.rendered.get(&index).expect("We just made sure this was present")
}
- fn ensure_enough_rendered(&mut self) -> usize {
+ fn ensure_enough_rendered(&mut self) -> Option<usize> {
let (w, h) = self.last_size.expect(
"ensure_enough_rendered before resize");
let mut item = item;
let mut lines_rendered = line;
- while item > self.contents.first_index() && lines_rendered + 1 < h {
+ while item > self.contents.first_index() && lines_rendered < h {
item -= 1;
lines_rendered += self.ensure_item_rendered(item, w).len();
}
- h - min(h, lines_rendered + 1)
+ if lines_rendered + 1 <= h {
+ Some(h - 1 - lines_rendered)
+ } else {
+ None
+ }
+ }
+
+ fn after_setting_pos(&mut self) {
+ let (w, _h) = self.last_size.expect(
+ "ensure_enough_rendered before setting pos");
+ let at_top = self.at_top();
+ match &mut self.contents.extender {
+ Some(ref mut ext) => {
+ ext.set_primed(at_top);
+ },
+ _ => (),
+ };
+ if let Some(ei) = self.contents.extender_index() {
+ self.rendered.remove(&ei);
+ self.ensure_item_rendered(ei, w);
+ }
}
fn refine_pos(&mut self, w: usize) -> (isize, usize) {
self.pos = newpos;
}
+ fn fix_overshoot_at_top(&mut self) {
+ // If an attempt to move up the document left us with no line
+ // to display at the top of the screen, move down again to fix
+ // it
+ if let Some(overshoot) = self.ensure_enough_rendered() {
+ // Clip position at top of file
+ if overshoot > 0 {
+ self.move_down_inner(overshoot);
+ }
+ }
+ }
+
+ fn at_top(&mut self) -> bool { self.ensure_enough_rendered().is_some() }
+
fn move_up(&mut self, distance: usize) {
let (w, _h) = self.last_size.expect("move_up before resize");
let (item, line) = match self.pos {
}
}
self.pos = FilePosition::Fine(item, line);
- let overshoot = self.ensure_enough_rendered();
- // Clip position at top of file
- if overshoot > 0 {
- self.move_down(overshoot);
- }
+ self.fix_overshoot_at_top();
+ self.after_setting_pos();
}
- fn move_down(&mut self, distance: usize) {
+ fn move_down_inner(&mut self, distance: usize) {
let (w, _h) = self.last_size.expect("move_down before resize");
let (item, line) = match self.pos {
FilePosition::Fine(item, line) => (item, line),
self.ensure_enough_rendered();
}
+ fn move_down(&mut self, distance: usize) {
+ self.move_down_inner(distance);
+ self.after_setting_pos();
+ }
+
fn goto_top(&mut self) {
self.pos = FilePosition::Fine(self.contents.first_index(), 0);
- let overshoot = self.ensure_enough_rendered();
- // Clip position at top of file
- if overshoot > 0 {
- self.move_down(overshoot);
- }
+ self.fix_overshoot_at_top();
+ self.after_setting_pos();
}
fn goto_bottom(&mut self) {
let line = self.ensure_item_rendered(item, w).len();
self.pos = FilePosition::Fine(item, line);
self.ensure_enough_rendered();
+ self.after_setting_pos();
}
}
self.rendered.clear();
}
self.ensure_enough_rendered();
+ self.after_setting_pos();
}
fn draw(&self, w: usize, h: usize)
(lines, CursorPosition::End)
}
- fn handle_keypress(&mut self, key: OurKey, _client: &mut Client) ->
+ fn handle_keypress(&mut self, key: OurKey, client: &mut Client) ->
LogicalAction
{
let (_w, h) = match self.last_size {
LogicalAction::Nothing
},
Pr('0') | Home => {
- self.goto_top();
- LogicalAction::Nothing
+ if self.at_top() && self.contents.extender.is_some() {
+ match client.fetch_feed(&self.contents.id,
+ FeedExtend::Past) {
+ Ok(any_new) => {
+ self.rendered.remove(&self.contents.first_index());
+ if let Some(i) = self.contents.extender_index() {
+ self.rendered.remove(&i);
+ }
+
+ if !any_new {
+ // Can't extend this any further into the past
+ // FIXME: this is not tested yet
+ self.contents.extender = None;
+ }
+
+ self.contents.update_items(client);
+ self.ensure_enough_rendered();
+ LogicalAction::Nothing
+ },
+ Err(e) => LogicalAction::Error(e),
+ }
+ } else {
+ self.goto_top();
+ LogicalAction::Nothing
+ }
}
Pr('z') | Pr('Z') | End => {
self.goto_bottom();