diff --git a/src/ui/chat.rs b/src/ui/chat.rs index 0f97ee2..44d2699 100644 --- a/src/ui/chat.rs +++ b/src/ui/chat.rs @@ -1,8 +1,12 @@ mod tree; +use std::sync::Arc; + use async_trait::async_trait; use crossterm::event::KeyEvent; +use parking_lot::FairMutex; use toss::frame::{Frame, Size}; +use toss::terminal::Terminal; use crate::store::{Msg, MsgStore}; @@ -55,26 +59,20 @@ impl> ChatState { } } - // pub async fn handle_messaging( - // &mut self, - // terminal: &mut Terminal, - // crossterm_lock: &Arc>, - // event: KeyEvent, - // ) -> Option<(Option, String)> { - // match self.mode { - // Mode::Tree => { - // self.tree - // .handle_messaging( - // &mut self.store, - // &mut self.cursor, - // terminal, - // crossterm_lock, - // event, - // ) - // .await - // } - // } - // } + pub async fn handle_messaging( + &mut self, + terminal: &mut Terminal, + crossterm_lock: &Arc>, + event: KeyEvent, + ) -> Option<(Option, String)> { + match self.mode { + Mode::Tree => { + self.tree + .handle_messaging(terminal, crossterm_lock, event) + .await + } + } + } } //////////// diff --git a/src/ui/chat/tree.rs b/src/ui/chat/tree.rs index eda2dea..f491b28 100644 --- a/src/ui/chat/tree.rs +++ b/src/ui/chat/tree.rs @@ -1,4 +1,4 @@ -// mod action; +mod action; mod blocks; mod cursor; mod layout; @@ -9,8 +9,10 @@ use std::sync::Arc; use async_trait::async_trait; use crossterm::event::{KeyCode, KeyEvent}; +use parking_lot::FairMutex; use tokio::sync::Mutex; use toss::frame::{Frame, Size}; +use toss::terminal::Terminal; use crate::store::{Msg, MsgStore}; use crate::ui::widgets::Widget; @@ -55,6 +57,22 @@ impl> InnerTreeViewState { _ => {} } } + + async fn handle_messaging( + &self, + terminal: &mut Terminal, + crossterm_lock: &Arc>, + event: KeyEvent, + ) -> Option<(Option, String)> { + match event.code { + KeyCode::Char('r') => self.reply_normal(terminal, crossterm_lock).await, + KeyCode::Char('R') => self.reply_alternate(terminal, crossterm_lock).await, + KeyCode::Char('t') | KeyCode::Char('T') => { + Self::create_new_thread(terminal, crossterm_lock) + } + _ => None, + } + } } pub struct TreeViewState>(Arc>>); @@ -71,6 +89,19 @@ impl> TreeViewState { pub async fn handle_navigation(&mut self, event: KeyEvent) { self.0.lock().await.handle_navigation(event).await; } + + pub async fn handle_messaging( + &self, + terminal: &mut Terminal, + crossterm_lock: &Arc>, + event: KeyEvent, + ) -> Option<(Option, String)> { + self.0 + .lock() + .await + .handle_messaging(terminal, crossterm_lock, event) + .await + } } //////////// diff --git a/src/ui/chat/tree/action.rs b/src/ui/chat/tree/action.rs index 2bb2911..32e4016 100644 --- a/src/ui/chat/tree/action.rs +++ b/src/ui/chat/tree/action.rs @@ -4,93 +4,94 @@ use parking_lot::FairMutex; use toss::terminal::Terminal; use crate::store::{Msg, MsgStore}; +use crate::ui::util; -use super::{Cursor, TreeView}; +use super::{Cursor, InnerTreeViewState}; -impl TreeView { - fn prompt_msg(crossterm_lock: &Arc>, terminal: &mut Terminal) -> Option { - let content = { - let _guard = crossterm_lock.lock(); - terminal.suspend().expect("could not suspend"); - let content = edit::edit("").expect("could not edit"); - terminal.unsuspend().expect("could not unsuspend"); - content - }; - - if content.trim().is_empty() { - None - } else { - Some(content) - } - } - - pub async fn reply_normal>( - store: &S, - cursor: &Option>, +impl> InnerTreeViewState { + pub async fn reply_normal( + &self, terminal: &mut Terminal, crossterm_lock: &Arc>, ) -> Option<(Option, String)> { - if let Some(cursor) = cursor { - let tree = store.tree(store.path(&cursor.id).await.first()).await; - let parent_id = if tree.next_sibling(&cursor.id).is_some() { - // A reply to a message that has further siblings should be a - // direct reply. An indirect reply might end up a lot further - // down in the current conversation. - cursor.id.clone() - } else if let Some(parent) = tree.parent(&cursor.id) { - // A reply to a message without further siblings should be an - // indirect reply so as not to create unnecessarily deep - // threads. In the case that our message has children, this - // might get a bit confusing. I'm not sure yet how well this - // "smart" reply actually works in practice. - parent - } else { - // When replying to a top-level message, it makes sense to avoid - // creating unnecessary new threads. - cursor.id.clone() - }; - - if let Some(content) = Self::prompt_msg(crossterm_lock, terminal) { - return Some((Some(parent_id), content)); + match &self.cursor { + Cursor::Bottom => { + if let Some(content) = util::prompt(terminal, crossterm_lock) { + return Some((None, content)); + } } + Cursor::Msg(msg) => { + let path = self.store.path(msg).await; + let tree = self.store.tree(path.first()).await; + let parent_id = if tree.next_sibling(msg).is_some() { + // A reply to a message that has further siblings should be a + // direct reply. An indirect reply might end up a lot further + // down in the current conversation. + msg.clone() + } else if let Some(parent) = tree.parent(msg) { + // A reply to a message without younger siblings should be + // an indirect reply so as not to create unnecessarily deep + // threads. In the case that our message has children, this + // might get a bit confusing. I'm not sure yet how well this + // "smart" reply actually works in practice. + parent + } else { + // When replying to a top-level message, it makes sense to avoid + // creating unnecessary new threads. + msg.clone() + }; + + if let Some(content) = util::prompt(terminal, crossterm_lock) { + return Some((Some(parent_id), content)); + } + } + _ => {} } None } /// Does approximately the opposite of [`Self::reply_normal`]. - pub async fn reply_alternate>( - store: &S, - cursor: &Option>, + pub async fn reply_alternate( + &self, terminal: &mut Terminal, crossterm_lock: &Arc>, ) -> Option<(Option, String)> { - if let Some(cursor) = cursor { - let tree = store.tree(store.path(&cursor.id).await.first()).await; - let parent_id = if tree.next_sibling(&cursor.id).is_none() { - // The opposite of replying normally - cursor.id.clone() - } else if let Some(parent) = tree.parent(&cursor.id) { - // The opposite of replying normally - parent - } else { - // The same as replying normally, still to avoid creating - // unnecessary new threads - cursor.id.clone() - }; - - if let Some(content) = Self::prompt_msg(crossterm_lock, terminal) { - return Some((Some(parent_id), content)); + match &self.cursor { + Cursor::Bottom => { + if let Some(content) = util::prompt(terminal, crossterm_lock) { + return Some((None, content)); + } } + Cursor::Msg(msg) => { + let path = self.store.path(msg).await; + let tree = self.store.tree(path.first()).await; + let parent_id = if tree.next_sibling(msg).is_none() { + // The opposite of replying normally + msg.clone() + } else if let Some(parent) = tree.parent(msg) { + // The opposite of replying normally + parent + } else { + // The same as replying normally, still to avoid creating + // unnecessary new threads + msg.clone() + }; + + if let Some(content) = util::prompt(terminal, crossterm_lock) { + return Some((Some(parent_id), content)); + } + } + _ => {} } None } - pub async fn create_new_thread( + pub fn create_new_thread( terminal: &mut Terminal, crossterm_lock: &Arc>, ) -> Option<(Option, String)> { - Self::prompt_msg(crossterm_lock, terminal).map(|c| (None, c)) + util::prompt(terminal, crossterm_lock).map(|content| (None, content)) } } diff --git a/src/ui/room.rs b/src/ui/room.rs index c789e93..c0fb6c0 100644 --- a/src/ui/room.rs +++ b/src/ui/room.rs @@ -304,13 +304,13 @@ impl EuphRoom { } } - // if let Some((parent, content)) = self - // .chat - // .handle_messaging(terminal, crossterm_lock, event) - // .await - // { - // let _ = room.send(parent, content); - // } + let potential_message = self + .chat + .handle_messaging(terminal, crossterm_lock, event) + .await; + if let Some((parent, content)) = potential_message { + let _ = room.send(parent, content); + } } } }