delete function and menu, open existing note

This commit is contained in:
catto 2024-04-13 20:51:49 +02:00
parent b4f58c84d6
commit a5e6806aca
6 changed files with 186 additions and 61 deletions

View File

@ -20,8 +20,11 @@
<body>
<div class="top-bar">
<div class="menu" id="contextMenu">
<button id="deleteButton">Delete</button>
</div>
<h1>Snotes Deck</h1>
<div id="button-row">
<button class="row" id="show-notes-button">Refresh Notes</button>
<button type="submit" id="save-button">Save</button>
@ -39,12 +42,15 @@
</div>
<div class="editor">
<input id="create-tag" placeholder="Tag..." />
<textarea id="create-input" placeholder="Note..." ></textarea>
<p id="create-msg"></p>
<textarea id="create-input" placeholder="Note..."></textarea>
<p id="create-msg"></p>
<p style="white-space: pre-line" id="notes-list"></p>
</div>
<p style="white-space: pre-line" id="notes-list"></p>
</div>
</body>

View File

@ -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<String, String> {
}
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<String, String> {
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 {

View File

@ -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");
}

View File

@ -8,7 +8,7 @@
},
"package": {
"productName": "snotes-deck",
"version": "0.0.1"
"version": "0.0.2"
},
"tauri": {
"allowlist": {
@ -41,4 +41,4 @@
]
}
}
}
}

View File

@ -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<any> = 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<Array<JSON>> {
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<HTMLElement> = 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';
}
});
}
}

View File

@ -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;
}