use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Clear, Paragraph},
Frame,
};
use super::super::theme::get_theme_colors;
use super::utils::centered_rect;
use crate::app::App;
pub fn render_unified_composer_modal(frame: &mut Frame, app: &mut App, area: Rect) {
use crate::app::ComposerMode;
let theme = get_theme_colors(app);
let (title, has_context, context_lines, max_chars, instructions) =
match &app.composer_state.mode {
Some(ComposerMode::NewPost) => (
"New Post",
false,
vec![],
280,
"✨ Type to compose | Enter: Submit | Esc: Cancel ✨",
),
Some(ComposerMode::Reply {
parent_author,
parent_content,
..
}) => {
let context_width = area.width.saturating_sub(24).min(66) as usize;
let truncated_content = if parent_content.chars().count() > context_width {
let truncated: String = parent_content
.chars()
.take(context_width.saturating_sub(3))
.collect();
format!("{}...", truncated)
} else {
parent_content.clone()
};
let lines = vec![
Line::from(vec![
Span::styled("Replying to ", Style::default().fg(theme.text_dim)),
Span::styled(
format!("@{}", parent_author),
Style::default()
.fg(theme.primary)
.add_modifier(Modifier::BOLD),
),
]),
Line::from(Span::styled(
truncated_content,
Style::default()
.fg(theme.text_dim)
.add_modifier(Modifier::ITALIC),
)),
];
(
"Reply to Post",
true,
lines,
280,
"Type to compose | Enter: Submit | Esc: Cancel",
)
}
Some(ComposerMode::EditPost { .. }) => (
"Edit Post",
false,
vec![],
280,
"Type to edit | Enter: Submit | Esc: Cancel",
),
Some(ComposerMode::EditBio) => (
"Edit Bio",
false,
vec![],
160,
"Type to edit | Enter: Submit | Esc: Cancel",
),
None => return, };
let error_message = match &app.composer_state.mode {
Some(ComposerMode::NewPost) => app.posts_state.error.clone(),
Some(ComposerMode::Reply { .. }) | Some(ComposerMode::EditPost { .. }) => {
app.post_detail_state.as_ref().and_then(|s| s.error.clone())
}
Some(ComposerMode::EditBio) => app.profile_state.error.clone(),
None => None,
};
let height_percent = match &app.composer_state.mode {
Some(ComposerMode::Reply { .. }) => 56, _ => 80,
};
let modal_area = centered_rect(70, height_percent, area);
frame.render_widget(Clear, modal_area);
let outer_block = Block::default()
.title(format!(" {} ", title))
.borders(Borders::ALL)
.border_style(
Style::default()
.fg(theme.accent)
.add_modifier(Modifier::BOLD),
)
.style(Style::default().bg(theme.background));
let inner = outer_block.inner(modal_area);
frame.render_widget(outer_block, modal_area);
let mut constraints = if has_context {
vec![
Constraint::Length(4), Constraint::Min(0), Constraint::Length(3), ]
} else {
vec![
Constraint::Min(0), Constraint::Length(3), ]
};
if error_message.is_some() {
constraints.push(Constraint::Length(3)); }
constraints.push(Constraint::Length(3));
let modal_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(inner);
let mut chunk_idx = 0;
if has_context {
let context = Paragraph::new(context_lines).block(
Block::default()
.borders(Borders::ALL)
.title("Context")
.border_style(Style::default().fg(theme.text_dim)),
);
frame.render_widget(context, modal_chunks[chunk_idx]);
chunk_idx += 1;
}
let content_area = modal_chunks[chunk_idx];
let content_block = Block::default()
.borders(Borders::ALL)
.title("Content")
.border_style(Style::default().fg(theme.primary));
let inner_content_area = content_block.inner(content_area);
frame.render_widget(content_block, content_area);
frame.render_widget(&app.composer_state.textarea, inner_content_area);
chunk_idx += 1;
let char_count = app.composer_state.char_count();
let counter_style = if char_count >= max_chars {
Style::default()
.fg(theme.error)
.add_modifier(Modifier::BOLD)
} else if char_count >= (max_chars * 9 / 10) {
Style::default().fg(theme.warning)
} else {
Style::default().fg(theme.success)
};
let counter_text = format!("{}/{} characters", char_count, max_chars);
let counter = Paragraph::new(counter_text)
.style(counter_style)
.alignment(Alignment::Center)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border)),
);
frame.render_widget(counter, modal_chunks[chunk_idx]);
chunk_idx += 1;
if let Some(err) = error_message {
let error_widget = Paragraph::new(err)
.style(Style::default().fg(theme.error).add_modifier(Modifier::BOLD))
.alignment(Alignment::Center)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.error)),
);
frame.render_widget(error_widget, modal_chunks[chunk_idx]);
chunk_idx += 1;
}
let instructions_widget = Paragraph::new(instructions)
.style(Style::default().fg(theme.text))
.alignment(Alignment::Center)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(theme.border)),
);
frame.render_widget(instructions_widget, modal_chunks[chunk_idx]);
}