Make MsgStore fallible

This commit is contained in:
Joscha 2023-02-12 21:08:38 +01:00
parent 5581fc1fc2
commit 35a140e21f
9 changed files with 324 additions and 242 deletions

View file

@ -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 {

View file

@ -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>;
} }

View file

@ -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 => {
.log_chat let reaction = match self
.handle_input_event(terminal, crossterm_lock, &event, false) .log_chat
.await .handle_input_event(terminal, crossterm_lock, &event, false)
.handled(), .await
{
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

View file

@ -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 {

View file

@ -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 {

View file

@ -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,
} })
} }
} }

View file

@ -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)
} }
} }

View file

@ -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 } => {

View file

@ -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(())
} }
} }