From c38b66ac34f1f7d4e6b715827e8bf18f28658850 Mon Sep 17 00:00:00 2001 From: wiru Date: Sun, 6 Oct 2024 17:45:16 -0300 Subject: [PATCH] feat: editing requests from sidebar --- .../sidebar/create_request_form.rs | 2 + .../sidebar/edit_request_form.rs | 81 +++++++++++-- .../pages/collection_viewer/sidebar/mod.rs | 18 +-- hac-store/src/collection.rs | 112 ++++++++++++++++-- hac-store/src/slab.rs | 10 +- 5 files changed, 194 insertions(+), 29 deletions(-) diff --git a/hac-client/src/pages/collection_viewer/sidebar/create_request_form.rs b/hac-client/src/pages/collection_viewer/sidebar/create_request_form.rs index 6d04f30..bbafded 100644 --- a/hac-client/src/pages/collection_viewer/sidebar/create_request_form.rs +++ b/hac-client/src/pages/collection_viewer/sidebar/create_request_form.rs @@ -76,6 +76,7 @@ impl CreateRequestForm { KeyCode::Enter => { let request = hac_store::collection::Request::new(self.name.clone(), self.method, self.parent); hac_store::collection::push_request(request, self.parent); + hac_store::collection::rebuild_tree_layout(); router_drop_dialog!(&self.messager, Routes::CreateRequest.into()); } _ => {} @@ -88,6 +89,7 @@ impl CreateRequestForm { match key_event.code { KeyCode::Char('j') | KeyCode::Down => self.parent_listing.select_down(), KeyCode::Char('k') | KeyCode::Up => self.parent_listing.select_up(), + KeyCode::Esc => self.form_step = FormStep::MainForm, KeyCode::Enter => { if hac_store::collection::has_folders() { self.parent = Some(self.parent_listing.selected); diff --git a/hac-client/src/pages/collection_viewer/sidebar/edit_request_form.rs b/hac-client/src/pages/collection_viewer/sidebar/edit_request_form.rs index 67c0a50..3171321 100644 --- a/hac-client/src/pages/collection_viewer/sidebar/edit_request_form.rs +++ b/hac-client/src/pages/collection_viewer/sidebar/edit_request_form.rs @@ -2,7 +2,7 @@ use std::sync::mpsc::{channel, Sender}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use hac_core::command::Command; -use hac_store::collection::ReqMethod; +use hac_store::collection::{EntryStatus, ReqMethod, WhichSlab}; use hac_store::slab::Key; use ratatui::layout::Rect; use ratatui::Frame; @@ -75,12 +75,74 @@ impl EditRequestForm { KeyCode::Tab => self.focus.next(), KeyCode::BackTab => self.focus.prev(), KeyCode::Esc => { - router_drop_dialog!(&self.messager, Routes::CreateRequest.into()); + router_drop_dialog!(&self.messager, Routes::EditRequest.into()); } KeyCode::Enter => { - let request = hac_store::collection::Request::new(self.name.clone(), self.method, self.parent); - hac_store::collection::push_request(request, self.parent); - router_drop_dialog!(&self.messager, Routes::CreateRequest.into()); + // editing a request gives us a few possible scenarios: + // 1. parent of the request didn't change, either if it had no parent and still has + // no parent, or if it had one and it wasn't changed. + // - in this case, we just need to update the request on the current slab it + // lives in + // 2. parent of the request changed from not having a parent to having a parent. + // - in this case we need to remove the request from root_requests and move it + // to requests, also updating the new parent to hold the key to its new request + // 3. parent of the request changed from having a parent to not having a parent. + // - similar to the previous one, we need to remove it from requests, and also + // remove the key from the current parent, and then add it into root_requests. + // 4. parent of the request changed to another parent, so we only need to update + // both folders + match (self.prev_parent, self.parent) { + (Some(a), Some(b)) if a == b => hac_store::collection::get_request_mut(self.key, |req, _| { + req.name.clone_from(&self.name); + req.method = self.method; + req.parent = self.parent; + }), + (None, None) => hac_store::collection::get_root_request_mut(self.key, |req, _| { + req.name.clone_from(&self.name); + req.method = self.method; + req.parent = self.parent; + }), + (None, Some(new_parent)) => { + let status = hac_store::collection::get_root_request(self.key, |_, status| status); + let is_selected = matches!(status, EntryStatus::Selected | EntryStatus::Both); + let mut req = hac_store::collection::remove_root_request(self.key); + req.name.clone_from(&self.name); + req.method = self.method; + req.parent = self.parent; + let key = hac_store::collection::push_request(req, Some(new_parent)); + hac_store::collection::set_hovered_request(Some((WhichSlab::Requests, key))); + if is_selected { + hac_store::collection::set_selected_request(Some((WhichSlab::Requests, key))); + } + } + (Some(_), None) => { + let status = hac_store::collection::get_request(self.key, |_, status| status); + let is_selected = matches!(status, EntryStatus::Selected | EntryStatus::Both); + let mut req = hac_store::collection::remove_request(self.key); + req.name.clone_from(&self.name); + req.method = self.method; + req.parent = self.parent; + let key = hac_store::collection::push_request(req, None); + hac_store::collection::set_hovered_request(Some((WhichSlab::RootRequests, key))); + if is_selected { + hac_store::collection::set_selected_request(Some((WhichSlab::RootRequests, key))); + } + } + (Some(curr_parent), Some(new_parent)) => { + hac_store::collection::get_request_mut(self.key, |req, _| { + req.name.clone_from(&self.name); + req.method = self.method; + req.parent = self.parent; + }); + hac_store::collection::get_folder_mut(curr_parent, |folder, _| { + folder.requests.retain(|r| *r != self.key) + }); + hac_store::collection::get_folder_mut(new_parent, |folder, _| folder.requests.push(self.key)); + } + } + + hac_store::collection::rebuild_tree_layout(); + router_drop_dialog!(&self.messager, Routes::EditRequest.into()); } _ => {} }; @@ -92,6 +154,7 @@ impl EditRequestForm { match key_event.code { KeyCode::Char('j') | KeyCode::Down => self.parent_listing.select_down(), KeyCode::Char('k') | KeyCode::Up => self.parent_listing.select_up(), + KeyCode::Esc => self.form_step = FormStep::MainForm, KeyCode::Enter => { if hac_store::collection::has_folders() { self.parent = Some(self.parent_listing.selected); @@ -110,8 +173,6 @@ impl Renderable for EditRequestForm { type Input = (); type Output = (); - fn data(&self, _requester: u8) -> Self::Output {} - fn draw(&mut self, frame: &mut Frame, _: Rect) -> anyhow::Result<()> { make_overlay(self.colors.clone(), self.colors.normal.black, 0.15, frame); @@ -132,6 +193,12 @@ impl Renderable for EditRequestForm { Ok(()) } + + fn data(&self, _requester: u8) -> Self::Output {} + + fn attach_navigator(&mut self, messager: Sender) { + self.messager = messager; + } } impl Eventful for EditRequestForm { diff --git a/hac-client/src/pages/collection_viewer/sidebar/mod.rs b/hac-client/src/pages/collection_viewer/sidebar/mod.rs index 22f7bf6..12a3851 100755 --- a/hac-client/src/pages/collection_viewer/sidebar/mod.rs +++ b/hac-client/src/pages/collection_viewer/sidebar/mod.rs @@ -86,27 +86,27 @@ impl Sidebar { if self.show_extended_hint { let lines = vec![ Line::from(vec![ - "j/k ↑/↓".fg(self.colors.normal.green), + "j/k ↑/↓".fg(self.colors.bright.green).bold(), " - Choose • ".fg(self.colors.bright.black), - "n".fg(self.colors.normal.green), + "n".fg(self.colors.bright.green).bold(), " - New request • ".fg(self.colors.bright.black), - "Enter".fg(self.colors.normal.green), + "Enter".fg(self.colors.bright.green).bold(), " - Select request".fg(self.colors.bright.black), ]), Line::from(vec![ - "?".fg(self.colors.normal.green), + "?".fg(self.colors.bright.green).bold(), " - Show less • ".fg(self.colors.bright.black), - "Ctrl c".fg(self.colors.normal.green), + "Ctrl c".fg(self.colors.bright.green).bold(), " - Quit • ".fg(self.colors.bright.black), - "d".fg(self.colors.normal.green), + "d".fg(self.colors.bright.green).bold(), " - Delete item".fg(self.colors.bright.black), ]), Line::from(vec![ - "e".fg(self.colors.normal.green), + "e".fg(self.colors.bright.green).bold(), " - Edit • ".fg(self.colors.bright.black), - "Tab".fg(self.colors.normal.green), + "Tab".fg(self.colors.bright.green).bold(), " - Next • ".fg(self.colors.bright.black), - "S-Tab".fg(self.colors.normal.green), + "S-Tab".fg(self.colors.bright.green).bold(), " - Prev".fg(self.colors.bright.black), ]), ]; diff --git a/hac-store/src/collection.rs b/hac-store/src/collection.rs index adaeb2b..abed43f 100644 --- a/hac-store/src/collection.rs +++ b/hac-store/src/collection.rs @@ -14,7 +14,7 @@ pub struct ReqTree { pub nodes: Vec, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum EntryStatus { None, Hovered, @@ -244,14 +244,52 @@ pub fn tree_layout() -> ReqTree { nodes } -pub fn get_root_request(key: Key, f: F) +pub fn remove_request(key: Key) -> Request { + HAC_STORE.with_borrow_mut(|store| { + assert!(store.collection.is_some()); + let collection = store.collection.as_mut().unwrap(); + let req = collection.requests.remove(key); + assert!(req.parent.is_some()); + let folder = collection.folders.get_mut(req.parent.unwrap()); + folder.requests.retain(|r| *r != key); + req + }) +} + +pub fn remove_root_request(key: Key) -> Request { + HAC_STORE.with_borrow_mut(|store| { + assert!(store.collection.is_some()); + let collection = store.collection.as_mut().unwrap(); + collection.root_requests.remove(key) + }) +} + +pub fn get_root_request(key: Key, f: F) -> R where - F: FnOnce(&Request, EntryStatus), + F: FnOnce(&Request, EntryStatus) -> R, { HAC_STORE.with_borrow(|store| { assert!(store.collection.is_some()); let collection = store.collection.as_ref().unwrap(); let req = collection.root_requests.get(key); + let is_hovered = collection + .hovered_request + .is_some_and(|(slab, k)| matches!(slab, WhichSlab::RootRequests) && key == k); + let is_selected = collection + .selected_request + .is_some_and(|(slab, k)| matches!(slab, WhichSlab::RootRequests) && key == k); + f(req, (is_hovered, is_selected).into()) + }) +} + +pub fn get_root_request_mut(key: Key, f: F) +where + F: FnOnce(&mut Request, EntryStatus), +{ + HAC_STORE.with_borrow_mut(|store| { + assert!(store.collection.is_some()); + let collection = store.collection.as_mut().unwrap(); + let req = collection.root_requests.get_mut(key); let is_hovered = collection .hovered_request .is_some_and(|(slab, k)| matches!(slab, WhichSlab::RootRequests) && key == k); @@ -262,14 +300,32 @@ where }) } -pub fn get_request(key: Key, f: F) +pub fn get_request(key: Key, f: F) -> R where - F: FnOnce(&Request, EntryStatus), + F: FnOnce(&Request, EntryStatus) -> R, { HAC_STORE.with_borrow(|store| { assert!(store.collection.is_some()); let collection = store.collection.as_ref().unwrap(); let req = collection.requests.get(key); + let is_hovered = collection + .hovered_request + .is_some_and(|(slab, k)| matches!(slab, WhichSlab::Requests) && key == k); + let is_selected = collection + .selected_request + .is_some_and(|(slab, k)| matches!(slab, WhichSlab::Requests) && key == k); + f(req, (is_hovered, is_selected).into()) + }) +} + +pub fn get_request_mut(key: Key, f: F) +where + F: FnOnce(&mut Request, EntryStatus), +{ + HAC_STORE.with_borrow_mut(|store| { + assert!(store.collection.is_some()); + let collection = store.collection.as_mut().unwrap(); + let req = collection.requests.get_mut(key); let is_hovered = collection .hovered_request .is_some_and(|(slab, k)| matches!(slab, WhichSlab::Requests) && key == k); @@ -317,6 +373,24 @@ where }) } +pub fn get_folder_mut(key: Key, f: F) +where + F: FnOnce(&mut Folder, EntryStatus), +{ + HAC_STORE.with_borrow_mut(|store| { + assert!(store.collection.is_some()); + let collection = store.collection.as_mut().unwrap(); + let folder = collection.folders.get_mut(key); + let is_hovered = collection + .hovered_request + .is_some_and(|(slab, k)| matches!(slab, WhichSlab::Folders) && key == k); + let is_selected = collection + .selected_request + .is_some_and(|(slab, k)| matches!(slab, WhichSlab::Folders) && key == k); + f(folder, (is_hovered, is_selected).into()); + }) +} + pub fn hover_next() { HAC_STORE.with_borrow_mut(|store| { assert!(store.collection.is_some()); @@ -448,6 +522,22 @@ pub fn hover_prev() { }) } +pub fn set_selected_request(new_hovered: Option<(WhichSlab, Key)>) { + HAC_STORE.with_borrow_mut(|store| { + assert!(store.collection.is_some()); + let collection = store.collection.as_mut().unwrap(); + collection.selected_request = new_hovered; + }) +} + +pub fn set_hovered_request(new_hovered: Option<(WhichSlab, Key)>) { + HAC_STORE.with_borrow_mut(|store| { + assert!(store.collection.is_some()); + let collection = store.collection.as_mut().unwrap(); + collection.hovered_request = new_hovered; + }) +} + pub fn get_hovered_request(f: F) -> R where F: FnOnce(Option<(WhichSlab, Key)>) -> R, @@ -476,7 +566,7 @@ pub fn toggle_dir(key: Key) { }); } -pub fn push_request(request: Request, parent: Option) { +pub fn push_request(request: Request, parent: Option) -> Key { HAC_STORE.with_borrow_mut(|store| { assert!(store.collection.is_some()); let collection = store.collection.as_mut().unwrap(); @@ -485,14 +575,14 @@ pub fn push_request(request: Request, parent: Option) { let new_key = collection.requests.push(request); let folder = collection.folders.get_mut(key); folder.requests.push(new_key); + new_key } - None => _ = collection.root_requests.push(request), - }; - }); - rebuild_tree_layout(); + None => collection.root_requests.push(request), + } + }) } -fn rebuild_tree_layout() { +pub fn rebuild_tree_layout() { HAC_STORE.with_borrow_mut(|store| { assert!(store.collection.is_some()); let collection = store.collection.as_mut().unwrap(); diff --git a/hac-store/src/slab.rs b/hac-store/src/slab.rs index a512265..8ec8aa4 100644 --- a/hac-store/src/slab.rs +++ b/hac-store/src/slab.rs @@ -103,11 +103,17 @@ impl Slab { } pub fn len(&self) -> usize { - self.inner.len() + self.inner + .iter() + .filter_map(|e| match e { + Entry::Full(val) => Some(val), + Entry::Free(_) => None, + }) + .count() } pub fn is_empty(&self) -> bool { - self.inner.is_empty() + self.len() == 0 } pub fn next_idx(&self) -> Key {