Use terminal window focus to determine when a message has actually been seen (#94)

This commit is contained in:
Ulyssa 2023-06-14 22:42:23 -07:00
parent 430c601ff2
commit 885b56038f
No known key found for this signature in database
GPG key ID: 1B3965A3D18B9B64
5 changed files with 51 additions and 13 deletions

2
Cargo.lock generated
View file

@ -2137,7 +2137,7 @@ dependencies = [
[[package]]
name = "modalkit"
version = "0.0.15"
source = "git+https://github.com/ulyssa/modalkit?rev=7979680#797968002c53287d907ca3ae823e3482265c94bb"
source = "git+https://github.com/ulyssa/modalkit?rev=f641162#f6411625caa1ddb764c91a3fc47e052cfdc405b5"
dependencies = [
"anymap2",
"arboard",

View file

@ -52,7 +52,7 @@ url = {version = "^2.2.2", features = ["serde"]}
[dependencies.modalkit]
git = "https://github.com/ulyssa/modalkit"
rev = "7979680"
rev = "f641162"
[dependencies.matrix-sdk]
version = "0.6"

View file

@ -22,7 +22,15 @@ use matrix_sdk::ruma::OwnedUserId;
use modalkit::crossterm::{
self,
cursor::Show as CursorShow,
event::{poll, read, DisableBracketedPaste, EnableBracketedPaste, Event},
event::{
poll,
read,
DisableBracketedPaste,
DisableFocusChange,
EnableBracketedPaste,
EnableFocusChange,
Event,
},
execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle},
};
@ -109,6 +117,7 @@ struct Application {
bindings: KeyManager<TerminalKey, ProgramAction, RepeatType, ProgramContext>,
actstack: VecDeque<(ProgramAction, ProgramContext)>,
screen: ScreenState<IambWindow, IambInfo>,
focused: bool,
}
impl Application {
@ -120,6 +129,7 @@ impl Application {
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(stdout, EnterAlternateScreen)?;
crossterm::execute!(stdout, EnableBracketedPaste)?;
crossterm::execute!(stdout, EnableFocusChange)?;
let title = format!("iamb ({})", settings.profile.user_id);
crossterm::execute!(stdout, SetTitle(title))?;
@ -154,11 +164,13 @@ impl Application {
bindings,
actstack,
screen,
focused: true,
})
}
fn redraw(&mut self, full: bool, store: &mut ProgramStore) -> Result<(), std::io::Error> {
let bindings = &mut self.bindings;
let focused = self.focused;
let sstate = &mut self.screen;
let term = &mut self.terminal;
@ -176,7 +188,11 @@ impl Application {
// Don't show terminal cursor when we show a dialog.
let hide_cursor = !dialogstr.is_empty();
let screen = Screen::new(store).show_dialog(dialogstr).show_mode(modestr).borders(true);
let screen = Screen::new(store)
.show_dialog(dialogstr)
.show_mode(modestr)
.borders(true)
.focus(focused);
f.render_stateful_widget(screen, area, sstate);
if hide_cursor {
@ -212,8 +228,11 @@ impl Application {
Event::Mouse(_) => {
// Do nothing for now.
},
Event::FocusGained | Event::FocusLost => {
// Do nothing for now.
Event::FocusGained => {
self.focused = true;
},
Event::FocusLost => {
self.focused = false;
},
Event::Resize(_, _) => {
// We'll redraw for the new size next time step() is called.
@ -532,13 +551,18 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
login(worker, &settings).await.unwrap_or_else(print_exit);
fn restore_tty() {
let _ = crossterm::terminal::disable_raw_mode();
let _ = crossterm::execute!(stdout(), DisableBracketedPaste);
let _ = crossterm::execute!(stdout(), DisableFocusChange);
let _ = crossterm::execute!(stdout(), LeaveAlternateScreen);
let _ = crossterm::execute!(stdout(), CursorShow);
}
// Make sure panics clean up the terminal properly.
let orig_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
let _ = crossterm::terminal::disable_raw_mode();
let _ = crossterm::execute!(stdout(), DisableBracketedPaste);
let _ = crossterm::execute!(stdout(), LeaveAlternateScreen);
let _ = crossterm::execute!(stdout(), CursorShow);
restore_tty();
orig_hook(panic_info);
process::exit(1);
}));
@ -547,6 +571,7 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
// We can now run the application.
application.run().await?;
restore_tty();
Ok(())
}

View file

@ -848,7 +848,9 @@ impl<'a> StatefulWidget for Chat<'a> {
// Render the message scrollback.
let scrollback_focused = state.focus.is_scrollback() && self.focused;
let scrollback = Scrollback::new(self.store).focus(scrollback_focused);
let scrollback = Scrollback::new(self.store)
.focus(scrollback_focused)
.room_focus(self.focused);
scrollback.render(scrollarea, buf, &mut state.scrollback);
}
}

View file

@ -1206,13 +1206,21 @@ impl TerminalCursor for ScrollbackState {
}
pub struct Scrollback<'a> {
room_focused: bool,
focused: bool,
store: &'a mut ProgramStore,
}
impl<'a> Scrollback<'a> {
pub fn new(store: &'a mut ProgramStore) -> Self {
Scrollback { focused: false, store }
Scrollback { room_focused: false, focused: false, store }
}
/// Indicate whether the room window is currently focused, regardless of whether the scrollback
/// also is.
pub fn room_focus(mut self, focused: bool) -> Self {
self.room_focused = focused;
self
}
/// Indicate whether the scrollback is currently focused.
@ -1307,7 +1315,10 @@ impl<'a> StatefulWidget for Scrollback<'a> {
y += 1;
}
if settings.tunables.read_receipt_send && state.cursor.timestamp.is_none() {
if self.room_focused &&
settings.tunables.read_receipt_send &&
state.cursor.timestamp.is_none()
{
// If the cursor is at the last message, then update the read marker.
info.read_till = info.messages.last_key_value().map(|(k, _)| k.1.clone());
}