mirror of
https://gitlab.gnome.org/World/Authenticator.git
synced 2025-03-04 00:34:40 +01:00
gobjectify account & link it to a database
This commit is contained in:
parent
06457fe63a
commit
73eafa58fd
17 changed files with 359 additions and 239 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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<Account> for NewAccount {
|
||||
type Error = anyhow::Error;
|
||||
fn insert(&self) -> Result<Account> {
|
||||
#[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<i32>,
|
||||
pub name: RefCell<String>,
|
||||
pub token_id: RefCell<String>,
|
||||
pub provider_id: Cell<i32>,
|
||||
}
|
||||
|
||||
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<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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<glib::Value, ()> {
|
||||
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::InstanceStruct<AccountPriv>, subclass::simple::ClassStruct<AccountPriv>, AccountClass>);
|
||||
|
||||
match fn {
|
||||
get_type => || AccountPriv::get_type().to_glib(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Account {
|
||||
pub fn create(name: &str, token_id: &str, provider_id: i32) -> Result<Account> {
|
||||
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::<Account>(&conn)
|
||||
.first::<DiAccount>(&conn)
|
||||
.map_err(From::from)
|
||||
.map(From::from)
|
||||
}
|
||||
|
||||
pub fn load() -> Result<Vec<Self>> {
|
||||
use crate::schema::accounts::dsl::*;
|
||||
let db = database::connection();
|
||||
let conn = db.get()?;
|
||||
|
||||
let results = accounts
|
||||
.load::<DiAccount>(&conn)?
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Account>>();
|
||||
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<DiAccount> for Account {
|
||||
fn from(account: DiAccount) -> Self {
|
||||
Self::new(
|
||||
account.id,
|
||||
&account.name,
|
||||
&account.token_id,
|
||||
account.provider_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::<ObjectWrapper>().unwrap().deserialize();
|
||||
let mut account_b: Account = b.downcast_ref::<ObjectWrapper>().unwrap().deserialize();
|
||||
let mut account_a: &Account = a.downcast_ref::<Account>().unwrap();
|
||||
let mut account_b: &Account = b.downcast_ref::<Account>().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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T> {
|
|||
|
||||
fn insert(&self) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn get_accounts_by_provider(provider_model: Provider) -> Result<Vec<Account>> {
|
||||
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::<Account>(&conn)
|
||||
.map_err(From::from)
|
||||
}
|
||||
|
@ -66,3 +65,4 @@ pub fn get_accounts() -> Result<Vec<Account>> {
|
|||
|
||||
accounts.load::<Account>(&conn).map_err(From::from)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Option<String>>,
|
||||
}
|
||||
|
||||
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<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
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<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
subclass::Property("data", ..) => Ok(self.data.borrow().to_value()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib_wrapper! {
|
||||
pub struct ObjectWrapper(Object<subclass::simple::InstanceStruct<imp::ObjectWrapper>, subclass::simple::ClassStruct<imp::ObjectWrapper>, ObjectWrapperClass>);
|
||||
|
||||
match fn {
|
||||
get_type => || imp::ObjectWrapper::get_type().to_glib(),
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectWrapper {
|
||||
pub fn new<O>(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<O>(&self) -> O
|
||||
where
|
||||
O: DeserializeOwned,
|
||||
{
|
||||
let data = self
|
||||
.get_property("data")
|
||||
.unwrap()
|
||||
.get::<String>()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
serde_json::from_str(&data).unwrap()
|
||||
}
|
||||
}
|
|
@ -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<String>,
|
||||
pub help_url: Option<String>,
|
||||
pub image_uri: Option<String>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
) -> Result<Self> {
|
||||
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::<DiProvider>(&conn)
|
||||
.map_err(From::from)
|
||||
.map(From::from)
|
||||
}
|
||||
|
||||
pub fn load() -> Result<Vec<Self>> {
|
||||
use crate::schema::providers::dsl::*;
|
||||
let db = database::connection();
|
||||
|
@ -202,17 +242,7 @@ impl Provider {
|
|||
let results = providers
|
||||
.load::<DiProvider>(&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::<Vec<Provider>>();
|
||||
Ok(results)
|
||||
}
|
||||
|
@ -304,3 +334,17 @@ impl Provider {
|
|||
priv_.image_uri.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DiProvider> 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
table! {
|
||||
accounts (id) {
|
||||
id -> Integer,
|
||||
username -> Text,
|
||||
name -> Text,
|
||||
token_id -> Text,
|
||||
provider -> Integer,
|
||||
provider_id -> Integer,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Option<Receiver<AddAccountAction>>>,
|
||||
model: Rc<ProvidersModel>,
|
||||
selected_provider: Rc<RefCell<Option<Provider>>>,
|
||||
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<Account> {
|
||||
// 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::<gio::SimpleActionGroup>().unwrap();
|
||||
//let save_action = action_group.lookup_action("save").unwrap().downcast::<gio::SimpleAction>().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<Self>) {
|
||||
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::<i32>().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<Self>) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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::<ObjectWrapper>()
|
||||
.unwrap()
|
||||
.deserialize();
|
||||
let account: &Account = account
|
||||
.downcast_ref::<Account>()
|
||||
.unwrap();
|
||||
let row = AccountRow::new(account, sender.clone());
|
||||
/*row.set_on_click_callback(move |_, _| {
|
||||
// sender.send(Action::LoadChapter(chapter.clone())).unwrap();
|
||||
|
|
|
@ -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<Action>,
|
||||
account: Account,
|
||||
account: &'a Account,
|
||||
}
|
||||
|
||||
impl AccountRow {
|
||||
pub fn new(account: Account, sender: Sender<Action>) -> Self {
|
||||
impl<'a> AccountRow<'a> {
|
||||
pub fn new(account: &'a Account, sender: Sender<Action>) -> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue