mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-11-08 04:40:47 +08:00
patch/list: Load patches with git stats in background
This commit is contained in:
+138
-2
@@ -20,7 +20,7 @@ use radicle_tui as tui;
|
||||
|
||||
use tui::event::Key;
|
||||
use tui::store;
|
||||
use tui::task::EmptyProcessors;
|
||||
use tui::task::{Process, Task};
|
||||
use tui::ui;
|
||||
use tui::ui::layout::Spacing;
|
||||
use tui::ui::widget::{
|
||||
@@ -101,6 +101,7 @@ pub enum PatchOperation {
|
||||
|
||||
type Selection = tui::Selection<PatchOperation>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Context {
|
||||
pub profile: Profile,
|
||||
pub rid: RepoId,
|
||||
@@ -109,6 +110,59 @@ pub struct Context {
|
||||
pub search: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Loader {
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl Loader {
|
||||
fn new(context: Context) -> Self {
|
||||
Self { context }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PatchLoader {
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl PatchLoader {
|
||||
fn new(context: Context) -> Self {
|
||||
PatchLoader { context }
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for PatchLoader {
|
||||
type Return = Message;
|
||||
|
||||
fn run(&self) -> anyhow::Result<Vec<Self::Return>> {
|
||||
let context = &self.context;
|
||||
let profile = context.profile.clone();
|
||||
let repo = profile.storage.repository(context.rid)?;
|
||||
let cache = profile.patches(&repo)?;
|
||||
let patches = cache
|
||||
.list()?
|
||||
.filter_map(|patch| patch.ok())
|
||||
.flat_map(|patch| Patch::new(&context.profile, &repo, patch.clone()).ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(vec![Message::Loaded(patches)])
|
||||
}
|
||||
}
|
||||
|
||||
impl Process<Message> for Loader {
|
||||
async fn process(&mut self, message: Message) -> anyhow::Result<Vec<Message>> {
|
||||
match message {
|
||||
Message::Initialize | Message::Reload => {
|
||||
let loader = PatchLoader::new(self.context.clone());
|
||||
let messages = tokio::spawn(async move { loader.run() }).await.unwrap()?;
|
||||
Ok(messages)
|
||||
}
|
||||
_ => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tui {
|
||||
context: Context,
|
||||
}
|
||||
@@ -123,7 +177,13 @@ impl Tui {
|
||||
let channel = Channel::default();
|
||||
let state = App::try_from(&self.context)?;
|
||||
|
||||
tui::im(state, viewport, channel, EmptyProcessors::new()).await
|
||||
tui::im(
|
||||
state,
|
||||
viewport,
|
||||
channel,
|
||||
vec![Loader::new(self.context.clone())],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +208,12 @@ pub enum Change {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
Initialize,
|
||||
Changed(Change),
|
||||
ShowSearch,
|
||||
HideSearch { apply: bool },
|
||||
Reload,
|
||||
Loaded(Vec<Patch>),
|
||||
Exit { operation: Option<PatchOperation> },
|
||||
Quit,
|
||||
}
|
||||
@@ -170,6 +233,8 @@ pub struct AppState {
|
||||
show_search: bool,
|
||||
help: TextViewState,
|
||||
filter: PatchFilter,
|
||||
loading: bool,
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -226,6 +291,8 @@ impl TryFrom<&Context> for App {
|
||||
show_search: false,
|
||||
help: TextViewState::new(Position::default()),
|
||||
filter,
|
||||
loading: false,
|
||||
initialized: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -236,6 +303,21 @@ impl store::Update<Message> for App {
|
||||
|
||||
fn update(&mut self, message: Message) -> Option<tui::Exit<Selection>> {
|
||||
match message {
|
||||
Message::Initialize => {
|
||||
self.state.loading = true;
|
||||
self.state.initialized = true;
|
||||
None
|
||||
}
|
||||
Message::Reload => {
|
||||
self.state.loading = true;
|
||||
None
|
||||
}
|
||||
Message::Loaded(patches) => {
|
||||
self.apply_patches(patches);
|
||||
self.apply_sorting();
|
||||
self.state.loading = false;
|
||||
None
|
||||
}
|
||||
Message::Quit => Some(Exit { value: None }),
|
||||
Message::Exit { operation } => Some(Exit {
|
||||
value: Some(Selection {
|
||||
@@ -295,6 +377,11 @@ impl store::Update<Message> for App {
|
||||
impl Show<Message> for App {
|
||||
fn show(&self, ctx: &ui::Context<Message>, frame: &mut Frame) -> Result<()> {
|
||||
Window::default().show(ctx, |ui| {
|
||||
// Initialize
|
||||
if !self.state.initialized {
|
||||
ui.send_message(Message::Initialize);
|
||||
}
|
||||
|
||||
match self.state.page {
|
||||
Page::Main => {
|
||||
let show_search = self.state.show_search;
|
||||
@@ -406,6 +493,10 @@ impl App {
|
||||
state: TableState::new(selected),
|
||||
}));
|
||||
}
|
||||
|
||||
if self.state.loading {
|
||||
self.show_loading_popup(frame, ui);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -413,6 +504,9 @@ impl App {
|
||||
if ui.has_input(|key| key == Key::Char('/')) {
|
||||
ui.send_message(Message::ShowSearch);
|
||||
}
|
||||
if ui.has_input(|key| key == Key::Char('r')) {
|
||||
ui.send_message(Message::Reload);
|
||||
}
|
||||
|
||||
if let Ok(args) = OperationArguments::try_from((&patches, &self.state)) {
|
||||
if ui.has_input(|key| key == Key::Enter) {
|
||||
@@ -623,6 +717,7 @@ impl App {
|
||||
("c", "checkout"),
|
||||
("d", "diff"),
|
||||
("/", "search"),
|
||||
("r", "reload"),
|
||||
("?", "help"),
|
||||
],
|
||||
'∙',
|
||||
@@ -630,6 +725,35 @@ impl App {
|
||||
);
|
||||
}
|
||||
|
||||
fn show_loading_popup(&self, frame: &mut Frame, ui: &mut Ui<Message>) {
|
||||
ui.popup(Layout::vertical([Constraint::Min(1)]), |ui| {
|
||||
ui.layout(
|
||||
Layout::vertical([Constraint::Min(1), Constraint::Length(3)]).margin(1),
|
||||
None,
|
||||
|ui| {
|
||||
ui.label(frame, "");
|
||||
ui.layout(
|
||||
Layout::horizontal([Constraint::Min(1), Constraint::Length(11)]),
|
||||
None,
|
||||
|ui| {
|
||||
ui.label(frame, "");
|
||||
ui.column_bar(
|
||||
frame,
|
||||
[Column::new(
|
||||
Span::raw(" Loading ").magenta().rapid_blink(),
|
||||
Constraint::Fill(1),
|
||||
)]
|
||||
.to_vec(),
|
||||
Spacing::from(0),
|
||||
Some(Borders::All),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn show_help_text(&self, frame: &mut Frame, ui: &mut Ui<Message>) {
|
||||
ui.column_bar(
|
||||
frame,
|
||||
@@ -677,3 +801,15 @@ impl App {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn apply_patches(&mut self, patches: Vec<Patch>) {
|
||||
let mut items = self.patches.lock().unwrap();
|
||||
*items = patches;
|
||||
}
|
||||
|
||||
fn apply_sorting(&mut self) {
|
||||
let mut items = self.patches.lock().unwrap();
|
||||
items.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user