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 class="container">
<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">
<!-- This is how the generated notes will look like:
<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)]
mod tests {
use super::*;

View File

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

View File

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

View File

@ -7,6 +7,7 @@ let notesMsgEl: HTMLElement | null;
let createNoteContentEl: HTMLTextAreaElement | null;
let createNoteTagEl: HTMLInputElement | null;
let searchbarEl: HTMLInputElement | null;
let noteSidebarContainerEl: HTMLDivElement | null;
let noteArray: Note[] = []
@ -46,13 +47,14 @@ async function saveNote() {
content: createNoteContentEl.value,
tag: createNoteTagEl.value
});
clearEditor();
// do not clear the editor
//clearEditor();
} else {
console.error("No note is currently being edited");
}
break;
}
showNotes();
}
}
@ -83,10 +85,15 @@ async function retrieveNotes(): Promise<Array<JSON>> {
return notesJson;
}
/**
* Handle even listeners on load.
* This does not handle listeners for generated fields like
* the Notes in the sidebar.
*/
window.addEventListener("DOMContentLoaded", () => {
createNoteContentEl = document.querySelector("#create-input");
createNoteTagEl = document.querySelector("#create-tag");
// createMsgEl = document.querySelector("#create-msg");
searchbarEl = document.querySelector("#note-searchbar");
notesMsgEl = document.querySelector("#notes-list");
showNotes();
document.querySelector("#save-button")?.addEventListener("click", (e) => {
@ -98,10 +105,38 @@ window.addEventListener("DOMContentLoaded", () => {
e.preventDefault();
clearEditor();
showNotes();
})
});
document.querySelector("#show-notes-button")?.addEventListener("click", (e) => {
e.preventDefault();
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();
@ -274,3 +309,68 @@ function clearEditor() {
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;
-webkit-text-size-adjust: 100%;
overflow-y: hidden;
}
#button-row {
@ -31,7 +33,7 @@
justify-content: space-between;
text-align: center;
height: 80vh;
height: 90vh;
}
.editor {
@ -42,6 +44,7 @@
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.row {
@ -104,7 +107,7 @@ button:active {
padding: 10px;
border: 1px solid #ccc;
border: 1px solid #3b3b3b;
border-radius: 5px;
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;
background-color: #252525;
color: #f0f0f0
color: #f0f0f0;
box-sizing: border-box;
}
#create-input:focus {
@ -145,7 +150,7 @@ button {
}
.note-sidebar-container {
max-height: 100%;
max-height: 90%;
overflow-y: scroll;
}
@ -203,6 +208,24 @@ button {
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 */
/* Fancier Scrollbar */