Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
tui: Split application into separate modules
Erik Kundt committed 3 years ago
commit a2da673c3132801795a0adf3af7ab01575a0ad21
parent 969219c51d2462d2d37b449ef728693525e86e02
6 files changed +348 -336
modified radicle-tui/src/app.rs
@@ -1,31 +1,22 @@
+
pub mod event;
+
pub mod page;
+
pub mod subscription;
+

use anyhow::Result;

-
use tui_realm_stdlib::Phantom;
+
use radicle::cob::patch::{Patch, PatchId};
+
use radicle::identity::{Id, Project};
+
use radicle::profile::Profile;
+

use tuirealm::application::PollStrategy;
-
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
-
use tuirealm::event::{Event, Key, KeyEvent};
-
use tuirealm::{Application, Frame, MockComponent, NoUserEvent, State, StateValue};
+
use tuirealm::{Application, Frame, NoUserEvent};

use radicle_tui::cob::patch::{self};

-
use radicle_tui::subs;
use radicle_tui::ui;
-
use radicle_tui::ui::components::container::{GlobalListener, LabeledContainer, Tabs};
-
use radicle_tui::ui::components::context::{ContextBar, Shortcuts};
-
use radicle_tui::ui::components::list::PropertyList;
-
use radicle_tui::ui::components::workspace::{
-
    Browser, Dashboard, IssueBrowser, PatchActivity, PatchFiles,
-
};
-
use radicle_tui::ui::layout;
use radicle_tui::ui::theme::{self, Theme};
-
use radicle_tui::ui::widget::{self, Widget};
-

use radicle_tui::Tui;

-
use radicle::cob::patch::{Patch, PatchId};
-
use radicle::identity::{Id, Project};
-
use radicle::profile::Profile;
-

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum HomeCid {
    Navigation,
@@ -81,7 +72,7 @@ pub struct Context {
#[allow(dead_code)]
pub struct App {
    context: Context,
-
    active_page: Box<dyn ViewPage>,
+
    active_page: Box<dyn page::ViewPage>,
    theme: Theme,
    quit: bool,
}
@@ -100,7 +91,7 @@ impl App {
                patches,
            },
            theme: theme::default_dark(),
-
            active_page: Box::<Home>::default(),
+
            active_page: Box::<page::Home>::default(),
            quit: false,
        }
    }
@@ -110,7 +101,7 @@ impl App {
        app: &mut Application<Cid, Message, NoUserEvent>,
        theme: &Theme,
    ) -> Result<()> {
-
        self.active_page = Box::<Home>::default();
+
        self.active_page = Box::<page::Home>::default();
        self.active_page.mount(app, &self.context, theme)?;
        self.active_page.activate(app)?;

@@ -122,7 +113,7 @@ impl App {
        app: &mut Application<Cid, Message, NoUserEvent>,
        theme: &Theme,
    ) -> Result<()> {
-
        self.active_page = Box::<PatchView>::default();
+
        self.active_page = Box::<page::PatchView>::default();
        self.active_page.mount(app, &self.context, theme)?;
        self.active_page.activate(app)?;

@@ -136,7 +127,7 @@ impl Tui<Cid, Message> for App {

        // Add global key listener and subscribe to key events
        let global = ui::widget::common::global_listener().to_boxed();
-
        app.mount(Cid::GlobalListener, global, subs::global())?;
+
        app.mount(Cid::GlobalListener, global, subscription::global())?;

        Ok(())
    }
@@ -178,280 +169,3 @@ impl Tui<Cid, Message> for App {
        self.quit
    }
}
-

-
/// `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 {
-
    fn mount(
-
        &self,
-
        app: &mut Application<Cid, Message, NoUserEvent>,
-
        context: &Context,
-
        theme: &Theme,
-
    ) -> Result<()>;
-

-
    fn update(&mut self, message: Message);
-

-
    fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame);
-

-
    fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()>;
-
}
-

-
///
-
/// Home
-
///
-
pub struct Home {
-
    active_component: Cid,
-
}
-

-
impl Default for Home {
-
    fn default() -> Self {
-
        Home {
-
            active_component: Cid::Home(HomeCid::Dashboard),
-
        }
-
    }
-
}
-

-
impl ViewPage for Home {
-
    fn mount(
-
        &self,
-
        app: &mut Application<Cid, Message, NoUserEvent>,
-
        context: &Context,
-
        theme: &Theme,
-
    ) -> Result<()> {
-
        let navigation = widget::home::navigation(theme).to_boxed();
-

-
        let dashboard = widget::home::dashboard(theme, &context.id, &context.project).to_boxed();
-
        let issue_browser = widget::home::issues(theme).to_boxed();
-
        let patch_browser =
-
            widget::home::patches(theme, &context.patches, &context.profile).to_boxed();
-

-
        app.remount(
-
            Cid::Home(HomeCid::Navigation),
-
            navigation,
-
            subs::navigation(),
-
        )?;
-

-
        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![])?;
-

-
        Ok(())
-
    }
-

-
    fn update(&mut self, message: Message) {
-
        if let Message::NavigationChanged(index) = message {
-
            self.active_component = match index {
-
                0 => Cid::Home(HomeCid::Dashboard),
-
                1 => Cid::Home(HomeCid::IssueBrowser),
-
                2 => Cid::Home(HomeCid::PatchBrowser),
-
                _ => Cid::Home(HomeCid::Dashboard),
-
            };
-
        }
-
    }
-

-
    fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
-
        let area = frame.size();
-
        let navigation_h = 2u16;
-
        let layout = layout::default_page(area, navigation_h);
-

-
        app.view(&Cid::Home(HomeCid::Navigation), frame, layout[0]);
-
        app.view(&self.active_component, frame, layout[1]);
-
    }
-

-
    fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()> {
-
        app.active(&self.active_component)?;
-
        Ok(())
-
    }
-
}
-

-
///
-
/// Patch detail page
-
///
-
pub struct PatchView {
-
    active_component: Cid,
-
}
-

-
impl Default for PatchView {
-
    fn default() -> Self {
-
        PatchView {
-
            active_component: Cid::Patch(PatchCid::Activity),
-
        }
-
    }
-
}
-

-
impl ViewPage for PatchView {
-
    fn mount(
-
        &self,
-
        app: &mut Application<Cid, Message, NoUserEvent>,
-
        context: &Context,
-
        theme: &Theme,
-
    ) -> Result<()> {
-
        if let Some((id, patch)) = context.patches.get(context.selected_patch) {
-
            let navigation = widget::patch::navigation(theme).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::Navigation),
-
                navigation,
-
                subs::navigation(),
-
            )?;
-
            app.remount(Cid::Patch(PatchCid::Activity), activity, vec![])?;
-
            app.remount(Cid::Patch(PatchCid::Files), files, vec![])?;
-
        }
-
        Ok(())
-
    }
-

-
    fn update(&mut self, message: Message) {
-
        if let Message::NavigationChanged(index) = message {
-
            self.active_component = match index {
-
                0 => Cid::Patch(PatchCid::Activity),
-
                1 => Cid::Patch(PatchCid::Files),
-
                _ => Cid::Patch(PatchCid::Activity),
-
            };
-
        }
-
    }
-

-
    fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
-
        let area = frame.size();
-
        let navigation_h = 2u16;
-

-
        let layout = layout::default_page(area, navigation_h);
-

-
        app.view(&Cid::Patch(PatchCid::Navigation), frame, layout[0]);
-
        app.view(&self.active_component, frame, layout[1]);
-
    }
-

-
    fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()> {
-
        app.active(&self.active_component)?;
-
        Ok(())
-
    }
-
}
-

-
/// 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.
-
impl tuirealm::Component<Message, NoUserEvent> for Widget<GlobalListener> {
-
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
-
        match event {
-
            Event::WindowResize(_, _) => Some(Message::Tick),
-
            Event::Keyboard(KeyEvent {
-
                code: Key::Char('q'),
-
                ..
-
            }) => Some(Message::Quit),
-
            _ => None,
-
        }
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<Tabs> {
-
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
-
        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<Message, NoUserEvent> for Widget<Browser<(PatchId, Patch)>> {
-
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
-
        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, ..
-
            }) => match self.perform(Cmd::Submit) {
-
                CmdResult::Submit(State::One(StateValue::Usize(index))) => {
-
                    Some(Message::Patch(PatchMessage::Show(index)))
-
                }
-
                _ => None,
-
            },
-
            _ => None,
-
        }
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<Dashboard> {
-
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
-
        None
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<IssueBrowser> {
-
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
-
        None
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<PatchActivity> {
-
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
-
        match event {
-
            Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
-
                Some(Message::Patch(PatchMessage::Leave))
-
            }
-
            _ => None,
-
        }
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<PatchFiles> {
-
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
-
        match event {
-
            Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
-
                Some(Message::Patch(PatchMessage::Leave))
-
            }
-
            _ => None,
-
        }
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<LabeledContainer> {
-
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
-
        None
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<PropertyList> {
-
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
-
        None
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<ContextBar> {
-
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
-
        None
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Widget<Shortcuts> {
-
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
-
        None
-
    }
-
}
-

-
impl tuirealm::Component<Message, NoUserEvent> for Phantom {
-
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
-
        None
-
    }
-
}
added radicle-tui/src/app/event.rs
@@ -0,0 +1,139 @@
+
use radicle::cob::patch::{Patch, PatchId};
+

+
use tui_realm_stdlib::Phantom;
+
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
+
use tuirealm::event::{Event, Key, KeyEvent};
+
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
+

+
use radicle_tui::ui::components::container::{GlobalListener, LabeledContainer, Tabs};
+
use radicle_tui::ui::components::context::{ContextBar, Shortcuts};
+
use radicle_tui::ui::components::list::PropertyList;
+
use radicle_tui::ui::components::workspace::{
+
    Browser, Dashboard, IssueBrowser, PatchActivity, PatchFiles,
+
};
+

+
use radicle_tui::ui::widget::Widget;
+

+
use super::{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.
+
impl tuirealm::Component<Message, NoUserEvent> for Widget<GlobalListener> {
+
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
+
        match event {
+
            Event::WindowResize(_, _) => Some(Message::Tick),
+
            Event::Keyboard(KeyEvent {
+
                code: Key::Char('q'),
+
                ..
+
            }) => Some(Message::Quit),
+
            _ => None,
+
        }
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<Tabs> {
+
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
+
        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<Message, NoUserEvent> for Widget<Browser<(PatchId, Patch)>> {
+
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
+
        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, ..
+
            }) => match self.perform(Cmd::Submit) {
+
                CmdResult::Submit(State::One(StateValue::Usize(index))) => {
+
                    Some(Message::Patch(PatchMessage::Show(index)))
+
                }
+
                _ => None,
+
            },
+
            _ => None,
+
        }
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<Dashboard> {
+
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
+
        None
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<IssueBrowser> {
+
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
+
        None
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<PatchActivity> {
+
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
+
        match event {
+
            Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
+
                Some(Message::Patch(PatchMessage::Leave))
+
            }
+
            _ => None,
+
        }
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<PatchFiles> {
+
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
+
        match event {
+
            Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
+
                Some(Message::Patch(PatchMessage::Leave))
+
            }
+
            _ => None,
+
        }
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<LabeledContainer> {
+
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
+
        None
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<PropertyList> {
+
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
+
        None
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<ContextBar> {
+
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
+
        None
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Widget<Shortcuts> {
+
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
+
        None
+
    }
+
}
+

+
impl tuirealm::Component<Message, NoUserEvent> for Phantom {
+
    fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
+
        None
+
    }
+
}
added radicle-tui/src/app/page.rs
@@ -0,0 +1,160 @@
+
use anyhow::Result;
+
use radicle_tui::ui::{layout, theme::Theme, widget};
+
use tuirealm::{Frame, NoUserEvent};
+

+
use super::{subscription, Application, Cid, Context, HomeCid, 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 {
+
    fn mount(
+
        &self,
+
        app: &mut Application<Cid, Message, NoUserEvent>,
+
        context: &Context,
+
        theme: &Theme,
+
    ) -> Result<()>;
+

+
    fn update(&mut self, message: Message);
+

+
    fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame);
+

+
    fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()>;
+
}
+

+
///
+
/// Home
+
///
+
pub struct Home {
+
    active_component: Cid,
+
}
+

+
impl Default for Home {
+
    fn default() -> Self {
+
        Home {
+
            active_component: Cid::Home(HomeCid::Dashboard),
+
        }
+
    }
+
}
+

+
impl ViewPage for Home {
+
    fn mount(
+
        &self,
+
        app: &mut Application<Cid, Message, NoUserEvent>,
+
        context: &Context,
+
        theme: &Theme,
+
    ) -> Result<()> {
+
        let navigation = widget::home::navigation(theme).to_boxed();
+

+
        let dashboard = widget::home::dashboard(theme, &context.id, &context.project).to_boxed();
+
        let issue_browser = widget::home::issues(theme).to_boxed();
+
        let patch_browser =
+
            widget::home::patches(theme, &context.patches, &context.profile).to_boxed();
+

+
        app.remount(
+
            Cid::Home(HomeCid::Navigation),
+
            navigation,
+
            subscription::navigation(),
+
        )?;
+

+
        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![])?;
+

+
        Ok(())
+
    }
+

+
    fn update(&mut self, message: Message) {
+
        if let Message::NavigationChanged(index) = message {
+
            self.active_component = match index {
+
                0 => Cid::Home(HomeCid::Dashboard),
+
                1 => Cid::Home(HomeCid::IssueBrowser),
+
                2 => Cid::Home(HomeCid::PatchBrowser),
+
                _ => Cid::Home(HomeCid::Dashboard),
+
            };
+
        }
+
    }
+

+
    fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
+
        let area = frame.size();
+
        let navigation_h = 2u16;
+
        let layout = layout::default_page(area, navigation_h);
+

+
        app.view(&Cid::Home(HomeCid::Navigation), frame, layout[0]);
+
        app.view(&self.active_component, frame, layout[1]);
+
    }
+

+
    fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()> {
+
        app.active(&self.active_component)?;
+
        Ok(())
+
    }
+
}
+

+
///
+
/// Patch detail page
+
///
+
pub struct PatchView {
+
    active_component: Cid,
+
}
+

+
impl Default for PatchView {
+
    fn default() -> Self {
+
        PatchView {
+
            active_component: Cid::Patch(PatchCid::Activity),
+
        }
+
    }
+
}
+

+
impl ViewPage for PatchView {
+
    fn mount(
+
        &self,
+
        app: &mut Application<Cid, Message, NoUserEvent>,
+
        context: &Context,
+
        theme: &Theme,
+
    ) -> Result<()> {
+
        if let Some((id, patch)) = context.patches.get(context.selected_patch) {
+
            let navigation = widget::patch::navigation(theme).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::Navigation),
+
                navigation,
+
                subscription::navigation(),
+
            )?;
+
            app.remount(Cid::Patch(PatchCid::Activity), activity, vec![])?;
+
            app.remount(Cid::Patch(PatchCid::Files), files, vec![])?;
+
        }
+
        Ok(())
+
    }
+

+
    fn update(&mut self, message: Message) {
+
        if let Message::NavigationChanged(index) = message {
+
            self.active_component = match index {
+
                0 => Cid::Patch(PatchCid::Activity),
+
                1 => Cid::Patch(PatchCid::Files),
+
                _ => Cid::Patch(PatchCid::Activity),
+
            };
+
        }
+
    }
+

+
    fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
+
        let area = frame.size();
+
        let navigation_h = 2u16;
+

+
        let layout = layout::default_page(area, navigation_h);
+

+
        app.view(&Cid::Patch(PatchCid::Navigation), frame, layout[0]);
+
        app.view(&self.active_component, frame, layout[1]);
+
    }
+

+
    fn activate(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()> {
+
        app.active(&self.active_component)?;
+
        Ok(())
+
    }
+
}
added radicle-tui/src/app/subscription.rs
@@ -0,0 +1,35 @@
+
use std::hash::Hash;
+

+
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
+
use tuirealm::{Sub, SubClause, SubEventClause};
+

+
pub fn navigation<Id, UserEvent>() -> Vec<Sub<Id, UserEvent>>
+
where
+
    Id: Clone + Hash + Eq + PartialEq,
+
    UserEvent: Clone + Eq + PartialEq + PartialOrd,
+
{
+
    vec![Sub::new(
+
        SubEventClause::Keyboard(KeyEvent {
+
            code: Key::Tab,
+
            modifiers: KeyModifiers::NONE,
+
        }),
+
        SubClause::Always,
+
    )]
+
}
+

+
pub fn global<Id, UserEvent>() -> Vec<Sub<Id, UserEvent>>
+
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),
+
    ]
+
}
modified radicle-tui/src/lib.rs
@@ -8,7 +8,6 @@ use tuirealm::Frame;
use tuirealm::{Application, EventListenerCfg, NoUserEvent};

pub mod cob;
-
pub mod subs;
pub mod ui;

/// Trait that must be implemented by client applications in order to be run
deleted radicle-tui/src/subs.rs
@@ -1,35 +0,0 @@
-
use std::hash::Hash;
-

-
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
-
use tuirealm::{Sub, SubClause, SubEventClause};
-

-
pub fn navigation<Id, UserEvent>() -> Vec<Sub<Id, UserEvent>>
-
where
-
    Id: Clone + Hash + Eq + PartialEq,
-
    UserEvent: Clone + Eq + PartialEq + PartialOrd,
-
{
-
    vec![Sub::new(
-
        SubEventClause::Keyboard(KeyEvent {
-
            code: Key::Tab,
-
            modifiers: KeyModifiers::NONE,
-
        }),
-
        SubClause::Always,
-
    )]
-
}
-

-
pub fn global<Id, UserEvent>() -> Vec<Sub<Id, UserEvent>>
-
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),
-
    ]
-
}