From a5e6806aca74f68640ef72eb1485123ba13dca67 Mon Sep 17 00:00:00 2001 From: catto Date: Sat, 13 Apr 2024 20:51:49 +0200 Subject: [PATCH] delete function and menu, open existing note --- index.html | 18 +++-- libsnotes/src/lib.rs | 43 +++++++----- src-tauri/src/main.rs | 13 +++- src-tauri/tauri.conf.json | 4 +- src/main.ts | 134 +++++++++++++++++++++++++++++--------- src/styles.css | 35 ++++++++-- 6 files changed, 186 insertions(+), 61 deletions(-) diff --git a/index.html b/index.html index f7a0b2d..91d3d7c 100644 --- a/index.html +++ b/index.html @@ -20,8 +20,11 @@
+

Snotes Deck

- +
@@ -39,12 +42,15 @@
- -

+ +

+ +

+ +
+ + -

- -
diff --git a/libsnotes/src/lib.rs b/libsnotes/src/lib.rs index 924f42c..3115288 100644 --- a/libsnotes/src/lib.rs +++ b/libsnotes/src/lib.rs @@ -10,7 +10,6 @@ pub struct Note { tag: String, } - pub fn init_db() -> Result<()> { let home = home_dir().unwrap().join(".snotes.db"); let connection = Connection::open(home)?; @@ -64,19 +63,21 @@ pub fn show_notes(all: bool, tag: &str) -> Result { } if !tag.is_empty() { - query = format!("SELECT * FROM notes WHERE tag IS '{}'", tag); + query = format!("SELECT * FROM notes WHERE tag IS '{tag}'"); } let mut prepare = connection.prepare(&query).unwrap(); - let notes = prepare.query_map([], |row| { - Ok(Note { - id: row.get(0)?, - content: row.get(1)?, - date: row.get(2)?, - tag: row.get(3)?, + let notes = prepare + .query_map([], |row| { + Ok(Note { + id: row.get(0)?, + content: row.get(1)?, + date: row.get(2)?, + tag: row.get(3)?, + }) }) - }).unwrap(); + .unwrap(); let mut json_array = Vec::new(); @@ -96,20 +97,32 @@ pub fn show_notes(all: bool, tag: &str) -> Result { Ok(json_string) } -pub fn delete_latest_note() -> Result<()> { +pub fn delete_latest_note() -> Result<(), String> { let home = home_dir().unwrap().join(".snotes.db"); - let connection = Connection::open(home)?; + let connection = Connection::open(home).map_err(|e| format!("Database Error: {e}"))?; let query = String::from("DELETE FROM NOTES WHERE nid = (SELECT MAX(nid) FROM notes)"); match connection.execute(&query, []) { - Ok(v) => println!("DELETE OK {}", v), - Err(e) => println!("DELETE ERR {}", e), + Ok(v) => { + println!("DELETE OK {}", v); + Ok(()) + } + Err(e) => Err(format!("Delete Error: {e}")), } - - Ok(()) } +pub fn delete_specific_note(id: i32) -> Result<(), String> { + let home = home_dir().unwrap().join(".snotes.db"); + let connection = Connection::open(home).map_err(|e| format!("Database Error: {e}"))?; + + let query = "DELETE FROM notes WHERE nid = ?1"; + match connection.execute(query, [id]) { + Ok(1) => Ok(()), // 1 row affected means the note was deleted successfully + Ok(_) => Err("No note with the provided ID found.".to_string()), + Err(e) => Err(format!("Delete Error: {e}")), + } +} #[cfg(test)] mod tests { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 17ea91a..71eb928 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -11,8 +11,8 @@ fn greet(name: &str) -> String { #[tauri::command] fn get_notes_list() -> String { - let notes = show_notes(false, &String::new()).unwrap(); - format!("{}", notes) + let notes = show_notes(false, "").unwrap(); + notes.to_string() } #[tauri::command] @@ -23,9 +23,16 @@ fn create_note(content: &str, tag: &str) -> bool { true } +#[tauri::command] +fn delete_specific_note(id: u32) -> bool { + println!("reched Delete"); + + libsnotes::delete_specific_note(id.try_into().unwrap()).is_ok() +} + fn main() { tauri::Builder::default() - .invoke_handler(tauri::generate_handler![greet, get_notes_list, create_note]) + .invoke_handler(tauri::generate_handler![greet, get_notes_list, create_note, delete_specific_note]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 06bfe44..d19e06c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "snotes-deck", - "version": "0.0.1" + "version": "0.0.2" }, "tauri": { "allowlist": { @@ -41,4 +41,4 @@ ] } } -} +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index e7928e4..e76bd58 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,13 +3,14 @@ import { Note } from "./model"; let notesMsgEl: HTMLElement | null; -//let createMsgEl: HTMLElement | null; let createNoteContentEl: HTMLTextAreaElement | null; let createNoteTagEl: HTMLInputElement | null; let noteSidebarContainerEl: HTMLDivElement | null; +let noteArray: Note[] = [] + // create async function createNote() { console.log("reached ssssjs") @@ -25,13 +26,9 @@ async function createNote() { // read async function showNotes() { if (notesMsgEl) { - //const notesJson: string = await invoke("get_notes_list"); - //const formattedJson = JSON.stringify(JSON.parse(notesJson), null, 2); // Indentation of 2 spaces - //notesMsgEl.textContent = formattedJson; - const array: Array = await retrieveNotes(); - const noteArray: Note[] = array.map((jsonObj) => ({ + noteArray = array.map((jsonObj) => ({ id: jsonObj.id, content: jsonObj.content, date: jsonObj.date, @@ -51,31 +48,6 @@ async function retrieveNotes(): Promise> { return notesJson; } - -// TODO: read better array of note elements with id iterable the whole thing - -// update -// async function updateNote() { -// if (true) { -// await invoke("update_note", { -// id: null, -// content: null, -// tag: null -// }); -// } -// } - -// delete -// async function deleteNote() { -// if (true) { -// await invoke("delete_note", { -// id: null -// } -// ) -// } -// } - - window.addEventListener("DOMContentLoaded", () => { createNoteContentEl = document.querySelector("#create-input"); createNoteTagEl = document.querySelector("#create-tag"); @@ -91,8 +63,57 @@ window.addEventListener("DOMContentLoaded", () => { e.preventDefault(); showNotes(); }) + + refreshContextMenuElements(); }); +/** + * We need to add new event listeners every time we refresh the note list + */ +function refreshContextMenuElements() { + const elements: NodeListOf = document.querySelectorAll(".rightclick-element") + const contextMenu = document.getElementById('contextMenu'); + + if (contextMenu) { + elements.forEach(element => { + + element.addEventListener("contextmenu", (e: MouseEvent) => { + e.preventDefault(); + + // get the position with mouse and everything + const mouseX = e.clientX; + const mouseY = e.clientY; + + // Calculate the position of the context menu relative to the mouse cursor + const menuWidth = contextMenu.offsetWidth; + const menuHeight = contextMenu.offsetHeight; + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + + let posX = mouseX + menuWidth > viewportWidth ? mouseX - menuWidth : mouseX; + let posY = mouseY + menuHeight > viewportHeight ? mouseY - menuHeight : mouseY; + + + contextMenu.style.display = 'block'; // Show the custom context menu + contextMenu.style.left = `${posX}px`; + contextMenu.style.top = `${posY}px`; + + const noteIdElement = element.querySelector('.sidebar-note-id'); + if (noteIdElement) { + const noteIdStr = noteIdElement.textContent; + //console.log('Right-clicked note id:', noteId); + if (noteIdStr) { + const noteId: Number = parseInt(noteIdStr); + showNoteSidebarContextMenu(noteId); + } + } else { + console.error('.sidebar-note-id element not found within the note.'); + } + }); + + }) + } +} function fillNoteSidebar(noteArray: Note[]) { @@ -106,6 +127,7 @@ function fillNoteSidebar(noteArray: Note[]) { // Create HTML elements for each note const noteEl: HTMLDivElement = document.createElement('div'); noteEl.classList.add('sidebar-note'); + noteEl.classList.add('rightclick-element'); noteEl.addEventListener("click", () => handleSidebarNoteClick(note.id), false); const idSpan: HTMLSpanElement = document.createElement('span'); @@ -131,11 +153,61 @@ function fillNoteSidebar(noteArray: Note[]) { // Append noteEl to the container, if it still exists? noteSidebarContainerEl ? noteSidebarContainerEl.appendChild(noteEl) : null; }); + + refreshContextMenuElements(); } } function handleSidebarNoteClick(id: Number): any { console.log("huh " + id); + if (createNoteContentEl && createNoteTagEl) { + // search for note + let n: Note = { + id: 0, + content: "undefined", + date: "undefined", + tag: "undefined" + }; + + noteArray.forEach(note => { + if (note.id === id) { + n = note; + } + }); + + if (n) { + createNoteContentEl.value = n.content as string; + createNoteTagEl.value = n.tag as string; + } else { + // don't destory currently editing note if this fails + console.error("Error fetching note"); + } + + } +} + +function showNoteSidebarContextMenu(noteId: Number) { + const contextMenu = document.getElementById('contextMenu'); + const deleteButton = document.getElementById('deleteButton'); + + if (contextMenu && deleteButton) { + deleteButton.addEventListener('click', async function () { + console.log('Deleting...'); + await invoke("delete_specific_note", { + id: noteId + }); + // hide after delete + contextMenu.style.display = 'none'; + showNotes(); + }); + + // hide when clicking outside of it + document.addEventListener('click', function (event) { + if (!contextMenu.contains(event.target as Node)) { + contextMenu.style.display = 'none'; + } + }); + } } diff --git a/src/styles.css b/src/styles.css index adcbcf6..73a44b5 100644 --- a/src/styles.css +++ b/src/styles.css @@ -12,7 +12,7 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; - + } #button-row { @@ -90,11 +90,11 @@ button:active { background-color: #e8e8e8; } -#create-tag{ +#create-tag { margin: 0.5em; } -#create-input{ +#create-input { margin: 0.5em; height: 100%; @@ -114,7 +114,8 @@ button:active { #create-input:focus { border-color: #cf66e9; box-shadow: 0 0 5px rgba(102, 175, 233, 0.6); - outline: none; /* Remove default focus outline */ + outline: none; + /* Remove default focus outline */ } input, @@ -165,4 +166,30 @@ button { margin-left: 10px; padding: 2px 6px; border-radius: 4px; +} + +/* CONTEXT MENU */ + +.context-menu { + position: relative; +} + +.menu { + display: none; + position: absolute; + z-index: 999; + border: 1px solid #ccc; + padding: 5px; +} + +.menu button { + display: block; + background-color: transparent; + border: none; + cursor: pointer; + padding: 5px; +} + +.menu button:hover { + background-color: #770079; } \ No newline at end of file