1use {
2 crate::{context::Context, views},
3 glues_core::Glues,
4 ratatui::Frame,
5};
6
7#[cfg(not(target_arch = "wasm32"))]
8use {
9 crate::{
10 input::{Input, KeyCode, KeyEvent, KeyEventKind},
11 logger::*,
12 },
13 ratatui::DefaultTerminal,
14 std::time::Duration,
15};
16
17#[cfg(target_arch = "wasm32")]
18#[cfg(not(target_arch = "wasm32"))]
19use crate::logger::*;
20
21pub struct App {
22 pub(crate) glues: Glues,
23 pub(crate) context: Context,
24}
25
26impl Default for App {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl App {
33 pub fn new() -> Self {
34 let glues = Glues::new();
35 let context = Context::default();
36
37 Self { glues, context }
38 }
39
40 #[doc(hidden)]
41 pub fn glues_mut(&mut self) -> &mut Glues {
42 &mut self.glues
47 }
48
49 #[cfg(not(target_arch = "wasm32"))]
50 pub async fn run(mut self, mut terminal: DefaultTerminal) -> color_eyre::Result<()> {
51 use ratatui::crossterm as ct;
52
53 loop {
54 if self
55 .context
56 .last_log
57 .as_ref()
58 .is_some_and(|(_, created_at)| created_at.elapsed().log_unwrap().as_secs() > 5)
59 {
60 self.context.last_log = None;
61 }
62
63 terminal.draw(|frame| self.draw(frame))?;
64
65 if !ct::event::poll(Duration::from_millis(1500))? {
66 let mut transitions = Vec::new();
67 {
68 let mut queue = self.glues.transition_queue.lock().log_unwrap();
69
70 while let Some(transition) = queue.pop_front() {
71 transitions.push(transition);
72 }
73 }
74
75 for transition in transitions {
76 self.handle_transition(transition).await;
77 }
78
79 self.save().await;
80 continue;
81 }
82
83 let raw_input = ct::event::read()?;
84 let input: Input = raw_input.into();
85
86 if !matches!(
87 input,
88 Input::Key(KeyEvent {
89 kind: KeyEventKind::Press,
90 ..
91 })
92 ) {
93 continue;
94 }
95
96 match input {
97 Input::Key(KeyEvent {
98 code: KeyCode::Char('c'),
99 modifiers,
100 ..
101 }) if modifiers.ctrl => {
102 self.save().await;
103 return Ok(());
104 }
105 _ => {
106 let action = self.context.consume(&input).await;
107 let quit = self.handle_action(action, input).await;
108 if quit {
109 return Ok(());
110 }
111 }
112 }
113 }
114 }
115
116 pub fn draw(&mut self, frame: &mut Frame) {
117 use ratatui::layout::{
118 Constraint::{Length, Percentage},
119 Layout,
120 };
121
122 let state = &self.glues.state;
123 let context = &mut self.context;
124 let vertical = Layout::vertical([Length(1), Percentage(100)]);
125 let [statusbar, body] = vertical.areas(frame.area());
126
127 views::statusbar::draw(frame, statusbar, state, &context.notebook);
128 views::body::draw(frame, body, context);
129 views::dialog::draw(frame, state, context);
130 }
131
132 pub fn context_mut(&mut self) -> &mut Context {
133 &mut self.context
134 }
135}