Implement "hide notes" and "show notes", retry on failed password and minor fixes
* implemented "hide notes" * implemented "show notes" (internally "unhide") * improved the title and metadata renderer to use enums and return values instead of callbacks * added system to retry on password fails (adds `native-dialog` dep for convenience)
This commit is contained in:
parent
19fe7a4ef5
commit
fcdbfbc932
3 changed files with 98 additions and 10 deletions
|
@ -14,4 +14,5 @@ chrono = "0.4"
|
|||
serde_json = "1.0"
|
||||
rand = "0.8"
|
||||
pwbox = "0.5"
|
||||
directories = "4.0"
|
||||
directories = "4.0"
|
||||
native-dialog = "0.6"
|
13
src/main.rs
13
src/main.rs
|
@ -13,6 +13,7 @@ use directories::ProjectDirs;
|
|||
use eframe::{CreationContext, Frame};
|
||||
use egui::{Context, RichText, TextEdit, Widget};
|
||||
use log::info;
|
||||
use native_dialog::MessageDialog;
|
||||
use simple_logger::SimpleLogger;
|
||||
use crate::notes::{Note, NoteMetadata};
|
||||
use crate::saving::Saving;
|
||||
|
@ -100,7 +101,17 @@ impl eframe::App for App {
|
|||
|
||||
if !self.tested_password && self.password.is_some() {
|
||||
if let Some(note) = self.notes.get(0) {
|
||||
note.decrypt(self.password.as_ref().unwrap()).unwrap();
|
||||
if note.decrypt(self.password.as_ref().unwrap()).is_err() {
|
||||
let _ = MessageDialog::new()
|
||||
.set_title("invalid password")
|
||||
.set_text("failed to verify the password against an existing note. please try again")
|
||||
.show_alert();
|
||||
|
||||
self.password = None;
|
||||
self.tested_password = false;
|
||||
return;
|
||||
}
|
||||
|
||||
self.tested_password = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
use std::collections::HashMap;
|
||||
use std::time::SystemTime;
|
||||
use chrono::{DateTime, Utc};
|
||||
use egui::{RichText, Ui};
|
||||
use egui::{RichText, Ui, WidgetText};
|
||||
use pwbox::sodium::Sodium;
|
||||
use pwbox::{ErasedPwBox, Eraser, Suite};
|
||||
|
||||
|
@ -25,6 +25,12 @@ pub enum Note {
|
|||
title: String,
|
||||
metadata: NoteMetadata,
|
||||
text: String,
|
||||
},
|
||||
Hidden {
|
||||
id: String,
|
||||
title: String,
|
||||
metadata: NoteMetadata,
|
||||
text: String,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,20 +79,64 @@ impl Note {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn hide(self) -> Self {
|
||||
match self {
|
||||
Note::Decrypted { id, title, metadata, text } => {
|
||||
Self::Hidden { id, title, metadata, text }
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unhide(self) -> Self {
|
||||
match self {
|
||||
Note::Hidden { id, title, metadata, text } => {
|
||||
Self::Decrypted { id, title, metadata, text }
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, ui: &mut Ui, password: &str, cb: impl FnOnce(String)) -> Option<Self> {
|
||||
let mut value = None;
|
||||
ui.group(|ui| {
|
||||
match self {
|
||||
Note::Encrypted { id, title, metadata, .. } => {
|
||||
render_title_and_metadata(ui, title, metadata, false, (id.clone(), Box::new(cb)));
|
||||
let result = render_title_and_metadata(ui, title, metadata, false, None);
|
||||
if let Some(action) = result {
|
||||
match action {
|
||||
RenderResult::DeleteNote => cb(id.to_string()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
if ui.button("decrypt note").clicked() {
|
||||
value = Some(self.decrypt(password).unwrap());
|
||||
}
|
||||
}
|
||||
Note::Decrypted { id, title, metadata, text, .. } => {
|
||||
render_title_and_metadata(ui, title, metadata, true, (id.clone(), Box::new(cb)));
|
||||
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()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
ui.label(text);
|
||||
}
|
||||
Note::Hidden { id, title, metadata, .. } => {
|
||||
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()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
ui.label(WidgetText::from("note is decrypted, but hidden").italics());
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -97,11 +147,24 @@ impl Note {
|
|||
match self {
|
||||
Note::Encrypted { id, .. } => id.clone(),
|
||||
Note::Decrypted { id, .. } => id.clone(),
|
||||
Note::Hidden { id, .. } => id.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_title_and_metadata(ui: &mut Ui, title: impl Into<String>, metadata: &NoteMetadata, show_delete_button: bool, delete_info: (String, Box<impl FnOnce(String)>)) {
|
||||
enum RenderResult {
|
||||
DeleteNote,
|
||||
HideNote,
|
||||
UnhideNote,
|
||||
}
|
||||
|
||||
enum ButtonType {
|
||||
Hide,
|
||||
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> {
|
||||
let mut result = None;
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(RichText::new(title).size(14.0));
|
||||
|
||||
|
@ -117,13 +180,26 @@ fn render_title_and_metadata(ui: &mut Ui, title: impl Into<String>, metadata: &N
|
|||
}
|
||||
|
||||
ui.separator();
|
||||
if show_delete_button {
|
||||
let (id, cb) = delete_info;
|
||||
if ui.button("delete note").clicked() {
|
||||
cb(id);
|
||||
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)
|
||||
}
|
||||
}
|
||||
ButtonType::Unhide => {
|
||||
if ui.button("show note").clicked() {
|
||||
result = Some(RenderResult::UnhideNote)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
result
|
||||
}
|
Loading…
Reference in a new issue