Implement the addition of arbitrary metadata when creating a note
The UI/UX needs to improve, but this is good enough for a proof of concept to start iterating. Also fix a few clippy warnings.
This commit is contained in:
parent
2078c82f45
commit
0e2d79fc72
3 changed files with 70 additions and 27 deletions
|
@ -4,8 +4,8 @@ This is a simple and experimental password-based note-taking app with built-in
|
|||
[password-based encryption](https://docs.rs/pwbox/0.5.0/pwbox/).
|
||||
|
||||
## To-Do list
|
||||
- [ ] Improve password checking
|
||||
- [ ] Allow the addition of arbitrary metadata when creating a note
|
||||
- [x] Improve password checking
|
||||
- [x] Allow the addition of arbitrary metadata when creating a note
|
||||
- [ ] Add basic markdown support (bold, italics, underline)
|
||||
- [ ] Improve performance (duplicate decryption operations, tons of copying/cloning, etc)
|
||||
|
||||
|
|
44
src/main.rs
44
src/main.rs
|
@ -6,7 +6,7 @@
|
|||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use std::collections::{VecDeque};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::fs;
|
||||
use std::time::SystemTime;
|
||||
use directories::ProjectDirs;
|
||||
|
@ -42,6 +42,8 @@ struct App {
|
|||
|
||||
title_buffer: String,
|
||||
text_buffer: String,
|
||||
metadata_key_buffer: String,
|
||||
metadata_buffer: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
@ -58,6 +60,8 @@ impl App {
|
|||
|
||||
title_buffer: String::new(),
|
||||
text_buffer: String::new(),
|
||||
metadata_key_buffer: String::new(),
|
||||
metadata_buffer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -198,6 +202,37 @@ impl eframe::App for App {
|
|||
ui.label("text:");
|
||||
ui.text_edit_multiline(&mut self.text_buffer);
|
||||
|
||||
if let Some(metadata) = &mut self.metadata_buffer {
|
||||
ui.label("metadata:");
|
||||
for (k, v) in &mut *metadata {
|
||||
ui.horizontal(|ui| {
|
||||
ui.monospace(format!("{k}:"));
|
||||
ui.text_edit_singleline(v);
|
||||
});
|
||||
}
|
||||
if !metadata.is_empty() {
|
||||
ui.add_space(10.0);
|
||||
}
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("key:");
|
||||
ui.text_edit_singleline(&mut self.metadata_key_buffer);
|
||||
|
||||
if ui.button("+").clicked() && !self.metadata_key_buffer.is_empty() {
|
||||
metadata.insert(self.metadata_key_buffer.clone(), "".into());
|
||||
self.metadata_key_buffer = String::new();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("metadata:");
|
||||
|
||||
if ui.button("create").clicked() {
|
||||
self.metadata_buffer = Some(HashMap::new());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
if ui.button("add note").clicked() {
|
||||
let note = Note::Decrypted {
|
||||
|
@ -209,7 +244,7 @@ impl eframe::App for App {
|
|||
title: self.title_buffer.clone(),
|
||||
metadata: NoteMetadata {
|
||||
date: SystemTime::now(),
|
||||
arbitrary: None,
|
||||
arbitrary: self.metadata_buffer.clone(),
|
||||
},
|
||||
text: self.text_buffer.clone(),
|
||||
};
|
||||
|
@ -221,6 +256,8 @@ impl eframe::App for App {
|
|||
|
||||
self.notes = Vec::from(vec_deque);
|
||||
|
||||
self.metadata_buffer = None;
|
||||
self.metadata_key_buffer = String::new();
|
||||
self.title_buffer = String::new();
|
||||
self.text_buffer = String::new();
|
||||
self.mode = CurrentMode::View;
|
||||
|
@ -250,8 +287,7 @@ impl eframe::App for App {
|
|||
ui.label(text);
|
||||
|
||||
let entropy = entropy(&self.password_buffer)
|
||||
.or(Some(0.0))
|
||||
.unwrap();
|
||||
.unwrap_or(0.0);
|
||||
let text = if entropy < 35_f64 {
|
||||
WidgetText::from(format!("entropy: {:.2}", entropy))
|
||||
.color(Color32::from_rgb(240, 5, 5))
|
||||
|
|
|
@ -105,7 +105,7 @@ impl Note {
|
|||
let result = render_title_and_metadata(ui, title, metadata, false, None);
|
||||
if let Some(action) = result {
|
||||
match action {
|
||||
RenderResult::DeleteNote => cb(id.to_string()),
|
||||
NoteRenderAction::Delete => cb(id.to_string()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -117,8 +117,8 @@ impl Note {
|
|||
let result = render_title_and_metadata(ui, title, metadata, true, Some(ButtonType::Hide));
|
||||
if let Some(action) = result {
|
||||
match action {
|
||||
RenderResult::DeleteNote => cb(id.to_string()),
|
||||
RenderResult::HideNote => value = Some(self.clone().hide()),
|
||||
NoteRenderAction::Delete => cb(id.to_string()),
|
||||
NoteRenderAction::Hide => value = Some(self.clone().hide()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -129,8 +129,8 @@ impl Note {
|
|||
let result = render_title_and_metadata(ui, title, metadata, true, Some(ButtonType::Unhide));
|
||||
if let Some(action) = result {
|
||||
match action {
|
||||
RenderResult::DeleteNote => cb(id.to_string()),
|
||||
RenderResult::UnhideNote => value = Some(self.clone().unhide()),
|
||||
NoteRenderAction::Delete => cb(id.to_string()),
|
||||
NoteRenderAction::Unhide => value = Some(self.clone().unhide()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -152,10 +152,10 @@ impl Note {
|
|||
}
|
||||
}
|
||||
|
||||
enum RenderResult {
|
||||
DeleteNote,
|
||||
HideNote,
|
||||
UnhideNote,
|
||||
enum NoteRenderAction {
|
||||
Delete,
|
||||
Hide,
|
||||
Unhide,
|
||||
}
|
||||
|
||||
enum ButtonType {
|
||||
|
@ -163,7 +163,7 @@ enum ButtonType {
|
|||
Unhide,
|
||||
}
|
||||
|
||||
fn render_title_and_metadata(ui: &mut Ui, title: impl Into<String>, metadata: &NoteMetadata, show_delete_button: bool, show_hide_button: Option<ButtonType>) -> Option<RenderResult> {
|
||||
fn render_title_and_metadata(ui: &mut Ui, title: impl Into<String>, metadata: &NoteMetadata, show_delete_button: bool, show_hide_button: Option<ButtonType>) -> Option<NoteRenderAction> {
|
||||
let mut result = None;
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(RichText::new(title).size(14.0));
|
||||
|
@ -172,34 +172,41 @@ fn render_title_and_metadata(ui: &mut Ui, title: impl Into<String>, metadata: &N
|
|||
ui.separator();
|
||||
ui.label(date.format("%d-%m-%y %H:%M").to_string());
|
||||
|
||||
if let Some(arbitrary) = &metadata.arbitrary {
|
||||
for (k, v) in arbitrary.iter() {
|
||||
ui.separator();
|
||||
ui.label(RichText::new(format!("{}: {}", k, v)).monospace());
|
||||
if show_delete_button {
|
||||
ui.separator();
|
||||
if ui.button("delete note").clicked() {
|
||||
result = Some(NoteRenderAction::Delete);
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
if show_delete_button && ui.button("delete note").clicked() {
|
||||
result = Some(RenderResult::DeleteNote);
|
||||
}
|
||||
|
||||
if let Some(button_type) = show_hide_button {
|
||||
match button_type {
|
||||
ButtonType::Hide => {
|
||||
if ui.button("hide note").clicked() {
|
||||
result = Some(RenderResult::HideNote)
|
||||
result = Some(NoteRenderAction::Hide)
|
||||
}
|
||||
}
|
||||
ButtonType::Unhide => {
|
||||
if ui.button("show note").clicked() {
|
||||
result = Some(RenderResult::UnhideNote)
|
||||
result = Some(NoteRenderAction::Unhide)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(arbitrary) = &metadata.arbitrary {
|
||||
ui.horizontal(|ui| {
|
||||
let mut arbitrary_iter = arbitrary.iter().peekable();
|
||||
while let Some((k, v)) = arbitrary_iter.next() {
|
||||
ui.label(RichText::new(format!("{}: {}", k, v)).monospace());
|
||||
if arbitrary_iter.peek().is_some() {
|
||||
ui.separator();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
result
|
||||
}
|
Loading…
Reference in a new issue