chiark / gitweb /
When thrown into Read Mentions, show the new mention.
authorSimon Tatham <anakin@pobox.com>
Sat, 6 Jan 2024 13:57:23 +0000 (13:57 +0000)
committerSimon Tatham <anakin@pobox.com>
Sat, 6 Jan 2024 14:06:13 +0000 (14:06 +0000)
This adds an 'is_interrupt' flag to every change of activity; the only
activity constructor that uses it is mentions(), which uses it to vary
the policy about file positioning. When you enter Read Mentions on
your own initiative, you get the same position you were at before, but
when _thrown_ into it, you go to the new message.

In order to still show the new message when you're _already_ in your
mentions, we remove ReadMentions from the list of activities that
don't cause a reload of ReadMentions.

TODO.md
src/activity_stack.rs
src/file.rs
src/tui.rs

diff --git a/TODO.md b/TODO.md
index df7f094439231c724698d2449cb0dce135ca2163..6e9c9659acf26b845e0abc5e33ff8d1c23d017b5 100644 (file)
--- a/TODO.md
+++ b/TODO.md
@@ -32,16 +32,11 @@ Each of those should instead catch the error, make an Error Log
 record, and transfer you to the error-log viewer with a beep, just the
 way Mono did it.
 
-## Tracking what you've read
+## LDB checkpointing
 
-When you restart Mastodonochrome and open a feed, it ought to put in
-view the first thing you haven't yet read in that feed. This means
-tracking what _is_ read during run time, and saving it to a file in
-the config directory.
-
-Similarly, when the client automatically moves you into the Read
-Mentions field because a new mention has arrived, it should
-automatically jump the position to the first unread one.
+It would be nice to checkpoint the LDB to disk every few minutes, if
+it's changed, as a precaution against data loss if the client crashes
+after sitting in the same `File` for a long time.
 
 ## Reading
 
@@ -111,6 +106,18 @@ and also showing a red error marker for 'no alt text here yet'.
 You should be able to post polls, again via some kind of internal
 editor markup that inserts magic delimiter characters.
 
+### Mentions arriving while you're editing
+
+If you got a beep for an incoming mention while you were writing a
+post of your own, then in real Mono, finishing up your post causes you
+to be belatedly thrown into your mentions. Perhaps we should replicate
+that.
+
+### Saving a draft
+
+If you ESC out of the editor, your draft should be saved to come back
+to.
+
 ## Editor improvements
 
 ### Block cut and paste
index 42a013509903300f796b63f1dd27bb2b342e8540..ad9b3c905bc1555bf1d56ef43bc40b24f6dbe57d 100644 (file)
@@ -67,10 +67,13 @@ pub struct ActivityStack {
 
 impl Activity {
     pub fn throw_into_mentions(&self) -> bool {
-        // ReadMentions itself doesn't count. This will have to return
-        // false for post-editing activities.
+        // When you're editing a post, you don't get thrown into your
+        // mentions. From anywhere else, you do.
+        //
+        // This even counts the ReadMentions activity _itself_ - it
+        // gets reinitialised, because that's the simplest way to jump
+        // you down to the new message.
         match self {
-            Activity::Util(UtilityActivity::ReadMentions) |
             Activity::NonUtil(NonUtilityActivity::ComposeToplevel) |
             Activity::NonUtil(NonUtilityActivity::PostComposeMenu) |
             Activity::Util(UtilityActivity::ComposeReply(..)) |
index 8110e0f88a5237286bf2ef147074651c431b09f1..f0458c3ed927fab3e2f4dba53e7cc3efb21f13c8 100644 (file)
@@ -365,14 +365,17 @@ impl<Type: FileType, Source: FileDataSource> File<Type, Source> {
                     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;
+        // it's even better to use that ... unless we're supposed to
+        // be showing a new thing eagerly.
+        if !show_new {
+            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
@@ -1377,7 +1380,7 @@ pub fn public_timeline(file_positions: &HashMap<FeedId, SavedFilePos>,
 }
 
 pub fn mentions(file_positions: &HashMap<FeedId, SavedFilePos>,
-                client: &mut Client) ->
+                client: &mut Client, is_interrupt: bool) ->
     Result<Box<dyn ActivityState>, ClientError>
 {
     let feed = FeedId::Mentions;
@@ -1386,7 +1389,7 @@ pub fn mentions(file_positions: &HashMap<FeedId, SavedFilePos>,
     let file = File::new(
         client, FeedSource::new(feed), ColouredString::general(
             "Mentions   [ESC][R]",
-            "HHHHHHHHHHHHKKKHHKH"), desc, pos, false)?;
+            "HHHHHHHHHHHHKKKHHKH"), desc, pos, is_interrupt)?;
     Ok(Box::new(file))
 }
 
index 86627857e33edfd0d797b895d708d95db87c65b2..296bd9a38fc8977b2dc3ef3dd6ce4d5e47a9029d 100644 (file)
@@ -580,12 +580,12 @@ impl TuiLogicalState {
                 LogicalAction::Nothing => break PhysicalAction::Nothing,
                 LogicalAction::Goto(activity) => {
                     self.activity_stack.goto(activity);
-                    self.changed_activity(client, None);
+                    self.changed_activity(client, None, false);
                     break PhysicalAction::Nothing
                 }
                 LogicalAction::Pop => {
                     self.activity_stack.pop();
-                    self.changed_activity(client, None);
+                    self.changed_activity(client, None, false);
                     break PhysicalAction::Nothing
                 }
                 LogicalAction::PopOverlaySilent => {
@@ -612,7 +612,7 @@ impl TuiLogicalState {
                         act => panic!("can't postcompose {act:?}"),
                     };
                     self.activity_stack.chain_to(newact);
-                    self.changed_activity(client, Some(post));
+                    self.changed_activity(client, Some(post), false);
                     break PhysicalAction::Nothing
                 }
                 LogicalAction::PostReEdit(post) => {
@@ -624,7 +624,7 @@ impl TuiLogicalState {
                         act => panic!("can't reedit {act:?}"),
                     };
                     self.activity_stack.chain_to(newact);
-                    self.changed_activity(client, Some(post));
+                    self.changed_activity(client, Some(post), false);
                     break PhysicalAction::Nothing
                 }
             };
@@ -638,7 +638,7 @@ impl TuiLogicalState {
         if feeds_updated.contains(&FeedId::Mentions) {
             if self.activity_stack.top().throw_into_mentions() {
                 self.activity_stack.goto(UtilityActivity::ReadMentions.into());
-                self.changed_activity(client, None);
+                self.changed_activity(client, None, true);
             }
 
             // FIXME: we'd quite like a double-beep if you're in the composer
@@ -648,7 +648,8 @@ impl TuiLogicalState {
         }
     }
 
-    fn changed_activity(&mut self, client: &mut Client, post: Option<Post>) {
+    fn changed_activity(&mut self, client: &mut Client, post: Option<Post>,
+                        is_interrupt: bool) {
         if let Some((feed_id, saved_pos)) =
             self.activity_state.save_file_position()
         {
@@ -661,10 +662,10 @@ impl TuiLogicalState {
             }
         }
         self.activity_state = self.new_activity_state(
-            self.activity_stack.top(), client, post);
+            self.activity_stack.top(), client, post, is_interrupt);
         self.overlay_activity_state = match self.activity_stack.overlay() {
             Some(activity) =>
-                Some(self.new_activity_state(activity, client, None)),
+                Some(self.new_activity_state(activity, client, None, false)),
             None => None,
         };
         if let Some(area) = self.last_area {
@@ -682,7 +683,8 @@ impl TuiLogicalState {
     }
 
     fn new_activity_state(&self, activity: Activity, client: &mut Client,
-                          post: Option<Post>) -> Box<dyn ActivityState>
+                          post: Option<Post>, is_interrupt: bool)
+                          -> Box<dyn ActivityState>
     {
         let result = match activity {
             Activity::NonUtil(NonUtilityActivity::MainMenu) =>
@@ -704,7 +706,7 @@ impl TuiLogicalState {
             Activity::NonUtil(NonUtilityActivity::HashtagTimeline(ref id)) =>
                 hashtag_timeline(client, id),
             Activity::Util(UtilityActivity::ReadMentions) =>
-                mentions(&self.file_positions, client),
+                mentions(&self.file_positions, client, is_interrupt),
             Activity::Util(UtilityActivity::EgoLog) =>
                 ego_log(&self.file_positions, client),
             Activity::Overlay(OverlayActivity::GetUserToExamine) =>