diff --git a/crates/wayfarer/src/tui.rs b/crates/wayfarer/src/tui.rs
index 36fd15e..4c7b5d2 100644
--- a/crates/wayfarer/src/tui.rs
+++ b/crates/wayfarer/src/tui.rs
@@ -17,7 +17,7 @@ use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use ratatui::backend::CrosstermBackend;
-use tracing::{debug, error, info};
+use tracing::{debug, info};
use self::state::{Mode, State};
@@ -104,6 +104,8 @@ fn run(terminal: &mut Terminal, mut state: State) -> Result<()> {
events::handle(&mut msg_tx, &mut state)?;
+ state.clear_expired_error_message();
+
match msg_rx.try_recv() {
Ok(Message::Exit) => {
debug!("Exiting...");
@@ -111,8 +113,7 @@ fn run(terminal: &mut Terminal, mut state: State) -> Result<()> {
}
Ok(message) => {
if let Err(err) = handle_message(&mut state, &mut msg_tx, message) {
- error!(message = ?err);
- state.mode = Mode::ShowError(format!("{}", err));
+ state.show_error_message(err);
}
}
Err(TryRecvError::Empty) => (),
@@ -174,18 +175,18 @@ fn handle_message(
Message::StartEditEntry => state.start_editing_entry(),
Message::CommitEditEntry => {
if let Err(err) = state.commit_entry_edit() {
- error!(%err);
+ state.show_error_message(err);
}
}
Message::CancelEditEntry => state.cancel_editing_entry(),
Message::NextEntryValue => {
if let Err(err) = state.next_entry_value() {
- error!(%err);
+ state.show_error_message(err);
}
}
Message::PreviousEntryValue => {
if let Err(err) = state.previous_entry_value() {
- error!(%err);
+ state.show_error_message(err);
}
}
_ => (),
diff --git a/crates/wayfarer/src/tui/events.rs b/crates/wayfarer/src/tui/events.rs
index 5b1012b..0b980f7 100644
--- a/crates/wayfarer/src/tui/events.rs
+++ b/crates/wayfarer/src/tui/events.rs
@@ -57,10 +57,6 @@ fn handle_keyboard_input(
msg_tx.send(Message::SetMode(Mode::Normal))?;
}
- (Mode::ShowError(_), _) => {
- msg_tx.send(Message::SetMode(Mode::Normal))?;
- }
-
(Mode::SelectFile, KeyCode::Enter) => {
msg_tx.send(Message::LoadFile)?;
}
@@ -147,5 +143,7 @@ fn handle_keyboard_input(
_ => (),
};
+ state.clear_error_message();
+
Ok(())
}
diff --git a/crates/wayfarer/src/tui/state.rs b/crates/wayfarer/src/tui/state.rs
index c9ab44b..ef45cc1 100644
--- a/crates/wayfarer/src/tui/state.rs
+++ b/crates/wayfarer/src/tui/state.rs
@@ -1,12 +1,14 @@
+use core::fmt;
use std::fs::{self, create_dir_all, read_to_string};
use std::io::Write;
use std::os::unix::prelude::OsStrExt;
use std::path::Path;
+use std::time::{Duration, Instant};
use anyhow::{bail, Context, Result};
use jrny_save::{RobeColor, Savefile, LEVEL_NAMES};
use ratatui::widgets::TableState;
-use tracing::debug;
+use tracing::{debug, error};
use tui_input::Input;
use super::view::info::glyphs::TABLE_RANGE as GLYPHS_TABLE_RANGE;
@@ -27,8 +29,6 @@ pub enum Mode {
Insert,
- ShowError(String),
-
SelectFile,
}
@@ -57,6 +57,7 @@ pub struct State {
pub stats_table: TableState,
pub glyphs_table: TableState,
pub murals_table: TableState,
+ pub error_msg: Option<(Instant, String)>,
pub mode: Mode,
pub edit_input: Option,
pub file_select: Input,
@@ -66,6 +67,29 @@ pub struct State {
impl State {
+ const ERROR_MSG_DURATION: Duration = Duration::new(3, 0);
+
+ pub fn show_error_message(&mut self, msg: S)
+ where
+ S: fmt::Display,
+ {
+ let until = Instant::now() + Self::ERROR_MSG_DURATION;
+ error!(%msg);
+ self.error_msg = Some((until, msg.to_string()));
+ }
+
+ pub fn clear_expired_error_message(&mut self) {
+ if let Some((until, _)) = self.error_msg {
+ if Instant::now() >= until {
+ self.error_msg = None;
+ }
+ }
+ }
+
+ pub fn clear_error_message(&mut self) {
+ self.error_msg.take();
+ }
+
pub fn load() -> Result {
let data_dir = DIRS.data_local_dir();
diff --git a/crates/wayfarer/src/tui/view/status_bar.rs b/crates/wayfarer/src/tui/view/status_bar.rs
index 2441167..e9f0851 100644
--- a/crates/wayfarer/src/tui/view/status_bar.rs
+++ b/crates/wayfarer/src/tui/view/status_bar.rs
@@ -6,21 +6,28 @@ use crate::tui::view::Frame;
use crate::tui::{Mode, State};
-pub fn render(state: &State, mut frame: &mut Frame, area: Rect) {
+pub fn render(state: &State, frame: &mut Frame, area: Rect) {
let status_block = Block::default().padding(Padding::horizontal(2));
- match &state.mode {
- Mode::ShowError(error_msg) => {
- let error_msg = Paragraph::new(error_msg.clone())
- .style(Style::default().fg(ratatui::style::Color::LightRed))
- .block(status_block);
- frame.render_widget(error_msg, area);
- }
+ match &state.error_msg {
+ Some((_, msg)) => render_error_message(&msg, frame, status_block, area),
+ None => render_status(state, frame, status_block, area),
+ }
+}
+fn render_error_message(msg: &str, frame: &mut Frame, block: Block, area: Rect) {
+ let error_msg = Paragraph::new(msg)
+ .style(Style::default().fg(ratatui::style::Color::LightRed))
+ .block(block);
+ frame.render_widget(error_msg, area);
+}
+
+pub fn render_status(state: &State, mut frame: &mut Frame, block: Block, area: Rect) {
+ match &state.mode {
Mode::Edit | Mode::Insert => {
if let Some(savefile) = &state.savefile {
let text = format!("Editing file: {}", savefile.path.display());
- let status = Paragraph::new(text).block(status_block);
+ let status = Paragraph::new(text).block(block);
frame.render_widget(status, area);
}
}
@@ -29,24 +36,23 @@ pub fn render(state: &State, mut frame: &mut Frame, area: Rect) {
Mode::Normal if state.is_watching_file() => {
if let Some(savefile) = &state.savefile {
let text = format!("Watching file: {}", savefile.path.display());
- let status = Paragraph::new(text).block(status_block);
+ let status = Paragraph::new(text).block(block);
frame.render_widget(status, area);
}
}
- Mode::SelectFile => render_file_select(&state, &mut frame, status_block, area),
+ Mode::SelectFile => render_file_select(&state, &mut frame, block, area),
_ => {
if let Some(savefile) = &state.savefile {
let text = format!("Showing file: {}", savefile.path.display());
- let status = Paragraph::new(text).block(status_block);
+ let status = Paragraph::new(text).block(block);
frame.render_widget(status, area);
}
}
}
}
-
fn render_file_select(state: &State, frame: &mut Frame, block: Block, area: Rect) {
const PROMPT: &str = "Open file:";
const PADDING: usize = 2;