From 24e328d5d696fefbc0b5ac9177a9ab164ac48ef9 Mon Sep 17 00:00:00 2001 From: micielski <73398428+micielski@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:47:37 +0100 Subject: [PATCH] perf: improve startup speed (#129) * perf: improve startup speed * perf: make ctx a global variable --- .envrc | 1 + .gitignore | 1 - Cargo.lock | 6 +- rm-main/src/transmission/action.rs | 42 +++---- rm-main/src/transmission/fetchers.rs | 30 ++--- rm-main/src/tui/app.rs | 110 ++++++++++-------- rm-main/src/tui/global_popups/help.rs | 14 +-- rm-main/src/tui/global_popups/mod.rs | 19 ++- rm-main/src/tui/main_window.rs | 16 +-- rm-main/src/tui/tabs/search/bottom_bar.rs | 21 ++-- rm-main/src/tui/tabs/search/mod.rs | 53 ++++----- rm-main/src/tui/tabs/search/popups/mod.rs | 13 +-- rm-main/src/tui/tabs/torrents/mod.rs | 96 ++++++++------- .../src/tui/tabs/torrents/popups/details.rs | 17 ++- rm-main/src/tui/tabs/torrents/popups/files.rs | 40 +++---- rm-main/src/tui/tabs/torrents/popups/mod.rs | 8 +- rm-main/src/tui/tabs/torrents/task_manager.rs | 47 +++----- .../src/tui/tabs/torrents/tasks/add_magnet.rs | 28 +++-- .../tabs/torrents/tasks/change_category.rs | 23 ++-- .../tui/tabs/torrents/tasks/delete_torrent.rs | 16 +-- rm-main/src/tui/tabs/torrents/tasks/filter.rs | 12 +- .../tui/tabs/torrents/tasks/move_torrent.rs | 13 +-- rm-main/src/tui/tabs/torrents/tasks/rename.rs | 13 +-- rm-main/src/tui/tabs/torrents/tasks/status.rs | 21 ++-- rm-shared/src/action.rs | 3 +- 25 files changed, 308 insertions(+), 355 deletions(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index 47d3de2..4fecf2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target .direnv/ -.envrc /result diff --git a/Cargo.lock b/Cargo.lock index 87c60e4..d552ad0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1812,7 +1812,7 @@ dependencies = [ [[package]] name = "rm-config" -version = "0.5.0" +version = "0.5.1" dependencies = [ "anyhow", "crossterm", @@ -1830,7 +1830,7 @@ dependencies = [ [[package]] name = "rm-shared" -version = "0.5.0" +version = "0.5.1" dependencies = [ "chrono", "crossterm", @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "rustmission" -version = "0.5.0" +version = "0.5.1" dependencies = [ "anyhow", "base64", diff --git a/rm-main/src/transmission/action.rs b/rm-main/src/transmission/action.rs index 99a8539..db7a253 100644 --- a/rm-main/src/transmission/action.rs +++ b/rm-main/src/transmission/action.rs @@ -51,7 +51,7 @@ pub enum TorrentAction { pub async fn action_handler( mut client: TransClient, mut trans_rx: UnboundedReceiver, - action_tx: UnboundedSender, + update_tx: UnboundedSender, ) { while let Some(action) = trans_rx.recv().await { match action { @@ -74,15 +74,15 @@ pub async fn action_handler( }; match client.torrent_add(args).await { Ok(_) => { - action_tx.send(UpdateAction::StatusTaskSuccess).unwrap(); + update_tx.send(UpdateAction::StatusTaskSuccess).unwrap(); } Err(err) => { let msg = format!("Failed to add torrent with URL/Path: \"{url}\""); let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); - action_tx.send(UpdateAction::StatusTaskFailure).unwrap(); + update_tx.send(UpdateAction::StatusTaskFailure).unwrap(); } } } @@ -92,7 +92,7 @@ pub async fn action_handler( Err(err) => { let msg = format!("Failed to stop torrents with these IDs: {:?}", ids); let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); } @@ -104,7 +104,7 @@ pub async fn action_handler( Err(err) => { let msg = format!("Failed to start torrents with these IDs: {:?}", ids); let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); } @@ -112,27 +112,27 @@ pub async fn action_handler( } TorrentAction::DelWithFiles(ids) => { match client.torrent_remove(ids.clone(), true).await { - Ok(_) => action_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), + Ok(_) => update_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), Err(err) => { let msg = format!("Failed to remove torrents with these IDs: {:?}", ids); let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); - action_tx.send(UpdateAction::StatusTaskFailure).unwrap(); + update_tx.send(UpdateAction::StatusTaskFailure).unwrap(); } } } TorrentAction::DelWithoutFiles(ids) => { match client.torrent_remove(ids.clone(), false).await { - Ok(_) => action_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), + Ok(_) => update_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), Err(err) => { let msg = format!("Failed to remove torrents with these IDs: {:?}", ids); let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); - action_tx.send(UpdateAction::StatusTaskFailure).unwrap(); + update_tx.send(UpdateAction::StatusTaskFailure).unwrap(); } } } @@ -145,7 +145,7 @@ pub async fn action_handler( ids ); let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); } @@ -158,7 +158,7 @@ pub async fn action_handler( Err(err) => { let msg = "Failed to get session data"; let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); } @@ -170,7 +170,7 @@ pub async fn action_handler( { let msg = format!("Failed to move torrent to new directory:\n{new_directory}"); let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); } @@ -222,14 +222,14 @@ pub async fn action_handler( ..Default::default() }; match client.torrent_set(args, Some(ids)).await { - Ok(_) => action_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), + Ok(_) => update_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), Err(err) => { let msg = "Failed to set category"; let err_message = ErrorMessage::new(FAILED_TO_COMMUNICATE, msg, err); - action_tx + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); - action_tx.send(UpdateAction::StatusTaskFailure).unwrap(); + update_tx.send(UpdateAction::StatusTaskFailure).unwrap(); } } } @@ -238,14 +238,14 @@ pub async fn action_handler( .torrent_rename_path(vec![id], current_name, new_name) .await { - Ok(_) => action_tx.send(UpdateAction::StatusTaskSuccess).unwrap(), + Ok(_) => update_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 + update_tx .send(UpdateAction::Error(Box::new(err_message))) .unwrap(); - action_tx.send(UpdateAction::StatusTaskFailure).unwrap(); + update_tx.send(UpdateAction::StatusTaskFailure).unwrap(); } } } diff --git a/rm-main/src/transmission/fetchers.rs b/rm-main/src/transmission/fetchers.rs index 90d187e..a503f8c 100644 --- a/rm-main/src/transmission/fetchers.rs +++ b/rm-main/src/transmission/fetchers.rs @@ -6,21 +6,21 @@ use transmission_rpc::types::TorrentGetField; use rm_shared::action::UpdateAction; -use crate::tui::app; +use crate::tui::app::CTX; use super::TorrentAction; -pub async fn stats(ctx: app::Ctx) { +pub async fn stats() { loop { let (stats_tx, stats_rx) = oneshot::channel(); - ctx.send_torrent_action(TorrentAction::GetSessionStats(stats_tx)); + CTX.send_torrent_action(TorrentAction::GetSessionStats(stats_tx)); match stats_rx.await.unwrap() { Ok(stats) => { - ctx.send_update_action(UpdateAction::SessionStats(stats)); + CTX.send_update_action(UpdateAction::SessionStats(stats)); } Err(err_message) => { - ctx.send_update_action(UpdateAction::Error(err_message)); + CTX.send_update_action(UpdateAction::Error(err_message)); } }; @@ -28,16 +28,16 @@ pub async fn stats(ctx: app::Ctx) { } } -pub async fn free_space(ctx: app::Ctx) { +pub async fn free_space() { let download_dir = loop { let (sess_tx, sess_rx) = oneshot::channel(); - ctx.send_torrent_action(TorrentAction::GetSessionGet(sess_tx)); + CTX.send_torrent_action(TorrentAction::GetSessionGet(sess_tx)); match sess_rx.await.unwrap() { Ok(sess) => { break sess.download_dir.leak(); } Err(err_message) => { - ctx.send_update_action(UpdateAction::Error(err_message)); + CTX.send_update_action(UpdateAction::Error(err_message)); tokio::time::sleep(Duration::from_secs(10)).await; } }; @@ -45,17 +45,17 @@ pub async fn free_space(ctx: app::Ctx) { loop { let (space_tx, space_rx) = oneshot::channel(); - ctx.send_torrent_action(TorrentAction::GetFreeSpace( + CTX.send_torrent_action(TorrentAction::GetFreeSpace( download_dir.to_string(), space_tx, )); match space_rx.await.unwrap() { Ok(free_space) => { - ctx.send_update_action(UpdateAction::FreeSpace(Arc::new(free_space))); + CTX.send_update_action(UpdateAction::FreeSpace(Arc::new(free_space))); } Err(err_message) => { - ctx.send_update_action(UpdateAction::Error(err_message)); + CTX.send_update_action(UpdateAction::Error(err_message)); } } @@ -63,7 +63,7 @@ pub async fn free_space(ctx: app::Ctx) { } } -pub async fn torrents(ctx: app::Ctx) { +pub async fn torrents() { loop { let fields = vec![ TorrentGetField::Id, @@ -87,14 +87,14 @@ pub async fn torrents(ctx: app::Ctx) { TorrentGetField::Labels, ]; let (torrents_tx, torrents_rx) = oneshot::channel(); - ctx.send_torrent_action(TorrentAction::GetTorrents(fields, torrents_tx)); + CTX.send_torrent_action(TorrentAction::GetTorrents(fields, torrents_tx)); match torrents_rx.await.unwrap() { Ok(torrents) => { - ctx.send_update_action(UpdateAction::UpdateTorrents(torrents)); + CTX.send_update_action(UpdateAction::UpdateTorrents(torrents)); } Err(err_message) => { - ctx.send_update_action(UpdateAction::Error(err_message)); + CTX.send_update_action(UpdateAction::Error(err_message)); } }; diff --git a/rm-main/src/tui/app.rs b/rm-main/src/tui/app.rs index 064ed40..58b5a0e 100644 --- a/rm-main/src/tui/app.rs +++ b/rm-main/src/tui/app.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::sync::{LazyLock, Mutex}; use crate::{ transmission::{self, TorrentAction}, @@ -9,47 +9,56 @@ use intuitils::Terminal; use rm_config::CONFIG; use rm_shared::action::{Action, UpdateAction}; -use anyhow::{Error, Result}; +use anyhow::Result; use crossterm::event::{Event, KeyCode, KeyModifiers}; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use transmission_rpc::{types::SessionGet, TransClient}; +use tokio::sync::mpsc::{self, unbounded_channel, UnboundedReceiver, UnboundedSender}; use super::main_window::{CurrentTab, MainWindow}; +pub static CTX: LazyLock = LazyLock::new(|| CTX_RAW.0.clone()); + +static CTX_RAW: LazyLock<( + Ctx, + Mutex< + Option<( + UnboundedReceiver, + UnboundedReceiver, + UnboundedReceiver, + )>, + >, +)> = LazyLock::new(|| { + let (ctx, act_rx, upd_rx, tor_rx) = Ctx::new(); + (ctx, Mutex::new(Some((act_rx, upd_rx, tor_rx)))) +}); + #[derive(Clone)] pub struct Ctx { - pub session_info: Arc, action_tx: UnboundedSender, update_tx: UnboundedSender, trans_tx: UnboundedSender, } impl Ctx { - async fn new( - client: &mut TransClient, - action_tx: UnboundedSender, - update_tx: UnboundedSender, - trans_tx: UnboundedSender, - ) -> Result { - let response = client.session_get().await; - match response { - Ok(res) => { - let session_info = Arc::new(res.arguments); - Ok(Self { - action_tx, - trans_tx, - update_tx, - session_info, - }) - } - Err(e) => { - let config_path = CONFIG.directories.main_path; - Err(Error::msg(format!( - "{e}\nIs the connection info in {:?} correct?", - config_path - ))) - } - } + fn new() -> ( + Self, + UnboundedReceiver, + UnboundedReceiver, + UnboundedReceiver, + ) { + let (action_tx, action_rx) = unbounded_channel(); + let (update_tx, update_rx) = unbounded_channel(); + let (trans_tx, trans_rx) = unbounded_channel(); + + ( + Self { + action_tx, + update_tx, + trans_tx, + }, + action_rx, + update_rx, + trans_rx, + ) } pub(crate) fn send_action(&self, action: Action) { @@ -67,7 +76,6 @@ impl Ctx { pub struct App { should_quit: bool, - ctx: Ctx, action_rx: UnboundedReceiver, update_rx: UnboundedReceiver, main_window: MainWindow, @@ -76,22 +84,26 @@ pub struct App { impl App { pub async fn new() -> Result { - let (action_tx, action_rx) = mpsc::unbounded_channel(); - let (update_tx, update_rx) = mpsc::unbounded_channel(); - - let mut client = transmission::utils::new_client(); + let client = transmission::utils::new_client(); - let (trans_tx, trans_rx) = mpsc::unbounded_channel(); - let ctx = Ctx::new(&mut client, action_tx.clone(), update_tx.clone(), trans_tx).await?; + let (action_rx, update_rx, torrent_rx) = CTX_RAW + .1 + .lock() + .unwrap() + .take() + .expect("it wasn't taken before"); - tokio::spawn(transmission::action_handler(client, trans_rx, update_tx)); + tokio::spawn(transmission::action_handler( + client, + torrent_rx, + CTX.update_tx.clone(), + )); Ok(Self { should_quit: false, - main_window: MainWindow::new(ctx.clone()), + main_window: MainWindow::new(), action_rx, update_rx, - ctx, mode: Mode::Normal, }) } @@ -123,7 +135,7 @@ impl App { _ = tick_action => self.tick(), event = tui_event => { - event_to_action(&self.ctx, self.mode, current_tab, event.unwrap()); + event_to_action(self.mode, current_tab, event.unwrap()); }, update_action = update_action => self.handle_update_action(update_action.unwrap()).await, @@ -177,7 +189,7 @@ impl App { _ => self.main_window.handle_update_action(action), }; - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn tick(&mut self) { @@ -191,23 +203,23 @@ pub enum Mode { Normal, } -pub fn event_to_action(ctx: &Ctx, mode: Mode, current_tab: CurrentTab, event: Event) { +pub fn event_to_action(mode: Mode, current_tab: CurrentTab, event: Event) { // Handle CTRL+C first if let Event::Key(key_event) = event { if key_event.modifiers == KeyModifiers::CONTROL && (key_event.code == KeyCode::Char('c') || key_event.code == KeyCode::Char('C')) { - ctx.send_action(Action::HardQuit); + CTX.send_action(Action::HardQuit); } } match event { - Event::Key(key) if mode == Mode::Input => ctx.send_action(Action::Input(key)), + Event::Key(key) if mode == Mode::Input => CTX.send_action(Action::Input(key)), Event::Mouse(mouse_event) => match mouse_event.kind { crossterm::event::MouseEventKind::ScrollDown => { - ctx.send_action(Action::ScrollDownBy(3)) + CTX.send_action(Action::ScrollDownBy(3)) } - crossterm::event::MouseEventKind::ScrollUp => ctx.send_action(Action::ScrollUpBy(3)), + crossterm::event::MouseEventKind::ScrollUp => CTX.send_action(Action::ScrollUpBy(3)), _ => (), }, Event::Key(key) => { @@ -236,12 +248,12 @@ pub fn event_to_action(ctx: &Ctx, mode: Mode, current_tab: CurrentTab, event: Ev for keymap in keymaps { if let Some(action) = keymap.get(&keybinding).cloned() { - ctx.send_action(action); + CTX.send_action(action); return; } } } - Event::Resize(_, _) => ctx.send_action(Action::Render), + Event::Resize(_, _) => CTX.send_action(Action::Render), _ => (), } } diff --git a/rm-main/src/tui/global_popups/help.rs b/rm-main/src/tui/global_popups/help.rs index 8f88890..d3a147f 100644 --- a/rm-main/src/tui/global_popups/help.rs +++ b/rm-main/src/tui/global_popups/help.rs @@ -10,7 +10,7 @@ use rm_config::CONFIG; use rm_shared::action::Action; use crate::tui::{ - app, + app::CTX, components::{popup_block_with_close_highlight, popup_rects, Component, ComponentAction}, }; @@ -25,7 +25,6 @@ macro_rules! add_line { } pub struct HelpPopup { - ctx: app::Ctx, scroll: Option, global_keys: Vec<(String, &'static str)>, torrent_keys: Vec<(String, &'static str)>, @@ -51,7 +50,7 @@ impl Scroll { } impl HelpPopup { - pub fn new(ctx: app::Ctx) -> Self { + pub fn new() -> Self { fn override_keycode(key: KeyCode) -> Option> { match key { KeyCode::Left => Some(CONFIG.icons.arrow_left.as_str().into()), @@ -99,7 +98,6 @@ impl HelpPopup { debug_assert!(max_key_len > 0); debug_assert!(max_line_len > 0); Self { - ctx, scroll: None, global_keys, torrent_keys, @@ -117,7 +115,7 @@ impl HelpPopup { scroll.position = scroll.position.saturating_add(1); scroll.state.next(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing } @@ -126,7 +124,7 @@ impl HelpPopup { if let Some(scroll) = &mut self.scroll { scroll.position = scroll.position.saturating_sub(1); scroll.state.prev(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing } @@ -135,7 +133,7 @@ impl HelpPopup { if let Some(scroll) = &mut self.scroll { scroll.position = scroll.position_max; scroll.state.last(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing } @@ -144,7 +142,7 @@ impl HelpPopup { if let Some(scroll) = &mut self.scroll { scroll.position = 0; scroll.state.first(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing } diff --git a/rm-main/src/tui/global_popups/mod.rs b/rm-main/src/tui/global_popups/mod.rs index c9815ab..0fa94de 100644 --- a/rm-main/src/tui/global_popups/mod.rs +++ b/rm-main/src/tui/global_popups/mod.rs @@ -8,22 +8,21 @@ pub use help::HelpPopup; use rm_shared::action::Action; -use crate::tui::app; - -use super::components::{Component, ComponentAction}; +use super::{ + app::CTX, + components::{Component, ComponentAction}, +}; pub(super) struct GlobalPopupManager { pub error_popup: Option, pub help_popup: Option, - ctx: app::Ctx, } impl GlobalPopupManager { - pub fn new(ctx: app::Ctx) -> Self { + pub fn new() -> Self { Self { error_popup: None, help_popup: None, - ctx, } } @@ -35,7 +34,7 @@ impl GlobalPopupManager { if self.help_popup.is_some() { self.help_popup = None; } else { - self.help_popup = Some(HelpPopup::new(self.ctx.clone())); + self.help_popup = Some(HelpPopup::new()); } } @@ -43,12 +42,12 @@ impl GlobalPopupManager { if let Some(popup) = &mut self.error_popup { if popup.handle_actions(action).is_quit() { self.error_popup = None; - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } else if let Some(popup) = &mut self.help_popup { if popup.handle_actions(action).is_quit() { self.help_popup = None; - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } } @@ -59,7 +58,7 @@ impl Component for GlobalPopupManager { use Action as A; if action == A::ShowHelp { self.toggle_help(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); return ComponentAction::Nothing; } diff --git a/rm-main/src/tui/main_window.rs b/rm-main/src/tui/main_window.rs index ac0a766..74b2ca7 100644 --- a/rm-main/src/tui/main_window.rs +++ b/rm-main/src/tui/main_window.rs @@ -6,6 +6,8 @@ use ratatui::prelude::*; use rm_config::CONFIG; use rm_shared::action::{Action, UpdateAction}; +use crate::tui::app::CTX; + use super::{ app, components::{Component, ComponentAction}, @@ -39,17 +41,15 @@ pub struct MainWindow { torrents_tab: TorrentsTab, search_tab: SearchTab, global_popup_manager: GlobalPopupManager, - ctx: app::Ctx, } impl MainWindow { - pub fn new(ctx: app::Ctx) -> Self { + pub fn new() -> Self { Self { tabs: TabsState::new(vec![CurrentTab::Torrents, CurrentTab::Search]), - torrents_tab: TorrentsTab::new(ctx.clone()), - search_tab: SearchTab::new(ctx.clone()), - global_popup_manager: GlobalPopupManager::new(ctx.clone()), - ctx, + torrents_tab: TorrentsTab::new(), + search_tab: SearchTab::new(), + global_popup_manager: GlobalPopupManager::new(), } } } @@ -68,13 +68,13 @@ impl Component for MainWindow { A::Left | A::ChangeTab(1) => { if self.tabs.current() != CurrentTab::Torrents { self.tabs.set(1); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } A::Right | A::ChangeTab(2) => { if self.tabs.current() != CurrentTab::Search { self.tabs.set(2); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } _ if self.tabs.current() == CurrentTab::Torrents => { diff --git a/rm-main/src/tui/tabs/search/bottom_bar.rs b/rm-main/src/tui/tabs/search/bottom_bar.rs index f584d24..b9b383e 100644 --- a/rm-main/src/tui/tabs/search/bottom_bar.rs +++ b/rm-main/src/tui/tabs/search/bottom_bar.rs @@ -10,7 +10,7 @@ use rm_shared::action::{Action, UpdateAction}; use throbber_widgets_tui::ThrobberState; use crate::tui::{ - app, + app::CTX, components::{keybinding_style, Component, ComponentAction}, tabs::torrents::tasks, }; @@ -20,21 +20,19 @@ use super::{ConfiguredProvider, ProviderState}; pub struct BottomBar { pub search_state: SearchState, pub task: Option, - ctx: app::Ctx, } impl BottomBar { - pub fn new(ctx: app::Ctx, providers: &Vec) -> Self { + pub fn new(providers: &Vec) -> Self { Self { - search_state: SearchState::new(ctx.clone(), providers), - ctx, + search_state: SearchState::new(providers), task: None, } } pub fn add_magnet(&mut self, magnet: impl Into) { - self.task = Some(tasks::AddMagnet::new(self.ctx.clone()).magnet(magnet)); - self.ctx.send_update_action(UpdateAction::SwitchToInputMode); + self.task = Some(tasks::AddMagnet::new().magnet(magnet)); + CTX.send_update_action(UpdateAction::SwitchToInputMode); } pub fn requires_input(&self) -> bool { @@ -55,8 +53,7 @@ impl Component for BottomBar { if let Some(task) = &mut self.task { if task.handle_actions(action).is_quit() { self.task = None; - self.ctx - .send_update_action(UpdateAction::SwitchToNormalMode); + CTX.send_update_action(UpdateAction::SwitchToNormalMode); }; } @@ -73,7 +70,6 @@ impl Component for BottomBar { } pub struct SearchState { - ctx: app::Ctx, stage: SearchStage, providers_finished: u8, providers_errored: u8, @@ -89,7 +85,7 @@ enum SearchStage { } impl SearchState { - fn new(ctx: app::Ctx, providers: &Vec) -> Self { + fn new(providers: &Vec) -> Self { let mut providers_count = 0u8; for provider in providers { if provider.enabled { @@ -98,7 +94,6 @@ impl SearchState { } Self { - ctx, stage: SearchStage::Nothing, providers_errored: 0, providers_finished: 0, @@ -190,7 +185,7 @@ impl Component for SearchState { fn tick(&mut self) { if let SearchStage::Searching(state) = &mut self.stage { state.calc_next(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } } diff --git a/rm-main/src/tui/tabs/search/mod.rs b/rm-main/src/tui/tabs/search/mod.rs index 81843c6..057c0f5 100644 --- a/rm-main/src/tui/tabs/search/mod.rs +++ b/rm-main/src/tui/tabs/search/mod.rs @@ -19,7 +19,7 @@ use tokio::sync::mpsc::{self, UnboundedSender}; use tui_input::{backend::crossterm::to_input_request, Input}; use crate::tui::{ - app, + app::CTX, components::{Component, ComponentAction, GenericTable}, }; use rm_shared::{ @@ -42,11 +42,10 @@ pub(crate) struct SearchTab { configured_providers: Vec, bottom_bar: BottomBar, currently_displaying_no: u16, - ctx: app::Ctx, } impl SearchTab { - pub(crate) fn new(ctx: app::Ctx) -> Self { + pub(crate) fn new() -> Self { let (search_query_tx, mut search_query_rx) = mpsc::unbounded_channel::(); let table = GenericTable::new(vec![]); @@ -66,15 +65,14 @@ impl SearchTab { } } - let bottom_bar = BottomBar::new(ctx.clone(), &configured_providers); + let bottom_bar = BottomBar::new(&configured_providers); tokio::task::spawn({ - let ctx = ctx.clone(); let configured_providers = configured_providers.clone(); async move { let client = Client::new(); while let Some(phrase) = search_query_rx.recv().await { - ctx.send_update_action(UpdateAction::SearchStarted); + CTX.send_update_action(UpdateAction::SearchStarted); let mut futures = FuturesUnordered::new(); for configured_provider in &configured_providers { if configured_provider.enabled { @@ -91,9 +89,9 @@ impl SearchTab { if let Some(result) = maybe_result { match result { Ok(response) => { - ctx.send_update_action(UpdateAction::ProviderResult(response)) + CTX.send_update_action(UpdateAction::ProviderResult(response)) }, - Err(e) => ctx.send_update_action(UpdateAction::ProviderError(e)), + Err(e) => CTX.send_update_action(UpdateAction::ProviderError(e)), } } else { // This means that the whole search is finished. @@ -103,7 +101,7 @@ impl SearchTab { }, }; } - ctx.send_update_action(UpdateAction::SearchFinished); + CTX.send_update_action(UpdateAction::SearchFinished); } } }); @@ -115,8 +113,7 @@ impl SearchTab { bottom_bar, search_query_rx: search_query_tx, currently_displaying_no: 0, - popup_manager: PopupManager::new(ctx.clone()), - ctx, + popup_manager: PopupManager::new(), configured_providers, } } @@ -136,7 +133,7 @@ impl SearchTab { } else { self.focus = SearchTabFocus::Search; } - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn add_magnet(&mut self) { @@ -154,20 +151,18 @@ impl SearchTab { if !self.input.to_string().is_empty() { self.search_query_rx.send(self.input.to_string()).unwrap(); self.focus = SearchTabFocus::List; - self.ctx - .send_update_action(UpdateAction::SwitchToNormalMode); + CTX.send_update_action(UpdateAction::SwitchToNormalMode); } } KeyCode::Esc => { self.focus = SearchTabFocus::List; - self.ctx - .send_update_action(UpdateAction::SwitchToNormalMode); + CTX.send_update_action(UpdateAction::SwitchToNormalMode); } _ => { let event = Event::Key(input); if let Some(req) = to_input_request(&event) { self.input.handle(req); - self.ctx.send_action(A::Render); + CTX.send_action(A::Render); } } } @@ -175,49 +170,49 @@ impl SearchTab { fn start_search(&mut self) { self.focus = SearchTabFocus::Search; - self.ctx.send_update_action(UpdateAction::SwitchToInputMode); + CTX.send_update_action(UpdateAction::SwitchToInputMode); } fn next_torrent(&mut self) { self.table.next(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn previous_torrent(&mut self) { self.table.previous(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_up_by(&mut self, amount: u8) { self.table.scroll_up_by(usize::from(amount)); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_down_by(&mut self, amount: u8) { self.table.scroll_down_by(usize::from(amount)); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_down_page(&mut self) { self.table .scroll_down_by(usize::from(self.currently_displaying_no)); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_up_page(&mut self) { self.table .scroll_up_by(usize::from(self.currently_displaying_no)); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_to_end(&mut self) { self.table.select_last(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_to_home(&mut self) { self.table.select_first(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn xdg_open(&mut self) { @@ -229,7 +224,7 @@ impl SearchTab { fn show_providers_info(&mut self) { self.popup_manager .show_providers_info_popup(self.configured_providers.clone()); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn providers_searching(&mut self) { @@ -275,11 +270,11 @@ impl Component for SearchTab { } if action.is_quit() { - self.ctx.send_action(Action::HardQuit); + CTX.send_action(Action::HardQuit); } match action { - A::Quit => self.ctx.send_action(Action::Quit), + A::Quit => CTX.send_action(Action::Quit), A::Search => self.start_search(), A::ChangeFocus => self.change_focus(), A::Input(_) if self.bottom_bar.requires_input() => { diff --git a/rm-main/src/tui/tabs/search/popups/mod.rs b/rm-main/src/tui/tabs/search/popups/mod.rs index c49f544..499b7ad 100644 --- a/rm-main/src/tui/tabs/search/popups/mod.rs +++ b/rm-main/src/tui/tabs/search/popups/mod.rs @@ -1,18 +1,16 @@ mod providers; +use crate::tui::app::CTX; +use crate::tui::components::Component; +use crate::tui::components::ComponentAction; use providers::ProvidersPopup; use ratatui::prelude::*; use ratatui::Frame; use rm_shared::action::Action; -use crate::tui::app; -use crate::tui::components::Component; -use crate::tui::components::ComponentAction; - use super::ConfiguredProvider; pub struct PopupManager { - ctx: app::Ctx, pub current_popup: Option, } @@ -21,9 +19,8 @@ pub enum CurrentPopup { } impl PopupManager { - pub const fn new(ctx: app::Ctx) -> Self { + pub const fn new() -> Self { Self { - ctx, current_popup: None, } } @@ -52,7 +49,7 @@ impl Component for PopupManager { CurrentPopup::Providers(popup) => { if popup.handle_actions(action).is_quit() { self.close_popup(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } } diff --git a/rm-main/src/tui/tabs/torrents/mod.rs b/rm-main/src/tui/tabs/torrents/mod.rs index 1eaae76..da5a842 100644 --- a/rm-main/src/tui/tabs/torrents/mod.rs +++ b/rm-main/src/tui/tabs/torrents/mod.rs @@ -5,8 +5,10 @@ pub mod table_manager; pub mod task_manager; pub mod tasks; +use std::sync::OnceLock; + use crate::transmission::TorrentAction; -use crate::tui::app; +use crate::tui::app::CTX; use crate::tui::components::{Component, ComponentAction}; use popups::details::DetailsPopup; @@ -17,7 +19,7 @@ use rm_config::CONFIG; use rm_shared::status_task::StatusTask; use rustmission_torrent::RustmissionTorrent; use tasks::TorrentSelection; -use transmission_rpc::types::{Id, TorrentStatus}; +use transmission_rpc::types::{Id, SessionGet, TorrentStatus}; use crate::transmission; use rm_shared::action::{Action, ErrorMessage, UpdateAction}; @@ -28,8 +30,9 @@ use self::popups::{CurrentPopup, PopupManager}; use self::table_manager::TableManager; use self::task_manager::TaskManager; +pub static SESSION_GET: OnceLock = OnceLock::new(); + pub struct TorrentsTab { - ctx: app::Ctx, table_manager: TableManager, popup_manager: PopupManager, task_manager: TaskManager, @@ -37,20 +40,19 @@ pub struct TorrentsTab { } impl TorrentsTab { - pub fn new(ctx: app::Ctx) -> Self { + pub fn new() -> Self { let table_manager = TableManager::new(); let bottom_stats = BottomStats::new(); - tokio::spawn(transmission::fetchers::stats(ctx.clone())); - tokio::spawn(transmission::fetchers::torrents(ctx.clone())); - tokio::spawn(transmission::fetchers::free_space(ctx.clone())); + tokio::spawn(transmission::fetchers::stats()); + tokio::spawn(transmission::fetchers::torrents()); + tokio::spawn(transmission::fetchers::free_space()); Self { bottom_stats, - task_manager: TaskManager::new(ctx.clone()), + task_manager: TaskManager::new(), table_manager, - popup_manager: PopupManager::new(ctx.clone()), - ctx, + popup_manager: PopupManager::new(), } } } @@ -82,24 +84,24 @@ impl Component for TorrentsTab { A::Close => { self.table_manager.leave_sorting(); self.task_manager.default(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } A::MoveToColumnLeft => { self.table_manager.move_to_column_left(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } A::MoveToColumnRight => { self.table_manager.move_to_column_right(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } A::Down | A::Up => { self.table_manager.reverse_sort(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } A::Confirm => { self.table_manager.apply_sort(); self.task_manager.default(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } _ => (), } @@ -114,12 +116,12 @@ impl Component for TorrentsTab { .for_each(|t| t.is_selected = false); self.table_manager.selected_torrents_ids.drain(..); self.task_manager.default(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); return ComponentAction::Nothing; } if action.is_quit() { - self.ctx.send_action(Action::HardQuit); + CTX.send_action(Action::HardQuit); return ComponentAction::Nothing; } @@ -143,7 +145,7 @@ impl Component for TorrentsTab { } else { self.task_manager.default(); } - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } A::Pause => self.pause_current_torrent(), A::Delete => { @@ -167,8 +169,10 @@ impl Component for TorrentsTab { ), A::MoveTorrent => { if let Some(selection) = self.get_currently_selected() { - self.task_manager - .move_torrent(selection, self.ctx.session_info.download_dir.clone()); + if let Some(session_info) = SESSION_GET.get() { + self.task_manager + .move_torrent(selection, session_info.download_dir.clone()); + } } } A::ChangeCategory => { @@ -180,7 +184,7 @@ impl Component for TorrentsTab { A::MoveToColumnLeft | A::MoveToColumnRight => { self.table_manager.enter_sorting_selection(); self.task_manager.sort(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } other => { self.task_manager.handle_actions(other); @@ -242,8 +246,7 @@ impl Component for TorrentsTab { } else { self.task_manager.default(); } - self.ctx - .send_update_action(UpdateAction::SwitchToNormalMode); + CTX.send_update_action(UpdateAction::SwitchToNormalMode); } other => self.task_manager.handle_update_action(other), } @@ -353,17 +356,17 @@ impl TorrentsTab { fn show_files_popup(&mut self) { if let Some(highlighted_torrent) = self.table_manager.current_torrent() { - let popup = FilesPopup::new(self.ctx.clone(), highlighted_torrent.id.clone()); + let popup = FilesPopup::new(highlighted_torrent.id.clone()); self.popup_manager.show_popup(CurrentPopup::Files(popup)); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } fn show_details_popup(&mut self) { if let Some(highlighted_torrent) = self.table_manager.current_torrent() { - let popup = DetailsPopup::new(self.ctx.clone(), highlighted_torrent.clone()); + let popup = DetailsPopup::new(highlighted_torrent.clone()); self.popup_manager.show_popup(CurrentPopup::Details(popup)); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } @@ -371,7 +374,7 @@ impl TorrentsTab { if let Some(stats) = &self.bottom_stats.stats { let popup = StatisticsPopup::new(stats.clone()); self.popup_manager.show_popup(CurrentPopup::Stats(popup)); - self.ctx.send_action(Action::Render) + CTX.send_action(Action::Render) } } @@ -379,28 +382,28 @@ impl TorrentsTab { self.table_manager.table.previous(); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn next_torrent(&mut self) { self.table_manager.table.next(); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_up_by(&mut self, amount: u8) { self.table_manager.table.scroll_up_by(usize::from(amount)); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_down_by(&mut self, amount: u8) { self.table_manager.table.scroll_down_by(usize::from(amount)); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_page_down(&mut self) { @@ -408,7 +411,7 @@ impl TorrentsTab { self.table_manager.table.scroll_down_by(scroll_by as usize); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn scroll_page_up(&mut self) { @@ -416,21 +419,21 @@ impl TorrentsTab { self.table_manager.table.scroll_up_by(scroll_by as usize); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn select_first(&mut self) { self.table_manager.table.select_first(); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn select_last(&mut self) { self.table_manager.table.select_last(); self.bottom_stats .update_selected_indicator(&self.table_manager); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } fn pause_current_torrent(&mut self) { @@ -438,16 +441,14 @@ impl TorrentsTab { let torrent_id = torrent.id.clone(); match torrent.status() { TorrentStatus::Stopped => { - self.ctx - .send_torrent_action(TorrentAction::Start(vec![torrent_id])); + CTX.send_torrent_action(TorrentAction::Start(vec![torrent_id])); torrent.update_status(TorrentStatus::Downloading); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } _ => { - self.ctx - .send_torrent_action(TorrentAction::Stop(vec![torrent_id])); + CTX.send_torrent_action(TorrentAction::Stop(vec![torrent_id])); torrent.update_status(TorrentStatus::Stopped); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } } @@ -457,11 +458,9 @@ impl TorrentsTab { if let Some(torrent) = self.table_manager.current_torrent() { let torrent_location = torrent.torrent_location(); match open::that_detached(&torrent_location) { - Ok(()) => self - .ctx - .send_update_action(UpdateAction::StatusTaskSetSuccess(StatusTask::new_open( - torrent_location, - ))), + Ok(()) => CTX.send_update_action(UpdateAction::StatusTaskSetSuccess( + StatusTask::new_open(torrent_location), + )), Err(err) => { let desc = format!( "Encountered an error while trying to open \"{}\"", @@ -472,8 +471,7 @@ impl TorrentsTab { desc, Box::new(err), ); - self.ctx - .send_update_action(UpdateAction::Error(Box::new(err_msg))); + CTX.send_update_action(UpdateAction::Error(Box::new(err_msg))); } }; } diff --git a/rm-main/src/tui/tabs/torrents/popups/details.rs b/rm-main/src/tui/tabs/torrents/popups/details.rs index 564f852..4abccfe 100644 --- a/rm-main/src/tui/tabs/torrents/popups/details.rs +++ b/rm-main/src/tui/tabs/torrents/popups/details.rs @@ -7,20 +7,19 @@ use rm_shared::{action::Action, utils::bytes_to_human_format}; use style::Styled; use crate::tui::{ - app, + app::CTX, components::{keybinding_style, popup_close_button_highlight, Component, ComponentAction}, main_window::centered_rect, tabs::torrents::rustmission_torrent::{CategoryType, RustmissionTorrent}, }; pub struct DetailsPopup { - ctx: app::Ctx, torrent: RustmissionTorrent, } impl DetailsPopup { - pub fn new(ctx: app::Ctx, torrent: RustmissionTorrent) -> Self { - Self { ctx, torrent } + pub fn new(torrent: RustmissionTorrent) -> Self { + Self { torrent } } } @@ -30,23 +29,23 @@ impl Component for DetailsPopup { _ if action.is_soft_quit() => ComponentAction::Quit, Action::Confirm => ComponentAction::Quit, Action::Delete => { - self.ctx.send_action(Action::Delete); + CTX.send_action(Action::Delete); ComponentAction::Quit } Action::ShowFiles => { - self.ctx.send_action(Action::ShowFiles); + CTX.send_action(Action::ShowFiles); ComponentAction::Quit } Action::Rename => { - self.ctx.send_action(Action::Rename); + CTX.send_action(Action::Rename); ComponentAction::Quit } Action::ChangeCategory => { - self.ctx.send_action(Action::ChangeCategory); + CTX.send_action(Action::ChangeCategory); ComponentAction::Quit } Action::MoveTorrent => { - self.ctx.send_action(Action::MoveTorrent); + CTX.send_action(Action::MoveTorrent); ComponentAction::Quit } _ => ComponentAction::Nothing, diff --git a/rm-main/src/tui/tabs/torrents/popups/files.rs b/rm-main/src/tui/tabs/torrents/popups/files.rs index 81bd937..b2157a1 100644 --- a/rm-main/src/tui/tabs/torrents/popups/files.rs +++ b/rm-main/src/tui/tabs/torrents/popups/files.rs @@ -16,7 +16,7 @@ use tui_tree_widget::{Tree, TreeItem, TreeState}; use crate::{ transmission::TorrentAction, tui::{ - app, + app::CTX, components::{ keybinding_style, popup_block, popup_close_button, popup_close_button_highlight, popup_rects, Component, ComponentAction, @@ -29,7 +29,6 @@ use rm_shared::{ }; pub struct FilesPopup { - ctx: app::Ctx, torrent: Option, torrent_id: Id, tree_state: TreeState, @@ -39,22 +38,22 @@ pub struct FilesPopup { torrent_info_task_handle: JoinHandle<()>, } -async fn fetch_new_files(ctx: app::Ctx, torrent_id: Id) { +async fn fetch_new_files(torrent_id: Id) { loop { let (torrent_tx, torrent_rx) = oneshot::channel(); - ctx.send_torrent_action(TorrentAction::GetTorrentsById( + CTX.send_torrent_action(TorrentAction::GetTorrentsById( vec![torrent_id.clone()], torrent_tx, )); match torrent_rx.await.unwrap() { Ok(mut torrents) => { - ctx.send_update_action(UpdateAction::UpdateCurrentTorrent(Box::new( + CTX.send_update_action(UpdateAction::UpdateCurrentTorrent(Box::new( torrents.pop().unwrap(), ))); } Err(err_message) => { - ctx.send_update_action(UpdateAction::Error(err_message)); + CTX.send_update_action(UpdateAction::Error(err_message)); } }; @@ -69,16 +68,14 @@ enum CurrentFocus { } impl FilesPopup { - pub fn new(ctx: app::Ctx, torrent_id: Id) -> Self { + pub fn new(torrent_id: Id) -> Self { let torrent = None; let tree_state = TreeState::default(); let tree = Node::new(); - let torrent_info_task_handle = - tokio::task::spawn(fetch_new_files(ctx.clone(), torrent_id.clone())); + let torrent_info_task_handle = tokio::task::spawn(fetch_new_files(torrent_id.clone())); Self { - ctx, torrent, tree_state, tree, @@ -116,7 +113,7 @@ impl Component for FilesPopup { } (A::ChangeFocus, _) => { self.switch_focus(); - self.ctx.send_action(A::Render); + CTX.send_action(A::Render); } (A::Confirm, CurrentFocus::CloseButton) => { self.torrent_info_task_handle.abort(); @@ -137,7 +134,7 @@ impl Component for FilesPopup { if selected_ids.is_empty() { self.tree_state.toggle_selected(); - self.ctx.send_action(A::Render); + CTX.send_action(A::Render); return ComponentAction::Nothing; } @@ -180,22 +177,22 @@ impl Component for FilesPopup { } }; - self.ctx.send_torrent_action(TorrentAction::SetArgs( + CTX.send_torrent_action(TorrentAction::SetArgs( Box::new(args), Some(vec![self.torrent_id.clone()]), )); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } (A::Up | A::ScrollUpBy(_), CurrentFocus::Files) => { self.tree_state.key_up(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } (A::Down | A::ScrollDownBy(_), CurrentFocus::Files) => { self.tree_state.key_down(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } (A::XdgOpen, CurrentFocus::Files) => { if let Some(torrent) = &self.torrent { @@ -216,18 +213,15 @@ impl Component for FilesPopup { let path = format!("{}/{}", torrent.download_dir.as_ref().unwrap(), sub_path,); match open::that_detached(&path) { - Ok(()) => self - .ctx - .send_update_action(UpdateAction::StatusTaskSetSuccess( - StatusTask::new_open(&path), - )), + Ok(()) => CTX.send_update_action(UpdateAction::StatusTaskSetSuccess( + StatusTask::new_open(&path), + )), Err(err) => { let desc = format!("An error occured while trying to open \"{}\"", path); let err_msg = ErrorMessage::new("Failed to open a file", desc, Box::new(err)); - self.ctx - .send_update_action(UpdateAction::Error(Box::new(err_msg))); + CTX.send_update_action(UpdateAction::Error(Box::new(err_msg))); } }; } diff --git a/rm-main/src/tui/tabs/torrents/popups/mod.rs b/rm-main/src/tui/tabs/torrents/popups/mod.rs index 9b24819..20dfa26 100644 --- a/rm-main/src/tui/tabs/torrents/popups/mod.rs +++ b/rm-main/src/tui/tabs/torrents/popups/mod.rs @@ -1,5 +1,5 @@ use crate::tui::{ - app, + app::CTX, components::{Component, ComponentAction}, }; @@ -14,7 +14,6 @@ pub mod files; pub mod stats; pub struct PopupManager { - ctx: app::Ctx, pub current_popup: Option, } @@ -25,9 +24,8 @@ pub enum CurrentPopup { } impl PopupManager { - pub const fn new(ctx: app::Ctx) -> Self { + pub const fn new() -> Self { Self { - ctx, current_popup: None, } } @@ -56,7 +54,7 @@ impl Component for PopupManager { if should_close { self.close_popup(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } } ComponentAction::Nothing diff --git a/rm-main/src/tui/tabs/torrents/task_manager.rs b/rm-main/src/tui/tabs/torrents/task_manager.rs index 1b693d8..84c3973 100644 --- a/rm-main/src/tui/tabs/torrents/task_manager.rs +++ b/rm-main/src/tui/tabs/torrents/task_manager.rs @@ -9,14 +9,13 @@ use rm_shared::{ use transmission_rpc::types::Id; use crate::tui::{ - app, + app::CTX, components::{Component, ComponentAction}, }; use super::tasks::{self, CurrentTaskState, TorrentSelection}; pub struct TaskManager { - ctx: app::Ctx, current_task: CurrentTask, // TODO: // Put Default task in a seperate field and merge it with Status task @@ -28,10 +27,9 @@ pub struct TaskManager { } impl TaskManager { - pub fn new(ctx: app::Ctx) -> Self { + pub fn new() -> Self { Self { current_task: CurrentTask::Default(tasks::Default::new()), - ctx, } } } @@ -144,37 +142,33 @@ impl Component for TaskManager { impl TaskManager { pub fn add_magnet(&mut self) { - self.current_task = CurrentTask::AddMagnet(tasks::AddMagnet::new(self.ctx.clone())); - self.ctx.send_update_action(UpdateAction::SwitchToInputMode); + self.current_task = CurrentTask::AddMagnet(tasks::AddMagnet::new()); + CTX.send_update_action(UpdateAction::SwitchToInputMode); } pub fn search(&mut self, current_pattern: &Option) { - self.current_task = - CurrentTask::Filter(tasks::Filter::new(self.ctx.clone(), current_pattern)); - self.ctx.send_update_action(UpdateAction::SwitchToInputMode); + self.current_task = CurrentTask::Filter(tasks::Filter::new(current_pattern)); + 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); + self.current_task = CurrentTask::Rename(tasks::Rename::new(id, curr_name)); + 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); + self.current_task = CurrentTask::Delete(tasks::Delete::new(selection)); + CTX.send_update_action(UpdateAction::SwitchToInputMode); } pub fn move_torrent(&mut self, selection: TorrentSelection, current_dir: String) { - self.current_task = - CurrentTask::Move(tasks::Move::new(self.ctx.clone(), selection, current_dir)); - self.ctx.send_update_action(UpdateAction::SwitchToInputMode); + self.current_task = CurrentTask::Move(tasks::Move::new(selection, current_dir)); + CTX.send_update_action(UpdateAction::SwitchToInputMode); } pub fn change_category(&mut self, selection: TorrentSelection) { - self.current_task = - CurrentTask::ChangeCategory(tasks::ChangeCategory::new(self.ctx.clone(), selection)); - self.ctx.send_update_action(UpdateAction::SwitchToInputMode); + self.current_task = CurrentTask::ChangeCategory(tasks::ChangeCategory::new(selection)); + CTX.send_update_action(UpdateAction::SwitchToInputMode); } pub fn default(&mut self) { @@ -191,7 +185,6 @@ impl TaskManager { fn success_task(&mut self, task: StatusTask) { self.current_task = CurrentTask::Status(tasks::Status::new( - self.ctx.clone(), task, CurrentTaskState::Success(Instant::now()), )) @@ -203,13 +196,9 @@ impl TaskManager { } let state = ThrobberState::default(); - self.current_task = CurrentTask::Status(tasks::Status::new( - self.ctx.clone(), - task, - CurrentTaskState::Loading(state), - )); - self.ctx - .send_update_action(UpdateAction::SwitchToNormalMode); + self.current_task = + CurrentTask::Status(tasks::Status::new(task, CurrentTaskState::Loading(state))); + CTX.send_update_action(UpdateAction::SwitchToNormalMode); } fn cancel_task(&mut self) { @@ -217,7 +206,7 @@ impl TaskManager { return; } - self.ctx.send_update_action(UpdateAction::CancelTorrentTask); + CTX.send_update_action(UpdateAction::CancelTorrentTask); } pub fn is_status_task_in_progress(&self) -> bool { diff --git a/rm-main/src/tui/tabs/torrents/tasks/add_magnet.rs b/rm-main/src/tui/tabs/torrents/tasks/add_magnet.rs index e230611..5a82f40 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/add_magnet.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/add_magnet.rs @@ -5,8 +5,9 @@ use rm_config::CONFIG; use crate::{ transmission::TorrentAction, tui::{ - app, + app::CTX, components::{Component, ComponentAction, InputManager}, + tabs::torrents::SESSION_GET, }, }; use rm_shared::{ @@ -19,7 +20,6 @@ pub struct AddMagnet { input_category_mgr: InputManager, input_location_mgr: InputManager, stage: Stage, - ctx: app::Ctx, } enum Stage { @@ -33,17 +33,16 @@ const CATEGORY_PROMPT: &str = "Category (empty for default): "; const LOCATION_PROMPT: &str = "Directory: "; impl AddMagnet { - pub fn new(ctx: app::Ctx) -> Self { + pub fn new() -> Self { Self { input_magnet_mgr: InputManager::new(MAGNET_PROMPT.to_string()), input_category_mgr: InputManager::new(CATEGORY_PROMPT.to_string()) .autocompletions(CONFIG.categories.map.keys().cloned().collect()), input_location_mgr: InputManager::new_with_value( LOCATION_PROMPT.to_string(), - ctx.session_info.download_dir.clone(), + SESSION_GET.get().unwrap().download_dir.clone(), ), stage: Stage::Magnet, - ctx, } } @@ -73,7 +72,7 @@ impl AddMagnet { } else { self.stage = Stage::Category; } - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); return ComponentAction::Nothing; } @@ -82,7 +81,7 @@ impl AddMagnet { } if self.input_magnet_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing @@ -92,7 +91,7 @@ impl AddMagnet { if input.code == KeyCode::Enter { if self.input_category_mgr.text().is_empty() { self.stage = Stage::Location; - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); return ComponentAction::Nothing; } else if let Some(category) = CONFIG.categories.map.get(&self.input_category_mgr.text()) @@ -102,14 +101,14 @@ impl AddMagnet { category.default_dir.to_string(), ); self.stage = Stage::Location; - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); return ComponentAction::Nothing; } else { self.input_category_mgr.set_prompt(format!( "Category ({} not found): ", self.input_category_mgr.text() )); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); return ComponentAction::Nothing; }; } @@ -119,7 +118,7 @@ impl AddMagnet { } if self.input_category_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing @@ -138,17 +137,16 @@ impl AddMagnet { Some(self.input_location_mgr.text()), category, ); - self.ctx.send_torrent_action(torrent_action); + CTX.send_torrent_action(torrent_action); let task = StatusTask::new_add(self.input_magnet_mgr.text()); - self.ctx - .send_update_action(UpdateAction::StatusTaskSet(task)); + CTX.send_update_action(UpdateAction::StatusTaskSet(task)); ComponentAction::Quit } else if input.code == KeyCode::Esc { ComponentAction::Quit } else if self.input_location_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); ComponentAction::Nothing } else { ComponentAction::Nothing diff --git a/rm-main/src/tui/tabs/torrents/tasks/change_category.rs b/rm-main/src/tui/tabs/torrents/tasks/change_category.rs index 8cd3e08..5b99740 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/change_category.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/change_category.rs @@ -9,7 +9,7 @@ use rm_shared::{ use crate::{ transmission::TorrentAction, tui::{ - app, + app::CTX, components::{Component, ComponentAction, InputManager}, }, }; @@ -18,7 +18,6 @@ use super::TorrentSelection; pub struct ChangeCategory { selection: TorrentSelection, - ctx: app::Ctx, category_input_mgr: InputManager, directory_input_mgr: InputManager, default_dir: Option, @@ -31,14 +30,13 @@ enum Stage { } impl ChangeCategory { - pub fn new(ctx: app::Ctx, selection: TorrentSelection) -> Self { + pub fn new(selection: TorrentSelection) -> Self { let prompt = "New category: ".to_string(); Self { selection, category_input_mgr: InputManager::new(prompt) .autocompletions(CONFIG.categories.map.keys().cloned().collect()), - ctx, directory_input_mgr: InputManager::new( "Move to category's default dir? (Y/n): ".into(), ), @@ -48,8 +46,7 @@ impl ChangeCategory { } fn send_status_task(&self) { let task = StatusTask::new_category(self.category_input_mgr.text()); - self.ctx - .send_update_action(UpdateAction::StatusTaskSet(task)); + CTX.send_update_action(UpdateAction::StatusTaskSet(task)); } fn set_stage_directory(&mut self, directory: String) { @@ -69,12 +66,12 @@ impl ChangeCategory { if self.directory_input_mgr.text().to_lowercase() == "y" || self.directory_input_mgr.text().is_empty() { - self.ctx.send_torrent_action(TorrentAction::ChangeCategory( + CTX.send_torrent_action(TorrentAction::ChangeCategory( self.selection.ids(), self.category_input_mgr.text(), )); - self.ctx.send_torrent_action(TorrentAction::Move( + CTX.send_torrent_action(TorrentAction::Move( self.selection.ids(), self.default_dir .take() @@ -85,7 +82,7 @@ impl ChangeCategory { return ComponentAction::Quit; } else if self.directory_input_mgr.text().to_lowercase() == "n" { - self.ctx.send_torrent_action(TorrentAction::ChangeCategory( + CTX.send_torrent_action(TorrentAction::ChangeCategory( self.selection.ids(), self.category_input_mgr.text(), )); @@ -101,7 +98,7 @@ impl ChangeCategory { } if self.directory_input_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing @@ -113,10 +110,10 @@ impl ChangeCategory { if let Some(config_category) = CONFIG.categories.map.get(&category) { self.set_stage_directory(config_category.default_dir.clone()); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); return ComponentAction::Nothing; } else { - self.ctx.send_torrent_action(TorrentAction::ChangeCategory( + CTX.send_torrent_action(TorrentAction::ChangeCategory( self.selection.ids(), category.clone(), )); @@ -130,7 +127,7 @@ impl ChangeCategory { } if self.category_input_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing diff --git a/rm-main/src/tui/tabs/torrents/tasks/delete_torrent.rs b/rm-main/src/tui/tabs/torrents/tasks/delete_torrent.rs index aa6dc72..9e0b39f 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/delete_torrent.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/delete_torrent.rs @@ -2,7 +2,7 @@ use crossterm::event::KeyCode; use ratatui::prelude::*; use crate::transmission::TorrentAction; -use crate::tui::app; +use crate::tui::app::CTX; use crate::tui::components::{Component, ComponentAction, InputManager}; use rm_shared::action::{Action, UpdateAction}; use rm_shared::status_task::StatusTask; @@ -13,27 +13,24 @@ pub struct Delete { delete_with_files: bool, torrents_to_delete: TorrentSelection, input_mgr: InputManager, - ctx: app::Ctx, } impl Delete { - pub fn new(ctx: app::Ctx, to_delete: TorrentSelection) -> Self { + pub fn new(to_delete: TorrentSelection) -> Self { let prompt = String::from("Delete selected with files? (Y/n) "); Self { delete_with_files: false, torrents_to_delete: to_delete, input_mgr: InputManager::new(prompt), - ctx, } } fn delete(&self) { if self.delete_with_files { - self.ctx - .send_torrent_action(TorrentAction::DelWithFiles(self.torrents_to_delete.ids())) + CTX.send_torrent_action(TorrentAction::DelWithFiles(self.torrents_to_delete.ids())) } else { - self.ctx.send_torrent_action(TorrentAction::DelWithoutFiles( + CTX.send_torrent_action(TorrentAction::DelWithoutFiles( self.torrents_to_delete.ids(), )) } @@ -43,8 +40,7 @@ impl Delete { TorrentSelection::Many(ids) => StatusTask::new_del(ids.len().to_string()), }; - self.ctx - .send_update_action(UpdateAction::StatusTaskSet(task)); + CTX.send_update_action(UpdateAction::StatusTaskSet(task)); } } @@ -67,7 +63,7 @@ impl Component for Delete { } if self.input_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing diff --git a/rm-main/src/tui/tabs/torrents/tasks/filter.rs b/rm-main/src/tui/tabs/torrents/tasks/filter.rs index 3114fa5..754db4e 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/filter.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/filter.rs @@ -4,20 +4,19 @@ use ratatui::prelude::*; use rm_shared::action::{Action, UpdateAction}; use crate::tui::{ - app, + app::CTX, components::{Component, ComponentAction, InputManager}, }; pub struct Filter { - ctx: app::Ctx, input: InputManager, } impl Filter { - pub fn new(ctx: app::Ctx, current_pattern: &Option) -> Self { + pub fn new(current_pattern: &Option) -> Self { let pattern = current_pattern.as_ref().cloned().unwrap_or_default(); let input = InputManager::new_with_value("Search: ".to_string(), pattern); - Self { ctx, input } + Self { input } } } @@ -27,12 +26,11 @@ impl Component for Filter { Action::Input(input) => { if matches!(input.code, KeyCode::Enter | KeyCode::Esc) { if self.input.text().is_empty() { - self.ctx.send_update_action(UpdateAction::SearchFilterClear); + CTX.send_update_action(UpdateAction::SearchFilterClear); } ComponentAction::Quit } else if self.input.handle_key(input).is_some() { - self.ctx - .send_update_action(UpdateAction::SearchFilterApply(self.input.text())); + CTX.send_update_action(UpdateAction::SearchFilterApply(self.input.text())); ComponentAction::Nothing } else { ComponentAction::Nothing diff --git a/rm-main/src/tui/tabs/torrents/tasks/move_torrent.rs b/rm-main/src/tui/tabs/torrents/tasks/move_torrent.rs index 4ebe4e2..08fe180 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/move_torrent.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/move_torrent.rs @@ -8,7 +8,7 @@ use rm_shared::{ use crate::{ transmission::TorrentAction, tui::{ - app, + app::{self, CTX}, components::{Component, ComponentAction, InputManager}, }, }; @@ -17,18 +17,16 @@ use super::TorrentSelection; pub struct Move { selection: TorrentSelection, - ctx: app::Ctx, input_mgr: InputManager, } impl Move { - pub fn new(ctx: app::Ctx, selection: TorrentSelection, existing_location: String) -> Self { + pub fn new(selection: TorrentSelection, existing_location: String) -> Self { let prompt = "New directory: ".to_string(); Self { selection, input_mgr: InputManager::new_with_value(prompt, existing_location), - ctx, } } @@ -37,17 +35,16 @@ impl Move { let new_location = self.input_mgr.text(); let torrent_action = TorrentAction::Move(self.selection.ids(), new_location.clone()); - self.ctx.send_torrent_action(torrent_action); + CTX.send_torrent_action(torrent_action); let task = StatusTask::new_move(new_location); - self.ctx - .send_update_action(UpdateAction::StatusTaskSet(task)); + CTX.send_update_action(UpdateAction::StatusTaskSet(task)); ComponentAction::Quit } else if input.code == KeyCode::Esc { ComponentAction::Quit } else if self.input_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); ComponentAction::Nothing } else { ComponentAction::Nothing diff --git a/rm-main/src/tui/tabs/torrents/tasks/rename.rs b/rm-main/src/tui/tabs/torrents/tasks/rename.rs index cde4143..1929c69 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/rename.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/rename.rs @@ -9,7 +9,7 @@ use transmission_rpc::types::Id; use crate::{ transmission::TorrentAction, tui::{ - app, + app::CTX, components::{Component, ComponentAction, InputManager}, }, }; @@ -18,16 +18,14 @@ 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 { + pub fn new(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, } @@ -42,9 +40,8 @@ impl Rename { let task = StatusTask::new_rename(self.curr_name.clone()); - self.ctx - .send_update_action(UpdateAction::StatusTaskSet(task)); - self.ctx.send_torrent_action(TorrentAction::Rename( + CTX.send_update_action(UpdateAction::StatusTaskSet(task)); + CTX.send_torrent_action(TorrentAction::Rename( self.id.clone(), self.curr_name.clone(), self.input_mgr.text(), @@ -64,7 +61,7 @@ impl Component for Rename { } if self.input_mgr.handle_key(input).is_some() { - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } ComponentAction::Nothing diff --git a/rm-main/src/tui/tabs/torrents/tasks/status.rs b/rm-main/src/tui/tabs/torrents/tasks/status.rs index d859d15..4b72e27 100644 --- a/rm-main/src/tui/tabs/torrents/tasks/status.rs +++ b/rm-main/src/tui/tabs/torrents/tasks/status.rs @@ -6,12 +6,11 @@ use rm_shared::{ use throbber_widgets_tui::ThrobberState; use tokio::time::{self, Instant}; -use crate::tui::{app, components::Component}; +use crate::tui::{app::CTX, components::Component}; pub struct Status { task: StatusTask, pub task_status: CurrentTaskState, - ctx: app::Ctx, } #[derive(Clone)] @@ -22,12 +21,8 @@ pub enum CurrentTaskState { } impl Status { - pub const fn new(ctx: app::Ctx, task: StatusTask, task_status: CurrentTaskState) -> Self { - Self { - task, - task_status, - ctx, - } + pub const fn new(task: StatusTask, task_status: CurrentTaskState) -> Self { + Self { task, task_status } } pub fn set_failure(&mut self) { @@ -70,11 +65,11 @@ impl Component for Status { match action { UpdateAction::StatusTaskSuccess => { self.set_success(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } UpdateAction::Error(_) => { self.set_failure(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } _ => (), } @@ -84,18 +79,18 @@ impl Component for Status { match &mut self.task_status { CurrentTaskState::Loading(state) => { state.calc_next(); - self.ctx.send_action(Action::Render); + CTX.send_action(Action::Render); } CurrentTaskState::Success(start) => { let expiration_duration = time::Duration::from_secs(5); if start.elapsed() >= expiration_duration { - self.ctx.send_update_action(UpdateAction::StatusTaskClear); + CTX.send_update_action(UpdateAction::StatusTaskClear); } } CurrentTaskState::Failure(start) => { let expiration_duration = time::Duration::from_secs(5); if start.elapsed() >= expiration_duration { - self.ctx.send_update_action(UpdateAction::StatusTaskClear); + CTX.send_update_action(UpdateAction::StatusTaskClear); } } } diff --git a/rm-shared/src/action.rs b/rm-shared/src/action.rs index ea21076..c029077 100644 --- a/rm-shared/src/action.rs +++ b/rm-shared/src/action.rs @@ -2,7 +2,7 @@ use std::{error::Error, sync::Arc}; use crossterm::event::KeyEvent; use magnetease::{MagneteaseError, MagneteaseResult}; -use transmission_rpc::types::{FreeSpace, SessionStats, Torrent}; +use transmission_rpc::types::{FreeSpace, SessionGet, SessionStats, Torrent}; use crate::status_task::StatusTask; @@ -53,6 +53,7 @@ pub enum UpdateAction { Error(Box), // Torrents Tab SessionStats(Arc), + SessionGet(Arc), FreeSpace(Arc), UpdateTorrents(Vec), UpdateCurrentTorrent(Box),