router implementation
This commit is contained in:
@@ -12,6 +12,7 @@ pub mod buffer;
|
||||
pub mod sidebar;
|
||||
pub mod search;
|
||||
pub mod bottom_panel;
|
||||
pub mod pages;
|
||||
|
||||
pub use ui::run_ui;
|
||||
|
||||
|
||||
@@ -4,10 +4,7 @@ use crossterm::event::KeyEvent;
|
||||
use crate::config::binds::config::Config;
|
||||
use crate::state::app::state::AppState;
|
||||
use crate::state::pages::form::FormState;
|
||||
use crate::state::pages::auth::LoginState;
|
||||
use crate::state::pages::auth::RegisterState;
|
||||
use crate::state::pages::intro::IntroState;
|
||||
use crate::state::pages::admin::AdminState;
|
||||
use crate::pages::routing::{Router, Page};
|
||||
use crate::ui::handlers::context::UiContext;
|
||||
use crate::modes::handlers::event::EventOutcome;
|
||||
use crate::modes::general::command_navigation::{handle_command_navigation_event, NavigationState};
|
||||
@@ -18,10 +15,7 @@ pub async fn handle_navigation_event(
|
||||
key: KeyEvent,
|
||||
config: &Config,
|
||||
app_state: &mut AppState,
|
||||
login_state: &mut LoginState,
|
||||
register_state: &mut RegisterState,
|
||||
intro_state: &mut IntroState,
|
||||
admin_state: &mut AdminState,
|
||||
router: &mut Router,
|
||||
command_mode: &mut bool,
|
||||
command_input: &mut String,
|
||||
command_message: &mut String,
|
||||
@@ -35,19 +29,19 @@ pub async fn handle_navigation_event(
|
||||
if let Some(action) = config.get_general_action(key.code, key.modifiers) {
|
||||
match action {
|
||||
"move_up" => {
|
||||
move_up(app_state, login_state, register_state, intro_state, admin_state);
|
||||
move_up(app_state, router);
|
||||
return Ok(EventOutcome::Ok(String::new()));
|
||||
}
|
||||
"move_down" => {
|
||||
move_down(app_state, intro_state, admin_state);
|
||||
move_down(app_state, router);
|
||||
return Ok(EventOutcome::Ok(String::new()));
|
||||
}
|
||||
"next_option" => {
|
||||
next_option(app_state, intro_state);
|
||||
next_option(app_state, router);
|
||||
return Ok(EventOutcome::Ok(String::new()));
|
||||
}
|
||||
"previous_option" => {
|
||||
previous_option(app_state, intro_state);
|
||||
previous_option(app_state, router);
|
||||
return Ok(EventOutcome::Ok(String::new()));
|
||||
}
|
||||
"next_field" => {
|
||||
@@ -67,18 +61,21 @@ pub async fn handle_navigation_event(
|
||||
return Ok(EventOutcome::Ok(String::new()));
|
||||
}
|
||||
"select" => {
|
||||
let (context, index) = if app_state.ui.show_intro {
|
||||
(UiContext::Intro, intro_state.selected_option)
|
||||
} else if app_state.ui.show_login && app_state.ui.focus_outside_canvas {
|
||||
let (context, index) = match &router.current {
|
||||
Page::Intro(state) => (UiContext::Intro, state.selected_option),
|
||||
Page::Login(_) if app_state.ui.focus_outside_canvas => {
|
||||
(UiContext::Login, app_state.focused_button_index)
|
||||
} else if app_state.ui.show_register && app_state.ui.focus_outside_canvas {
|
||||
}
|
||||
Page::Register(_) if app_state.ui.focus_outside_canvas => {
|
||||
(UiContext::Register, app_state.focused_button_index)
|
||||
} else if app_state.ui.show_admin {
|
||||
(UiContext::Admin, admin_state.get_selected_index().unwrap_or(0))
|
||||
} else if app_state.ui.dialog.dialog_show {
|
||||
}
|
||||
Page::Admin(state) => {
|
||||
(UiContext::Admin, state.get_selected_index().unwrap_or(0))
|
||||
}
|
||||
_ if app_state.ui.dialog.dialog_show => {
|
||||
(UiContext::Dialog, app_state.ui.dialog.dialog_active_button_index)
|
||||
} else {
|
||||
return Ok(EventOutcome::Ok("Select (No Action)".to_string()));
|
||||
}
|
||||
_ => return Ok(EventOutcome::Ok("Select (No Action)".to_string())),
|
||||
};
|
||||
return Ok(EventOutcome::ButtonSelected { context, index });
|
||||
}
|
||||
@@ -88,61 +85,77 @@ pub async fn handle_navigation_event(
|
||||
Ok(EventOutcome::Ok(String::new()))
|
||||
}
|
||||
|
||||
pub fn move_up(app_state: &mut AppState, login_state: &mut LoginState, register_state: &mut RegisterState, intro_state: &mut IntroState, admin_state: &mut AdminState) {
|
||||
if app_state.ui.focus_outside_canvas && app_state.ui.show_login || app_state.ui.show_register{
|
||||
pub fn move_up(app_state: &mut AppState, router: &mut Router) {
|
||||
match &mut router.current {
|
||||
Page::Login(state) if app_state.ui.focus_outside_canvas => {
|
||||
if app_state.focused_button_index == 0 {
|
||||
app_state.ui.focus_outside_canvas = false;
|
||||
if app_state.ui.show_login {
|
||||
let last_field_index = login_state.field_count().saturating_sub(1);
|
||||
login_state.set_current_field(last_field_index);
|
||||
let last_field_index = state.field_count().saturating_sub(1);
|
||||
state.set_current_field(last_field_index);
|
||||
} else {
|
||||
let last_field_index = register_state.field_count().saturating_sub(1);
|
||||
register_state.set_current_field(last_field_index);
|
||||
app_state.focused_button_index =
|
||||
app_state.focused_button_index.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
Page::Register(state) if app_state.ui.focus_outside_canvas => {
|
||||
if app_state.focused_button_index == 0 {
|
||||
app_state.ui.focus_outside_canvas = false;
|
||||
let last_field_index = state.field_count().saturating_sub(1);
|
||||
state.set_current_field(last_field_index);
|
||||
} else {
|
||||
app_state.focused_button_index = app_state.focused_button_index.saturating_sub(1);
|
||||
app_state.focused_button_index =
|
||||
app_state.focused_button_index.saturating_sub(1);
|
||||
}
|
||||
} else if app_state.ui.show_intro {
|
||||
intro_state.previous_option();
|
||||
} else if app_state.ui.show_admin {
|
||||
admin_state.previous();
|
||||
}
|
||||
Page::Intro(state) => state.previous_option(),
|
||||
Page::Admin(state) => state.previous(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_down(app_state: &mut AppState, intro_state: &mut IntroState, admin_state: &mut AdminState) {
|
||||
if app_state.ui.focus_outside_canvas && app_state.ui.show_login || app_state.ui.show_register {
|
||||
pub fn move_down(app_state: &mut AppState, router: &mut Router) {
|
||||
match &mut router.current {
|
||||
Page::Login(_) | Page::Register(_) if app_state.ui.focus_outside_canvas => {
|
||||
let num_general_elements = 2;
|
||||
if app_state.focused_button_index < num_general_elements - 1 {
|
||||
app_state.focused_button_index += 1;
|
||||
}
|
||||
} else if app_state.ui.show_intro {
|
||||
intro_state.next_option();
|
||||
} else if app_state.ui.show_admin {
|
||||
admin_state.next();
|
||||
}
|
||||
Page::Intro(state) => state.next_option(),
|
||||
Page::Admin(state) => state.next(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_option(app_state: &mut AppState, intro_state: &mut IntroState) {
|
||||
if app_state.ui.show_intro {
|
||||
intro_state.next_option();
|
||||
} else {
|
||||
// Get option count from state instead of parameter
|
||||
pub fn next_option(app_state: &mut AppState, router: &mut Router) {
|
||||
match &mut router.current {
|
||||
Page::Intro(state) => state.next_option(),
|
||||
Page::Admin(_) => {
|
||||
let option_count = app_state.profile_tree.profiles.len();
|
||||
app_state.focused_button_index = (app_state.focused_button_index + 1) % option_count;
|
||||
if option_count > 0 {
|
||||
app_state.focused_button_index =
|
||||
(app_state.focused_button_index + 1) % option_count;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn previous_option(app_state: &mut AppState, intro_state: &mut IntroState) {
|
||||
if app_state.ui.show_intro {
|
||||
intro_state.previous_option();
|
||||
} else {
|
||||
pub fn previous_option(app_state: &mut AppState, router: &mut Router) {
|
||||
match &mut router.current {
|
||||
Page::Intro(state) => state.previous_option(),
|
||||
Page::Admin(_) => {
|
||||
let option_count = app_state.profile_tree.profiles.len();
|
||||
if option_count > 0 {
|
||||
app_state.focused_button_index = if app_state.focused_button_index == 0 {
|
||||
option_count.saturating_sub(1)
|
||||
} else {
|
||||
app_state.focused_button_index - 1
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_field(form_state: &mut FormState) {
|
||||
@@ -164,7 +177,7 @@ pub fn prev_field(form_state: &mut FormState) {
|
||||
pub fn handle_enter_command_mode(
|
||||
command_mode: &mut bool,
|
||||
command_input: &mut String,
|
||||
command_message: &mut String
|
||||
command_message: &mut String,
|
||||
) {
|
||||
*command_mode = true;
|
||||
command_input.clear();
|
||||
|
||||
3
client/src/pages/mod.rs
Normal file
3
client/src/pages/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
// src/pages/mod.rs
|
||||
|
||||
pub mod routing;
|
||||
5
client/src/pages/routing/mod.rs
Normal file
5
client/src/pages/routing/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
// src/pages/routing/mod.rs
|
||||
|
||||
pub mod router;
|
||||
|
||||
pub use router::{Page, Router};
|
||||
36
client/src/pages/routing/router.rs
Normal file
36
client/src/pages/routing/router.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
// src/pages/routing/router.rs
|
||||
use crate::state::pages::{
|
||||
admin::AdminState,
|
||||
auth::{AuthState, LoginState, RegisterState},
|
||||
form::FormState,
|
||||
intro::IntroState,
|
||||
add_logic::AddLogicState,
|
||||
add_table::AddTableState,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Page {
|
||||
Intro(IntroState),
|
||||
Login(LoginState),
|
||||
Register(RegisterState),
|
||||
Admin(AdminState),
|
||||
AddLogic(AddLogicState),
|
||||
AddTable(AddTableState),
|
||||
Form(FormState),
|
||||
}
|
||||
|
||||
pub struct Router {
|
||||
pub current: Page,
|
||||
}
|
||||
|
||||
impl Router {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current: Page::Intro(IntroState::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn navigate(&mut self, page: Page) {
|
||||
self.current = page;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ pub struct AuthState {
|
||||
}
|
||||
|
||||
/// Represents the state of the Login form UI
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoginState {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
@@ -49,7 +49,7 @@ impl Default for LoginState {
|
||||
}
|
||||
|
||||
/// Represents the state of the Registration form UI
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RegisterState {
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct FieldDefinition {
|
||||
pub link_target_table: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FormState {
|
||||
pub id: i64,
|
||||
pub profile_name: String,
|
||||
|
||||
@@ -28,16 +28,11 @@ use ratatui::{
|
||||
layout::{Constraint, Direction, Layout},
|
||||
Frame,
|
||||
};
|
||||
use crate::pages::routing::{Router, Page};
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn render_ui(
|
||||
f: &mut Frame,
|
||||
form_state: &mut FormState,
|
||||
auth_state: &mut AuthState,
|
||||
login_state: &LoginState,
|
||||
register_state: &RegisterState,
|
||||
intro_state: &IntroState,
|
||||
admin_state: &mut AdminState,
|
||||
router: &mut Router,
|
||||
buffer_state: &BufferState,
|
||||
theme: &Theme,
|
||||
is_event_handler_edit_mode: bool,
|
||||
@@ -78,56 +73,52 @@ pub fn render_ui(
|
||||
let main_content_area = root_chunks[chunk_idx];
|
||||
chunk_idx += 1;
|
||||
|
||||
if app_state.ui.show_intro {
|
||||
render_intro(f, intro_state, main_content_area, theme);
|
||||
} else if app_state.ui.show_register {
|
||||
render_register(
|
||||
// ✅ Instead of checking app_state.ui.show_*, just match router.current
|
||||
match &mut router.current {
|
||||
Page::Intro(state) => render_intro(f, state, main_content_area, theme),
|
||||
Page::Login(state) => render_login(
|
||||
f,
|
||||
main_content_area,
|
||||
theme,
|
||||
register_state,
|
||||
state,
|
||||
app_state,
|
||||
register_state.current_field() < 4, // Now using CanvasState trait method
|
||||
);
|
||||
} else if app_state.ui.show_add_table {
|
||||
render_add_table(
|
||||
state.current_field() < 2,
|
||||
),
|
||||
Page::Register(state) => render_register(
|
||||
f,
|
||||
main_content_area,
|
||||
theme,
|
||||
state,
|
||||
app_state,
|
||||
&mut admin_state.add_table_state,
|
||||
is_event_handler_edit_mode,
|
||||
);
|
||||
} else if app_state.ui.show_add_logic {
|
||||
render_add_logic(
|
||||
f,
|
||||
main_content_area,
|
||||
theme,
|
||||
app_state,
|
||||
&mut admin_state.add_logic_state,
|
||||
is_event_handler_edit_mode,
|
||||
);
|
||||
} else if app_state.ui.show_login {
|
||||
render_login(
|
||||
f,
|
||||
main_content_area,
|
||||
theme,
|
||||
login_state,
|
||||
app_state,
|
||||
login_state.current_field() < 2, // Now using CanvasState trait method
|
||||
);
|
||||
} else if app_state.ui.show_admin {
|
||||
crate::components::admin::admin_panel::render_admin_panel(
|
||||
state.current_field() < 4,
|
||||
),
|
||||
Page::Admin(state) => crate::components::admin::admin_panel::render_admin_panel(
|
||||
f,
|
||||
app_state,
|
||||
auth_state,
|
||||
admin_state,
|
||||
&mut AuthState::default(), // TODO: later move AuthState into Router
|
||||
state,
|
||||
main_content_area,
|
||||
theme,
|
||||
&app_state.profile_tree,
|
||||
&app_state.selected_profile,
|
||||
);
|
||||
} else if app_state.ui.show_form {
|
||||
),
|
||||
Page::AddLogic(state) => render_add_logic(
|
||||
f,
|
||||
main_content_area,
|
||||
theme,
|
||||
app_state,
|
||||
state,
|
||||
is_event_handler_edit_mode,
|
||||
),
|
||||
Page::AddTable(state) => render_add_table(
|
||||
f,
|
||||
main_content_area,
|
||||
theme,
|
||||
app_state,
|
||||
state,
|
||||
is_event_handler_edit_mode,
|
||||
),
|
||||
Page::Form(state) => {
|
||||
let (sidebar_area, form_actual_area) =
|
||||
calculate_sidebar_layout(app_state.ui.show_sidebar, main_content_area);
|
||||
if let Some(sidebar_rect) = sidebar_area {
|
||||
@@ -160,19 +151,19 @@ pub fn render_ui(
|
||||
f,
|
||||
form_render_area,
|
||||
app_state,
|
||||
form_state,
|
||||
state,
|
||||
app_state.current_view_table_name.as_deref().unwrap_or(""),
|
||||
theme,
|
||||
form_state.total_count,
|
||||
form_state.current_position,
|
||||
state.total_count,
|
||||
state.current_position,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(area) = buffer_list_area {
|
||||
render_buffer_list(f, area, theme, buffer_state, app_state);
|
||||
}
|
||||
|
||||
// This block now correctly handles drawing popups over any view.
|
||||
if app_state.ui.show_search_palette {
|
||||
if let Some(search_state) = &app_state.search_state {
|
||||
render_search_palette(f, f.area(), theme, search_state);
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::state::pages::auth::RegisterState;
|
||||
use crate::state::pages::admin::AdminState;
|
||||
use crate::state::pages::admin::AdminFocus;
|
||||
use crate::state::pages::intro::IntroState;
|
||||
use crate::pages::routing::{Router, Page};
|
||||
use crate::buffer::state::BufferState;
|
||||
use crate::buffer::state::AppView;
|
||||
use crate::state::app::state::AppState;
|
||||
@@ -68,6 +69,7 @@ pub async fn run_ui() -> Result<()> {
|
||||
let mut register_state = RegisterState::default();
|
||||
let mut intro_state = IntroState::default();
|
||||
let mut admin_state = AdminState::default();
|
||||
let mut router = Router::new();
|
||||
let mut buffer_state = BufferState::default();
|
||||
let mut app_state = AppState::new().context("Failed to create initial app state")?;
|
||||
|
||||
@@ -341,17 +343,10 @@ pub async fn run_ui() -> Result<()> {
|
||||
}
|
||||
|
||||
if let Some(active_view) = buffer_state.get_active_view() {
|
||||
app_state.ui.show_intro = false;
|
||||
app_state.ui.show_login = false;
|
||||
app_state.ui.show_register = false;
|
||||
app_state.ui.show_admin = false;
|
||||
app_state.ui.show_add_table = false;
|
||||
app_state.ui.show_add_logic = false;
|
||||
app_state.ui.show_form = false;
|
||||
match active_view {
|
||||
AppView::Intro => app_state.ui.show_intro = true,
|
||||
AppView::Login => app_state.ui.show_login = true,
|
||||
AppView::Register => app_state.ui.show_register = true,
|
||||
AppView::Intro => router.navigate(Page::Intro(intro_state.clone())),
|
||||
AppView::Login => router.navigate(Page::Login(login_state.clone())),
|
||||
AppView::Register => router.navigate(Page::Register(register_state.clone())),
|
||||
AppView::Admin => {
|
||||
info!("Active view is Admin, refreshing profile tree...");
|
||||
match grpc_client.get_profile_tree().await {
|
||||
@@ -360,36 +355,42 @@ pub async fn run_ui() -> Result<()> {
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to refresh profile tree for Admin panel: {}", e);
|
||||
event_handler.command_message = format!("Error refreshing admin data: {}", e);
|
||||
event_handler.command_message =
|
||||
format!("Error refreshing admin data: {}", e);
|
||||
}
|
||||
}
|
||||
app_state.ui.show_admin = true;
|
||||
let profile_names = app_state.profile_tree.profiles.iter()
|
||||
.map(|p| p.name.clone())
|
||||
.collect();
|
||||
admin_state.set_profiles(profile_names);
|
||||
|
||||
if admin_state.current_focus == AdminFocus::default() ||
|
||||
!matches!(admin_state.current_focus,
|
||||
if admin_state.current_focus == AdminFocus::default()
|
||||
|| !matches!(admin_state.current_focus,
|
||||
AdminFocus::InsideProfilesList |
|
||||
AdminFocus::Tables | AdminFocus::InsideTablesList |
|
||||
AdminFocus::Button1 | AdminFocus::Button2 | AdminFocus::Button3) {
|
||||
AdminFocus::Button1 | AdminFocus::Button2 | AdminFocus::Button3)
|
||||
{
|
||||
admin_state.current_focus = AdminFocus::ProfilesPane;
|
||||
}
|
||||
if admin_state.profile_list_state.selected().is_none() && !app_state.profile_tree.profiles.is_empty() {
|
||||
if admin_state.profile_list_state.selected().is_none()
|
||||
&& !app_state.profile_tree.profiles.is_empty()
|
||||
{
|
||||
admin_state.profile_list_state.select(Some(0));
|
||||
}
|
||||
|
||||
router.navigate(Page::Admin(admin_state.clone()));
|
||||
}
|
||||
AppView::AddTable => router.navigate(Page::AddTable(admin_state.add_table_state.clone())),
|
||||
AppView::AddLogic => router.navigate(Page::AddLogic(admin_state.add_logic_state.clone())),
|
||||
AppView::Form => {
|
||||
if let Some(form_state) = app_state.form_state().cloned() {
|
||||
router.navigate(Page::Form(form_state));
|
||||
}
|
||||
}
|
||||
AppView::AddTable => app_state.ui.show_add_table = true,
|
||||
AppView::AddLogic => app_state.ui.show_add_logic = true,
|
||||
AppView::Form => app_state.ui.show_form = true,
|
||||
AppView::Scratch => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Continue with the rest of the function...
|
||||
// (The rest remains the same, but now CanvasState trait methods are available)
|
||||
|
||||
if app_state.ui.show_form {
|
||||
let current_view_profile = app_state.current_view_profile_name.clone();
|
||||
let current_view_table = app_state.current_view_table_name.clone();
|
||||
@@ -654,12 +655,7 @@ pub async fn run_ui() -> Result<()> {
|
||||
let mut temp_form_state = form_state_clone.clone();
|
||||
render_ui(
|
||||
f,
|
||||
&mut temp_form_state,
|
||||
&mut auth_state,
|
||||
&login_state,
|
||||
®ister_state,
|
||||
&intro_state,
|
||||
&mut admin_state,
|
||||
&mut router,
|
||||
&buffer_state,
|
||||
&theme,
|
||||
event_handler.is_edit_mode,
|
||||
|
||||
Reference in New Issue
Block a user