use crate::action::mode::Mode;
use crate::action::Action;
use crate::router::{Address, Cacheable, Kind, Message, Payload};
use crate::tui::Frame;
use color_eyre::eyre::{eyre, Result};
use crossterm::event::KeyEvent;
use email_address::EmailAddress;
use ratatui::{
prelude::*,
widgets::{Block, Borders},
};
use tokio::sync::mpsc;
use tui_textarea::{Input, Key, TextArea};
#[derive(Debug)]
pub struct EmailPrompt<'a> {
message_tx_to_router: mpsc::UnboundedSender<Message>,
text: TextArea<'a>,
is_active: bool,
is_valid: bool,
}
impl<'a> EmailPrompt<'a> {
pub fn new(tx: mpsc::UnboundedSender<Message>) -> EmailPrompt<'a> {
EmailPrompt {
message_tx_to_router: tx,
text: TextArea::default(),
is_active: false,
is_valid: false,
}
}
fn layer(&mut self, area: Rect) -> Rect {
self.configure_text_field();
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Max(3)]);
if self.is_active {
self.text.set_block(Self::active_block_style());
layout.split(area.inner(&Margin {
horizontal: 1,
vertical: 0,
}))[0]
} else {
self.text.set_block(Self::inactive_block_style());
layout.split(area)[0]
}
}
fn configure_text_field(&mut self) {
self.text.set_cursor_line_style(Style::default());
self.text.set_placeholder_text("Your email");
self.text.set_placeholder_style(Style::default());
}
fn active_block_style() -> Block<'a> {
Block::default()
.borders(Borders::ALL)
.style(Style::default().fg(Color::LightGreen))
}
fn inactive_block_style() -> Block<'a> {
Block::default()
.title("Email Prompt")
.title_alignment(Alignment::Left)
.borders(Borders::ALL)
.style(Style::default())
}
pub fn activate(&mut self) -> Result<Option<Action>> {
self.is_active = true;
Ok(Some(Action::ChangeMode(Mode::TextInput)))
}
pub fn deactivate(&mut self) {
self.is_active = false;
}
pub fn is_active(&self) -> bool {
self.is_active
}
fn upgrade_api_key(&mut self, email: EmailAddress) -> Result<()> {
self
.message_tx_to_router
.send(Message {
source: Address::Home,
destination: Address::IrxClient,
payload: Payload::Email(email),
tag: None,
cacheable: Cacheable::No,
kind: Kind::Tell,
})
.map_err(|e| eyre!(e))
}
pub fn render(&mut self, area: Rect, f: &mut Frame<'_>) {
let layer = self.layer(area);
f.render_widget(self.text.widget(), layer);
}
pub fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> {
self.validate();
match key_event.into() {
Input { key: Key::Esc, .. }
| Input {
key: Key::Char('c'),
ctrl: true,
..
} => {
self.deactivate();
}
Input {
key: Key::Enter, ..
} if self.is_valid => {
self.deactivate();
let email = self.get_email()?;
if self.upgrade_api_key(email).is_ok() {
self.reset();
}
}
input => {
if self.text.lines()[0].len() < 80 {
if self.text.input(input) {
self.validate();
}
} else {
self.reset();
}
}
}
Ok(())
}
fn reset(&mut self) {
self.text = TextArea::default();
self.is_valid = false;
self.deactivate();
}
fn validate(&mut self) -> bool {
if let Err(_err) = self.text.lines()[0].parse::<EmailAddress>() {
self.text.set_style(Style::default().fg(Color::Yellow));
self.text.set_block(
Block::default()
.borders(Borders::ALL)
.title("Press Esc to cancel"),
);
self.is_valid = false;
} else {
self.text.set_style(Style::default().fg(Color::LightGreen));
self.text.set_block(
Block::default()
.borders(Borders::ALL)
.title("Press Enter to submit"),
);
self.is_valid = true;
}
self.is_valid
}
pub fn get_email(&mut self) -> Result<EmailAddress> {
self.text.lines()[0]
.parse::<EmailAddress>()
.map_err(|e| eyre!(e))
}
}