Create or open file. Save on add/remove/edit task
This commit is contained in:
parent
e7564f8f82
commit
6a323d06c1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
/.idea/
|
/.idea/
|
||||||
/kanban-tui.json
|
/kanban-tui.json
|
||||||
|
/kanban.json
|
||||||
|
44
src/app.rs
44
src/app.rs
@ -2,6 +2,8 @@
|
|||||||
// use int_enum::IntEnum;
|
// use int_enum::IntEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Read;
|
||||||
use tui_textarea::TextArea;
|
use tui_textarea::TextArea;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -34,6 +36,7 @@ impl Default for Task {
|
|||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub filepath: String,
|
||||||
pub selected_column_idx: usize,
|
pub selected_column_idx: usize,
|
||||||
pub columns: Vec<Column>
|
pub columns: Vec<Column>
|
||||||
}
|
}
|
||||||
@ -46,16 +49,6 @@ pub enum KanbanError {
|
|||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Project {
|
|
||||||
fn default() -> Self {
|
|
||||||
Project {
|
|
||||||
name: String::new(),
|
|
||||||
columns: vec![],
|
|
||||||
selected_column_idx: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TaskEditFocus {
|
pub enum TaskEditFocus {
|
||||||
Title,
|
Title,
|
||||||
@ -135,14 +128,14 @@ impl<'a> Column {
|
|||||||
|
|
||||||
pub fn select_next_task(&mut self) {
|
pub fn select_next_task(&mut self) {
|
||||||
let task_idx = &mut self.selected_task_idx;
|
let task_idx = &mut self.selected_task_idx;
|
||||||
*task_idx = min(*task_idx + 1, self.tasks.len() - 1)
|
*task_idx = min(*task_idx + 1, self.tasks.len().saturating_sub(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_last_task(&mut self) {
|
pub fn select_last_task(&mut self) {
|
||||||
self.selected_task_idx = self.tasks.len() - 1;
|
self.selected_task_idx = self.tasks.len() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_task_state_from_curr_selected_task(&self) -> Option<TaskState<'a>> {
|
pub fn get_task_state_from_curr_selected_task(&self) -> Option<TaskState<'a>> {
|
||||||
self.get_selected_task().map(|t| {
|
self.get_selected_task().map(|t| {
|
||||||
TaskState {
|
TaskState {
|
||||||
title: TextArea::from(t.title.lines()),
|
title: TextArea::from(t.title.lines()),
|
||||||
@ -155,10 +148,16 @@ impl<'a> Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
pub fn new(name: &str) -> Self {
|
pub fn new(name: &str, filepath: String) -> Self {
|
||||||
Project {
|
Project {
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
columns: vec![],
|
filepath,
|
||||||
|
columns: vec![
|
||||||
|
Column::new("Todo"),
|
||||||
|
Column::new("InProgress"),
|
||||||
|
Column::new("Done"),
|
||||||
|
Column::new("Ideas"),
|
||||||
|
],
|
||||||
selected_column_idx: 0,
|
selected_column_idx: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,13 +167,24 @@ impl Project {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(filepath: String) -> Result<Self, KanbanError> {
|
pub fn load(filepath: String) -> Result<Self, KanbanError> {
|
||||||
let json = std::fs::read_to_string(filepath)?;
|
let mut file = OpenOptions::new()
|
||||||
Self::load_from_json(&json)
|
.write(true)
|
||||||
|
.read(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&filepath)?;
|
||||||
|
|
||||||
|
let mut json = String::new();
|
||||||
|
file.read_to_string(&mut json)?;
|
||||||
|
if json.is_empty() {
|
||||||
|
Ok(Project::new("", filepath))
|
||||||
|
} else {
|
||||||
|
Self::load_from_json(&json)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self) {
|
pub fn save(&self) {
|
||||||
let json = serde_json::to_string_pretty(&self).unwrap();
|
let json = serde_json::to_string_pretty(&self).unwrap();
|
||||||
std::fs::write("kanban-tui.json", json).unwrap();
|
std::fs::write(&self.filepath, json).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_selected_column(&self) -> &Column {
|
pub fn get_selected_column(&self) -> &Column {
|
||||||
|
@ -44,7 +44,8 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
|
|||||||
} else {
|
} else {
|
||||||
column.add_task(title, description);
|
column.add_task(title, description);
|
||||||
}
|
}
|
||||||
state.task_edit_state = None
|
state.task_edit_state = None;
|
||||||
|
project.save();
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -83,7 +84,10 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
|
|||||||
KeyCode::Char('n') => state.task_edit_state = Some(TaskState::default()),
|
KeyCode::Char('n') => state.task_edit_state = Some(TaskState::default()),
|
||||||
KeyCode::Char('e') =>
|
KeyCode::Char('e') =>
|
||||||
state.task_edit_state = column.get_task_state_from_curr_selected_task(),
|
state.task_edit_state = column.get_task_state_from_curr_selected_task(),
|
||||||
KeyCode::Char('D') => column.remove_task(),
|
KeyCode::Char('D') => {
|
||||||
|
column.remove_task();
|
||||||
|
project.save();
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use tui::backend::CrosstermBackend;
|
|||||||
use tui::Terminal;
|
use tui::Terminal;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let pattern = env::args().nth(1).expect("Path to task database not provided");
|
let pattern = env::args().nth(1).unwrap_or("kanban.json".to_string());
|
||||||
let mut state = AppState::new(Project::load(pattern)?);
|
let mut state = AppState::new(Project::load(pattern)?);
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user