mirror of
https://gitlab.gnome.org/World/Authenticator.git
synced 2025-03-04 00:34:40 +01:00
gobjectify provider
This commit is contained in:
parent
7db25ac562
commit
46d0902a36
18 changed files with 435 additions and 99 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -63,9 +63,9 @@ dependencies = [
|
|||
name = "authenticator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"diesel_migrations 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gdk-pixbuf 0.9.0 (git+https://github.com/gtk-rs/gdk-pixbuf)",
|
||||
"gdk4 0.1.0 (git+https://github.com/gtk-rs/gdk4)",
|
||||
"gettext-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -8,7 +8,7 @@ log = "0.4"
|
|||
gettext-rs= { version = "0.5", features = ["gettext-system"] }
|
||||
pretty_env_logger = "0.3"
|
||||
lazy_static = "1.3"
|
||||
failure = "0.1"
|
||||
anyhow = "1.0"
|
||||
diesel = { version = "1.4", features = ["sqlite", "r2d2"] }
|
||||
diesel_migrations = { version = "1.4" , features = ["sqlite"] }
|
||||
serde = "1.0"
|
||||
|
|
|
@ -149,6 +149,9 @@
|
|||
<child>
|
||||
<object class="HdyComboRow" id="algorithm_comborow">
|
||||
<property name="title" translatable="yes">Algorithm</property>
|
||||
<property name="expression">
|
||||
<lookup type="HdyEnumValueObject" name="name"/>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Your SQL goes here
|
||||
CREATE TABLE "accounts" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"username" VARCHAR NOT NULL,
|
||||
"token_id" VARCHAR NOT NULL,
|
||||
"provider" INTEGER NOT NULL)
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"username" VARCHAR NOT NULL,
|
||||
"token_id" VARCHAR NOT NULL,
|
||||
"provider_id" INTEGER NOT NULL
|
||||
)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
-- Your SQL goes here
|
||||
CREATE TABLE "providers" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"name" VARCHAR NOT NULL,
|
||||
"website" VARCHAR NULL,
|
||||
"help_url" VARCHAR NULL,
|
||||
"image_uri" VARCHAR NULL
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"name" VARCHAR NOT NULL,
|
||||
"website" VARCHAR NULL,
|
||||
"help_url" VARCHAR NULL,
|
||||
"image_uri" VARCHAR NULL,
|
||||
"period" INTEGER NULL DEFAULT 30,
|
||||
"algorithm" VARCHAR DEFAULT "OTP"
|
||||
);
|
||||
|
|
0
src/helpers/qrcode.rs
Normal file
0
src/helpers/qrcode.rs
Normal file
|
@ -1,8 +1,6 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate diesel_migrations;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use crate::schema::accounts;
|
||||
|
||||
use crate::models::database;
|
||||
use crate::schema::accounts;
|
||||
use anyhow::Result;
|
||||
|
||||
use diesel::RunQueryDsl;
|
||||
pub use failure::Error;
|
||||
|
||||
use diesel::prelude::*;
|
||||
|
||||
|
@ -24,8 +23,8 @@ pub struct NewAccount {
|
|||
}
|
||||
|
||||
impl database::Insert<Account> for NewAccount {
|
||||
type Error = database::Error;
|
||||
fn insert(&self) -> Result<Account, database::Error> {
|
||||
type Error = anyhow::Error;
|
||||
fn insert(&self) -> Result<Account> {
|
||||
let db = database::connection();
|
||||
let conn = db.get()?;
|
||||
|
||||
|
|
38
src/models/algorithm.rs
Normal file
38
src/models/algorithm.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
use glib::StaticType;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]
|
||||
#[repr(u32)]
|
||||
#[genum(type_name = "ProviderAlgorithm")]
|
||||
pub enum Algorithm {
|
||||
#[genum(name = "OTP")]
|
||||
OTP = 0,
|
||||
#[genum(name = "HOTP")]
|
||||
HOTP = 1,
|
||||
#[genum(name = "Steam")]
|
||||
Steam = 2,
|
||||
}
|
||||
|
||||
impl FromStr for Algorithm {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_ref() {
|
||||
"otp" => Ok(Self::OTP),
|
||||
"hotp" => Ok(Self::HOTP),
|
||||
"steam" => Ok(Self::Steam),
|
||||
_ => anyhow::bail!("Unsupported algorithm"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Algorithm {
|
||||
fn to_string(&self) -> String {
|
||||
match *self {
|
||||
Algorithm::OTP => "otp",
|
||||
Algorithm::HOTP => "hotp",
|
||||
Algorithm::Steam => "steam",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
use crate::models::{Account, Provider};
|
||||
|
||||
use anyhow::Result;
|
||||
use diesel::prelude::*;
|
||||
use diesel::r2d2;
|
||||
use diesel::r2d2::ConnectionManager;
|
||||
pub use failure::Error;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, fs::File};
|
||||
|
||||
|
@ -20,15 +19,15 @@ pub(crate) fn connection() -> Pool {
|
|||
POOL.clone()
|
||||
}
|
||||
|
||||
fn run_migration_on(connection: &SqliteConnection) -> Result<(), Error> {
|
||||
fn run_migration_on(connection: &SqliteConnection) -> Result<()> {
|
||||
info!("Running DB Migrations...");
|
||||
embedded_migrations::run_with_output(connection, &mut std::io::stdout()).map_err(From::from)
|
||||
}
|
||||
|
||||
fn init_pool() -> Result<Pool, Error> {
|
||||
fn init_pool() -> Result<Pool> {
|
||||
let db_path = &DB_PATH;
|
||||
fs::create_dir_all(&db_path.to_str().unwrap())?;
|
||||
let db_path = db_path.join("library.db");
|
||||
let db_path = db_path.join("authenticator.db");
|
||||
if !db_path.exists() {
|
||||
File::create(&db_path.to_str().unwrap())?;
|
||||
}
|
||||
|
@ -49,29 +48,21 @@ pub trait Insert<T> {
|
|||
fn insert(&self) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
pub fn get_accounts_by_provider(provider_model: Provider) -> Result<Vec<Account>, 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.eq(provider_model.id()))
|
||||
.load::<Account>(&conn)
|
||||
.map_err(From::from)
|
||||
}
|
||||
|
||||
pub fn get_accounts() -> Result<Vec<Account>, Error> {
|
||||
pub fn get_accounts() -> Result<Vec<Account>> {
|
||||
use crate::schema::accounts::dsl::*;
|
||||
let db = connection();
|
||||
let conn = db.get()?;
|
||||
|
||||
accounts.load::<Account>(&conn).map_err(From::from)
|
||||
}
|
||||
|
||||
pub fn get_providers() -> Result<Vec<Provider>, Error> {
|
||||
use crate::schema::providers::dsl::*;
|
||||
let db = connection();
|
||||
let conn = db.get()?;
|
||||
|
||||
providers.load::<Provider>(&conn).map_err(From::from)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod account;
|
||||
mod accounts;
|
||||
mod algorithm;
|
||||
pub mod database;
|
||||
mod object_wrapper;
|
||||
mod provider;
|
||||
|
@ -7,6 +8,7 @@ mod providers;
|
|||
|
||||
pub use self::account::{Account, NewAccount};
|
||||
pub use self::accounts::AccountsModel;
|
||||
pub use self::algorithm::Algorithm;
|
||||
pub use self::object_wrapper::ObjectWrapper;
|
||||
pub use self::provider::{NewProvider, Provider};
|
||||
pub use self::provider::Provider;
|
||||
pub use self::providers::ProvidersModel;
|
||||
|
|
|
@ -1,43 +1,277 @@
|
|||
use crate::schema::providers;
|
||||
|
||||
use super::algorithm::Algorithm;
|
||||
use crate::models::database;
|
||||
|
||||
use anyhow::Result;
|
||||
use diesel::RunQueryDsl;
|
||||
pub use failure::Error;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::translate::*;
|
||||
use glib::Cast;
|
||||
use glib::{StaticType, ToValue};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
#[derive(Queryable, Hash, PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Provider {
|
||||
struct DiProvider {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub website: String,
|
||||
pub help_url: String,
|
||||
pub image_uri: String,
|
||||
pub period: i32,
|
||||
pub algorithm: String,
|
||||
pub website: Option<String>,
|
||||
pub help_url: Option<String>,
|
||||
pub image_uri: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name = "providers"]
|
||||
pub struct NewProvider {
|
||||
pub name: String,
|
||||
pub website: String,
|
||||
pub help_url: String,
|
||||
pub image_uri: String,
|
||||
pub struct ProviderPriv {
|
||||
pub id: Cell<i32>,
|
||||
pub name: RefCell<String>,
|
||||
pub period: Cell<i32>,
|
||||
pub algorithm: RefCell<String>,
|
||||
pub website: RefCell<Option<String>>,
|
||||
pub help_url: RefCell<Option<String>>,
|
||||
pub image_uri: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
impl database::Insert<Provider> for NewProvider {
|
||||
type Error = database::Error;
|
||||
fn insert(&self) -> Result<Provider, database::Error> {
|
||||
static PROPERTIES: [subclass::Property; 7] = [
|
||||
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("period", |name| {
|
||||
glib::ParamSpec::int(
|
||||
name,
|
||||
"period",
|
||||
"Period",
|
||||
0,
|
||||
1000,
|
||||
30,
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
subclass::Property("algorithm", |name| {
|
||||
glib::ParamSpec::string(
|
||||
name,
|
||||
"algorithm",
|
||||
"Algorithm",
|
||||
Some(&Algorithm::OTP.to_string()),
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
subclass::Property("website", |name| {
|
||||
glib::ParamSpec::string(
|
||||
name,
|
||||
"website",
|
||||
"Website",
|
||||
None,
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
subclass::Property("help-url", |name| {
|
||||
glib::ParamSpec::string(
|
||||
name,
|
||||
"help url",
|
||||
"Help URL",
|
||||
None,
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
subclass::Property("image-uri", |name| {
|
||||
glib::ParamSpec::string(
|
||||
name,
|
||||
"image uri",
|
||||
"Image URI",
|
||||
None,
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
];
|
||||
|
||||
impl ObjectSubclass for ProviderPriv {
|
||||
const NAME: &'static str = "Provider";
|
||||
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()),
|
||||
website: RefCell::new(None),
|
||||
help_url: RefCell::new(None),
|
||||
image_uri: RefCell::new(None),
|
||||
algorithm: RefCell::new(Algorithm::OTP.to_string()),
|
||||
period: Cell::new(30),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for ProviderPriv {
|
||||
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("period", ..) => {
|
||||
let period = value
|
||||
.get_some()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
self.period.replace(period);
|
||||
}
|
||||
subclass::Property("algorithm", ..) => {
|
||||
let algorithm = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`")
|
||||
.unwrap();
|
||||
self.algorithm.replace(algorithm);
|
||||
}
|
||||
subclass::Property("website", ..) => {
|
||||
let website = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
self.website.replace(website);
|
||||
}
|
||||
subclass::Property("help-url", ..) => {
|
||||
let help_url = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
self.help_url.replace(help_url);
|
||||
}
|
||||
subclass::Property("image-uri", ..) => {
|
||||
let image_uri = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
self.image_uri.replace(image_uri);
|
||||
}
|
||||
_ => 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("period", ..) => Ok(self.period.get().to_value()),
|
||||
subclass::Property("algorithm", ..) => Ok(self.algorithm.borrow().to_value()),
|
||||
subclass::Property("website", ..) => Ok(self.website.borrow().to_value()),
|
||||
subclass::Property("help-url", ..) => Ok(self.help_url.borrow().to_value()),
|
||||
subclass::Property("image-uri", ..) => Ok(self.image_uri.borrow().to_value()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
glib_wrapper! {
|
||||
pub struct Provider(Object<subclass::simple::InstanceStruct<ProviderPriv>, subclass::simple::ClassStruct<ProviderPriv>, ProviderClass>);
|
||||
|
||||
match fn {
|
||||
get_type => || ProviderPriv::get_type().to_glib(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Provider {
|
||||
pub fn load() -> Result<Vec<Self>> {
|
||||
use crate::schema::providers::dsl::*;
|
||||
let db = database::connection();
|
||||
let conn = db.get()?;
|
||||
|
||||
diesel::insert_into(providers::table)
|
||||
.values(self)
|
||||
.execute(&conn)?;
|
||||
let results = providers
|
||||
.load::<DiProvider>(&conn)?
|
||||
.into_iter()
|
||||
.map(|p| {
|
||||
Self::new(
|
||||
p.id,
|
||||
&p.name,
|
||||
p.website,
|
||||
p.help_url,
|
||||
p.image_uri,
|
||||
p.period,
|
||||
Algorithm::from_str(&p.algorithm).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Provider>>();
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
providers::table
|
||||
.order(providers::columns::id.desc())
|
||||
.first::<Provider>(&conn)
|
||||
.map_err(From::from)
|
||||
pub fn new(
|
||||
id: i32,
|
||||
name: &str,
|
||||
website: Option<String>,
|
||||
help_url: Option<String>,
|
||||
image_uri: Option<String>,
|
||||
period: i32,
|
||||
algorithm: Algorithm,
|
||||
) -> Provider {
|
||||
glib::Object::new(
|
||||
Provider::static_type(),
|
||||
&[
|
||||
("id", &id),
|
||||
("name", &name),
|
||||
("website", &website),
|
||||
("help-url", &help_url),
|
||||
("image-uri", &image_uri),
|
||||
("period", &period),
|
||||
("algorithm", &algorithm.to_string()),
|
||||
],
|
||||
)
|
||||
.expect("Failed to create provider")
|
||||
.downcast()
|
||||
.expect("Created provider is of wrong type")
|
||||
}
|
||||
|
||||
pub fn id(&self) -> i32 {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
priv_.id.get()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
priv_.name.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn period(&self) -> i32 {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
priv_.period.get()
|
||||
}
|
||||
|
||||
pub fn algorithm(&self) -> Algorithm {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
Algorithm::from_str(&priv_.algorithm.borrow().clone()).unwrap()
|
||||
}
|
||||
|
||||
pub fn website(&self) -> Option<String> {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
priv_.website.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn help_url(&self) -> Option<String> {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
priv_.help_url.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn image_uri(&self) -> Option<String> {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
priv_.image_uri.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +1,60 @@
|
|||
use super::accounts::AccountsModel;
|
||||
use super::database;
|
||||
use super::provider::Provider;
|
||||
use std::collections::HashMap;
|
||||
use gio::prelude::*;
|
||||
use glib::StaticType;
|
||||
use gtk::prelude::*;
|
||||
|
||||
pub struct ProvidersModel {
|
||||
pub model: HashMap<Provider, AccountsModel>,
|
||||
pub model: gio::ListStore,
|
||||
}
|
||||
|
||||
impl ProvidersModel {
|
||||
pub fn new() -> Self {
|
||||
let mut model = Self {
|
||||
model: HashMap::new(),
|
||||
let model = Self {
|
||||
model: gio::ListStore::new(Provider::static_type()),
|
||||
};
|
||||
model.init();
|
||||
model
|
||||
}
|
||||
|
||||
pub fn add_provider(&mut self, provider: Provider) {
|
||||
let accounts_model = AccountsModel::from_provider(&provider);
|
||||
self.model.insert(provider, accounts_model);
|
||||
pub fn find_by_id(&self, id: i32) -> Option<Provider> {
|
||||
for pos in 0..self.count() {
|
||||
let obj = self.model.get_object(pos).unwrap();
|
||||
let provider = obj.downcast::<Provider>().unwrap();
|
||||
if provider.id() == id {
|
||||
return Some(provider);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_count(&self) -> usize {
|
||||
println!("{}", self.model.len());
|
||||
self.model.len()
|
||||
pub fn completion_model(&self) -> gtk::ListStore {
|
||||
let store = gtk::ListStore::new(&[i32::static_type(), String::static_type()]);
|
||||
for pos in 0..self.count() {
|
||||
let obj = self.model.get_object(pos).unwrap();
|
||||
let provider = obj.downcast_ref::<Provider>().unwrap();
|
||||
store.set(
|
||||
&store.append(),
|
||||
&[0, 1],
|
||||
&[&provider.id(), &provider.name()],
|
||||
);
|
||||
}
|
||||
store
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
pub fn add_provider(&self, provider: &Provider) {
|
||||
//let accounts_model = AccountsModel::from_provider(&provider);
|
||||
self.model.append(provider);
|
||||
}
|
||||
|
||||
pub fn count(&self) -> u32 {
|
||||
self.model.get_n_items()
|
||||
}
|
||||
|
||||
fn init(&self) {
|
||||
// fill in the providers from the database
|
||||
let providers = database::get_providers().unwrap();
|
||||
let providers = Provider::load().unwrap();
|
||||
|
||||
for provider in providers.into_iter() {
|
||||
for provider in providers.iter() {
|
||||
self.add_provider(provider);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,10 @@ table! {
|
|||
providers (id) {
|
||||
id -> Integer,
|
||||
name -> Text,
|
||||
website -> Text,
|
||||
help_url -> Text,
|
||||
image_uri -> Text,
|
||||
period -> Integer,
|
||||
algorithm -> Text,
|
||||
website -> Nullable<Text>,
|
||||
help_url -> Nullable<Text>,
|
||||
image_uri -> Nullable<Text>,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
use crate::application::Action;
|
||||
use crate::models::database::{self, *};
|
||||
use crate::models::{Account, NewAccount};
|
||||
use crate::models::database::*;
|
||||
use crate::models::{Account, Algorithm, NewAccount, Provider, ProvidersModel};
|
||||
use anyhow::Result;
|
||||
use gio::prelude::*;
|
||||
use glib::Sender;
|
||||
use glib::StaticType;
|
||||
use glib::{signal::Inhibit, Sender};
|
||||
use gtk::prelude::*;
|
||||
use libhandy::ComboRowExt;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct AddAccountDialog {
|
||||
pub widget: libhandy::Window,
|
||||
builder: gtk::Builder,
|
||||
sender: Sender<Action>,
|
||||
model: Rc<ProvidersModel>,
|
||||
selected_provider: Rc<RefCell<Option<Provider>>>,
|
||||
}
|
||||
|
||||
impl AddAccountDialog {
|
||||
|
@ -21,15 +27,17 @@ impl AddAccountDialog {
|
|||
widget: add_dialog,
|
||||
builder,
|
||||
sender,
|
||||
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_signals();
|
||||
add_account_dialog.setup_widgets();
|
||||
add_account_dialog.setup_widgets(add_account_dialog.clone());
|
||||
add_account_dialog
|
||||
}
|
||||
|
||||
fn add_account(&self, account: NewAccount) -> Result<Account, database::Error> {
|
||||
fn add_account(&self, account: NewAccount) -> Result<Account> {
|
||||
// TODO: add the account to the provider model.
|
||||
account.insert()
|
||||
}
|
||||
|
@ -91,7 +99,7 @@ impl AddAccountDialog {
|
|||
|
||||
action!(
|
||||
actions,
|
||||
"sqcan-qr",
|
||||
"scan-qr",
|
||||
clone!(@strong self.sender as sender => move |_, _| {
|
||||
// sender.send(Action::OpenAddAccountDialog).unwrap();
|
||||
|
||||
|
@ -100,16 +108,49 @@ impl AddAccountDialog {
|
|||
self.widget.insert_action_group("add", Some(&actions));
|
||||
}
|
||||
|
||||
fn setup_widgets(&self) {
|
||||
// Fill the providers gtk::ListStore
|
||||
/*get_widget!(self.builder, gtk::ListStore, providers_store);
|
||||
if let Ok(providers) = database::get_providers() {
|
||||
for provider in providers.iter() {
|
||||
let values: [&dyn ToValue; 2] = [&provider.id, &provider.name];
|
||||
providers_store.set(&providers_store.append(), &[0, 1], &values);
|
||||
}
|
||||
}*/
|
||||
fn setup_widgets(&self, dialog: Rc<Self>) {
|
||||
get_widget!(self.builder, gtk::EntryCompletion, provider_completion);
|
||||
provider_completion.set_model(Some(&self.model.completion_model()));
|
||||
|
||||
get_widget!(self.builder, gtk::Entry, @token_entry)
|
||||
.set_property_secondary_icon_sensitive(false);
|
||||
|
||||
get_widget!(self.builder, libhandy::ComboRow, algorithm_comborow);
|
||||
let algoirthms_model = libhandy::EnumListModel::new(Algorithm::static_type());
|
||||
algorithm_comborow.set_model(Some(&algoirthms_model));
|
||||
|
||||
provider_completion.connect_match_selected(
|
||||
clone!(@strong dialog,
|
||||
@strong self.model as model,
|
||||
@strong self.builder as builder =>
|
||||
move |completion, store, iter| {
|
||||
let provider_id = store.get_value(iter, 0). get_some::<i32>().unwrap();
|
||||
let provider = model.find_by_id(provider_id).unwrap();
|
||||
|
||||
get_widget!(builder, gtk::Entry, provider_website_entry);
|
||||
if let Some(ref website) = provider.website() {
|
||||
provider_website_entry.set_text(website);
|
||||
}
|
||||
|
||||
get_widget!(builder, gtk::SpinButton, @period_spinbutton).set_value(provider.period() as f64);
|
||||
//let selected_position = algorithms_model.position(provider.algorithm()).unwrap_or(0);
|
||||
//get_widget!(builder, libhandy::ComboRow, @algorithm_comborow).set_selected(selected_position);
|
||||
get_widget!(builder, gtk::Entry, @token_entry)
|
||||
.set_property_secondary_icon_sensitive(provider.help_url().is_some());
|
||||
|
||||
dialog.selected_provider.replace(Some(provider));
|
||||
|
||||
Inhibit(false)
|
||||
}));
|
||||
|
||||
get_widget!(self.builder, gtk::Entry, token_entry);
|
||||
token_entry.connect_icon_press(clone!(@strong dialog => move |entry, pos| {
|
||||
if pos == gtk::EntryIconPosition::Secondary {
|
||||
if let Some(ref provider) = dialog.selected_provider.borrow().clone() {
|
||||
gio::AppInfo::launch_default_for_uri(&provider.help_url().unwrap(), None::<&gio::AppLaunchContext>);
|
||||
}
|
||||
}
|
||||
}));
|
||||
get_widget!(self.builder, gtk::SpinButton, @period_spinbutton).set_value(30.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use gtk::prelude::*;
|
||||
|
||||
use glib::{signal::Inhibit, Sender};
|
||||
use glib::Sender;
|
||||
|
||||
use crate::application::Action;
|
||||
use crate::models::{Account, AccountsModel, ObjectWrapper, Provider};
|
||||
|
@ -36,7 +36,7 @@ impl<'a> AccountsList<'a> {
|
|||
|
||||
fn init(&self) {
|
||||
get_widget!(self.builder, gtk::Label, provider_name);
|
||||
provider_name.set_text(&self.provider.name);
|
||||
provider_name.set_text(&self.provider.name());
|
||||
|
||||
get_widget!(self.builder, gtk::ListBox, listbox);
|
||||
listbox.bind_model(
|
||||
|
|
|
@ -32,7 +32,7 @@ impl ProvidersList {
|
|||
|
||||
fn init(&self) {
|
||||
get_widget!(self.builder, gtk::Box, providers_container);
|
||||
|
||||
/*
|
||||
for (provider, accounts_model) in &self.model.borrow().model {
|
||||
if accounts_model.get_count() != 0 {
|
||||
let accounts_list =
|
||||
|
@ -40,5 +40,6 @@ impl ProvidersList {
|
|||
providers_container.append(&accounts_list.widget);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::widgets::providers::ProvidersList;
|
|||
use crate::window_state;
|
||||
|
||||
use gio::prelude::*;
|
||||
use glib::{signal::Inhibit, Sender, WeakRef};
|
||||
use glib::{signal::Inhibit, Sender};
|
||||
use gtk::prelude::*;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
|
Loading…
Add table
Reference in a new issue