use ratatui::{
layout::{Alignment, Rect},
style::{Modifier, Style},
text::{Line, Span},
widgets::Paragraph,
Frame,
};
use super::super::components::modal::{render_modal_container, ModalConfig};
use super::super::theme::get_theme_colors;
use crate::app::App;
pub fn render_help_modal(frame: &mut Frame, app: &mut App, area: Rect) {
let theme = get_theme_colors(app);
let config = ModalConfig::new(" Keyboard Shortcuts ").with_size(80, 85);
let inner = render_modal_container(frame, area, &config, &theme);
let shortcuts = get_shortcuts_for_context(app);
let mut lines = vec![Line::from("")];
for (category, items) in shortcuts {
lines.push(Line::from(Span::styled(
category,
Style::default()
.fg(theme.accent)
.add_modifier(Modifier::BOLD),
)));
lines.push(Line::from(""));
for (key, description) in items {
lines.push(Line::from(vec![
Span::styled(format!(" {:<15}", key), Style::default().fg(theme.success)),
Span::styled(description, Style::default().fg(theme.text)),
]));
}
lines.push(Line::from(""));
}
let help_content = Paragraph::new(lines)
.alignment(Alignment::Center)
.style(Style::default().fg(theme.text))
.wrap(ratatui::widgets::Wrap { trim: false });
frame.render_widget(help_content, inner);
}
pub fn render_save_confirmation_modal(frame: &mut Frame, app: &App, area: Rect) {
let theme = get_theme_colors(app);
let config = ModalConfig::new(" Unsaved Changes ")
.with_size(50, 25)
.with_border_color(theme.warning);
let inner = render_modal_container(frame, area, &config, &theme);
let content = vec![
Line::from(""),
Line::from(Span::styled(
"You have unsaved changes in Settings.",
Style::default().fg(theme.text),
)),
Line::from(""),
Line::from(Span::styled(
"Do you want to save before leaving?",
Style::default().fg(theme.text),
)),
Line::from(""),
Line::from("─".repeat(46)).style(Style::default().fg(theme.border)),
Line::from(""),
Line::from(vec![
Span::styled(
"Y",
Style::default()
.fg(theme.success)
.add_modifier(Modifier::BOLD),
),
Span::styled(": Save ", Style::default().fg(theme.text)),
Span::styled(
"N",
Style::default()
.fg(theme.error)
.add_modifier(Modifier::BOLD),
),
Span::styled(": Discard ", Style::default().fg(theme.text)),
Span::styled(
"Esc",
Style::default()
.fg(theme.accent)
.add_modifier(Modifier::BOLD),
),
Span::styled(": Cancel", Style::default().fg(theme.text)),
]),
];
let modal = Paragraph::new(content)
.alignment(Alignment::Center)
.style(Style::default().fg(theme.text));
frame.render_widget(modal, inner);
}
pub fn get_shortcuts_for_context(
app: &mut App,
) -> Vec<(&'static str, Vec<(&'static str, &'static str)>)> {
let mut shortcuts = vec![];
shortcuts.push((
"Global",
vec![("q / Esc", "Quit application"), ("?", "Toggle this help")],
));
if matches!(app.current_screen, crate::app::Screen::Main) {
shortcuts.push(("Account", vec![("Shift+L", "Logout")]));
}
match app.current_screen {
crate::app::Screen::Auth => {
add_auth_shortcuts(&mut shortcuts);
}
crate::app::Screen::Main => {
add_main_screen_shortcuts(app, &mut shortcuts);
}
}
shortcuts
}
pub fn add_auth_shortcuts(shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>) {
shortcuts.push((
"Authentication",
vec![
("l", "Load test users"),
("↑/k", "Move up"),
("↓/j", "Move down"),
("Enter", "Login with selected user"),
],
));
}
pub fn add_main_screen_shortcuts(
app: &mut App,
shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>,
) {
shortcuts.push((
"Tab Navigation",
vec![("Tab", "Next tab"), ("Shift+Tab", "Previous tab")],
));
match app.current_tab {
crate::app::Tab::Posts => add_posts_tab_shortcuts(app, shortcuts),
crate::app::Tab::DMs => add_dms_tab_shortcuts(shortcuts),
crate::app::Tab::Profile => add_profile_tab_shortcuts(app, shortcuts),
crate::app::Tab::Settings => add_settings_tab_shortcuts(shortcuts),
}
if app.user_profile_view.is_some() {
shortcuts.push((
"User Profile View",
vec![
("f", "Follow/Unfollow user"),
("m", "Open DM conversation (mutual friends only)"),
("Esc / q", "Close profile"),
],
));
}
}
pub fn add_posts_tab_shortcuts(
app: &mut App,
shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>,
) {
if app.viewing_post_detail {
add_post_detail_shortcuts(app, shortcuts);
} else {
add_posts_feed_shortcuts(app, shortcuts);
}
}
pub fn add_post_detail_shortcuts(
app: &mut App,
shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>,
) {
if let Some(detail_state) = &app.post_detail_state {
shortcuts.push((
"Post Detail View",
vec![
("Esc", "Close post detail"),
("↑/k", "Previous reply"),
("↓/j", "Next reply"),
("r", "Reply to post"),
("u", "Upvote post/reply"),
("d", "Downvote post/reply"),
("p", "View author profile"),
],
));
if let Some(post) = &detail_state.post {
if let Some(user) = &app.auth_state.current_user {
if post.author_id == user.id {
shortcuts.push(("Post Owner Actions", vec![("x", "Delete post")]));
}
}
}
if detail_state.show_reply_composer {
shortcuts.push((
"Reply Composer",
vec![
("Enter", "Submit reply"),
("Esc", "Cancel reply"),
(":emoji:", "Use emoji shortcodes"),
],
));
} else if detail_state.show_delete_confirmation {
shortcuts.push((
"Delete Confirmation",
vec![
("y / Y", "Confirm deletion"),
("n / N / Esc", "Cancel deletion"),
],
));
}
}
}
pub fn add_posts_feed_shortcuts(
app: &mut App,
shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>,
) {
shortcuts.push((
"Posts Tab",
vec![
("↓/j", "Next post"),
("↑/k", "Previous post"),
("Space/Enter", "Open post detail"),
("u", "Upvote selected post"),
("d", "Downvote selected post"),
("n", "New post"),
("f", "Filter posts"),
("s", "Search users"),
("p", "View author profile"),
],
));
if app.posts_state.show_new_post_modal {
shortcuts.push((
"New Post Modal",
vec![
("Enter", "Submit post"),
("Esc", "Cancel"),
(":emoji:", "Use emoji shortcodes"),
],
));
}
if app.user_search_state.show_modal {
shortcuts.push((
"User Search Modal",
vec![
("Type", "Search for users"),
("↓/j", "Next result"),
("↑/k", "Previous result"),
("Enter", "View profile"),
("d", "Send DM"),
("Esc", "Close search"),
],
));
}
}
pub fn add_dms_tab_shortcuts(
shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>,
) {
shortcuts.push((
"DMs Tab",
vec![
("↓/j", "Next conversation"),
("↑/k", "Previous conversation / New button"),
("Enter", "Open conversation / Start new"),
("Type", "Compose message"),
("Enter", "Send message"),
("Esc", "Clear message / Stop typing"),
(":emoji:", "Use emoji shortcodes"),
],
));
}
pub fn add_profile_tab_shortcuts(
app: &mut App,
shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>,
) {
shortcuts.push((
"Profile Tab",
vec![
("↓/j", "Next post"),
("↑/k", "Previous post"),
("e", "Edit bio"),
],
));
if app.profile_state.show_edit_bio_modal {
shortcuts.push((
"Edit Bio Modal",
vec![
("Enter", "Save bio"),
("Esc", "Cancel"),
("←/→", "Move cursor"),
("Home/End", "Start/End of text"),
],
));
}
}
pub fn add_settings_tab_shortcuts(
shortcuts: &mut Vec<(&'static str, Vec<(&'static str, &'static str)>)>,
) {
shortcuts.push((
"Settings Tab",
vec![
("↓/j", "Next setting"),
("↑/k", "Previous setting"),
("←/h / →/l / Enter", "Change value"),
("s", "Save settings"),
],
));
}