use ratatui::{
Frame,
layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},
style::Style,
text::{Line, Span},
widgets::{Block, Borders, Paragraph, Wrap},
};
use crate::app::App;
use crate::input::login::database_missing;
use crate::util::default_new_database_path;
pub fn draw_login(frame: &mut Frame, app: &mut App) {
let area = frame.area().inner(Margin { horizontal: 1, vertical: 1 });
let logo_height = if app.slim_mode { 0 } else { 20 };
let vertical = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Fill(1), Constraint::Length(logo_height), Constraint::Length(3), Constraint::Fill(1)])
.split(area);
let logo_area = vertical[1];
let input_row = vertical[2];
let horizontal = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Fill(1), Constraint::Length(50), Constraint::Fill(1)])
.split(input_row);
let input_area = horizontal[1];
app.max_len = (input_area.width - 2) as usize;
let missing = database_missing(app);
enum BoxContent<'a> {
Masked(&'a str),
Static(&'a str),
}
let (title, content) = if app.creating_database {
if app.confirming_new_db_password {
(" Confirm master password ", BoxContent::Masked(&app.new_db_confirm))
} else {
(" New master password ", BoxContent::Masked(&app.password))
}
} else if missing {
(" No database found ", BoxContent::Static("[c] Create a new database"))
} else {
("", BoxContent::Masked(&app.password))
};
let input_text = match content {
BoxContent::Masked(buf) => "•".repeat(buf.chars().count()),
BoxContent::Static(text) => text.to_string(),
};
let input = Paragraph::new(input_text)
.alignment(Alignment::Center)
.block(Block::default().borders(Borders::ALL).border_style(Style::default().fg(app.theme.border)).title(title));
if !app.slim_mode {
let claws_style = Style::default().fg(app.theme.claws);
let claws_light_style = Style::default().fg(app.theme.claws_light);
let claws_shadow_style = Style::default().fg(app.theme.claws_shadow);
let shell_style = Style::default().fg(app.theme.shell);
let shell_light_style = Style::default().fg(app.theme.shell_light);
let shell_shadow_style = Style::default().fg(app.theme.shell_shadow);
let logo = Paragraph::new(vec![
Line::from(vec![Span::styled(" ███", claws_light_style), Span::styled(" ███ ", claws_style)]),
Line::from(vec![Span::styled(" ██", claws_light_style), Span::styled("█ kptui ███ ", claws_style)]),
Line::from(vec![Span::styled(" ██", claws_light_style), Span::styled("█ █ █ ███ ", claws_style)]),
Line::from(vec![
Span::styled(" ██", claws_light_style),
Span::styled("█ ██ ", claws_light_style),
Span::styled(format!("v{:<8}", env!("CARGO_PKG_VERSION")), claws_light_style),
Span::styled(" ██ ███ ", claws_style),
]),
Line::from(vec![Span::styled(" ██", claws_light_style), Span::styled("███ █████ ", claws_style)]),
Line::from(vec![
Span::styled(" ██", claws_light_style),
Span::styled(" ██ ", claws_style),
Span::styled("█ █", claws_light_style),
Span::styled(" ██ ██ ", claws_style),
]),
Line::from(vec![
Span::styled("██", claws_light_style),
Span::styled("█ ██ ", claws_style),
Span::styled("█ █", claws_style),
Span::styled(" ██ ███", claws_style),
]),
Line::from(vec![
Span::styled("██", claws_light_style),
Span::styled("██ ██ ", claws_style),
Span::styled("████████████████", shell_light_style),
Span::styled(" ██ ████", claws_style),
]),
Line::from(vec![
Span::styled(" ██", claws_light_style),
Span::styled("██ ", claws_style),
Span::styled("██", shell_light_style),
Span::styled("██████████████████", shell_style),
Span::styled(" ████ ", claws_style),
]),
Line::from(vec![
Span::styled(" ████ ", claws_style),
Span::styled("██", shell_light_style),
Span::styled("████████████████████", shell_style),
Span::styled(" ████ ", claws_style),
]),
Line::from(vec![
Span::styled(" ███", claws_style),
Span::styled("██", shell_light_style),
Span::styled("████████████████████", shell_style),
Span::styled("███ ", claws_style),
]),
Line::from(vec![Span::styled(" ████████████████████", shell_style), Span::styled("██ ", shell_shadow_style)]),
Line::from(vec![
Span::styled(" ███", claws_style),
Span::styled("████████████████████", shell_style),
Span::styled("██", shell_shadow_style),
Span::styled("███ ", claws_shadow_style),
]),
Line::from(vec![
Span::styled(" ███ ██", claws_style),
Span::styled("████████████████", shell_style),
Span::styled("██", shell_shadow_style),
Span::styled("██ ███ ", claws_shadow_style),
]),
Line::from(vec![
Span::styled(" ██ ██ ", claws_style),
Span::styled("████████████████", shell_shadow_style),
Span::styled(" ██ ██ ", claws_shadow_style),
]),
Line::from(vec![Span::styled(" █ ██ ", claws_style), Span::styled("██ ██ ██ █ ", claws_shadow_style)]),
Line::from(vec![Span::styled(" █ ", claws_style), Span::styled("██ ██ █ ", claws_shadow_style)]),
Line::from(vec![Span::styled(" █ █ ", claws_shadow_style)]),
])
.alignment(Alignment::Center)
.style(claws_style);
frame.render_widget(logo, logo_area);
}
frame.render_widget(input, input_area);
if let Some(error) = &app.login_error {
let error_area = Rect { x: input_area.x, y: input_area.y + input_area.height, width: input_area.width, height: 3 };
let error_text = Paragraph::new(error.as_str())
.alignment(Alignment::Center)
.wrap(Wrap { trim: true })
.style(Style::new().fg(app.theme.error));
frame.render_widget(error_text, error_area);
} else if app.creating_database {
let path = app.config.default_database.clone().unwrap_or_else(default_new_database_path);
let hint_area = Rect { x: input_area.x, y: input_area.y + input_area.height, width: input_area.width, height: 2 };
let hint = Paragraph::new(vec![Line::from(format!("will create {}", path.display())), Line::from("[Enter] Confirm [Esc] Cancel")])
.alignment(Alignment::Center)
.style(Style::new().fg(app.theme.warning));
frame.render_widget(hint, hint_area);
} else if missing {
let hint_area = Rect { x: input_area.x, y: input_area.y + input_area.height, width: input_area.width, height: 1 };
let hint = Paragraph::new("[n] Create a new database [Esc] Quit")
.alignment(Alignment::Center)
.style(Style::new().fg(app.theme.warning));
frame.render_widget(hint, hint_area);
} else {
let hint_area = Rect { x: input_area.x, y: input_area.y + input_area.height, width: input_area.width, height: 1 };
let hint = Paragraph::new("Enter the master password [Esc] Quit")
.alignment(Alignment::Center)
.style(Style::new().fg(app.theme.warning));
frame.render_widget(hint, hint_area);
}
let typed_len = if app.creating_database {
if app.confirming_new_db_password {
app.new_db_confirm.chars().count() as u16
} else {
app.password.chars().count() as u16
}
} else if missing {
return;
} else {
app.password.chars().count() as u16
};
let inner = Block::default().borders(Borders::ALL).inner(input_area);
let cursor_x = inner.x + (inner.width / 2).saturating_sub(typed_len / 2) + typed_len;
frame.set_cursor_position((cursor_x, input_area.y + 1));
}