From 9559c4c5428144db1ae57599911fd2b56445ea7c Mon Sep 17 00:00:00 2001 From: Ivin Joel Abraham Date: Fri, 27 Jun 2025 18:56:52 +0530 Subject: [PATCH 1/4] refactor: rename `app` module to `model` and `App` struct to `Model` The `App` struct serves as the model in the MVC pattern. The proposed MVVM refactor will continue using it as the primary Model. Rename `App` to `Model` to better reflect it's purpose. Signed-off-by: Ivin Joel Abraham --- src/cli.rs | 2 +- src/handler/bookmarked.rs | 18 +++---- src/handler/details_actions.rs | 20 ++++---- src/handler/edit_config.rs | 16 +++--- src/handler/latest.rs | 16 +++--- src/handler/mail_list.rs | 38 +++++++------- src/handler/mod.rs | 49 ++++++++++--------- .../logging/garbage_collector.rs | 2 +- src/infrastructure/logging/mod.rs | 2 +- src/main.rs | 8 +-- src/{app => model}/config/mod.rs | 0 src/{app => model}/config/tests.rs | 0 src/{app => model}/cover_renderer.rs | 0 src/{app => model}/mod.rs | 6 +-- src/{app => model}/patch_renderer.rs | 0 src/{app => model}/screens/bookmarked.rs | 0 src/{app => model}/screens/details_actions.rs | 2 +- src/{app => model}/screens/edit_config.rs | 2 +- src/{app => model}/screens/latest.rs | 0 src/{app => model}/screens/mail_list.rs | 0 src/{app => model}/screens/mod.rs | 0 src/ui/bookmarked.rs | 2 +- src/ui/details_actions.rs | 29 ++++++----- src/ui/edit_config.rs | 14 +++--- src/ui/latest.rs | 20 ++++---- src/ui/mail_list.rs | 24 ++++----- src/ui/mod.rs | 20 ++++---- src/ui/navigation_bar.rs | 16 +++--- src/ui/popup/review_trailers.rs | 2 +- 29 files changed, 158 insertions(+), 150 deletions(-) rename src/{app => model}/config/mod.rs (100%) rename src/{app => model}/config/tests.rs (100%) rename src/{app => model}/cover_renderer.rs (100%) rename src/{app => model}/mod.rs (99%) rename src/{app => model}/patch_renderer.rs (100%) rename src/{app => model}/screens/bookmarked.rs (100%) rename src/{app => model}/screens/details_actions.rs (99%) rename src/{app => model}/screens/edit_config.rs (99%) rename src/{app => model}/screens/latest.rs (100%) rename src/{app => model}/screens/mail_list.rs (100%) rename src/{app => model}/screens/mod.rs (100%) diff --git a/src/cli.rs b/src/cli.rs index 621ff15..d921da0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,7 +4,7 @@ use ratatui::{prelude::Backend, Terminal}; use std::ops::ControlFlow; -use crate::{app::config::Config, infrastructure::terminal::restore}; +use crate::{infrastructure::terminal::restore, model::config::Config}; #[derive(Debug, Parser)] #[command(version, about)] diff --git a/src/handler/bookmarked.rs b/src/handler/bookmarked.rs index 3bca0cc..7b9a418 100644 --- a/src/handler/bookmarked.rs +++ b/src/handler/bookmarked.rs @@ -7,13 +7,13 @@ use ratatui::{ use std::ops::ControlFlow; use crate::{ - app::{screens::CurrentScreen, App}, loading_screen, + model::{screens::CurrentScreen, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; pub fn handle_bookmarked_patchsets( - app: &mut App, + model: &mut Model, key: KeyEvent, mut terminal: Terminal, ) -> color_eyre::Result>> @@ -23,25 +23,25 @@ where match key.code { KeyCode::Char('?') => { let popup = generate_help_popup(); - app.popup = Some(popup); + model.popup = Some(popup); } KeyCode::Esc | KeyCode::Char('q') => { - app.bookmarked_patchsets.patchset_index = 0; - app.set_current_screen(CurrentScreen::MailingListSelection); + model.bookmarked_patchsets.patchset_index = 0; + model.set_current_screen(CurrentScreen::MailingListSelection); } KeyCode::Char('j') | KeyCode::Down => { - app.bookmarked_patchsets.select_below_patchset(); + model.bookmarked_patchsets.select_below_patchset(); } KeyCode::Char('k') | KeyCode::Up => { - app.bookmarked_patchsets.select_above_patchset(); + model.bookmarked_patchsets.select_above_patchset(); } KeyCode::Enter => { terminal = loading_screen! { terminal, "Loading patchset" => { - let result = app.init_details_actions(); + let result = model.init_details_actions(); if result.is_ok() { - app.set_current_screen(CurrentScreen::PatchsetDetails); + model.set_current_screen(CurrentScreen::PatchsetDetails); } result } diff --git a/src/handler/details_actions.rs b/src/handler/details_actions.rs index 26a072b..7da0474 100644 --- a/src/handler/details_actions.rs +++ b/src/handler/details_actions.rs @@ -7,19 +7,19 @@ use ratatui::{ use std::time::Duration; use crate::{ - app::{screens::CurrentScreen, App}, infrastructure::terminal::{setup_user_io, teardown_user_io}, + model::{screens::CurrentScreen, Model}, ui::popup::{help::HelpPopUpBuilder, review_trailers::ReviewTrailersPopUp, PopUp}, }; use super::wait_key_press; pub fn handle_patchset_details( - app: &mut App, + model: &mut Model, key: KeyEvent, terminal: &mut Terminal, ) -> color_eyre::Result<()> { - let patchset_details_and_actions = app.details_actions.as_mut().unwrap(); + let patchset_details_and_actions = model.details_actions.as_mut().unwrap(); if key.modifiers.contains(KeyModifiers::SHIFT) { match key.code { @@ -51,7 +51,7 @@ pub fn handle_patchset_details( KeyCode::Char('t') => { let popup = ReviewTrailersPopUp::generate_trailers_popup(patchset_details_and_actions); - app.popup = Some(popup); + model.popup = Some(popup); } _ => {} } @@ -61,12 +61,12 @@ pub fn handle_patchset_details( match key.code { KeyCode::Char('?') => { let popup = generate_help_popup(); - app.popup = Some(popup); + model.popup = Some(popup); } KeyCode::Esc | KeyCode::Char('q') => { let ps_da_clone = patchset_details_and_actions.last_screen.clone(); - app.set_current_screen(ps_da_clone); - app.reset_details_actions(); + model.set_current_screen(ps_da_clone); + model.reset_details_actions(); } KeyCode::Char('a') => { patchset_details_and_actions.toggle_apply_action(); @@ -109,7 +109,7 @@ pub fn handle_patchset_details( KeyCode::Enter => { if patchset_details_and_actions.actions_require_user_io() { setup_user_io(terminal)?; - app.consolidate_patchset_actions()?; + model.consolidate_patchset_actions()?; println!("\nPress ENTER continue..."); loop { if let Event::Key(key) = event::read()? { @@ -120,9 +120,9 @@ pub fn handle_patchset_details( } teardown_user_io(terminal)?; } else { - app.consolidate_patchset_actions()?; + model.consolidate_patchset_actions()?; } - app.set_current_screen(CurrentScreen::PatchsetDetails); + model.set_current_screen(CurrentScreen::PatchsetDetails); } _ => {} } diff --git a/src/handler/edit_config.rs b/src/handler/edit_config.rs index f0f2f15..991caa4 100644 --- a/src/handler/edit_config.rs +++ b/src/handler/edit_config.rs @@ -1,12 +1,12 @@ use ratatui::crossterm::event::{KeyCode, KeyEvent}; use crate::{ - app::{screens::CurrentScreen, App}, + model::{screens::CurrentScreen, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; -pub fn handle_edit_config(app: &mut App, key: KeyEvent) -> color_eyre::Result<()> { - if let Some(edit_config_state) = app.edit_config.as_mut() { +pub fn handle_edit_config(model: &mut Model, key: KeyEvent) -> color_eyre::Result<()> { + if let Some(edit_config_state) = model.edit_config.as_mut() { match edit_config_state.is_editing() { true => match key.code { KeyCode::Esc => { @@ -29,13 +29,13 @@ pub fn handle_edit_config(app: &mut App, key: KeyEvent) -> color_eyre::Result<() false => match key.code { KeyCode::Char('?') => { let popup = generate_help_popup(); - app.popup = Some(popup); + model.popup = Some(popup); } KeyCode::Esc | KeyCode::Char('q') => { - app.consolidate_edit_config(); - app.config.save_patch_hub_config()?; - app.reset_edit_config(); - app.set_current_screen(CurrentScreen::MailingListSelection); + model.consolidate_edit_config(); + model.config.save_patch_hub_config()?; + model.reset_edit_config(); + model.set_current_screen(CurrentScreen::MailingListSelection); } KeyCode::Enter => { edit_config_state.toggle_editing(); diff --git a/src/handler/latest.rs b/src/handler/latest.rs index 1d0493e..da0487f 100644 --- a/src/handler/latest.rs +++ b/src/handler/latest.rs @@ -7,29 +7,29 @@ use ratatui::{ use std::ops::ControlFlow; use crate::{ - app::{screens::CurrentScreen, App}, loading_screen, + model::{screens::CurrentScreen, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; pub fn handle_latest_patchsets( - app: &mut App, + model: &mut Model, key: KeyEvent, mut terminal: Terminal, ) -> color_eyre::Result>> where B: Backend + Send + 'static, { - let latest_patchsets = app.latest_patchsets.as_mut().unwrap(); + let latest_patchsets = model.latest_patchsets.as_mut().unwrap(); match key.code { KeyCode::Char('?') => { let popup = generate_help_popup(); - app.popup = Some(popup); + model.popup = Some(popup); } KeyCode::Esc | KeyCode::Char('q') => { - app.reset_latest_patchsets(); - app.set_current_screen(CurrentScreen::MailingListSelection); + model.reset_latest_patchsets(); + model.set_current_screen(CurrentScreen::MailingListSelection); } KeyCode::Char('j') | KeyCode::Down => { latest_patchsets.select_below_patchset(); @@ -54,9 +54,9 @@ where terminal = loading_screen! { terminal, "Loading patchset" => { - let result = app.init_details_actions(); + let result = model.init_details_actions(); if result.is_ok() { - app.set_current_screen(CurrentScreen::PatchsetDetails); + model.set_current_screen(CurrentScreen::PatchsetDetails); } result } diff --git a/src/handler/mail_list.rs b/src/handler/mail_list.rs index d5a0ffc..4809794 100644 --- a/src/handler/mail_list.rs +++ b/src/handler/mail_list.rs @@ -7,13 +7,13 @@ use ratatui::{ use std::ops::ControlFlow; use crate::{ - app::{screens::CurrentScreen, App}, loading_screen, + model::{screens::CurrentScreen, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; pub fn handle_mailing_list_selection( - app: &mut App, + model: &mut Model, key: KeyEvent, mut terminal: Terminal, ) -> color_eyre::Result>> @@ -23,12 +23,12 @@ where match key.code { KeyCode::Char('?') => { let popup = generate_help_popup(); - app.popup = Some(popup); + model.popup = Some(popup); } KeyCode::Enter => { - if app.mailing_list_selection.has_valid_target_list() { - app.init_latest_patchsets(); - let list_name = app + if model.mailing_list_selection.has_valid_target_list() { + model.init_latest_patchsets(); + let list_name = model .latest_patchsets .as_ref() .unwrap() @@ -39,11 +39,11 @@ where terminal, format!("Fetching patchsets from {}", list_name) => { let result = - app.latest_patchsets.as_mut().unwrap() + model.latest_patchsets.as_mut().unwrap() .fetch_current_page(); if result.is_ok() { - app.mailing_list_selection.clear_target_list(); - app.set_current_screen(CurrentScreen::LatestPatchsets); + model.mailing_list_selection.clear_target_list(); + model.set_current_screen(CurrentScreen::LatestPatchsets); } result } @@ -54,35 +54,35 @@ where terminal = loading_screen! { terminal, "Refreshing lists" => { - app.mailing_list_selection + model.mailing_list_selection .refresh_available_mailing_lists() } }; } KeyCode::F(2) => { - app.init_edit_config(); - app.set_current_screen(CurrentScreen::EditConfig); + model.init_edit_config(); + model.set_current_screen(CurrentScreen::EditConfig); } KeyCode::F(1) => { - if !app.bookmarked_patchsets.bookmarked_patchsets.is_empty() { - app.mailing_list_selection.clear_target_list(); - app.set_current_screen(CurrentScreen::BookmarkedPatchsets); + if !model.bookmarked_patchsets.bookmarked_patchsets.is_empty() { + model.mailing_list_selection.clear_target_list(); + model.set_current_screen(CurrentScreen::BookmarkedPatchsets); } } KeyCode::Backspace => { - app.mailing_list_selection.remove_last_target_list_char(); + model.mailing_list_selection.remove_last_target_list_char(); } KeyCode::Esc => { return Ok(ControlFlow::Break(())); } KeyCode::Char(ch) => { - app.mailing_list_selection.push_char_to_target_list(ch); + model.mailing_list_selection.push_char_to_target_list(ch); } KeyCode::Down => { - app.mailing_list_selection.highlight_below_list(); + model.mailing_list_selection.highlight_below_list(); } KeyCode::Up => { - app.mailing_list_selection.highlight_above_list(); + model.mailing_list_selection.highlight_above_list(); } _ => {} } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 6dc2a59..75c6143 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -16,9 +16,9 @@ use std::{ }; use crate::{ - app::{screens::CurrentScreen, App}, infrastructure::logging::Logger, loading_screen, + model::{screens::CurrentScreen, Model}, ui::draw_ui, }; @@ -31,56 +31,59 @@ use mail_list::handle_mailing_list_selection; fn key_handling( mut terminal: Terminal, - app: &mut App, + model: &mut Model, key: KeyEvent, ) -> color_eyre::Result>> where B: Backend + Send + 'static, { - if let Some(popup) = app.popup.as_mut() { + if let Some(popup) = model.popup.as_mut() { if matches!(key.code, KeyCode::Esc | KeyCode::Char('q')) { - app.popup = None; + model.popup = None; } else { popup.handle(key)?; } } else { - match app.current_screen { + match model.current_screen { CurrentScreen::MailingListSelection => { - return handle_mailing_list_selection(app, key, terminal); + return handle_mailing_list_selection(model, key, terminal); } CurrentScreen::BookmarkedPatchsets => { - return handle_bookmarked_patchsets(app, key, terminal); + return handle_bookmarked_patchsets(model, key, terminal); } CurrentScreen::PatchsetDetails => { - handle_patchset_details(app, key, &mut terminal)?; + handle_patchset_details(model, key, &mut terminal)?; } CurrentScreen::EditConfig => { - handle_edit_config(app, key)?; + handle_edit_config(model, key)?; } CurrentScreen::LatestPatchsets => { - return handle_latest_patchsets(app, key, terminal); + return handle_latest_patchsets(model, key, terminal); } } } Ok(ControlFlow::Continue(terminal)) } -fn logic_handling(mut terminal: Terminal, app: &mut App) -> color_eyre::Result> +fn logic_handling( + mut terminal: Terminal, + model: &mut Model, +) -> color_eyre::Result> where B: Backend + Send + 'static, { - match app.current_screen { + match model.current_screen { CurrentScreen::MailingListSelection => { - if app.mailing_list_selection.mailing_lists.is_empty() { + if model.mailing_list_selection.mailing_lists.is_empty() { terminal = loading_screen! { terminal, "Fetching mailing lists" => { - app.mailing_list_selection.refresh_available_mailing_lists() + model.mailing_list_selection.refresh_available_mailing_lists() } }; } } CurrentScreen::LatestPatchsets => { - let patchsets_state = app.latest_patchsets.as_mut().unwrap(); + let patchsets_state = model.latest_patchsets.as_mut().unwrap(); let target_list = patchsets_state.target_list().to_string(); if patchsets_state.processed_patchsets_count() == 0 { terminal = loading_screen! { @@ -90,12 +93,12 @@ where } }; - app.mailing_list_selection.clear_target_list(); + model.mailing_list_selection.clear_target_list(); } } CurrentScreen::BookmarkedPatchsets => { - if app.bookmarked_patchsets.bookmarked_patchsets.is_empty() { - app.set_current_screen(CurrentScreen::MailingListSelection); + if model.bookmarked_patchsets.bookmarked_patchsets.is_empty() { + model.set_current_screen(CurrentScreen::MailingListSelection); } } _ => {} @@ -104,19 +107,19 @@ where Ok(terminal) } -pub fn run_app(mut terminal: Terminal, mut app: App) -> color_eyre::Result<()> +pub fn run_app(mut terminal: Terminal, mut model: Model) -> color_eyre::Result<()> where B: Backend + Send + 'static, { - if !app.check_external_deps() { + if !model.check_external_deps() { Logger::error("patch-hub cannot be executed because some dependencies are missing"); bail!("patch-hub cannot be executed because some dependencies are missing, check logs for more information"); } loop { - terminal = logic_handling(terminal, &mut app)?; + terminal = logic_handling(terminal, &mut model)?; - terminal.draw(|f| draw_ui(f, &app))?; + terminal.draw(|f| draw_ui(f, &model))?; // *IMPORTANT*: Uncommenting the if below makes `patch-hub` not block // until an event is captured. We should only do it when (if ever) we @@ -127,7 +130,7 @@ where if key.kind == KeyEventKind::Release { continue; } - match key_handling(terminal, &mut app, key)? { + match key_handling(terminal, &mut model, key)? { ControlFlow::Continue(t) => terminal = t, ControlFlow::Break(_) => return Ok(()), } diff --git a/src/infrastructure/logging/garbage_collector.rs b/src/infrastructure/logging/garbage_collector.rs index e06892b..be02368 100644 --- a/src/infrastructure/logging/garbage_collector.rs +++ b/src/infrastructure/logging/garbage_collector.rs @@ -2,7 +2,7 @@ //! //! This module is responsible for cleaning up the log files. -use crate::app::config::Config; +use crate::model::config::Config; use super::Logger; diff --git a/src/infrastructure/logging/mod.rs b/src/infrastructure/logging/mod.rs index c4c60bd..01f15ef 100644 --- a/src/infrastructure/logging/mod.rs +++ b/src/infrastructure/logging/mod.rs @@ -8,7 +8,7 @@ use std::{ io::Write, }; -use crate::app::config::Config; +use crate::model::config::Config; const LATEST_LOG_FILENAME: &str = "latest.log"; diff --git a/src/main.rs b/src/main.rs index 5d9b0c8..91f756a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,13 @@ -mod app; mod cli; mod handler; mod infrastructure; mod lore; mod macros; +mod model; mod ui; use std::ops::ControlFlow; -use app::{config::Config, App}; use clap::Parser; use cli::Cli; use handler::run_app; @@ -16,6 +15,7 @@ use infrastructure::{ logging::Logger, terminal::{init, restore}, }; +use model::{config::Config, Model}; fn main() -> color_eyre::Result<()> { let args = Cli::parse(); @@ -31,9 +31,9 @@ fn main() -> color_eyre::Result<()> { ControlFlow::Continue(t) => terminal = t, } - let app = App::new(config)?; + let model = Model::new(config)?; - run_app(terminal, app)?; + run_app(terminal, model)?; restore()?; Logger::info("patch-hub finished"); diff --git a/src/app/config/mod.rs b/src/model/config/mod.rs similarity index 100% rename from src/app/config/mod.rs rename to src/model/config/mod.rs diff --git a/src/app/config/tests.rs b/src/model/config/tests.rs similarity index 100% rename from src/app/config/tests.rs rename to src/model/config/tests.rs diff --git a/src/app/cover_renderer.rs b/src/model/cover_renderer.rs similarity index 100% rename from src/app/cover_renderer.rs rename to src/model/cover_renderer.rs diff --git a/src/app/mod.rs b/src/model/mod.rs similarity index 99% rename from src/app/mod.rs rename to src/model/mod.rs index 9ea1783..c326dc8 100644 --- a/src/app/mod.rs +++ b/src/model/mod.rs @@ -34,7 +34,7 @@ use screens::{ /// Type that represents the overall state of the application. It can be viewed /// as the **Model** component of `patch-hub`. -pub struct App { +pub struct Model { /// The current active screen pub current_screen: CurrentScreen, /// Screen to navigate and select the mailing lists archived on Lore @@ -56,7 +56,7 @@ pub struct App { pub popup: Option>, } -impl App { +impl Model { /// Creates a new instance of `App`. It dynamically loads configurations /// based on precedence (see [crate::app::Config::build]), app data /// (available mailing lists, bookmarked patchsets, reviewed patchsets), and @@ -84,7 +84,7 @@ impl App { Logger::info("patch-hub started"); garbage_collector::collect_garbage(&config); - Ok(App { + Ok(Model { current_screen: CurrentScreen::MailingListSelection, mailing_list_selection: MailingListSelection { mailing_lists: mailing_lists.clone(), diff --git a/src/app/patch_renderer.rs b/src/model/patch_renderer.rs similarity index 100% rename from src/app/patch_renderer.rs rename to src/model/patch_renderer.rs diff --git a/src/app/screens/bookmarked.rs b/src/model/screens/bookmarked.rs similarity index 100% rename from src/app/screens/bookmarked.rs rename to src/model/screens/bookmarked.rs diff --git a/src/app/screens/details_actions.rs b/src/model/screens/details_actions.rs similarity index 99% rename from src/app/screens/details_actions.rs rename to src/model/screens/details_actions.rs index 076f167..fbcc68e 100644 --- a/src/app/screens/details_actions.rs +++ b/src/model/screens/details_actions.rs @@ -8,12 +8,12 @@ use std::{ }; use crate::{ - app::config::{Config, KernelTree}, lore::{ lore_api_client::BlockingLoreAPIClient, lore_session, patch::{Author, Patch}, }, + model::config::{Config, KernelTree}, }; use super::CurrentScreen; diff --git a/src/app/screens/edit_config.rs b/src/model/screens/edit_config.rs similarity index 99% rename from src/app/screens/edit_config.rs rename to src/model/screens/edit_config.rs index 8714f1c..6431686 100644 --- a/src/app/screens/edit_config.rs +++ b/src/model/screens/edit_config.rs @@ -3,7 +3,7 @@ use derive_getters::Getters; use std::{collections::HashMap, fmt::Display, path::Path}; -use crate::app::config::Config; +use crate::model::config::Config; #[derive(Debug, Getters)] pub struct EditConfig { diff --git a/src/app/screens/latest.rs b/src/model/screens/latest.rs similarity index 100% rename from src/app/screens/latest.rs rename to src/model/screens/latest.rs diff --git a/src/app/screens/mail_list.rs b/src/model/screens/mail_list.rs similarity index 100% rename from src/app/screens/mail_list.rs rename to src/model/screens/mail_list.rs diff --git a/src/app/screens/mod.rs b/src/model/screens/mod.rs similarity index 100% rename from src/app/screens/mod.rs rename to src/model/screens/mod.rs diff --git a/src/ui/bookmarked.rs b/src/ui/bookmarked.rs index a6a9ccf..f194c78 100644 --- a/src/ui/bookmarked.rs +++ b/src/ui/bookmarked.rs @@ -6,7 +6,7 @@ use ratatui::{ Frame, }; -use crate::app::screens::bookmarked::BookmarkedPatchsets; +use crate::model::screens::bookmarked::BookmarkedPatchsets; pub fn render_main(f: &mut Frame, bookmarked_patchsets: &BookmarkedPatchsets, chunk: Rect) { let patchset_index = bookmarked_patchsets.patchset_index; diff --git a/src/ui/details_actions.rs b/src/ui/details_actions.rs index 92a4020..75b7593 100644 --- a/src/ui/details_actions.rs +++ b/src/ui/details_actions.rs @@ -6,9 +6,9 @@ use ratatui::{ Frame, }; -use crate::app::{ +use crate::model::{ screens::details_actions::{DetailsActions, PatchsetAction}, - App, + Model, }; /// Returns a `Line` type that represents a line containing stats about reply @@ -47,8 +47,13 @@ fn review_trailers_details(details_actions: &DetailsActions) -> Line<'static> { ]) } -fn render_details_and_actions(f: &mut Frame, app: &App, details_chunk: Rect, actions_chunk: Rect) { - let patchset_details_and_actions = app.details_actions.as_ref().unwrap(); +fn render_details_and_actions( + f: &mut Frame, + model: &Model, + details_chunk: Rect, + actions_chunk: Rect, +) { + let patchset_details_and_actions = model.details_actions.as_ref().unwrap(); let mut staged_to_reply = String::new(); if let Some(true) = patchset_details_and_actions @@ -199,8 +204,8 @@ fn render_details_and_actions(f: &mut Frame, app: &App, details_chunk: Rect, act f.render_widget(patchset_actions, actions_chunk); } -fn render_preview(f: &mut Frame, app: &App, chunk: Rect) { - let patchset_details_and_actions = app.details_actions.as_ref().unwrap(); +fn render_preview(f: &mut Frame, model: &Model, chunk: Rect) { + let patchset_details_and_actions = model.details_actions.as_ref().unwrap(); let preview_index = patchset_details_and_actions.preview_index; @@ -210,7 +215,7 @@ fn render_preview(f: &mut Frame, app: &App, chunk: Rect) { .href; let mut preview_title = String::from(" Preview "); if matches!( - app.reviewed_patchsets.get(representative_patch_message_id), + model.reviewed_patchsets.get(representative_patch_message_id), Some(successful_indexes) if successful_indexes.contains(&preview_index) ) { preview_title = " Preview [REVIEWED-BY] ".to_string(); @@ -242,11 +247,11 @@ fn render_preview(f: &mut Frame, app: &App, chunk: Rect) { f.render_widget(patch_preview, chunk); } -pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { - let patchset_details_and_actions = app.details_actions.as_ref().unwrap(); +pub fn render_main(f: &mut Frame, model: &Model, chunk: Rect) { + let patchset_details_and_actions = model.details_actions.as_ref().unwrap(); if patchset_details_and_actions.preview_fullscreen { - render_preview(f, app, chunk); + render_preview(f, model, chunk); } else { let chunks = Layout::default() .direction(Direction::Horizontal) @@ -260,11 +265,11 @@ pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { render_details_and_actions( f, - app, + model, details_and_actions_chunks[0], details_and_actions_chunks[1], ); - render_preview(f, app, chunks[1]); + render_preview(f, model, chunks[1]); } } diff --git a/src/ui/edit_config.rs b/src/ui/edit_config.rs index ef62a8e..e136f17 100644 --- a/src/ui/edit_config.rs +++ b/src/ui/edit_config.rs @@ -6,10 +6,10 @@ use ratatui::{ Frame, }; -use crate::{app::App, infrastructure::logging::Logger}; +use crate::{infrastructure::logging::Logger, model::Model}; -pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { - let edit_config = app.edit_config.as_ref().unwrap(); +pub fn render_main(f: &mut Frame, model: &Model, chunk: Rect) { + let edit_config = model.edit_config.as_ref().unwrap(); let mut constraints = Vec::new(); for _ in 0..(chunk.height / 3) { @@ -63,8 +63,8 @@ pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { } } -pub fn mode_footer_text(app: &App) -> Vec { - let edit_config_state = app.edit_config.as_ref().unwrap(); +pub fn mode_footer_text(model: &Model) -> Vec { + let edit_config_state = model.edit_config.as_ref().unwrap(); vec![if edit_config_state.is_editing() { Span::styled("Editing...", Style::default().fg(Color::LightYellow)) } else { @@ -72,8 +72,8 @@ pub fn mode_footer_text(app: &App) -> Vec { }] } -pub fn keys_hint(app: &App) -> Span { - let edit_config_state = app.edit_config.as_ref().unwrap(); +pub fn keys_hint(model: &Model) -> Span { + let edit_config_state = model.edit_config.as_ref().unwrap(); match edit_config_state.is_editing() { true => Span::styled( "(ESC) cancel | (ENTER) confirm", diff --git a/src/ui/latest.rs b/src/ui/latest.rs index e677636..3d1b1a6 100644 --- a/src/ui/latest.rs +++ b/src/ui/latest.rs @@ -6,21 +6,21 @@ use ratatui::{ Frame, }; -use crate::{app::App, lore::patch::Patch}; +use crate::{lore::patch::Patch, model::Model}; -pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { - let page_number = app.latest_patchsets.as_ref().unwrap().page_number(); - let patchset_index = app.latest_patchsets.as_ref().unwrap().patchset_index(); +pub fn render_main(f: &mut Frame, model: &Model, chunk: Rect) { + let page_number = model.latest_patchsets.as_ref().unwrap().page_number(); + let patchset_index = model.latest_patchsets.as_ref().unwrap().patchset_index(); let mut list_items = Vec::::new(); - let patch_feed_page: Vec<&Patch> = app + let patch_feed_page: Vec<&Patch> = model .latest_patchsets .as_ref() .unwrap() .get_current_patch_feed_page() .unwrap(); - let mut index: usize = (page_number - 1) * app.config.page_size(); + let mut index: usize = (page_number - 1) * model.config.page_size(); for patch in patch_feed_page { let patch_title = format!("{:width$}", patch.title(), width = 70); let patch_title = format!("{:.width$}", patch_title, width = 70); @@ -61,18 +61,18 @@ pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { let mut list_state = ListState::default(); list_state.select(Some( - patchset_index - (page_number - 1) * app.config.page_size(), + patchset_index - (page_number - 1) * model.config.page_size(), )); f.render_stateful_widget(list, chunk, &mut list_state); } -pub fn mode_footer_text(app: &App) -> Vec { +pub fn mode_footer_text(model: &Model) -> Vec { vec![Span::styled( format!( "Latest Patchsets from {} (page {})", - &app.latest_patchsets.as_ref().unwrap().target_list(), - &app.latest_patchsets.as_ref().unwrap().page_number() + &model.latest_patchsets.as_ref().unwrap().target_list(), + &model.latest_patchsets.as_ref().unwrap().page_number() ), Style::default().fg(Color::Green), )] diff --git a/src/ui/mail_list.rs b/src/ui/mail_list.rs index 16686db..91f4c5a 100644 --- a/src/ui/mail_list.rs +++ b/src/ui/mail_list.rs @@ -6,13 +6,13 @@ use ratatui::{ Frame, }; -use crate::app::App; +use crate::model::Model; -pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { - let highlighted_list_index = app.mailing_list_selection.highlighted_list_index; +pub fn render_main(f: &mut Frame, model: &Model, chunk: Rect) { + let highlighted_list_index = model.mailing_list_selection.highlighted_list_index; let mut list_items = Vec::::new(); - for mailing_list in &app.mailing_list_selection.possible_mailing_lists { + for mailing_list in &model.mailing_list_selection.possible_mailing_lists { list_items.push(ListItem::new( Line::from(vec![ Span::styled( @@ -50,35 +50,35 @@ pub fn render_main(f: &mut Frame, app: &App, chunk: Rect) { f.render_stateful_widget(list, chunk, &mut list_state); } -pub fn mode_footer_text(app: &App) -> Vec { +pub fn mode_footer_text(model: &Model) -> Vec { let mut text_area = Span::default(); - if app.mailing_list_selection.target_list.is_empty() { + if model.mailing_list_selection.target_list.is_empty() { text_area = Span::styled("type the target list", Style::default().fg(Color::DarkGray)) } else { - for mailing_list in &app.mailing_list_selection.mailing_lists { + for mailing_list in &model.mailing_list_selection.mailing_lists { if mailing_list .name() - .eq(&app.mailing_list_selection.target_list) + .eq(&model.mailing_list_selection.target_list) { text_area = Span::styled( - &app.mailing_list_selection.target_list, + &model.mailing_list_selection.target_list, Style::default().fg(Color::Green), ); break; } else if mailing_list .name() - .starts_with(&app.mailing_list_selection.target_list) + .starts_with(&model.mailing_list_selection.target_list) { text_area = Span::styled( - &app.mailing_list_selection.target_list, + &model.mailing_list_selection.target_list, Style::default().fg(Color::LightCyan), ); } } if text_area.content.is_empty() { text_area = Span::styled( - &app.mailing_list_selection.target_list, + &model.mailing_list_selection.target_list, Style::default().fg(Color::Red), ); } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index ed52875..cdfab16 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -15,9 +15,9 @@ use ratatui::{ Frame, }; -use crate::app::{screens::CurrentScreen, App}; +use crate::model::{screens::CurrentScreen, Model}; -pub fn draw_ui(f: &mut Frame, app: &App) { +pub fn draw_ui(f: &mut Frame, model: &Model) { // Clear the whole screen for sanitizing reasons f.render_widget(Clear, f.area()); @@ -32,19 +32,19 @@ pub fn draw_ui(f: &mut Frame, app: &App) { render_title(f, chunks[0]); - match app.current_screen { - CurrentScreen::MailingListSelection => mail_list::render_main(f, app, chunks[1]), + match model.current_screen { + CurrentScreen::MailingListSelection => mail_list::render_main(f, model, chunks[1]), CurrentScreen::BookmarkedPatchsets => { - bookmarked::render_main(f, &app.bookmarked_patchsets, chunks[1]) + bookmarked::render_main(f, &model.bookmarked_patchsets, chunks[1]) } - CurrentScreen::LatestPatchsets => latest::render_main(f, app, chunks[1]), - CurrentScreen::PatchsetDetails => details_actions::render_main(f, app, chunks[1]), - CurrentScreen::EditConfig => edit_config::render_main(f, app, chunks[1]), + CurrentScreen::LatestPatchsets => latest::render_main(f, model, chunks[1]), + CurrentScreen::PatchsetDetails => details_actions::render_main(f, model, chunks[1]), + CurrentScreen::EditConfig => edit_config::render_main(f, model, chunks[1]), } - navigation_bar::render(f, app, chunks[2]); + navigation_bar::render(f, model, chunks[2]); - app.popup.as_ref().inspect(|p| { + model.popup.as_ref().inspect(|p| { let (x, y) = p.dimensions(); let rect = centered_rect(x, y, f.area()); p.render(f, rect); diff --git a/src/ui/navigation_bar.rs b/src/ui/navigation_bar.rs index 47dd6a1..58bfd39 100644 --- a/src/ui/navigation_bar.rs +++ b/src/ui/navigation_bar.rs @@ -5,29 +5,29 @@ use ratatui::{ Frame, }; -use crate::app::{screens::CurrentScreen, App}; +use crate::model::{screens::CurrentScreen, Model}; use super::{bookmarked, details_actions, edit_config, latest, mail_list}; -pub fn render(f: &mut Frame, app: &App, chunk: Rect) { - let mode_footer_text = match app.current_screen { - CurrentScreen::MailingListSelection => mail_list::mode_footer_text(app), +pub fn render(f: &mut Frame, model: &Model, chunk: Rect) { + let mode_footer_text = match model.current_screen { + CurrentScreen::MailingListSelection => mail_list::mode_footer_text(model), CurrentScreen::BookmarkedPatchsets => bookmarked::mode_footer_text(), - CurrentScreen::LatestPatchsets => latest::mode_footer_text(app), + CurrentScreen::LatestPatchsets => latest::mode_footer_text(model), CurrentScreen::PatchsetDetails => details_actions::mode_footer_text(), - CurrentScreen::EditConfig => edit_config::mode_footer_text(app), + CurrentScreen::EditConfig => edit_config::mode_footer_text(model), }; let mode_footer = Paragraph::new(Line::from(mode_footer_text)) .block(Block::default().borders(Borders::ALL)) .centered(); let current_keys_hint = { - match app.current_screen { + match model.current_screen { CurrentScreen::MailingListSelection => mail_list::keys_hint(), CurrentScreen::BookmarkedPatchsets => bookmarked::keys_hint(), CurrentScreen::LatestPatchsets => latest::keys_hint(), CurrentScreen::PatchsetDetails => details_actions::keys_hint(), - CurrentScreen::EditConfig => edit_config::keys_hint(app), + CurrentScreen::EditConfig => edit_config::keys_hint(model), } }; diff --git a/src/ui/popup/review_trailers.rs b/src/ui/popup/review_trailers.rs index f626bcc..36d5993 100644 --- a/src/ui/popup/review_trailers.rs +++ b/src/ui/popup/review_trailers.rs @@ -8,7 +8,7 @@ use ratatui::{ use std::collections::HashSet; -use crate::{app::screens::details_actions::DetailsActions, lore::patch::Author}; +use crate::{lore::patch::Author, model::screens::details_actions::DetailsActions}; use super::PopUp; From 56414ed2f13f73d166a75b7e709cdab461293be8 Mon Sep 17 00:00:00 2001 From: Ivin Joel Abraham Date: Sat, 28 Jun 2025 22:35:44 +0530 Subject: [PATCH 2/4] refactor: rename `CurrentScreen` enum to `View` to improve clarity The `View` is a crucial part of MVVM that will handle the UI. Rename the struct from `CurrentScreen`, which conveys logic, to simply `View` that will reflect it's purpose in the architecture. Signed-off-by: Ivin Joel Abraham --- src/handler/bookmarked.rs | 6 +++--- src/handler/details_actions.rs | 4 ++-- src/handler/edit_config.rs | 4 ++-- src/handler/latest.rs | 6 +++--- src/handler/mail_list.rs | 8 ++++---- src/handler/mod.rs | 20 ++++++++++---------- src/model/mod.rs | 12 ++++++------ src/model/screens/details_actions.rs | 4 ++-- src/model/screens/mod.rs | 2 +- src/ui/mod.rs | 12 ++++++------ src/ui/navigation_bar.rs | 22 +++++++++++----------- 11 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/handler/bookmarked.rs b/src/handler/bookmarked.rs index 7b9a418..29350d6 100644 --- a/src/handler/bookmarked.rs +++ b/src/handler/bookmarked.rs @@ -8,7 +8,7 @@ use std::ops::ControlFlow; use crate::{ loading_screen, - model::{screens::CurrentScreen, Model}, + model::{screens::View, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; @@ -27,7 +27,7 @@ where } KeyCode::Esc | KeyCode::Char('q') => { model.bookmarked_patchsets.patchset_index = 0; - model.set_current_screen(CurrentScreen::MailingListSelection); + model.set_current_screen(View::MailingListSelection); } KeyCode::Char('j') | KeyCode::Down => { model.bookmarked_patchsets.select_below_patchset(); @@ -41,7 +41,7 @@ where "Loading patchset" => { let result = model.init_details_actions(); if result.is_ok() { - model.set_current_screen(CurrentScreen::PatchsetDetails); + model.set_current_screen(View::PatchsetDetails); } result } diff --git a/src/handler/details_actions.rs b/src/handler/details_actions.rs index 7da0474..172c7a1 100644 --- a/src/handler/details_actions.rs +++ b/src/handler/details_actions.rs @@ -8,7 +8,7 @@ use std::time::Duration; use crate::{ infrastructure::terminal::{setup_user_io, teardown_user_io}, - model::{screens::CurrentScreen, Model}, + model::{screens::View, Model}, ui::popup::{help::HelpPopUpBuilder, review_trailers::ReviewTrailersPopUp, PopUp}, }; @@ -122,7 +122,7 @@ pub fn handle_patchset_details( } else { model.consolidate_patchset_actions()?; } - model.set_current_screen(CurrentScreen::PatchsetDetails); + model.set_current_screen(View::PatchsetDetails); } _ => {} } diff --git a/src/handler/edit_config.rs b/src/handler/edit_config.rs index 991caa4..f2f818d 100644 --- a/src/handler/edit_config.rs +++ b/src/handler/edit_config.rs @@ -1,7 +1,7 @@ use ratatui::crossterm::event::{KeyCode, KeyEvent}; use crate::{ - model::{screens::CurrentScreen, Model}, + model::{screens::View, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; @@ -35,7 +35,7 @@ pub fn handle_edit_config(model: &mut Model, key: KeyEvent) -> color_eyre::Resul model.consolidate_edit_config(); model.config.save_patch_hub_config()?; model.reset_edit_config(); - model.set_current_screen(CurrentScreen::MailingListSelection); + model.set_current_screen(View::MailingListSelection); } KeyCode::Enter => { edit_config_state.toggle_editing(); diff --git a/src/handler/latest.rs b/src/handler/latest.rs index da0487f..362bd73 100644 --- a/src/handler/latest.rs +++ b/src/handler/latest.rs @@ -8,7 +8,7 @@ use std::ops::ControlFlow; use crate::{ loading_screen, - model::{screens::CurrentScreen, Model}, + model::{screens::View, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; @@ -29,7 +29,7 @@ where } KeyCode::Esc | KeyCode::Char('q') => { model.reset_latest_patchsets(); - model.set_current_screen(CurrentScreen::MailingListSelection); + model.set_current_screen(View::MailingListSelection); } KeyCode::Char('j') | KeyCode::Down => { latest_patchsets.select_below_patchset(); @@ -56,7 +56,7 @@ where "Loading patchset" => { let result = model.init_details_actions(); if result.is_ok() { - model.set_current_screen(CurrentScreen::PatchsetDetails); + model.set_current_screen(View::PatchsetDetails); } result } diff --git a/src/handler/mail_list.rs b/src/handler/mail_list.rs index 4809794..aa5003c 100644 --- a/src/handler/mail_list.rs +++ b/src/handler/mail_list.rs @@ -8,7 +8,7 @@ use std::ops::ControlFlow; use crate::{ loading_screen, - model::{screens::CurrentScreen, Model}, + model::{screens::View, Model}, ui::popup::{help::HelpPopUpBuilder, PopUp}, }; @@ -43,7 +43,7 @@ where .fetch_current_page(); if result.is_ok() { model.mailing_list_selection.clear_target_list(); - model.set_current_screen(CurrentScreen::LatestPatchsets); + model.set_current_screen(View::LatestPatchsets); } result } @@ -61,12 +61,12 @@ where } KeyCode::F(2) => { model.init_edit_config(); - model.set_current_screen(CurrentScreen::EditConfig); + model.set_current_screen(View::EditConfig); } KeyCode::F(1) => { if !model.bookmarked_patchsets.bookmarked_patchsets.is_empty() { model.mailing_list_selection.clear_target_list(); - model.set_current_screen(CurrentScreen::BookmarkedPatchsets); + model.set_current_screen(View::BookmarkedPatchsets); } } KeyCode::Backspace => { diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 75c6143..79e7519 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -18,7 +18,7 @@ use std::{ use crate::{ infrastructure::logging::Logger, loading_screen, - model::{screens::CurrentScreen, Model}, + model::{screens::View, Model}, ui::draw_ui, }; @@ -45,19 +45,19 @@ where } } else { match model.current_screen { - CurrentScreen::MailingListSelection => { + View::MailingListSelection => { return handle_mailing_list_selection(model, key, terminal); } - CurrentScreen::BookmarkedPatchsets => { + View::BookmarkedPatchsets => { return handle_bookmarked_patchsets(model, key, terminal); } - CurrentScreen::PatchsetDetails => { + View::PatchsetDetails => { handle_patchset_details(model, key, &mut terminal)?; } - CurrentScreen::EditConfig => { + View::EditConfig => { handle_edit_config(model, key)?; } - CurrentScreen::LatestPatchsets => { + View::LatestPatchsets => { return handle_latest_patchsets(model, key, terminal); } } @@ -73,7 +73,7 @@ where B: Backend + Send + 'static, { match model.current_screen { - CurrentScreen::MailingListSelection => { + View::MailingListSelection => { if model.mailing_list_selection.mailing_lists.is_empty() { terminal = loading_screen! { terminal, "Fetching mailing lists" => { @@ -82,7 +82,7 @@ where }; } } - CurrentScreen::LatestPatchsets => { + View::LatestPatchsets => { let patchsets_state = model.latest_patchsets.as_mut().unwrap(); let target_list = patchsets_state.target_list().to_string(); if patchsets_state.processed_patchsets_count() == 0 { @@ -96,9 +96,9 @@ where model.mailing_list_selection.clear_target_list(); } } - CurrentScreen::BookmarkedPatchsets => { + View::BookmarkedPatchsets => { if model.bookmarked_patchsets.bookmarked_patchsets.is_empty() { - model.set_current_screen(CurrentScreen::MailingListSelection); + model.set_current_screen(View::MailingListSelection); } } _ => {} diff --git a/src/model/mod.rs b/src/model/mod.rs index c326dc8..3a6ec77 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -29,14 +29,14 @@ use screens::{ edit_config::EditConfig, latest::LatestPatchsets, mail_list::MailingListSelection, - CurrentScreen, + View, }; /// Type that represents the overall state of the application. It can be viewed /// as the **Model** component of `patch-hub`. pub struct Model { /// The current active screen - pub current_screen: CurrentScreen, + pub current_screen: View, /// Screen to navigate and select the mailing lists archived on Lore pub mailing_list_selection: MailingListSelection, /// Screen with listing patchsets that were previously bookmarked @@ -85,7 +85,7 @@ impl Model { garbage_collector::collect_garbage(&config); Ok(Model { - current_screen: CurrentScreen::MailingListSelection, + current_screen: View::MailingListSelection, mailing_list_selection: MailingListSelection { mailing_lists: mailing_lists.clone(), target_list: String::new(), @@ -140,10 +140,10 @@ impl Model { let mut acked_by = Vec::new(); match &self.current_screen { - CurrentScreen::BookmarkedPatchsets => { + View::BookmarkedPatchsets => { representative_patch = self.bookmarked_patchsets.get_selected_patchset(); } - CurrentScreen::LatestPatchsets => { + View::LatestPatchsets => { representative_patch = self .latest_patchsets .as_ref() @@ -380,7 +380,7 @@ impl Model { } /// Change the current active screen in [App::current_screen]. - pub fn set_current_screen(&mut self, new_current_screen: CurrentScreen) { + pub fn set_current_screen(&mut self, new_current_screen: View) { self.current_screen = new_current_screen; } diff --git a/src/model/screens/details_actions.rs b/src/model/screens/details_actions.rs index fbcc68e..7432aa5 100644 --- a/src/model/screens/details_actions.rs +++ b/src/model/screens/details_actions.rs @@ -16,7 +16,7 @@ use crate::{ model::config::{Config, KernelTree}, }; -use super::CurrentScreen; +use super::View; pub struct DetailsActions { pub representative_patch: Patch, @@ -44,7 +44,7 @@ pub struct DetailsActions { pub tested_by: Vec>, /// For each patch, a set of `Authors` that appear in `Acked-by` trailers pub acked_by: Vec>, - pub last_screen: CurrentScreen, + pub last_screen: View, pub lore_api_client: BlockingLoreAPIClient, } diff --git a/src/model/screens/mod.rs b/src/model/screens/mod.rs index 4fb235b..a39799d 100644 --- a/src/model/screens/mod.rs +++ b/src/model/screens/mod.rs @@ -5,7 +5,7 @@ pub mod latest; pub mod mail_list; #[derive(Debug, Clone, PartialEq)] -pub enum CurrentScreen { +pub enum View { MailingListSelection, BookmarkedPatchsets, LatestPatchsets, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index cdfab16..d855d6e 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -15,7 +15,7 @@ use ratatui::{ Frame, }; -use crate::model::{screens::CurrentScreen, Model}; +use crate::model::{screens::View, Model}; pub fn draw_ui(f: &mut Frame, model: &Model) { // Clear the whole screen for sanitizing reasons @@ -33,13 +33,13 @@ pub fn draw_ui(f: &mut Frame, model: &Model) { render_title(f, chunks[0]); match model.current_screen { - CurrentScreen::MailingListSelection => mail_list::render_main(f, model, chunks[1]), - CurrentScreen::BookmarkedPatchsets => { + View::MailingListSelection => mail_list::render_main(f, model, chunks[1]), + View::BookmarkedPatchsets => { bookmarked::render_main(f, &model.bookmarked_patchsets, chunks[1]) } - CurrentScreen::LatestPatchsets => latest::render_main(f, model, chunks[1]), - CurrentScreen::PatchsetDetails => details_actions::render_main(f, model, chunks[1]), - CurrentScreen::EditConfig => edit_config::render_main(f, model, chunks[1]), + View::LatestPatchsets => latest::render_main(f, model, chunks[1]), + View::PatchsetDetails => details_actions::render_main(f, model, chunks[1]), + View::EditConfig => edit_config::render_main(f, model, chunks[1]), } navigation_bar::render(f, model, chunks[2]); diff --git a/src/ui/navigation_bar.rs b/src/ui/navigation_bar.rs index 58bfd39..d9b394e 100644 --- a/src/ui/navigation_bar.rs +++ b/src/ui/navigation_bar.rs @@ -5,17 +5,17 @@ use ratatui::{ Frame, }; -use crate::model::{screens::CurrentScreen, Model}; +use crate::model::{screens::View, Model}; use super::{bookmarked, details_actions, edit_config, latest, mail_list}; pub fn render(f: &mut Frame, model: &Model, chunk: Rect) { let mode_footer_text = match model.current_screen { - CurrentScreen::MailingListSelection => mail_list::mode_footer_text(model), - CurrentScreen::BookmarkedPatchsets => bookmarked::mode_footer_text(), - CurrentScreen::LatestPatchsets => latest::mode_footer_text(model), - CurrentScreen::PatchsetDetails => details_actions::mode_footer_text(), - CurrentScreen::EditConfig => edit_config::mode_footer_text(model), + View::MailingListSelection => mail_list::mode_footer_text(model), + View::BookmarkedPatchsets => bookmarked::mode_footer_text(), + View::LatestPatchsets => latest::mode_footer_text(model), + View::PatchsetDetails => details_actions::mode_footer_text(), + View::EditConfig => edit_config::mode_footer_text(model), }; let mode_footer = Paragraph::new(Line::from(mode_footer_text)) .block(Block::default().borders(Borders::ALL)) @@ -23,11 +23,11 @@ pub fn render(f: &mut Frame, model: &Model, chunk: Rect) { let current_keys_hint = { match model.current_screen { - CurrentScreen::MailingListSelection => mail_list::keys_hint(), - CurrentScreen::BookmarkedPatchsets => bookmarked::keys_hint(), - CurrentScreen::LatestPatchsets => latest::keys_hint(), - CurrentScreen::PatchsetDetails => details_actions::keys_hint(), - CurrentScreen::EditConfig => edit_config::keys_hint(model), + View::MailingListSelection => mail_list::keys_hint(), + View::BookmarkedPatchsets => bookmarked::keys_hint(), + View::LatestPatchsets => latest::keys_hint(), + View::PatchsetDetails => details_actions::keys_hint(), + View::EditConfig => edit_config::keys_hint(model), } }; From ac1616c2017dd218d3180fcaf7ff3a8364b5054c Mon Sep 17 00:00:00 2001 From: Ivin Joel Abraham Date: Sat, 28 Jun 2025 23:30:07 +0530 Subject: [PATCH 3/4] refactor: introduce ViewModel trait The MVVM refactor will require a trait for viewmodels to establish a reliable interface. Introduce a basic implementation of the trait with no usage. Signed-off-by: Ivin Joel Abraham --- src/viewmodels/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/viewmodels/mod.rs diff --git a/src/viewmodels/mod.rs b/src/viewmodels/mod.rs new file mode 100644 index 0000000..46c2d4c --- /dev/null +++ b/src/viewmodels/mod.rs @@ -0,0 +1,36 @@ +use ratatui::crossterm::event::Event; +#[allow(dead_code)] +/// A trait representing a view model that can handle key events and expose its state. +/// +/// # Associated Types +/// - `State`: The type representing the state of the view model. Must have a `'static` lifetime. +/// +/// # Required Methods +/// - `handle_key`: Handles a key event, potentially mutating the view model. +/// - `state`: Returns a reference to the current state. +pub trait ViewModel { + /// Handles a key event, potentially mutating the view model. + /// + /// # Arguments + /// + /// * `event` - The event to handle. + fn handle_key(&mut self, event: Event); + + /// Returns a reference to the current state. + fn state(&self) -> ViewModelState; +} + +/// `ViewModelState` is an enum intended to represent the various possible +/// state structs used by different screens in the application. Each variant +/// of this enum should wrap the state struct corresponding to a specific screen, +/// allowing for type-safe handling and dynamic dispatch of view model states. +/// +/// Extend this enum by adding variants for each screen's state struct, for example: +/// +/// ``` +/// enum ViewModelState { +/// HomeScreen(HomeScreenState), +/// SettingsScreen(SettingsScreenState), +/// // Add more variants as needed +/// } +pub enum ViewModelState {} From 4b10584ea5fd47613a9ca64d78610a5d45b4ac4a Mon Sep 17 00:00:00 2001 From: Ivin Joel Abraham Date: Sun, 29 Jun 2025 08:58:16 +0530 Subject: [PATCH 4/4] refactor: derive `Eq`, `Hash` and `Copy` for View To use as a key in a hashmap, a type must implement `Eq` and `Hash`. Additionally, `Copy` is required as well to be used in the .entry() method of Hashmap. Derive `Eq`, `Copy` and `Hash` on View to use it as a key in Hashmaps. Signed-off-by: Ivin Joel Abraham --- src/model/screens/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/screens/mod.rs b/src/model/screens/mod.rs index a39799d..9529c20 100644 --- a/src/model/screens/mod.rs +++ b/src/model/screens/mod.rs @@ -4,7 +4,7 @@ pub mod edit_config; pub mod latest; pub mod mail_list; -#[derive(Debug, Clone, PartialEq)] +#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash)] pub enum View { MailingListSelection, BookmarkedPatchsets,