From 1b9906db099c8edd0781058994ecab90d1d0a3c7 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Tue, 8 Aug 2023 16:31:25 +0200 Subject: [PATCH] Remove `radicle-tui` crate This crate is now in its own repository: rid:z39mP9rQAaGmERfUMPULfPUi473tY --- Cargo.lock | 175 +------ Cargo.toml | 1 - radicle-tui/Cargo.toml | 31 -- radicle-tui/src/app.rs | 208 -------- radicle-tui/src/app/event.rs | 197 -------- radicle-tui/src/app/page.rs | 403 --------------- radicle-tui/src/app/subscription.rs | 31 -- radicle-tui/src/cob.rs | 2 - radicle-tui/src/cob/issue.rs | 19 - radicle-tui/src/cob/patch.rs | 20 - radicle-tui/src/lib.rs | 98 ---- radicle-tui/src/main.rs | 84 ---- radicle-tui/src/ui.rs | 7 - radicle-tui/src/ui/cob.rs | 361 -------------- radicle-tui/src/ui/context.rs | 40 -- radicle-tui/src/ui/ext.rs | 113 ----- radicle-tui/src/ui/layout.rs | 210 -------- radicle-tui/src/ui/state.rs | 85 ---- radicle-tui/src/ui/theme.rs | 122 ----- radicle-tui/src/ui/widget.rs | 106 ---- radicle-tui/src/ui/widget/common.rs | 154 ------ radicle-tui/src/ui/widget/common/container.rs | 458 ------------------ radicle-tui/src/ui/widget/common/context.rs | 175 ------- radicle-tui/src/ui/widget/common/label.rs | 81 ---- radicle-tui/src/ui/widget/common/list.rs | 376 -------------- radicle-tui/src/ui/widget/home.rs | 284 ----------- radicle-tui/src/ui/widget/issue.rs | 182 ------- radicle-tui/src/ui/widget/patch.rs | 202 -------- radicle-tui/src/ui/widget/utils.rs | 43 -- 29 files changed, 2 insertions(+), 4266 deletions(-) delete mode 100644 radicle-tui/Cargo.toml delete mode 100644 radicle-tui/src/app.rs delete mode 100644 radicle-tui/src/app/event.rs delete mode 100644 radicle-tui/src/app/page.rs delete mode 100644 radicle-tui/src/app/subscription.rs delete mode 100644 radicle-tui/src/cob.rs delete mode 100644 radicle-tui/src/cob/issue.rs delete mode 100644 radicle-tui/src/cob/patch.rs delete mode 100644 radicle-tui/src/lib.rs delete mode 100644 radicle-tui/src/main.rs delete mode 100644 radicle-tui/src/ui.rs delete mode 100644 radicle-tui/src/ui/cob.rs delete mode 100644 radicle-tui/src/ui/context.rs delete mode 100644 radicle-tui/src/ui/ext.rs delete mode 100644 radicle-tui/src/ui/layout.rs delete mode 100644 radicle-tui/src/ui/state.rs delete mode 100644 radicle-tui/src/ui/theme.rs delete mode 100644 radicle-tui/src/ui/widget.rs delete mode 100644 radicle-tui/src/ui/widget/common.rs delete mode 100644 radicle-tui/src/ui/widget/common/container.rs delete mode 100644 radicle-tui/src/ui/widget/common/context.rs delete mode 100644 radicle-tui/src/ui/widget/common/label.rs delete mode 100644 radicle-tui/src/ui/widget/common/list.rs delete mode 100644 radicle-tui/src/ui/widget/home.rs delete mode 100644 radicle-tui/src/ui/widget/issue.rs delete mode 100644 radicle-tui/src/ui/widget/patch.rs delete mode 100644 radicle-tui/src/ui/widget/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 2559e9f7..006f953a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,17 +38,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.10", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.3" @@ -420,12 +409,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - [[package]] name = "cc" version = "1.0.79" @@ -1053,9 +1036,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hashbrown" @@ -1063,7 +1043,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash", ] [[package]] @@ -1307,15 +1287,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "isolang" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80f221db1bc708b71128757b9396727c04de86968081e18e89b0575e03be071" -dependencies = [ - "phf", -] - [[package]] name = "itoa" version = "1.0.8" @@ -1360,29 +1331,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "lazy-regex" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff63c423c68ea6814b7da9e88ce585f793c87ddd9e78f646970891769c8235d4" -dependencies = [ - "lazy-regex-proc_macros", - "once_cell", - "regex", -] - -[[package]] -name = "lazy-regex-proc_macros" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8edfc11b8f56ce85e207e62ea21557cfa09bb24a8f6b04ae181b086ff8611c22" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1736,24 +1684,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" version = "1.1.2" @@ -1969,7 +1899,7 @@ dependencies = [ "similar", "tempfile", "thiserror", - "timeago 0.3.1", + "timeago", "ureq", "zeroize", ] @@ -2202,21 +2132,6 @@ dependencies = [ "radicle-git-ext", ] -[[package]] -name = "radicle-tui" -version = "0.1.0" -dependencies = [ - "anyhow", - "lexopt", - "radicle", - "radicle-cli", - "radicle-surf", - "radicle-term", - "timeago 0.4.1", - "tui-realm-stdlib", - "tuirealm", -] - [[package]] name = "rand" version = "0.7.3" @@ -2634,12 +2549,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" -[[package]] -name = "smawk" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" - [[package]] name = "snapbox" version = "0.4.11" @@ -2853,17 +2762,6 @@ dependencies = [ "redox_termios", ] -[[package]] -name = "textwrap" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.43" @@ -2938,16 +2836,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ec32dde57efb15c035ac074118d7f32820451395f28cb0524a01d4e94983b26" -[[package]] -name = "timeago" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5082dc942361cdfb74eab98bf995762d6015e5bb3a20bf7c5c71213778b4fcb4" -dependencies = [ - "chrono", - "isolang", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -3134,55 +3022,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "tui" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" -dependencies = [ - "bitflags 1.3.2", - "cassowary", - "termion 1.5.6", - "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "tui-realm-stdlib" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66f252bf8b07c6fd708ddd6349b5f044ae5b488b26929c745728d9c7e2cebfa6" -dependencies = [ - "textwrap", - "tuirealm", - "unicode-width", -] - -[[package]] -name = "tuirealm" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "265411b5606f400459af94fbc5aae6a7bc0e98094d08cb5868390c932be88e26" -dependencies = [ - "bitflags 1.3.2", - "lazy-regex", - "termion 1.5.6", - "thiserror", - "tui", - "tuirealm_derive", -] - -[[package]] -name = "tuirealm_derive" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0adcdaf59881626555558eae08f8a53003c8a1961723b4d7a10c51599abbc81" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "typenum" version = "1.16.0" @@ -3201,16 +3040,6 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" -[[package]] -name = "unicode-linebreak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" -dependencies = [ - "hashbrown 0.12.3", - "regex", -] - [[package]] name = "unicode-normalization" version = "0.1.22" diff --git a/Cargo.toml b/Cargo.toml index 1fe5c8cb..6814b72b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ "radicle-remote-helper", "radicle-ssh", "radicle-tools", - "radicle-tui", ] default-members = [ "radicle", diff --git a/radicle-tui/Cargo.toml b/radicle-tui/Cargo.toml deleted file mode 100644 index 4ecd51c2..00000000 --- a/radicle-tui/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "radicle-tui" -license = "MIT OR Apache-2.0" -version = "0.1.0" -authors = ["Erik Kundt "] -edition = "2021" -build = "../build.rs" - -[[bin]] -name = "radicle-tui" -path = "src/main.rs" - -[dependencies] -anyhow = { version = "1" } -lexopt = { version = "0.2" } -radicle-surf = { version = "0.14.0" } -timeago = { version = "0.4.1" } -tuirealm = { version = "1.8.0", default-features = false, features = [ "with-termion" ] } -tui-realm-stdlib = { version = "1.2.0", default-features = false, features = [ "with-termion" ] } - -[dependencies.radicle] -version = "0" -path = "../radicle" - -[dependencies.radicle-cli] -version = "0" -path = "../radicle-cli" - -[dependencies.radicle-term] -version = "0" -path = "../radicle-term" diff --git a/radicle-tui/src/app.rs b/radicle-tui/src/app.rs deleted file mode 100644 index 721b0609..00000000 --- a/radicle-tui/src/app.rs +++ /dev/null @@ -1,208 +0,0 @@ -pub mod event; -pub mod page; -pub mod subscription; - -use anyhow::Result; - -use radicle::cob::issue::IssueId; -use radicle::cob::patch::PatchId; -use radicle::identity::{Id, Project}; -use radicle::profile::Profile; - -use tuirealm::application::PollStrategy; -use tuirealm::{Application, Frame, NoUserEvent}; - -use radicle_tui::ui::context::Context; -use radicle_tui::ui::theme::{self, Theme}; -use radicle_tui::Tui; -use radicle_tui::{cob, ui}; - -use page::{HomeView, PatchView}; - -use self::page::{IssuePage, PageStack}; - -#[derive(Debug, Eq, PartialEq, Clone, Hash)] -pub enum HomeCid { - Header, - Dashboard, - IssueBrowser, - PatchBrowser, -} - -#[derive(Debug, Eq, PartialEq, Clone, Hash)] -pub enum PatchCid { - Header, - Activity, - Files, -} - -#[derive(Debug, Eq, PartialEq, Clone, Hash)] -pub enum IssueCid { - Header, - List, - Details, - Shortcuts, -} - -/// All component ids known to this application. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] -pub enum Cid { - Home(HomeCid), - Issue(IssueCid), - Patch(PatchCid), - GlobalListener, -} - -/// Messages handled by this application. -#[derive(Debug, Eq, PartialEq)] -pub enum HomeMessage {} - -#[derive(Debug, Eq, PartialEq)] -pub enum IssueMessage { - Show(IssueId), - Changed(IssueId), - Focus(IssueCid), - Leave, -} - -#[derive(Debug, Eq, PartialEq)] -pub enum PatchMessage { - Show(PatchId), - Leave, -} - -#[derive(Debug, Eq, PartialEq)] -pub enum Message { - Home(HomeMessage), - Issue(IssueMessage), - Patch(PatchMessage), - NavigationChanged(u16), - Tick, - Quit, -} - -#[allow(dead_code)] -pub struct App { - context: Context, - pages: PageStack, - theme: Theme, - quit: bool, -} - -/// Creates a new application using a tui-realm-application, mounts all -/// components and sets focus to a default one. -impl App { - pub fn new(profile: Profile, id: Id, project: Project) -> Self { - Self { - context: Context::new(profile, id, project), - pages: PageStack::default(), - theme: theme::default_dark(), - quit: false, - } - } - - fn view_home( - &mut self, - app: &mut Application, - theme: &Theme, - ) -> Result<()> { - let home = Box::::default(); - self.pages.push(home, app, &self.context, theme)?; - - Ok(()) - } - - fn view_patch( - &mut self, - app: &mut Application, - id: PatchId, - theme: &Theme, - ) -> Result<()> { - let repo = self.context.repository(); - - if let Some(patch) = cob::patch::find(repo, &id)? { - let view = Box::new(PatchView::new((id, patch))); - self.pages.push(view, app, &self.context, theme)?; - - Ok(()) - } else { - Err(anyhow::anyhow!( - "Could not mount 'page::PatchView'. Patch not found." - )) - } - } - - fn view_issue( - &mut self, - app: &mut Application, - id: IssueId, - theme: &Theme, - ) -> Result<()> { - let repo = self.context.repository(); - - if let Some(issue) = cob::issue::find(repo, &id)? { - let view = Box::new(IssuePage::new((id, issue))); - self.pages.push(view, app, &self.context, theme)?; - - Ok(()) - } else { - Err(anyhow::anyhow!( - "Could not mount 'page::IssueView'. Issue not found." - )) - } - } -} - -impl Tui for App { - fn init(&mut self, app: &mut Application) -> Result<()> { - self.view_home(app, &self.theme.clone())?; - - // Add global key listener and subscribe to key events - let global = ui::widget::common::global_listener().to_boxed(); - app.mount(Cid::GlobalListener, global, subscription::global())?; - - Ok(()) - } - - fn view(&mut self, app: &mut Application, frame: &mut Frame) { - if let Ok(page) = self.pages.peek_mut() { - page.view(app, frame); - } - } - - fn update(&mut self, app: &mut Application) -> Result { - match app.tick(PollStrategy::Once) { - Ok(messages) if !messages.is_empty() => { - let theme = theme::default_dark(); - for message in messages { - match message { - Message::Issue(IssueMessage::Show(id)) => { - self.view_issue(app, id, &theme)?; - } - Message::Issue(IssueMessage::Leave) => { - self.pages.pop(app)?; - } - Message::Patch(PatchMessage::Show(id)) => { - self.view_patch(app, id, &theme)?; - } - Message::Patch(PatchMessage::Leave) => { - self.pages.pop(app)?; - } - Message::Quit => self.quit = true, - _ => { - self.pages - .peek_mut()? - .update(app, &self.context, &theme, message)?; - } - } - } - Ok(true) - } - _ => Ok(false), - } - } - - fn quit(&self) -> bool { - self.quit - } -} diff --git a/radicle-tui/src/app/event.rs b/radicle-tui/src/app/event.rs deleted file mode 100644 index cd352735..00000000 --- a/radicle-tui/src/app/event.rs +++ /dev/null @@ -1,197 +0,0 @@ -use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection}; -use tuirealm::event::{Event, Key, KeyEvent}; -use tuirealm::{MockComponent, NoUserEvent, State, StateValue}; - -use radicle_tui::ui::widget::common::container::{AppHeader, GlobalListener, LabeledContainer}; -use radicle_tui::ui::widget::common::context::{ContextBar, Shortcuts}; -use radicle_tui::ui::widget::common::list::PropertyList; -use radicle_tui::ui::widget::home::{Dashboard, IssueBrowser, PatchBrowser}; -use radicle_tui::ui::widget::{issue, patch}; - -use radicle_tui::ui::widget::Widget; - -use super::{IssueMessage, Message, PatchMessage}; - -/// Since the framework does not know the type of messages that are being -/// passed around in the app, the following handlers need to be implemented for -/// each component used. -/// -/// TODO: should handle `Event::WindowResize`, which is not emitted by `termion`. -impl tuirealm::Component for Widget { - fn on(&mut self, event: Event) -> Option { - match event { - Event::Keyboard(KeyEvent { - code: Key::Char('q'), - .. - }) => Some(Message::Quit), - _ => None, - } - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, event: Event) -> Option { - match event { - Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => { - match self.perform(Cmd::Move(MoveDirection::Right)) { - CmdResult::Changed(State::One(StateValue::U16(index))) => { - Some(Message::NavigationChanged(index)) - } - _ => None, - } - } - _ => None, - } - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, event: Event) -> Option { - match event { - Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => { - Some(Message::Issue(IssueMessage::Leave)) - } - Event::Keyboard(KeyEvent { code: Key::Up, .. }) => { - let result = self.perform(Cmd::Move(MoveDirection::Up)); - match result { - CmdResult::Changed(State::One(StateValue::Usize(selected))) => { - let item = self.items().get(selected)?; - Some(Message::Issue(IssueMessage::Changed(item.id().to_owned()))) - } - _ => None, - } - } - Event::Keyboard(KeyEvent { - code: Key::Down, .. - }) => { - let result = self.perform(Cmd::Move(MoveDirection::Down)); - match result { - CmdResult::Changed(State::One(StateValue::Usize(selected))) => { - let item = self.items().get(selected)?; - Some(Message::Issue(IssueMessage::Changed(item.id().to_owned()))) - } - _ => None, - } - } - _ => None, - } - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, _event: Event) -> Option { - None - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, event: Event) -> Option { - match event { - Event::Keyboard(KeyEvent { code: Key::Up, .. }) => { - self.perform(Cmd::Move(MoveDirection::Up)); - Some(Message::Tick) - } - Event::Keyboard(KeyEvent { - code: Key::Down, .. - }) => { - self.perform(Cmd::Move(MoveDirection::Down)); - Some(Message::Tick) - } - Event::Keyboard(KeyEvent { - code: Key::Enter, .. - }) => { - let result = self.perform(Cmd::Submit); - match result { - CmdResult::Submit(State::One(StateValue::Usize(selected))) => { - let item = self.items().get(selected)?; - Some(Message::Patch(PatchMessage::Show(item.id().to_owned()))) - } - _ => None, - } - } - _ => None, - } - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, event: Event) -> Option { - match event { - Event::Keyboard(KeyEvent { code: Key::Up, .. }) => { - self.perform(Cmd::Move(MoveDirection::Up)); - Some(Message::Tick) - } - Event::Keyboard(KeyEvent { - code: Key::Down, .. - }) => { - self.perform(Cmd::Move(MoveDirection::Down)); - Some(Message::Tick) - } - Event::Keyboard(KeyEvent { - code: Key::Enter, .. - }) => { - let result = self.perform(Cmd::Submit); - match result { - CmdResult::Submit(State::One(StateValue::Usize(selected))) => { - let item = self.items().get(selected)?; - Some(Message::Issue(IssueMessage::Show(item.id().to_owned()))) - } - _ => None, - } - } - _ => None, - } - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, _event: Event) -> Option { - None - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, event: Event) -> Option { - match event { - Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => { - Some(Message::Patch(PatchMessage::Leave)) - } - _ => None, - } - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, event: Event) -> Option { - match event { - Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => { - Some(Message::Patch(PatchMessage::Leave)) - } - _ => None, - } - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, _event: Event) -> Option { - None - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, _event: Event) -> Option { - None - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, _event: Event) -> Option { - None - } -} - -impl tuirealm::Component for Widget { - fn on(&mut self, _event: Event) -> Option { - None - } -} diff --git a/radicle-tui/src/app/page.rs b/radicle-tui/src/app/page.rs deleted file mode 100644 index 937e06fb..00000000 --- a/radicle-tui/src/app/page.rs +++ /dev/null @@ -1,403 +0,0 @@ -use anyhow::Result; - -use radicle::cob::issue::{Issue, IssueId}; -use radicle::cob::patch::{Patch, PatchId}; - -use radicle_tui::cob; -use tuirealm::{Frame, NoUserEvent, Sub, SubClause}; - -use radicle_tui::ui::context::Context; -use radicle_tui::ui::layout; -use radicle_tui::ui::theme::Theme; -use radicle_tui::ui::widget; - -use super::{subscription, Application, Cid, HomeCid, IssueCid, IssueMessage, Message, PatchCid}; - -/// `tuirealm`'s event and prop system is designed to work with flat component hierarchies. -/// Building deep nested component hierarchies would need a lot more additional effort to -/// properly pass events and props down these hierarchies. This makes it hard to implement -/// full app views (home, patch details etc) as components. -/// -/// View pages take into account these flat component hierarchies, and provide -/// switchable sets of components. -pub trait ViewPage { - /// Will be called whenever a view page is pushed onto the page stack. Should create and mount all widgets. - fn mount( - &self, - app: &mut Application, - context: &Context, - theme: &Theme, - ) -> Result<()>; - - /// Will be called whenever a view page is popped from the page stack. Should unmount all widgets. - fn unmount(&self, app: &mut Application) -> Result<()>; - - /// Will be called whenever a view page is on top of the stack and can be used to update its internal - /// state depending on the message passed. - fn update( - &mut self, - app: &mut Application, - context: &Context, - theme: &Theme, - message: Message, - ) -> Result<()>; - - /// Will be called whenever a view page is on top of the page stack and needs to be rendered. - fn view(&mut self, app: &mut Application, frame: &mut Frame); - - /// Will be called whenever this view page is pushed to the stack, or it is on top of the stack again - /// after another view page was popped from the stack. - fn subscribe(&self, app: &mut Application) -> Result<()>; - - /// Will be called whenever this view page is on top of the stack and another view page is pushed - /// to the stack, or if this is popped from the stack. - fn unsubscribe(&self, app: &mut Application) -> Result<()>; -} - -/// -/// Home -/// -pub struct HomeView { - active_component: Cid, -} - -impl Default for HomeView { - fn default() -> Self { - HomeView { - active_component: Cid::Home(HomeCid::Dashboard), - } - } -} - -impl ViewPage for HomeView { - fn mount( - &self, - app: &mut Application, - context: &Context, - theme: &Theme, - ) -> Result<()> { - let navigation = widget::home::navigation(theme); - let header = widget::common::app_header(context, theme, Some(navigation)).to_boxed(); - - let dashboard = widget::home::dashboard(context, theme).to_boxed(); - let issue_browser = widget::home::issues(context, theme).to_boxed(); - let patch_browser = widget::home::patches(context, theme).to_boxed(); - - app.remount(Cid::Home(HomeCid::Header), header, vec![])?; - - app.remount(Cid::Home(HomeCid::Dashboard), dashboard, vec![])?; - app.remount(Cid::Home(HomeCid::IssueBrowser), issue_browser, vec![])?; - app.remount(Cid::Home(HomeCid::PatchBrowser), patch_browser, vec![])?; - - app.active(&self.active_component)?; - - Ok(()) - } - - fn unmount(&self, app: &mut Application) -> Result<()> { - app.umount(&Cid::Home(HomeCid::Header))?; - app.umount(&Cid::Home(HomeCid::Dashboard))?; - app.umount(&Cid::Home(HomeCid::IssueBrowser))?; - app.umount(&Cid::Home(HomeCid::PatchBrowser))?; - Ok(()) - } - - fn update( - &mut self, - app: &mut Application, - _context: &Context, - _theme: &Theme, - message: Message, - ) -> Result<()> { - if let Message::NavigationChanged(index) = message { - self.active_component = Cid::Home(HomeCid::from(index as usize)); - app.active(&self.active_component)?; - } - - Ok(()) - } - - fn view(&mut self, app: &mut Application, frame: &mut Frame) { - let area = frame.size(); - let layout = layout::default_page(area); - - app.view(&Cid::Home(HomeCid::Header), frame, layout[0]); - app.view(&self.active_component, frame, layout[1]); - } - - fn subscribe(&self, app: &mut Application) -> Result<()> { - app.subscribe( - &Cid::Home(HomeCid::Header), - Sub::new(subscription::navigation_clause(), SubClause::Always), - )?; - - Ok(()) - } - - fn unsubscribe(&self, app: &mut Application) -> Result<()> { - app.unsubscribe( - &Cid::Home(HomeCid::Header), - subscription::navigation_clause(), - )?; - - Ok(()) - } -} - -/// -/// Issue detail page -/// -pub struct IssuePage { - issue: (IssueId, Issue), -} - -impl IssuePage { - pub fn new(issue: (IssueId, Issue)) -> Self { - IssuePage { issue } - } -} - -impl ViewPage for IssuePage { - fn mount( - &self, - app: &mut Application, - context: &Context, - theme: &Theme, - ) -> Result<()> { - let (id, issue) = &self.issue; - let header = widget::common::app_header(context, theme, None).to_boxed(); - let list = widget::issue::list(context, theme, (*id, issue.clone())).to_boxed(); - let details = widget::issue::details(context, theme, (*id, issue.clone())).to_boxed(); - let shortcuts = widget::common::shortcuts( - theme, - vec![ - widget::common::shortcut(theme, "esc", "back"), - widget::common::shortcut(theme, "q", "quit"), - ], - ) - .to_boxed(); - - app.remount(Cid::Issue(IssueCid::Header), header, vec![])?; - app.remount(Cid::Issue(IssueCid::List), list, vec![])?; - app.remount(Cid::Issue(IssueCid::Details), details, vec![])?; - app.remount(Cid::Issue(IssueCid::Shortcuts), shortcuts, vec![])?; - - app.active(&Cid::Issue(IssueCid::List))?; - - Ok(()) - } - - fn unmount(&self, app: &mut Application) -> Result<()> { - app.umount(&Cid::Issue(IssueCid::Header))?; - app.umount(&Cid::Issue(IssueCid::List))?; - app.umount(&Cid::Issue(IssueCid::Details))?; - app.umount(&Cid::Issue(IssueCid::Shortcuts))?; - Ok(()) - } - - fn update( - &mut self, - app: &mut Application, - context: &Context, - theme: &Theme, - message: Message, - ) -> Result<()> { - match message { - Message::Issue(IssueMessage::Changed(id)) => { - let repo = context.repository(); - if let Some(issue) = cob::issue::find(repo, &id)? { - let details = widget::issue::details(context, theme, (id, issue)).to_boxed(); - app.remount(Cid::Issue(IssueCid::Details), details, vec![])?; - } - } - Message::Issue(IssueMessage::Focus(cid)) => { - app.active(&Cid::Issue(cid))?; - } - _ => {} - } - - Ok(()) - } - - fn view(&mut self, app: &mut Application, frame: &mut Frame) { - let area = frame.size(); - let shortcuts_h = 1u16; - let layout = layout::issue_preview(area, shortcuts_h); - - app.view(&Cid::Issue(IssueCid::Header), frame, layout.header); - app.view(&Cid::Issue(IssueCid::List), frame, layout.list); - app.view(&Cid::Issue(IssueCid::Details), frame, layout.details); - app.view(&Cid::Issue(IssueCid::Shortcuts), frame, layout.shortcuts); - } - - fn subscribe(&self, _app: &mut Application) -> Result<()> { - Ok(()) - } - - fn unsubscribe(&self, _app: &mut Application) -> Result<()> { - Ok(()) - } -} - -/// -/// Patch detail page -/// -pub struct PatchView { - active_component: Cid, - patch: (PatchId, Patch), -} - -impl PatchView { - pub fn new(patch: (PatchId, Patch)) -> Self { - PatchView { - active_component: Cid::Patch(PatchCid::Activity), - patch, - } - } -} - -impl ViewPage for PatchView { - fn mount( - &self, - app: &mut Application, - context: &Context, - theme: &Theme, - ) -> Result<()> { - let (id, patch) = &self.patch; - let navigation = widget::patch::navigation(theme); - let header = widget::common::app_header(context, theme, Some(navigation)).to_boxed(); - - let activity = widget::patch::activity(theme, (*id, patch), context.profile()).to_boxed(); - let files = widget::patch::files(theme, (*id, patch), context.profile()).to_boxed(); - - app.remount(Cid::Patch(PatchCid::Header), header, vec![])?; - app.remount(Cid::Patch(PatchCid::Activity), activity, vec![])?; - app.remount(Cid::Patch(PatchCid::Files), files, vec![])?; - - app.active(&self.active_component)?; - - Ok(()) - } - - fn unmount(&self, app: &mut Application) -> Result<()> { - app.umount(&Cid::Patch(PatchCid::Header))?; - app.umount(&Cid::Patch(PatchCid::Activity))?; - app.umount(&Cid::Patch(PatchCid::Files))?; - Ok(()) - } - - fn update( - &mut self, - app: &mut Application, - _context: &Context, - _theme: &Theme, - message: Message, - ) -> Result<()> { - if let Message::NavigationChanged(index) = message { - self.active_component = Cid::Patch(PatchCid::from(index as usize)); - } - app.active(&self.active_component)?; - - Ok(()) - } - - fn view(&mut self, app: &mut Application, frame: &mut Frame) { - let area = frame.size(); - let layout = layout::default_page(area); - - app.view(&Cid::Patch(PatchCid::Header), frame, layout[0]); - app.view(&self.active_component, frame, layout[1]); - } - - fn subscribe(&self, app: &mut Application) -> Result<()> { - app.subscribe( - &Cid::Patch(PatchCid::Header), - Sub::new(subscription::navigation_clause(), SubClause::Always), - )?; - - Ok(()) - } - - fn unsubscribe(&self, app: &mut Application) -> Result<()> { - app.unsubscribe( - &Cid::Patch(PatchCid::Header), - subscription::navigation_clause(), - )?; - - Ok(()) - } -} - -/// View pages need to preserve their state (e.g. selected navigation tab, contents -/// and the selected row of a table). Therefor they should not be (re-)created -/// each time they are displayed. -/// Instead the application can push a new page onto the page stack if it needs to -/// be displayed. Its components are then created using the internal state. If a -/// new page needs to be displayed, it will also be pushed onto the stack. Leaving -/// that page again will pop it from the stack. The application can then return to -/// the previously displayed page in the state it was left. -#[derive(Default)] -pub struct PageStack { - pages: Vec>, -} - -impl PageStack { - pub fn push( - &mut self, - page: Box, - app: &mut Application, - context: &Context, - theme: &Theme, - ) -> Result<()> { - if let Some(page) = self.pages.last() { - page.unsubscribe(app)?; - } - - page.mount(app, context, theme)?; - page.subscribe(app)?; - - self.pages.push(page); - - Ok(()) - } - - pub fn pop(&mut self, app: &mut Application) -> Result<()> { - self.peek_mut()?.unsubscribe(app)?; - self.peek_mut()?.unmount(app)?; - self.pages.pop(); - - self.peek_mut()?.subscribe(app)?; - - Ok(()) - } - - pub fn peek_mut(&mut self) -> Result<&mut Box> { - match self.pages.last_mut() { - Some(page) => Ok(page), - None => Err(anyhow::anyhow!( - "Could not peek active page. Page stack is empty." - )), - } - } -} - -impl From for HomeCid { - fn from(index: usize) -> Self { - match index { - 0 => HomeCid::Dashboard, - 1 => HomeCid::IssueBrowser, - 2 => HomeCid::PatchBrowser, - _ => HomeCid::Dashboard, - } - } -} - -impl From for PatchCid { - fn from(index: usize) -> Self { - match index { - 0 => PatchCid::Activity, - 1 => PatchCid::Files, - _ => PatchCid::Activity, - } - } -} diff --git a/radicle-tui/src/app/subscription.rs b/radicle-tui/src/app/subscription.rs deleted file mode 100644 index 32a0153c..00000000 --- a/radicle-tui/src/app/subscription.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::hash::Hash; - -use tuirealm::event::{Key, KeyEvent, KeyModifiers}; -use tuirealm::{Sub, SubClause, SubEventClause}; - -pub fn navigation_clause() -> SubEventClause -where - UserEvent: Clone + Eq + PartialEq + PartialOrd, -{ - SubEventClause::Keyboard(KeyEvent { - code: Key::Tab, - modifiers: KeyModifiers::NONE, - }) -} - -pub fn global() -> Vec> -where - Id: Clone + Hash + Eq + PartialEq, - UserEvent: Clone + Eq + PartialEq + PartialOrd, -{ - vec![ - Sub::new( - SubEventClause::Keyboard(KeyEvent { - code: Key::Char('q'), - modifiers: KeyModifiers::NONE, - }), - SubClause::Always, - ), - Sub::new(SubEventClause::WindowResize, SubClause::Always), - ] -} diff --git a/radicle-tui/src/cob.rs b/radicle-tui/src/cob.rs deleted file mode 100644 index a5dfc54b..00000000 --- a/radicle-tui/src/cob.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod issue; -pub mod patch; diff --git a/radicle-tui/src/cob/issue.rs b/radicle-tui/src/cob/issue.rs deleted file mode 100644 index 0e0f1e5c..00000000 --- a/radicle-tui/src/cob/issue.rs +++ /dev/null @@ -1,19 +0,0 @@ -use anyhow::Result; -use radicle::cob::issue::{Issue, IssueId, Issues}; -use radicle::storage::git::Repository; - -pub fn all(repository: &Repository) -> Result> { - let patches = Issues::open(repository)? - .all() - .map(|iter| iter.flatten().collect::>())?; - - Ok(patches - .into_iter() - .map(|(id, issue)| (id, issue)) - .collect::>()) -} - -pub fn find(repository: &Repository, id: &IssueId) -> Result> { - let issues = Issues::open(repository)?; - Ok(issues.get(id)?) -} diff --git a/radicle-tui/src/cob/patch.rs b/radicle-tui/src/cob/patch.rs deleted file mode 100644 index dc73955e..00000000 --- a/radicle-tui/src/cob/patch.rs +++ /dev/null @@ -1,20 +0,0 @@ -use anyhow::Result; - -use radicle::cob::patch::{Patch, PatchId, Patches}; -use radicle::storage::git::Repository; - -pub fn all(repository: &Repository) -> Result> { - let patches = Patches::open(repository)? - .all() - .map(|iter| iter.flatten().collect::>())?; - - Ok(patches - .into_iter() - .map(|(id, patch)| (id, patch)) - .collect::>()) -} - -pub fn find(repository: &Repository, id: &PatchId) -> Result> { - let patches = Patches::open(repository)?; - Ok(patches.get(id)?) -} diff --git a/radicle-tui/src/lib.rs b/radicle-tui/src/lib.rs deleted file mode 100644 index adf335d0..00000000 --- a/radicle-tui/src/lib.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::hash::Hash; -use std::time::Duration; - -use anyhow::Result; - -use tuirealm::terminal::TerminalBridge; -use tuirealm::tui::layout::Rect; -use tuirealm::Frame; -use tuirealm::{Application, EventListenerCfg, NoUserEvent}; - -pub mod cob; -pub mod ui; - -/// Trait that must be implemented by client applications in order to be run -/// as tui-application using tui-realm. Implementors act as models to the -/// tui-realm application that can be polled for new messages, updated -/// accordingly and rendered with new state. -/// -/// Please see `examples/` for further information on how to use it. -pub trait Tui -where - Id: Eq + PartialEq + Clone + Hash, - Message: Eq, -{ - /// Should initialize an application by mounting and activating components. - fn init(&mut self, app: &mut Application) -> Result<()>; - - /// Should update the current state by handling a message from the view. Returns true - /// if view should be updated (e.g. a message was received and the current state changed). - fn update(&mut self, app: &mut Application) -> Result; - - /// Should draw the application to a frame. - fn view(&mut self, app: &mut Application, frame: &mut Frame); - - /// Should return true if the application is requested to quit. - fn quit(&self) -> bool; -} - -/// A tui-window using the cross-platform Terminal helper provided -/// by tui-realm. -pub struct Window { - /// Helper around `Terminal` to quickly setup and perform on terminal. - pub terminal: TerminalBridge, -} - -impl Default for Window { - fn default() -> Self { - Self::new() - } -} - -/// Provides a way to create and run a new tui-application. -impl Window { - /// Creates a tui-window using the default cross-platform Terminal - /// helper and panics if its creation fails. - pub fn new() -> Self { - Self { - terminal: TerminalBridge::new().expect("Cannot create terminal bridge"), - } - } - - /// Runs this tui-window with the tui-application given and performs the - /// following steps: - /// 1. Enter alternative terminal screen - /// 2. Run main loop until application should quit and with each iteration - /// - poll new events (tick or user event) - /// - update application state - /// - redraw view - /// 3. Leave alternative terminal screen - pub fn run(&mut self, tui: &mut T, interval: u64) -> Result<()> - where - T: Tui, - Id: Eq + PartialEq + Clone + Hash, - Message: Eq, - { - let mut update = true; - let mut resize = false; - let mut size = Rect::default(); - let mut app = Application::init( - EventListenerCfg::default().default_input_listener(Duration::from_millis(interval)), - ); - tui.init(&mut app)?; - - while !tui.quit() { - if update || resize { - self.terminal - .raw_mut() - .draw(|frame| tui.view(&mut app, frame))?; - } - update = tui.update(&mut app)?; - - resize = size != self.terminal.raw().size()?; - size = self.terminal.raw().size()?; - } - - Ok(()) - } -} diff --git a/radicle-tui/src/main.rs b/radicle-tui/src/main.rs deleted file mode 100644 index 7980f5af..00000000 --- a/radicle-tui/src/main.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::process; - -use anyhow::{anyhow, Context}; - -use radicle::storage::ReadStorage; - -use radicle_cli as cli; -use radicle_term as term; -use radicle_tui::Window; - -mod app; - -pub const NAME: &str = "radicle-tui"; -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -pub const GIT_HEAD: &str = env!("GIT_HEAD"); -pub const FPS: u64 = 60; - -pub const HELP: &str = r#" -Usage - - radicle-tui [