keyboard shortcuts, searchbar

This commit is contained in:
catto 2024-04-14 17:33:35 +02:00
parent c4de94d395
commit 012e86a155
6 changed files with 185 additions and 16 deletions

View File

@ -33,6 +33,9 @@
</div> </div>
<div class="container"> <div class="container">
<div class="sidebar"> <div class="sidebar">
<div class="searchbar-container">
<input type="search" name="note-search" id="note-searchbar" placeholder="Search...">
</div>
<div class="note-sidebar-container" id="note-sidebar-container"> <div class="note-sidebar-container" id="note-sidebar-container">
<!-- This is how the generated notes will look like: <!-- This is how the generated notes will look like:
<div class="sidebar-note rightclick-element"> <div class="sidebar-note rightclick-element">

View File

@ -136,6 +136,48 @@ pub fn edit_specific_note(id: i32, tag: &str, content: &str) -> Result<(), Strin
} }
} }
/// Looks for matches in both content and tag.
pub fn search_notes(query: &str) -> Result<String, String> {
let home = home_dir().unwrap().join(".snotes.db");
let connection = Connection::open(home).map_err(|e| format!("Database Error: {}", e))?;
let query = format!(
"SELECT * FROM notes WHERE content LIKE '%{}%' OR tag LIKE '%{}%'",
query, query
);
let mut prepare = connection.prepare(&query).map_err(|e| format!("Query Error: {}", e))?;
let notes = prepare
.query_map([], |row| {
Ok(Note {
id: row.get(0)?,
content: row.get(1)?,
date: row.get(2)?,
tag: row.get(3)?,
})
})
.map_err(|e| format!("Mapping Error: {}", e))?;
let mut json_array = Vec::new();
for note in notes {
let unwrapped = note.map_err(|e| format!("Note Error: {}", e))?;
let note_json = json!({
"id": unwrapped.id,
"date": unwrapped.date,
"content": unwrapped.content,
"tag": unwrapped.tag
});
json_array.push(note_json);
}
let json_string = serde_json::to_string(&json_array).map_err(|e| format!("JSON Error: {}", e))?;
Ok(json_string)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -17,24 +17,24 @@ fn get_notes_list() -> String {
} }
#[tauri::command] #[tauri::command]
fn create_note(content: &str, tag: &str) -> bool { fn search_notes(query: &str) -> String {
println!("reached"); let results = libsnotes::search_notes(query).unwrap();
libsnotes::create_note(&content.to_string(), &tag.to_string()).unwrap(); results.to_string()
}
#[tauri::command]
fn create_note(content: &str, tag: &str) -> bool {
libsnotes::create_note(&content.to_string(), &tag.to_string()).unwrap();
true true
} }
#[tauri::command] #[tauri::command]
fn delete_specific_note(id: u32) -> bool { fn delete_specific_note(id: u32) -> bool {
println!("reched Delete");
libsnotes::delete_specific_note(id.try_into().unwrap()).is_ok() libsnotes::delete_specific_note(id.try_into().unwrap()).is_ok()
} }
#[tauri::command] #[tauri::command]
fn update_specific_note(id: u32, content: &str, tag: &str) -> bool { fn update_specific_note(id: u32, content: &str, tag: &str) -> bool {
println!("update specific note");
libsnotes::edit_specific_note(id.try_into().unwrap(), tag, content).is_ok() libsnotes::edit_specific_note(id.try_into().unwrap(), tag, content).is_ok()
} }
@ -43,6 +43,7 @@ fn main() {
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
greet, greet,
get_notes_list, get_notes_list,
search_notes,
create_note, create_note,
delete_specific_note, delete_specific_note,
update_specific_note update_specific_note

View File

@ -8,7 +8,7 @@
}, },
"package": { "package": {
"productName": "snotes-deck", "productName": "snotes-deck",
"version": "0.0.3" "version": "0.0.4"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

@ -7,6 +7,7 @@ let notesMsgEl: HTMLElement | null;
let createNoteContentEl: HTMLTextAreaElement | null; let createNoteContentEl: HTMLTextAreaElement | null;
let createNoteTagEl: HTMLInputElement | null; let createNoteTagEl: HTMLInputElement | null;
let searchbarEl: HTMLInputElement | null;
let noteSidebarContainerEl: HTMLDivElement | null; let noteSidebarContainerEl: HTMLDivElement | null;
let noteArray: Note[] = [] let noteArray: Note[] = []
@ -46,13 +47,14 @@ async function saveNote() {
content: createNoteContentEl.value, content: createNoteContentEl.value,
tag: createNoteTagEl.value tag: createNoteTagEl.value
}); });
clearEditor(); // do not clear the editor
//clearEditor();
} else { } else {
console.error("No note is currently being edited"); console.error("No note is currently being edited");
} }
break; break;
} }
showNotes();
} }
} }
@ -83,10 +85,15 @@ async function retrieveNotes(): Promise<Array<JSON>> {
return notesJson; return notesJson;
} }
/**
* Handle even listeners on load.
* This does not handle listeners for generated fields like
* the Notes in the sidebar.
*/
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");
// createMsgEl = document.querySelector("#create-msg"); searchbarEl = document.querySelector("#note-searchbar");
notesMsgEl = document.querySelector("#notes-list"); notesMsgEl = document.querySelector("#notes-list");
showNotes(); showNotes();
document.querySelector("#save-button")?.addEventListener("click", (e) => { document.querySelector("#save-button")?.addEventListener("click", (e) => {
@ -98,10 +105,38 @@ window.addEventListener("DOMContentLoaded", () => {
e.preventDefault(); e.preventDefault();
clearEditor(); clearEditor();
showNotes(); showNotes();
}) });
document.querySelector("#show-notes-button")?.addEventListener("click", (e) => { document.querySelector("#show-notes-button")?.addEventListener("click", (e) => {
e.preventDefault(); e.preventDefault();
showNotes(); showNotes();
});
// Pressing TAB should insert intends in the editor.
// This could potentially cause issues later...
document.querySelector("#create-input")?.addEventListener("keydown", (event: Event) => {
const e = event as KeyboardEvent;
if (e.key === 'Tab') {
e.preventDefault();
const target = e.target as HTMLTextAreaElement;
const start = target.selectionStart;
const end = target.selectionEnd;
const newValue = target.value.substring(0, start) +
"\t" + target.value.substring(end);
target.value = newValue;
target.setSelectionRange(start + 1, start + 1);
}
});
// searchbar event listener
document.querySelector("#note-searchbar")?.addEventListener("input", (event: Event) => {
const target = event.target as HTMLInputElement;
const input = target.value;
searchNote(input);
}) })
refreshContextMenuElements(); refreshContextMenuElements();
@ -274,3 +309,68 @@ function clearEditor() {
editorState = EditorState.NEW; editorState = EditorState.NEW;
} }
} }
// Listen for global key presses
document.addEventListener('keydown', (e) => handleKeyboardShortcuts(e));
/**
* Handle global keyboard shortcuts like save, search, new
*/
function handleKeyboardShortcuts(event: KeyboardEvent) {
// save
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
saveNote();
}
// new
if (event.ctrlKey && event.key === 'n') {
event.preventDefault();
clearEditor();
}
// refresh
if (event.ctrlKey && event.key === 'r') {
event.preventDefault();
showNotes();
}
// focus searchbox
if (event.ctrlKey && event.key === 'f') {
event.preventDefault();
if (searchbarEl) {
searchbarEl.focus();
} else {
console.error("failed to focus on searchbar");
}
}
// open by id
// quick switch note 1-9
}
/**
* Searches for note and displays the results accordingly
*/
async function searchNote(input: string) {
if (notesMsgEl) {
const array: Array<any> = await getSearchResults(input);
noteArray = array.map((jsonObj) => ({
id: jsonObj.id,
content: jsonObj.content,
date: jsonObj.date,
tag: jsonObj.tag
}));
console.log(noteArray[0])
fillNoteSidebar(noteArray);
}
}
async function getSearchResults(input: string): Promise<Array<JSON>> {
const resultsString: string = await invoke("search_notes", {
query: input
});
const resultsJson = JSON.parse(resultsString);
console.log(resultsJson);
return resultsJson;
}

View File

@ -13,6 +13,8 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
overflow-y: hidden;
} }
#button-row { #button-row {
@ -31,7 +33,7 @@
justify-content: space-between; justify-content: space-between;
text-align: center; text-align: center;
height: 80vh; height: 90vh;
} }
.editor { .editor {
@ -42,6 +44,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-end;
} }
.row { .row {
@ -104,7 +107,7 @@ button:active {
padding: 10px; padding: 10px;
border: 1px solid #ccc; border: 1px solid #3b3b3b;
border-radius: 5px; border-radius: 5px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
@ -112,7 +115,9 @@ button:active {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
background-color: #252525; background-color: #252525;
color: #f0f0f0 color: #f0f0f0;
box-sizing: border-box;
} }
#create-input:focus { #create-input:focus {
@ -145,7 +150,7 @@ button {
} }
.note-sidebar-container { .note-sidebar-container {
max-height: 100%; max-height: 90%;
overflow-y: scroll; overflow-y: scroll;
} }
@ -203,6 +208,24 @@ button {
background-color: #770079; background-color: #770079;
} }
/* Searchbar */
.searchbar-container {
margin: 0.5em;
}
#note-searchbar {
width: 100%;
padding: 10px;
border: none;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
outline: none;
font-size: 16px;
}
#note-searchbar::placeholder {}
/* MISC */ /* MISC */
/* Fancier Scrollbar */ /* Fancier Scrollbar */