Skip to content

Commit

Permalink
feat: introduce password history (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
sdanialraza authored Jan 27, 2024
1 parent 964731b commit 030b5cc
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 7 deletions.
115 changes: 114 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ edition = "2021"

[dependencies]
copypasta = "0.10.0"
eframe = "0.25.0"
eframe = { version = "0.25.0", features = ["persistence"] }
rand = "0.8.5"
serde_json = "1.0.111"
74 changes: 69 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,86 @@ mod random_password;
use constants::MAX_PASSWORD_LENGTH;
use random_password::{RandomPassword, RandomPasswordOptions};

use std::time::Duration;

use copypasta::{ClipboardContext, ClipboardProvider};
use eframe::{
egui::{CentralPanel, Context, RichText, Slider, ViewportBuilder},
App, Error, Frame, NativeOptions,
run_native, App, Error, Frame, NativeOptions,
};
use serde_json::{from_str, json};

fn main() -> Result<(), Error> {
let options = NativeOptions {
centered: true,
follow_system_theme: true,
viewport: ViewportBuilder::default().with_inner_size([300.0, 200.0]),
viewport: ViewportBuilder::default()
.with_app_id("password-generator")
.with_inner_size([300.0, 200.0]),
..Default::default()
};

eframe::run_native(
run_native(
"Password Generator",
options,
Box::new(|_app_creator| Box::<PasswordGenerator>::default()),
Box::new(|creation_context| {
let storage = match creation_context.storage {
Some(storage) => storage,
None => return Box::<PasswordGenerator>::default(),
};

let recent_passwords = match storage.get_string("recent_passwords") {
Some(passwords) => from_str(passwords.as_str()).unwrap_or_default(),
None => return Box::<PasswordGenerator>::default(),
};

Box::new(PasswordGenerator {
recent_passwords,
..Default::default()
})
}),
)
}

struct PasswordGenerator {
options: RandomPasswordOptions,
password: String,
recent_passwords: Vec<String>,
}

impl Default for PasswordGenerator {
fn default() -> Self {
Self {
options: RandomPasswordOptions::default(),
password: RandomPassword::new(RandomPasswordOptions::default()).password,
recent_passwords: Vec::with_capacity(10),
}
}
}

impl App for PasswordGenerator {
fn save(&mut self, storage: &mut dyn eframe::Storage) {
let mut passwords_to_add: Vec<String> = Vec::with_capacity(10);

passwords_to_add.push(self.password.clone());

let mut recent_passwords = match storage.get_string("recent_passwords") {
Some(passwords) => {
let passwords: Vec<_> = from_str(passwords.as_str()).unwrap_or_default();

passwords_to_add.extend(passwords);

passwords_to_add
}
None => passwords_to_add,
};

recent_passwords.sort();

recent_passwords.dedup();

storage.set_string("recent_passwords", json!(recent_passwords).to_string());
}

fn update(&mut self, ctx: &Context, _frame: &mut Frame) {
CentralPanel::default().show(ctx, |ui| {
ui.label(RichText::new(self.password.to_string()).size(18.0));
Expand Down Expand Up @@ -69,13 +113,33 @@ impl App for PasswordGenerator {
}

self.password = RandomPassword::new(self.options).password;

if self.recent_passwords.len() >= 10 {
self.recent_passwords.pop();
}

self.recent_passwords.push(self.password.clone());
}

if ui.button(RichText::new("Copy")).clicked() {
let mut context = ClipboardContext::new().unwrap();

let _copied = context.set_contents(self.password.to_string());
}

ui.separator();

ui.heading("Recent Passwords");

ui.vertical(|ui| {
for password in self.recent_passwords.iter().rev() {
ui.label(RichText::new(password.to_string()).size(14.0));
}
});
});
}

fn auto_save_interval(&self) -> std::time::Duration {
Duration::from_secs(5)
}
}

0 comments on commit 030b5cc

Please sign in to comment.