From b5b0e5044514c3cb075531561dcae8f5531c5b25 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 4 Jan 2024 12:24:28 +0000 Subject: [PATCH] Save and restore file positions when changing activity. Now you go back to where you last were in the file. --- src/file.rs | 102 +++++++++++++++++++++++++++++++++++++++++----------- src/tui.rs | 17 +++++---- 2 files changed, 92 insertions(+), 27 deletions(-) diff --git a/src/file.rs b/src/file.rs index ea66e26..dac6e1a 100644 --- a/src/file.rs +++ b/src/file.rs @@ -12,7 +12,7 @@ use super::tui::{ }; #[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 @@ -117,9 +117,24 @@ trait FileType { fn get_from_client(id: &str, client: &mut Client) -> Result; + + fn feed_id(&self) -> Option<&FeedId> { None } + + fn save_file_position(&self, pos: FilePosition, + file_positions: &mut HashMap) { + if let Some(id) = self.feed_id() { + file_positions.insert(id.clone(), pos); + } + } } -struct StatusFeedType {} +struct StatusFeedType { + id: Option, +} +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; @@ -129,9 +144,16 @@ impl FileType for StatusFeedType { 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; @@ -143,9 +165,16 @@ impl FileType for NotificationStatusFeedType { "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; @@ -155,6 +184,8 @@ impl FileType for EgoNotificationFeedType { let not = client.notification_by_id(&id)?; Ok(NotificationLog::from_notification(¬, client)) } + + fn feed_id(&self) -> Option<&FeedId> { Some(&self.id) } } struct UserListFeedType {} @@ -1089,55 +1120,81 @@ impl self.fix_overshoot_at_top(); } } + + fn save_file_position( + &self, file_positions: &mut HashMap) + { + self.file_desc.save_file_position(self.pos, file_positions); + } } -pub fn home_timeline(client: &mut Client) -> +pub fn home_timeline(file_positions: &HashMap, + client: &mut Client) -> Result, 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 ", - "HHHHHHHHHHHHHHHHHKH"), StatusFeedType{}, None)?; + "HHHHHHHHHHHHHHHHHKH"), desc, pos)?; Ok(Box::new(file)) } -pub fn local_timeline(client: &mut Client) -> +pub fn local_timeline(file_positions: &HashMap, + client: &mut Client) -> Result, 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 ", - "HHHHHHHHHHHHHHHHHHHHHHHHHKH"), StatusFeedType{}, None)?; + "HHHHHHHHHHHHHHHHHHHHHHHHHKH"), desc, pos)?; Ok(Box::new(file)) } -pub fn public_timeline(client: &mut Client) -> +pub fn public_timeline(file_positions: &HashMap, + client: &mut Client) -> Result, 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

", - "HHHHHHHHHHHHHHHHHHHKH"), StatusFeedType{}, None)?; + "HHHHHHHHHHHHHHHHHHHKH"), desc, pos)?; Ok(Box::new(file)) } -pub fn mentions(client: &mut Client) -> +pub fn mentions(file_positions: &HashMap, + client: &mut Client) -> Result, 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, + client: &mut Client) -> Result, 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)) } @@ -1196,9 +1253,11 @@ pub fn hashtag_timeline(client: &mut Client, tag: &str) -> { 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)) } @@ -1292,7 +1351,8 @@ pub fn view_thread(client: &mut Client, start_id: &str, full: bool) -> .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)) } diff --git a/src/tui.rs b/src/tui.rs index fa78235..af8d238 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -11,7 +11,7 @@ use ratatui::{ 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; @@ -415,6 +415,8 @@ pub trait ActivityState { LogicalAction; fn handle_feed_updates(&mut self, _feeds_updated: &HashSet, _client: &mut Client) {} + fn save_file_position( + &self, _file_positions: &mut HashMap) {} } struct TuiLogicalState { @@ -422,6 +424,7 @@ struct TuiLogicalState { activity_state: Box, overlay_activity_state: Option>, last_area: Option, + file_positions: HashMap, } impl TuiLogicalState { @@ -437,6 +440,7 @@ impl TuiLogicalState { activity_state, overlay_activity_state: None, last_area: None, + file_positions: HashMap::new(), } } @@ -586,6 +590,7 @@ impl TuiLogicalState { } fn changed_activity(&mut self, client: &mut Client, post: Option) { + 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() { @@ -622,17 +627,17 @@ fn new_activity_state(&self, activity: Activity, client: &mut Client, 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) => -- 2.30.2