support events

This commit is contained in:
Julian Freeman
2026-03-22 17:35:44 -04:00
parent 4016ed0d53
commit 6aff740207
6 changed files with 494 additions and 296 deletions

135
src-tauri/src/db.rs Normal file
View File

@@ -0,0 +1,135 @@
use rusqlite::{params, Connection};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Clone)]
pub struct Tag {
pub id: i64,
pub name: String,
pub parent_id: Option<i64>,
pub color: String,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct Event {
pub id: i64,
pub date: String,
pub start_minute: i32,
pub end_minute: i32,
pub main_tag_id: i64,
pub sub_tag_id: Option<i64>,
pub content: String,
}
pub fn init_db(path: &str) -> anyhow::Result<()> {
let conn = Connection::open(path)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
parent_id INTEGER,
color TEXT NOT NULL,
FOREIGN KEY(parent_id) REFERENCES tags(id)
)",
[],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
start_minute INTEGER NOT NULL,
end_minute INTEGER NOT NULL,
main_tag_id INTEGER NOT NULL,
sub_tag_id INTEGER,
content TEXT,
FOREIGN KEY(main_tag_id) REFERENCES tags(id),
FOREIGN KEY(sub_tag_id) REFERENCES tags(id)
)",
[],
)?;
Ok(())
}
pub fn get_tags(path: &str) -> anyhow::Result<Vec<Tag>> {
let conn = Connection::open(path)?;
let mut stmt = conn.prepare("SELECT id, name, parent_id, color FROM tags")?;
let tag_iter = stmt.query_map([], |row| {
Ok(Tag {
id: row.get(0)?,
name: row.get(1)?,
parent_id: row.get(2)?,
color: row.get(3)?,
})
})?;
let mut results = Vec::new();
for tag in tag_iter {
results.push(tag?);
}
Ok(results)
}
pub fn add_tag(path: &str, name: &str, parent_id: Option<i64>, color: &str) -> anyhow::Result<i64> {
let conn = Connection::open(path)?;
conn.execute(
"INSERT INTO tags (name, parent_id, color) VALUES (?1, ?2, ?3)",
params![name, parent_id, color],
)?;
Ok(conn.last_insert_rowid())
}
pub fn delete_tag(path: &str, id: i64) -> anyhow::Result<()> {
let conn = Connection::open(path)?;
// Also delete child tags
conn.execute("DELETE FROM tags WHERE parent_id = ?1", params![id])?;
conn.execute("DELETE FROM tags WHERE id = ?1", params![id])?;
Ok(())
}
pub fn get_events(path: &str, date: &str) -> anyhow::Result<Vec<Event>> {
let conn = Connection::open(path)?;
let mut stmt = conn.prepare("SELECT id, date, start_minute, end_minute, main_tag_id, sub_tag_id, content FROM events WHERE date = ?1")?;
let event_iter = stmt.query_map(params![date], |row| {
Ok(Event {
id: row.get(0)?,
date: row.get(1)?,
start_minute: row.get(2)?,
end_minute: row.get(3)?,
main_tag_id: row.get(4)?,
sub_tag_id: row.get(5)?,
content: row.get(6)?,
})
})?;
let mut results = Vec::new();
for event in event_iter {
results.push(event?);
}
Ok(results)
}
pub fn save_event(path: &str, event: Event) -> anyhow::Result<i64> {
let conn = Connection::open(path)?;
if event.id > 0 {
conn.execute(
"UPDATE events SET date=?1, start_minute=?2, end_minute=?3, main_tag_id=?4, sub_tag_id=?5, content=?6 WHERE id=?7",
params![event.date, event.start_minute, event.end_minute, event.main_tag_id, event.sub_tag_id, event.content, event.id],
)?;
Ok(event.id)
} else {
conn.execute(
"INSERT INTO events (date, start_minute, end_minute, main_tag_id, sub_tag_id, content) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
params![event.date, event.start_minute, event.end_minute, event.main_tag_id, event.sub_tag_id, event.content],
)?;
Ok(conn.last_insert_rowid())
}
}
pub fn delete_event(path: &str, id: i64) -> anyhow::Result<()> {
let conn = Connection::open(path)?;
conn.execute("DELETE FROM events WHERE id = ?1", params![id])?;
Ok(())
}

View File

@@ -11,9 +11,60 @@ use tauri_plugin_store::StoreExt;
pub struct AppState {
pub is_paused: Arc<AtomicBool>,
pub capture_interval_secs: std::sync::atomic::AtomicU64,
pub db_path: std::sync::Mutex<Option<String>>,
pub toggle_menu_item: std::sync::Mutex<Option<tauri::menu::MenuItem<tauri::Wry>>>,
}
#[tauri::command]
pub fn update_db_path(state: tauri::State<'_, AppState>, path: String) -> Result<(), String> {
crate::db::init_db(&path).map_err(|e| e.to_string())?;
let mut db_path = state.db_path.lock().unwrap();
*db_path = Some(path);
Ok(())
}
#[tauri::command]
pub fn get_tags(state: tauri::State<'_, AppState>) -> Result<Vec<crate::db::Tag>, String> {
let path = state.db_path.lock().unwrap();
let path = path.as_ref().ok_or("Database path not set")?;
crate::db::get_tags(path).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn add_tag(state: tauri::State<'_, AppState>, name: String, parent_id: Option<i64>, color: String) -> Result<i64, String> {
let path = state.db_path.lock().unwrap();
let path = path.as_ref().ok_or("Database path not set")?;
crate::db::add_tag(path, &name, parent_id, &color).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn delete_tag(state: tauri::State<'_, AppState>, id: i64) -> Result<(), String> {
let path = state.db_path.lock().unwrap();
let path = path.as_ref().ok_or("Database path not set")?;
crate::db::delete_tag(path, id).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn get_events(state: tauri::State<'_, AppState>, date: String) -> Result<Vec<crate::db::Event>, String> {
let path = state.db_path.lock().unwrap();
let path = path.as_ref().ok_or("Database path not set")?;
crate::db::get_events(path, &date).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn save_event(state: tauri::State<'_, AppState>, event: crate::db::Event) -> Result<i64, String> {
let path = state.db_path.lock().unwrap();
let path = path.as_ref().ok_or("Database path not set")?;
crate::db::save_event(path, event).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn delete_event(state: tauri::State<'_, AppState>, id: i64) -> Result<(), String> {
let path = state.db_path.lock().unwrap();
let path = path.as_ref().ok_or("Database path not set")?;
crate::db::delete_event(path, id).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn update_interval(state: tauri::State<'_, AppState>, seconds: u64) {
state.capture_interval_secs.store(seconds, Ordering::SeqCst);

View File

@@ -1,3 +1,4 @@
mod db;
mod engine;
mod tray;
@@ -21,12 +22,20 @@ pub fn run() {
engine::get_pause_state,
engine::get_timeline,
engine::get_image_base64,
engine::update_interval
engine::update_interval,
engine::update_db_path,
engine::get_tags,
engine::add_tag,
engine::delete_tag,
engine::get_events,
engine::save_event,
engine::delete_event
])
.setup(|app| {
app.manage(engine::AppState {
is_paused: Arc::new(AtomicBool::new(false)),
capture_interval_secs: std::sync::atomic::AtomicU64::new(30),
db_path: std::sync::Mutex::new(None),
toggle_menu_item: std::sync::Mutex::new(None),
});
tray::create_tray(app.handle())?;