tui: Use table model in patch browser
This commit is contained in:
parent
0a673bc552
commit
0361ca3818
|
|
@ -4,14 +4,14 @@ pub mod subscription;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use radicle::cob::patch::{PatchId, Patches};
|
||||||
use radicle::identity::{Id, Project};
|
use radicle::identity::{Id, Project};
|
||||||
use radicle::profile::Profile;
|
use radicle::profile::Profile;
|
||||||
|
use radicle::storage::ReadStorage;
|
||||||
|
|
||||||
use tuirealm::application::PollStrategy;
|
use tuirealm::application::PollStrategy;
|
||||||
use tuirealm::{Application, Frame, NoUserEvent};
|
use tuirealm::{Application, Frame, NoUserEvent};
|
||||||
|
|
||||||
use radicle_tui::cob::patch::{self};
|
|
||||||
|
|
||||||
use radicle_tui::ui;
|
use radicle_tui::ui;
|
||||||
use radicle_tui::ui::theme::{self, Theme};
|
use radicle_tui::ui::theme::{self, Theme};
|
||||||
use radicle_tui::Tui;
|
use radicle_tui::Tui;
|
||||||
|
|
@ -45,13 +45,11 @@ pub enum Cid {
|
||||||
|
|
||||||
/// Messages handled by this application.
|
/// Messages handled by this application.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum HomeMessage {
|
pub enum HomeMessage {}
|
||||||
PatchChanged(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum PatchMessage {
|
pub enum PatchMessage {
|
||||||
Show,
|
Show(PatchId),
|
||||||
Leave,
|
Leave,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,8 +97,7 @@ impl App {
|
||||||
app: &mut Application<Cid, Message, NoUserEvent>,
|
app: &mut Application<Cid, Message, NoUserEvent>,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let patches = patch::load_all(&self.context.profile, self.context.id);
|
let home = Box::<HomeView>::default();
|
||||||
let home = Box::new(HomeView::new(patches));
|
|
||||||
self.pages.push(home, app, &self.context, theme)?;
|
self.pages.push(home, app, &self.context, theme)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -109,29 +106,26 @@ impl App {
|
||||||
fn view_patch(
|
fn view_patch(
|
||||||
&mut self,
|
&mut self,
|
||||||
app: &mut Application<Cid, Message, NoUserEvent>,
|
app: &mut Application<Cid, Message, NoUserEvent>,
|
||||||
|
id: PatchId,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let page = self.pages.peek_mut()?;
|
let repo = self
|
||||||
let state = page.state().unwrap_map();
|
.context
|
||||||
let patches = state
|
.profile
|
||||||
.and_then(|mut values| values.remove("patches"))
|
.storage
|
||||||
.and_then(|value| value.unwrap_patches());
|
.repository(self.context.id)
|
||||||
|
.unwrap();
|
||||||
|
let patches = Patches::open(&repo).unwrap();
|
||||||
|
|
||||||
match patches {
|
if let Some(patch) = patches.get(&id)? {
|
||||||
Some((patches, selection)) => match patches.get(selection) {
|
let view = Box::new(PatchView::new((id, patch)));
|
||||||
Some((id, patch)) => {
|
self.pages.push(view, app, &self.context, theme)?;
|
||||||
let view = Box::new(PatchView::new((*id, patch.clone())));
|
|
||||||
self.pages.push(view, app, &self.context, theme)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
} else {
|
||||||
None => Err(anyhow::anyhow!(
|
Err(anyhow::anyhow!(
|
||||||
"Could not mount 'page::PatchView'. Patch not found."
|
"Could not mount 'page::PatchView'. Patch not found."
|
||||||
)),
|
))
|
||||||
},
|
|
||||||
None => Err(anyhow::anyhow!(
|
|
||||||
"Could not mount 'page::PatchView'. No state value for 'patches' found."
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,8 +153,8 @@ impl Tui<Cid, Message> for App {
|
||||||
let theme = theme::default_dark();
|
let theme = theme::default_dark();
|
||||||
for message in messages {
|
for message in messages {
|
||||||
match message {
|
match message {
|
||||||
Message::Patch(PatchMessage::Show) => {
|
Message::Patch(PatchMessage::Show(id)) => {
|
||||||
self.view_patch(app, &theme)?;
|
self.view_patch(app, id, &theme)?;
|
||||||
}
|
}
|
||||||
Message::Patch(PatchMessage::Leave) => {
|
Message::Patch(PatchMessage::Leave) => {
|
||||||
self.pages.pop(app)?;
|
self.pages.pop(app)?;
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,16 @@
|
||||||
use radicle::cob::patch::{Patch, PatchId};
|
|
||||||
|
|
||||||
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
|
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
|
||||||
use tuirealm::event::{Event, Key, KeyEvent};
|
use tuirealm::event::{Event, Key, KeyEvent};
|
||||||
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
|
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
|
||||||
|
|
||||||
use radicle_tui::ui::widget::common::container::{GlobalListener, LabeledContainer, Tabs};
|
use radicle_tui::ui::widget::common::container::{GlobalListener, LabeledContainer, Tabs};
|
||||||
use radicle_tui::ui::widget::common::context::{ContextBar, Shortcuts};
|
use radicle_tui::ui::widget::common::context::{ContextBar, Shortcuts};
|
||||||
|
|
||||||
use radicle_tui::ui::widget::common::list::PropertyList;
|
use radicle_tui::ui::widget::common::list::PropertyList;
|
||||||
|
use radicle_tui::ui::widget::home::{Dashboard, IssueBrowser, PatchBrowser};
|
||||||
use radicle_tui::ui::widget::common::Browser;
|
|
||||||
use radicle_tui::ui::widget::home::{Dashboard, IssueBrowser};
|
|
||||||
use radicle_tui::ui::widget::patch;
|
use radicle_tui::ui::widget::patch;
|
||||||
|
|
||||||
use radicle_tui::ui::widget::Widget;
|
use radicle_tui::ui::widget::Widget;
|
||||||
|
|
||||||
use super::{HomeMessage, Message, PatchMessage};
|
use super::{Message, PatchMessage};
|
||||||
|
|
||||||
/// Since the framework does not know the type of messages that are being
|
/// 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
|
/// passed around in the app, the following handlers need to be implemented for
|
||||||
|
|
@ -50,28 +45,24 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<Tabs> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl tuirealm::Component<Message, NoUserEvent> for Widget<Browser<(PatchId, Patch)>> {
|
impl tuirealm::Component<Message, NoUserEvent> for Widget<PatchBrowser> {
|
||||||
fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
|
fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
|
||||||
match event {
|
match event {
|
||||||
Event::Keyboard(KeyEvent { code: Key::Up, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Up, .. }) => {
|
||||||
match self.perform(Cmd::Move(MoveDirection::Up)) {
|
self.perform(Cmd::Move(MoveDirection::Up));
|
||||||
CmdResult::Changed(State::One(StateValue::Usize(index))) => {
|
Some(Message::Tick)
|
||||||
Some(Message::Home(HomeMessage::PatchChanged(index)))
|
|
||||||
}
|
|
||||||
_ => Some(Message::Tick),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Down, ..
|
code: Key::Down, ..
|
||||||
}) => match self.perform(Cmd::Move(MoveDirection::Down)) {
|
}) => {
|
||||||
CmdResult::Changed(State::One(StateValue::Usize(index))) => {
|
self.perform(Cmd::Move(MoveDirection::Down));
|
||||||
Some(Message::Home(HomeMessage::PatchChanged(index)))
|
Some(Message::Tick)
|
||||||
}
|
}
|
||||||
_ => Some(Message::Tick),
|
|
||||||
},
|
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Enter, ..
|
code: Key::Enter, ..
|
||||||
}) => Some(Message::Patch(PatchMessage::Show)),
|
}) => self
|
||||||
|
.selected_item()
|
||||||
|
.map(|item| Message::Patch(PatchMessage::Show(item.id()))),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,63 +3,20 @@ pub mod context;
|
||||||
pub mod label;
|
pub mod label;
|
||||||
pub mod list;
|
pub mod list;
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use tuirealm::props::{AttrValue, Attribute};
|
||||||
|
use tuirealm::MockComponent;
|
||||||
use radicle::Profile;
|
|
||||||
|
|
||||||
use tuirealm::command::{Cmd, CmdResult};
|
|
||||||
use tuirealm::props::{AttrValue, Attribute, PropPayload, PropValue, TextSpan};
|
|
||||||
use tuirealm::tui::layout::Rect;
|
|
||||||
use tuirealm::{Frame, MockComponent, Props, State};
|
|
||||||
|
|
||||||
use container::{GlobalListener, Header, LabeledContainer, Tabs};
|
use container::{GlobalListener, Header, LabeledContainer, Tabs};
|
||||||
use context::{Shortcut, Shortcuts};
|
use context::{Shortcut, Shortcuts};
|
||||||
use label::Label;
|
use label::Label;
|
||||||
use list::{List, Property, PropertyList, Table};
|
use list::{Property, PropertyList};
|
||||||
|
|
||||||
use super::{Widget, WidgetComponent};
|
use self::list::{ColumnWidth, TableModel};
|
||||||
|
|
||||||
|
use super::Widget;
|
||||||
|
|
||||||
use crate::ui::layout;
|
|
||||||
use crate::ui::theme::Theme;
|
use crate::ui::theme::Theme;
|
||||||
|
|
||||||
pub struct Browser<T> {
|
|
||||||
list: Widget<Table>,
|
|
||||||
shortcuts: Widget<Shortcuts>,
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: List> Browser<T> {
|
|
||||||
pub fn new(list: Widget<Table>, shortcuts: Widget<Shortcuts>) -> Self {
|
|
||||||
Self {
|
|
||||||
list,
|
|
||||||
shortcuts,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: List> WidgetComponent for Browser<T> {
|
|
||||||
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
|
|
||||||
let shortcuts_h = self
|
|
||||||
.shortcuts
|
|
||||||
.query(Attribute::Height)
|
|
||||||
.unwrap_or(AttrValue::Size(0))
|
|
||||||
.unwrap_size();
|
|
||||||
let layout = layout::root_component(area, shortcuts_h);
|
|
||||||
|
|
||||||
self.list.view(frame, layout[0]);
|
|
||||||
self.shortcuts.view(frame, layout[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state(&self) -> State {
|
|
||||||
State::None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn perform(&mut self, _properties: &Props, cmd: Cmd) -> CmdResult {
|
|
||||||
self.list.perform(cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn global_listener() -> Widget<GlobalListener> {
|
pub fn global_listener() -> Widget<GlobalListener> {
|
||||||
Widget::new(GlobalListener::default())
|
Widget::new(GlobalListener::default())
|
||||||
}
|
}
|
||||||
|
|
@ -76,34 +33,14 @@ pub fn label(content: &str) -> Widget<Label> {
|
||||||
|
|
||||||
pub fn reversable_label(content: &str) -> Widget<Label> {
|
pub fn reversable_label(content: &str) -> Widget<Label> {
|
||||||
let content = &format!(" {content} ");
|
let content = &format!(" {content} ");
|
||||||
|
|
||||||
label(content)
|
label(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn container_header(theme: &Theme, label: &str) -> Widget<Header> {
|
pub fn container_header(theme: &Theme, label: Widget<Label>) -> Widget<Header<(), 1>> {
|
||||||
let content = AttrValue::Payload(PropPayload::Vec(vec![PropValue::TextSpan(
|
let model = TableModel::new([label], [ColumnWidth::Fixed(100)]);
|
||||||
TextSpan::from(&format!(" {label} ")).fg(theme.colors.default_fg),
|
|
||||||
)]));
|
|
||||||
let widths = AttrValue::Payload(PropPayload::Vec(vec![PropValue::U16(100)]));
|
|
||||||
|
|
||||||
Widget::new(Header::default())
|
Widget::new(Header::new(model, theme.clone()))
|
||||||
.content(content)
|
|
||||||
.custom("widths", widths)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn table_header(theme: &Theme, labels: &[&str], widths: &[u16]) -> Widget<Header> {
|
|
||||||
let content = labels
|
|
||||||
.iter()
|
|
||||||
.map(|label| {
|
|
||||||
PropValue::TextSpan(TextSpan::from(label.to_string()).fg(theme.colors.default_fg))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let widths = AttrValue::Payload(PropPayload::Vec(
|
|
||||||
widths.iter().map(|w| PropValue::U16(*w)).collect(),
|
|
||||||
));
|
|
||||||
|
|
||||||
Widget::new(Header::default())
|
|
||||||
.content(AttrValue::Payload(PropPayload::Vec(content)))
|
|
||||||
.custom("widths", widths)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn labeled_container(
|
pub fn labeled_container(
|
||||||
|
|
@ -111,7 +48,10 @@ pub fn labeled_container(
|
||||||
title: &str,
|
title: &str,
|
||||||
component: Box<dyn MockComponent>,
|
component: Box<dyn MockComponent>,
|
||||||
) -> Widget<LabeledContainer> {
|
) -> Widget<LabeledContainer> {
|
||||||
let header = container_header(theme, title);
|
let header = container_header(
|
||||||
|
theme,
|
||||||
|
label(&format!(" {title} ")).foreground(theme.colors.default_fg),
|
||||||
|
);
|
||||||
let container = LabeledContainer::new(header, component);
|
let container = LabeledContainer::new(header, component);
|
||||||
|
|
||||||
Widget::new(container)
|
Widget::new(container)
|
||||||
|
|
@ -170,26 +110,3 @@ pub fn tabs(theme: &Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
|
||||||
|
|
||||||
Widget::new(tabs).height(2)
|
Widget::new(tabs).height(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table(
|
|
||||||
theme: &Theme,
|
|
||||||
labels: &[&str],
|
|
||||||
widths: &[u16],
|
|
||||||
items: &[impl List],
|
|
||||||
profile: &Profile,
|
|
||||||
) -> Widget<Table> {
|
|
||||||
let items = items.iter().map(|item| item.row(theme, profile)).collect();
|
|
||||||
|
|
||||||
let header = table_header(theme, labels, widths);
|
|
||||||
let table = Table::new(header);
|
|
||||||
|
|
||||||
let widths = AttrValue::Payload(PropPayload::Vec(
|
|
||||||
widths.iter().map(|w| PropValue::U16(*w)).collect(),
|
|
||||||
));
|
|
||||||
|
|
||||||
Widget::new(table)
|
|
||||||
.content(AttrValue::Table(items))
|
|
||||||
.custom("widths", widths)
|
|
||||||
.background(theme.colors.labeled_container_bg)
|
|
||||||
.highlight(theme.colors.item_list_highlighted_bg)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
use radicle::cob::patch::{Patch, PatchId};
|
use radicle::prelude::{Id, Project};
|
||||||
|
use radicle::storage::ReadStorage;
|
||||||
use radicle::Profile;
|
use radicle::Profile;
|
||||||
|
|
||||||
use radicle::prelude::{Id, Project};
|
use radicle::cob::patch::Patches;
|
||||||
|
|
||||||
use tuirealm::command::{Cmd, CmdResult};
|
use tuirealm::command::{Cmd, CmdResult};
|
||||||
use tuirealm::tui::layout::Rect;
|
use tuirealm::tui::layout::Rect;
|
||||||
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State};
|
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State};
|
||||||
|
|
||||||
use super::{Widget, WidgetComponent};
|
use super::common;
|
||||||
|
|
||||||
use super::common::container::{LabeledContainer, Tabs};
|
use super::common::container::{LabeledContainer, Tabs};
|
||||||
use super::common::context::Shortcuts;
|
use super::common::context::Shortcuts;
|
||||||
use super::common::label::Label;
|
use super::common::label::Label;
|
||||||
use super::common::{self, Browser};
|
use super::common::list::{ColumnWidth, Table};
|
||||||
|
|
||||||
|
use super::{Widget, WidgetComponent};
|
||||||
|
|
||||||
|
use crate::ui::cob::PatchItem;
|
||||||
use crate::ui::layout;
|
use crate::ui::layout;
|
||||||
use crate::ui::theme::Theme;
|
use crate::ui::theme::Theme;
|
||||||
|
|
||||||
|
|
@ -88,6 +92,84 @@ impl WidgetComponent for IssueBrowser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PatchBrowser {
|
||||||
|
table: Widget<Table<PatchItem, 8>>,
|
||||||
|
shortcuts: Widget<Shortcuts>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PatchBrowser {
|
||||||
|
pub fn new(profile: &Profile, id: &Id, shortcuts: Widget<Shortcuts>, theme: Theme) -> Self {
|
||||||
|
let repo = profile.storage.repository(*id).unwrap();
|
||||||
|
let patches = Patches::open(&repo)
|
||||||
|
.and_then(|patches| patches.all().map(|iter| iter.flatten().collect::<Vec<_>>()));
|
||||||
|
|
||||||
|
let header = [
|
||||||
|
common::label(" ● "),
|
||||||
|
common::label("ID"),
|
||||||
|
common::label("Title"),
|
||||||
|
common::label("Author"),
|
||||||
|
common::label("Head"),
|
||||||
|
common::label("+"),
|
||||||
|
common::label("-"),
|
||||||
|
common::label("Updated"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let widths = [
|
||||||
|
ColumnWidth::Fixed(3),
|
||||||
|
ColumnWidth::Fixed(7),
|
||||||
|
ColumnWidth::Grow,
|
||||||
|
ColumnWidth::Fixed(21),
|
||||||
|
ColumnWidth::Fixed(7),
|
||||||
|
ColumnWidth::Fixed(4),
|
||||||
|
ColumnWidth::Fixed(4),
|
||||||
|
ColumnWidth::Fixed(18),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut items = vec![];
|
||||||
|
if let Ok(mut patches) = patches {
|
||||||
|
patches.sort_by(|(_, a, _), (_, b, _)| b.timestamp().cmp(&a.timestamp()));
|
||||||
|
patches.sort_by(|(_, a, _), (_, b, _)| a.state().cmp(b.state()));
|
||||||
|
|
||||||
|
for (id, patch, _) in patches {
|
||||||
|
if let Ok(item) = PatchItem::try_from((profile, &repo, id, patch)) {
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let table = Widget::new(Table::new(&items, header, widths, theme.clone()))
|
||||||
|
.highlight(theme.colors.item_list_highlighted_bg);
|
||||||
|
|
||||||
|
Self { table, shortcuts }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_item(&self) -> Option<&PatchItem> {
|
||||||
|
self.table.selection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetComponent for PatchBrowser {
|
||||||
|
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
|
||||||
|
let shortcuts_h = self
|
||||||
|
.shortcuts
|
||||||
|
.query(Attribute::Height)
|
||||||
|
.unwrap_or(AttrValue::Size(0))
|
||||||
|
.unwrap_size();
|
||||||
|
let layout = layout::root_component(area, shortcuts_h);
|
||||||
|
|
||||||
|
self.table.view(frame, layout[0]);
|
||||||
|
self.shortcuts.view(frame, layout[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> State {
|
||||||
|
self.table.state()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform(&mut self, _properties: &Props, cmd: Cmd) -> CmdResult {
|
||||||
|
self.table.perform(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
|
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
|
||||||
common::tabs(
|
common::tabs(
|
||||||
theme,
|
theme,
|
||||||
|
|
@ -125,11 +207,7 @@ pub fn dashboard(theme: &Theme, id: &Id, project: &Project) -> Widget<Dashboard>
|
||||||
Widget::new(dashboard)
|
Widget::new(dashboard)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn patches(
|
pub fn patches(theme: &Theme, id: &Id, profile: &Profile) -> Widget<PatchBrowser> {
|
||||||
theme: &Theme,
|
|
||||||
items: &[(PatchId, Patch)],
|
|
||||||
profile: &Profile,
|
|
||||||
) -> Widget<Browser<(PatchId, Patch)>> {
|
|
||||||
let shortcuts = common::shortcuts(
|
let shortcuts = common::shortcuts(
|
||||||
theme,
|
theme,
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -140,13 +218,7 @@ pub fn patches(
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
let labels = vec!["", "title", "author", "time", "comments", "tags"];
|
Widget::new(PatchBrowser::new(profile, id, shortcuts, theme.clone()))
|
||||||
let widths = vec![3u16, 42, 15, 10, 5, 25];
|
|
||||||
|
|
||||||
let table = common::table(theme, &labels, &widths, items, profile);
|
|
||||||
let browser: Browser<(PatchId, Patch)> = Browser::new(table, shortcuts);
|
|
||||||
|
|
||||||
Widget::new(browser)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn issues(theme: &Theme) -> Widget<IssueBrowser> {
|
pub fn issues(theme: &Theme) -> Widget<IssueBrowser> {
|
||||||
|
|
|
||||||
|
|
@ -179,8 +179,8 @@ pub fn context(theme: &Theme, patch: (PatchId, &Patch), profile: &Profile) -> Wi
|
||||||
|
|
||||||
let id = format::cob(&id);
|
let id = format::cob(&id);
|
||||||
let title = patch.title();
|
let title = patch.title();
|
||||||
let author = cob::format_author(patch, profile);
|
let author = cob::format_author(patch.author().id(), is_you);
|
||||||
let comments = rev.discussion().comments().count();
|
let comments = rev.discussion().len();
|
||||||
|
|
||||||
let context = common::label(" patch ").background(theme.colors.context_badge_bg);
|
let context = common::label(" patch ").background(theme.colors.context_badge_bg);
|
||||||
let id = common::label(&format!(" {id} "))
|
let id = common::label(&format!(" {id} "))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue