Skip to content

Commit

Permalink
feat: torrent tab status bar
Browse files Browse the repository at this point in the history
  • Loading branch information
aidanaden committed Jun 27, 2024
1 parent 96cc5ea commit c8f157b
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 15 deletions.
1 change: 1 addition & 0 deletions rm-main/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub(crate) enum Action {
ChangeTab(u8),
Input(KeyEvent),
Error(Box<ErrorPopup>),
Success,
}

impl Action {
Expand Down
24 changes: 16 additions & 8 deletions rm-main/src/transmission/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ pub async fn action_handler(ctx: app::Ctx, mut trans_rx: UnboundedReceiver<Torre
..Default::default()
};

if let Err(e) = ctx.client.lock().await.torrent_add(args).await {
let error_title = "Failed to add a torrent";
let msg = "Failed to add torrent with URL/Path:\n\"".to_owned()
+ url
+ "\"\n"
+ &e.to_string();
let error_popup = Box::new(ErrorPopup::new(error_title, msg));
ctx.send_action(Action::Error(error_popup));
match ctx.client.lock().await.torrent_add(args).await {
Ok(_) => {
ctx.send_action(Action::Success);
}
Err(e) => {
let error_title = "Failed to add a torrent";
let msg = "Failed to add torrent with URL/Path:\n\"".to_owned()
+ url
+ "\"\n"
+ &e.to_string();
let error_popup = Box::new(ErrorPopup::new(error_title, msg));
ctx.send_action(Action::Error(error_popup));
}
}
}
TorrentAction::Stop(ids) => {
Expand All @@ -64,6 +69,7 @@ pub async fn action_handler(ctx: app::Ctx, mut trans_rx: UnboundedReceiver<Torre
.torrent_remove(ids, true)
.await
.unwrap();
ctx.send_action(Action::Success)
}
TorrentAction::DeleteWithoutFiles(ids) => {
ctx.client
Expand All @@ -72,6 +78,8 @@ pub async fn action_handler(ctx: app::Ctx, mut trans_rx: UnboundedReceiver<Torre
.torrent_remove(ids, false)
.await
.unwrap();

ctx.send_action(Action::Success)
}
TorrentAction::GetTorrentInfo(id, torrent_info) => {
let new_torrent_info = ctx
Expand Down
129 changes: 124 additions & 5 deletions rm-main/src/ui/tabs/torrents/task_manager.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::sync::{Arc, Mutex};

use ratatui::prelude::*;
use throbber_widgets_tui::ThrobberState;

use crate::{action::Action, app, ui::components::Component};

Expand All @@ -10,6 +11,7 @@ use super::{
default::DefaultBar,
delete_torrent::{self, DeleteBar},
filter::FilterBar,
status::{CurrentTaskState, StatusBar, StatusTask},
},
TableManager,
};
Expand All @@ -30,11 +32,12 @@ impl TaskManager {
}
}

enum CurrentTask {
pub enum CurrentTask {
AddMagnetBar(AddMagnetBar),
DeleteBar(DeleteBar),
FilterBar(FilterBar),
Default(DefaultBar),
Status(StatusBar),
}

impl Component for TaskManager {
Expand All @@ -43,23 +46,31 @@ impl Component for TaskManager {
use Action as A;
match &mut self.current_task {
CurrentTask::AddMagnetBar(magnet_bar) => match magnet_bar.handle_actions(action) {
Some(A::Quit) => self.finish_task(),
Some(A::Confirm) => self.pending_task(StatusTask::Add),
Some(A::Quit) => self.cancel_task(),
Some(A::Render) => Some(A::Render),
_ => None,
},

CurrentTask::DeleteBar(delete_bar) => match delete_bar.handle_actions(action) {
Some(A::Quit) => self.finish_task(),
Some(A::Confirm) => self.pending_task(StatusTask::Delete),
Some(A::Quit) => self.cancel_task(),
Some(A::Render) => Some(A::Render),
_ => None,
},

CurrentTask::FilterBar(filter_bar) => match filter_bar.handle_actions(action) {
Some(A::Quit) => self.finish_task(),
Some(A::Quit) => self.cancel_task(),
Some(A::Render) => Some(A::Render),
_ => None,
},

CurrentTask::Status(status_bar) => match status_bar.handle_actions(action) {
Some(A::Render) => Some(A::Render),
Some(action) => self.handle_events_to_manager(&action),
_ => None,
},

CurrentTask::Default(_) => self.handle_events_to_manager(&action),
}
}
Expand All @@ -70,6 +81,14 @@ impl Component for TaskManager {
CurrentTask::DeleteBar(delete_bar) => delete_bar.render(f, rect),
CurrentTask::FilterBar(filter_bar) => filter_bar.render(f, rect),
CurrentTask::Default(default_bar) => default_bar.render(f, rect),
CurrentTask::Status(status_bar) => status_bar.render(f, rect),
}
}

fn tick(&mut self) -> Option<Action> {
match &mut self.current_task {
CurrentTask::Status(status_bar) => status_bar.tick(),
_ => None,
}
}
}
Expand Down Expand Up @@ -108,7 +127,18 @@ impl TaskManager {
}
}

fn finish_task(&mut self) -> Option<Action> {
fn pending_task(&mut self, task: StatusTask) -> Option<Action> {
if !matches!(self.current_task, CurrentTask::Status(_)) {
let state = Arc::new(Mutex::new(ThrobberState::default()));
self.current_task =
CurrentTask::Status(StatusBar::new(task, CurrentTaskState::Loading(state)));
Some(Action::SwitchToNormalMode)
} else {
None
}
}

fn cancel_task(&mut self) -> Option<Action> {
if !matches!(self.current_task, CurrentTask::Default(_)) {
self.current_task = CurrentTask::Default(DefaultBar::new(self.ctx.clone()));
Some(Action::SwitchToNormalMode)
Expand All @@ -117,3 +147,92 @@ impl TaskManager {
}
}
}

// enum CurrentTaskState {
// Nothing,
// Loading(Arc<Mutex<ThrobberState>>),
// Success(),
// Failure(),
// }
//
// impl CurrentTaskState {
// fn new() -> Self {
// Self::Nothing
// }
//
// fn loading(&mut self, state: Arc<Mutex<ThrobberState>>) {
// *self = Self::Loading(task, state);
// }
//
// fn failure(&mut self) {
// *self = Self::Failure(task);
// }
//
// fn success(&mut self, task: CurrentTask) {
// *self = Self::Success(task);
// }
// }
//
// impl Component for CurrentTaskState {
// fn render(&mut self, f: &mut Frame, rect: Rect) {
// match self {
// CurrentTaskState::Nothing => return,
// CurrentTaskState::Loading(task, state) => {
// let label = match task {
// CurrentTask::DeleteBar(_) => Some("Deleting..."),
// CurrentTask::AddMagnetBar(_) => Some("Adding..."),
// _ => None,
// };
//
// if let Some(text) = label {
// let default_throbber = throbber_widgets_tui::Throbber::default()
// .label(text)
// .style(ratatui::style::Style::default().fg(ratatui::style::Color::Yellow));
// f.render_stateful_widget(
// default_throbber.clone(),
// rect,
// &mut state.lock().unwrap(),
// );
// }
// }
// CurrentTaskState::Failure(task) => {
// let label = match task {
// CurrentTask::DeleteBar(_) => Some(" Error deleting torrent"),
// CurrentTask::AddMagnetBar(_) => Some(" Error adding torrent"),
// _ => None,
// };
// if let Some(text) = label {
// let mut line = Line::default();
// line.push_span(Span::styled("", Style::default().red()));
// line.push_span(Span::raw(text));
// let paragraph = Paragraph::new(line);
// f.render_widget(paragraph, rect);
// }
// }
// CurrentTaskState::Success(task) => {
// let label = match task {
// CurrentTask::DeleteBar(_) => Some(" Deleted torrent"),
// CurrentTask::AddMagnetBar(_) => Some(" Added torrent"),
// _ => None,
// };
// if let Some(text) = label {
// let mut line = Line::default();
// line.push_span(Span::styled("", Style::default().green()));
// line.push_span(Span::raw(text));
// let paragraph = Paragraph::new(line);
// f.render_widget(paragraph, rect);
// }
// }
// }
// }
//
// fn tick(&mut self) -> Option<Action> {
// match self {
// CurrentTaskState::Loading(_, state) => {
// state.lock().unwrap().calc_next();
// Some(Action::Render)
// }
// _ => None,
// }
// }
// }
2 changes: 1 addition & 1 deletion rm-main/src/ui/tabs/torrents/tasks/add_magnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl AddMagnetBar {
self.input_magnet_mgr.text(),
Some(self.input_location_mgr.text()),
));
return Some(Action::Quit);
return Some(Action::Confirm);
}
if input.code == KeyCode::Esc {
return Some(Action::Quit);
Expand Down
2 changes: 1 addition & 1 deletion rm-main/src/ui/tabs/torrents/tasks/delete_torrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl Component for DeleteBar {
))
}
}
return Some(Action::Quit);
return Some(Action::Confirm);
} else if text == "n" || text == "no" {
return Some(Action::Quit);
}
Expand Down
1 change: 1 addition & 0 deletions rm-main/src/ui/tabs/torrents/tasks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod add_magnet;
pub mod default;
pub mod delete_torrent;
pub mod filter;
pub mod status;
119 changes: 119 additions & 0 deletions rm-main/src/ui/tabs/torrents/tasks/status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use std::sync::{Arc, Mutex};

use crate::{action::Action, ui::components::Component};

use ratatui::{prelude::*, widgets::Paragraph};
use throbber_widgets_tui::ThrobberState;

pub struct StatusBar {
task: StatusTask,
task_status: CurrentTaskState,
}

impl StatusBar {
pub const fn new(task: StatusTask, task_status: CurrentTaskState) -> Self {
Self { task, task_status }
}
}

impl Component for StatusBar {
fn render(&mut self, f: &mut Frame, rect: Rect) {
match &self.task_status {
CurrentTaskState::Nothing => return,
CurrentTaskState::Loading(state) => {
let title = match self.task {
StatusTask::Add => "Adding torrent...",
StatusTask::Delete => "Deleting torrent...",
};
let default_throbber = throbber_widgets_tui::Throbber::default()
.label(title)
.style(ratatui::style::Style::default().fg(ratatui::style::Color::Yellow));
f.render_stateful_widget(
default_throbber.clone(),
rect,
&mut state.lock().unwrap(),
);
}
CurrentTaskState::Failure() => {
let title = match self.task {
StatusTask::Add => " Error adding torrent",
StatusTask::Delete => " Error deleting torrent",
};
let mut line = Line::default();
line.push_span(Span::styled("", Style::default().red()));
line.push_span(Span::raw(title));
let paragraph = Paragraph::new(line);
f.render_widget(paragraph, rect);
}
CurrentTaskState::Success() => {
let title = match self.task {
StatusTask::Add => " Added torrent",
StatusTask::Delete => " Deleted torrent",
};
let mut line = Line::default();
line.push_span(Span::styled("", Style::default().green()));
line.push_span(Span::raw(title));
let paragraph = Paragraph::new(line);
f.render_widget(paragraph, rect);
}
}
}

fn handle_actions(&mut self, _action: Action) -> Option<Action> {
match _action {
Action::Tick => self.tick(),
Action::Success => {
self.task_status.success();
Some(Action::Render)
}
Action::Error(_) => {
self.task_status.failure();
Some(Action::Render)
}
_ => Some(_action),
}
}

fn tick(&mut self) -> Option<Action> {
match &self.task_status {
CurrentTaskState::Loading(state) => {
state.lock().unwrap().calc_next();
Some(Action::Render)
}
_ => None,
}
}
}

pub enum StatusTask {
Add,
Delete,
}

#[derive(Clone)]
pub enum CurrentTaskState {
Nothing,
Loading(Arc<Mutex<ThrobberState>>),
Success(),
Failure(),
}

impl CurrentTaskState {
fn new() -> Self {
Self::Nothing
}

fn loading(&mut self, state: Arc<Mutex<ThrobberState>>) {
*self = Self::Loading(state);
}

fn failure(&mut self) {
*self = Self::Failure();
}

fn success(&mut self) {
*self = Self::Success();
}
}

impl Component for CurrentTaskState {}

0 comments on commit c8f157b

Please sign in to comment.