use super::tui::{
ActivityState, CursorPosition, LogicalAction,
OurKey, OurKey::*,
+ SavedFilePos,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
Result<Self::Item, ClientError>;
fn feed_id(&self) -> Option<&FeedId> { None }
-
- fn save_file_position(&self, pos: FilePosition,
- file_positions: &mut HashMap<FeedId, FilePosition>) {
- if let Some(id) = self.feed_id() {
- file_positions.insert(id.clone(), pos);
- }
- }
}
struct StatusFeedType {
}
}
+ fn id_at_index(&self, index: isize) -> Option<&str> {
+ index.checked_sub(self.origin)
+ .and_then(|i: isize| i.try_into().ok())
+ .and_then(|u: usize| self.items.get(u))
+ .map(|item: &(String, Type::Item)| &item.0 as &str)
+ }
+
fn index_of_id(&self, id: &str) -> Option<isize> {
// We can't do anything efficient like binary search, because
// our ids might not be in any sensible order. (If they're,
impl<Type: FileType, Source: FileDataSource> File<Type, Source> {
fn new(client: &mut Client, source: Source, desc: ColouredString,
- file_desc: Type, initial_pos: Option<FilePosition>) ->
+ file_desc: Type, saved_pos: Option<&SavedFilePos>,
+ show_new: bool) ->
Result<Self, ClientError>
{
source.init(client)?;
contents.update_items(client);
- // FIXME: once we have an LDB, that's where initial pos comes from
- let mut initial_pos = initial_pos.unwrap_or_else(
- || FilePosition::item_bottom(isize::MAX));
+ // Start with the initial position at the file top
+ let mut initial_pos = FilePosition::item_top(contents.first_index());
+
+ // If we have a 'latest read item' from the saved file
+ // position, determine it, and override the initial position
+ // with it
+ let mut latest_read_index = None;
+ if let Some(saved_pos) = saved_pos {
+ latest_read_index = saved_pos.latest_read_id.as_ref().and_then(
+ |id| contents.index_of_id(id));
+ if let Some(latest_read_index) = latest_read_index {
+ initial_pos = if show_new {
+ FilePosition::item_top(latest_read_index + 1)
+ } else {
+ FilePosition::item_bottom(latest_read_index)
+ }
+ }
+ }
+
+ // But if we have an actual FilePosition in our SavedFilePos,
+ // it's even better to use that.
+ if let Some(file_pos) = saved_pos.and_then(|sp| sp.file_pos) {
+ initial_pos = file_pos;
+ }
+
+ // Now clip initial_pos at the top and bottom of the data we have
initial_pos = initial_pos.clip(
contents.first_index(), contents.last_index());
file_desc,
search_direction: None,
last_search: None,
- latest_read_index: None,
+ latest_read_index,
};
Ok(ff)
}
}
fn save_file_position(
- &self, file_positions: &mut HashMap<FeedId, FilePosition>)
+ &self, file_positions: &mut HashMap<FeedId, SavedFilePos>)
{
- self.file_desc.save_file_position(self.pos, file_positions);
+ if let Some(id) = self.file_desc.feed_id() {
+ let sfp = SavedFilePos {
+ file_pos: Some(self.pos),
+ latest_read_id: self.latest_read_index
+ .and_then(|i| self.contents.id_at_index(i))
+ .map(|s| s.to_owned()),
+ };
+ file_positions.insert(id.clone(), sfp);
+ todo!();
+ }
}
fn got_search_expression(&mut self, dir: SearchDirection, regex: String)
}
}
-pub fn home_timeline(file_positions: &HashMap<FeedId, FilePosition>,
+pub fn home_timeline(file_positions: &HashMap<FeedId, SavedFilePos>,
client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
let feed = FeedId::Home;
- let pos = file_positions.get(&feed).cloned();
+ let pos = file_positions.get(&feed);
let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
client, FeedSource::new(feed), ColouredString::general(
"Home timeline <H>",
- "HHHHHHHHHHHHHHHHHKH"), desc, pos)?;
+ "HHHHHHHHHHHHHHHHHKH"), desc, pos, false)?;
Ok(Box::new(file))
}
-pub fn local_timeline(file_positions: &HashMap<FeedId, FilePosition>,
+pub fn local_timeline(file_positions: &HashMap<FeedId, SavedFilePos>,
client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
let feed = FeedId::Local;
- let pos = file_positions.get(&feed).cloned();
+ let pos = file_positions.get(&feed);
let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
client, FeedSource::new(feed), ColouredString::general(
"Local public timeline <L>",
- "HHHHHHHHHHHHHHHHHHHHHHHHHKH"), desc, pos)?;
+ "HHHHHHHHHHHHHHHHHHHHHHHHHKH"), desc, pos, false)?;
Ok(Box::new(file))
}
-pub fn public_timeline(file_positions: &HashMap<FeedId, FilePosition>,
+pub fn public_timeline(file_positions: &HashMap<FeedId, SavedFilePos>,
client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
let feed = FeedId::Public;
- let pos = file_positions.get(&feed).cloned();
+ let pos = file_positions.get(&feed);
let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
client, FeedSource::new(feed), ColouredString::general(
"Public timeline <P>",
- "HHHHHHHHHHHHHHHHHHHKH"), desc, pos)?;
+ "HHHHHHHHHHHHHHHHHHHKH"), desc, pos, false)?;
Ok(Box::new(file))
}
-pub fn mentions(file_positions: &HashMap<FeedId, FilePosition>,
+pub fn mentions(file_positions: &HashMap<FeedId, SavedFilePos>,
client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
let feed = FeedId::Mentions;
- let pos = file_positions.get(&feed).cloned();
+ let pos = file_positions.get(&feed);
let desc = NotificationStatusFeedType::with_feed(feed.clone());
let file = File::new(
client, FeedSource::new(feed), ColouredString::general(
"Mentions [ESC][R]",
- "HHHHHHHHHHHHKKKHHKH"), desc, pos)?;
+ "HHHHHHHHHHHHKKKHHKH"), desc, pos, false)?;
Ok(Box::new(file))
}
-pub fn ego_log(file_positions: &HashMap<FeedId, FilePosition>,
+pub fn ego_log(file_positions: &HashMap<FeedId, SavedFilePos>,
client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
let feed = FeedId::Ego;
- let pos = file_positions.get(&feed).cloned();
+ let pos = file_positions.get(&feed);
let desc = EgoNotificationFeedType::with_feed(feed.clone());
let file = File::new(
client, FeedSource::new(feed), ColouredString::general(
"Ego Log [ESC][L][L][E]",
- "HHHHHHHHHHHKKKHHKHHKHHKH"), desc, pos)?;
+ "HHHHHHHHHHHKKKHHKHHKHHKH"), desc, pos, false)?;
Ok(Box::new(file))
}
pub fn user_posts(
- file_positions: &HashMap<FeedId, FilePosition>, client: &mut Client,
+ file_positions: &HashMap<FeedId, SavedFilePos>, client: &mut Client,
user: &str, boosts: Boosts, replies: Replies)
-> Result<Box<dyn ActivityState>, ClientError>
{
let feed = FeedId::User(user.to_owned(), boosts, replies);
- let pos = file_positions.get(&feed).cloned();
+ let pos = file_positions.get(&feed);
let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
client, FeedSource::new(feed), ColouredString::general(
"Public timeline <P>",
- "HHHHHHHHHHHHHHHHHHHKH"), desc, pos)?;
+ "HHHHHHHHHHHHHHHHHHHKH"), desc, pos, false)?;
Ok(Box::new(file))
}
client, FeedSource::new(FeedId::Favouriters(id.to_owned())),
ColouredString::uniform(
&format!("Users who favourited post {id}"), 'H'),
- UserListFeedType{}, None)?;
+ UserListFeedType{}, None, false)?;
Ok(Box::new(file))
}
client, FeedSource::new(FeedId::Boosters(id.to_owned())),
ColouredString::uniform(
&format!("Users who boosted post {id}"), 'H'),
- UserListFeedType{}, None)?;
+ UserListFeedType{}, None, false)?;
Ok(Box::new(file))
}
client, FeedSource::new(FeedId::Followers(id.to_owned())),
ColouredString::uniform(
&format!("Users who follow {name}"), 'H'),
- UserListFeedType{}, None)?;
+ UserListFeedType{}, None, false)?;
Ok(Box::new(file))
}
client, FeedSource::new(FeedId::Followees(id.to_owned())),
ColouredString::uniform(
&format!("Users who {name} follows"), 'H'),
- UserListFeedType{}, None)?;
+ UserListFeedType{}, None, false)?;
Ok(Box::new(file))
}
let desc = StatusFeedType::with_feed(feed);
let file = File::new(
client, FeedSource::new(FeedId::Hashtag(tag.to_owned())), title,
- desc, None)?;
+ desc, None, false)?;
Ok(Box::new(file))
}
let file = File::new(
client, StaticSource::singleton(ac.id), title, ExamineUserFileType{},
- Some(FilePosition::item_top(isize::MIN)))?;
+ Some(&FilePosition::item_top(isize::MIN).into()), false)?;
Ok(Box::new(file))
}
let file = File::new(
client, StaticSource::singleton(st.id), title,
DetailedStatusFileType{},
- Some(FilePosition::item_top(isize::MIN)))?;
+ Some(&FilePosition::item_top(isize::MIN).into()), false)?;
Ok(Box::new(file))
}
let file = File::new(
client, StaticSource::vector(ids), title,
StatusFeedType::without_feed(),
- Some(FilePosition::item_top(index)))?;
+ Some(&FilePosition::item_top(index).into()), false)?;
Ok(Box::new(file))
}