diff --git a/src/app/browser/window/tab/item/identity/gemini/widget.rs b/src/app/browser/window/tab/item/identity/gemini/widget.rs index a80bef03..3c64660e 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget.rs @@ -68,9 +68,6 @@ impl Widget { let form = form.clone(); let alert_dialog = alert_dialog.clone(); move || { - // Update form with it children components - form.update(); - // Deactivate apply button if the form values could not be processed alert_dialog.set_response_enabled(RESPONSE_APPLY.0, form.is_applicable()); } @@ -96,14 +93,13 @@ impl Widget { this.set_response_enabled(response, false); // Result - callback(form.list.selected_item().value_enum()) + callback(form.list.selected().value_enum()) } }); } /// Show dialog with new preset pub fn present(&self, parent: Option<&impl IsA>) { - self.form.update(); self.alert_dialog.present(parent) } } 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 e9b3aca2..86c53011 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 @@ -18,13 +18,13 @@ use gtk::{prelude::BoxExt, Box, Orientation}; use std::rc::Rc; pub struct Form { - // pub action: Rc, - pub drop: Rc, - pub exit: Rc, + // pub widget_action: Rc, + // pub drop: Rc, + // pub exit: Rc, pub file: Rc, pub list: Rc, pub name: Rc, - pub save: Rc, + // pub save: Rc, pub g_box: Box, } @@ -32,14 +32,14 @@ impl Form { // Constructors /// Create new `Self` - pub fn new(profile: Rc, action: Rc, auth_url: &str) -> Self { + pub fn new(profile: Rc, widget_action: Rc, auth_url: &str) -> Self { // Init components - let file = Rc::new(File::new(action.clone())); - let list = Rc::new(List::new(profile.clone(), action.clone(), auth_url)); - let name = Rc::new(Name::new(action.clone())); - let save = Rc::new(Save::new(profile.clone())); - let drop = Rc::new(Drop::new(profile.clone(), action.clone(), list.clone())); - let exit = Rc::new(Exit::new(profile.clone(), action.clone(), list.clone())); + let list = Rc::new(List::new(profile.clone(), auth_url)); + let file = Rc::new(File::new(widget_action.clone())); + let name = Rc::new(Name::new(widget_action.clone())); + let save = Rc::new(Save::new(profile.clone(), list.clone())); + let drop = Rc::new(Drop::new(profile.clone(), list.clone())); + let exit = Rc::new(Exit::new(profile.clone(), list.clone())); // Init main container let g_box = Box::builder().orientation(Orientation::Vertical).build(); @@ -51,15 +51,49 @@ impl Form { g_box.append(&drop.button); g_box.append(&save.button); + // Connect events + list.dropdown.connect_selected_notify({ + let list = list.clone(); + let name = name.clone(); + let file = file.clone(); + // let drop = drop.clone(); + // let exit = exit.clone(); + // let save = save.clone(); + move |_| { + // Get selected item + let item = list.selected(); + + // Update name entry visibility + name.set_visible(matches!(item.value_enum(), Value::GeneratePem)); + + // Update file choose button visibility + file.set_visible(matches!(item.value_enum(), Value::ImportPem)); + + // Update ID-related components + match item.value_enum() { + Value::ProfileIdentityGeminiId(_) => { + drop.set_visible(true); + exit.set_visible(true); + save.set_visible(true); + } + _ => { + drop.set_visible(false); + exit.set_visible(false); + save.set_visible(false); + } + } + } + }); + // Return activated `Self` Self { - // action, - drop, - exit, + // widget_action, + // drop, + // exit, file, list, name, - save, + // save, g_box, } } @@ -68,38 +102,11 @@ impl Form { /// Validate `Self` components match current selection pub fn is_applicable(&self) -> bool { - match self.list.selected_item().value_enum() { + match self.list.selected().value_enum() { Value::GeneratePem => self.name.is_valid(), Value::ImportPem => self.file.is_valid(), - Value::ProfileIdentityGeminiId(_) => !self.list.selected_item().is_active(), + Value::ProfileIdentityGeminiId(_) => !self.list.selected().is_active(), _ => true, } } - - pub fn update(&self) { - // Get selected item - let item = self.list.selected_item(); - - // Update name entry visibility - self.name - .update(matches!(item.value_enum(), Value::GeneratePem)); - - // Update file choose button visibility - self.file - .update(matches!(item.value_enum(), Value::ImportPem)); - - // Update ID-related components - match item.value_enum() { - Value::ProfileIdentityGeminiId(value) => { - self.drop.update(Some(value)); - self.exit.update(Some(value)); - self.save.update(Some(value)); - } - _ => { - self.drop.update(None); - self.exit.update(None); - self.save.update(None); - } - } - } } diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/drop.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/drop.rs index cc082aba..ae1363b7 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form/drop.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/drop.rs @@ -1,5 +1,4 @@ -use super::Action; -use super::List; +use super::list::{item::Value, List}; use crate::profile::Profile; use adw::{ prelude::{AdwDialogExt, AlertDialogExt, AlertDialogExtManual}, @@ -9,7 +8,7 @@ use gtk::{ prelude::{ButtonExt, WidgetExt}, Button, }; -use std::{cell::RefCell, rc::Rc}; +use std::rc::Rc; // Defaults @@ -23,7 +22,6 @@ const RESPONSE_CANCEL: (&str, &str) = ("cancel", "Cancel"); const RESPONSE_CONFIRM: (&str, &str) = ("confirm", "Confirm"); pub struct Drop { - profile_identity_gemini_id: Rc>>, pub button: Button, } @@ -31,10 +29,7 @@ impl Drop { // Constructors /// Create new `Self` - pub fn new(profile: Rc, action: Rc, list: Rc) -> Self { - // Init selected option holder - let profile_identity_gemini_id = Rc::new(RefCell::new(None::)); - + pub fn new(profile: Rc, list: Rc) -> Self { // Init main widget let button = Button::builder() .label(LABEL) @@ -45,13 +40,11 @@ impl Drop { // Init events button.connect_clicked({ - let action = action.clone(); let button = button.clone(); - let profile_identity_gemini_id = profile_identity_gemini_id.clone(); + let list = list.clone(); move |_| { - // Get selected identity from holder - match profile_identity_gemini_id.borrow().as_ref() { - Some(profile_identity_gemini_id) => { + match list.selected().value_enum() { + Value::ProfileIdentityGeminiId(profile_identity_gemini_id) => { // Init sub-widget let alert_dialog = AlertDialog::builder() .heading(HEADING) @@ -76,61 +69,45 @@ impl Drop { // Connect confirmation event alert_dialog.connect_response(Some(RESPONSE_CONFIRM.0), { - let action = action.clone(); let button = button.clone(); let list = list.clone(); let profile = profile.clone(); - let profile_identity_gemini_id = *profile_identity_gemini_id; - move |_, _| { - match profile.identity.gemini.delete(profile_identity_gemini_id) { - Ok(_) => { - if list.remove(profile_identity_gemini_id).is_some() { - button.set_css_classes(&["success"]); - button.set_label("Identity successfully deleted") - } else { - button.set_css_classes(&["error"]); - button.set_label("List item not found") - // @TODO unexpected - } - } - Err(e) => { + move |_, _| match profile + .identity + .gemini + .delete(profile_identity_gemini_id) + { + Ok(_) => { + if list.remove(profile_identity_gemini_id).is_some() { + button.set_css_classes(&["success"]); + button.set_label("Identity successfully deleted") + } else { button.set_css_classes(&["error"]); - button.set_label(&e.to_string()) + button.set_label("List item not found") } } - action.update.activate() + Err(e) => { + button.set_css_classes(&["error"]); + button.set_label(&e.to_string()) + } } }); // Show dialog alert_dialog.present(Some(&button)) } - None => todo!(), // unexpected + _ => todo!(), // unexpected } } }); // Return activated `Self` - Self { - profile_identity_gemini_id, - button, - } + Self { button } } // Actions - /// Update `profile_identity_gemini_id` holder, - /// toggle visibility depending on given value - pub fn update(&self, profile_identity_gemini_id: Option) { - self.button.set_visible(match profile_identity_gemini_id { - Some(value) => { - self.profile_identity_gemini_id.replace(Some(value)); - true - } - None => { - self.profile_identity_gemini_id.replace(None); - false - } - }) + pub fn set_visible(&self, is_visible: bool) { + self.button.set_visible(is_visible) } } diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/exit.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/exit.rs index fd4b38f6..11700b68 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form/exit.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/exit.rs @@ -1,5 +1,4 @@ -use super::Action; -use super::List; +use super::list::{item::Value, List}; use crate::profile::Profile; use adw::{ prelude::{AdwDialogExt, AlertDialogExt, AlertDialogExtManual}, @@ -9,7 +8,7 @@ use gtk::{ prelude::{ButtonExt, WidgetExt}, Button, }; -use std::{cell::RefCell, rc::Rc}; +use std::rc::Rc; // Defaults @@ -23,7 +22,6 @@ const RESPONSE_CANCEL: (&str, &str) = ("cancel", "Cancel"); const RESPONSE_CONFIRM: (&str, &str) = ("confirm", "Confirm"); pub struct Exit { - profile_identity_gemini_id: Rc>>, pub button: Button, } @@ -31,10 +29,7 @@ impl Exit { // Constructors /// Create new `Self` - pub fn new(profile: Rc, action: Rc, list: Rc) -> Self { - // Init selected option holder - let profile_identity_gemini_id = Rc::new(RefCell::new(None::)); - + pub fn new(profile: Rc, list: Rc) -> Self { // Init main widget let button = Button::builder() .label(LABEL) @@ -45,13 +40,11 @@ impl Exit { // Init events button.connect_clicked({ - let action = action.clone(); let button = button.clone(); - let profile_identity_gemini_id = profile_identity_gemini_id.clone(); move |_| { // Get selected identity from holder - match profile_identity_gemini_id.borrow().as_ref() { - Some(profile_identity_gemini_id) => { + match list.selected().value_enum() { + Value::ProfileIdentityGeminiId(profile_identity_gemini_id) => { // Init sub-widget let alert_dialog = AlertDialog::builder() .heading(HEADING) @@ -76,66 +69,44 @@ impl Exit { // Connect confirmation event alert_dialog.connect_response(Some(RESPONSE_CONFIRM.0), { - let action = action.clone(); let button = button.clone(); let list = list.clone(); let profile = profile.clone(); - let profile_identity_gemini_id = *profile_identity_gemini_id; - move |_, _| { - match profile - .identity - .gemini - .auth - .remove_ref(profile_identity_gemini_id) - { - Ok(_) => { - if list.remove(profile_identity_gemini_id).is_some() { - button.set_css_classes(&["success"]); - button.set_label("Identity successfully disconnected") - } else { - button.set_css_classes(&["error"]); - button.set_label("List item not found") - // @TODO unexpected - } - } - Err(e) => { - button.set_css_classes(&["error"]); - button.set_label(&e.to_string()) + move |_, _| match profile + .identity + .gemini + .auth + .remove_ref(profile_identity_gemini_id) + { + Ok(_) => match list.update(profile_identity_gemini_id) { + Some(_) => { + button.set_css_classes(&["success"]); + button.set_label("Identity successfully disconnected"); } + None => todo!(), + }, + Err(e) => { + button.set_css_classes(&["error"]); + button.set_label(&e.to_string()) } - action.update.activate() } }); // Show dialog alert_dialog.present(Some(&button)) } - None => todo!(), // unexpected + _ => todo!(), // unexpected } } }); // Return activated `Self` - Self { - profile_identity_gemini_id, - button, - } + Self { button } } // Actions - /// Update `profile_identity_gemini_id` holder, - /// toggle visibility depending on given value - pub fn update(&self, profile_identity_gemini_id: Option) { - self.button.set_visible(match profile_identity_gemini_id { - Some(value) => { - self.profile_identity_gemini_id.replace(Some(value)); - true - } - None => { - self.profile_identity_gemini_id.replace(None); - false - } - }) + pub fn set_visible(&self, is_visible: bool) { + self.button.set_visible(is_visible) } } diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/file.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/file.rs index 6cea2370..7bcdf477 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form/file.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/file.rs @@ -21,7 +21,7 @@ impl File { // Constructors /// Create new `Self` - pub fn new(action: Rc) -> Self { + pub fn new(widget_action: Rc) -> Self { // Init PEM let pem = Rc::new(RefCell::new(None)); @@ -37,7 +37,7 @@ impl File { button.connect_clicked({ let button = button.clone(); let pem = pem.clone(); - let update = action.update.clone(); + let widget_action = widget_action.clone(); move |_| { // Lock open button (prevent double click) button.set_sensitive(false); @@ -63,7 +63,7 @@ impl File { .open(None::<&Window>, None::<&Cancellable>, { let button = button.clone(); let pem = pem.clone(); - let update = update.clone(); + let widget_action = widget_action.clone(); move |result| { match result { Ok(file) => match file.path() { @@ -89,7 +89,7 @@ impl File { } } button.set_sensitive(true); // unlock - update.activate() + widget_action.update.activate() } }); } @@ -102,12 +102,8 @@ impl File { // Actions /// Change visibility status - /// * grab focus on `is_visible` - pub fn update(&self, is_visible: bool) { + pub fn set_visible(&self, is_visible: bool) { self.button.set_visible(is_visible); - if is_visible { - self.button.grab_focus(); - } } // Getters diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs index 60330841..2ecdeb6c 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/list.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use item::Item; -use super::Action; use crate::profile::Profile; use gtk::{ gdk::Cursor, @@ -18,22 +17,23 @@ use gtk::{ pub struct List { pub dropdown: DropDown, list_store: ListStore, + // profile: Rc, } impl List { // Constructors /// Create new `Self` - pub fn new(profile: Rc, action: Rc, auth_url: &str) -> Self { + pub fn new(profile: Rc, auth_url: &str) -> Self { // Init model let list_store = ListStore::new::(); + list_store.remove_all(); + list_store.append(&Item::new_guest_session()); list_store.append(&Item::new_generate_pem()); list_store.append(&Item::new_import_pem()); - // Append identities from profile database - // * memory cache synced also and could be faster @TODO match profile.identity.gemini.database.records() { Ok(identities) => { for identity in identities { @@ -132,41 +132,52 @@ impl List { .factory(&factory) .build(); - // Connect events - dropdown.connect_selected_notify(move |_| action.update.activate()); - // Return activated `Self` Self { - list_store, dropdown, + list_store, + // profile, } } // Actions - /// Find list item by `value` (stores ID) + /// Find list item by `profile_identity_gemini_id` /// * return `position` found - pub fn find(&self, value: i64) -> Option { - self.list_store - .find_with_equal_func(|this| value == this.clone().downcast::().unwrap().value()) + pub fn find(&self, profile_identity_gemini_id: i64) -> Option { + self.list_store.find_with_equal_func(|this| { + profile_identity_gemini_id == this.downcast_ref::().unwrap().value() + }) } - /// Remove list item by `value` (stores ID) + /// Remove list item by `profile_identity_gemini_id` (stores ID) /// * return `position` of removed list item - pub fn remove(&self, value: i64) -> Option { - match self.find(value) { + pub fn remove(&self, profile_identity_gemini_id: i64) -> Option { + match self.find(profile_identity_gemini_id) { Some(position) => { self.list_store.remove(position); Some(position) } - None => None, + None => todo!(), + } + } + + /// Update list item by `profile_identity_gemini_id` (stores ID) + /// * return `position` of updated list item + pub fn update(&self, profile_identity_gemini_id: i64) -> Option { + match self.find(profile_identity_gemini_id) { + Some(position) => { + // @TODO + Some(position) + } + None => todo!(), } } // Getters /// Get selected `Item` GObject - pub fn selected_item(&self) -> Item { + pub fn selected(&self) -> Item { self.dropdown .selected_item() .and_downcast::() diff --git a/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs b/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs index 2f87c2a6..e1cace63 100644 --- a/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs +++ b/src/app/browser/window/tab/item/identity/gemini/widget/form/name.rs @@ -19,7 +19,7 @@ impl Name { // Constructors /// Create new `Self` - pub fn new(action: Rc) -> Self { + pub fn new(widget_action: Rc) -> Self { // Init main gobject let entry = Entry::builder() .margin_top(MARGIN) @@ -29,7 +29,7 @@ impl Name { .build(); // Init events - entry.connect_changed(move |_| action.update.activate()); + entry.connect_changed(move |_| widget_action.update.activate()); // Return activated `Self` Self { entry } @@ -39,7 +39,7 @@ impl Name { /// Change visibility status /// * grab focus on `is_visible` - pub fn update(&self, is_visible: bool) { + pub fn set_visible(&self, is_visible: bool) { self.entry.set_visible(is_visible); if is_visible { self.entry.grab_focus(); 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 index 78effd53..27c1f95d 100644 --- 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 @@ -1,20 +1,20 @@ mod certificate; use certificate::Certificate; +use super::list::{item::Value, List}; use crate::profile::Profile; use gtk::{ gio::{Cancellable, ListStore}, prelude::{ButtonExt, FileExt, WidgetExt}, Button, FileDialog, FileFilter, Window, }; -use std::{cell::RefCell, fs::File, io::Write, rc::Rc}; +use std::{fs::File, io::Write, rc::Rc}; const LABEL: &str = "Export"; const TOOLTIP_TEXT: &str = "Export selected identity to file"; const MARGIN: i32 = 8; pub struct Save { - profile_identity_gemini_id: Rc>>, pub button: Button, } @@ -22,10 +22,7 @@ impl Save { // Constructors /// Create new `Self` - pub fn new(profile: Rc) -> Self { - // Init selected option holder - let profile_identity_gemini_id = Rc::new(RefCell::new(None)); - + pub fn new(profile: Rc, list: Rc) -> Self { // Init main widget let button = Button::builder() .label(LABEL) @@ -36,17 +33,16 @@ impl Save { // Init events button.connect_clicked({ - let profile_identity_gemini_id = profile_identity_gemini_id.clone(); let button = button.clone(); move |_| { // Get selected identity from holder - match profile_identity_gemini_id.borrow().as_ref() { - Some(profile_identity_gemini_id) => { + match list.selected().value_enum() { + Value::ProfileIdentityGeminiId(profile_identity_gemini_id) => { // Lock open button (prevent double click) button.set_sensitive(false); // Create PEM file based on option ID selected - match Certificate::new(profile.clone(), *profile_identity_gemini_id) { + match Certificate::new(profile.clone(), profile_identity_gemini_id) { Ok(certificate) => { // Init file filters related with PEM extension let filters = ListStore::new::(); @@ -121,32 +117,18 @@ impl Save { } } } - None => todo!(), // unexpected + _ => todo!(), // unexpected } } }); // Return activated `Self` - Self { - profile_identity_gemini_id, - button, - } + Self { button } } // Actions - /// Update `profile_identity_gemini_id` holder, - /// toggle visibility depending on given value - pub fn update(&self, profile_identity_gemini_id: Option) { - self.button.set_visible(match profile_identity_gemini_id { - Some(value) => { - self.profile_identity_gemini_id.replace(Some(value)); - true - } - None => { - self.profile_identity_gemini_id.replace(None); - false - } - }) + pub fn set_visible(&self, is_visible: bool) { + self.button.set_visible(is_visible) } }