keyboard shortcuts, searchbar
This commit is contained in:
parent
c4de94d395
commit
012e86a155
|
@ -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">
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
},
|
||||
"package": {
|
||||
"productName": "snotes-deck",
|
||||
"version": "0.0.3"
|
||||
"version": "0.0.4"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
|
108
src/main.ts
108
src/main.ts
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue