1mod detail;
2mod list;
3
4use anyhow::{Error, Result};
5use crossterm::event::{self, KeyCode, KeyEventKind};
6use memos_api::models::V1Memo;
7use ratatui::prelude::*;
8use ratatui::DefaultTerminal;
9use std::cell::RefCell;
10use std::{cmp, io};
11
12use self::detail::Detail;
13use self::list::List;
14
15pub struct ListView<'a> {
16 memos: &'a Vec<V1Memo>,
17 current_index: RefCell<usize>,
18}
19
20impl<'a> ListView<'a> {
21 pub fn try_new(value: &'a Vec<V1Memo>) -> Result<Self> {
22 if value.len() < 1 {
23 return Err(Error::msg("List is empty."));
24 }
25
26 let list_view = ListView {
27 memos: value,
28 current_index: 0.into(),
29 };
30
31 Ok(list_view)
32 }
33
34 pub fn draw(&self) -> io::Result<()> {
35 let mut terminal = ratatui::init();
36
37 terminal.clear()?;
38
39 let result = self.run(terminal);
40
41 ratatui::restore();
42
43 result
44 }
45
46 fn run(&self, mut terminal: DefaultTerminal) -> io::Result<()> {
47 loop {
48 terminal.draw(|frame| {
49 let layout = Layout::default()
50 .constraints([Constraint::Min(2)])
51 .horizontal_margin(20)
52 .vertical_margin(10)
53 .direction(Direction::Horizontal)
54 .constraints(vec![Constraint::Percentage(25), Constraint::Percentage(50)])
55 .split(frame.area());
56
57 let list = List::new(&self);
58 let detail = Detail::new(&self);
59
60 frame.render_widget(list, layout[0]);
61 frame.render_widget(detail, layout[1]);
62 })?;
63
64 if let event::Event::Key(key) = event::read()? {
65 match (key.kind, key.code) {
66 (KeyEventKind::Press, KeyCode::Char('j')) => {
67 let current_index = *self.current_index.borrow();
68 let current_index = current_index.saturating_add(1);
69 let current_index = cmp::min(self.memos.len() - 1, current_index);
70 self.current_index.replace(current_index);
71 }
72 (KeyEventKind::Press, KeyCode::Char('k')) => {
73 let current_index = *self.current_index.borrow();
74 let current_index = current_index.saturating_sub(1);
75 self.current_index.replace(current_index);
76 }
77 (KeyEventKind::Press, KeyCode::Char('q')) => return Ok(()),
78 _ => (),
79 }
80 }
81 }
82 }
83
84 fn get_current_memo(&self) -> &V1Memo {
85 let current_index = *self.current_index.borrow();
86 &self.memos[current_index]
87 }
88}