Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
tui: Add issue page with placeholder
Erik Kundt committed 2 years ago
commit 33d4ec0f1a2ec0efc531c889d77dce189534f7bf
parent 53db584b58eafab15cf5971ce4d7a411ac1b2b03
6 files changed +176 -5
modified radicle-tui/src/app.rs
@@ -4,6 +4,7 @@ pub mod subscription;

use anyhow::Result;

+
use radicle::cob::issue::{IssueId, Issues};
use radicle::cob::patch::{PatchId, Patches};
use radicle::identity::{Id, Project};
use radicle::profile::Profile;
@@ -18,7 +19,7 @@ use radicle_tui::Tui;

use page::{HomeView, PatchView};

-
use self::page::PageStack;
+
use self::page::{IssuePage, PageStack};

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum HomeCid {
@@ -35,10 +36,16 @@ pub enum PatchCid {
    Files,
}

+
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
+
pub enum IssueCid {
+
    List,
+
}
+

/// All component ids known to this application.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Cid {
    Home(HomeCid),
+
    Issue(IssueCid),
    Patch(PatchCid),
    GlobalListener,
}
@@ -48,6 +55,12 @@ pub enum Cid {
pub enum HomeMessage {}

#[derive(Debug, Eq, PartialEq)]
+
pub enum IssueMessage {
+
    Show(IssueId),
+
    Leave,
+
}
+

+
#[derive(Debug, Eq, PartialEq)]
pub enum PatchMessage {
    Show(PatchId),
    Leave,
@@ -56,6 +69,7 @@ pub enum PatchMessage {
#[derive(Debug, Eq, PartialEq)]
pub enum Message {
    Home(HomeMessage),
+
    Issue(IssueMessage),
    Patch(PatchMessage),
    NavigationChanged(u16),
    Tick,
@@ -128,6 +142,32 @@ impl App {
            ))
        }
    }
+

+
    fn view_issue(
+
        &mut self,
+
        app: &mut Application<Cid, Message, NoUserEvent>,
+
        id: IssueId,
+
        theme: &Theme,
+
    ) -> Result<()> {
+
        let repo = self
+
            .context
+
            .profile
+
            .storage
+
            .repository(self.context.id)
+
            .unwrap();
+
        let issues = Issues::open(&repo).unwrap();
+

+
        if let Some(issue) = issues.get(&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<Cid, Message> for App {
@@ -153,6 +193,9 @@ impl Tui<Cid, Message> for App {
                let theme = theme::default_dark();
                for message in messages {
                    match message {
+
                        Message::Issue(IssueMessage::Show(id)) => {
+
                            self.view_issue(app, id, &theme)?;
+
                        }
                        Message::Patch(PatchMessage::Show(id)) => {
                            self.view_patch(app, id, &theme)?;
                        }
modified radicle-tui/src/app/event.rs
@@ -10,7 +10,7 @@ use radicle_tui::ui::widget::patch;

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

-
use super::{Message, PatchMessage};
+
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
@@ -83,7 +83,9 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<IssueBrowser> {
            }
            Event::Keyboard(KeyEvent {
                code: Key::Enter, ..
-
            }) => Some(Message::Tick),
+
            }) => self
+
                .selected_item()
+
                .map(|item| Message::Issue(IssueMessage::Show(item.id()))),
            _ => None,
        }
    }
modified radicle-tui/src/app/page.rs
@@ -1,13 +1,16 @@
use anyhow::Result;

-
use radicle::cob::patch::{Patch, PatchId};
+
use radicle::cob::{
+
    issue::{Issue, IssueId},
+
    patch::{Patch, PatchId},
+
};
use tuirealm::{Frame, NoUserEvent};

use radicle_tui::ui::layout;
use radicle_tui::ui::theme::Theme;
use radicle_tui::ui::widget;

-
use super::{subscription, Application, Cid, Context, HomeCid, Message, PatchCid};
+
use super::{subscription, Application, Cid, Context, HomeCid, IssueCid, 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
@@ -114,6 +117,63 @@ impl ViewPage for HomeView {
}

///
+
/// Issue detail page
+
///
+
pub struct IssuePage {
+
    active_component: Cid,
+
    issue: (IssueId, Issue),
+
}
+

+
impl IssuePage {
+
    pub fn new(issue: (IssueId, Issue)) -> Self {
+
        IssuePage {
+
            active_component: Cid::Issue(IssueCid::List),
+
            issue,
+
        }
+
    }
+
}
+

+
impl ViewPage for IssuePage {
+
    fn mount(
+
        &self,
+
        app: &mut Application<Cid, Message, NoUserEvent>,
+
        context: &Context,
+
        theme: &Theme,
+
    ) -> Result<()> {
+
        let (id, issue) = &self.issue;
+
        let list = widget::issue::list(theme, (*id, issue), &context.profile).to_boxed();
+

+
        app.remount(Cid::Issue(IssueCid::List), list, vec![])?;
+
        app.active(&self.active_component)?;
+

+
        Ok(())
+
    }
+

+
    fn unmount(&self, app: &mut Application<Cid, Message, NoUserEvent>) -> Result<()> {
+
        app.umount(&Cid::Issue(IssueCid::List))?;
+
        Ok(())
+
    }
+

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

+
        Ok(())
+
    }
+

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

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

+
///
/// Patch detail page
///
pub struct PatchView {
modified radicle-tui/src/ui/cob.rs
@@ -139,6 +139,12 @@ pub struct IssueItem {
    timestamp: Timestamp,
}

+
impl IssueItem {
+
    pub fn id(&self) -> IssueId {
+
        self.id
+
    }
+
}
+

impl TryFrom<(&Profile, &Repository, IssueId, Issue)> for IssueItem {
    type Error = anyhow::Error;

modified radicle-tui/src/ui/widget.rs
@@ -1,5 +1,6 @@
pub mod common;
pub mod home;
+
pub mod issue;
pub mod patch;

use std::ops::Deref;
added radicle-tui/src/ui/widget/issue.rs
@@ -0,0 +1,59 @@
+
use radicle_cli::terminal::format;
+

+
use radicle::cob::issue::Issue;
+
use radicle::cob::issue::IssueId;
+
use radicle::Profile;
+
use tuirealm::props::Color;
+

+
use super::common;
+
use super::Widget;
+

+
use crate::ui::cob;
+
use crate::ui::theme::Theme;
+
use crate::ui::widget::common::context::ContextBar;
+
use crate::ui::widget::patch::Activity;
+

+
pub fn list(theme: &Theme, issue: (IssueId, &Issue), profile: &Profile) -> Widget<Activity> {
+
    let (id, issue) = issue;
+
    let shortcuts = common::shortcuts(
+
        theme,
+
        vec![
+
            common::shortcut(theme, "esc", "back"),
+
            common::shortcut(theme, "q", "quit"),
+
        ],
+
    );
+
    let context = context(theme, (id, issue), profile);
+

+
    let not_implemented = common::label("not implemented").foreground(theme.colors.default_fg);
+
    let activity = Activity::new(not_implemented, context, shortcuts);
+

+
    Widget::new(activity)
+
}
+

+
pub fn context(theme: &Theme, issue: (IssueId, &Issue), profile: &Profile) -> Widget<ContextBar> {
+
    let (id, issue) = issue;
+
    let is_you = *issue.author().id() == profile.did();
+

+
    let id = format::cob(&id);
+
    let title = issue.title();
+
    let author = cob::format_author(issue.author().id(), is_you);
+
    let comments = issue.comments().count();
+

+
    let context = common::label(" issue ").background(theme.colors.context_badge_bg);
+
    let id = common::label(&format!(" {id} "))
+
        .foreground(theme.colors.context_id_fg)
+
        .background(theme.colors.context_id_bg);
+
    let title = common::label(&format!(" {title} "))
+
        .foreground(theme.colors.default_fg)
+
        .background(theme.colors.context_bg);
+
    let author = common::label(&format!(" {author} "))
+
        .foreground(theme.colors.context_id_author_fg)
+
        .background(theme.colors.context_bg);
+
    let comments = common::label(&format!(" {comments} "))
+
        .foreground(Color::Rgb(70, 70, 70))
+
        .background(theme.colors.context_light_bg);
+

+
    let context_bar = ContextBar::new(context, id, author, title, comments);
+

+
    Widget::new(context_bar).height(1)
+
}