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::vec;
|
||||
|
||||
|
|
@ -100,81 +101,87 @@ pub struct Logger {
|
|||
|
||||
#[async_trait]
|
||||
impl MsgStore<LogMsg> for Logger {
|
||||
async fn path(&self, id: &usize) -> Path<usize> {
|
||||
Path::new(vec![*id])
|
||||
type Error = Infallible;
|
||||
|
||||
async fn path(&self, id: &usize) -> Result<Path<usize>, Self::Error> {
|
||||
Ok(Path::new(vec![*id]))
|
||||
}
|
||||
|
||||
async fn msg(&self, id: &usize) -> Option<LogMsg> {
|
||||
self.messages.lock().get(*id).cloned()
|
||||
async fn msg(&self, id: &usize) -> Result<Option<LogMsg>, Self::Error> {
|
||||
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
|
||||
.messages
|
||||
.lock()
|
||||
.get(*root_id)
|
||||
.map(|msg| vec![msg.clone()])
|
||||
.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();
|
||||
Some(0).filter(|_| !empty)
|
||||
Ok(Some(0).filter(|_| !empty))
|
||||
}
|
||||
|
||||
async fn last_root_id(&self) -> Option<usize> {
|
||||
self.messages.lock().len().checked_sub(1)
|
||||
async fn last_root_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||
Ok(self.messages.lock().len().checked_sub(1))
|
||||
}
|
||||
|
||||
async fn prev_root_id(&self, root_id: &usize) -> Option<usize> {
|
||||
root_id.checked_sub(1)
|
||||
async fn prev_root_id(&self, root_id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||
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();
|
||||
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
|
||||
}
|
||||
|
||||
async fn newest_msg_id(&self) -> Option<usize> {
|
||||
async fn newest_msg_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
async fn oldest_unseen_msg_id(&self) -> Option<usize> {
|
||||
None
|
||||
async fn oldest_unseen_msg_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn newest_unseen_msg_id(&self) -> Option<usize> {
|
||||
None
|
||||
async fn newest_unseen_msg_id(&self) -> Result<Option<usize>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn older_unseen_msg_id(&self, _id: &usize) -> Option<usize> {
|
||||
None
|
||||
async fn older_unseen_msg_id(&self, _id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn newer_unseen_msg_id(&self, _id: &usize) -> Option<usize> {
|
||||
None
|
||||
async fn newer_unseen_msg_id(&self, _id: &usize) -> Result<Option<usize>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn unseen_msgs_count(&self) -> usize {
|
||||
0
|
||||
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error> {
|
||||
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 {
|
||||
|
|
|
|||
37
src/store.rs
37
src/store.rs
|
|
@ -132,22 +132,23 @@ impl<M: Msg> Tree<M> {
|
|||
|
||||
#[async_trait]
|
||||
pub trait MsgStore<M: Msg> {
|
||||
async fn path(&self, id: &M::Id) -> Path<M::Id>;
|
||||
async fn msg(&self, id: &M::Id) -> Option<M>;
|
||||
async fn tree(&self, root_id: &M::Id) -> Tree<M>;
|
||||
async fn first_root_id(&self) -> Option<M::Id>;
|
||||
async fn last_root_id(&self) -> Option<M::Id>;
|
||||
async fn prev_root_id(&self, root_id: &M::Id) -> Option<M::Id>;
|
||||
async fn next_root_id(&self, root_id: &M::Id) -> Option<M::Id>;
|
||||
async fn oldest_msg_id(&self) -> Option<M::Id>;
|
||||
async fn newest_msg_id(&self) -> Option<M::Id>;
|
||||
async fn older_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
||||
async fn newer_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
||||
async fn oldest_unseen_msg_id(&self) -> Option<M::Id>;
|
||||
async fn newest_unseen_msg_id(&self) -> Option<M::Id>;
|
||||
async fn older_unseen_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
||||
async fn newer_unseen_msg_id(&self, id: &M::Id) -> Option<M::Id>;
|
||||
async fn unseen_msgs_count(&self) -> usize;
|
||||
async fn set_seen(&self, id: &M::Id, seen: bool);
|
||||
async fn set_older_seen(&self, id: &M::Id, seen: bool);
|
||||
type Error;
|
||||
async fn path(&self, id: &M::Id) -> Result<Path<M::Id>, Self::Error>;
|
||||
async fn msg(&self, id: &M::Id) -> Result<Option<M>, Self::Error>;
|
||||
async fn tree(&self, root_id: &M::Id) -> Result<Tree<M>, Self::Error>;
|
||||
async fn first_root_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn last_root_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn prev_root_id(&self, root_id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn next_root_id(&self, root_id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn oldest_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn newest_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn older_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn newer_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn oldest_unseen_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn newest_unseen_msg_id(&self) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn older_unseen_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn newer_unseen_msg_id(&self, id: &M::Id) -> Result<Option<M::Id>, Self::Error>;
|
||||
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error>;
|
||||
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>;
|
||||
}
|
||||
|
|
|
|||
20
src/ui.rs
20
src/ui.rs
|
|
@ -10,6 +10,7 @@ use std::io;
|
|||
use std::sync::{Arc, Weak};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::error;
|
||||
use parking_lot::FairMutex;
|
||||
use tokio::sync::mpsc::error::TryRecvError;
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
|
|
@ -286,11 +287,20 @@ impl Ui {
|
|||
.handle_input_event(terminal, crossterm_lock, &event)
|
||||
.await
|
||||
}
|
||||
Mode::Log => self
|
||||
.log_chat
|
||||
.handle_input_event(terminal, crossterm_lock, &event, false)
|
||||
.await
|
||||
.handled(),
|
||||
Mode::Log => {
|
||||
let reaction = match self
|
||||
.log_chat
|
||||
.handle_input_event(terminal, crossterm_lock, &event, false)
|
||||
.await
|
||||
{
|
||||
Ok(reaction) => reaction,
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
panic!("{err}");
|
||||
}
|
||||
};
|
||||
reaction.handled()
|
||||
}
|
||||
};
|
||||
|
||||
// Pressing '?' should only open the key bindings list if it doesn't
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
mod blocks;
|
||||
mod tree;
|
||||
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use parking_lot::FairMutex;
|
||||
|
|
@ -102,7 +102,7 @@ impl<M: Msg, S: MsgStore<M>> ChatState<M, S> {
|
|||
crossterm_lock: &Arc<FairMutex<()>>,
|
||||
event: &InputEvent,
|
||||
can_compose: bool,
|
||||
) -> Reaction<M> {
|
||||
) -> Result<Reaction<M>, S::Error> {
|
||||
match self.mode {
|
||||
Mode::Tree => {
|
||||
self.tree
|
||||
|
|
@ -144,6 +144,7 @@ where
|
|||
M: Msg + ChatMsg,
|
||||
M::Id: 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 {
|
||||
match self {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ mod tree_blocks;
|
|||
mod widgets;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::error;
|
||||
use parking_lot::FairMutex;
|
||||
use tokio::sync::Mutex;
|
||||
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?
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
match event {
|
||||
key!('k') | key!(Up) => self.move_cursor_up().await,
|
||||
key!('j') | key!(Down) => self.move_cursor_down().await,
|
||||
key!('K') | key!(Ctrl + Up) => self.move_cursor_up_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_root().await,
|
||||
key!('h') | key!(Left) => self.move_cursor_older().await,
|
||||
key!('l') | key!(Right) => self.move_cursor_newer().await,
|
||||
key!('H') | key!(Ctrl + Left) => self.move_cursor_older_unseen().await,
|
||||
key!('L') | key!(Ctrl + Right) => self.move_cursor_newer_unseen().await,
|
||||
key!('g') | key!(Home) => self.move_cursor_to_top().await,
|
||||
key!('k') | key!(Up) => self.move_cursor_up().await?,
|
||||
key!('j') | key!(Down) => self.move_cursor_down().await?,
|
||||
key!('K') | key!(Ctrl + Up) => self.move_cursor_up_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_root().await?,
|
||||
key!('h') | key!(Left) => self.move_cursor_older().await?,
|
||||
key!('l') | key!(Right) => self.move_cursor_newer().await?,
|
||||
key!('H') | key!(Ctrl + Left) => self.move_cursor_older_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!(End) => self.move_cursor_to_bottom().await,
|
||||
key!(Ctrl + 'y') => self.scroll_up(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())
|
||||
}
|
||||
key!('z') => self.center_cursor(),
|
||||
_ => return false,
|
||||
_ => return Ok(false),
|
||||
}
|
||||
|
||||
true
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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 {
|
||||
key!(' ') => {
|
||||
if let Some(id) = id {
|
||||
if !self.folded.remove(id) {
|
||||
self.folded.insert(id.clone());
|
||||
}
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
key!('s') => {
|
||||
if let Some(id) = id {
|
||||
if let Some(msg) = self.store.tree(id).await.msg(id) {
|
||||
self.store.set_seen(id, !msg.seen()).await;
|
||||
if let Some(msg) = self.store.tree(id).await?.msg(id) {
|
||||
self.store.set_seen(id, !msg.seen()).await?;
|
||||
}
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
key!('S') => {
|
||||
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') => {
|
||||
if let Some(id) = id {
|
||||
self.store.set_older_seen(id, true).await;
|
||||
self.store.set_older_seen(id, true).await?;
|
||||
} else {
|
||||
self.store
|
||||
.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) {
|
||||
|
|
@ -169,16 +179,16 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
&mut self,
|
||||
event: &InputEvent,
|
||||
id: Option<M::Id>,
|
||||
) -> bool {
|
||||
) -> Result<bool, S::Error> {
|
||||
match event {
|
||||
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.correction = Some(Correction::MakeCursorVisible);
|
||||
}
|
||||
}
|
||||
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.correction = Some(Correction::MakeCursorVisible);
|
||||
}
|
||||
|
|
@ -187,10 +197,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
self.cursor = Cursor::editor(id, None);
|
||||
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) {
|
||||
|
|
@ -209,17 +219,17 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
event: &InputEvent,
|
||||
can_compose: bool,
|
||||
id: Option<M::Id>,
|
||||
) -> bool {
|
||||
) -> Result<bool, S::Error> {
|
||||
#[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
|
||||
} 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
|
||||
} else if can_compose {
|
||||
self.handle_edit_initiating_input_event(event, id).await
|
||||
self.handle_edit_initiating_input_event(event, id).await?
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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<()>>,
|
||||
event: &InputEvent,
|
||||
can_compose: bool,
|
||||
) -> Reaction<M> {
|
||||
match &self.cursor {
|
||||
) -> Result<Reaction<M>, S::Error> {
|
||||
Ok(match &self.cursor {
|
||||
Cursor::Bottom => {
|
||||
if self
|
||||
.handle_normal_input_event(terminal.frame(), event, can_compose, None)
|
||||
.await
|
||||
.await?
|
||||
{
|
||||
Reaction::Handled
|
||||
} else {
|
||||
|
|
@ -310,7 +320,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
let id = id.clone();
|
||||
if self
|
||||
.handle_normal_input_event(terminal.frame(), event, can_compose, Some(id))
|
||||
.await
|
||||
.await?
|
||||
{
|
||||
Reaction::Handled
|
||||
} else {
|
||||
|
|
@ -330,14 +340,14 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
Cursor::Pseudo { .. } => {
|
||||
if self
|
||||
.handle_movement_input_event(terminal.frame(), event)
|
||||
.await
|
||||
.await?
|
||||
{
|
||||
Reaction::Handled
|
||||
} else {
|
||||
Reaction::NotHandled
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn cursor(&self) -> Option<M::Id> {
|
||||
|
|
@ -388,7 +398,7 @@ impl<M: Msg, S: MsgStore<M>> TreeViewState<M, S> {
|
|||
crossterm_lock: &Arc<FairMutex<()>>,
|
||||
event: &InputEvent,
|
||||
can_compose: bool,
|
||||
) -> Reaction<M> {
|
||||
) -> Result<Reaction<M>, S::Error> {
|
||||
self.0
|
||||
.lock()
|
||||
.await
|
||||
|
|
@ -421,6 +431,7 @@ where
|
|||
M: Msg + ChatMsg,
|
||||
M::Id: 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 {
|
||||
Size::ZERO
|
||||
|
|
@ -428,7 +439,13 @@ where
|
|||
|
||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||
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();
|
||||
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.
|
||||
///
|
||||
/// Always stays at the same level of indentation.
|
||||
async fn find_prev_sibling(store: &S, tree: &mut Tree<M>, id: &mut M::Id) -> bool {
|
||||
if let Some(prev_sibling) = tree.prev_sibling(id) {
|
||||
async fn find_prev_sibling(
|
||||
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;
|
||||
true
|
||||
} else if tree.parent(id).is_none() {
|
||||
// We're at the root of our tree, so we need to move to the root of
|
||||
// the previous tree.
|
||||
if let Some(prev_root_id) = store.prev_root_id(tree.root()).await {
|
||||
*tree = store.tree(&prev_root_id).await;
|
||||
if let Some(prev_root_id) = store.prev_root_id(tree.root()).await? {
|
||||
*tree = store.tree(&prev_root_id).await?;
|
||||
*id = prev_root_id;
|
||||
true
|
||||
} else {
|
||||
|
|
@ -110,21 +114,26 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
Ok(moved)
|
||||
}
|
||||
|
||||
/// Move to the next sibling, or don't move if this is not possible.
|
||||
///
|
||||
/// Always stays at the same level of indentation.
|
||||
async fn find_next_sibling(store: &S, tree: &mut Tree<M>, id: &mut M::Id) -> bool {
|
||||
if let Some(next_sibling) = tree.next_sibling(id) {
|
||||
async fn find_next_sibling(
|
||||
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;
|
||||
true
|
||||
} else if tree.parent(id).is_none() {
|
||||
// We're at the root of our tree, so we need to move to the root of
|
||||
// the next tree.
|
||||
if let Some(next_root_id) = store.next_root_id(tree.root()).await {
|
||||
*tree = store.tree(&next_root_id).await;
|
||||
if let Some(next_root_id) = store.next_root_id(tree.root()).await? {
|
||||
*tree = store.tree(&next_root_id).await?;
|
||||
*id = next_root_id;
|
||||
true
|
||||
} else {
|
||||
|
|
@ -132,7 +141,8 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
Ok(moved)
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
tree: &mut Tree<M>,
|
||||
id: &mut M::Id,
|
||||
) -> bool {
|
||||
) -> Result<bool, S::Error> {
|
||||
// Move to previous sibling, then to its last child
|
||||
// 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) {}
|
||||
true
|
||||
} else {
|
||||
Self::find_parent(tree, id)
|
||||
}
|
||||
};
|
||||
Ok(moved)
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
tree: &mut Tree<M>,
|
||||
id: &mut M::Id,
|
||||
) -> bool {
|
||||
) -> Result<bool, S::Error> {
|
||||
if Self::find_first_child(folded, tree, id) {
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
if Self::find_next_sibling(store, tree, id).await {
|
||||
return true;
|
||||
if Self::find_next_sibling(store, tree, id).await? {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Temporary id to avoid modifying the original one if no parent-sibling
|
||||
// can be found.
|
||||
let mut tmp_id = id.clone();
|
||||
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;
|
||||
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 {
|
||||
Cursor::Bottom | Cursor::Pseudo { parent: None, .. } => {
|
||||
if let Some(last_root_id) = self.store.last_root_id().await {
|
||||
let tree = self.store.tree(&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 mut id = last_root_id;
|
||||
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
||||
self.cursor = Cursor::Msg(id);
|
||||
}
|
||||
}
|
||||
Cursor::Msg(msg) => {
|
||||
let path = self.store.path(msg).await;
|
||||
let mut tree = self.store.tree(path.first()).await;
|
||||
Self::find_prev_msg(&self.store, &self.folded, &mut tree, msg).await;
|
||||
let path = self.store.path(msg).await?;
|
||||
let mut tree = self.store.tree(path.first()).await?;
|
||||
Self::find_prev_msg(&self.store, &self.folded, &mut tree, msg).await?;
|
||||
}
|
||||
Cursor::Editor { .. } => {}
|
||||
Cursor::Pseudo {
|
||||
parent: Some(parent),
|
||||
..
|
||||
} => {
|
||||
let tree = self.store.tree(parent).await;
|
||||
let tree = self.store.tree(parent).await?;
|
||||
let mut id = parent.clone();
|
||||
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
||||
self.cursor = Cursor::Msg(id);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
Cursor::Msg(msg) => {
|
||||
let path = self.store.path(msg).await;
|
||||
let mut tree = self.store.tree(path.first()).await;
|
||||
if !Self::find_next_msg(&self.store, &self.folded, &mut tree, msg).await {
|
||||
let path = self.store.path(msg).await?;
|
||||
let mut tree = self.store.tree(path.first()).await?;
|
||||
if !Self::find_next_msg(&self.store, &self.folded, &mut tree, msg).await? {
|
||||
self.cursor = Cursor::Bottom;
|
||||
}
|
||||
}
|
||||
|
|
@ -225,11 +237,11 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
parent: Some(parent),
|
||||
..
|
||||
} => {
|
||||
let mut tree = self.store.tree(parent).await;
|
||||
let mut tree = self.store.tree(parent).await?;
|
||||
let mut id = parent.clone();
|
||||
while Self::find_last_child(&self.folded, &tree, &mut id) {}
|
||||
// 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);
|
||||
} else {
|
||||
self.cursor = Cursor::Bottom;
|
||||
|
|
@ -238,27 +250,28 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
_ => {}
|
||||
}
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
Cursor::Msg(msg) => {
|
||||
let path = self.store.path(msg).await;
|
||||
let mut tree = self.store.tree(path.first()).await;
|
||||
Self::find_prev_sibling(&self.store, &mut tree, msg).await;
|
||||
let path = self.store.path(msg).await?;
|
||||
let mut tree = self.store.tree(path.first()).await?;
|
||||
Self::find_prev_sibling(&self.store, &mut tree, msg).await?;
|
||||
}
|
||||
Cursor::Editor { .. } => {}
|
||||
Cursor::Pseudo {
|
||||
parent: Some(parent),
|
||||
..
|
||||
} => {
|
||||
let path = self.store.path(parent).await;
|
||||
let tree = self.store.tree(path.first()).await;
|
||||
let path = self.store.path(parent).await?;
|
||||
let tree = self.store.tree(path.first()).await?;
|
||||
if let Some(children) = tree.children(parent) {
|
||||
if let Some(last_child) = children.last() {
|
||||
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);
|
||||
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 {
|
||||
Cursor::Msg(msg) => {
|
||||
let path = self.store.path(msg).await;
|
||||
let mut tree = self.store.tree(path.first()).await;
|
||||
if !Self::find_next_sibling(&self.store, &mut tree, msg).await
|
||||
let path = self.store.path(msg).await?;
|
||||
let mut tree = self.store.tree(path.first()).await?;
|
||||
if !Self::find_next_sibling(&self.store, &mut tree, msg).await?
|
||||
&& tree.parent(msg).is_none()
|
||||
{
|
||||
self.cursor = Cursor::Bottom;
|
||||
|
|
@ -286,9 +300,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
_ => {}
|
||||
}
|
||||
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 {
|
||||
Cursor::Pseudo {
|
||||
parent: Some(parent),
|
||||
|
|
@ -297,53 +312,56 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
Cursor::Msg(id) => {
|
||||
// Could also be done via retrieving the path, but it doesn't
|
||||
// really matter here
|
||||
let tree = self.store.tree(id).await;
|
||||
let tree = self.store.tree(id).await?;
|
||||
Self::find_parent(&tree, id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
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 {
|
||||
Cursor::Pseudo {
|
||||
parent: Some(parent),
|
||||
..
|
||||
} => {
|
||||
let path = self.store.path(parent).await;
|
||||
let path = self.store.path(parent).await?;
|
||||
self.cursor = Cursor::Msg(path.first().clone());
|
||||
}
|
||||
Cursor::Msg(msg) => {
|
||||
let path = self.store.path(msg).await;
|
||||
let path = self.store.path(msg).await?;
|
||||
*msg = path.first().clone();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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.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 {
|
||||
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;
|
||||
} else {
|
||||
self.cursor = Cursor::Bottom;
|
||||
|
|
@ -355,29 +373,31 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
_ => {}
|
||||
}
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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.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 {
|
||||
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;
|
||||
} else {
|
||||
self.cursor = Cursor::Bottom;
|
||||
|
|
@ -389,13 +409,15 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
_ => {}
|
||||
}
|
||||
self.correction = Some(Correction::MakeCursorVisible);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_cursor_to_top(&mut self) {
|
||||
if let Some(first_root_id) = self.store.first_root_id().await {
|
||||
pub async fn move_cursor_to_top(&mut self) -> Result<(), S::Error> {
|
||||
if let Some(first_root_id) = self.store.first_root_id().await? {
|
||||
self.cursor = Cursor::Msg(first_root_id);
|
||||
self.correction = Some(Correction::MakeCursorVisible);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
pub async fn parent_for_normal_reply(&self) -> Option<Option<M::Id>> {
|
||||
match &self.cursor {
|
||||
/// The outer `Option` shows whether a parent exists or not. The inner
|
||||
/// `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::Msg(id) => {
|
||||
let path = self.store.path(id).await;
|
||||
let tree = self.store.tree(path.first()).await;
|
||||
let path = self.store.path(id).await?;
|
||||
let tree = self.store.tree(path.first()).await?;
|
||||
|
||||
Some(Some(if tree.next_sibling(id).is_some() {
|
||||
// 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,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn parent_for_alternate_reply(&self) -> Option<Option<M::Id>> {
|
||||
match &self.cursor {
|
||||
/// The outer `Option` shows whether a parent exists or not. The inner
|
||||
/// `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::Msg(id) => {
|
||||
let path = self.store.path(id).await;
|
||||
let tree = self.store.tree(path.first()).await;
|
||||
let path = self.store.path(id).await?;
|
||||
let tree = self.store.tree(path.first()).await?;
|
||||
|
||||
Some(Some(if tree.next_sibling(id).is_none() {
|
||||
// The opposite of replying normally
|
||||
|
|
@ -467,6 +493,6 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
}))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ struct Context {
|
|||
}
|
||||
|
||||
impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Path<M::Id> {
|
||||
match cursor {
|
||||
Cursor::Msg(id) => self.store.path(id).await,
|
||||
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Result<Path<M::Id>, S::Error> {
|
||||
Ok(match cursor {
|
||||
Cursor::Msg(id) => self.store.path(id).await?,
|
||||
Cursor::Bottom
|
||||
| Cursor::Editor { parent: None, .. }
|
||||
| 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),
|
||||
..
|
||||
} => {
|
||||
let mut path = self.store.path(parent).await;
|
||||
let mut path = self.store.path(parent).await?;
|
||||
path.push(M::last_possible_id());
|
||||
path
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
frame: &mut Frame,
|
||||
blocks: &mut TreeBlocks<M::Id>,
|
||||
) {
|
||||
) -> Result<(), S::Error> {
|
||||
let top_line = 0;
|
||||
|
||||
while blocks.blocks().top_line > top_line {
|
||||
let top_root = blocks.top_root();
|
||||
let prev_root_id = match top_root {
|
||||
Root::Bottom => self.store.last_root_id().await,
|
||||
Root::Tree(root_id) => self.store.prev_root_id(root_id).await,
|
||||
Root::Bottom => self.store.last_root_id().await?,
|
||||
Root::Tree(root_id) => self.store.prev_root_id(root_id).await?,
|
||||
};
|
||||
let prev_root_id = match prev_root_id {
|
||||
Some(id) => id,
|
||||
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));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn expand_to_bottom(
|
||||
|
|
@ -225,22 +227,24 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
context: &Context,
|
||||
frame: &mut Frame,
|
||||
blocks: &mut TreeBlocks<M::Id>,
|
||||
) {
|
||||
) -> Result<(), S::Error> {
|
||||
let bottom_line = frame.size().height as i32 - 1;
|
||||
|
||||
while blocks.blocks().bottom_line < bottom_line {
|
||||
let bottom_root = blocks.bottom_root();
|
||||
let next_root_id = match bottom_root {
|
||||
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 {
|
||||
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));
|
||||
} else {
|
||||
blocks.append(self.layout_bottom(context, frame));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fill_screen_and_clamp_scrolling(
|
||||
|
|
@ -248,23 +252,25 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
context: &Context,
|
||||
frame: &mut Frame,
|
||||
blocks: &mut TreeBlocks<M::Id>,
|
||||
) {
|
||||
) -> Result<(), S::Error> {
|
||||
let top_line = 0;
|
||||
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 {
|
||||
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 {
|
||||
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(
|
||||
|
|
@ -272,8 +278,8 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
context: &Context,
|
||||
frame: &mut Frame,
|
||||
last_cursor_path: &Path<M::Id>,
|
||||
) -> TreeBlocks<M::Id> {
|
||||
match &self.last_cursor {
|
||||
) -> Result<TreeBlocks<M::Id>, S::Error> {
|
||||
Ok(match &self.last_cursor {
|
||||
Cursor::Bottom => {
|
||||
let mut blocks = self.layout_bottom(context, frame);
|
||||
|
||||
|
|
@ -299,7 +305,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
parent: Some(_), ..
|
||||
} => {
|
||||
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);
|
||||
|
||||
blocks
|
||||
|
|
@ -308,7 +314,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
|
||||
blocks
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn layout_cursor_seed(
|
||||
|
|
@ -317,10 +323,10 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
frame: &mut Frame,
|
||||
last_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;
|
||||
|
||||
match &self.cursor {
|
||||
Ok(match &self.cursor {
|
||||
Cursor::Bottom
|
||||
| Cursor::Editor { parent: None, .. }
|
||||
| Cursor::Pseudo { parent: None, .. } => {
|
||||
|
|
@ -338,7 +344,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
parent: Some(_), ..
|
||||
} => {
|
||||
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 cursor_above_last = cursor_path < last_cursor_path;
|
||||
|
|
@ -349,7 +355,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
|
||||
blocks
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn layout_initial_seed(
|
||||
|
|
@ -358,7 +364,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
frame: &mut Frame,
|
||||
last_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 {
|
||||
self.layout_cursor_seed(context, frame, last_cursor_path, cursor_path)
|
||||
.await
|
||||
|
|
@ -513,7 +519,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
nick: String,
|
||||
focused: bool,
|
||||
frame: &mut Frame,
|
||||
) -> TreeBlocks<M::Id> {
|
||||
) -> Result<TreeBlocks<M::Id>, S::Error> {
|
||||
// The basic idea is this:
|
||||
//
|
||||
// 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 last_cursor_path = self.cursor_path(&self.last_cursor).await;
|
||||
let cursor_path = self.cursor_path(&self.cursor).await;
|
||||
let last_cursor_path = self.cursor_path(&self.last_cursor).await?;
|
||||
let cursor_path = self.cursor_path(&self.cursor).await?;
|
||||
self.make_path_visible(&cursor_path);
|
||||
|
||||
let mut blocks = self
|
||||
.layout_initial_seed(&context, frame, &last_cursor_path, &cursor_path)
|
||||
.await;
|
||||
.await?;
|
||||
blocks.blocks_mut().offset(self.scroll);
|
||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
if !self.contains_cursor(&blocks) {
|
||||
blocks = self
|
||||
.layout_cursor_seed(&context, frame, &last_cursor_path, &cursor_path)
|
||||
.await;
|
||||
.await?;
|
||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||
.await;
|
||||
.await?;
|
||||
}
|
||||
|
||||
match self.correction {
|
||||
Some(Correction::MakeCursorVisible) => {
|
||||
self.scroll_so_cursor_is_visible(frame, &mut blocks);
|
||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||
.await;
|
||||
.await?;
|
||||
}
|
||||
Some(Correction::MoveCursorToVisibleArea) => {
|
||||
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.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
|
||||
.layout_last_cursor_seed(&context, frame, &last_cursor_path)
|
||||
.await;
|
||||
.await?;
|
||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||
.await;
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Some(Correction::CenterCursor) => {
|
||||
self.scroll_so_cursor_is_centered(frame, &mut blocks);
|
||||
self.fill_screen_and_clamp_scrolling(&context, frame, &mut blocks)
|
||||
.await;
|
||||
.await?;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
|
@ -594,6 +600,6 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
self.scroll = 0;
|
||||
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::bot::instance::{Event, ServerConfig};
|
||||
use euphoxide::conn::{self, Joined, Joining, SessionInfo};
|
||||
use log::error;
|
||||
use parking_lot::FairMutex;
|
||||
use tokio::sync::oneshot::error::TryRecvError;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
|
@ -326,11 +327,19 @@ impl EuphRoom {
|
|||
Some(euph::State::Connected(_, conn::State::Joined(_)))
|
||||
);
|
||||
|
||||
match self
|
||||
let reaction = match self
|
||||
.chat
|
||||
.handle_input_event(terminal, crossterm_lock, event, can_compose)
|
||||
.await
|
||||
{
|
||||
Ok(reaction) => reaction,
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
panic!("{err}");
|
||||
}
|
||||
};
|
||||
|
||||
match reaction {
|
||||
Reaction::NotHandled => {}
|
||||
Reaction::Handled => return true,
|
||||
Reaction::Composed { parent, content } => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::convert::Infallible;
|
||||
use std::mem;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
|
@ -86,76 +87,80 @@ impl EuphRoomVault {
|
|||
|
||||
#[async_trait]
|
||||
impl MsgStore<SmallMessage> for EuphRoomVault {
|
||||
async fn path(&self, id: &MessageId) -> Path<MessageId> {
|
||||
self.path(*id).await
|
||||
type Error = Infallible;
|
||||
|
||||
async fn path(&self, id: &MessageId) -> Result<Path<MessageId>, Self::Error> {
|
||||
Ok(self.path(*id).await)
|
||||
}
|
||||
|
||||
async fn msg(&self, id: &MessageId) -> Option<SmallMessage> {
|
||||
self.msg(*id).await
|
||||
async fn msg(&self, id: &MessageId) -> Result<Option<SmallMessage>, Self::Error> {
|
||||
Ok(self.msg(*id).await)
|
||||
}
|
||||
|
||||
async fn tree(&self, root_id: &MessageId) -> Tree<SmallMessage> {
|
||||
self.tree(*root_id).await
|
||||
async fn tree(&self, root_id: &MessageId) -> Result<Tree<SmallMessage>, Self::Error> {
|
||||
Ok(self.tree(*root_id).await)
|
||||
}
|
||||
|
||||
async fn first_root_id(&self) -> Option<MessageId> {
|
||||
self.first_root_id().await
|
||||
async fn first_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.first_root_id().await)
|
||||
}
|
||||
|
||||
async fn last_root_id(&self) -> Option<MessageId> {
|
||||
self.last_root_id().await
|
||||
async fn last_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.last_root_id().await)
|
||||
}
|
||||
|
||||
async fn prev_root_id(&self, root_id: &MessageId) -> Option<MessageId> {
|
||||
self.prev_root_id(*root_id).await
|
||||
async fn prev_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.prev_root_id(*root_id).await)
|
||||
}
|
||||
|
||||
async fn next_root_id(&self, root_id: &MessageId) -> Option<MessageId> {
|
||||
self.next_root_id(*root_id).await
|
||||
async fn next_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.next_root_id(*root_id).await)
|
||||
}
|
||||
|
||||
async fn oldest_msg_id(&self) -> Option<MessageId> {
|
||||
self.oldest_msg_id().await
|
||||
async fn oldest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.oldest_msg_id().await)
|
||||
}
|
||||
|
||||
async fn newest_msg_id(&self) -> Option<MessageId> {
|
||||
self.newest_msg_id().await
|
||||
async fn newest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.newest_msg_id().await)
|
||||
}
|
||||
|
||||
async fn older_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
||||
self.older_msg_id(*id).await
|
||||
async fn older_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.older_msg_id(*id).await)
|
||||
}
|
||||
|
||||
async fn newer_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
||||
self.newer_msg_id(*id).await
|
||||
async fn newer_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.newer_msg_id(*id).await)
|
||||
}
|
||||
|
||||
async fn oldest_unseen_msg_id(&self) -> Option<MessageId> {
|
||||
self.oldest_unseen_msg_id().await
|
||||
async fn oldest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.oldest_unseen_msg_id().await)
|
||||
}
|
||||
|
||||
async fn newest_unseen_msg_id(&self) -> Option<MessageId> {
|
||||
self.newest_unseen_msg_id().await
|
||||
async fn newest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.newest_unseen_msg_id().await)
|
||||
}
|
||||
|
||||
async fn older_unseen_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
||||
self.older_unseen_msg_id(*id).await
|
||||
async fn older_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.older_unseen_msg_id(*id).await)
|
||||
}
|
||||
|
||||
async fn newer_unseen_msg_id(&self, id: &MessageId) -> Option<MessageId> {
|
||||
self.newer_unseen_msg_id(*id).await
|
||||
async fn newer_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
|
||||
Ok(self.newer_unseen_msg_id(*id).await)
|
||||
}
|
||||
|
||||
async fn unseen_msgs_count(&self) -> usize {
|
||||
self.unseen_msgs_count().await
|
||||
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error> {
|
||||
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);
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue