From 5a4d5c7395f7c5a26a45e50df2eda295cd3d08b6 Mon Sep 17 00:00:00 2001 From: micielski <73398428+micielski@users.noreply.github.com> Date: Sat, 26 Oct 2024 15:14:04 +0200 Subject: [PATCH] feat: renaming torrents (#123) --- rm-config/defaults/keymap.toml | 1 + rm-config/src/keymap/actions/torrents_tab.rs | 3 + rm-main/src/transmission/action.rs | 18 +++++ rm-main/src/tui/tabs/torrents/mod.rs | 6 ++ .../src/tui/tabs/torrents/popups/details.rs | 16 ++++ rm-main/src/tui/tabs/torrents/task_manager.rs | 14 ++++ rm-main/src/tui/tabs/torrents/tasks/mod.rs | 2 + rm-main/src/tui/tabs/torrents/tasks/rename.rs | 80 +++++++++++++++++++ rm-shared/src/action.rs | 1 + rm-shared/src/status_task.rs | 11 +++ 10 files changed, 152 insertions(+) create mode 100644 rm-main/src/tui/tabs/torrents/tasks/rename.rs diff --git a/rm-config/defaults/keymap.toml b/rm-config/defaults/keymap.toml index a873e3c..b5750c6 100644 --- a/rm-config/defaults/keymap.toml +++ b/rm-config/defaults/keymap.toml @@ -43,6 +43,7 @@ keybindings = [ keybindings = [ { on = "a", action = "AddMagnet" }, { on = "m", action = "MoveTorrent" }, + { on = "r", action = "Rename" }, { on = "c", action = "ChangeCategory" }, { on = "p", action = "Pause" }, { on = "f", action = "ShowFiles" }, diff --git a/rm-config/src/keymap/actions/torrents_tab.rs b/rm-config/src/keymap/actions/torrents_tab.rs index d783fc3..858e58f 100644 --- a/rm-config/src/keymap/actions/torrents_tab.rs +++ b/rm-config/src/keymap/actions/torrents_tab.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; pub enum TorrentsAction { AddMagnet, MoveTorrent, + Rename, Pause, Delete, ShowFiles, @@ -23,6 +24,7 @@ impl UserAction for TorrentsAction { TorrentsAction::ShowFiles => "show files", TorrentsAction::ShowStats => "show statistics", TorrentsAction::ChangeCategory => "change category", + TorrentsAction::Rename => "rename torrent path", } } } @@ -37,6 +39,7 @@ impl From for Action { TorrentsAction::ShowFiles => Action::ShowFiles, TorrentsAction::ShowStats => Action::ShowStats, TorrentsAction::ChangeCategory => Action::ChangeCategory, + TorrentsAction::Rename => Action::Rename, } } } diff --git a/rm-main/src/transmission/action.rs b/rm-main/src/transmission/action.rs index 0bf2c99..99a8539 100644 --- a/rm-main/src/transmission/action.rs +++ b/rm-main/src/transmission/action.rs @@ -23,6 +23,8 @@ pub enum TorrentAction { Start(Vec), // Torrent ID, Directory to move to Move(Vec, String), + // Torrent ID, Current name, Name to change to + Rename(Id, String, String), // Torrent ID, Category to set ChangeCategory(Vec, String), // Delete Torrents with these given IDs (without files) @@ -231,6 +233,22 @@ pub async fn action_handler( } } } + TorrentAction::Rename(id, current_name, new_name) => { + match client + .torrent_rename_path(vec![id], current_name, new_name) + .await + { + Ok(_) => action_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), + Err(err) => { + let msg = "Failed to rename a torrent"; + let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); + action_tx + .send(UpdateAction::Error(Box::new(err_message))) + .unwrap(); + action_tx.send(UpdateAction::StatusTaskFailure).unwrap(); + } + } + } } } } diff --git a/rm-main/src/tui/tabs/torrents/mod.rs b/rm-main/src/tui/tabs/torrents/mod.rs index 52e7930..60578f8 100644 --- a/rm-main/src/tui/tabs/torrents/mod.rs +++ b/rm-main/src/tui/tabs/torrents/mod.rs @@ -151,6 +151,12 @@ impl Component for TorrentsTab { self.task_manager.delete_torrents(torrent_selection); } } + A::Rename => { + if let Some(TorrentSelection::Single(id, curr_name)) = self.get_currently_selected() + { + self.task_manager.rename(id, curr_name); + } + } A::AddMagnet => self.task_manager.add_magnet(), A::Search => self.task_manager.search( &self diff --git a/rm-main/src/tui/tabs/torrents/popups/details.rs b/rm-main/src/tui/tabs/torrents/popups/details.rs index 2b2f523..564f852 100644 --- a/rm-main/src/tui/tabs/torrents/popups/details.rs +++ b/rm-main/src/tui/tabs/torrents/popups/details.rs @@ -37,6 +37,10 @@ impl Component for DetailsPopup { self.ctx.send_action(Action::ShowFiles); ComponentAction::Quit } + Action::Rename => { + self.ctx.send_action(Action::Rename); + ComponentAction::Quit + } Action::ChangeCategory => { self.ctx.send_action(Action::ChangeCategory); ComponentAction::Quit @@ -103,6 +107,17 @@ impl Component for DetailsPopup { keybinding_style(), )); + let mut rename_line = Line::default(); + rename_line.push_span(Span::raw("Rename: ")); + rename_line.push_span(Span::styled( + CONFIG + .keybindings + .torrents_tab + .get_keys_for_action_joined(TorrentsAction::Rename) + .unwrap_or_default(), + keybinding_style(), + )); + let mut delete_line = Line::default(); delete_line.push_span(Span::raw("Delete: ")); delete_line.push_span(Span::styled( @@ -158,6 +173,7 @@ impl Component for DetailsPopup { lines.push(padding_line); lines.push(delete_line); lines.push(show_files_line); + lines.push(rename_line); lines.push(move_location_line); lines.push(change_category_line); diff --git a/rm-main/src/tui/tabs/torrents/task_manager.rs b/rm-main/src/tui/tabs/torrents/task_manager.rs index e11a515..1b693d8 100644 --- a/rm-main/src/tui/tabs/torrents/task_manager.rs +++ b/rm-main/src/tui/tabs/torrents/task_manager.rs @@ -6,6 +6,7 @@ use rm_shared::{ action::{Action, UpdateAction}, status_task::StatusTask, }; +use transmission_rpc::types::Id; use crate::tui::{ app, @@ -45,6 +46,7 @@ pub enum CurrentTask { Status(tasks::Status), Sort(tasks::Sort), Selection(tasks::Selection), + Rename(tasks::Rename), } impl CurrentTask { @@ -89,6 +91,11 @@ impl Component for TaskManager { self.cancel_task() } } + CurrentTask::Rename(rename_bar) => { + if rename_bar.handle_actions(action).is_quit() { + self.cancel_task() + } + } CurrentTask::Default(_) => (), CurrentTask::Sort(_) => (), CurrentTask::Selection(_) => (), @@ -126,6 +133,7 @@ impl Component for TaskManager { CurrentTask::ChangeCategory(category_bar) => category_bar.render(f, rect), CurrentTask::Sort(sort_bar) => sort_bar.render(f, rect), CurrentTask::Selection(selection_bar) => selection_bar.render(f, rect), + CurrentTask::Rename(rename_bar) => rename_bar.render(f, rect), } } @@ -146,6 +154,12 @@ impl TaskManager { self.ctx.send_update_action(UpdateAction::SwitchToInputMode); } + pub fn rename(&mut self, id: Id, curr_name: String) { + self.current_task = + CurrentTask::Rename(tasks::Rename::new(self.ctx.clone(), id, curr_name)); + self.ctx.send_update_action(UpdateAction::SwitchToInputMode); + } + pub fn delete_torrents(&mut self, selection: TorrentSelection) { self.current_task = CurrentTask::Delete(tasks::Delete::new(self.ctx.clone(), selection)); self.ctx.send_update_action(UpdateAction::SwitchToInputMode); diff --git a/rm-main/src/tui/tabs/torrents/tasks/mod.rs b/rm-main/src/tui/tabs/torrents/tasks/mod.rs index 2668c2c..d5c1241 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/mod.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/mod.rs @@ -4,6 +4,7 @@ mod default; mod delete_torrent; mod filter; mod move_torrent; +mod rename; mod selection; mod sort; mod status; @@ -14,6 +15,7 @@ pub use default::Default; pub use delete_torrent::Delete; pub use filter::Filter; pub use move_torrent::Move; +pub use rename::Rename; pub use selection::Selection; pub use sort::Sort; pub use status::{CurrentTaskState, Status}; diff --git a/rm-main/src/tui/tabs/torrents/tasks/rename.rs b/rm-main/src/tui/tabs/torrents/tasks/rename.rs new file mode 100644 index 0000000..cde4143 --- /dev/null +++ b/rm-main/src/tui/tabs/torrents/tasks/rename.rs @@ -0,0 +1,80 @@ +use crossterm::event::KeyCode; +use ratatui::{prelude::*, Frame}; +use rm_shared::{ + action::{Action, UpdateAction}, + status_task::StatusTask, +}; +use transmission_rpc::types::Id; + +use crate::{ + transmission::TorrentAction, + tui::{ + app, + components::{Component, ComponentAction, InputManager}, + }, +}; + +pub struct Rename { + id: Id, + curr_name: String, + input_mgr: InputManager, + ctx: app::Ctx, +} + +impl Rename { + pub fn new(ctx: app::Ctx, to_rename: Id, curr_name: String) -> Self { + let prompt = String::from("New name: "); + + Self { + id: to_rename, + ctx, + input_mgr: InputManager::new_with_value(prompt, curr_name.clone()), + curr_name, + } + } + + fn rename(&self) { + let new_name = self.input_mgr.text(); + + if self.curr_name == new_name { + return; + } + + let task = StatusTask::new_rename(self.curr_name.clone()); + + self.ctx + .send_update_action(UpdateAction::StatusTaskSet(task)); + self.ctx.send_torrent_action(TorrentAction::Rename( + self.id.clone(), + self.curr_name.clone(), + self.input_mgr.text(), + )) + } +} + +impl Component for Rename { + fn handle_actions(&mut self, action: Action) -> crate::tui::components::ComponentAction { + match action { + Action::Input(input) => { + if input.code == KeyCode::Esc { + return ComponentAction::Quit; + } else if input.code == KeyCode::Enter { + self.rename(); + return ComponentAction::Quit; + } + + if self.input_mgr.handle_key(input).is_some() { + self.ctx.send_action(Action::Render); + } + + ComponentAction::Nothing + } + + _ => ComponentAction::Nothing, + } + } + + fn render(&mut self, f: &mut Frame, rect: Rect) { + self.input_mgr.render(f, rect) + } +} diff --git a/rm-shared/src/action.rs b/rm-shared/src/action.rs index 9c5ee72..ea21076 100644 --- a/rm-shared/src/action.rs +++ b/rm-shared/src/action.rs @@ -41,6 +41,7 @@ pub enum Action { AddMagnet, MoveTorrent, ChangeCategory, + Rename, // Search Tab ShowProvidersInfo, } diff --git a/rm-shared/src/status_task.rs b/rm-shared/src/status_task.rs index ae9f731..0e99bb3 100644 --- a/rm-shared/src/status_task.rs +++ b/rm-shared/src/status_task.rs @@ -9,6 +9,7 @@ pub struct StatusTask { enum TaskType { Add, Delete, + Rename, Move, Open, ChangeCategory, @@ -22,6 +23,13 @@ impl StatusTask { } } + pub fn new_rename(what: impl Into) -> Self { + StatusTask { + task_type: TaskType::Rename, + what: what.into(), + } + } + pub fn new_del(what: impl Into) -> Self { StatusTask { task_type: TaskType::Delete, @@ -65,6 +73,7 @@ impl StatusTask { format!(" Category set to {truncated}!") } } + TaskType::Rename => format!("Renamed {truncated}"), } } @@ -77,6 +86,7 @@ impl StatusTask { TaskType::Move => format!(" Error moving to {truncated}"), TaskType::Open => format!(" Error opening {truncated}"), TaskType::ChangeCategory => format!(" Error changing category to {truncated}"), + TaskType::Rename => format!(" Error renaming {truncated}"), } } @@ -89,6 +99,7 @@ impl StatusTask { TaskType::Move => format!(" Moving {truncated}"), TaskType::Open => format!(" Opening {truncated}"), TaskType::ChangeCategory => format!(" Changing category to {truncated}"), + TaskType::Rename => format!(" Renaming {truncated}"), } } }