diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form.rs index bce08ad2..5be033a5 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form.rs @@ -1,10 +1,12 @@ mod file; pub mod list; mod name; +mod save; use file::File; use list::{item::value::Value, List}; use name::Name; +use save::Save; use super::Action; use gtk::{prelude::BoxExt, Box, Orientation}; @@ -15,6 +17,7 @@ pub struct Form { pub file: Rc, pub list: Rc, pub name: Rc, + pub save: Rc, pub gobject: Box, } @@ -24,9 +27,10 @@ impl Form { /// Create new `Self` pub fn new(action: Rc) -> Self { // Init components + let file = Rc::new(File::new(action.clone())); let list = Rc::new(List::new()); let name = Rc::new(Name::new(action.clone())); - let file = Rc::new(File::new(action.clone())); + let save = Rc::new(Save::new(action.clone())); // Init main container let gobject = Box::builder().orientation(Orientation::Vertical).build(); @@ -34,11 +38,13 @@ impl Form { gobject.append(&list.gobject); gobject.append(&name.gobject); gobject.append(&file.gobject); + gobject.append(&save.gobject); // Connect events list.on_select({ let file = file.clone(); let name = name.clone(); + let save = save.clone(); let update = action.update.clone(); move |item| { // Change name entry visibility @@ -47,6 +53,16 @@ impl Form { // Change file choose button visibility file.show(matches!(item, Value::ImportPem)); + // Change export button visibility, update holder @TODO + match item { + Value::ProfileIdentityGeminiId(id) => { + // save.show(Some(id)); + } + _ => { + // save.show(None); + } + } + // Update widget update.activate(); } @@ -59,6 +75,7 @@ impl Form { file, list, name, + save, } } diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/save.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/save.rs new file mode 100644 index 00000000..ec105818 --- /dev/null +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/save.rs @@ -0,0 +1,126 @@ +mod certificate; +use certificate::Certificate; + +use super::Action; +use gtk::{ + gio::{Cancellable, ListStore}, + prelude::{ButtonExt, FileExt, WidgetExt}, + Button, FileDialog, FileFilter, Window, +}; +use std::{cell::RefCell, fs::File, io::Write, rc::Rc}; + +const LABEL: &str = "Export to file.."; +const MARGIN: i32 = 8; + +pub struct Save { + certificate: Rc>>, + pub gobject: Button, +} + +impl Save { + // Constructors + + /// Create new `Self` + pub fn new(action: Rc) -> Self { + // Init `Certificate` holder + let certificate = Rc::new(RefCell::new(None::)); + + // Init `GObject` + let gobject = Button::builder() + .label(LABEL) + .margin_top(MARGIN) + .visible(false) + .build(); + + // Init events + gobject.connect_clicked({ + let certificate = certificate.clone(); + let gobject = gobject.clone(); + move |_| { + // Get certificate selected from holder + match certificate.borrow().as_ref() { + Some(certificate) => { + // Copy certificate holder values + let name = certificate.name.clone(); + let data = certificate.data.clone(); + + // Lock open button (prevent double click) + gobject.set_sensitive(false); + + // Init file filters related with PEM extension + let filters = ListStore::new::(); + + let filter_all = FileFilter::new(); + filter_all.add_pattern("*.*"); + filter_all.set_name(Some("All")); + filters.append(&filter_all); + + let filter_pem = FileFilter::new(); + filter_pem.add_mime_type("application/x-x509-ca-cert"); + filter_pem.set_name(Some("Certificate (*.pem)")); + filters.append(&filter_pem); + + // Init file dialog + FileDialog::builder() + .default_filter(&filter_pem) + .filters(&filters) + .initial_name(format!("{name}.pem")) + .build() + .save(None::<&Window>, None::<&Cancellable>, { + let gobject = gobject.clone(); + move |result| { + match result { + Ok(file) => match file.path() { + Some(path) => match File::create(&path) { + Ok(mut destination) => { + match destination.write_all(data.as_bytes()) { + Ok(_) => { + // @TODO + gobject.set_css_classes(&["success"]); + gobject.set_label(&format!( + "Saved to {}", + path.to_string_lossy() + )) + } + Err(reason) => { + gobject.set_css_classes(&["error"]); + gobject.set_label(&reason.to_string()) + } + } + } + Err(reason) => { + gobject.set_css_classes(&["error"]); + gobject.set_label(&reason.to_string()) + } + }, + None => todo!(), + }, + Err(reason) => { + gobject.set_css_classes(&["warning"]); + gobject.set_label(reason.message()) + } + } + gobject.set_sensitive(true); // unlock + } + }); + } + None => todo!(), // unexpected + } + } + }); + + // Return activated `Self` + Self { + certificate, + gobject, + } + } + + // Actions + + /// Change visibility status + /// * grab focus on `is_visible` + pub fn show(&self, is_visible: bool) { + self.gobject.set_visible(is_visible) + } +} diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/save/certificate.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/save/certificate.rs new file mode 100644 index 00000000..5ad06d70 --- /dev/null +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/save/certificate.rs @@ -0,0 +1,4 @@ +pub struct Certificate { + pub name: String, + pub data: String, +}