};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-struct FilePosition {
+pub struct FilePosition {
item: isize, // The selected item in the file
line: usize, // The line number within that item
fn get_from_client(id: &str, client: &mut Client) ->
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 {}
+struct StatusFeedType {
+ id: Option<FeedId>,
+}
+impl StatusFeedType {
+ fn with_feed(id: FeedId) -> Self { Self { id: Some(id) } }
+ fn without_feed() -> Self { Self { id: None } }
+}
impl FileType for StatusFeedType {
type Item = StatusDisplay;
let st = client.status_by_id(&id)?;
Ok(StatusDisplay::new(st.clone(), client))
}
+
+ fn feed_id(&self) -> Option<&FeedId> { self.id.as_ref() }
}
-struct NotificationStatusFeedType {}
+struct NotificationStatusFeedType {
+ id: FeedId,
+}
+impl NotificationStatusFeedType {
+ fn with_feed(id: FeedId) -> Self { Self { id } }
+}
impl FileType for NotificationStatusFeedType {
type Item = StatusDisplay;
"expected all notifications in this feed would have statuses");
Ok(StatusDisplay::new(st.clone(), client))
}
+
+ fn feed_id(&self) -> Option<&FeedId> { Some(&self.id) }
}
-struct EgoNotificationFeedType {}
+struct EgoNotificationFeedType {
+ id: FeedId,
+}
+impl EgoNotificationFeedType {
+ fn with_feed(id: FeedId) -> Self { Self { id } }
+}
impl FileType for EgoNotificationFeedType {
type Item = NotificationLog;
let not = client.notification_by_id(&id)?;
Ok(NotificationLog::from_notification(¬, client))
}
+
+ fn feed_id(&self) -> Option<&FeedId> { Some(&self.id) }
}
struct UserListFeedType {}
self.fix_overshoot_at_top();
}
}
+
+ fn save_file_position(
+ &self, file_positions: &mut HashMap<FeedId, FilePosition>)
+ {
+ self.file_desc.save_file_position(self.pos, file_positions);
+ }
}
-pub fn home_timeline(client: &mut Client) ->
+pub fn home_timeline(file_positions: &HashMap<FeedId, FilePosition>,
+ client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
+ let feed = FeedId::Home;
+ let pos = file_positions.get(&feed).cloned();
+ let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
- client, FeedSource::new(FeedId::Home), ColouredString::general(
+ client, FeedSource::new(feed), ColouredString::general(
"Home timeline <H>",
- "HHHHHHHHHHHHHHHHHKH"), StatusFeedType{}, None)?;
+ "HHHHHHHHHHHHHHHHHKH"), desc, pos)?;
Ok(Box::new(file))
}
-pub fn local_timeline(client: &mut Client) ->
+pub fn local_timeline(file_positions: &HashMap<FeedId, FilePosition>,
+ client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
+ let feed = FeedId::Local;
+ let pos = file_positions.get(&feed).cloned();
+ let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
- client, FeedSource::new(FeedId::Local), ColouredString::general(
+ client, FeedSource::new(feed), ColouredString::general(
"Local public timeline <L>",
- "HHHHHHHHHHHHHHHHHHHHHHHHHKH"), StatusFeedType{}, None)?;
+ "HHHHHHHHHHHHHHHHHHHHHHHHHKH"), desc, pos)?;
Ok(Box::new(file))
}
-pub fn public_timeline(client: &mut Client) ->
+pub fn public_timeline(file_positions: &HashMap<FeedId, FilePosition>,
+ client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
+ let feed = FeedId::Public;
+ let pos = file_positions.get(&feed).cloned();
+ let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
- client, FeedSource::new(FeedId::Public), ColouredString::general(
+ client, FeedSource::new(feed), ColouredString::general(
"Public timeline <P>",
- "HHHHHHHHHHHHHHHHHHHKH"), StatusFeedType{}, None)?;
+ "HHHHHHHHHHHHHHHHHHHKH"), desc, pos)?;
Ok(Box::new(file))
}
-pub fn mentions(client: &mut Client) ->
+pub fn mentions(file_positions: &HashMap<FeedId, FilePosition>,
+ client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
+ let feed = FeedId::Mentions;
+ let pos = file_positions.get(&feed).cloned();
+ let desc = NotificationStatusFeedType::with_feed(feed.clone());
let file = File::new(
- client, FeedSource::new(FeedId::Mentions), ColouredString::general(
+ client, FeedSource::new(feed), ColouredString::general(
"Mentions [ESC][R]",
- "HHHHHHHHHHHHKKKHHKH"), NotificationStatusFeedType{}, None)?;
+ "HHHHHHHHHHHHKKKHHKH"), desc, pos)?;
Ok(Box::new(file))
}
-pub fn ego_log(client: &mut Client) ->
+pub fn ego_log(file_positions: &HashMap<FeedId, FilePosition>,
+ client: &mut Client) ->
Result<Box<dyn ActivityState>, ClientError>
{
+ let feed = FeedId::Ego;
+ let pos = file_positions.get(&feed).cloned();
+ let desc = EgoNotificationFeedType::with_feed(feed.clone());
let file = File::new(
- client, FeedSource::new(FeedId::Ego), ColouredString::general(
+ client, FeedSource::new(feed), ColouredString::general(
"Ego Log [ESC][L][L][E]",
- "HHHHHHHHHHHKKKHHKHHKHHKH"), EgoNotificationFeedType{}, None)?;
+ "HHHHHHHHHHHKKKHHKHHKHHKH"), desc, pos)?;
Ok(Box::new(file))
}
{
let title = ColouredString::uniform(
&format!("Posts mentioning hashtag #{tag}"), 'H');
+ let feed = FeedId::Hashtag(tag.to_owned());
+ let desc = StatusFeedType::with_feed(feed.clone());
let file = File::new(
client, FeedSource::new(FeedId::Hashtag(tag.to_owned())), title,
- StatusFeedType{}, None)?;
+ desc, None)?;
Ok(Box::new(file))
}
.map_or(isize::MIN, |u| u as isize);
let file = File::new(
- client, StaticSource::vector(ids), title, StatusFeedType{},
+ client, StaticSource::vector(ids), title,
+ StatusFeedType::without_feed(),
Some(FilePosition::item_top(index as isize)))?;
Ok(Box::new(file))
}
style::{Style, Color, Modifier},
};
use std::cmp::min;
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
use std::io::{Stdout, Write, stdout};
use std::fs::File;
use unicode_width::UnicodeWidthStr;
LogicalAction;
fn handle_feed_updates(&mut self, _feeds_updated: &HashSet<FeedId>,
_client: &mut Client) {}
+ fn save_file_position(
+ &self, _file_positions: &mut HashMap<FeedId, FilePosition>) {}
}
struct TuiLogicalState {
activity_state: Box<dyn ActivityState>,
overlay_activity_state: Option<Box<dyn ActivityState>>,
last_area: Option<Rect>,
+ file_positions: HashMap<FeedId, FilePosition>,
}
impl TuiLogicalState {
activity_state,
overlay_activity_state: None,
last_area: None,
+ file_positions: HashMap::new(),
}
}
}
fn changed_activity(&mut self, client: &mut Client, post: Option<Post>) {
+ self.activity_state.save_file_position(&mut self.file_positions);
self.activity_state = self.new_activity_state(
self.activity_stack.top(), client, post);
self.overlay_activity_state = match self.activity_stack.overlay() {
Activity::Util(UtilityActivity::LogsMenu2) =>
Ok(logs_menu_2()),
Activity::NonUtil(NonUtilityActivity::HomeTimelineFile) =>
- home_timeline(client),
+ home_timeline(&self.file_positions, client),
Activity::NonUtil(NonUtilityActivity::PublicTimelineFile) =>
- public_timeline(client),
+ public_timeline(&self.file_positions, client),
Activity::NonUtil(NonUtilityActivity::LocalTimelineFile) =>
- local_timeline(client),
+ local_timeline(&self.file_positions, client),
Activity::NonUtil(NonUtilityActivity::HashtagTimeline(ref id)) =>
hashtag_timeline(client, id),
Activity::Util(UtilityActivity::ReadMentions) =>
- mentions(client),
+ mentions(&self.file_positions, client),
Activity::Util(UtilityActivity::EgoLog) =>
- ego_log(client),
+ ego_log(&self.file_positions, client),
Activity::Overlay(OverlayActivity::GetUserToExamine) =>
Ok(get_user_to_examine()),
Activity::Overlay(OverlayActivity::GetPostIdToRead) =>