Make MsgStore fallible
This commit is contained in:
parent
5581fc1fc2
commit
35a140e21f
9 changed files with 324 additions and 242 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::convert::Infallible;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
|
|
@ -100,81 +101,87 @@ pub struct Logger {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl MsgStore<LogMsg> for Logger {
|
impl MsgStore<LogMsg> for Logger {
|
||||||
async fn path(&self, id: &usize) -> Path<usize> {
|
type Error = Infallible;
|
||||||
Path::new(vec![*id])
|
|
||||||
|
async fn path(&self, id: &usize) -> Result<Path<usize>, Self::Error> {
|
||||||
|
Ok(Path::new(vec![*id]))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn msg(&self, id: &usize) -> Option<LogMsg> {
|
async fn msg(&self, id: &usize) -> Result<Option<LogMsg>, Self::Error> {
|
||||||
self.messages.lock().get(*id).cloned()
|
Ok(self.messages.lock().get(*id).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tree(&self, root_id: &usize) -> Tree<LogMsg> {
|
async fn tree(&self, root_id: &usize) -> Result<Tree<LogMsg>, Self::Error> {
|
||||||
let msgs = self
|
let msgs = self
|
||||||
.messages
|
.messages
|
||||||
.lock()
|
.lock()
|
||||||
.get(*root_id)
|
.get(*root_id)
|
||||||
.map(|msg| vec![msg.clone()])
|
.map(|msg| vec![msg.clone()])
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
Tree::new(*root_id, msgs)
|
Ok(Tree::new(*root_id, msgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn first_root_id(&self) -> Option<usize> {
|
async fn first_root_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||||
let empty = self.messages.lock().is_empty();
|
let empty = self.messages.lock().is_empty();
|
||||||
Some(0).filter(|_| !empty)
|
Ok(Some(0).filter(|_| !empty))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn last_root_id(&self) -> Option<usize> {
|
async fn last_root_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||||
self.messages.lock().len().checked_sub(1)
|
Ok(self.messages.lock().len().checked_sub(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn prev_root_id(&self, root_id: &usize) -> Option<usize> {
|
async fn prev_root_id(&self, root_id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||||
root_id.checked_sub(1)
|
Ok(root_id.checked_sub(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn next_root_id(&self, root_id: &usize) -> Option<usize> {
|
async fn next_root_id(&self, root_id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||||
let len = self.messages.lock().len();
|
let len = self.messages.lock().len();
|
||||||
root_id.checked_add(1).filter(|t| *t < len)
|
Ok(root_id.checked_add(1).filter(|t| *t < len))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn oldest_msg_id(&self) -> Option<usize> {
|
async fn oldest_msg_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||||
self.first_root_id().await
|
self.first_root_id().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newest_msg_id(&self) -> Option<usize> {
|
async fn newest_msg_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||||
self.last_root_id().await
|
self.last_root_id().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn older_msg_id(&self, id: &usize) -> Option<usize> {
|
async fn older_msg_id(&self, id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||||
self.prev_root_id(id).await
|
self.prev_root_id(id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newer_msg_id(&self, id: &usize) -> Option<usize> {
|
async fn newer_msg_id(&self, id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||||
self.next_root_id(id).await
|
self.next_root_id(id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn oldest_unseen_msg_id(&self) -> Option<usize> {
|
async fn oldest_unseen_msg_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newest_unseen_msg_id(&self) -> Option<usize> {
|
async fn newest_unseen_msg_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn older_unseen_msg_id(&self, _id: &usize) -> Option<usize> {
|
async fn older_unseen_msg_id(&self, _id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newer_unseen_msg_id(&self, _id: &usize) -> Option<usize> {
|
async fn newer_unseen_msg_id(&self, _id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn unseen_msgs_count(&self) -> usize {
|
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error> {
|
||||||
0
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_seen(&self, _id: &usize, _seen: bool) {}
|
async fn set_seen(&self, _id: &usize, _seen: bool) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn set_older_seen(&self, _id: &usize, _seen: bool) {}
|
async fn set_older_seen(&self, _id: &usize, _seen: bool) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Log for Logger {
|
impl Log for Logger {
|
||||||
|
|
|
||||||
37
src/store.rs
37
src/store.rs
|
|
@ -132,22 +132,23 @@ impl<M: Msg> Tree<M> {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait MsgStore<M: Msg> {
|
pub trait MsgStore<M: Msg> {
|
||||||
async fn path(&self, id: &M::Id) -> Path<M::Id>;
|
type Error;
|
||||||
async fn msg(&self, id: &M::Id) -> Option<M>;
|
async fn path(&self, id: &M::Id) -> Result<Path<M::Id>, Self::Error>;
|
||||||
async fn tree(&self, root_id: &M::Id) -> Tree<M>;
|
async fn msg(&self, id: &M::Id) -> Result<Option<M>, Self::Error>;
|
||||||
async fn first_root_id(&self) -> Option<M::Id>;
|
async fn tree(&self, root_id: &M::Id) -> Result<Tree<M>, Self::Error>;
|
||||||
async fn last_root_id(&self) -> Option<M::Id>;
|
async fn first_root_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn prev_root_id(&self, root_id: &M::Id) -> Option<M::Id>;
|
async fn last_root_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn next_root_id(&self, root_id: &M::Id) -> Option<M::Id>;
|
async fn prev_root_id(&self, root_id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn oldest_msg_id(&self) -> Option<M::Id>;
|
async fn next_root_id(&self, root_id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn newest_msg_id(&self) -> Option<M::Id>;
|
async fn oldest_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn older_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
async fn newest_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn newer_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
async fn older_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn oldest_unseen_msg_id(&self) -> Option<M::Id>;
|
async fn newer_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn newest_unseen_msg_id(&self) -> Option<M::Id>;
|
async fn oldest_unseen_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn older_unseen_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
async fn newest_unseen_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn newer_unseen_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
async fn older_unseen_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn unseen_msgs_count(&self) -> usize;
|
async fn newer_unseen_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||||
async fn set_seen(&self, id: &M::Id, seen: bool);
|
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error>;
|
||||||
async fn set_older_seen(&self, id: &M::Id, seen: bool);
|
async fn set_seen(&self, id: &M::Id, seen: bool) -> Result<(), Self::Error>;
|
||||||
|
async fn set_older_seen(&self, id: &M::Id, seen: bool) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
src/ui.rs
14
src/ui.rs
|
|
@ -10,6 +10,7 @@ use std::io;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use log::error;
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use tokio::sync::mpsc::error::TryRecvError;
|
use tokio::sync::mpsc::error::TryRecvError;
|
||||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||||
|
|
@ -286,11 +287,20 @@ impl Ui {
|
||||||
.handle_input_event(terminal, crossterm_lock, &event)
|
.handle_input_event(terminal, crossterm_lock, &event)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Mode::Log => self
|
Mode::Log => {
|
||||||
|
let reaction = match self
|
||||||
.log_chat
|
.log_chat
|
||||||
.handle_input_event(terminal, crossterm_lock, &event, false)
|
.handle_input_event(terminal, crossterm_lock, &event, false)
|
||||||
.await
|
.await
|
||||||
.handled(),
|
{
|
||||||
|
Ok(reaction) => reaction,
|
||||||
|
Err(err) => {
|
||||||
|
error!("{err}");
|
||||||
|
panic!("{err}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reaction.handled()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pressing '?' should only open the key bindings list if it doesn't
|
// Pressing '?' should only open the key bindings list if it doesn't
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
mod blocks;
|
mod blocks;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{fmt, io};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
|
|
@ -102,7 +102,7 @@ impl<M: Msg, S: MsgStore<M>> ChatState<M, S> {
|
||||||
crossterm_lock: &Arc<FairMutex<()>>,
|
crossterm_lock: &Arc<FairMutex<()>>,
|
||||||
event: &InputEvent,
|
event: &InputEvent,
|
||||||
can_compose: bool,
|
can_compose: bool,
|
||||||
) -> Reaction<M> {
|
) -> Result<Reaction<M>, S::Error> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Tree => {
|
Mode::Tree => {
|
||||||
self.tree
|
self.tree
|
||||||
|
|
@ -144,6 +144,7 @@ where
|
||||||
M: Msg + ChatMsg,
|
M: Msg + ChatMsg,
|
||||||
M::Id: Send + Sync,
|
M::Id: Send + Sync,
|
||||||
S: MsgStore<M> + Send + Sync,
|
S: MsgStore<M> + Send + Sync,
|
||||||
|
S::Error: fmt::Display,
|
||||||
{
|
{
|
||||||
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ mod tree_blocks;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use log::error;
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use toss::frame::{Frame, Pos, Size};
|
use toss::frame::{Frame, Pos, Size};
|
||||||
|
|
@ -82,21 +84,25 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
// TODO Bindings inspired by vim's ()/[]/{} bindings?
|
// TODO Bindings inspired by vim's ()/[]/{} bindings?
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_movement_input_event(&mut self, frame: &mut Frame, event: &InputEvent) -> bool {
|
async fn handle_movement_input_event(
|
||||||
|
&mut self,
|
||||||
|
frame: &mut Frame,
|
||||||
|
event: &InputEvent,
|
||||||
|
) -> Result<bool, S::Error> {
|
||||||
let chat_height = frame.size().height - 3;
|
let chat_height = frame.size().height - 3;
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
key!('k') | key!(Up) => self.move_cursor_up().await,
|
key!('k') | key!(Up) => self.move_cursor_up().await?,
|
||||||
key!('j') | key!(Down) => self.move_cursor_down().await,
|
key!('j') | key!(Down) => self.move_cursor_down().await?,
|
||||||
key!('K') | key!(Ctrl + Up) => self.move_cursor_up_sibling().await,
|
key!('K') | key!(Ctrl + Up) => self.move_cursor_up_sibling().await?,
|
||||||
key!('J') | key!(Ctrl + Down) => self.move_cursor_down_sibling().await,
|
key!('J') | key!(Ctrl + Down) => self.move_cursor_down_sibling().await?,
|
||||||
key!('p') => self.move_cursor_to_parent().await,
|
key!('p') => self.move_cursor_to_parent().await?,
|
||||||
key!('P') => self.move_cursor_to_root().await,
|
key!('P') => self.move_cursor_to_root().await?,
|
||||||
key!('h') | key!(Left) => self.move_cursor_older().await,
|
key!('h') | key!(Left) => self.move_cursor_older().await?,
|
||||||
key!('l') | key!(Right) => self.move_cursor_newer().await,
|
key!('l') | key!(Right) => self.move_cursor_newer().await?,
|
||||||
key!('H') | key!(Ctrl + Left) => self.move_cursor_older_unseen().await,
|
key!('H') | key!(Ctrl + Left) => self.move_cursor_older_unseen().await?,
|
||||||
key!('L') | key!(Ctrl + Right) => self.move_cursor_newer_unseen().await,
|
key!('L') | key!(Ctrl + Right) => self.move_cursor_newer_unseen().await?,
|
||||||
key!('g') | key!(Home) => self.move_cursor_to_top().await,
|
key!('g') | key!(Home) => self.move_cursor_to_top().await?,
|
||||||
key!('G') | key!(End) => self.move_cursor_to_bottom().await,
|
key!('G') | key!(End) => self.move_cursor_to_bottom().await,
|
||||||
key!(Ctrl + 'y') => self.scroll_up(1),
|
key!(Ctrl + 'y') => self.scroll_up(1),
|
||||||
key!(Ctrl + 'e') => self.scroll_down(1),
|
key!(Ctrl + 'e') => self.scroll_down(1),
|
||||||
|
|
@ -107,10 +113,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.scroll_down(chat_height.saturating_sub(1).into())
|
self.scroll_down(chat_height.saturating_sub(1).into())
|
||||||
}
|
}
|
||||||
key!('z') => self.center_cursor(),
|
key!('z') => self.center_cursor(),
|
||||||
_ => return false,
|
_ => return Ok(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_action_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
pub fn list_action_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
||||||
|
|
@ -120,43 +126,47 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
bindings.binding("ctrl+s", "mark all older messages as seen");
|
bindings.binding("ctrl+s", "mark all older messages as seen");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_action_input_event(&mut self, event: &InputEvent, id: Option<&M::Id>) -> bool {
|
async fn handle_action_input_event(
|
||||||
|
&mut self,
|
||||||
|
event: &InputEvent,
|
||||||
|
id: Option<&M::Id>,
|
||||||
|
) -> Result<bool, S::Error> {
|
||||||
match event {
|
match event {
|
||||||
key!(' ') => {
|
key!(' ') => {
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
if !self.folded.remove(id) {
|
if !self.folded.remove(id) {
|
||||||
self.folded.insert(id.clone());
|
self.folded.insert(id.clone());
|
||||||
}
|
}
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key!('s') => {
|
key!('s') => {
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
if let Some(msg) = self.store.tree(id).await.msg(id) {
|
if let Some(msg) = self.store.tree(id).await?.msg(id) {
|
||||||
self.store.set_seen(id, !msg.seen()).await;
|
self.store.set_seen(id, !msg.seen()).await?;
|
||||||
}
|
}
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key!('S') => {
|
key!('S') => {
|
||||||
for id in &self.last_visible_msgs {
|
for id in &self.last_visible_msgs {
|
||||||
self.store.set_seen(id, true).await;
|
self.store.set_seen(id, true).await?;
|
||||||
}
|
}
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
key!(Ctrl + 's') => {
|
key!(Ctrl + 's') => {
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
self.store.set_older_seen(id, true).await;
|
self.store.set_older_seen(id, true).await?;
|
||||||
} else {
|
} else {
|
||||||
self.store
|
self.store
|
||||||
.set_older_seen(&M::last_possible_id(), true)
|
.set_older_seen(&M::last_possible_id(), true)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
false
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_edit_initiating_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
pub fn list_edit_initiating_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
||||||
|
|
@ -169,16 +179,16 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &InputEvent,
|
event: &InputEvent,
|
||||||
id: Option<M::Id>,
|
id: Option<M::Id>,
|
||||||
) -> bool {
|
) -> Result<bool, S::Error> {
|
||||||
match event {
|
match event {
|
||||||
key!('r') => {
|
key!('r') => {
|
||||||
if let Some(parent) = self.parent_for_normal_reply().await {
|
if let Some(parent) = self.parent_for_normal_reply().await? {
|
||||||
self.cursor = Cursor::editor(id, parent);
|
self.cursor = Cursor::editor(id, parent);
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key!('R') => {
|
key!('R') => {
|
||||||
if let Some(parent) = self.parent_for_alternate_reply().await {
|
if let Some(parent) = self.parent_for_alternate_reply().await? {
|
||||||
self.cursor = Cursor::editor(id, parent);
|
self.cursor = Cursor::editor(id, parent);
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
}
|
}
|
||||||
|
|
@ -187,10 +197,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.cursor = Cursor::editor(id, None);
|
self.cursor = Cursor::editor(id, None);
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
}
|
}
|
||||||
_ => return false,
|
_ => return Ok(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_normal_key_bindings(&self, bindings: &mut KeyBindingsList, can_compose: bool) {
|
pub fn list_normal_key_bindings(&self, bindings: &mut KeyBindingsList, can_compose: bool) {
|
||||||
|
|
@ -209,17 +219,17 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
event: &InputEvent,
|
event: &InputEvent,
|
||||||
can_compose: bool,
|
can_compose: bool,
|
||||||
id: Option<M::Id>,
|
id: Option<M::Id>,
|
||||||
) -> bool {
|
) -> Result<bool, S::Error> {
|
||||||
#[allow(clippy::if_same_then_else)]
|
#[allow(clippy::if_same_then_else)]
|
||||||
if self.handle_movement_input_event(frame, event).await {
|
Ok(if self.handle_movement_input_event(frame, event).await? {
|
||||||
true
|
true
|
||||||
} else if self.handle_action_input_event(event, id.as_ref()).await {
|
} else if self.handle_action_input_event(event, id.as_ref()).await? {
|
||||||
true
|
true
|
||||||
} else if can_compose {
|
} else if can_compose {
|
||||||
self.handle_edit_initiating_input_event(event, id).await
|
self.handle_edit_initiating_input_event(event, id).await?
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_editor_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
fn list_editor_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
||||||
|
|
@ -294,12 +304,12 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
crossterm_lock: &Arc<FairMutex<()>>,
|
crossterm_lock: &Arc<FairMutex<()>>,
|
||||||
event: &InputEvent,
|
event: &InputEvent,
|
||||||
can_compose: bool,
|
can_compose: bool,
|
||||||
) -> Reaction<M> {
|
) -> Result<Reaction<M>, S::Error> {
|
||||||
match &self.cursor {
|
Ok(match &self.cursor {
|
||||||
Cursor::Bottom => {
|
Cursor::Bottom => {
|
||||||
if self
|
if self
|
||||||
.handle_normal_input_event(terminal.frame(), event, can_compose, None)
|
.handle_normal_input_event(terminal.frame(), event, can_compose, None)
|
||||||
.await
|
.await?
|
||||||
{
|
{
|
||||||
Reaction::Handled
|
Reaction::Handled
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -310,7 +320,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
let id = id.clone();
|
let id = id.clone();
|
||||||
if self
|
if self
|
||||||
.handle_normal_input_event(terminal.frame(), event, can_compose, Some(id))
|
.handle_normal_input_event(terminal.frame(), event, can_compose, Some(id))
|
||||||
.await
|
.await?
|
||||||
{
|
{
|
||||||
Reaction::Handled
|
Reaction::Handled
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -330,14 +340,14 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
Cursor::Pseudo { .. } => {
|
Cursor::Pseudo { .. } => {
|
||||||
if self
|
if self
|
||||||
.handle_movement_input_event(terminal.frame(), event)
|
.handle_movement_input_event(terminal.frame(), event)
|
||||||
.await
|
.await?
|
||||||
{
|
{
|
||||||
Reaction::Handled
|
Reaction::Handled
|
||||||
} else {
|
} else {
|
||||||
Reaction::NotHandled
|
Reaction::NotHandled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor(&self) -> Option<M::Id> {
|
fn cursor(&self) -> Option<M::Id> {
|
||||||
|
|
@ -388,7 +398,7 @@ impl<M: Msg, S: MsgStore<M>> TreeViewState<M, S> {
|
||||||
crossterm_lock: &Arc<FairMutex<()>>,
|
crossterm_lock: &Arc<FairMutex<()>>,
|
||||||
event: &InputEvent,
|
event: &InputEvent,
|
||||||
can_compose: bool,
|
can_compose: bool,
|
||||||
) -> Reaction<M> {
|
) -> Result<Reaction<M>, S::Error> {
|
||||||
self.0
|
self.0
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
|
|
@ -421,6 +431,7 @@ where
|
||||||
M: Msg + ChatMsg,
|
M: Msg + ChatMsg,
|
||||||
M::Id: Send + Sync,
|
M::Id: Send + Sync,
|
||||||
S: MsgStore<M> + Send + Sync,
|
S: MsgStore<M> + Send + Sync,
|
||||||
|
S::Error: fmt::Display,
|
||||||
{
|
{
|
||||||
fn size(&self, _frame: &mut Frame, _max_width: Option<u16>, _max_height: Option<u16>) -> Size {
|
fn size(&self, _frame: &mut Frame, _max_width: Option<u16>, _max_height: Option<u16>) -> Size {
|
||||||
Size::ZERO
|
Size::ZERO
|
||||||
|
|
@ -428,7 +439,13 @@ where
|
||||||
|
|
||||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||||
let mut guard = self.inner.lock().await;
|
let mut guard = self.inner.lock().await;
|
||||||
let blocks = guard.relayout(self.nick, self.focused, frame).await;
|
let blocks = match guard.relayout(self.nick, self.focused, frame).await {
|
||||||
|
Ok(blocks) => blocks,
|
||||||
|
Err(err) => {
|
||||||
|
error!("{err}");
|
||||||
|
panic!("{err}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let size = frame.size();
|
let size = frame.size();
|
||||||
for block in blocks.into_blocks().blocks {
|
for block in blocks.into_blocks().blocks {
|
||||||
|
|
|
||||||
|
|
@ -94,15 +94,19 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
/// Move to the previous sibling, or don't move if this is not possible.
|
/// Move to the previous sibling, or don't move if this is not possible.
|
||||||
///
|
///
|
||||||
/// Always stays at the same level of indentation.
|
/// Always stays at the same level of indentation.
|
||||||
async fn find_prev_sibling(store: &S, tree: &mut Tree<M>, id: &mut M::Id) -> bool {
|
async fn find_prev_sibling(
|
||||||
if let Some(prev_sibling) = tree.prev_sibling(id) {
|
store: &S,
|
||||||
|
tree: &mut Tree<M>,
|
||||||
|
id: &mut M::Id,
|
||||||
|
) -> Result<bool, S::Error> {
|
||||||
|
let moved = if let Some(prev_sibling) = tree.prev_sibling(id) {
|
||||||
*id = prev_sibling;
|
*id = prev_sibling;
|
||||||
true
|
true
|
||||||
} else if tree.parent(id).is_none() {
|
} else if tree.parent(id).is_none() {
|
||||||
// We're at the root of our tree, so we need to move to the root of
|
// We're at the root of our tree, so we need to move to the root of
|
||||||
// the previous tree.
|
// the previous tree.
|
||||||
if let Some(prev_root_id) = store.prev_root_id(tree.root()).await {
|
if let Some(prev_root_id) = store.prev_root_id(tree.root()).await? {
|
||||||
*tree = store.tree(&prev_root_id).await;
|
*tree = store.tree(&prev_root_id).await?;
|
||||||
*id = prev_root_id;
|
*id = prev_root_id;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -110,21 +114,26 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
};
|
||||||
|
Ok(moved)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move to the next sibling, or don't move if this is not possible.
|
/// Move to the next sibling, or don't move if this is not possible.
|
||||||
///
|
///
|
||||||
/// Always stays at the same level of indentation.
|
/// Always stays at the same level of indentation.
|
||||||
async fn find_next_sibling(store: &S, tree: &mut Tree<M>, id: &mut M::Id) -> bool {
|
async fn find_next_sibling(
|
||||||
if let Some(next_sibling) = tree.next_sibling(id) {
|
store: &S,
|
||||||
|
tree: &mut Tree<M>,
|
||||||
|
id: &mut M::Id,
|
||||||
|
) -> Result<bool, S::Error> {
|
||||||
|
let moved = if let Some(next_sibling) = tree.next_sibling(id) {
|
||||||
*id = next_sibling;
|
*id = next_sibling;
|
||||||
true
|
true
|
||||||
} else if tree.parent(id).is_none() {
|
} else if tree.parent(id).is_none() {
|
||||||
// We're at the root of our tree, so we need to move to the root of
|
// We're at the root of our tree, so we need to move to the root of
|
||||||
// the next tree.
|
// the next tree.
|
||||||
if let Some(next_root_id) = store.next_root_id(tree.root()).await {
|
if let Some(next_root_id) = store.next_root_id(tree.root()).await? {
|
||||||
*tree = store.tree(&next_root_id).await;
|
*tree = store.tree(&next_root_id).await?;
|
||||||
*id = next_root_id;
|
*id = next_root_id;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -132,7 +141,8 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
};
|
||||||
|
Ok(moved)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move to the previous message, or don't move if this is not possible.
|
/// Move to the previous message, or don't move if this is not possible.
|
||||||
|
|
@ -141,15 +151,16 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
folded: &HashSet<M::Id>,
|
folded: &HashSet<M::Id>,
|
||||||
tree: &mut Tree<M>,
|
tree: &mut Tree<M>,
|
||||||
id: &mut M::Id,
|
id: &mut M::Id,
|
||||||
) -> bool {
|
) -> Result<bool, S::Error> {
|
||||||
// Move to previous sibling, then to its last child
|
// Move to previous sibling, then to its last child
|
||||||
// If not possible, move to parent
|
// If not possible, move to parent
|
||||||
if Self::find_prev_sibling(store, tree, id).await {
|
let moved = if Self::find_prev_sibling(store, tree, id).await? {
|
||||||
while Self::find_last_child(folded, tree, id) {}
|
while Self::find_last_child(folded, tree, id) {}
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
Self::find_parent(tree, id)
|
Self::find_parent(tree, id)
|
||||||
}
|
};
|
||||||
|
Ok(moved)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move to the next message, or don't move if this is not possible.
|
/// Move to the next message, or don't move if this is not possible.
|
||||||
|
|
@ -158,63 +169,64 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
folded: &HashSet<M::Id>,
|
folded: &HashSet<M::Id>,
|
||||||
tree: &mut Tree<M>,
|
tree: &mut Tree<M>,
|
||||||
id: &mut M::Id,
|
id: &mut M::Id,
|
||||||
) -> bool {
|
) -> Result<bool, S::Error> {
|
||||||
if Self::find_first_child(folded, tree, id) {
|
if Self::find_first_child(folded, tree, id) {
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if Self::find_next_sibling(store, tree, id).await {
|
if Self::find_next_sibling(store, tree, id).await? {
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary id to avoid modifying the original one if no parent-sibling
|
// Temporary id to avoid modifying the original one if no parent-sibling
|
||||||
// can be found.
|
// can be found.
|
||||||
let mut tmp_id = id.clone();
|
let mut tmp_id = id.clone();
|
||||||
while Self::find_parent(tree, &mut tmp_id) {
|
while Self::find_parent(tree, &mut tmp_id) {
|
||||||
if Self::find_next_sibling(store, tree, &mut tmp_id).await {
|
if Self::find_next_sibling(store, tree, &mut tmp_id).await? {
|
||||||
*id = tmp_id;
|
*id = tmp_id;
|
||||||
return true;
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_up(&mut self) {
|
pub async fn move_cursor_up(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Bottom | Cursor::Pseudo { parent: None, .. } => {
|
Cursor::Bottom | Cursor::Pseudo { parent: None, .. } => {
|
||||||
if let Some(last_root_id) = self.store.last_root_id().await {
|
if let Some(last_root_id) = self.store.last_root_id().await? {
|
||||||
let tree = self.store.tree(&last_root_id).await;
|
let tree = self.store.tree(&last_root_id).await?;
|
||||||
let mut id = last_root_id;
|
let mut id = last_root_id;
|
||||||
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
||||||
self.cursor = Cursor::Msg(id);
|
self.cursor = Cursor::Msg(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cursor::Msg(msg) => {
|
Cursor::Msg(msg) => {
|
||||||
let path = self.store.path(msg).await;
|
let path = self.store.path(msg).await?;
|
||||||
let mut tree = self.store.tree(path.first()).await;
|
let mut tree = self.store.tree(path.first()).await?;
|
||||||
Self::find_prev_msg(&self.store, &self.folded, &mut tree, msg).await;
|
Self::find_prev_msg(&self.store, &self.folded, &mut tree, msg).await?;
|
||||||
}
|
}
|
||||||
Cursor::Editor { .. } => {}
|
Cursor::Editor { .. } => {}
|
||||||
Cursor::Pseudo {
|
Cursor::Pseudo {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let tree = self.store.tree(parent).await;
|
let tree = self.store.tree(parent).await?;
|
||||||
let mut id = parent.clone();
|
let mut id = parent.clone();
|
||||||
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
||||||
self.cursor = Cursor::Msg(id);
|
self.cursor = Cursor::Msg(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_down(&mut self) {
|
pub async fn move_cursor_down(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Msg(msg) => {
|
Cursor::Msg(msg) => {
|
||||||
let path = self.store.path(msg).await;
|
let path = self.store.path(msg).await?;
|
||||||
let mut tree = self.store.tree(path.first()).await;
|
let mut tree = self.store.tree(path.first()).await?;
|
||||||
if !Self::find_next_msg(&self.store, &self.folded, &mut tree, msg).await {
|
if !Self::find_next_msg(&self.store, &self.folded, &mut tree, msg).await? {
|
||||||
self.cursor = Cursor::Bottom;
|
self.cursor = Cursor::Bottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,11 +237,11 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut tree = self.store.tree(parent).await;
|
let mut tree = self.store.tree(parent).await?;
|
||||||
let mut id = parent.clone();
|
let mut id = parent.clone();
|
||||||
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
||||||
// Now we're at the previous message
|
// Now we're at the previous message
|
||||||
if Self::find_next_msg(&self.store, &self.folded, &mut tree, &mut id).await {
|
if Self::find_next_msg(&self.store, &self.folded, &mut tree, &mut id).await? {
|
||||||
self.cursor = Cursor::Msg(id);
|
self.cursor = Cursor::Msg(id);
|
||||||
} else {
|
} else {
|
||||||
self.cursor = Cursor::Bottom;
|
self.cursor = Cursor::Bottom;
|
||||||
|
|
@ -238,27 +250,28 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_up_sibling(&mut self) {
|
pub async fn move_cursor_up_sibling(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Bottom | Cursor::Pseudo { parent: None, .. } => {
|
Cursor::Bottom | Cursor::Pseudo { parent: None, .. } => {
|
||||||
if let Some(last_root_id) = self.store.last_root_id().await {
|
if let Some(last_root_id) = self.store.last_root_id().await? {
|
||||||
self.cursor = Cursor::Msg(last_root_id);
|
self.cursor = Cursor::Msg(last_root_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cursor::Msg(msg) => {
|
Cursor::Msg(msg) => {
|
||||||
let path = self.store.path(msg).await;
|
let path = self.store.path(msg).await?;
|
||||||
let mut tree = self.store.tree(path.first()).await;
|
let mut tree = self.store.tree(path.first()).await?;
|
||||||
Self::find_prev_sibling(&self.store, &mut tree, msg).await;
|
Self::find_prev_sibling(&self.store, &mut tree, msg).await?;
|
||||||
}
|
}
|
||||||
Cursor::Editor { .. } => {}
|
Cursor::Editor { .. } => {}
|
||||||
Cursor::Pseudo {
|
Cursor::Pseudo {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let path = self.store.path(parent).await;
|
let path = self.store.path(parent).await?;
|
||||||
let tree = self.store.tree(path.first()).await;
|
let tree = self.store.tree(path.first()).await?;
|
||||||
if let Some(children) = tree.children(parent) {
|
if let Some(children) = tree.children(parent) {
|
||||||
if let Some(last_child) = children.last() {
|
if let Some(last_child) = children.last() {
|
||||||
self.cursor = Cursor::Msg(last_child.clone());
|
self.cursor = Cursor::Msg(last_child.clone());
|
||||||
|
|
@ -267,14 +280,15 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_down_sibling(&mut self) {
|
pub async fn move_cursor_down_sibling(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Msg(msg) => {
|
Cursor::Msg(msg) => {
|
||||||
let path = self.store.path(msg).await;
|
let path = self.store.path(msg).await?;
|
||||||
let mut tree = self.store.tree(path.first()).await;
|
let mut tree = self.store.tree(path.first()).await?;
|
||||||
if !Self::find_next_sibling(&self.store, &mut tree, msg).await
|
if !Self::find_next_sibling(&self.store, &mut tree, msg).await?
|
||||||
&& tree.parent(msg).is_none()
|
&& tree.parent(msg).is_none()
|
||||||
{
|
{
|
||||||
self.cursor = Cursor::Bottom;
|
self.cursor = Cursor::Bottom;
|
||||||
|
|
@ -286,9 +300,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_to_parent(&mut self) {
|
pub async fn move_cursor_to_parent(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Pseudo {
|
Cursor::Pseudo {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
|
|
@ -297,53 +312,56 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
Cursor::Msg(id) => {
|
Cursor::Msg(id) => {
|
||||||
// Could also be done via retrieving the path, but it doesn't
|
// Could also be done via retrieving the path, but it doesn't
|
||||||
// really matter here
|
// really matter here
|
||||||
let tree = self.store.tree(id).await;
|
let tree = self.store.tree(id).await?;
|
||||||
Self::find_parent(&tree, id);
|
Self::find_parent(&tree, id);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_to_root(&mut self) {
|
pub async fn move_cursor_to_root(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Pseudo {
|
Cursor::Pseudo {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let path = self.store.path(parent).await;
|
let path = self.store.path(parent).await?;
|
||||||
self.cursor = Cursor::Msg(path.first().clone());
|
self.cursor = Cursor::Msg(path.first().clone());
|
||||||
}
|
}
|
||||||
Cursor::Msg(msg) => {
|
Cursor::Msg(msg) => {
|
||||||
let path = self.store.path(msg).await;
|
let path = self.store.path(msg).await?;
|
||||||
*msg = path.first().clone();
|
*msg = path.first().clone();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_older(&mut self) {
|
pub async fn move_cursor_older(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Msg(id) => {
|
Cursor::Msg(id) => {
|
||||||
if let Some(prev_id) = self.store.older_msg_id(id).await {
|
if let Some(prev_id) = self.store.older_msg_id(id).await? {
|
||||||
*id = prev_id;
|
*id = prev_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cursor::Bottom | Cursor::Pseudo { .. } => {
|
Cursor::Bottom | Cursor::Pseudo { .. } => {
|
||||||
if let Some(id) = self.store.newest_msg_id().await {
|
if let Some(id) = self.store.newest_msg_id().await? {
|
||||||
self.cursor = Cursor::Msg(id);
|
self.cursor = Cursor::Msg(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_newer(&mut self) {
|
pub async fn move_cursor_newer(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Msg(id) => {
|
Cursor::Msg(id) => {
|
||||||
if let Some(prev_id) = self.store.newer_msg_id(id).await {
|
if let Some(prev_id) = self.store.newer_msg_id(id).await? {
|
||||||
*id = prev_id;
|
*id = prev_id;
|
||||||
} else {
|
} else {
|
||||||
self.cursor = Cursor::Bottom;
|
self.cursor = Cursor::Bottom;
|
||||||
|
|
@ -355,29 +373,31 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_older_unseen(&mut self) {
|
pub async fn move_cursor_older_unseen(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Msg(id) => {
|
Cursor::Msg(id) => {
|
||||||
if let Some(prev_id) = self.store.older_unseen_msg_id(id).await {
|
if let Some(prev_id) = self.store.older_unseen_msg_id(id).await? {
|
||||||
*id = prev_id;
|
*id = prev_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cursor::Bottom | Cursor::Pseudo { .. } => {
|
Cursor::Bottom | Cursor::Pseudo { .. } => {
|
||||||
if let Some(id) = self.store.newest_unseen_msg_id().await {
|
if let Some(id) = self.store.newest_unseen_msg_id().await? {
|
||||||
self.cursor = Cursor::Msg(id);
|
self.cursor = Cursor::Msg(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_newer_unseen(&mut self) {
|
pub async fn move_cursor_newer_unseen(&mut self) -> Result<(), S::Error> {
|
||||||
match &mut self.cursor {
|
match &mut self.cursor {
|
||||||
Cursor::Msg(id) => {
|
Cursor::Msg(id) => {
|
||||||
if let Some(prev_id) = self.store.newer_unseen_msg_id(id).await {
|
if let Some(prev_id) = self.store.newer_unseen_msg_id(id).await? {
|
||||||
*id = prev_id;
|
*id = prev_id;
|
||||||
} else {
|
} else {
|
||||||
self.cursor = Cursor::Bottom;
|
self.cursor = Cursor::Bottom;
|
||||||
|
|
@ -389,13 +409,15 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_to_top(&mut self) {
|
pub async fn move_cursor_to_top(&mut self) -> Result<(), S::Error> {
|
||||||
if let Some(first_root_id) = self.store.first_root_id().await {
|
if let Some(first_root_id) = self.store.first_root_id().await? {
|
||||||
self.cursor = Cursor::Msg(first_root_id);
|
self.cursor = Cursor::Msg(first_root_id);
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn move_cursor_to_bottom(&mut self) {
|
pub async fn move_cursor_to_bottom(&mut self) {
|
||||||
|
|
@ -418,12 +440,14 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.correction = Some(Correction::CenterCursor);
|
self.correction = Some(Correction::CenterCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn parent_for_normal_reply(&self) -> Option<Option<M::Id>> {
|
/// The outer `Option` shows whether a parent exists or not. The inner
|
||||||
match &self.cursor {
|
/// `Option` shows if that parent has an id.
|
||||||
|
pub async fn parent_for_normal_reply(&self) -> Result<Option<Option<M::Id>>, S::Error> {
|
||||||
|
Ok(match &self.cursor {
|
||||||
Cursor::Bottom => Some(None),
|
Cursor::Bottom => Some(None),
|
||||||
Cursor::Msg(id) => {
|
Cursor::Msg(id) => {
|
||||||
let path = self.store.path(id).await;
|
let path = self.store.path(id).await?;
|
||||||
let tree = self.store.tree(path.first()).await;
|
let tree = self.store.tree(path.first()).await?;
|
||||||
|
|
||||||
Some(Some(if tree.next_sibling(id).is_some() {
|
Some(Some(if tree.next_sibling(id).is_some() {
|
||||||
// A reply to a message that has further siblings should be a
|
// A reply to a message that has further siblings should be a
|
||||||
|
|
@ -444,15 +468,17 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn parent_for_alternate_reply(&self) -> Option<Option<M::Id>> {
|
/// The outer `Option` shows whether a parent exists or not. The inner
|
||||||
match &self.cursor {
|
/// `Option` shows if that parent has an id.
|
||||||
|
pub async fn parent_for_alternate_reply(&self) -> Result<Option<Option<M::Id>>, S::Error> {
|
||||||
|
Ok(match &self.cursor {
|
||||||
Cursor::Bottom => Some(None),
|
Cursor::Bottom => Some(None),
|
||||||
Cursor::Msg(id) => {
|
Cursor::Msg(id) => {
|
||||||
let path = self.store.path(id).await;
|
let path = self.store.path(id).await?;
|
||||||
let tree = self.store.tree(path.first()).await;
|
let tree = self.store.tree(path.first()).await?;
|
||||||
|
|
||||||
Some(Some(if tree.next_sibling(id).is_none() {
|
Some(Some(if tree.next_sibling(id).is_none() {
|
||||||
// The opposite of replying normally
|
// The opposite of replying normally
|
||||||
|
|
@ -467,6 +493,6 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ struct Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Path<M::Id> {
|
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Result<Path<M::Id>, S::Error> {
|
||||||
match cursor {
|
Ok(match cursor {
|
||||||
Cursor::Msg(id) => self.store.path(id).await,
|
Cursor::Msg(id) => self.store.path(id).await?,
|
||||||
Cursor::Bottom
|
Cursor::Bottom
|
||||||
| Cursor::Editor { parent: None, .. }
|
| Cursor::Editor { parent: None, .. }
|
||||||
| Cursor::Pseudo { parent: None, .. } => Path::new(vec![M::last_possible_id()]),
|
| Cursor::Pseudo { parent: None, .. } => Path::new(vec![M::last_possible_id()]),
|
||||||
|
|
@ -36,11 +36,11 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut path = self.store.path(parent).await;
|
let mut path = self.store.path(parent).await?;
|
||||||
path.push(M::last_possible_id());
|
path.push(M::last_possible_id());
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_path_visible(&mut self, path: &Path<M::Id>) {
|
fn make_path_visible(&mut self, path: &Path<M::Id>) {
|
||||||
|
|
@ -202,22 +202,24 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
context: &Context,
|
context: &Context,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
blocks: &mut TreeBlocks<M::Id>,
|
blocks: &mut TreeBlocks<M::Id>,
|
||||||
) {
|
) -> Result<(), S::Error> {
|
||||||
let top_line = 0;
|
let top_line = 0;
|
||||||
|
|
||||||
while blocks.blocks().top_line > top_line {
|
while blocks.blocks().top_line > top_line {
|
||||||
let top_root = blocks.top_root();
|
let top_root = blocks.top_root();
|
||||||
let prev_root_id = match top_root {
|
let prev_root_id = match top_root {
|
||||||
Root::Bottom => self.store.last_root_id().await,
|
Root::Bottom => self.store.last_root_id().await?,
|
||||||
Root::Tree(root_id) => self.store.prev_root_id(root_id).await,
|
Root::Tree(root_id) => self.store.prev_root_id(root_id).await?,
|
||||||
};
|
};
|
||||||
let prev_root_id = match prev_root_id {
|
let prev_root_id = match prev_root_id {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
let prev_tree = self.store.tree(&prev_root_id).await;
|
let prev_tree = self.store.tree(&prev_root_id).await?;
|
||||||
blocks.prepend(self.layout_tree(context, frame, prev_tree));
|
blocks.prepend(self.layout_tree(context, frame, prev_tree));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn expand_to_bottom(
|
async fn expand_to_bottom(
|
||||||
|
|
@ -225,22 +227,24 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
context: &Context,
|
context: &Context,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
blocks: &mut TreeBlocks<M::Id>,
|
blocks: &mut TreeBlocks<M::Id>,
|
||||||
) {
|
) -> Result<(), S::Error> {
|
||||||
let bottom_line = frame.size().height as i32 - 1;
|
let bottom_line = frame.size().height as i32 - 1;
|
||||||
|
|
||||||
while blocks.blocks().bottom_line < bottom_line {
|
while blocks.blocks().bottom_line < bottom_line {
|
||||||
let bottom_root = blocks.bottom_root();
|
let bottom_root = blocks.bottom_root();
|
||||||
let next_root_id = match bottom_root {
|
let next_root_id = match bottom_root {
|
||||||
Root::Bottom => break,
|
Root::Bottom => break,
|
||||||
Root::Tree(root_id) => self.store.next_root_id(root_id).await,
|
Root::Tree(root_id) => self.store.next_root_id(root_id).await?,
|
||||||
};
|
};
|
||||||
if let Some(next_root_id) = next_root_id {
|
if let Some(next_root_id) = next_root_id {
|
||||||
let next_tree = self.store.tree(&next_root_id).await;
|
let next_tree = self.store.tree(&next_root_id).await?;
|
||||||
blocks.append(self.layout_tree(context, frame, next_tree));
|
blocks.append(self.layout_tree(context, frame, next_tree));
|
||||||
} else {
|
} else {
|
||||||
blocks.append(self.layout_bottom(context, frame));
|
blocks.append(self.layout_bottom(context, frame));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fill_screen_and_clamp_scrolling(
|
async fn fill_screen_and_clamp_scrolling(
|
||||||
|
|
@ -248,23 +252,25 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
context: &Context,
|
context: &Context,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
blocks: &mut TreeBlocks<M::Id>,
|
blocks: &mut TreeBlocks<M::Id>,
|
||||||
) {
|
) -> Result<(), S::Error> {
|
||||||
let top_line = 0;
|
let top_line = 0;
|
||||||
let bottom_line = frame.size().height as i32 - 1;
|
let bottom_line = frame.size().height as i32 - 1;
|
||||||
|
|
||||||
self.expand_to_top(context, frame, blocks).await;
|
self.expand_to_top(context, frame, blocks).await?;
|
||||||
|
|
||||||
if blocks.blocks().top_line > top_line {
|
if blocks.blocks().top_line > top_line {
|
||||||
blocks.blocks_mut().set_top_line(0);
|
blocks.blocks_mut().set_top_line(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expand_to_bottom(context, frame, blocks).await;
|
self.expand_to_bottom(context, frame, blocks).await?;
|
||||||
|
|
||||||
if blocks.blocks().bottom_line < bottom_line {
|
if blocks.blocks().bottom_line < bottom_line {
|
||||||
blocks.blocks_mut().set_bottom_line(bottom_line);
|
blocks.blocks_mut().set_bottom_line(bottom_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expand_to_top(context, frame, blocks).await;
|
self.expand_to_top(context, frame, blocks).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_last_cursor_seed(
|
async fn layout_last_cursor_seed(
|
||||||
|
|
@ -272,8 +278,8 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
context: &Context,
|
context: &Context,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
last_cursor_path: &Path<M::Id>,
|
last_cursor_path: &Path<M::Id>,
|
||||||
) -> TreeBlocks<M::Id> {
|
) -> Result<TreeBlocks<M::Id>, S::Error> {
|
||||||
match &self.last_cursor {
|
Ok(match &self.last_cursor {
|
||||||
Cursor::Bottom => {
|
Cursor::Bottom => {
|
||||||
let mut blocks = self.layout_bottom(context, frame);
|
let mut blocks = self.layout_bottom(context, frame);
|
||||||
|
|
||||||
|
|
@ -299,7 +305,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
parent: Some(_), ..
|
parent: Some(_), ..
|
||||||
} => {
|
} => {
|
||||||
let root = last_cursor_path.first();
|
let root = last_cursor_path.first();
|
||||||
let tree = self.store.tree(root).await;
|
let tree = self.store.tree(root).await?;
|
||||||
let mut blocks = self.layout_tree(context, frame, tree);
|
let mut blocks = self.layout_tree(context, frame, tree);
|
||||||
|
|
||||||
blocks
|
blocks
|
||||||
|
|
@ -308,7 +314,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
|
|
||||||
blocks
|
blocks
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_cursor_seed(
|
async fn layout_cursor_seed(
|
||||||
|
|
@ -317,10 +323,10 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
last_cursor_path: &Path<M::Id>,
|
last_cursor_path: &Path<M::Id>,
|
||||||
cursor_path: &Path<M::Id>,
|
cursor_path: &Path<M::Id>,
|
||||||
) -> TreeBlocks<M::Id> {
|
) -> Result<TreeBlocks<M::Id>, S::Error> {
|
||||||
let bottom_line = frame.size().height as i32 - 1;
|
let bottom_line = frame.size().height as i32 - 1;
|
||||||
|
|
||||||
match &self.cursor {
|
Ok(match &self.cursor {
|
||||||
Cursor::Bottom
|
Cursor::Bottom
|
||||||
| Cursor::Editor { parent: None, .. }
|
| Cursor::Editor { parent: None, .. }
|
||||||
| Cursor::Pseudo { parent: None, .. } => {
|
| Cursor::Pseudo { parent: None, .. } => {
|
||||||
|
|
@ -338,7 +344,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
parent: Some(_), ..
|
parent: Some(_), ..
|
||||||
} => {
|
} => {
|
||||||
let root = cursor_path.first();
|
let root = cursor_path.first();
|
||||||
let tree = self.store.tree(root).await;
|
let tree = self.store.tree(root).await?;
|
||||||
let mut blocks = self.layout_tree(context, frame, tree);
|
let mut blocks = self.layout_tree(context, frame, tree);
|
||||||
|
|
||||||
let cursor_above_last = cursor_path < last_cursor_path;
|
let cursor_above_last = cursor_path < last_cursor_path;
|
||||||
|
|
@ -349,7 +355,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
|
|
||||||
blocks
|
blocks
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_initial_seed(
|
async fn layout_initial_seed(
|
||||||
|
|
@ -358,7 +364,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
last_cursor_path: &Path<M::Id>,
|
last_cursor_path: &Path<M::Id>,
|
||||||
cursor_path: &Path<M::Id>,
|
cursor_path: &Path<M::Id>,
|
||||||
) -> TreeBlocks<M::Id> {
|
) -> Result<TreeBlocks<M::Id>, S::Error> {
|
||||||
if let Cursor::Bottom = self.cursor {
|
if let Cursor::Bottom = self.cursor {
|
||||||
self.layout_cursor_seed(context, frame, last_cursor_path, cursor_path)
|
self.layout_cursor_seed(context, frame, last_cursor_path, cursor_path)
|
||||||
.await
|
.await
|
||||||
|
|
@ -513,7 +519,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
nick: String,
|
nick: String,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
) -> TreeBlocks<M::Id> {
|
) -> Result<TreeBlocks<M::Id>, S::Error> {
|
||||||
// The basic idea is this:
|
// The basic idea is this:
|
||||||
//
|
//
|
||||||
// First, layout a full screen of blocks around self.last_cursor, using
|
// First, layout a full screen of blocks around self.last_cursor, using
|
||||||
|
|
@ -534,30 +540,30 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
|
|
||||||
let context = Context { nick, focused };
|
let context = Context { nick, focused };
|
||||||
|
|
||||||
let last_cursor_path = self.cursor_path(&self.last_cursor).await;
|
let last_cursor_path = self.cursor_path(&self.last_cursor).await?;
|
||||||
let cursor_path = self.cursor_path(&self.cursor).await;
|
let cursor_path = self.cursor_path(&self.cursor).await?;
|
||||||
self.make_path_visible(&cursor_path);
|
self.make_path_visible(&cursor_path);
|
||||||
|
|
||||||
let mut blocks = self
|
let mut blocks = self
|
||||||
.layout_initial_seed(&context, frame, &last_cursor_path, &cursor_path)
|
.layout_initial_seed(&context, frame, &last_cursor_path, &cursor_path)
|
||||||
.await;
|
.await?;
|
||||||
blocks.blocks_mut().offset(self.scroll);
|
blocks.blocks_mut().offset(self.scroll);
|
||||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||||
.await;
|
.await?;
|
||||||
|
|
||||||
if !self.contains_cursor(&blocks) {
|
if !self.contains_cursor(&blocks) {
|
||||||
blocks = self
|
blocks = self
|
||||||
.layout_cursor_seed(&context, frame, &last_cursor_path, &cursor_path)
|
.layout_cursor_seed(&context, frame, &last_cursor_path, &cursor_path)
|
||||||
.await;
|
.await?;
|
||||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.correction {
|
match self.correction {
|
||||||
Some(Correction::MakeCursorVisible) => {
|
Some(Correction::MakeCursorVisible) => {
|
||||||
self.scroll_so_cursor_is_visible(frame, &mut blocks);
|
self.scroll_so_cursor_is_visible(frame, &mut blocks);
|
||||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
Some(Correction::MoveCursorToVisibleArea) => {
|
Some(Correction::MoveCursorToVisibleArea) => {
|
||||||
let new_cursor_msg_id = self.move_cursor_so_it_is_visible(frame, &blocks);
|
let new_cursor_msg_id = self.move_cursor_so_it_is_visible(frame, &blocks);
|
||||||
|
|
@ -572,18 +578,18 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.scroll = 0;
|
self.scroll = 0;
|
||||||
self.correction = None;
|
self.correction = None;
|
||||||
|
|
||||||
let last_cursor_path = self.store.path(&cursor_msg_id).await;
|
let last_cursor_path = self.store.path(&cursor_msg_id).await?;
|
||||||
blocks = self
|
blocks = self
|
||||||
.layout_last_cursor_seed(&context, frame, &last_cursor_path)
|
.layout_last_cursor_seed(&context, frame, &last_cursor_path)
|
||||||
.await;
|
.await?;
|
||||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Correction::CenterCursor) => {
|
Some(Correction::CenterCursor) => {
|
||||||
self.scroll_so_cursor_is_centered(frame, &mut blocks);
|
self.scroll_so_cursor_is_centered(frame, &mut blocks);
|
||||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||||
.await;
|
.await?;
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
@ -594,6 +600,6 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.scroll = 0;
|
self.scroll = 0;
|
||||||
self.correction = None;
|
self.correction = None;
|
||||||
|
|
||||||
blocks
|
Ok(blocks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crossterm::style::{ContentStyle, Stylize};
|
||||||
use euphoxide::api::{Data, Message, MessageId, PacketType, SessionId};
|
use euphoxide::api::{Data, Message, MessageId, PacketType, SessionId};
|
||||||
use euphoxide::bot::instance::{Event, ServerConfig};
|
use euphoxide::bot::instance::{Event, ServerConfig};
|
||||||
use euphoxide::conn::{self, Joined, Joining, SessionInfo};
|
use euphoxide::conn::{self, Joined, Joining, SessionInfo};
|
||||||
|
use log::error;
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use tokio::sync::oneshot::error::TryRecvError;
|
use tokio::sync::oneshot::error::TryRecvError;
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
|
|
@ -326,11 +327,19 @@ impl EuphRoom {
|
||||||
Some(euph::State::Connected(_, conn::State::Joined(_)))
|
Some(euph::State::Connected(_, conn::State::Joined(_)))
|
||||||
);
|
);
|
||||||
|
|
||||||
match self
|
let reaction = match self
|
||||||
.chat
|
.chat
|
||||||
.handle_input_event(terminal, crossterm_lock, event, can_compose)
|
.handle_input_event(terminal, crossterm_lock, event, can_compose)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
Ok(reaction) => reaction,
|
||||||
|
Err(err) => {
|
||||||
|
error!("{err}");
|
||||||
|
panic!("{err}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match reaction {
|
||||||
Reaction::NotHandled => {}
|
Reaction::NotHandled => {}
|
||||||
Reaction::Handled => return true,
|
Reaction::Handled => return true,
|
||||||
Reaction::Composed { parent, content } => {
|
Reaction::Composed { parent, content } => {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::convert::Infallible;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
|
@ -86,76 +87,80 @@ impl EuphRoomVault {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl MsgStore<SmallMessage> for EuphRoomVault {
|
impl MsgStore<SmallMessage> for EuphRoomVault {
|
||||||
async fn path(&self, id: &MessageId) -> Path<MessageId> {
|
type Error = Infallible;
|
||||||
self.path(*id).await
|
|
||||||
|
async fn path(&self, id: &MessageId) -> Result<Path<MessageId>, Self::Error> {
|
||||||
|
Ok(self.path(*id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn msg(&self, id: &MessageId) -> Option<SmallMessage> {
|
async fn msg(&self, id: &MessageId) -> Result<Option<SmallMessage>, Self::Error> {
|
||||||
self.msg(*id).await
|
Ok(self.msg(*id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tree(&self, root_id: &MessageId) -> Tree<SmallMessage> {
|
async fn tree(&self, root_id: &MessageId) -> Result<Tree<SmallMessage>, Self::Error> {
|
||||||
self.tree(*root_id).await
|
Ok(self.tree(*root_id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn first_root_id(&self) -> Option<MessageId> {
|
async fn first_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.first_root_id().await
|
Ok(self.first_root_id().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn last_root_id(&self) -> Option<MessageId> {
|
async fn last_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.last_root_id().await
|
Ok(self.last_root_id().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn prev_root_id(&self, root_id: &MessageId) -> Option<MessageId> {
|
async fn prev_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.prev_root_id(*root_id).await
|
Ok(self.prev_root_id(*root_id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn next_root_id(&self, root_id: &MessageId) -> Option<MessageId> {
|
async fn next_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.next_root_id(*root_id).await
|
Ok(self.next_root_id(*root_id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn oldest_msg_id(&self) -> Option<MessageId> {
|
async fn oldest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.oldest_msg_id().await
|
Ok(self.oldest_msg_id().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newest_msg_id(&self) -> Option<MessageId> {
|
async fn newest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.newest_msg_id().await
|
Ok(self.newest_msg_id().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn older_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
async fn older_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.older_msg_id(*id).await
|
Ok(self.older_msg_id(*id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newer_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
async fn newer_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.newer_msg_id(*id).await
|
Ok(self.newer_msg_id(*id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn oldest_unseen_msg_id(&self) -> Option<MessageId> {
|
async fn oldest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.oldest_unseen_msg_id().await
|
Ok(self.oldest_unseen_msg_id().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newest_unseen_msg_id(&self) -> Option<MessageId> {
|
async fn newest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.newest_unseen_msg_id().await
|
Ok(self.newest_unseen_msg_id().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn older_unseen_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
async fn older_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.older_unseen_msg_id(*id).await
|
Ok(self.older_unseen_msg_id(*id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn newer_unseen_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
async fn newer_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||||
self.newer_unseen_msg_id(*id).await
|
Ok(self.newer_unseen_msg_id(*id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn unseen_msgs_count(&self) -> usize {
|
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error> {
|
||||||
self.unseen_msgs_count().await
|
Ok(self.unseen_msgs_count().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_seen(&self, id: &MessageId, seen: bool) {
|
async fn set_seen(&self, id: &MessageId, seen: bool) -> Result<(), Self::Error> {
|
||||||
self.set_seen(*id, seen);
|
self.set_seen(*id, seen);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_older_seen(&self, id: &MessageId, seen: bool) {
|
async fn set_older_seen(&self, id: &MessageId, seen: bool) -> Result<(), Self::Error> {
|
||||||
self.set_older_seen(*id, seen);
|
self.set_older_seen(*id, seen);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue