fin app
This commit is contained in:
parent
68411dc657
commit
a798137f1f
102
src/db.rs
102
src/db.rs
@ -1,24 +1,58 @@
|
||||
use crate::todo::{ChangeType, TaskColumn};
|
||||
use crate::todo;
|
||||
use crate::todo::{ChangeType, TaskColumn, TaskListDB};
|
||||
use rusqlite::{params, Connection, Error, Result};
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn startup(path: &Path) -> Result<(Connection)> {
|
||||
pub(crate) fn startup(path: &Path) -> std::result::Result<Connection, Error> {
|
||||
let conn = Connection::open(path)?;
|
||||
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS tasks (
|
||||
"CREATE TABLE IF NOT EXISTS task_group (
|
||||
id INTEGER PRIMARY KEY,
|
||||
checked INTEGER NOT NULL,
|
||||
value TEXT NOT NULL
|
||||
value TEXT NOT NULL,
|
||||
UNIQUE(value)
|
||||
)",
|
||||
()
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO task_group (value) VALUES (?1)",
|
||||
(&todo::DEFAULT_NAME,)
|
||||
)?;
|
||||
|
||||
if let Err(e) = create_tasklist_tables(&conn) {
|
||||
eprintln!("[ERROR] Failed to create tasklist tables:\n{e}");
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
pub(crate) fn get_tasks(conn: &Connection) -> std::result::Result<Vec<TaskColumn>, Error> {
|
||||
let mut stmt = conn.prepare("SELECT id, checked, value FROM tasks")?;
|
||||
fn create_tasklist_tables(conn: &Connection) -> Result<()> {
|
||||
let tl = get_tasklists(&conn)?;
|
||||
if tl.len() < 1 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for item in 1..tl.len()+1 {
|
||||
let stmt = format!("CREATE TABLE IF NOT EXISTS t{item} (
|
||||
id INTEGER PRIMARY KEY,
|
||||
checked INTEGER NOT NULL,
|
||||
value TEXT NOT NULL
|
||||
)");
|
||||
|
||||
conn.execute(
|
||||
&stmt.to_string(),
|
||||
()
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_tasks(conn: &Connection, tl_id: usize) -> std::result::Result<Vec<TaskColumn>, Error> {
|
||||
let stmt_format = format!("SELECT id, checked, value FROM t{tl_id}");
|
||||
let mut stmt = conn.prepare(stmt_format.as_ref())?;
|
||||
let tasks_iter = stmt.query_map([], |row| {
|
||||
Ok(TaskColumn {
|
||||
id: row.get(0)?,
|
||||
@ -36,22 +70,27 @@ pub(crate) fn get_tasks(conn: &Connection) -> std::result::Result<Vec<TaskColumn
|
||||
}
|
||||
|
||||
pub(crate) fn insert_task(conn: &Connection, task: TaskColumn) -> Result<i64> {
|
||||
let stmt = format!("INSERT INTO t{} (checked, value) VALUES (?1, ?2)", &task.id);
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO tasks (checked, value) VALUES (?1, ?2)",
|
||||
&stmt,
|
||||
(&task.checked, &task.value),
|
||||
)?;
|
||||
|
||||
Ok(conn.last_insert_rowid())
|
||||
}
|
||||
|
||||
pub(crate) fn update_task(conn: &Connection, task: TaskColumn, change: ChangeType) -> Result<()> {
|
||||
pub(crate) fn update_task(conn: &Connection, task: TaskColumn, change: ChangeType, tl_id: usize) -> Result<()> {
|
||||
let val = format!("UPDATE t{tl_id} SET value = ?1 WHERE id = ?2");
|
||||
let check = format!("UPDATE t{tl_id} SET checked = ?1 WHERE id = ?2");
|
||||
|
||||
let (query, params) = match change {
|
||||
ChangeType::Checked => (
|
||||
"UPDATE tasks SET checked = ?1 WHERE id = ?2",
|
||||
check.as_ref(),
|
||||
params![&task.checked, &task.id]
|
||||
),
|
||||
ChangeType::Value => (
|
||||
"UPDATE tasks SET value = ?1 WHERE id = ?2",
|
||||
val.as_ref(),
|
||||
params![&task.value, &task.id]
|
||||
),
|
||||
};
|
||||
@ -60,23 +99,28 @@ pub(crate) fn update_task(conn: &Connection, task: TaskColumn, change: ChangeTyp
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn update_unselect_tasks(conn: &Connection, checked: bool) -> Result<()> {
|
||||
pub(crate) fn update_unselect_tasks(conn: &Connection, checked: bool, tl_id: usize) -> Result<()> {
|
||||
let stmt = format!("UPDATE t{tl_id} SET checked = ?1");
|
||||
|
||||
conn.execute(
|
||||
"UPDATE tasks SET checked = ?1",
|
||||
stmt.as_ref(),
|
||||
(&checked,)
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn delete_tasks(conn: &Connection, id: Option<usize>) -> Result<()> {
|
||||
pub(crate) fn delete_tasks(conn: &Connection, id: Option<usize>, tl_id: usize) -> Result<()> {
|
||||
let del_id = format!("DELETE FROM t{tl_id} WHERE id = ?1").to_string();
|
||||
let full_del = format!("DELETE FROM t{tl_id}").to_string();
|
||||
|
||||
let (query, params) = match id {
|
||||
Some(t) => (
|
||||
"DELETE FROM tasks WHERE id = ?1",
|
||||
del_id.as_ref(),
|
||||
params![t.clone()]
|
||||
),
|
||||
None => (
|
||||
"DELETE FROM tasks",
|
||||
full_del.as_ref(),
|
||||
params![]
|
||||
),
|
||||
};
|
||||
@ -84,3 +128,29 @@ pub(crate) fn delete_tasks(conn: &Connection, id: Option<usize>) -> Result<()> {
|
||||
conn.execute(query, params)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn insert_tasklist(conn: &Connection, task_name: &str) -> Result<i64> {
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO task_group (value) VALUES (?1)",
|
||||
(&task_name,)
|
||||
)?;
|
||||
|
||||
Ok(conn.last_insert_rowid())
|
||||
}
|
||||
|
||||
pub(super) fn get_tasklists(conn: &Connection) -> std::result::Result<Vec<TaskListDB>, Error> {
|
||||
let mut stmt = conn.prepare("SELECT id, value FROM task_group")?;
|
||||
let tasks_iter = stmt.query_map([], |row| {
|
||||
Ok(TaskListDB {
|
||||
id: row.get(0)?,
|
||||
value: row.get(1)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut tasks = Vec::new();
|
||||
for task in tasks_iter {
|
||||
tasks.push(task?);
|
||||
}
|
||||
|
||||
Ok(tasks)
|
||||
}
|
129
src/todo.rs
129
src/todo.rs
@ -1,3 +1,4 @@
|
||||
use std::default::Default;
|
||||
use crate::db;
|
||||
use iced::keyboard::key;
|
||||
use iced::widget::text::danger;
|
||||
@ -29,13 +30,13 @@ pub(crate) struct Todo {
|
||||
updated_task: String,
|
||||
tasks: Vec<TaskData>,
|
||||
completed_tasks: usize,
|
||||
local_storage: bool,
|
||||
conn: Connection,
|
||||
select_all: bool,
|
||||
task_list_input: String,
|
||||
task_list_state: combo_box::State<String>,
|
||||
task_list: Vec<String>,
|
||||
current_task_group: Option<String>,
|
||||
task_list: Vec<TaskListDB>,
|
||||
current_task_list: Option<String>,
|
||||
curr_tl_id: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -48,7 +49,6 @@ pub enum Message {
|
||||
ContentUpdated(ContentType, String),
|
||||
Event(Event),
|
||||
TaskPush(Option<usize>),
|
||||
StorageToggle(bool),
|
||||
ToggleUnselect(bool),
|
||||
SelectedTaskList(String),
|
||||
AddTaskList,
|
||||
@ -66,12 +66,19 @@ enum ContentType {
|
||||
NewList,
|
||||
}
|
||||
|
||||
pub(crate) struct TaskListDB {
|
||||
pub(crate) id: usize,
|
||||
pub(crate) value: String,
|
||||
}
|
||||
|
||||
impl Default for Todo {
|
||||
fn default() -> Self {
|
||||
Todo::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT_NAME: &str = "general";
|
||||
|
||||
impl Todo {
|
||||
fn new() -> Self {
|
||||
let mut init = Self {
|
||||
@ -79,31 +86,35 @@ impl Todo {
|
||||
updated_task: String::new(),
|
||||
tasks: Vec::new(),
|
||||
completed_tasks: 0,
|
||||
local_storage: true, // todo eventually get this info from db - if no db set it to true
|
||||
conn: db::startup("./todolist-db.db3".as_ref())
|
||||
.expect("[ERROR] Failed to access the local storage"),
|
||||
conn: db::startup("./todolist-db.db3".as_ref()).expect("[ERROR] Failed to access the local storage"),
|
||||
select_all: false,
|
||||
task_list_input: String::from(""),
|
||||
task_list_input: String::new(),
|
||||
task_list_state: Default::default(),
|
||||
task_list: vec![String::from("general")],
|
||||
current_task_group: Some(String::from("")),
|
||||
task_list: Vec::new(),
|
||||
current_task_list: None,
|
||||
curr_tl_id: 1,
|
||||
};
|
||||
|
||||
Self::set_task_list(&mut init);
|
||||
Self::load_data_from_db(&mut init);
|
||||
init.task_list_state = combo_box::State::new(init.task_list.clone());
|
||||
init.current_task_group = Some(init.task_list[0].clone());
|
||||
|
||||
let mut combo_state = Vec::new();
|
||||
|
||||
for item in &init.task_list {
|
||||
combo_state.push(item.value.clone());
|
||||
}
|
||||
|
||||
init.task_list_state = combo_box::State::new(combo_state);
|
||||
if !init.task_list.is_empty() {
|
||||
init.current_task_list = Some(init.task_list[0].value.clone());
|
||||
init.curr_tl_id = init.task_list[0].id;
|
||||
}
|
||||
|
||||
init
|
||||
}
|
||||
pub(crate) fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match message {
|
||||
Message::ContentUpdated(new, value) => {
|
||||
/*if new {
|
||||
self.new_task = String::from(&value);
|
||||
} else {
|
||||
self.updated_task = String::from(&value)
|
||||
}*/
|
||||
|
||||
match new {
|
||||
ContentType::NewTask => self.new_task = String::from(&value),
|
||||
ContentType::ExistingTask => self.updated_task = String::from(&value),
|
||||
@ -130,7 +141,7 @@ impl Todo {
|
||||
let result = db::insert_task(
|
||||
&self.conn,
|
||||
TaskColumn {
|
||||
id: 0,
|
||||
id: self.curr_tl_id,
|
||||
checked: false,
|
||||
value: self.new_task.to_string(),
|
||||
},
|
||||
@ -161,7 +172,7 @@ impl Todo {
|
||||
self.completed_tasks -= 1;
|
||||
}
|
||||
|
||||
if let Err(e) = db::delete_tasks(&self.conn, Some(self.tasks[index].db_id)) {
|
||||
if let Err(e) = db::delete_tasks(&self.conn, Some(self.tasks[index].db_id), self.curr_tl_id) {
|
||||
eprintln!(
|
||||
"[ERROR] Failed to delete task '{}':\n{e}",
|
||||
self.tasks[index].value
|
||||
@ -188,6 +199,7 @@ impl Todo {
|
||||
value: self.tasks[id].value.to_string(),
|
||||
},
|
||||
ChangeType::Checked,
|
||||
self.curr_tl_id,
|
||||
);
|
||||
|
||||
if let Err(e) = result {
|
||||
@ -216,6 +228,7 @@ impl Todo {
|
||||
value: self.tasks[index].value.to_string(),
|
||||
},
|
||||
ChangeType::Value,
|
||||
self.curr_tl_id,
|
||||
);
|
||||
|
||||
if let Err(e) = result {
|
||||
@ -243,7 +256,7 @@ impl Todo {
|
||||
widget::focus_previous()
|
||||
}
|
||||
Message::DeleteAll => {
|
||||
if let Err(e) = db::delete_tasks(&self.conn, None) {
|
||||
if let Err(e) = db::delete_tasks(&self.conn, None, self.curr_tl_id) {
|
||||
eprintln!("[ERROR] Failed to delete all tasks:\n{e}")
|
||||
}
|
||||
|
||||
@ -283,20 +296,6 @@ impl Todo {
|
||||
Some(i) => Task::perform(async move { Message::EditTask(i) }, |result| result),
|
||||
None => Task::perform(async { Message::AddTask }, |result| result),
|
||||
},
|
||||
Message::StorageToggle(_toggle) => {
|
||||
// todo must be enabled eventually
|
||||
//self.local_storage = toggle;
|
||||
|
||||
/*
|
||||
todo
|
||||
here we only call for storage change not implement the whole system
|
||||
as the system should be running since the program startup
|
||||
*/
|
||||
|
||||
// also how do we even implement that
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::ToggleUnselect(toggle) => {
|
||||
self.select_all = toggle;
|
||||
for task in &mut self.tasks {
|
||||
@ -309,21 +308,50 @@ impl Todo {
|
||||
self.completed_tasks = 0;
|
||||
}
|
||||
|
||||
if let Err(e) = db::update_unselect_tasks(&self.conn, toggle) {
|
||||
if let Err(e) = db::update_unselect_tasks(&self.conn, toggle, self.curr_tl_id) {
|
||||
eprintln!("[ERROR] Failed to update un/select all operation in database:\n{e}");
|
||||
}
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::SelectedTaskList(task) => {
|
||||
self.current_task_group = Some(task);
|
||||
for item in &self.task_list {
|
||||
if item.value == task {
|
||||
self.curr_tl_id = item.id;
|
||||
}
|
||||
}
|
||||
self.current_task_list = Some(task);
|
||||
|
||||
// reload tasks
|
||||
self.tasks.clear();
|
||||
self.completed_tasks = 0;
|
||||
let _ = self.load_data_from_db();
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::AddTaskList => {
|
||||
if self.task_list_input.is_empty() { return Task::none(); }
|
||||
self.task_list.push(self.task_list_input.clone());
|
||||
self.task_list_state = combo_box::State::new(self.task_list.clone());
|
||||
let mut combo_state_vec = Vec::new();
|
||||
|
||||
match db::insert_tasklist(&self.conn, &self.task_list_input) {
|
||||
Ok(t) => {
|
||||
self.task_list.push(TaskListDB {
|
||||
id: t as usize,
|
||||
value: self.task_list_input.clone(),
|
||||
});
|
||||
self.current_task_list = Some(self.task_list_input.clone());
|
||||
self.task_list_input = String::from("");
|
||||
|
||||
for item in &self.task_list {
|
||||
combo_state_vec.push(item.value.clone());
|
||||
}
|
||||
self.task_list_state = combo_box::State::new(combo_state_vec);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ERROR] tasklist insertion failed:\n{e}");
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
@ -388,8 +416,8 @@ impl Todo {
|
||||
let status = Text::new(format!("{} / {}", self.completed_tasks, self.tasks.len()));
|
||||
let tasklist = combo_box(
|
||||
&self.task_list_state,
|
||||
"Test...",
|
||||
self.current_task_group.as_ref(),
|
||||
"Enter desired task list ...",
|
||||
self.current_task_list.as_ref(),
|
||||
Message::SelectedTaskList,
|
||||
)
|
||||
.on_input(|str| Message::ContentUpdated(ContentType::NewList, str));
|
||||
@ -397,13 +425,10 @@ impl Todo {
|
||||
let tasklist_group = row![tasklist.width(Length::Fill), add_tasklist].spacing(5);
|
||||
let unselect =
|
||||
checkbox("Select all", self.select_all).on_toggle(Message::ToggleUnselect);
|
||||
let storage =
|
||||
checkbox("Local storage", self.local_storage).on_toggle(Message::StorageToggle);
|
||||
let footer = row![
|
||||
status,
|
||||
tasklist_group,
|
||||
unselect,
|
||||
storage
|
||||
]
|
||||
.padding(10)
|
||||
.spacing(10);
|
||||
@ -437,7 +462,7 @@ impl Todo {
|
||||
}
|
||||
|
||||
fn load_data_from_db(&mut self) {
|
||||
if let Ok(t) = db::get_tasks(&self.conn) {
|
||||
if let Ok(t) = db::get_tasks(&self.conn, self.curr_tl_id) {
|
||||
for item in t {
|
||||
let data = TaskData {
|
||||
db_id: item.id,
|
||||
@ -474,4 +499,18 @@ impl Todo {
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn set_task_list(&mut self) {
|
||||
match db::get_tasklists(&self.conn) {
|
||||
Ok(tl_db) => {
|
||||
for item in tl_db {
|
||||
self.task_list.push(TaskListDB {
|
||||
id: item.id,
|
||||
value: item.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("[ERROR] Failed to get tasklists from DB:\n{e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user