From 73eafa58fdaad7e46aa0a2adf8abb30335842d8d Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Thu, 29 Oct 2020 04:59:12 +0100 Subject: [PATCH] gobjectify account & link it to a database --- Cargo.lock | 3 - Cargo.toml | 3 - .../2019-09-02-103718_create_accounts/up.sql | 2 +- src/application.rs | 5 + src/main.rs | 2 - src/meson.build | 1 - src/models/account.rs | 210 ++++++++++++++++-- src/models/accounts.rs | 17 +- src/models/database.rs | 6 +- src/models/mod.rs | 4 +- src/models/object_wrapper.rs | 110 --------- src/models/provider.rs | 68 +++++- src/schema.rs | 4 +- src/widgets/accounts/add.rs | 143 +++++++----- src/widgets/accounts/list.rs | 9 +- src/widgets/accounts/row.rs | 10 +- src/widgets/providers/list.rs | 1 - 17 files changed, 359 insertions(+), 239 deletions(-) delete mode 100644 src/models/object_wrapper.rs diff --git a/Cargo.lock b/Cargo.lock index 273789b..05585bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,9 +251,6 @@ dependencies = [ "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "quick-xml 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.59 (registry+https://github.com/rust-lang/crates.io-index)", "surf 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "zbar-rust 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index ed3c0d0..fa8274d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,6 @@ lazy_static = "1.4" log = "0.4" pretty_env_logger = "0.4" quick-xml = "0.20" -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" surf = "2.1" url = "2.1" zbar-rust = "0.0" diff --git a/migrations/2019-09-02-103718_create_accounts/up.sql b/migrations/2019-09-02-103718_create_accounts/up.sql index aa4c6a6..9f22031 100644 --- a/migrations/2019-09-02-103718_create_accounts/up.sql +++ b/migrations/2019-09-02-103718_create_accounts/up.sql @@ -1,7 +1,7 @@ -- Your SQL goes here CREATE TABLE "accounts" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, - "username" VARCHAR NOT NULL, + "name" VARCHAR NOT NULL, "token_id" VARCHAR NOT NULL, "provider_id" INTEGER NOT NULL ) diff --git a/src/application.rs b/src/application.rs index 1448c5c..326ccde 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,4 +1,5 @@ use crate::config; +use crate::models::Account; use crate::widgets::{AddAccountDialog, View, Window}; use gio::prelude::*; use gtk::prelude::*; @@ -9,6 +10,7 @@ use std::{cell::RefCell, rc::Rc}; use glib::{Receiver, Sender}; pub enum Action { ViewAccounts, + AccountCreated(Account), OpenAddAccountDialog, } @@ -106,6 +108,9 @@ impl Application { dialog.widget.show(); } Action::ViewAccounts => self.window.set_view(View::Accounts), + Action::AccountCreated(account) => { + println!("{:#?}", account); + } }; glib::Continue(true) diff --git a/src/main.rs b/src/main.rs index 70f5241..4e8b9b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,6 @@ extern crate diesel; #[macro_use] extern crate glib; #[macro_use] -extern crate serde_derive; -#[macro_use] extern crate gtk_macros; use gettextrs::*; diff --git a/src/meson.build b/src/meson.build index 380e3af..cf6939c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -48,7 +48,6 @@ sources = files( 'models/database.rs', 'models/favicon.rs', 'models/mod.rs', - 'models/object_wrapper.rs', 'models/provider.rs', 'models/providers.rs', 'widgets/accounts/add.rs', diff --git a/src/models/account.rs b/src/models/account.rs index 8447dc1..db7887a 100644 --- a/src/models/account.rs +++ b/src/models/account.rs @@ -1,40 +1,212 @@ use crate::models::database; use crate::schema::accounts; use anyhow::Result; - use diesel::RunQueryDsl; - -use diesel::prelude::*; - -#[derive(Queryable, PartialEq, Debug, Clone, Serialize, Deserialize)] -pub struct Account { - pub id: i32, - pub username: String, - pub token_id: String, - pub provider: i32, -} +use glib::subclass; +use glib::subclass::prelude::*; +use glib::translate::*; +use glib::Cast; +use glib::{StaticType, ToValue}; +use std::cell::{Cell, RefCell}; #[derive(Insertable)] #[table_name = "accounts"] -pub struct NewAccount { - pub username: String, +struct NewAccount { + pub name: String, pub token_id: String, - pub provider: i32, + pub provider_id: i32, } -impl database::Insert for NewAccount { - type Error = anyhow::Error; - fn insert(&self) -> Result { +#[derive(Queryable, Hash, PartialEq, Eq, Debug, Clone)] +struct DiAccount { + pub id: i32, + pub name: String, + pub token_id: String, + pub provider_id: i32, +} + +pub struct AccountPriv { + pub id: Cell, + pub name: RefCell, + pub token_id: RefCell, + pub provider_id: Cell, +} + +static PROPERTIES: [subclass::Property; 4] = [ + subclass::Property("id", |name| { + glib::ParamSpec::int(name, "id", "Id", 0, 1000, 0, glib::ParamFlags::READWRITE) + }), + subclass::Property("name", |name| { + glib::ParamSpec::string(name, "name", "Name", None, glib::ParamFlags::READWRITE) + }), + subclass::Property("token-id", |name| { + glib::ParamSpec::string( + name, + "token-id", + "token id", + None, + glib::ParamFlags::READWRITE, + ) + }), + subclass::Property("provider-id", |name| { + glib::ParamSpec::int( + name, + "provider-id", + "Provider Id", + 0, + 1000, + 0, + glib::ParamFlags::READWRITE, + ) + }), +]; + +impl ObjectSubclass for AccountPriv { + const NAME: &'static str = "Account"; + type ParentType = glib::Object; + type Instance = subclass::simple::InstanceStruct; + type Class = subclass::simple::ClassStruct; + + glib_object_subclass!(); + + fn class_init(klass: &mut Self::Class) { + klass.install_properties(&PROPERTIES); + } + + fn new() -> Self { + Self { + id: Cell::new(0), + name: RefCell::new("".to_string()), + token_id: RefCell::new("".to_string()), + provider_id: Cell::new(0), + } + } +} + +impl ObjectImpl for AccountPriv { + fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) { + let prop = &PROPERTIES[id]; + + match *prop { + subclass::Property("id", ..) => { + let id = value + .get() + .expect("type conformity checked by `Object::set_property`") + .unwrap(); + self.id.replace(id); + } + subclass::Property("name", ..) => { + let name = value + .get() + .expect("type conformity checked by `Object::set_property`") + .unwrap(); + self.name.replace(name); + } + subclass::Property("token-id", ..) => { + let token_id = value + .get() + .expect("type conformity checked by `Object::set_property`") + .unwrap(); + self.token_id.replace(token_id); + } + subclass::Property("provider-id", ..) => { + let provider_id = value + .get() + .expect("type conformity checked by `Object::set_property`") + .unwrap(); + self.provider_id.replace(provider_id); + } + _ => unimplemented!(), + } + } + + fn get_property(&self, _obj: &glib::Object, id: usize) -> Result { + let prop = &PROPERTIES[id]; + + match *prop { + subclass::Property("id", ..) => Ok(self.id.get().to_value()), + subclass::Property("name", ..) => Ok(self.name.borrow().to_value()), + subclass::Property("token-id", ..) => Ok(self.token_id.borrow().to_value()), + subclass::Property("provider-id", ..) => Ok(self.provider_id.get().to_value()), + _ => unimplemented!(), + } + } +} +glib_wrapper! { + pub struct Account(Object, subclass::simple::ClassStruct, AccountClass>); + + match fn { + get_type => || AccountPriv::get_type().to_glib(), + } +} + +impl Account { + pub fn create(name: &str, token_id: &str, provider_id: i32) -> Result { + use diesel::{ExpressionMethods, QueryDsl}; let db = database::connection(); let conn = db.get()?; diesel::insert_into(accounts::table) - .values(self) + .values(NewAccount { + name: name.to_string(), + token_id: token_id.to_string(), + provider_id, + }) .execute(&conn)?; accounts::table .order(accounts::columns::id.desc()) - .first::(&conn) + .first::(&conn) .map_err(From::from) + .map(From::from) + } + + pub fn load() -> Result> { + use crate::schema::accounts::dsl::*; + let db = database::connection(); + let conn = db.get()?; + + let results = accounts + .load::(&conn)? + .into_iter() + .map(From::from) + .collect::>(); + Ok(results) + } + + pub fn new(id: i32, name: &str, token_id: &str, provider_id: i32) -> Account { + glib::Object::new( + Account::static_type(), + &[ + ("id", &id), + ("name", &name), + ("token-id", &token_id), + ("provider-id", &provider_id), + ], + ) + .expect("Failed to create account") + .downcast() + .expect("Created account is of wrong type") + } + + pub fn id(&self) -> i32 { + let priv_ = AccountPriv::from_instance(self); + priv_.id.get() + } + + pub fn name(&self) -> String { + let priv_ = AccountPriv::from_instance(self); + priv_.name.borrow().clone() + } +} + +impl From for Account { + fn from(account: DiAccount) -> Self { + Self::new( + account.id, + &account.name, + &account.token_id, + account.provider_id, + ) } } diff --git a/src/models/accounts.rs b/src/models/accounts.rs index d6f1de6..2263270 100644 --- a/src/models/accounts.rs +++ b/src/models/accounts.rs @@ -1,6 +1,4 @@ use super::account::Account; -use super::database; -use super::object_wrapper::ObjectWrapper; use super::provider::Provider; use gio::prelude::*; use std::cell::RefCell; @@ -36,7 +34,7 @@ pub struct AccountsModel { impl AccountsModel { pub fn from_provider(provider: &Provider) -> Self { - let gio_model = gio::ListStore::new(ObjectWrapper::static_type()); + let gio_model = gio::ListStore::new(Account::static_type()); let model = Self { model: gio_model, sort_order: RefCell::new(SortOrder::Desc), @@ -49,18 +47,17 @@ impl AccountsModel { fn init(&self) { // fill in the accounts from the database - let accounts = database::get_accounts_by_provider(self.provider.clone()).unwrap(); + /*let accounts = database::get_accounts_by_provider(self.provider.clone()).unwrap(); for account in accounts.into_iter() { self.add_account(&account); - } + }*/ } fn add_account(&self, account: &Account) { - let object = ObjectWrapper::new(Box::new(account)); let sort_by = self.sort_by.clone(); let sort_order = self.sort_order.clone(); - self.model.insert_sorted(&object, move |a, b| { + self.model.insert_sorted(account, move |a, b| { Self::accounts_cmp(a, b, sort_by.borrow().clone(), sort_order.borrow().clone()) }); } @@ -95,8 +92,8 @@ impl AccountsModel { sort_by: SortBy, sort_order: SortOrder, ) -> std::cmp::Ordering { - let mut account_a: Account = a.downcast_ref::().unwrap().deserialize(); - let mut account_b: Account = b.downcast_ref::().unwrap().deserialize(); + let mut account_a: &Account = a.downcast_ref::().unwrap(); + let mut account_b: &Account = b.downcast_ref::().unwrap(); if sort_order == SortOrder::Desc { let tmp = account_a; @@ -107,6 +104,6 @@ impl AccountsModel { SortBy::Name => account_a.get_title().cmp(&account_b.get_title()), SortBy::Date => account_a.get_created_at().cmp(&account_b.get_created_at()), }*/ - account_a.username.cmp(&account_b.username) + account_a.name().cmp(&account_b.name()) } } diff --git a/src/models/database.rs b/src/models/database.rs index c35fa19..811ab2f 100644 --- a/src/models/database.rs +++ b/src/models/database.rs @@ -1,4 +1,3 @@ -use crate::models::{Account, Provider}; use anyhow::Result; use diesel::prelude::*; use diesel::r2d2; @@ -47,14 +46,14 @@ pub trait Insert { fn insert(&self) -> Result; } - +/* pub fn get_accounts_by_provider(provider_model: Provider) -> Result> { use crate::schema::accounts::dsl::*; let db = connection(); let conn = db.get()?; accounts - .filter(provider.eq(provider_model.id())) + .filter(provider_id.eq(provider_model.id())) .load::(&conn) .map_err(From::from) } @@ -66,3 +65,4 @@ pub fn get_accounts() -> Result> { accounts.load::(&conn).map_err(From::from) } +*/ diff --git a/src/models/mod.rs b/src/models/mod.rs index 72ba79a..09366b2 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -3,14 +3,12 @@ mod accounts; mod algorithm; pub mod database; mod favicon; -mod object_wrapper; mod provider; mod providers; -pub use self::account::{Account, NewAccount}; +pub use self::account::Account; pub use self::accounts::AccountsModel; pub use self::algorithm::Algorithm; pub use self::favicon::{FaviconError, FaviconScrapper}; -pub use self::object_wrapper::ObjectWrapper; pub use self::provider::Provider; pub use self::providers::ProvidersModel; diff --git a/src/models/object_wrapper.rs b/src/models/object_wrapper.rs deleted file mode 100644 index 46a6efc..0000000 --- a/src/models/object_wrapper.rs +++ /dev/null @@ -1,110 +0,0 @@ -// ObjectWrapper is a GObject subclass, which we need to carry the rustio::Station/song::Song struct. -// With this we can use gtk::ListBox bind_model() properly. -// -// For more details, you should look at this gtk-rs example: -// https://github.com/gtk-rs/examples/blob/master/src/bin/listbox_model.rs -// Source https://gitlab.gnome.org/World/Shortwave/blob/master/src/model/object_wrapper.rs - -use gtk::prelude::*; -use serde::de::DeserializeOwned; - -use glib::subclass; -use glib::subclass::prelude::*; -use glib::translate::*; - -mod imp { - use super::*; - use std::cell::RefCell; - - pub struct ObjectWrapper { - data: RefCell>, - } - - static PROPERTIES: [subclass::Property; 1] = [subclass::Property("data", |name| { - glib::ParamSpec::string( - name, - "Data", - "Data", - None, // Default value - glib::ParamFlags::READWRITE, - ) - })]; - - impl ObjectSubclass for ObjectWrapper { - const NAME: &'static str = "ObjectWrapper"; - type ParentType = glib::Object; - type Instance = subclass::simple::InstanceStruct; - type Class = subclass::simple::ClassStruct; - - glib_object_subclass!(); - - fn class_init(klass: &mut Self::Class) { - klass.install_properties(&PROPERTIES); - } - - fn new() -> Self { - Self { - data: RefCell::new(None), - } - } - } - - impl ObjectImpl for ObjectWrapper { - fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("data", ..) => { - let data = value.get().unwrap(); - self.data.replace(data); - } - _ => unimplemented!(), - } - } - - fn get_property(&self, _obj: &glib::Object, id: usize) -> Result { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("data", ..) => Ok(self.data.borrow().to_value()), - _ => unimplemented!(), - } - } - } -} - -glib_wrapper! { - pub struct ObjectWrapper(Object, subclass::simple::ClassStruct, ObjectWrapperClass>); - - match fn { - get_type => || imp::ObjectWrapper::get_type().to_glib(), - } -} - -impl ObjectWrapper { - pub fn new(object: O) -> ObjectWrapper - where - O: serde::ser::Serialize, - { - glib::Object::new( - Self::static_type(), - &[("data", &serde_json::to_string(&object).unwrap())], - ) - .unwrap() - .downcast() - .unwrap() - } - - pub fn deserialize(&self) -> O - where - O: DeserializeOwned, - { - let data = self - .get_property("data") - .unwrap() - .get::() - .unwrap() - .unwrap(); - serde_json::from_str(&data).unwrap() - } -} diff --git a/src/models/provider.rs b/src/models/provider.rs index da774c4..43eacd3 100644 --- a/src/models/provider.rs +++ b/src/models/provider.rs @@ -1,6 +1,7 @@ use super::algorithm::Algorithm; use crate::models::database; use crate::models::{FaviconError, FaviconScrapper}; +use crate::schema::providers; use anyhow::Result; use diesel::RunQueryDsl; use gio::FileExt; @@ -14,7 +15,18 @@ use std::str::FromStr; use std::string::ToString; use url::Url; -#[derive(Queryable, Hash, PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] +#[derive(Insertable)] +#[table_name = "providers"] +struct NewProvider { + pub name: String, + pub period: i32, + pub algorithm: String, + pub website: Option, + pub help_url: Option, + pub image_uri: Option, +} + +#[derive(Queryable, Hash, PartialEq, Eq, Debug, Clone)] struct DiProvider { pub id: i32, pub name: String, @@ -194,6 +206,34 @@ glib_wrapper! { } impl Provider { + pub fn create( + name: &str, + period: i32, + algorithm: Algorithm, + website: Option, + ) -> Result { + use crate::diesel::{ExpressionMethods, QueryDsl}; + let db = database::connection(); + let conn = db.get()?; + + diesel::insert_into(providers::table) + .values(NewProvider { + name: name.to_string(), + period, + algorithm: algorithm.to_string(), + website, + help_url: None, + image_uri: None, + }) + .execute(&conn)?; + + providers::table + .order(providers::columns::id.desc()) + .first::(&conn) + .map_err(From::from) + .map(From::from) + } + pub fn load() -> Result> { use crate::schema::providers::dsl::*; let db = database::connection(); @@ -202,17 +242,7 @@ impl Provider { let results = providers .load::(&conn)? .into_iter() - .map(|p| { - Self::new( - p.id, - &p.name, - p.period, - Algorithm::from_str(&p.algorithm).unwrap(), - p.website, - p.help_url, - p.image_uri, - ) - }) + .map(From::from) .collect::>(); Ok(results) } @@ -304,3 +334,17 @@ impl Provider { priv_.image_uri.borrow().clone() } } + +impl From for Provider { + fn from(p: DiProvider) -> Self { + Self::new( + p.id, + &p.name, + p.period, + Algorithm::from_str(&p.algorithm).unwrap(), + p.website, + p.help_url, + p.image_uri, + ) + } +} diff --git a/src/schema.rs b/src/schema.rs index f894aa2..b86566b 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,9 +1,9 @@ table! { accounts (id) { id -> Integer, - username -> Text, + name -> Text, token_id -> Text, - provider -> Integer, + provider_id -> Integer, } } diff --git a/src/widgets/accounts/add.rs b/src/widgets/accounts/add.rs index e83d989..cac8b59 100644 --- a/src/widgets/accounts/add.rs +++ b/src/widgets/accounts/add.rs @@ -1,9 +1,7 @@ use crate::application::Action; use crate::helpers::qrcode; -use crate::models::database::*; -use crate::models::{Account, Algorithm, NewAccount, Provider, ProvidersModel}; +use crate::models::{Account, Algorithm, Provider, ProvidersModel}; use anyhow::Result; -use futures::future::FutureExt; use gio::prelude::*; use glib::StaticType; use glib::{signal::Inhibit, Receiver, Sender}; @@ -14,6 +12,8 @@ use std::rc::Rc; pub enum AddAccountAction { SetIcon(gio::File), + Save, + ScanQR, } pub struct AddAccountDialog { @@ -24,6 +24,7 @@ pub struct AddAccountDialog { receiver: RefCell>>, model: Rc, selected_provider: Rc>>, + actions: gio::SimpleActionGroup, } impl AddAccountDialog { @@ -33,6 +34,7 @@ impl AddAccountDialog { let (sender, r) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let receiver = RefCell::new(Some(r)); + let actions = gio::SimpleActionGroup::new(); let add_account_dialog = Rc::new(Self { widget: add_dialog, @@ -40,40 +42,88 @@ impl AddAccountDialog { global_sender, sender, receiver, + actions, model: Rc::new(ProvidersModel::new()), selected_provider: Rc::new(RefCell::new(None)), }); - add_account_dialog.setup_actions(add_account_dialog.clone()); + add_account_dialog.setup_actions(); add_account_dialog.setup_signals(); add_account_dialog.setup_widgets(add_account_dialog.clone()); add_account_dialog } - fn add_account(&self, account: NewAccount) -> Result { - // TODO: add the account to the provider model. - account.insert() - } - fn setup_signals(&self) { get_widget!(self.builder, gtk::Entry, username_entry); get_widget!(self.builder, gtk::Entry, token_entry); - //let action_group = self.widget.get_action_group("add").unwrap().downcast::().unwrap(); - //let save_action = action_group.lookup_action("save").unwrap().downcast::().unwrap(); - - let validate_entries = clone!(@weak username_entry, @weak token_entry => move |_: >k::Entry| { + let validate_entries = clone!(@weak username_entry, @weak token_entry, @strong self.actions as actions => move |_: >k::Entry| { let username = username_entry.get_text().unwrap(); let token = token_entry.get_text().unwrap(); let is_valid = !(username.is_empty() || token.is_empty()); - //save_action.set_enabled(is_valid); + get_action!(actions, @save).set_enabled(is_valid); + }); username_entry.connect_changed(validate_entries.clone()); token_entry.connect_changed(validate_entries); } + fn scan_qr(&self) { + qrcode::screenshot_area( + clone!(@strong self.builder as builder, @strong self.model as model => move |screenshot| { + if let Ok(otpauth) = qrcode::scan(&gio::File::new_for_uri(&screenshot)) { + get_widget!(builder, gtk::Entry, @token_entry).set_text(&otpauth.token); + if let Some(ref username) = otpauth.account { + get_widget!(builder, gtk::Entry, @username_entry).set_text(&username); + } + if let Some(ref provider) = otpauth.issuer { + let provider = model.find_by_name(provider).unwrap(); + //dialog.set_provider(provider); + } + } + }), + ); + } + + fn save(&self) -> Result<()> { + let provider = match self.selected_provider.borrow().clone() { + Some(p) => p, + None => { + let provider_website = + get_widget!(self.builder, gtk::Entry, @provider_website_entry).get_text(); + let provider_name = get_widget!(self.builder, gtk::Entry, @provider_entry) + .get_text() + .unwrap(); + let period = get_widget!(self.builder, gtk::SpinButton, @period_spinbutton) + .get_value() as i32; + + let selected_alg = + get_widget!(self.builder, libhandy::ComboRow, @algorithm_comborow) + .get_selected(); + let algorithm: Algorithm = unsafe { std::mem::transmute(selected_alg) }; + Provider::create( + &provider_name, + period, + algorithm, + provider_website.map(|w| w.to_string()), + ) + .unwrap() + } + }; + let username = get_widget!(self.builder, gtk::Entry, @username_entry) + .get_text() + .unwrap(); + let token = get_widget!(self.builder, gtk::Entry, @token_entry) + .get_text() + .unwrap(); + + let account = Account::create(&username, &token, provider.id())?; + send!(self.global_sender, Action::AccountCreated(account)); + Ok(()) + } + fn set_provider(&self, provider: Provider) { get_widget!(self.builder, gtk::Entry, @provider_entry).set_text(&provider.name()); get_widget!(self.builder, gtk::SpinButton, @period_spinbutton) @@ -106,61 +156,32 @@ impl AddAccountDialog { self.selected_provider.replace(Some(provider)); } - fn setup_actions(&self, dialog: Rc) { - let actions = gio::SimpleActionGroup::new(); + fn setup_actions(&self) { action!( - actions, + self.actions, "back", clone!(@weak self.widget as dialog => move |_, _| { dialog.destroy(); }) ); - let save = gio::SimpleAction::new("save", None); - save.connect_activate(clone!(@strong self.builder as builder => move |_, _| { - get_widget!(builder, gtk::Entry, username_entry); - get_widget!(builder, gtk::Entry, token_entry); - get_widget!(builder, gtk::Entry, provider_entry); - get_widget!(builder, gtk::Entry, website_entry); - // get_widget!(builder, gtk::Entry, period_entry); - // get_widget!(builder, gtk::Entry, algorithm_model); - - /* - let new_account = NewAccount { - username: username_entry.get_text().unwrap().to_string(), - token_id: token_entry.get_text().unwrap().to_string(), - provider: provider_combobox.get_active_id().unwrap().parse::().unwrap(), - }; - if let Err(err) = add_account_dialog.add_account(new_account) { - add_account_dialog.notify_err("Failed to add a new account"); - } else { - // Close the dialog if everything is fine. - add_account_dialog.widget.destroy(); - } - */ - })); - save.set_enabled(false); - actions.add_action(&save); - action!( - actions, - "scan-qr", - clone!(@strong self.builder as builder, @strong dialog, @strong self.model as model => move |_, _| { - qrcode::screenshot_area(clone!(@strong builder, @strong dialog, @strong model => move |screenshot| { - if let Ok(otpauth) = qrcode::scan(&gio::File::new_for_uri(&screenshot)) { - get_widget!(builder, gtk::Entry, @token_entry).set_text(&otpauth.token); - if let Some(ref username) = otpauth.account { - get_widget!(builder, gtk::Entry, @username_entry).set_text(&username); - } - if let Some(ref provider) = otpauth.issuer { - let provider = model.find_by_name(provider). unwrap(); - dialog.set_provider(provider); - } - } - })); + self.actions, + "save", + clone!(@strong self.sender as sender => move |_, _| { + send!(sender, AddAccountAction::Save); }) ); - self.widget.insert_action_group("add", Some(&actions)); + + action!( + self.actions, + "scan-qr", + clone!(@strong self.sender as sender => move |_, _| { + send!(sender, AddAccountAction::ScanQR); + }) + ); + self.widget.insert_action_group("add", Some(&self.actions)); + get_action!(self.actions, @save).set_enabled(false); } fn setup_widgets(&self, dialog: Rc) { @@ -209,6 +230,10 @@ impl AddAccountDialog { get_widget!(self.builder, gtk::Spinner, @spinner).stop(); get_widget!(self.builder, gtk::Stack, @image_stack).set_visible_child_name("image"); } + AddAccountAction::Save => { + self.save().unwrap(); + } + AddAccountAction::ScanQR => self.scan_qr(), }; glib::Continue(true) } diff --git a/src/widgets/accounts/list.rs b/src/widgets/accounts/list.rs index 897d8b7..961330a 100644 --- a/src/widgets/accounts/list.rs +++ b/src/widgets/accounts/list.rs @@ -3,7 +3,7 @@ use gtk::prelude::*; use glib::Sender; use crate::application::Action; -use crate::models::{Account, AccountsModel, ObjectWrapper, Provider}; +use crate::models::{Account, AccountsModel, Provider}; use crate::widgets::accounts::AccountRow; pub struct AccountsList<'a> { @@ -43,10 +43,9 @@ impl<'a> AccountsList<'a> { Some(&self.model.model), Some(Box::new( clone!(@strong self.sender as sender => move |account: &glib::Object| { - let account: Account = account - .downcast_ref::() - .unwrap() - .deserialize(); + let account: &Account = account + .downcast_ref::() + .unwrap(); let row = AccountRow::new(account, sender.clone()); /*row.set_on_click_callback(move |_, _| { // sender.send(Action::LoadChapter(chapter.clone())).unwrap(); diff --git a/src/widgets/accounts/row.rs b/src/widgets/accounts/row.rs index 77d654a..fbffbaf 100644 --- a/src/widgets/accounts/row.rs +++ b/src/widgets/accounts/row.rs @@ -3,15 +3,15 @@ use crate::models::Account; use glib::Sender; use gtk::prelude::*; -pub struct AccountRow { +pub struct AccountRow<'a> { pub widget: gtk::ListBoxRow, builder: gtk::Builder, sender: Sender, - account: Account, + account: &'a Account, } -impl AccountRow { - pub fn new(account: Account, sender: Sender) -> Self { +impl<'a> AccountRow<'a> { + pub fn new(account: &'a Account, sender: Sender) -> Self { let builder = gtk::Builder::from_resource("/com/belmoussaoui/Authenticator/account_row.ui"); get_widget!(builder, gtk::ListBoxRow, account_row); @@ -27,6 +27,6 @@ impl AccountRow { fn init(&self) { get_widget!(self.builder, gtk::Label, username_label); - username_label.set_text(&self.account.username); + username_label.set_text(&self.account.name()); } } diff --git a/src/widgets/providers/list.rs b/src/widgets/providers/list.rs index 2483d56..56fe9d7 100644 --- a/src/widgets/providers/list.rs +++ b/src/widgets/providers/list.rs @@ -4,7 +4,6 @@ use std::cell::RefCell; use crate::application::Action; use crate::models::ProvidersModel; -use crate::widgets::AccountsList; pub struct ProvidersList { pub widget: gtk::Box,