delete function and menu, open existing note
This commit is contained in:
parent
b4f58c84d6
commit
a5e6806aca
14
index.html
14
index.html
|
@ -20,6 +20,9 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
|
<div class="menu" id="contextMenu">
|
||||||
|
<button id="deleteButton">Delete</button>
|
||||||
|
</div>
|
||||||
<h1>Snotes Deck</h1>
|
<h1>Snotes Deck</h1>
|
||||||
|
|
||||||
<div id="button-row">
|
<div id="button-row">
|
||||||
|
@ -39,12 +42,15 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="editor">
|
<div class="editor">
|
||||||
<input id="create-tag" placeholder="Tag..." />
|
<input id="create-tag" placeholder="Tag..." />
|
||||||
<textarea id="create-input" placeholder="Note..." ></textarea>
|
<textarea id="create-input" placeholder="Note..."></textarea>
|
||||||
<p id="create-msg"></p>
|
<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>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ pub struct Note {
|
||||||
tag: String,
|
tag: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn init_db() -> Result<()> {
|
pub fn init_db() -> Result<()> {
|
||||||
let home = home_dir().unwrap().join(".snotes.db");
|
let home = home_dir().unwrap().join(".snotes.db");
|
||||||
let connection = Connection::open(home)?;
|
let connection = Connection::open(home)?;
|
||||||
|
@ -64,19 +63,21 @@ pub fn show_notes(all: bool, tag: &str) -> Result<String, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tag.is_empty() {
|
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 mut prepare = connection.prepare(&query).unwrap();
|
||||||
|
|
||||||
let notes = prepare.query_map([], |row| {
|
let notes = prepare
|
||||||
Ok(Note {
|
.query_map([], |row| {
|
||||||
id: row.get(0)?,
|
Ok(Note {
|
||||||
content: row.get(1)?,
|
id: row.get(0)?,
|
||||||
date: row.get(2)?,
|
content: row.get(1)?,
|
||||||
tag: row.get(3)?,
|
date: row.get(2)?,
|
||||||
|
tag: row.get(3)?,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}).unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut json_array = Vec::new();
|
let mut json_array = Vec::new();
|
||||||
|
|
||||||
|
@ -96,20 +97,32 @@ pub fn show_notes(all: bool, tag: &str) -> Result<String, String> {
|
||||||
Ok(json_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 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)");
|
let query = String::from("DELETE FROM NOTES WHERE nid = (SELECT MAX(nid) FROM notes)");
|
||||||
|
|
||||||
match connection.execute(&query, []) {
|
match connection.execute(&query, []) {
|
||||||
Ok(v) => println!("DELETE OK {}", v),
|
Ok(v) => {
|
||||||
Err(e) => println!("DELETE ERR {}", e),
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -11,8 +11,8 @@ fn greet(name: &str) -> String {
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_notes_list() -> String {
|
fn get_notes_list() -> String {
|
||||||
let notes = show_notes(false, &String::new()).unwrap();
|
let notes = show_notes(false, "").unwrap();
|
||||||
format!("{}", notes)
|
notes.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
@ -23,9 +23,16 @@ fn create_note(content: &str, tag: &str) -> bool {
|
||||||
true
|
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() {
|
fn main() {
|
||||||
tauri::Builder::default()
|
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!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "snotes-deck",
|
"productName": "snotes-deck",
|
||||||
"version": "0.0.1"
|
"version": "0.0.2"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
|
134
src/main.ts
134
src/main.ts
|
@ -3,13 +3,14 @@ import { Note } from "./model";
|
||||||
|
|
||||||
|
|
||||||
let notesMsgEl: HTMLElement | null;
|
let notesMsgEl: HTMLElement | null;
|
||||||
//let createMsgEl: HTMLElement | null;
|
|
||||||
|
|
||||||
let createNoteContentEl: HTMLTextAreaElement | null;
|
let createNoteContentEl: HTMLTextAreaElement | null;
|
||||||
let createNoteTagEl: HTMLInputElement | null;
|
let createNoteTagEl: HTMLInputElement | null;
|
||||||
|
|
||||||
let noteSidebarContainerEl: HTMLDivElement | null;
|
let noteSidebarContainerEl: HTMLDivElement | null;
|
||||||
|
|
||||||
|
let noteArray: Note[] = []
|
||||||
|
|
||||||
// create
|
// create
|
||||||
async function createNote() {
|
async function createNote() {
|
||||||
console.log("reached ssssjs")
|
console.log("reached ssssjs")
|
||||||
|
@ -25,13 +26,9 @@ async function createNote() {
|
||||||
// read
|
// read
|
||||||
async function showNotes() {
|
async function showNotes() {
|
||||||
if (notesMsgEl) {
|
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 array: Array<any> = await retrieveNotes();
|
||||||
|
|
||||||
const noteArray: Note[] = array.map((jsonObj) => ({
|
noteArray = array.map((jsonObj) => ({
|
||||||
id: jsonObj.id,
|
id: jsonObj.id,
|
||||||
content: jsonObj.content,
|
content: jsonObj.content,
|
||||||
date: jsonObj.date,
|
date: jsonObj.date,
|
||||||
|
@ -51,31 +48,6 @@ async function retrieveNotes(): Promise<Array<JSON>> {
|
||||||
return notesJson;
|
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", () => {
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
createNoteContentEl = document.querySelector("#create-input");
|
createNoteContentEl = document.querySelector("#create-input");
|
||||||
createNoteTagEl = document.querySelector("#create-tag");
|
createNoteTagEl = document.querySelector("#create-tag");
|
||||||
|
@ -91,8 +63,57 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
showNotes();
|
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[]) {
|
function fillNoteSidebar(noteArray: Note[]) {
|
||||||
|
|
||||||
|
@ -106,6 +127,7 @@ function fillNoteSidebar(noteArray: Note[]) {
|
||||||
// Create HTML elements for each note
|
// Create HTML elements for each note
|
||||||
const noteEl: HTMLDivElement = document.createElement('div');
|
const noteEl: HTMLDivElement = document.createElement('div');
|
||||||
noteEl.classList.add('sidebar-note');
|
noteEl.classList.add('sidebar-note');
|
||||||
|
noteEl.classList.add('rightclick-element');
|
||||||
noteEl.addEventListener("click", () => handleSidebarNoteClick(note.id), false);
|
noteEl.addEventListener("click", () => handleSidebarNoteClick(note.id), false);
|
||||||
|
|
||||||
const idSpan: HTMLSpanElement = document.createElement('span');
|
const idSpan: HTMLSpanElement = document.createElement('span');
|
||||||
|
@ -131,11 +153,61 @@ function fillNoteSidebar(noteArray: Note[]) {
|
||||||
// Append noteEl to the container, if it still exists?
|
// Append noteEl to the container, if it still exists?
|
||||||
noteSidebarContainerEl ? noteSidebarContainerEl.appendChild(noteEl) : null;
|
noteSidebarContainerEl ? noteSidebarContainerEl.appendChild(noteEl) : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
refreshContextMenuElements();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function handleSidebarNoteClick(id: Number): any {
|
function handleSidebarNoteClick(id: Number): any {
|
||||||
console.log("huh " + id);
|
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';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,11 +90,11 @@ button:active {
|
||||||
background-color: #e8e8e8;
|
background-color: #e8e8e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
#create-tag{
|
#create-tag {
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#create-input{
|
#create-input {
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -114,7 +114,8 @@ button:active {
|
||||||
#create-input:focus {
|
#create-input:focus {
|
||||||
border-color: #cf66e9;
|
border-color: #cf66e9;
|
||||||
box-shadow: 0 0 5px rgba(102, 175, 233, 0.6);
|
box-shadow: 0 0 5px rgba(102, 175, 233, 0.6);
|
||||||
outline: none; /* Remove default focus outline */
|
outline: none;
|
||||||
|
/* Remove default focus outline */
|
||||||
}
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
|
@ -166,3 +167,29 @@ button {
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-radius: 4px;
|
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;
|
||||||
|
}
|
Loading…
Reference in New Issue