auth not working with canvas crate yet

This commit is contained in:
Priec
2025-07-30 12:08:35 +02:00
parent d711f4c491
commit 0d291fcf57
5 changed files with 60 additions and 130 deletions

View File

@@ -5,7 +5,6 @@ use crate::{
state::pages::auth::LoginState,
components::common::dialog,
state::app::state::AppState,
components::handlers::canvas_bridge::render_canvas_form, // Use our bridge function
};
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect, Margin},
@@ -14,6 +13,16 @@ use ratatui::{
Frame,
};
use crate::state::app::highlight::HighlightState;
use canvas::canvas::{render_canvas, HighlightState as CanvasHighlightState}; // Use canvas library's render function
// Helper function to convert between HighlightState types
fn convert_highlight_state(local: &HighlightState) -> CanvasHighlightState {
match local {
HighlightState::Off => CanvasHighlightState::Off,
HighlightState::Characterwise { anchor } => CanvasHighlightState::Characterwise { anchor: *anchor },
HighlightState::Linewise { anchor_line } => CanvasHighlightState::Linewise { anchor_line: *anchor_line },
}
}
pub fn render_login(
f: &mut Frame,
@@ -49,14 +58,15 @@ pub fn render_login(
])
.split(inner_area);
// --- FORM RENDERING (Using bridge function) ---
render_canvas_form(
// --- FORM RENDERING (Using canvas library directly) ---
let canvas_highlight_state = convert_highlight_state(highlight_state);
render_canvas(
f,
chunks[0],
login_state, // LoginState implements CanvasState
theme,
theme, // Theme implements CanvasTheme
is_edit_mode,
highlight_state,
&canvas_highlight_state,
);
// --- ERROR MESSAGE ---

View File

@@ -6,15 +6,25 @@ use crate::{
components::common::dialog,
state::app::state::AppState,
modes::handlers::mode_manager::AppMode,
components::handlers::canvas_bridge::render_canvas_form, // Use our bridge function
};
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect, Margin},
style::{Style, Modifier, Color},
widgets::{Block, BorderType, Borders, Paragraph, List, ListItem, ListState},
widgets::{Block, BorderType, Borders, Paragraph},
Frame,
};
use crate::state::app::highlight::HighlightState;
use canvas::canvas::{render_canvas, HighlightState as CanvasHighlightState}; // Use canvas library's render function
use canvas::autocomplete::gui::render_autocomplete_dropdown; // Use canvas library's autocomplete dropdown
// Helper function to convert between HighlightState types
fn convert_highlight_state(local: &HighlightState) -> CanvasHighlightState {
match local {
HighlightState::Off => CanvasHighlightState::Off,
HighlightState::Characterwise { anchor } => CanvasHighlightState::Characterwise { anchor: *anchor },
HighlightState::Linewise { anchor_line } => CanvasHighlightState::Linewise { anchor_line: *anchor_line },
}
}
pub fn render_register(
f: &mut Frame,
@@ -49,14 +59,15 @@ pub fn render_register(
])
.split(inner_area);
// --- FORM RENDERING (Using bridge function) ---
let input_rect = render_canvas_form(
// --- FORM RENDERING (Using canvas library directly) ---
let canvas_highlight_state = convert_highlight_state(highlight_state);
let input_rect = render_canvas(
f,
chunks[0],
state, // RegisterState implements CanvasState
theme,
theme, // Theme implements CanvasTheme
is_edit_mode,
highlight_state,
&canvas_highlight_state,
);
// --- HELP TEXT ---
@@ -135,13 +146,17 @@ pub fn render_register(
button_chunks[1],
);
// --- AUTOCOMPLETE DROPDOWN (Simple bridge implementation) ---
// --- AUTOCOMPLETE DROPDOWN (Using canvas library directly) ---
if app_state.current_mode == AppMode::Edit {
if let Some(autocomplete_state) = state.autocomplete_state() {
if autocomplete_state.is_active && !autocomplete_state.suggestions.is_empty() {
if let Some(field_rect) = input_rect {
render_simple_autocomplete_dropdown(f, field_rect, f.area(), theme, autocomplete_state);
}
if let Some(input_rect) = input_rect {
render_autocomplete_dropdown(
f,
f.area(), // Frame area
input_rect, // Current input field rect
theme, // Theme implements CanvasTheme
autocomplete_state,
);
}
}
}
@@ -160,89 +175,3 @@ pub fn render_register(
);
}
}
/// Simple autocomplete dropdown renderer (bridge implementation)
fn render_simple_autocomplete_dropdown(
f: &mut Frame,
input_rect: Rect,
frame_area: Rect,
theme: &Theme,
autocomplete_state: &canvas::AutocompleteState<String>,
) {
if autocomplete_state.is_loading {
// Show loading indicator
let loading_area = Rect {
x: input_rect.x,
y: input_rect.y + 1,
width: input_rect.width,
height: 3,
};
let loading_paragraph = Paragraph::new("Loading suggestions...")
.style(Style::default().fg(theme.fg))
.block(
Block::default()
.borders(ratatui::widgets::Borders::ALL)
.border_style(Style::default().fg(theme.accent))
.style(Style::default().bg(theme.bg)),
);
f.render_widget(loading_paragraph, loading_area);
return;
}
if autocomplete_state.suggestions.is_empty() {
return;
}
// Calculate dropdown position
let dropdown_height = (autocomplete_state.suggestions.len() as u16).min(8) + 2;
let dropdown_width = input_rect.width.max(20);
let mut dropdown_area = Rect {
x: input_rect.x,
y: input_rect.y + 1,
width: dropdown_width,
height: dropdown_height,
};
// Keep dropdown within bounds
if dropdown_area.bottom() > frame_area.height {
dropdown_area.y = input_rect.y.saturating_sub(dropdown_height);
}
if dropdown_area.right() > frame_area.width {
dropdown_area.x = frame_area.width.saturating_sub(dropdown_width);
}
// Create list items
let items: Vec<ListItem> = autocomplete_state
.suggestions
.iter()
.enumerate()
.map(|(i, suggestion)| {
let is_selected = autocomplete_state.selected_index == Some(i);
let style = if is_selected {
Style::default()
.fg(theme.bg)
.bg(theme.highlight)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(theme.fg).bg(theme.bg)
};
ListItem::new(suggestion.display_text.as_str()).style(style)
})
.collect();
let list = List::new(items).block(
Block::default()
.borders(ratatui::widgets::Borders::ALL)
.border_style(Style::default().fg(theme.accent))
.style(Style::default().bg(theme.bg)),
);
let mut list_state = ListState::default();
list_state.select(autocomplete_state.selected_index);
f.render_stateful_widget(list, dropdown_area, &mut list_state);
}

View File

@@ -1,6 +1,6 @@
// src/state/pages/auth.rs
use canvas::{CanvasState, ActionContext, CanvasAction}; // Import from external library
use canvas::{AutocompleteCanvasState, AutocompleteState, SuggestionItem}; // For autocomplete
use canvas::canvas::{CanvasState, ActionContext, CanvasAction};
use canvas::autocomplete::{AutocompleteCanvasState, AutocompleteState, SuggestionItem};
use lazy_static::lazy_static;
lazy_static! {
@@ -45,7 +45,6 @@ pub struct RegisterState {
pub current_field: usize,
pub current_cursor_pos: usize,
pub has_unsaved_changes: bool,
// NEW: Replace old autocomplete with external library's system
pub autocomplete: AutocompleteState<String>,
}
@@ -123,7 +122,6 @@ impl CanvasState for LoginState {
self.has_unsaved_changes = changed;
}
// Handle custom actions (like submit)
fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
match action {
CanvasAction::Custom(action_str) if action_str == "submit" => {

View File

@@ -16,7 +16,8 @@ use crate::components::{
};
use crate::config::colors::themes::Theme;
use crate::modes::general::command_navigation::NavigationState;
use crate::state::pages::canvas_state::CanvasState;
use crate::state::pages::canvas_state::CanvasState as LocalCanvasState; // Keep local one with alias
use canvas::canvas::CanvasState; // Import external library's CanvasState trait
use crate::state::app::buffer::BufferState;
use crate::state::app::highlight::HighlightState as LocalHighlightState; // CHANGED: Alias local version
use canvas::canvas::HighlightState as CanvasHighlightState; // CHANGED: Import canvas version with alias
@@ -136,7 +137,7 @@ pub fn render_ui(
theme,
register_state,
app_state,
register_state.current_field() < 4,
register_state.current_field() < 4, // Now using CanvasState trait method
highlight_state, // Uses local version
);
} else if app_state.ui.show_add_table {
@@ -166,7 +167,7 @@ pub fn render_ui(
theme,
login_state,
app_state,
login_state.current_field() < 2,
login_state.current_field() < 2, // Now using CanvasState trait method
highlight_state, // Uses local version
);
} else if app_state.ui.show_admin {

View File

@@ -8,7 +8,8 @@ use crate::config::storage::storage::load_auth_data;
use crate::modes::common::commands::CommandHandler;
use crate::modes::handlers::event::{EventHandler, EventOutcome};
use crate::modes::handlers::mode_manager::{AppMode, ModeManager};
use crate::state::pages::canvas_state::CanvasState;
use crate::state::pages::canvas_state::CanvasState as LocalCanvasState; // Keep local one with alias
use canvas::canvas::CanvasState; // Import external library's CanvasState trait
use crate::state::pages::form::{FormState, FieldDefinition}; // Import FieldDefinition
use crate::state::pages::auth::AuthState;
use crate::state::pages::auth::LoginState;
@@ -38,6 +39,7 @@ use crate::state::app::state::DebugState;
#[cfg(feature = "ui-debug")]
use crate::utils::debug_logger::pop_next_debug_message;
// Rest of the file remains the same...
pub async fn run_ui() -> Result<()> {
let config = Config::load().context("Failed to load configuration")?;
let theme = Theme::from_str(&config.colors.theme);
@@ -346,25 +348,25 @@ pub async fn run_ui() -> Result<()> {
}
}
// 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();
// This condition correctly detects a table switch.
if prev_view_profile_name != current_view_profile
|| prev_view_table_name != current_view_table
{
if let (Some(prof_name), Some(tbl_name)) =
(current_view_profile.as_ref(), current_view_table.as_ref())
{
// --- START OF REFACTORED LOGIC ---
app_state.show_loading_dialog(
"Loading Table",
&format!("Fetching data for {}.{}...", prof_name, tbl_name),
);
needs_redraw = true;
// 1. Call our new, central function. It handles fetching AND caching.
match UiService::load_table_view(
&mut grpc_client,
&mut app_state,
@@ -374,72 +376,62 @@ pub async fn run_ui() -> Result<()> {
.await
{
Ok(mut new_form_state) => {
// 2. The function succeeded, we have a new FormState.
// Now, fetch its data.
if let Err(e) = UiService::fetch_and_set_table_count(
&mut grpc_client,
&mut new_form_state,
)
.await
{
// Handle count fetching error
app_state.update_dialog_content(
&format!("Error fetching count: {}", e),
vec!["OK".to_string()],
DialogPurpose::LoginFailed, // Or a more appropriate purpose
DialogPurpose::LoginFailed,
);
} else if new_form_state.total_count > 0 {
// If there are records, load the first/last one
if let Err(e) = UiService::load_table_data_by_position(
&mut grpc_client,
&mut new_form_state,
)
.await
{
// Handle data loading error
app_state.update_dialog_content(
&format!("Error loading data: {}", e),
vec!["OK".to_string()],
DialogPurpose::LoginFailed, // Or a more appropriate purpose
DialogPurpose::LoginFailed,
);
} else {
// Success! Hide the loading dialog.
app_state.hide_dialog();
}
} else {
// No records, so just reset to an empty form.
new_form_state.reset_to_empty();
app_state.hide_dialog();
}
// 3. CRITICAL: Replace the old form_state with the new one.
form_state = new_form_state;
// 4. Update our tracking variables.
prev_view_profile_name = current_view_profile;
prev_view_table_name = current_view_table;
table_just_switched = true;
}
Err(e) => {
// This handles errors from load_table_view (e.g., schema fetch failed)
app_state.update_dialog_content(
&format!("Error loading table: {}", e),
vec!["OK".to_string()],
DialogPurpose::LoginFailed, // Or a more appropriate purpose
DialogPurpose::LoginFailed,
);
// Revert the view change in app_state to avoid a loop
app_state.current_view_profile_name =
prev_view_profile_name.clone();
app_state.current_view_table_name =
prev_view_table_name.clone();
}
}
// --- END OF REFACTORED LOGIC ---
}
needs_redraw = true;
}
}
// Continue with the rest of the positioning logic...
// 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 &&