router2, needs bug fixes

This commit is contained in:
Priec
2025-08-22 22:52:20 +02:00
parent 957f5bf9f0
commit 5d97e63f93
7 changed files with 337 additions and 335 deletions

View File

@@ -11,7 +11,8 @@ use tokio::sync::mpsc;
use anyhow::Result;
use crate::components::common::text_editor::TextEditor;
use crate::services::ui_service::UiService;
use tui_textarea::CursorMove; // Ensure this import is present
use tui_textarea::CursorMove;
use crate::pages::routing::{Router, Page};
pub type SaveLogicResultSender = mpsc::Sender<Result<String>>;
@@ -25,6 +26,7 @@ pub fn handle_add_logic_navigation(
grpc_client: GrpcClient,
_save_logic_sender: SaveLogicResultSender, // Marked as unused
command_message: &mut String,
router: &mut Router,
) -> bool {
// === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION ===
if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent {
@@ -380,7 +382,7 @@ pub fn handle_add_logic_navigation(
}
AddLogicFocus::CancelButton => {
buffer_state.update_history(AppView::Admin);
app_state.ui.show_add_logic = false;
router.navigate(Page::Admin(app_state.admin_state().clone()));
*command_message = "Cancelled Add Logic".to_string();
*is_edit_mode = false;
}

View File

@@ -1,7 +1,7 @@
// src/modes/canvas/common_mode.rs
use crate::tui::terminal::core::TerminalCore;
use crate::state::pages::{form::FormState, auth::LoginState, auth::RegisterState, auth::AuthState};
use crate::state::pages::auth::AuthState;
use crate::state::app::state::AppState;
use crate::services::grpc_client::GrpcClient;
use crate::services::auth::AuthClient;
@@ -13,74 +13,83 @@ use crate::tui::functions::common::{
login::{save as login_save, revert as login_revert},
register::{revert as register_revert},
};
use crate::pages::routing::{Router, Page};
pub async fn handle_core_action(
action: &str,
form_state: &mut FormState,
auth_state: &mut AuthState,
login_state: &mut LoginState,
register_state: &mut RegisterState,
grpc_client: &mut GrpcClient,
auth_client: &mut AuthClient,
terminal: &mut TerminalCore,
app_state: &mut AppState,
router: &mut Router,
) -> Result<EventOutcome> {
match action {
"save" => {
if app_state.ui.show_login {
let message = login_save(auth_state, login_state, auth_client, app_state).await.context("Login save action failed")?;
Ok(EventOutcome::Ok(message))
} else {
let save_outcome = form_save(
app_state,
form_state,
grpc_client,
).await.context("Register save action failed")?;
let message = match save_outcome {
SaveOutcome::NoChange => "No changes to save.".to_string(),
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
};
Ok(EventOutcome::DataSaved(save_outcome, message))
match &mut router.current {
Page::Login(state) => {
let message = login_save(auth_state, state, auth_client, app_state)
.await
.context("Login save action failed")?;
Ok(EventOutcome::Ok(message))
}
Page::Form(form_state) => {
let save_outcome = form_save(app_state, form_state, grpc_client)
.await
.context("Form save action failed")?;
let message = match save_outcome {
SaveOutcome::NoChange => "No changes to save.".to_string(),
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
};
Ok(EventOutcome::DataSaved(save_outcome, message))
}
_ => Ok(EventOutcome::Ok("Save not applicable".into())),
}
},
}
"force_quit" => {
terminal.cleanup()?;
Ok(EventOutcome::Exit("Force exiting without saving.".to_string()))
},
}
"save_and_quit" => {
let message = if app_state.ui.show_login {
login_save(auth_state, login_state, auth_client, app_state).await.context("Login save n quit action failed")?
} else {
let save_outcome = form_save(
app_state,
form_state,
grpc_client,
).await?;
match save_outcome {
SaveOutcome::NoChange => "No changes to save.".to_string(),
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
let message = match &mut router.current {
Page::Login(state) => {
login_save(auth_state, state, auth_client, app_state)
.await
.context("Login save and quit action failed")?
}
Page::Form(form_state) => {
let save_outcome = form_save(app_state, form_state, grpc_client).await?;
match save_outcome {
SaveOutcome::NoChange => "No changes to save.".to_string(),
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
}
}
_ => "Save not applicable".to_string(),
};
terminal.cleanup()?;
Ok(EventOutcome::Exit(format!("{}. Exiting application.", message)))
},
}
"revert" => {
if app_state.ui.show_login {
let message = login_revert(login_state, app_state).await;
Ok(EventOutcome::Ok(message))
} else if app_state.ui.show_register {
let message = register_revert(register_state, app_state).await;
Ok(EventOutcome::Ok(message))
} else {
let message = form_revert(
form_state,
grpc_client,
).await.context("Form revert x action failed")?;
Ok(EventOutcome::Ok(message))
match &mut router.current {
Page::Login(state) => {
let message = login_revert(state, app_state).await;
Ok(EventOutcome::Ok(message))
}
Page::Register(state) => {
let message = register_revert(state, app_state).await;
Ok(EventOutcome::Ok(message))
}
Page::Form(form_state) => {
let message = form_revert(form_state, grpc_client)
.await
.context("Form revert action failed")?;
Ok(EventOutcome::Ok(message))
}
_ => Ok(EventOutcome::Ok("Revert not applicable".into())),
}
},
}
_ => Ok(EventOutcome::Ok(format!("Core action not handled: {}", action))),
}
}

View File

@@ -1,7 +1,7 @@
// src/modes/common/commands.rs
use crate::tui::terminal::core::TerminalCore;
use crate::state::app::state::AppState;
use crate::state::pages::{auth::LoginState, auth::RegisterState};
use crate::pages::routing::{Router, Page};
use anyhow::Result;
pub struct CommandHandler;
@@ -16,11 +16,10 @@ impl CommandHandler {
action: &str,
terminal: &mut TerminalCore,
app_state: &mut AppState,
login_state: &LoginState,
register_state: &RegisterState,
router: &Router,
) -> Result<(bool, String)> {
match action {
"quit" => self.handle_quit(terminal, app_state, login_state, register_state).await,
"quit" => self.handle_quit(terminal, app_state, router).await,
"force_quit" => self.handle_force_quit(terminal).await,
"save_and_quit" => self.handle_save_quit(terminal).await,
_ => Ok((false, format!("Unknown command: {}", action))),
@@ -31,18 +30,14 @@ impl CommandHandler {
&self,
terminal: &mut TerminalCore,
app_state: &mut AppState,
login_state: &LoginState,
register_state: &RegisterState,
router: &Router,
) -> Result<(bool, String)> {
// Use actual unsaved changes state instead of is_saved flag
let has_unsaved = if app_state.ui.show_login {
login_state.has_unsaved_changes()
} else if app_state.ui.show_register {
register_state.has_unsaved_changes()
} else if let Some(fs) = app_state.form_state_mut() {
fs.has_unsaved_changes
} else {
false
// Use router to check unsaved changes
let has_unsaved = match &router.current {
Page::Login(state) => state.has_unsaved_changes(),
Page::Register(state) => state.has_unsaved_changes(),
Page::Form(fs) => fs.has_unsaved_changes,
_ => false,
};
if !has_unsaved {

View File

@@ -30,6 +30,7 @@ use crate::state::{
intro::IntroState,
},
};
use crate::pages::routing::{Router, Page};
use crate::search::state::SearchState;
use crate::tui::functions::common::login::LoginResult;
use crate::tui::functions::common::register::RegisterResult;
@@ -127,90 +128,69 @@ impl EventHandler {
// Helper functions - replace the removed event_helper functions
fn get_current_field_for_state(
app_state: &AppState,
login_state: &LoginState,
register_state: &RegisterState,
form_state: &FormState,
router: &Router,
) -> usize {
if app_state.ui.show_login {
login_state.current_field()
} else if app_state.ui.show_register {
register_state.current_field()
} else {
form_state.current_field()
match &router.current {
Page::Login(state) => state.current_field(),
Page::Register(state) => state.current_field(),
Page::Form(state) => state.current_field(),
_ => 0,
}
}
fn get_current_cursor_pos_for_state(
app_state: &AppState,
login_state: &LoginState,
register_state: &RegisterState,
form_state: &FormState,
router: &Router,
) -> usize {
if app_state.ui.show_login {
login_state.current_cursor_pos()
} else if app_state.ui.show_register {
register_state.current_cursor_pos()
} else {
form_state.current_cursor_pos()
match &router.current {
Page::Login(state) => state.current_cursor_pos(),
Page::Register(state) => state.current_cursor_pos(),
Page::Form(state) => state.current_cursor_pos(),
_ => 0,
}
}
fn get_has_unsaved_changes_for_state(
app_state: &AppState,
login_state: &LoginState,
register_state: &RegisterState,
form_state: &FormState,
router: &Router,
) -> bool {
if app_state.ui.show_login {
login_state.has_unsaved_changes()
} else if app_state.ui.show_register {
register_state.has_unsaved_changes()
} else {
form_state.has_unsaved_changes()
match &router.current {
Page::Login(state) => state.has_unsaved_changes(),
Page::Register(state) => state.has_unsaved_changes(),
Page::Form(state) => state.has_unsaved_changes(),
_ => false,
}
}
fn get_current_input_for_state<'a>(
app_state: &AppState,
login_state: &'a LoginState,
register_state: &'a RegisterState,
form_state: &'a FormState,
router: &'a Router,
) -> &'a str {
if app_state.ui.show_login {
login_state.get_current_input()
} else if app_state.ui.show_register {
register_state.get_current_input()
} else {
form_state.get_current_input()
match &router.current {
Page::Login(state) => state.get_current_input(),
Page::Register(state) => state.get_current_input(),
Page::Form(state) => state.get_current_input(),
_ => "",
}
}
fn set_current_cursor_pos_for_state(
app_state: &AppState,
login_state: &mut LoginState,
register_state: &mut RegisterState,
form_state: &mut FormState,
router: &mut Router,
pos: usize,
) {
if app_state.ui.show_login {
login_state.set_current_cursor_pos(pos);
} else if app_state.ui.show_register {
register_state.set_current_cursor_pos(pos);
} else {
form_state.set_current_cursor_pos(pos);
match &mut router.current {
Page::Login(state) => state.set_current_cursor_pos(pos),
Page::Register(state) => state.set_current_cursor_pos(pos),
Page::Form(state) => state.set_current_cursor_pos(pos),
_ => {},
}
}
fn get_cursor_pos_for_mixed_state(
app_state: &AppState,
login_state: &LoginState,
form_state: &FormState,
router: &Router,
) -> usize {
if app_state.ui.show_login || app_state.ui.show_register {
login_state.current_cursor_pos()
} else {
form_state.current_cursor_pos()
match &router.current {
Page::Login(state) => state.current_cursor_pos(),
Page::Register(state) => state.current_cursor_pos(),
Page::Form(state) => state.current_cursor_pos(),
_ => 0,
}
}
@@ -222,12 +202,9 @@ impl EventHandler {
terminal: &mut TerminalCore,
command_handler: &mut CommandHandler,
auth_state: &mut AuthState,
login_state: &mut LoginState,
register_state: &mut RegisterState,
intro_state: &mut IntroState,
admin_state: &mut AdminState,
buffer_state: &mut BufferState,
app_state: &mut AppState,
router: &mut Router,
) -> Result<EventOutcome> {
if app_state.ui.show_search_palette {
if let Event::Key(key_event) = event {
@@ -244,7 +221,7 @@ impl EventHandler {
}
let mut current_mode =
ModeManager::derive_mode(app_state, self, admin_state);
ModeManager::derive_mode(app_state, self, router);
if current_mode == AppMode::General && self.navigation_state.active {
if let Event::Key(key_event) = event {
@@ -258,7 +235,7 @@ impl EventHandler {
if !self.navigation_state.active {
self.command_message = outcome.get_message_if_ok();
current_mode =
ModeManager::derive_mode(app_state, self, admin_state);
ModeManager::derive_mode(app_state, self, router);
}
app_state.update_mode(current_mode);
return Ok(outcome);
@@ -269,25 +246,14 @@ impl EventHandler {
app_state.update_mode(current_mode);
let current_view = {
let ui = &app_state.ui;
if ui.show_intro {
AppView::Intro
} else if ui.show_login {
AppView::Login
} else if ui.show_register {
AppView::Register
} else if ui.show_admin {
AppView::Admin
} else if ui.show_add_logic {
AppView::AddLogic
} else if ui.show_add_table {
AppView::AddTable
} else if ui.show_form {
AppView::Form
} else {
AppView::Scratch
}
let current_view = match &router.current {
Page::Intro(_) => AppView::Intro,
Page::Login(_) => AppView::Login,
Page::Register(_) => AppView::Register,
Page::Admin(_) => AppView::Admin,
Page::AddLogic(_) => AppView::AddLogic,
Page::AddTable(_) => AppView::AddTable,
Page::Form(_) => AppView::Form,
};
buffer_state.update_history(current_view);
@@ -297,10 +263,8 @@ impl EventHandler {
&Event::Key(key_event),
config,
app_state,
login_state,
register_state,
buffer_state,
admin_state,
router,
)
.await
{
@@ -386,7 +350,7 @@ impl EventHandler {
config.get_general_action(key_code, modifiers)
{
if action == "open_search" {
if app_state.ui.show_form {
if let Page::Form(_) = &router.current {
if let Some(table_name) =
app_state.current_view_table_name.clone()
{
@@ -405,31 +369,31 @@ impl EventHandler {
match current_mode {
AppMode::General => {
if app_state.ui.show_admin
&& auth_state.role.as_deref() == Some("admin")
{
if admin_nav::handle_admin_navigation(
key_event,
config,
app_state,
admin_state,
buffer_state,
&mut self.command_message,
) {
return Ok(EventOutcome::Ok(
self.command_message.clone(),
));
if let Page::Admin(admin_state) = &router.current {
if auth_state.role.as_deref() == Some("admin") {
if admin_nav::handle_admin_navigation(
key_event,
config,
app_state,
admin_state,
buffer_state,
&mut self.command_message,
) {
return Ok(EventOutcome::Ok(
self.command_message.clone(),
));
}
}
}
if app_state.ui.show_add_logic {
if let Page::AddLogic(add_logic_state) = &mut router.current {
let client_clone = self.grpc_client.clone();
let sender_clone = self.save_logic_result_sender.clone();
if add_logic_nav::handle_add_logic_navigation(
key_event,
config,
app_state,
&mut admin_state.add_logic_state,
add_logic_state,
&mut self.is_edit_mode,
buffer_state,
client_clone,
@@ -442,14 +406,14 @@ impl EventHandler {
}
}
if app_state.ui.show_add_table {
if let Page::AddTable(add_table_state) = &mut router.current {
let client_clone = self.grpc_client.clone();
let sender_clone = self.save_table_result_sender.clone();
if add_table_nav::handle_add_table_navigation(
key_event,
config,
app_state,
&mut admin_state.add_table_state,
add_table_state,
client_clone,
sender_clone,
&mut self.command_message,
@@ -464,10 +428,7 @@ impl EventHandler {
key_event,
config,
app_state,
login_state,
register_state,
intro_state,
admin_state,
router,
&mut self.command_mode,
&mut self.command_input,
&mut self.command_message,
@@ -482,53 +443,68 @@ impl EventHandler {
buffer_state,
index,
);
if app_state.ui.show_admin
&& !app_state
if let Page::Admin(admin_state) = &router.current {
if !app_state
.profile_tree
.profiles
.is_empty()
{
admin_state
.profile_list_state
.select(Some(0));
{
admin_state
.profile_list_state
.select(Some(0));
}
}
format!("Intro Option {} selected", index)
}
UiContext::Login => match index {
0 => login::initiate_login(
login_state,
app_state,
self.auth_client.clone(),
self.login_result_sender.clone(),
),
1 => login::back_to_main(
login_state,
app_state,
buffer_state,
)
.await,
_ => "Invalid Login Option".to_string(),
},
UiContext::Register => match index {
0 => register::initiate_registration(
register_state,
app_state,
self.auth_client.clone(),
self.register_result_sender.clone(),
),
1 => register::back_to_login(
register_state,
app_state,
buffer_state,
)
.await,
_ => "Invalid Login Option".to_string(),
},
UiContext::Login => {
if let Page::Login(login_state) = &router.current {
match index {
0 => login::initiate_login(
login_state,
app_state,
self.auth_client.clone(),
self.login_result_sender.clone(),
),
1 => login::back_to_main(
login_state,
app_state,
buffer_state,
)
.await,
_ => "Invalid Login Option".to_string(),
}
} else {
"Invalid state".to_string()
}
}
UiContext::Register => {
if let Page::Register(register_state) = &router.current {
match index {
0 => register::initiate_registration(
register_state,
app_state,
self.auth_client.clone(),
self.register_result_sender.clone(),
),
1 => register::back_to_login(
register_state,
app_state,
buffer_state,
)
.await,
_ => "Invalid Login Option".to_string(),
}
} else {
"Invalid state".to_string()
}
}
UiContext::Admin => {
admin::handle_admin_selection(
app_state,
admin_state,
);
if let Page::Admin(admin_state) = &router.current {
admin::handle_admin_selection(
app_state,
admin_state,
);
}
format!("Admin Option {} selected", index)
}
UiContext::Dialog => "Internal error: Unexpected dialog state"
@@ -542,7 +518,7 @@ impl EventHandler {
AppMode::ReadOnly => {
// First let the canvas editor try to handle the key
if app_state.ui.show_form {
if let Page::Form(_) = &router.current {
if let Some(editor) = &mut app_state.form_editor {
let outcome = editor.handle_key_event(key_event);
let new_mode = AppMode::from(editor.mode());
@@ -588,10 +564,9 @@ impl EventHandler {
.handle_core_action(
action,
auth_state,
login_state,
register_state,
terminal,
app_state,
router,
)
.await;
}
@@ -603,7 +578,7 @@ impl EventHandler {
}
AppMode::Highlight => {
if app_state.ui.show_form {
if let Page::Form(_) = &router.current {
if let Some(editor) = &mut app_state.form_editor {
let outcome = editor.handle_key_event(key_event);
let new_mode = AppMode::from(editor.mode());
@@ -640,10 +615,9 @@ impl EventHandler {
.handle_core_action(
action,
auth_state,
login_state,
register_state,
terminal,
app_state,
router,
)
.await;
}
@@ -652,7 +626,7 @@ impl EventHandler {
}
// Let the canvas editor handle edit-mode keys
if app_state.ui.show_form {
if let Page::Form(_) = &router.current {
if let Some(editor) = &mut app_state.form_editor {
let outcome = editor.handle_key_event(key_event);
let new_mode = AppMode::from(editor.mode());
@@ -696,7 +670,7 @@ impl EventHandler {
}
if config.is_command_execute(key_code, modifiers) {
let (mut current_position, total_count) = if let Some(fs) = app_state.form_state() {
let (mut current_position, total_count) = if let Page::Form(fs) = &router.current {
(fs.current_position, fs.total_count)
} else {
(1, 0)
@@ -706,8 +680,7 @@ impl EventHandler {
key_event,
config,
app_state,
login_state,
register_state,
router,
&mut self.command_input,
&mut self.command_message,
&mut self.grpc_client,
@@ -716,7 +689,7 @@ impl EventHandler {
&mut current_position,
total_count,
).await?;
if let Some(fs) = app_state.form_state_mut() {
if let Page::Form(fs) = &mut router.current {
fs.current_position = current_position;
}
self.command_mode = false;
@@ -724,7 +697,7 @@ impl EventHandler {
let new_mode = ModeManager::derive_mode(
app_state,
self,
admin_state,
router,
);
app_state.update_mode(new_mode);
return Ok(outcome);
@@ -746,9 +719,7 @@ impl EventHandler {
&sequence,
) == Some("find_file_palette_toggle")
{
if app_state.ui.show_form
|| app_state.ui.show_intro
{
if matches!(&router.current, Page::Form(_) | Page::Intro(_)) {
let mut all_table_paths: Vec<String> =
app_state
.profile_tree
@@ -830,14 +801,13 @@ impl EventHandler {
&mut self,
action: &str,
auth_state: &mut AuthState,
login_state: &mut LoginState,
register_state: &mut RegisterState,
terminal: &mut TerminalCore,
app_state: &mut AppState,
router: &mut Router,
) -> Result<EventOutcome> {
match action {
"save" => {
if app_state.ui.show_login {
if let Page::Login(login_state) = &router.current {
let message = crate::tui::functions::common::login::save(
auth_state,
login_state,
@@ -847,7 +817,7 @@ impl EventHandler {
.await?;
Ok(EventOutcome::Ok(message))
} else {
let save_outcome = if let Some(fs) = app_state.form_state_mut() {
let save_outcome = if let Page::Form(_) = &router.current {
crate::tui::functions::common::form::save(
app_state,
&mut self.grpc_client,
@@ -874,7 +844,7 @@ impl EventHandler {
))
}
"save_and_quit" => {
let message = if app_state.ui.show_login {
let message = if let Page::Login(login_state) = &router.current {
crate::tui::functions::common::login::save(
auth_state,
login_state,
@@ -903,17 +873,17 @@ impl EventHandler {
)))
}
"revert" => {
let message = if app_state.ui.show_login {
let message = if let Page::Login(login_state) = &router.current {
crate::tui::functions::common::login::revert(login_state, app_state)
.await
} else if app_state.ui.show_register {
} else if let Page::Register(register_state) = &router.current {
crate::tui::functions::common::register::revert(
register_state,
app_state,
)
.await
} else {
if let Some(fs) = app_state.form_state_mut() {
if let Page::Form(_) = &router.current {
crate::tui::functions::common::form::revert(
app_state,
&mut self.grpc_client,

View File

@@ -2,7 +2,7 @@
use crate::state::app::state::AppState;
use crate::modes::handlers::event::EventHandler;
use crate::state::pages::add_logic::AddLogicFocus;
use crate::state::pages::admin::AdminState;
use crate::pages::routing::{Router, Page};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AppMode {
@@ -28,11 +28,11 @@ impl From<canvas::AppMode> for AppMode {
pub struct ModeManager;
impl ModeManager {
/// Determine current mode based on app state
/// Determine current mode based on app state + router
pub fn derive_mode(
app_state: &AppState,
event_handler: &EventHandler,
admin_state: &AdminState,
router: &Router,
) -> AppMode {
// Navigation palette always forces General
if event_handler.navigation_state.active {
@@ -44,16 +44,17 @@ impl ModeManager {
return AppMode::Command;
}
// Always trust the FormEditor when a form is active
if app_state.ui.show_form && !app_state.ui.focus_outside_canvas {
if let Some(editor) = &app_state.form_editor {
return AppMode::from(editor.mode());
match &router.current {
// --- Form view ---
Page::Form(_) if !app_state.ui.focus_outside_canvas => {
if let Some(editor) = &app_state.form_editor {
return AppMode::from(editor.mode());
}
AppMode::General
}
}
// --- Non-form views (add_logic, add_table, etc.) ---
if app_state.ui.show_add_logic {
match admin_state.add_logic_state.current_focus {
// --- AddLogic view ---
Page::AddLogic(state) => match state.current_focus {
AddLogicFocus::InputLogicName
| AddLogicFocus::InputTargetColumn
| AddLogicFocus::InputDescription => {
@@ -64,26 +65,30 @@ impl ModeManager {
}
}
_ => AppMode::General,
},
// --- AddTable view ---
Page::AddTable(_) => {
if app_state.ui.focus_outside_canvas {
AppMode::General
} else if event_handler.is_edit_mode {
AppMode::Edit
} else {
AppMode::ReadOnly
}
}
} else if app_state.ui.show_add_table {
if app_state.ui.focus_outside_canvas {
AppMode::General
} else if event_handler.is_edit_mode {
AppMode::Edit
} else {
AppMode::ReadOnly
// --- Login/Register views ---
Page::Login(_) | Page::Register(_) => {
if event_handler.is_edit_mode {
AppMode::Edit
} else {
AppMode::ReadOnly
}
}
} else if app_state.ui.show_login
|| app_state.ui.show_register
{
// login/register still use the old flag
if event_handler.is_edit_mode {
AppMode::Edit
} else {
AppMode::ReadOnly
}
} else {
AppMode::General
// --- Everything else (Intro, Admin, etc.) ---
_ => AppMode::General,
}
}

View File

@@ -8,7 +8,11 @@ use crate::components::{
intro::intro::render_intro,
render_background,
};
use crate::bottom_panel::{command_line::render_command_line, status_line::render_status_line, find_file_palette};
use crate::bottom_panel::{
command_line::render_command_line,
status_line::render_status_line,
find_file_palette,
};
use crate::sidebar::{calculate_sidebar_layout, render_sidebar};
use crate::buffer::render_buffer_list;
use crate::search::render_search_palette;
@@ -16,12 +20,7 @@ use crate::config::colors::themes::Theme;
use crate::modes::general::command_navigation::NavigationState;
use crate::buffer::state::BufferState;
use crate::state::app::state::AppState;
use crate::state::pages::admin::AdminState;
use crate::state::pages::auth::AuthState;
use crate::state::pages::auth::LoginState;
use crate::state::pages::auth::RegisterState;
use crate::state::pages::form::FormState;
use crate::state::pages::intro::IntroState;
use crate::bottom_panel::layout::{bottom_panel_constraints, render_bottom_panel};
use crate::components::render_form;
use ratatui::{
@@ -46,6 +45,7 @@ pub fn render_ui(
) {
render_background(f, f.area(), theme);
// Layout: optional buffer list + main content + bottom panel
let mut main_layout_constraints = vec![Constraint::Min(1)];
if app_state.ui.show_buffer_list {
main_layout_constraints.insert(0, Constraint::Length(1));
@@ -73,7 +73,7 @@ pub fn render_ui(
let main_content_area = root_chunks[chunk_idx];
chunk_idx += 1;
// ✅ Instead of checking app_state.ui.show_*, just match router.current
// Page rendering is now fully router-driven
match &mut router.current {
Page::Intro(state) => render_intro(f, state, main_content_area, theme),
Page::Login(state) => render_login(
@@ -160,6 +160,7 @@ pub fn render_ui(
}
}
// Global overlays (not tied to a page)
if let Some(area) = buffer_list_area {
render_buffer_list(f, area, theme, buffer_state, app_state);
}

View File

@@ -199,7 +199,7 @@ pub async fn run_ui() -> Result<()> {
event_processed = true;
if let crossterm_event::Event::Key(key_event) = &event {
if app_state.ui.show_form {
if let Page::Form(_) = &router.current {
if let Some(editor) = app_state.form_editor.as_mut() {
match editor.handle_key_event(*key_event) {
KeyEventOutcome::Consumed(Some(msg)) => {
@@ -391,7 +391,7 @@ pub async fn run_ui() -> Result<()> {
}
}
if app_state.ui.show_form {
if let Page::Form(_) = &router.current {
let current_view_profile = app_state.current_view_profile_name.clone();
let current_view_table = app_state.current_view_table_name.clone();
@@ -477,18 +477,21 @@ pub async fn run_ui() -> Result<()> {
// Now we can use CanvasState methods like get_current_input(), current_field(), etc.
if let Some((profile_name, table_name)) = app_state.pending_table_structure_fetch.take() {
if app_state.ui.show_add_logic {
if admin_state.add_logic_state.profile_name == profile_name &&
admin_state.add_logic_state.selected_table_name.as_deref() == Some(table_name.as_str()) {
if let Page::AddLogic(state) = &mut router.current {
if state.profile_name == profile_name
&& state.selected_table_name.as_deref() == Some(table_name.as_str())
{
info!("Fetching table structure for {}.{}", profile_name, table_name);
let fetch_message = UiService::initialize_add_logic_table_data(
&mut grpc_client,
&mut admin_state.add_logic_state,
state,
&app_state.profile_tree,
).await.unwrap_or_else(|e| {
error!("Error initializing add_logic_table_data: {}", e);
format!("Error fetching table structure: {}", e)
});
)
.await
.unwrap_or_else(|e| {
error!("Error initializing add_logic_table_data: {}", e);
format!("Error fetching table structure: {}", e)
});
if !fetch_message.contains("Error") && !fetch_message.contains("Warning") {
info!("{}", fetch_message);
@@ -498,10 +501,11 @@ pub async fn run_ui() -> Result<()> {
needs_redraw = true;
} else {
error!(
"Mismatch in pending_table_structure_fetch: app_state wants {}.{}, but add_logic_state is for {}.{:?}",
profile_name, table_name,
admin_state.add_logic_state.profile_name,
admin_state.add_logic_state.selected_table_name
"Mismatch in pending_table_structure_fetch: app_state wants {}.{}, but AddLogic state is for {}.{:?}",
profile_name,
table_name,
state.profile_name,
state.selected_table_name
);
}
} else {
@@ -512,21 +516,21 @@ pub async fn run_ui() -> Result<()> {
}
}
if let Some(table_name) = admin_state.add_logic_state.script_editor_awaiting_column_autocomplete.clone() {
if app_state.ui.show_add_logic {
let profile_name = admin_state.add_logic_state.profile_name.clone();
if let Page::AddLogic(state) = &mut router.current {
if let Some(table_name) = state.script_editor_awaiting_column_autocomplete.clone() {
let profile_name = state.profile_name.clone();
info!("Fetching columns for table selection: {}.{}", profile_name, table_name);
match UiService::fetch_columns_for_table(&mut grpc_client, &profile_name, &table_name).await {
Ok(columns) => {
admin_state.add_logic_state.set_columns_for_table_autocomplete(columns.clone());
state.set_columns_for_table_autocomplete(columns.clone());
info!("Loaded {} columns for table '{}'", columns.len(), table_name);
event_handler.command_message = format!("Columns for '{}' loaded. Select a column.", table_name);
}
Err(e) => {
error!("Failed to fetch columns for {}.{}: {}", profile_name, table_name, e);
admin_state.add_logic_state.script_editor_awaiting_column_autocomplete = None;
admin_state.add_logic_state.deactivate_script_editor_autocomplete();
state.script_editor_awaiting_column_autocomplete = None;
state.deactivate_script_editor_autocomplete();
event_handler.command_message = format!("Error loading columns for '{}': {}", table_name, e);
}
}
@@ -540,59 +544,75 @@ pub async fn run_ui() -> Result<()> {
let position_changed = current_position != position_before_event;
let mut position_logic_needs_redraw = false;
if app_state.ui.show_form && !table_just_switched {
if position_changed && !event_handler.is_edit_mode {
position_logic_needs_redraw = true;
if let Page::Form(form_state) = &mut router.current {
if !table_just_switched {
if position_changed && !event_handler.is_edit_mode {
position_logic_needs_redraw = true;
if let Some(form_state) = app_state.form_state_mut() {
if form_state.current_position > form_state.total_count {
form_state.reset_to_empty();
event_handler.command_message = format!("New entry for {}.{}", form_state.profile_name, form_state.table_name);
} else {
match UiService::load_table_data_by_position(&mut grpc_client, form_state).await {
Ok(load_message) => {
if event_handler.command_message.is_empty() || !load_message.starts_with("Error") {
event_handler.command_message = load_message;
if let Some(form_state) = app_state.form_state_mut() {
if form_state.current_position > form_state.total_count {
form_state.reset_to_empty();
event_handler.command_message = format!(
"New entry for {}.{}",
form_state.profile_name,
form_state.table_name
);
} else {
match UiService::load_table_data_by_position(&mut grpc_client, form_state).await {
Ok(load_message) => {
if event_handler.command_message.is_empty()
|| !load_message.starts_with("Error")
{
event_handler.command_message = load_message;
}
}
Err(e) => {
event_handler.command_message =
format!("Error loading data: {}", e);
}
}
Err(e) => {
event_handler.command_message = format!("Error loading data: {}", e);
}
}
}
let current_input_after_load_str = form_state.get_current_input();
let current_input_len_after_load = current_input_after_load_str.chars().count();
let max_cursor_pos = if current_input_len_after_load > 0 {
current_input_len_after_load.saturating_sub(1)
} else {
0
};
form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
}
} else if !position_changed && !event_handler.is_edit_mode {
if let Some(form_state) = app_state.form_state_mut() {
let current_input_str = form_state.get_current_input();
let current_input_len = current_input_str.chars().count();
let max_cursor_pos = if current_input_len > 0 {
current_input_len.saturating_sub(1)
} else {
0
};
form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
let current_input_after_load_str = form_state.get_current_input();
let current_input_len_after_load =
current_input_after_load_str.chars().count();
let max_cursor_pos = if current_input_len_after_load > 0 {
current_input_len_after_load.saturating_sub(1)
} else {
0
};
form_state.current_cursor_pos =
event_handler.ideal_cursor_column.min(max_cursor_pos);
}
} else if !position_changed && !event_handler.is_edit_mode {
if let Some(form_state) = app_state.form_state_mut() {
let current_input_str = form_state.get_current_input();
let current_input_len = current_input_str.chars().count();
let max_cursor_pos = if current_input_len > 0 {
current_input_len.saturating_sub(1)
} else {
0
};
form_state.current_cursor_pos =
event_handler.ideal_cursor_column.min(max_cursor_pos);
}
}
}
} else if app_state.ui.show_register {
} else if let Page::Register(state) = &mut router.current {
if !event_handler.is_edit_mode {
let current_input = register_state.get_current_input();
let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { 0 };
register_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
let current_input = state.get_current_input();
let max_cursor_pos =
if !current_input.is_empty() { current_input.len() - 1 } else { 0 };
state.current_cursor_pos =
event_handler.ideal_cursor_column.min(max_cursor_pos);
}
} else if app_state.ui.show_login {
} else if let Page::Login(state) = &mut router.current {
if !event_handler.is_edit_mode {
let current_input = login_state.get_current_input();
let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { 0 };
login_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
let current_input = state.get_current_input();
let max_cursor_pos =
if !current_input.is_empty() { current_input.len() - 1 } else { 0 };
state.current_cursor_pos =
event_handler.ideal_cursor_column.min(max_cursor_pos);
}
}
@@ -623,7 +643,7 @@ pub async fn run_ui() -> Result<()> {
}
if event_processed || needs_redraw || position_changed {
let current_mode = ModeManager::derive_mode(&app_state, &event_handler, &admin_state);
let current_mode = ModeManager::derive_mode(&app_state, &event_handler, &router);
match current_mode {
AppMode::Edit => { terminal.show_cursor()?; }
AppMode::Highlight => { terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?; terminal.show_cursor()?; }