Change columns up. Refactor input and state change code.

This commit is contained in:
Joseph Ferano 2022-12-05 00:32:48 +04:00
parent 77f9c95cf1
commit c38fa823c9
3 changed files with 63 additions and 47 deletions

View File

@ -1,28 +1,19 @@
use std::cmp::min;
use crossterm::event; use crossterm::event;
use crossterm::event::{Event, KeyCode}; use crossterm::event::{Event, KeyCode};
use int_enum::IntEnum; use crate::types::{AppState};
use crate::types::{AppState, TaskStatus};
pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> { pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
if let Event::Key(key) = event::read()? { if let Event::Key(key) = event::read()? {
match key.code { match key.code {
KeyCode::Char('q') => state.quit = true, KeyCode::Char('q') => state.quit = true,
KeyCode::Char('h') | KeyCode::Char('h') |
KeyCode::Left => state.selected_column = state.selected_column.saturating_sub(1), KeyCode::Left => state.select_previous_column(),
KeyCode::Char('j') | KeyCode::Char('j') |
KeyCode::Down => { KeyCode::Down => state.select_next_task(),
let column: TaskStatus = TaskStatus::from_int(state.selected_column).unwrap();
let tasks = state.current_project.tasks_per_column.get(&column).unwrap();
if tasks.len() > 0 {
let mins = min(state.selected_task[state.selected_column] + 1, tasks.len() - 1);
state.selected_task[state.selected_column] = mins;
}
}
KeyCode::Char('k') | KeyCode::Char('k') |
KeyCode::Up => state.selected_task[state.selected_column] = state.selected_task[state.selected_column].saturating_sub(1), KeyCode::Up => state.select_previous_task(),
KeyCode::Char('l') | KeyCode::Char('l') |
KeyCode::Right => state.selected_column = min(state.selected_column + 1, 4), KeyCode::Right => state.select_next_column(),
_ => {} _ => {}
} }
} }

View File

@ -1,3 +1,4 @@
use std::cmp::min;
use indexmap::IndexMap; use indexmap::IndexMap;
use int_enum::IntEnum; use int_enum::IntEnum;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -5,13 +6,13 @@ use serde::{Deserialize, Serialize};
#[repr(usize)] #[repr(usize)]
#[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, IntEnum)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, IntEnum)]
pub enum TaskStatus { pub enum TaskStatus {
Done = 0, Todo = 0,
Todo = 1, InProgress = 1,
InProgress = 2, Done = 2,
Testing = 3, Ideas = 3,
Backlog = 4,
} }
// #[derive(Deserialize, Serialize, Debug, Clone, Copy)]
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct Task { pub struct Task {
pub title: String, pub title: String,
@ -27,6 +28,7 @@ impl Default for Task {
} }
} }
/// Type used mainly for serialization at this time
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct Project { pub struct Project {
pub name: String, pub name: String,
@ -41,15 +43,30 @@ impl Project {
[(TaskStatus::Done, vec![]), [(TaskStatus::Done, vec![]),
(TaskStatus::Todo, vec![]), (TaskStatus::Todo, vec![]),
(TaskStatus::InProgress, vec![]), (TaskStatus::InProgress, vec![]),
(TaskStatus::Testing, vec![]), (TaskStatus::Ideas, vec![])],
(TaskStatus::Backlog, vec![])],
), ),
} }
} }
fn add_task(&mut self, status: TaskStatus, task: Task) { pub fn load() -> Self {
let json = std::fs::read_to_string("kanban-tui.json")
.expect("Could not read json file");
serde_json::from_str(&json)
.expect("There is something wrong with the json schema, it doesn't match Project struct")
}
pub fn add_task(&mut self, status: TaskStatus, task: Task) {
self.tasks_per_column.entry(status).or_default().push(task); self.tasks_per_column.entry(status).or_default().push(task);
} }
/// Comment out cause this is dangerous
pub fn save() {
// let mut project = Project::new("Kanban Tui");
// project.add_task(Task::default());
// project.add_task(Task::default());
// let json = serde_json::to_string_pretty(&project).unwrap();
// std::fs::write("./project.json", json).unwrap();
}
} }
impl Default for Project { impl Default for Project {
@ -61,27 +78,10 @@ impl Default for Project {
} }
} }
impl Project {
pub fn load() -> Self {
let json = std::fs::read_to_string("kanban-tui.json")
.expect("Could not read json file");
serde_json::from_str(&json)
.expect("There is something wrong with the json schema, it doesn't match Project struct")
}
/// Comment out cause this is dangerous
pub fn save() {
// let mut project = Project::new("Kanban Tui");
// project.add_task(Task::default());
// project.add_task(Task::default());
// let json = serde_json::to_string_pretty(&project).unwrap();
// std::fs::write("./project.json", json).unwrap();
}
}
pub struct AppState { pub struct AppState {
pub selected_column: usize, pub selected_column: usize,
pub selected_task: [usize; 5], pub selected_task: [usize; 4],
pub current_project: Project, pub project: Project,
pub quit: bool, pub quit: bool,
} }
@ -89,10 +89,35 @@ impl AppState {
pub fn new(project: Project) -> Self { pub fn new(project: Project) -> Self {
AppState { AppState {
selected_column: 0, selected_column: 0,
selected_task: [0, 0, 0, 0, 0], selected_task: [0, 0, 0, 0],
quit: false, quit: false,
current_project: project, project: project,
} }
} }
pub fn get_tasks_in_active_column(&self) -> &Vec<Task> {
let column: TaskStatus = TaskStatus::from_int(self.selected_column).unwrap().clone();
self.project.tasks_per_column.get(&column).unwrap()
}
pub fn select_previous_task(&mut self) {
self.selected_task[self.selected_column] = self.selected_task[self.selected_column].saturating_sub(1)
}
pub fn select_next_task(&mut self) {
let tasks = self.get_tasks_in_active_column();
if tasks.len() > 0 {
let mins = min(self.selected_task[self.selected_column] + 1, tasks.len() - 1);
self.selected_task[self.selected_column] = mins;
}
}
pub fn select_previous_column(&mut self) {
self.selected_column = self.selected_column.saturating_sub(1);
}
pub fn select_next_column(&mut self) {
self.selected_column = min(self.selected_column + 1, self.project.tasks_per_column.len() - 1)
}
} }

View File

@ -11,12 +11,12 @@ fn draw_tasks<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
let columns = Layout::default() let columns = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints( .constraints(
vec![Constraint::Percentage(20); vec![Constraint::Percentage(100 / state.project.tasks_per_column.len() as u16);
state.current_project.tasks_per_column.len()].as_ref() state.project.tasks_per_column.len()].as_ref()
) )
.split(*area); .split(*area);
for (i, (status, tasks)) in state.current_project.tasks_per_column.iter().enumerate() { for (i, (status, tasks)) in state.project.tasks_per_column.iter().enumerate() {
let items: Vec<ListItem> = tasks.iter().enumerate().map(|(j, task)| { let items: Vec<ListItem> = tasks.iter().enumerate().map(|(j, task)| {
let mut style = Style::default(); let mut style = Style::default();
if i == state.selected_column && j == state.selected_task[state.selected_column] { if i == state.selected_column && j == state.selected_task[state.selected_column] {
@ -48,7 +48,7 @@ fn draw_task_info<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
.title("TASK INFO") .title("TASK INFO")
.borders(Borders::ALL); .borders(Borders::ALL);
let column: TaskStatus = TaskStatus::from_int(state.selected_column).unwrap(); let column: TaskStatus = TaskStatus::from_int(state.selected_column).unwrap();
let tasks = state.current_project.tasks_per_column.get(&column).unwrap(); let tasks = state.project.tasks_per_column.get(&column).unwrap();
if tasks.len() > 0 { if tasks.len() > 0 {
let task: &Task = &tasks[state.selected_task[state.selected_column]]; let task: &Task = &tasks[state.selected_task[state.selected_column]];
let p = Paragraph::new(task.description.as_str()).block(block).wrap(Wrap { trim: true }); let p = Paragraph::new(task.description.as_str()).block(block).wrap(Wrap { trim: true });