mirror of
https://gitlab.gnome.org/World/Authenticator.git
synced 2025-03-04 08:44:40 +01:00
provider: download & display images properly
This commit is contained in:
parent
68aee23222
commit
9ed6188f07
13 changed files with 311 additions and 192 deletions
|
@ -26,6 +26,12 @@
|
|||
margin-bottom: 8px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.provider-image{
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.totp-progress,
|
||||
.totp-progress trough progress,
|
||||
.totp-progress trough {
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<child>
|
||||
|
||||
<object class="HdyClamp">
|
||||
<property name="valign">center</property>
|
||||
<property name="margin-top">12</property>
|
||||
|
@ -64,33 +63,6 @@
|
|||
<property name="spacing">42</property>
|
||||
<property name="margin-start">8</property>
|
||||
<property name="margin-end">8</property>
|
||||
<child>
|
||||
<object class="GtkStack" id="image_stack">
|
||||
<property name="transition-type">crossfade</property>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">image</property>
|
||||
<property name="child">
|
||||
<object class="GtkImage" id="image">
|
||||
<property name="icon-name">image-missing</property>
|
||||
<property name="pixel-size">96</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">loading</property>
|
||||
<property name="child">
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBox" id="basic_list">
|
||||
<property name="selection_mode">none</property>
|
||||
|
|
|
@ -1,65 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="ProviderImage" parent="GtkStack">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<template class="ProviderImage" parent="GtkBox">
|
||||
<child>
|
||||
<object class="GtkSpinner" id="provider_spinner">
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEventBox" id="image_eventbox">
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="transition-type">crossfade</property>
|
||||
<child>
|
||||
<object class="GtkOverlay">
|
||||
<child>
|
||||
<object class="GtkImage" id="provider_image">
|
||||
<property name="pixel_size">128</property>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">image</property>
|
||||
<property name="child">
|
||||
<object class="GtkImage" id="image">
|
||||
<property name="icon-name">image-missing-symbolic</property>
|
||||
<property name="pixel-size">96</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkImage" id="insert_image">
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="halign">center</property>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">loading</property>
|
||||
<property name="child">
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="valign">center</property>
|
||||
<property name="icon-name">insert-image-symbolic</property>
|
||||
<style>
|
||||
<class name="insert-image"/>
|
||||
</style>
|
||||
<property name="halign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="not_found_box">
|
||||
<property name="width_request">48</property>
|
||||
<property name="height_request">48</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkEventBox">
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="icon-name">insert-image-symbolic</property>
|
||||
<style>
|
||||
<class name="insert-image"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="insert-image-box"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="provider-image" />
|
||||
</style>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
|
@ -72,33 +72,6 @@
|
|||
<property name="spacing">42</property>
|
||||
<property name="margin-start">8</property>
|
||||
<property name="margin-end">8</property>
|
||||
<child>
|
||||
<object class="GtkStack" id="image_stack">
|
||||
<property name="transition-type">crossfade</property>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">image</property>
|
||||
<property name="child">
|
||||
<object class="GtkImage" id="image">
|
||||
<property name="icon-name">image-missing</property>
|
||||
<property name="pixel-size">96</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">loading</property>
|
||||
<property name="child">
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBox">
|
||||
<property name="selection_mode">none</property>
|
||||
|
|
|
@ -9,11 +9,17 @@
|
|||
<property name="orientation">vertical</property>
|
||||
<property name="vexpand">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="name_label">
|
||||
<property name="halign">start</property>
|
||||
<style>
|
||||
<class name="title-2" />
|
||||
</style>
|
||||
<object class="GtkBox" id="header">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="hexpand">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="name_label">
|
||||
<property name="halign">start</property>
|
||||
<style>
|
||||
<class name="title-2" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
|
|
@ -60,6 +60,7 @@ sources = files(
|
|||
'widgets/preferences/password_page.rs',
|
||||
'widgets/preferences/window.rs',
|
||||
'widgets/providers/all.rs',
|
||||
'widgets/providers/image.rs',
|
||||
'widgets/providers/list.rs',
|
||||
'widgets/providers/mod.rs',
|
||||
'widgets/providers/page.rs',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use super::algorithm::{Algorithm, HOTPAlgorithm};
|
||||
use crate::diesel::ExpressionMethods;
|
||||
use crate::models::{database, Account, AccountsModel, FaviconError, FaviconScrapper};
|
||||
use crate::schema::providers;
|
||||
use anyhow::Result;
|
||||
|
@ -301,7 +302,6 @@ impl Provider {
|
|||
digits: i32,
|
||||
default_counter: i32,
|
||||
) -> Result<Self> {
|
||||
use crate::diesel::ExpressionMethods;
|
||||
let db = database::connection();
|
||||
let conn = db.get()?;
|
||||
|
||||
|
@ -454,6 +454,19 @@ impl Provider {
|
|||
priv_.help_url.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn set_image_uri(&self, uri: &str) -> Result<()> {
|
||||
let db = database::connection();
|
||||
let conn = db.get()?;
|
||||
|
||||
let target = providers::table.filter(providers::columns::id.eq(self.id()));
|
||||
diesel::update(target)
|
||||
.set(providers::columns::image_uri.eq(uri))
|
||||
.execute(&conn)?;
|
||||
|
||||
self.set_property("image-uri", &uri)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn image_uri(&self) -> Option<String> {
|
||||
let priv_ = ProviderPriv::from_instance(self);
|
||||
priv_.image_uri.borrow().clone()
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
use crate::application::Action;
|
||||
use crate::helpers::{qrcode, Keyring};
|
||||
use crate::models::{Account, Algorithm, Provider, ProvidersModel};
|
||||
use crate::widgets::{ProviderImage, ProviderImageSize};
|
||||
use anyhow::Result;
|
||||
use gio::prelude::*;
|
||||
use gio::{subclass::ObjectSubclass, ActionMapExt};
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::{glib_object_subclass, glib_wrapper};
|
||||
use glib::{signal::Inhibit, Receiver, Sender};
|
||||
use glib::{signal::Inhibit, Sender};
|
||||
use gtk::{prelude::*, CompositeTemplate};
|
||||
use libhandy::ActionRowExt;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::cell::RefCell;
|
||||
|
||||
pub enum AccountAddAction {
|
||||
SetIcon(gio::File),
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
@ -24,11 +20,13 @@ mod imp {
|
|||
#[derive(CompositeTemplate)]
|
||||
pub struct AccountAddDialog {
|
||||
pub global_sender: OnceCell<Sender<Action>>,
|
||||
pub sender: Sender<AccountAddAction>,
|
||||
pub receiver: RefCell<Option<Receiver<AccountAddAction>>>,
|
||||
pub model: OnceCell<ProvidersModel>,
|
||||
pub selected_provider: OnceCell<Provider>,
|
||||
pub actions: gio::SimpleActionGroup,
|
||||
pub image: ProviderImage,
|
||||
|
||||
#[template_child(id = "main_container")]
|
||||
pub main_container: TemplateChild<gtk::Box>,
|
||||
|
||||
#[template_child(id = "username_entry")]
|
||||
pub username_entry: TemplateChild<gtk::Entry>,
|
||||
|
@ -68,15 +66,6 @@ mod imp {
|
|||
|
||||
#[template_child(id = "provider_completion")]
|
||||
pub provider_completion: TemplateChild<gtk::EntryCompletion>,
|
||||
|
||||
#[template_child(id = "image")]
|
||||
pub image: TemplateChild<gtk::Image>,
|
||||
|
||||
#[template_child(id = "spinner")]
|
||||
pub spinner: TemplateChild<gtk::Spinner>,
|
||||
|
||||
#[template_child(id = "image_stack")]
|
||||
pub image_stack: TemplateChild<gtk::Stack>,
|
||||
}
|
||||
|
||||
impl ObjectSubclass for AccountAddDialog {
|
||||
|
@ -89,18 +78,15 @@ mod imp {
|
|||
glib_object_subclass!();
|
||||
|
||||
fn new() -> Self {
|
||||
let (sender, r) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
let receiver = RefCell::new(Some(r));
|
||||
|
||||
let actions = gio::SimpleActionGroup::new();
|
||||
|
||||
Self {
|
||||
global_sender: OnceCell::new(),
|
||||
sender,
|
||||
receiver,
|
||||
actions,
|
||||
image: ProviderImage::new(ProviderImageSize::Large),
|
||||
model: OnceCell::new(),
|
||||
selected_provider: OnceCell::new(),
|
||||
main_container: TemplateChild::default(),
|
||||
token_entry: TemplateChild::default(),
|
||||
username_entry: TemplateChild::default(),
|
||||
more_list: TemplateChild::default(),
|
||||
|
@ -114,9 +100,6 @@ mod imp {
|
|||
hmac_algorithm_row: TemplateChild::default(),
|
||||
counter_row: TemplateChild::default(),
|
||||
period_row: TemplateChild::default(),
|
||||
image: TemplateChild::default(),
|
||||
spinner: TemplateChild::default(),
|
||||
image_stack: TemplateChild::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,8 +177,7 @@ impl AccountAddDialog {
|
|||
|
||||
qrcode::screenshot_area(
|
||||
self.clone().upcast::<gtk::Window>(),
|
||||
clone!(@weak self as dialog, @weak token_entry, @weak username_entry, @strong self_.model as model,
|
||||
@strong self_.sender as sender => move |screenshot| {
|
||||
clone!(@weak self as dialog, @weak token_entry, @weak username_entry, @strong self_.model as model => move |screenshot| {
|
||||
if let Ok(otpauth) = qrcode::scan(&screenshot) {
|
||||
token_entry.set_text(&otpauth.token);
|
||||
if let Some(ref username) = otpauth.account {
|
||||
|
@ -232,14 +214,15 @@ impl AccountAddDialog {
|
|||
|
||||
fn set_provider(&self, provider: Provider) {
|
||||
let self_ = imp::AccountAddDialog::from_instance(self);
|
||||
|
||||
self_.more_list.get().show();
|
||||
|
||||
self_.provider_entry.get().set_text(&provider.name());
|
||||
self_
|
||||
.period_label
|
||||
.get()
|
||||
.set_text(&provider.period().to_string());
|
||||
|
||||
self_.image.set_provider(&provider);
|
||||
|
||||
self_
|
||||
.algorithm_label
|
||||
.get()
|
||||
|
@ -249,6 +232,7 @@ impl AccountAddDialog {
|
|||
.digits_label
|
||||
.get()
|
||||
.set_text(&provider.digits().to_string());
|
||||
|
||||
match provider.algorithm() {
|
||||
Algorithm::TOTP => {
|
||||
self_.hmac_algorithm_row.get().hide();
|
||||
|
@ -269,18 +253,6 @@ impl AccountAddDialog {
|
|||
if let Some(ref help_url) = provider.help_url() {
|
||||
self_.provider_help_row.get().set_subtitle(Some(help_url));
|
||||
}
|
||||
|
||||
self_.image_stack.get().set_visible_child_name("loading");
|
||||
self_.spinner.get().start();
|
||||
|
||||
let p = provider.clone();
|
||||
let sender = self_.sender.clone();
|
||||
spawn!(async move {
|
||||
if let Ok(file) = p.favicon().await {
|
||||
send!(sender, AccountAddAction::SetIcon(file));
|
||||
}
|
||||
});
|
||||
|
||||
self_.selected_provider.set(provider);
|
||||
}
|
||||
|
||||
|
@ -316,16 +288,13 @@ impl AccountAddDialog {
|
|||
|
||||
fn setup_widgets(&self) {
|
||||
let self_ = imp::AccountAddDialog::from_instance(self);
|
||||
let receiver = self_.receiver.borrow_mut().take().unwrap();
|
||||
receiver.attach(
|
||||
None,
|
||||
clone!(@weak self as dialog => move |action| dialog.do_action(action)),
|
||||
);
|
||||
self_
|
||||
.provider_completion
|
||||
.get()
|
||||
.set_model(Some(&self_.model.get().unwrap().completion_model()));
|
||||
|
||||
self_.main_container.get().prepend(&self_.image);
|
||||
|
||||
self_.provider_completion.get().connect_match_selected(
|
||||
clone!(@weak self as dialog, @strong self_.model as model => move |_, store, iter| {
|
||||
let provider_id = store.get_value(iter, 0). get_some::<i32>().unwrap();
|
||||
|
@ -336,16 +305,4 @@ impl AccountAddDialog {
|
|||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn do_action(&self, action: AccountAddAction) -> glib::Continue {
|
||||
match action {
|
||||
AccountAddAction::SetIcon(file) => {
|
||||
let self_ = imp::AccountAddDialog::from_instance(self);
|
||||
self_.image.get().set_from_file(file.get_path().unwrap());
|
||||
self_.spinner.get().stop();
|
||||
self_.image_stack.get().set_visible_child_name("image");
|
||||
}
|
||||
};
|
||||
glib::Continue(true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ mod window;
|
|||
|
||||
pub use self::accounts::AccountAddDialog;
|
||||
pub use self::preferences::PreferencesWindow;
|
||||
pub use self::providers::{ProvidersDialog, ProvidersList};
|
||||
pub use self::providers::{ProviderImage, ProviderImageSize, ProvidersDialog, ProvidersList};
|
||||
pub use self::window::{View, Window};
|
||||
|
|
221
src/widgets/providers/image.rs
Normal file
221
src/widgets/providers/image.rs
Normal file
|
@ -0,0 +1,221 @@
|
|||
use crate::models::Provider;
|
||||
use gio::{subclass::ObjectSubclass, FileExt};
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::{glib_object_subclass, glib_wrapper};
|
||||
use glib::{Receiver, Sender};
|
||||
use gtk::{prelude::*, CompositeTemplate};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum ProviderImageSize {
|
||||
Small,
|
||||
Large,
|
||||
}
|
||||
|
||||
pub enum ImageAction {
|
||||
Ready(gio::File),
|
||||
Failed,
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use glib::subclass;
|
||||
use gtk::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
|
||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("provider", |name| {
|
||||
glib::ParamSpec::object(
|
||||
name,
|
||||
"provider",
|
||||
"Provider",
|
||||
Provider::static_type(),
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
})];
|
||||
|
||||
#[derive(Debug, CompositeTemplate)]
|
||||
pub struct ProviderImage {
|
||||
pub sender: Sender<ImageAction>,
|
||||
pub receiver: RefCell<Option<Receiver<ImageAction>>>,
|
||||
#[template_child(id = "stack")]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child(id = "image")]
|
||||
pub image: TemplateChild<gtk::Image>,
|
||||
#[template_child(id = "spinner")]
|
||||
pub spinner: TemplateChild<gtk::Spinner>,
|
||||
pub provider: RefCell<Option<Provider>>,
|
||||
}
|
||||
|
||||
impl ObjectSubclass for ProviderImage {
|
||||
const NAME: &'static str = "ProviderImage";
|
||||
type Type = super::ProviderImage;
|
||||
type ParentType = gtk::Box;
|
||||
type Instance = subclass::simple::InstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
glib_object_subclass!();
|
||||
|
||||
fn new() -> Self {
|
||||
let (sender, r) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
let receiver = RefCell::new(Some(r));
|
||||
Self {
|
||||
sender,
|
||||
receiver,
|
||||
stack: TemplateChild::default(),
|
||||
image: TemplateChild::default(),
|
||||
spinner: TemplateChild::default(),
|
||||
provider: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.set_template_from_resource("/com/belmoussaoui/Authenticator/provider_image.ui");
|
||||
Self::bind_template_children(klass);
|
||||
klass.install_properties(&PROPERTIES);
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for ProviderImage {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
obj.init_template();
|
||||
obj.setup_widgets();
|
||||
self.parent_constructed(obj);
|
||||
}
|
||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
subclass::Property("provider", ..) => {
|
||||
let provider = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
self.provider.replace(provider);
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
subclass::Property("provider", ..) => self.provider.borrow().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WidgetImpl for ProviderImage {}
|
||||
impl BoxImpl for ProviderImage {}
|
||||
}
|
||||
|
||||
glib_wrapper! {
|
||||
pub struct ProviderImage(ObjectSubclass<imp::ProviderImage>) @extends gtk::Widget, gtk::Box;
|
||||
}
|
||||
impl ProviderImage {
|
||||
pub fn new(image_size: ProviderImageSize) -> Self {
|
||||
let image = glib::Object::new(Self::static_type(), &[])
|
||||
.expect("Failed to create ProviderImage")
|
||||
.downcast::<ProviderImage>()
|
||||
.expect("Created ProviderImage is of wrong type");
|
||||
image.set_size(image_size);
|
||||
image
|
||||
}
|
||||
|
||||
pub fn set_provider(&self, provider: &Provider) {
|
||||
let self_ = imp::ProviderImage::from_instance(self);
|
||||
self_.stack.get().set_visible_child_name("loading");
|
||||
self_.spinner.get().start();
|
||||
|
||||
self.set_property("provider", &provider.clone()).unwrap();
|
||||
|
||||
match provider.image_uri() {
|
||||
Some(uri) => {
|
||||
// Very dirty hack to store that we couldn't find an icon
|
||||
// to avoid re-hitting the website every time we have to display it
|
||||
if uri == "invalid" {
|
||||
self_
|
||||
.image
|
||||
.get()
|
||||
.set_from_icon_name(Some("image-icon-missing"));
|
||||
self_.stack.get().set_visible_child_name("image");
|
||||
return;
|
||||
}
|
||||
|
||||
let file = gio::File::new_for_uri(&uri);
|
||||
if !file.query_exists(gio::NONE_CANCELLABLE) {
|
||||
self.fetch();
|
||||
return;
|
||||
}
|
||||
|
||||
self_.image.get().set_from_file(file.get_path().unwrap());
|
||||
self_.stack.get().set_visible_child_name("image");
|
||||
}
|
||||
_ => {
|
||||
self.fetch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch(&self) {
|
||||
let self_ = imp::ProviderImage::from_instance(self);
|
||||
let sender = self_.sender.clone();
|
||||
self_.stack.get().set_visible_child_name("loading");
|
||||
self_.spinner.get().start();
|
||||
let p = self.provider();
|
||||
spawn!(async move {
|
||||
match p.favicon().await {
|
||||
Ok(file) => send!(sender, ImageAction::Ready(file)),
|
||||
Err(_) => send!(sender, ImageAction::Failed),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_size(&self, image_size: ProviderImageSize) {
|
||||
let self_ = imp::ProviderImage::from_instance(self);
|
||||
|
||||
match image_size {
|
||||
ProviderImageSize::Small => {
|
||||
self_.image.get().set_pixel_size(48);
|
||||
self.set_halign(gtk::Align::Start);
|
||||
}
|
||||
ProviderImageSize::Large => {
|
||||
self_.image.get().set_pixel_size(96);
|
||||
self.set_halign(gtk::Align::Center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn provider(&self) -> Provider {
|
||||
let provider = self.get_property("provider").unwrap();
|
||||
provider.get::<Provider>().unwrap().unwrap()
|
||||
}
|
||||
|
||||
fn setup_widgets(&self) {
|
||||
let self_ = imp::ProviderImage::from_instance(self);
|
||||
let receiver = self_.receiver.borrow_mut().take().unwrap();
|
||||
receiver.attach(
|
||||
None,
|
||||
clone!(@weak self as image => move |action| image.do_action(action)),
|
||||
);
|
||||
}
|
||||
|
||||
fn do_action(&self, action: ImageAction) -> glib::Continue {
|
||||
let self_ = imp::ProviderImage::from_instance(self);
|
||||
match action {
|
||||
ImageAction::Failed => {
|
||||
self_
|
||||
.image
|
||||
.get()
|
||||
.set_from_icon_name(Some("image-missing-symbolic"));
|
||||
self.provider().set_image_uri("invalid");
|
||||
}
|
||||
ImageAction::Ready(image) => {
|
||||
self_.image.get().set_from_file(image.get_path().unwrap());
|
||||
self.provider().set_image_uri(&image.get_uri());
|
||||
}
|
||||
}
|
||||
self_.stack.get().set_visible_child_name("image");
|
||||
self_.spinner.get().stop();
|
||||
|
||||
glib::Continue(true)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
mod all;
|
||||
mod image;
|
||||
mod list;
|
||||
mod page;
|
||||
mod row;
|
||||
pub use self::all::ProvidersDialog;
|
||||
pub use self::image::{ProviderImage, ProviderImageSize};
|
||||
pub use self::list::ProvidersList;
|
||||
pub use self::page::{ProviderPage, ProviderPageMode};
|
||||
pub use self::row::ProviderRow;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::models::{Algorithm, HOTPAlgorithm, Provider};
|
||||
use crate::widgets::{ProviderImage, ProviderImageSize};
|
||||
use gio::subclass::ObjectSubclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::translate::ToGlib;
|
||||
|
@ -18,6 +19,9 @@ mod imp {
|
|||
|
||||
#[derive(Debug, CompositeTemplate)]
|
||||
pub struct ProviderPage {
|
||||
pub image: ProviderImage,
|
||||
#[template_child(id = "main_container")]
|
||||
pub main_container: TemplateChild<gtk::Box>,
|
||||
#[template_child(id = "name_entry")]
|
||||
pub name_entry: TemplateChild<gtk::Entry>,
|
||||
#[template_child(id = "period_spinbutton")]
|
||||
|
@ -30,10 +34,6 @@ mod imp {
|
|||
pub provider_website_entry: TemplateChild<gtk::Entry>,
|
||||
#[template_child(id = "provider_help_entry")]
|
||||
pub provider_help_entry: TemplateChild<gtk::Entry>,
|
||||
#[template_child(id = "image_stack")]
|
||||
pub image_stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child(id = "spinner")]
|
||||
pub spinner: TemplateChild<gtk::Spinner>,
|
||||
#[template_child(id = "algorithm_comborow")]
|
||||
pub algorithm_comborow: TemplateChild<libhandy::ComboRow>,
|
||||
#[template_child(id = "hmac_algorithm_comborow")]
|
||||
|
@ -64,14 +64,14 @@ mod imp {
|
|||
let hmac_algorithms_model = libhandy::EnumListModel::new(HOTPAlgorithm::static_type());
|
||||
|
||||
Self {
|
||||
image: ProviderImage::new(ProviderImageSize::Large),
|
||||
main_container: TemplateChild::default(),
|
||||
name_entry: TemplateChild::default(),
|
||||
period_spinbutton: TemplateChild::default(),
|
||||
digits_spinbutton: TemplateChild::default(),
|
||||
default_counter_spinbutton: TemplateChild::default(),
|
||||
provider_website_entry: TemplateChild::default(),
|
||||
provider_help_entry: TemplateChild::default(),
|
||||
image_stack: TemplateChild::default(),
|
||||
spinner: TemplateChild::default(),
|
||||
algorithm_comborow: TemplateChild::default(),
|
||||
hmac_algorithm_comborow: TemplateChild::default(),
|
||||
period_row: TemplateChild::default(),
|
||||
|
@ -127,9 +127,6 @@ impl ProviderPage {
|
|||
self_.provider_help_entry.get().set_text(website);
|
||||
}
|
||||
|
||||
self_.image_stack.get().set_visible_child_name("loading");
|
||||
self_.spinner.get().start();
|
||||
|
||||
self_.algorithm_comborow.get().set_selected(
|
||||
self_
|
||||
.algorithms_model
|
||||
|
@ -151,14 +148,7 @@ impl ProviderPage {
|
|||
.hmac_algorithms_model
|
||||
.find_position(provider.hmac_algorithm().to_glib()),
|
||||
);
|
||||
|
||||
/*let sender = self.sender.clone();
|
||||
spawn!(async move {
|
||||
if let Ok(file) = p.favicon().await {
|
||||
send!(sender, AddAccountAction::SetIcon(file));
|
||||
}
|
||||
});*/
|
||||
|
||||
self_.image.set_provider(&provider);
|
||||
self_
|
||||
.title
|
||||
.get()
|
||||
|
@ -172,6 +162,8 @@ impl ProviderPage {
|
|||
.get()
|
||||
.set_model(Some(&self_.algorithms_model));
|
||||
|
||||
self_.main_container.get().prepend(&self_.image);
|
||||
|
||||
self_
|
||||
.algorithm_comborow
|
||||
.get()
|
||||
|
@ -213,8 +205,6 @@ impl ProviderPage {
|
|||
self_.provider_website_entry.get().set_text("");
|
||||
self_.provider_help_entry.get().set_text("");
|
||||
|
||||
self_.image_stack.get().set_visible_child_name("image");
|
||||
self_.spinner.get().stop();
|
||||
self_.algorithm_comborow.get().set_selected(0);
|
||||
}
|
||||
ProviderPageMode::Edit => {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::models::{Account, AccountSorter, Algorithm, Provider};
|
||||
use crate::widgets::accounts::AccountRow;
|
||||
use crate::widgets::{accounts::AccountRow, ProviderImage, ProviderImageSize};
|
||||
use gio::prelude::*;
|
||||
use gio::subclass::ObjectSubclass;
|
||||
use glib::subclass::prelude::*;
|
||||
|
@ -23,6 +23,7 @@ mod imp {
|
|||
|
||||
#[derive(CompositeTemplate)]
|
||||
pub struct ProviderRow {
|
||||
pub image: ProviderImage,
|
||||
pub provider: RefCell<Option<Provider>>,
|
||||
#[template_child(id = "name_label")]
|
||||
pub name_label: TemplateChild<gtk::Label>,
|
||||
|
@ -30,6 +31,8 @@ mod imp {
|
|||
pub accounts_list: TemplateChild<gtk::ListBox>,
|
||||
#[template_child(id = "progress")]
|
||||
pub progress: TemplateChild<gtk::ProgressBar>,
|
||||
#[template_child(id = "header")]
|
||||
pub header: TemplateChild<gtk::Box>,
|
||||
}
|
||||
|
||||
impl ObjectSubclass for ProviderRow {
|
||||
|
@ -43,9 +46,11 @@ mod imp {
|
|||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
image: ProviderImage::new(ProviderImageSize::Small),
|
||||
name_label: TemplateChild::default(),
|
||||
accounts_list: TemplateChild::default(),
|
||||
progress: TemplateChild::default(),
|
||||
header: TemplateChild::default(),
|
||||
provider: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +116,9 @@ impl ProviderRow {
|
|||
fn setup_widgets(&self) {
|
||||
let self_ = imp::ProviderRow::from_instance(self);
|
||||
|
||||
self_.header.get().prepend(&self_.image);
|
||||
self_.image.set_provider(&self.provider());
|
||||
|
||||
let progress_bar = self_.progress.get();
|
||||
if self.provider().algorithm() == Algorithm::TOTP {
|
||||
progress_bar.set_fraction(1_f64);
|
||||
|
|
Loading…
Add table
Reference in a new issue