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:
Sofía Aritz 2023-03-08 21:12:55 +01:00
parent 2078c82f45
commit 0e2d79fc72
3 changed files with 70 additions and 27 deletions

View file

@ -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)

View file

@ -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))

View file

@ -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
}