From 49e0816018723dbfad44cb2b83a14d96b10e3e1a Mon Sep 17 00:00:00 2001 From: Kyosuke Fujimoto Date: Sat, 21 Dec 2024 10:21:55 +0900 Subject: [PATCH] Fix pages to have AppContext --- src/app.rs | 32 +++++++-------- src/pages/bucket_list.rs | 48 ++++++++++++----------- src/pages/help.rs | 20 +++++----- src/pages/initializing.rs | 16 ++++---- src/pages/object_detail.rs | 77 ++++++++++++++++--------------------- src/pages/object_list.rs | 49 +++++++++++------------ src/pages/object_preview.rs | 77 +++++++++++-------------------------- src/pages/page.rs | 42 ++++++++------------ 8 files changed, 153 insertions(+), 208 deletions(-) diff --git a/src/app.rs b/src/app.rs index 6222f47..5a3ca27 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{path::PathBuf, rc::Rc, sync::Arc}; use tokio::spawn; use crate::{ @@ -27,7 +27,7 @@ pub enum Notification { Error(String), } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct AppContext { pub config: Config, pub env: Environment, @@ -45,7 +45,7 @@ pub struct App { pub page_stack: PageStack, app_objects: AppObjects, client: Option>, - ctx: AppContext, + ctx: Rc, tx: Sender, notification: Notification, @@ -56,9 +56,10 @@ pub struct App { impl App { pub fn new(ctx: AppContext, tx: Sender, width: usize, height: usize) -> App { + let ctx = Rc::new(ctx); App { app_objects: AppObjects::default(), - page_stack: PageStack::new(ctx.theme.clone(), tx.clone()), + page_stack: PageStack::new(Rc::clone(&ctx), tx.clone()), client: None, ctx, tx, @@ -95,7 +96,7 @@ impl App { let bucket_list_page = Page::of_bucket_list( self.app_objects.get_bucket_items(), - self.ctx.theme.clone(), + Rc::clone(&self.ctx), self.tx.clone(), ); self.page_stack.pop(); // remove initializing page @@ -145,8 +146,7 @@ impl App { let object_list_page = Page::of_object_list( current_object_items, object_key, - self.ctx.config.ui.clone(), - self.ctx.theme.clone(), + Rc::clone(&self.ctx), self.tx.clone(), ); self.page_stack.push(object_list_page); @@ -179,8 +179,7 @@ impl App { object_list_page.object_list(), current_object_key, object_list_page.list_state(), - self.ctx.config.ui.clone(), - self.ctx.theme.clone(), + Rc::clone(&self.ctx), self.tx.clone(), ); self.page_stack.push(object_detail_page); @@ -196,8 +195,7 @@ impl App { let new_object_list_page = Page::of_object_list( current_object_items, object_key, - self.ctx.config.ui.clone(), - self.ctx.theme.clone(), + Rc::clone(&self.ctx), self.tx.clone(), ); self.page_stack.push(new_object_list_page); @@ -264,8 +262,7 @@ impl App { let object_list_page = Page::of_object_list( items, current_object_key, - self.ctx.config.ui.clone(), - self.ctx.theme.clone(), + Rc::clone(&self.ctx), self.tx.clone(), ); self.page_stack.push(object_list_page); @@ -333,8 +330,7 @@ impl App { object_page.object_list(), map_key, object_page.list_state(), - self.ctx.config.ui.clone(), - self.ctx.theme.clone(), + Rc::clone(&self.ctx), self.tx.clone(), ); self.page_stack.push(object_detail_page); @@ -404,7 +400,7 @@ impl App { if helps.is_empty() { return; } - let help_page = Page::of_help(helps, self.ctx.theme.clone(), self.tx.clone()); + let help_page = Page::of_help(helps, Rc::clone(&self.ctx), self.tx.clone()); self.page_stack.push(help_page); } @@ -527,9 +523,7 @@ impl App { obj, path.to_string_lossy().into(), current_object_key, - self.ctx.config.preview.clone(), - self.ctx.env.clone(), - self.ctx.theme.clone(), + Rc::clone(&self.ctx), self.tx.clone(), ); self.page_stack.push(object_preview_page); diff --git a/src/pages/bucket_list.rs b/src/pages/bucket_list.rs index 5acd8ca..227f7d1 100644 --- a/src/pages/bucket_list.rs +++ b/src/pages/bucket_list.rs @@ -1,4 +1,4 @@ -use std::cmp::Ordering; +use std::{cmp::Ordering, rc::Rc}; use laurier::{highlight::highlight_matched_text, key_code, key_code_char}; use ratatui::{ @@ -11,6 +11,7 @@ use ratatui::{ }; use crate::{ + app::AppContext, color::ColorTheme, event::{AppEventType, Sender}, object::{BucketItem, ObjectKey}, @@ -32,7 +33,7 @@ pub struct BucketListPage { filter_input_state: InputDialogState, sort_dialog_state: BucketListSortDialogState, - theme: ColorTheme, + ctx: Rc, tx: Sender, } @@ -45,7 +46,7 @@ enum ViewState { } impl BucketListPage { - pub fn new(bucket_items: Vec, theme: ColorTheme, tx: Sender) -> Self { + pub fn new(bucket_items: Vec, ctx: Rc, tx: Sender) -> Self { let items_len = bucket_items.len(); let view_indices = (0..items_len).collect(); Self { @@ -55,7 +56,7 @@ impl BucketListPage { list_state: ScrollListState::new(items_len), filter_input_state: InputDialogState::default(), sort_dialog_state: BucketListSortDialogState::default(), - theme, + ctx, tx, } } @@ -174,20 +175,20 @@ impl BucketListPage { &self.bucket_items, &self.view_indices, self.filter_input_state.input(), - &self.theme, + &self.ctx.theme, offset, selected, area, ); - let list = ScrollList::new(list_items).theme(&self.theme); + let list = ScrollList::new(list_items).theme(&self.ctx.theme); f.render_stateful_widget(list, area, &mut self.list_state); if let ViewState::FilterDialog = self.view_state { let filter_dialog = InputDialog::default() .title("Filter") .max_width(30) - .theme(&self.theme); + .theme(&self.ctx.theme); f.render_stateful_widget(filter_dialog, area, &mut self.filter_input_state); let (cursor_x, cursor_y) = self.filter_input_state.cursor(); @@ -195,12 +196,13 @@ impl BucketListPage { } if let ViewState::SortDialog = self.view_state { - let sort_dialog = BucketListSortDialog::new(self.sort_dialog_state).theme(&self.theme); + let sort_dialog = + BucketListSortDialog::new(self.sort_dialog_state).theme(&self.ctx.theme); f.render_widget(sort_dialog, area); } if let ViewState::CopyDetailDialog(state) = &mut self.view_state { - let copy_detail_dialog = CopyDetailDialog::default().theme(&self.theme); + let copy_detail_dialog = CopyDetailDialog::default().theme(&self.ctx.theme); f.render_stateful_widget(copy_detail_dialog, area, state); } } @@ -518,7 +520,7 @@ mod tests { #[test] fn test_render_without_scroll() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -527,7 +529,7 @@ mod tests { .into_iter() .map(bucket_item) .collect(); - let mut page = BucketListPage::new(items, theme, tx); + let mut page = BucketListPage::new(items, ctx, tx); let area = Rect::new(0, 0, 30, 10); page.render(f, area); })?; @@ -556,7 +558,7 @@ mod tests { #[test] fn test_render_with_scroll() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -564,7 +566,7 @@ mod tests { let items = (0..16) .map(|i| bucket_item(&format!("bucket{}", i + 1))) .collect(); - let mut page = BucketListPage::new(items, theme, tx); + let mut page = BucketListPage::new(items, ctx, tx); let area = Rect::new(0, 0, 30, 10); page.render(f, area); })?; @@ -594,7 +596,7 @@ mod tests { #[test] fn test_render_filter_items() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -602,7 +604,7 @@ mod tests { .into_iter() .map(bucket_item) .collect(); - let mut page = BucketListPage::new(items, theme, tx); + let mut page = BucketListPage::new(items, ctx, tx); let area = Rect::new(0, 0, 30, 10); page.handle_key(KeyEvent::from(KeyCode::Char('/'))); @@ -671,7 +673,7 @@ mod tests { #[test] fn test_render_sort_items() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -679,7 +681,7 @@ mod tests { .into_iter() .map(bucket_item) .collect(); - let mut page = BucketListPage::new(items, theme, tx); + let mut page = BucketListPage::new(items, ctx, tx); let area = Rect::new(0, 0, 30, 10); page.handle_key(KeyEvent::from(KeyCode::Char('o'))); @@ -717,14 +719,14 @@ mod tests { #[test] fn test_filter_items() { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let items = ["foo", "bar", "baz", "qux", "foobar"] .into_iter() .map(bucket_item) .collect(); - let mut page = BucketListPage::new(items, theme, tx); + let mut page = BucketListPage::new(items, ctx, tx); page.handle_key(KeyEvent::from(KeyCode::Char('/'))); page.handle_key(KeyEvent::from(KeyCode::Char('b'))); @@ -752,14 +754,14 @@ mod tests { #[test] fn test_sort_items() { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let items = ["foo", "bar", "baz", "qux", "foobar"] .into_iter() .map(bucket_item) .collect(); - let mut page = BucketListPage::new(items, theme, tx); + let mut page = BucketListPage::new(items, ctx, tx); page.handle_key(KeyEvent::from(KeyCode::Char('o'))); @@ -784,14 +786,14 @@ mod tests { #[test] fn test_filter_and_sort_items() { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let items = ["foo", "bar", "baz", "qux", "foobar"] .into_iter() .map(bucket_item) .collect(); - let mut page = BucketListPage::new(items, theme, tx); + let mut page = BucketListPage::new(items, ctx, tx); page.handle_key(KeyEvent::from(KeyCode::Char('/'))); page.handle_key(KeyEvent::from(KeyCode::Char('b'))); diff --git a/src/pages/help.rs b/src/pages/help.rs index 91a0699..041bf30 100644 --- a/src/pages/help.rs +++ b/src/pages/help.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use laurier::{key_code, key_code_char}; use ratatui::{ buffer::Buffer, @@ -10,7 +12,7 @@ use ratatui::{ }; use crate::{ - color::ColorTheme, + app::AppContext, constant::{APP_DESCRIPTION, APP_HOMEPAGE, APP_NAME, APP_VERSION}, event::{AppEventType, Sender}, pages::util::build_short_helps, @@ -22,13 +24,13 @@ use crate::{ pub struct HelpPage { helps: Vec, - theme: ColorTheme, + ctx: Rc, tx: Sender, } impl HelpPage { - pub fn new(helps: Vec, theme: ColorTheme, tx: Sender) -> Self { - Self { helps, theme, tx } + pub fn new(helps: Vec, ctx: Rc, tx: Sender) -> Self { + Self { helps, ctx, tx } } pub fn handle_key(&mut self, key: KeyEvent) { @@ -47,7 +49,7 @@ impl HelpPage { let block = Block::bordered() .padding(Padding::horizontal(1)) .title(APP_NAME) - .fg(self.theme.fg); + .fg(self.ctx.theme.fg); let content_area = block.inner(area); @@ -63,9 +65,9 @@ impl HelpPage { APP_DESCRIPTION, APP_VERSION, APP_HOMEPAGE, - self.theme.link, + self.ctx.theme.link, ); - let divider = Divider::default().color(self.theme.divider); + let divider = Divider::default().color(self.ctx.theme.divider); let help = Help::new(&self.helps); f.render_widget(block, area); @@ -187,7 +189,7 @@ mod tests { #[test] fn test_render() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -201,7 +203,7 @@ mod tests { .iter() .map(|s| s.to_string()) .collect(); - let mut page = HelpPage::new(helps, theme, tx); + let mut page = HelpPage::new(helps, ctx, tx); let area = Rect::new(0, 0, 70, 20); page.render(f, area); })?; diff --git a/src/pages/initializing.rs b/src/pages/initializing.rs index 1dd965c..a384cab 100644 --- a/src/pages/initializing.rs +++ b/src/pages/initializing.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use laurier::key_code; use ratatui::{ crossterm::event::{KeyCode, KeyEvent}, @@ -8,20 +10,20 @@ use ratatui::{ }; use crate::{ - color::ColorTheme, + app::AppContext, event::{AppEventType, Sender}, pages::util::build_short_helps, }; #[derive(Debug)] pub struct InitializingPage { - theme: ColorTheme, + ctx: Rc, tx: Sender, } impl InitializingPage { - pub fn new(theme: ColorTheme, tx: Sender) -> Self { - Self { theme, tx } + pub fn new(ctx: Rc, tx: Sender) -> Self { + Self { ctx, tx } } pub fn handle_key(&mut self, key: KeyEvent) { @@ -31,7 +33,7 @@ impl InitializingPage { } pub fn render(&mut self, f: &mut Frame, area: Rect) { - let content = Block::bordered().fg(self.theme.fg); + let content = Block::bordered().fg(self.ctx.theme.fg); f.render_widget(content, area); } @@ -54,12 +56,12 @@ mod tests { #[test] fn test_render() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; terminal.draw(|f| { - let mut page = InitializingPage::new(theme, tx); + let mut page = InitializingPage::new(ctx, tx); let area = Rect::new(0, 0, 30, 10); page.render(f, area); })?; diff --git a/src/pages/object_detail.rs b/src/pages/object_detail.rs index ffe54ef..0f79401 100644 --- a/src/pages/object_detail.rs +++ b/src/pages/object_detail.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use laurier::{key_code, key_code_char}; use ratatui::{ buffer::Buffer, @@ -10,6 +12,7 @@ use ratatui::{ }; use crate::{ + app::AppContext, color::ColorTheme, config::UiConfig, event::{AppEventType, Sender}, @@ -34,8 +37,7 @@ pub struct ObjectDetailPage { object_items: Vec, list_state: ScrollListState, - ui_config: UiConfig, - theme: ColorTheme, + ctx: Rc, tx: Sender, } @@ -67,11 +69,10 @@ impl ObjectDetailPage { object_items: Vec, object_key: ObjectKey, list_state: ScrollListState, - ui_config: UiConfig, - theme: ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { - let detail_tab_state = DetailTabState::new(&file_detail, &ui_config); + let detail_tab_state = DetailTabState::new(&file_detail, &ctx.config.ui); Self { file_detail, file_versions: Vec::new(), @@ -80,8 +81,7 @@ impl ObjectDetailPage { view_state: ViewState::Default, object_items, list_state, - ui_config, - theme, + ctx, tx, } } @@ -192,29 +192,29 @@ impl ObjectDetailPage { offset, selected, chunks[0], - &self.theme, + &self.ctx.theme, ); - let list = ScrollList::new(list_items).theme(&self.theme); + let list = ScrollList::new(list_items).theme(&self.ctx.theme); f.render_stateful_widget(list, chunks[0], &mut self.list_state); - let block = Block::bordered().fg(self.theme.fg); + let block = Block::bordered().fg(self.ctx.theme.fg); f.render_widget(block, chunks[1]); let chunks = Layout::vertical([Constraint::Length(2), Constraint::Min(0)]) .margin(1) .split(chunks[1]); - let tabs = build_tabs(&self.tab, &self.theme); + let tabs = build_tabs(&self.tab, &self.ctx.theme); f.render_widget(tabs, chunks[0]); match self.tab { Tab::Detail(ref mut state) => { - let detail = DetailTab::new(&self.theme); + let detail = DetailTab::new(&self.ctx.theme); f.render_stateful_widget(detail, chunks[1], state); } Tab::Version(ref mut state) => { - let version = VersionTab::new(&self.theme); + let version = VersionTab::new(&self.ctx.theme); f.render_stateful_widget(version, chunks[1], state); } } @@ -223,7 +223,7 @@ impl ObjectDetailPage { let save_dialog = InputDialog::default() .title("Save As") .max_width(40) - .theme(&self.theme); + .theme(&self.ctx.theme); f.render_stateful_widget(save_dialog, area, state); let (cursor_x, cursor_y) = state.cursor(); @@ -231,7 +231,7 @@ impl ObjectDetailPage { } if let ViewState::CopyDetailDialog(state) = &mut self.view_state { - let copy_detail_dialog = CopyDetailDialog::default().theme(&self.theme); + let copy_detail_dialog = CopyDetailDialog::default().theme(&self.ctx.theme); f.render_stateful_widget(copy_detail_dialog, area, state); } } @@ -332,11 +332,14 @@ impl ObjectDetailPage { } pub fn select_detail_tab(&mut self) { - self.tab = Tab::Detail(DetailTabState::new(&self.file_detail, &self.ui_config)); + self.tab = Tab::Detail(DetailTabState::new(&self.file_detail, &self.ctx.config.ui)); } pub fn select_versions_tab(&mut self) { - self.tab = Tab::Version(VersionTabState::new(&self.file_versions, &self.ui_config)); + self.tab = Tab::Version(VersionTabState::new( + &self.file_versions, + &self.ctx.config.ui, + )); } pub fn set_versions(&mut self, versions: Vec) { @@ -749,21 +752,19 @@ mod tests { #[test] fn test_render_detail_tab() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; terminal.draw(|f| { let (items, file_detail, _file_versions, object_key) = fixtures(); let items_len = items.len(); - let ui_config = UiConfig::default(); let mut page = ObjectDetailPage::new( file_detail, items, object_key, ScrollListState::new(items_len), - ui_config, - theme, + ctx, tx, ); let area = Rect::new(0, 0, 60, 20); @@ -817,22 +818,20 @@ mod tests { #[test] fn test_render_detail_tab_with_config() -> std::io::Result<()> { - let theme = ColorTheme::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; terminal.draw(|f| { let (items, file_detail, _file_versions, object_key) = fixtures(); let items_len = items.len(); - let mut ui_config = UiConfig::default(); - ui_config.object_detail.date_format = "%Y/%m/%d".to_string(); + let mut ctx = AppContext::default(); + ctx.config.ui.object_detail.date_format = "%Y/%m/%d".to_string(); let mut page = ObjectDetailPage::new( file_detail, items, object_key, ScrollListState::new(items_len), - ui_config, - theme, + Rc::new(ctx), tx, ); let area = Rect::new(0, 0, 60, 20); @@ -886,21 +885,19 @@ mod tests { #[test] fn test_render_version_tab() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; terminal.draw(|f| { let (items, file_detail, file_versions, object_key) = fixtures(); let items_len = items.len(); - let ui_config = UiConfig::default(); let mut page = ObjectDetailPage::new( file_detail, items, object_key, ScrollListState::new(items_len), - ui_config, - theme, + ctx, tx, ); page.set_versions(file_versions); @@ -956,22 +953,20 @@ mod tests { #[test] fn test_render_version_tab_with_config() -> std::io::Result<()> { - let theme = ColorTheme::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; terminal.draw(|f| { let (items, file_detail, file_versions, object_key) = fixtures(); let items_len = items.len(); - let mut ui_config = UiConfig::default(); - ui_config.object_detail.date_format = "%Y/%m/%d".to_string(); + let mut ctx = AppContext::default(); + ctx.config.ui.object_detail.date_format = "%Y/%m/%d".to_string(); let mut page = ObjectDetailPage::new( file_detail, items, object_key, ScrollListState::new(items_len), - ui_config, - theme, + Rc::new(ctx), tx, ); page.set_versions(file_versions); @@ -1027,21 +1022,19 @@ mod tests { #[test] fn test_render_save_dialog_detail_tab() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; terminal.draw(|f| { let (items, file_detail, _file_versions, object_key) = fixtures(); let items_len = items.len(); - let ui_config = UiConfig::default(); let mut page = ObjectDetailPage::new( file_detail, items, object_key, ScrollListState::new(items_len), - ui_config, - theme, + ctx, tx, ); page.open_save_dialog(); @@ -1094,21 +1087,19 @@ mod tests { #[test] fn test_render_copy_detail_dialog_detail_tab() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; terminal.draw(|f| { let (items, file_detail, _file_versions, object_key) = fixtures(); let items_len = items.len(); - let ui_config = UiConfig::default(); let mut page = ObjectDetailPage::new( file_detail, items, object_key, ScrollListState::new(items_len), - ui_config, - theme, + ctx, tx, ); page.open_copy_detail_dialog(); diff --git a/src/pages/object_list.rs b/src/pages/object_list.rs index 09eded3..05f2a19 100644 --- a/src/pages/object_list.rs +++ b/src/pages/object_list.rs @@ -1,4 +1,4 @@ -use std::cmp::Ordering; +use std::{cmp::Ordering, rc::Rc}; use chrono::{DateTime, Local}; use laurier::{highlight::highlight_matched_text, key_code, key_code_char}; @@ -12,6 +12,7 @@ use ratatui::{ }; use crate::{ + app::AppContext, color::ColorTheme, config::UiConfig, event::{AppEventType, Sender}, @@ -37,8 +38,7 @@ pub struct ObjectListPage { filter_input_state: InputDialogState, sort_dialog_state: ObjectListSortDialogState, - ui_config: UiConfig, - theme: ColorTheme, + ctx: Rc, tx: Sender, } @@ -54,8 +54,7 @@ impl ObjectListPage { pub fn new( object_items: Vec, object_key: ObjectKey, - ui_config: UiConfig, - theme: ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { let items_len = object_items.len(); @@ -68,8 +67,7 @@ impl ObjectListPage { list_state: ScrollListState::new(items_len), filter_input_state: InputDialogState::default(), sort_dialog_state: ObjectListSortDialogState::default(), - ui_config, - theme, + ctx, tx, } } @@ -197,18 +195,18 @@ impl ObjectListPage { offset, selected, area, - &self.ui_config, - &self.theme, + &self.ctx.config.ui, + &self.ctx.theme, ); - let list = ScrollList::new(list_items).theme(&self.theme); + let list = ScrollList::new(list_items).theme(&self.ctx.theme); f.render_stateful_widget(list, area, &mut self.list_state); if let ViewState::FilterDialog = self.view_state { let filter_dialog = InputDialog::default() .title("Filter") .max_width(30) - .theme(&self.theme); + .theme(&self.ctx.theme); f.render_stateful_widget(filter_dialog, area, &mut self.filter_input_state); let (cursor_x, cursor_y) = self.filter_input_state.cursor(); @@ -216,12 +214,13 @@ impl ObjectListPage { } if let ViewState::SortDialog = self.view_state { - let sort_dialog = ObjectListSortDialog::new(self.sort_dialog_state).theme(&self.theme); + let sort_dialog = + ObjectListSortDialog::new(self.sort_dialog_state).theme(&self.ctx.theme); f.render_widget(sort_dialog, area); } if let ViewState::CopyDetailDialog(state) = &mut self.view_state { - let copy_detail_dialog = CopyDetailDialog::default().theme(&self.theme); + let copy_detail_dialog = CopyDetailDialog::default().theme(&self.ctx.theme); f.render_stateful_widget(copy_detail_dialog, area, state); } } @@ -661,7 +660,7 @@ mod tests { #[test] fn test_render_without_scroll() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -676,8 +675,7 @@ mod tests { bucket_name: "test-bucket".to_string(), object_path: vec!["path".to_string(), "to".to_string()], }; - let ui_config = UiConfig::default(); - let mut page = ObjectListPage::new(items, object_key, ui_config, theme, tx); + let mut page = ObjectListPage::new(items, object_key, ctx, tx); let area = Rect::new(0, 0, 60, 10); page.render(f, area); })?; @@ -709,7 +707,7 @@ mod tests { #[test] fn test_render_with_scroll() -> std::io::Result<()> { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -721,8 +719,7 @@ mod tests { bucket_name: "test-bucket".to_string(), object_path: vec!["path".to_string(), "to".to_string()], }; - let ui_config = UiConfig::default(); - let mut page = ObjectListPage::new(items, object_key, ui_config, theme, tx); + let mut page = ObjectListPage::new(items, object_key, ctx, tx); let area = Rect::new(0, 0, 60, 10); page.render(f, area); })?; @@ -752,7 +749,6 @@ mod tests { #[test] fn test_render_with_config() -> std::io::Result<()> { - let theme = ColorTheme::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -767,10 +763,10 @@ mod tests { bucket_name: "test-bucket".to_string(), object_path: vec!["path".to_string(), "to".to_string()], }; - let mut ui_config = UiConfig::default(); - ui_config.object_list.date_format = "%Y/%m/%d".to_string(); - ui_config.object_list.date_width = 10; - let mut page = ObjectListPage::new(items, object_key, ui_config, theme, tx); + let mut ctx = AppContext::default(); + ctx.config.ui.object_list.date_format = "%Y/%m/%d".to_string(); + ctx.config.ui.object_list.date_width = 10; + let mut page = ObjectListPage::new(items, object_key, Rc::new(ctx), tx); let area = Rect::new(0, 0, 60, 10); page.render(f, area); })?; @@ -802,7 +798,7 @@ mod tests { #[test] fn test_sort_items() { - let theme = ColorTheme::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let items = vec![ object_dir_item("rid"), @@ -815,8 +811,7 @@ mod tests { bucket_name: "test-bucket".to_string(), object_path: vec!["path".to_string(), "to".to_string()], }; - let ui_config = UiConfig::default(); - let mut page = ObjectListPage::new(items, object_key, ui_config, theme, tx); + let mut page = ObjectListPage::new(items, object_key, ctx, tx); page.handle_key(KeyEvent::from(KeyCode::Char('o'))); page.handle_key(KeyEvent::from(KeyCode::Char('j'))); // select NameAsc diff --git a/src/pages/object_preview.rs b/src/pages/object_preview.rs index d40a2a7..3ca8e1e 100644 --- a/src/pages/object_preview.rs +++ b/src/pages/object_preview.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use laurier::{key_code, key_code_char}; use ratatui::{ crossterm::event::{KeyCode, KeyEvent}, @@ -6,9 +8,8 @@ use ratatui::{ }; use crate::{ - color::ColorTheme, - config::PreviewConfig, - environment::{Environment, ImagePicker}, + app::AppContext, + environment::ImagePicker, event::{AppEventType, Sender}, object::{FileDetail, ObjectKey, RawObject}, pages::util::{build_helps, build_short_helps}, @@ -30,7 +31,7 @@ pub struct ObjectPreviewPage { view_state: ViewState, - theme: ColorTheme, + ctx: Rc, tx: Sender, } @@ -54,13 +55,12 @@ impl ObjectPreviewPage { object: RawObject, path: String, object_key: ObjectKey, - preview_config: PreviewConfig, - env: Environment, - theme: ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { let preview_type = if infer::is_image(&object.bytes) { - let (state, msg) = ImagePreviewState::new(&object.bytes, env.image_picker.into()); + let (state, msg) = + ImagePreviewState::new(&object.bytes, ctx.env.image_picker.clone().into()); if let Some(msg) = msg { tx.send(AppEventType::NotifyWarn(msg)); } @@ -69,8 +69,8 @@ impl ObjectPreviewPage { let (state, msg) = TextPreviewState::new( &file_detail, &object, - preview_config.highlight, - &preview_config.highlight_theme, + ctx.config.preview.highlight, + &ctx.config.preview.highlight_theme, ); if let Some(msg) = msg { tx.send(AppEventType::NotifyWarn(msg)); @@ -86,7 +86,7 @@ impl ObjectPreviewPage { path, object_key, view_state: ViewState::Default, - theme, + ctx, tx, } } @@ -186,7 +186,7 @@ impl ObjectPreviewPage { let preview = TextPreview::new( self.file_detail.name.as_str(), self.file_version_id.as_deref(), - &self.theme, + &self.ctx.theme, ); f.render_stateful_widget(preview, area, state); } @@ -203,7 +203,7 @@ impl ObjectPreviewPage { let save_dialog = InputDialog::default() .title("Save As") .max_width(40) - .theme(&self.theme); + .theme(&self.ctx.theme); f.render_stateful_widget(save_dialog, area, state); let (cursor_x, cursor_y) = state.cursor(); @@ -346,8 +346,7 @@ mod tests { #[test] fn test_render_without_scroll() -> std::io::Result<()> { - let theme = ColorTheme::default(); - let env = Environment::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -365,18 +364,8 @@ mod tests { bucket_name: "test-bucket".to_string(), object_path: vec![file_path.clone()], }; - let preview_config = PreviewConfig::default(); - let mut page = ObjectPreviewPage::new( - file_detail, - None, - object, - file_path, - object_key, - preview_config, - env, - theme, - tx, - ); + let mut page = + ObjectPreviewPage::new(file_detail, None, object, file_path, object_key, ctx, tx); let area = Rect::new(0, 0, 30, 10); page.render(f, area); })?; @@ -405,8 +394,7 @@ mod tests { #[test] fn test_render_with_scroll() -> std::io::Result<()> { - let theme = ColorTheme::default(); - let env = Environment::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -419,18 +407,8 @@ mod tests { bucket_name: "test-bucket".to_string(), object_path: vec![file_path.clone()], }; - let preview_config = PreviewConfig::default(); - let mut page = ObjectPreviewPage::new( - file_detail, - None, - object, - file_path, - object_key, - preview_config, - env, - theme, - tx, - ); + let mut page = + ObjectPreviewPage::new(file_detail, None, object, file_path, object_key, ctx, tx); let area = Rect::new(0, 0, 30, 10); page.render(f, area); })?; @@ -459,8 +437,7 @@ mod tests { #[test] fn test_render_save_dialog_without_scroll() -> std::io::Result<()> { - let theme = ColorTheme::default(); - let env = Environment::default(); + let ctx = Rc::default(); let (tx, _) = event::new(); let mut terminal = setup_terminal()?; @@ -478,18 +455,8 @@ mod tests { bucket_name: "test-bucket".to_string(), object_path: vec![file_path.clone()], }; - let preview_config = PreviewConfig::default(); - let mut page = ObjectPreviewPage::new( - file_detail, - None, - object, - file_path, - object_key, - preview_config, - env, - theme, - tx, - ); + let mut page = + ObjectPreviewPage::new(file_detail, None, object, file_path, object_key, ctx, tx); page.open_save_dialog(); let area = Rect::new(0, 0, 30, 10); page.render(f, area); diff --git a/src/pages/page.rs b/src/pages/page.rs index a2ae27d..1c4e411 100644 --- a/src/pages/page.rs +++ b/src/pages/page.rs @@ -1,9 +1,9 @@ +use std::rc::Rc; + use ratatui::{crossterm::event::KeyEvent, layout::Rect, Frame}; use crate::{ - color::ColorTheme, - config::{PreviewConfig, UiConfig}, - environment::Environment, + app::AppContext, event::Sender, object::{BucketItem, FileDetail, ObjectItem, ObjectKey, RawObject}, pages::{ @@ -71,26 +71,24 @@ impl Page { } impl Page { - pub fn of_initializing(theme: ColorTheme, tx: Sender) -> Self { - Self::Initializing(Box::new(InitializingPage::new(theme, tx))) + pub fn of_initializing(ctx: Rc, tx: Sender) -> Self { + Self::Initializing(Box::new(InitializingPage::new(ctx, tx))) } - pub fn of_bucket_list(bucket_items: Vec, theme: ColorTheme, tx: Sender) -> Self { - Self::BucketList(Box::new(BucketListPage::new(bucket_items, theme, tx))) + pub fn of_bucket_list(bucket_items: Vec, ctx: Rc, tx: Sender) -> Self { + Self::BucketList(Box::new(BucketListPage::new(bucket_items, ctx, tx))) } pub fn of_object_list( object_items: Vec, object_key: ObjectKey, - ui_config: UiConfig, - theme: ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { Self::ObjectList(Box::new(ObjectListPage::new( object_items, object_key, - ui_config, - theme, + ctx, tx, ))) } @@ -100,8 +98,7 @@ impl Page { object_items: Vec, object_key: ObjectKey, list_state: ScrollListState, - ui_config: UiConfig, - theme: ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { Self::ObjectDetail(Box::new(ObjectDetailPage::new( @@ -109,8 +106,7 @@ impl Page { object_items, object_key, list_state, - ui_config, - theme, + ctx, tx, ))) } @@ -121,9 +117,7 @@ impl Page { object: RawObject, path: String, object_key: ObjectKey, - preview_config: PreviewConfig, - env: Environment, - theme: ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { Self::ObjectPreview(Box::new(ObjectPreviewPage::new( @@ -132,15 +126,13 @@ impl Page { object, path, object_key, - preview_config, - env, - theme, + ctx, tx, ))) } - pub fn of_help(helps: Vec, theme: ColorTheme, tx: Sender) -> Self { - Self::Help(Box::new(HelpPage::new(helps, theme, tx))) + pub fn of_help(helps: Vec, ctx: Rc, tx: Sender) -> Self { + Self::Help(Box::new(HelpPage::new(helps, ctx, tx))) } pub fn as_bucket_list(&self) -> &BucketListPage { @@ -192,9 +184,9 @@ pub struct PageStack { } impl PageStack { - pub fn new(theme: ColorTheme, tx: Sender) -> PageStack { + pub fn new(ctx: Rc, tx: Sender) -> PageStack { PageStack { - stack: vec![Page::of_initializing(theme, tx)], + stack: vec![Page::of_initializing(ctx, tx)], } }