mirror of
https://gitlab.gnome.org/World/Authenticator
synced 2024-06-30 22:54:31 +00:00
parent
377d768c64
commit
553857ad31
|
@ -34,5 +34,15 @@
|
|||
<key name="keyrings-migrated" type="b">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key name="download-favicons" type="b">
|
||||
<default>true</default>
|
||||
<summary>Download Favicons</summary>
|
||||
<description>Whether the application should attempt to find an icon for the providers.</description>
|
||||
</key>
|
||||
<key name="download-favicons-metered" type="b">
|
||||
<default>true</default>
|
||||
<summary>Download Favicons over metered connections</summary>
|
||||
<description>Whether the application should download favicons over a metered connection.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
|
|
@ -84,6 +84,37 @@
|
|||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes">Network</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">_Download Favicons</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="activatable-widget">download_favicons_switch</property>
|
||||
<property name="subtitle" translatable="yes">Automatically attempt fetching a website icon</property>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="download_favicons_switch">
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">_Metered Connection</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="activatable-widget">download_favicons_metered_switch</property>
|
||||
<property name="subtitle" translatable="yes">Fetch a website icon on a metered connection</property>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="download_favicons_metered_switch">
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -104,4 +135,3 @@
|
|||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::{
|
|||
config,
|
||||
models::{
|
||||
keyring, Account, OTPUri, Provider, ProvidersModel, FAVICONS_PATH, RUNTIME, SECRET_SERVICE,
|
||||
SETTINGS,
|
||||
},
|
||||
utils::spawn_tokio_blocking,
|
||||
widgets::{PreferencesWindow, ProvidersDialog, Window},
|
||||
|
@ -21,7 +22,7 @@ mod imp {
|
|||
|
||||
use adw::subclass::prelude::*;
|
||||
use glib::{ParamSpec, ParamSpecBoolean, Value, WeakRef};
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -34,7 +35,6 @@ mod imp {
|
|||
pub locked: Cell<bool>,
|
||||
pub lock_timeout_id: RefCell<Option<glib::SourceId>>,
|
||||
pub can_be_locked: Cell<bool>,
|
||||
pub settings: OnceCell<gio::Settings>,
|
||||
pub search_provider: RefCell<Option<SearchProvider<super::Application>>>,
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,6 @@ mod imp {
|
|||
.activate(|app: &Self::Type, _, _| {
|
||||
let model = &app.imp().model;
|
||||
let window = app.active_window();
|
||||
|
||||
let preferences = PreferencesWindow::new(model.clone());
|
||||
preferences.set_has_set_password(app.can_be_locked());
|
||||
preferences.connect_restore_completed(clone!(@weak window =>move |_| {
|
||||
|
@ -182,7 +181,7 @@ mod imp {
|
|||
}
|
||||
});
|
||||
|
||||
self.settings.get().unwrap().connect_changed(
|
||||
SETTINGS.connect_changed(
|
||||
None,
|
||||
clone!(@weak app => move |settings, key| {
|
||||
match key {
|
||||
|
@ -279,8 +278,7 @@ impl Application {
|
|||
std::fs::create_dir_all(&*FAVICONS_PATH).ok();
|
||||
|
||||
// To be removed in the upcoming release
|
||||
let settings = gio::Settings::new(config::APP_ID);
|
||||
if !settings.boolean("keyrings-migrated") {
|
||||
if !SETTINGS.boolean("keyrings-migrated") {
|
||||
tracing::info!("Migrating the secrets to the file backend");
|
||||
let output: oo7::Result<()> = RUNTIME.block_on(async {
|
||||
oo7::migrate(
|
||||
|
@ -295,7 +293,7 @@ impl Application {
|
|||
});
|
||||
match output {
|
||||
Ok(_) => {
|
||||
settings
|
||||
SETTINGS
|
||||
.set_boolean("keyrings-migrated", true)
|
||||
.expect("Failed to update settings");
|
||||
tracing::info!("Secrets were migrated successfully");
|
||||
|
@ -330,7 +328,6 @@ impl Application {
|
|||
if !has_set_password {
|
||||
app.imp().model.load();
|
||||
}
|
||||
app.imp().settings.set(settings).unwrap();
|
||||
|
||||
ApplicationExtManual::run(&app);
|
||||
}
|
||||
|
@ -390,8 +387,8 @@ impl Application {
|
|||
/// Starts or restarts the lock timeout.
|
||||
pub fn restart_lock_timeout(&self) {
|
||||
let imp = self.imp();
|
||||
let auto_lock = imp.settings.get().unwrap().boolean("auto-lock");
|
||||
let timeout = imp.settings.get().unwrap().uint("auto-lock-timeout") * 60;
|
||||
let auto_lock = SETTINGS.boolean("auto-lock");
|
||||
let timeout = SETTINGS.uint("auto-lock-timeout") * 60;
|
||||
|
||||
if !auto_lock {
|
||||
return;
|
||||
|
@ -420,7 +417,7 @@ impl Application {
|
|||
fn update_color_scheme(&self) {
|
||||
let manager = self.style_manager();
|
||||
if !manager.system_supports_color_schemes() {
|
||||
let color_scheme = if self.imp().settings.get().unwrap().boolean("dark-theme") {
|
||||
let color_scheme = if SETTINGS.boolean("dark-theme") {
|
||||
adw::ColorScheme::PreferDark
|
||||
} else {
|
||||
adw::ColorScheme::PreferLight
|
||||
|
|
|
@ -11,9 +11,11 @@ mod otp_uri;
|
|||
mod provider;
|
||||
mod provider_sorter;
|
||||
mod providers;
|
||||
mod settings;
|
||||
|
||||
pub static RUNTIME: Lazy<tokio::runtime::Runtime> =
|
||||
Lazy::new(|| tokio::runtime::Runtime::new().unwrap());
|
||||
pub static SETTINGS: Lazy<Settings> = Lazy::new(|| Settings::default());
|
||||
pub static FAVICONS_PATH: Lazy<std::path::PathBuf> = Lazy::new(|| {
|
||||
gtk::glib::user_cache_dir()
|
||||
.join("authenticator")
|
||||
|
@ -30,4 +32,5 @@ pub use self::{
|
|||
provider::{Provider, ProviderPatch},
|
||||
provider_sorter::ProviderSorter,
|
||||
providers::ProvidersModel,
|
||||
settings::Settings,
|
||||
};
|
||||
|
|
52
src/models/settings.rs
Normal file
52
src/models/settings.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use gtk::{gio, glib, prelude::*};
|
||||
|
||||
use crate::config;
|
||||
|
||||
pub struct Settings(gio::Settings);
|
||||
|
||||
impl Settings {
|
||||
pub fn download_favicons(&self) -> bool {
|
||||
self.boolean("download-favicons")
|
||||
}
|
||||
|
||||
pub fn connect_download_favicons_changed<F>(&self, callback: F) -> glib::SignalHandlerId
|
||||
where
|
||||
F: Fn(bool) + 'static,
|
||||
{
|
||||
self.connect_changed(Some("download-favicons"), move |settings, _key| {
|
||||
callback(settings.boolean("download-favicons"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn download_favicons_metered(&self) -> bool {
|
||||
self.boolean("download-favicons-metered")
|
||||
}
|
||||
|
||||
pub fn connect_download_favicons_metered_changed<F>(&self, callback: F) -> glib::SignalHandlerId
|
||||
where
|
||||
F: Fn(bool) + 'static,
|
||||
{
|
||||
self.connect_changed(Some("download-favicons-metered"), move |settings, _key| {
|
||||
callback(settings.boolean("download-favicons-metered"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self(gio::Settings::new(config::APP_ID))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Settings {
|
||||
type Target = gio::Settings;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Settings {}
|
||||
unsafe impl Sync for Settings {}
|
|
@ -11,8 +11,7 @@ use crate::{
|
|||
Aegis, AndOTP, Backupable, Bitwarden, FreeOTP, Google, LegacyAuthenticator, Operation,
|
||||
Restorable, RestorableItem,
|
||||
},
|
||||
config,
|
||||
models::ProvidersModel,
|
||||
models::{ProvidersModel, SETTINGS},
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
@ -33,7 +32,6 @@ mod imp {
|
|||
#[derive(Debug, CompositeTemplate)]
|
||||
#[template(resource = "/com/belmoussaoui/Authenticator/preferences.ui")]
|
||||
pub struct PreferencesWindow {
|
||||
pub settings: gio::Settings,
|
||||
pub model: OnceCell<ProvidersModel>,
|
||||
pub has_set_password: Cell<bool>,
|
||||
pub actions: gio::SimpleActionGroup,
|
||||
|
@ -49,6 +47,10 @@ mod imp {
|
|||
pub auto_lock: TemplateChild<gtk::Switch>,
|
||||
#[template_child(id = "dark_mode_switch")]
|
||||
pub dark_mode: TemplateChild<gtk::Switch>,
|
||||
#[template_child(id = "download_favicons_switch")]
|
||||
pub download_favicons: TemplateChild<gtk::Switch>,
|
||||
#[template_child(id = "download_favicons_metered_switch")]
|
||||
pub download_favicons_metered: TemplateChild<gtk::Switch>,
|
||||
#[template_child]
|
||||
pub dark_mode_group: TemplateChild<adw::PreferencesGroup>,
|
||||
#[template_child(id = "lock_timeout_spin_btn")]
|
||||
|
@ -63,11 +65,9 @@ mod imp {
|
|||
type ParentType = adw::PreferencesWindow;
|
||||
|
||||
fn new() -> Self {
|
||||
let settings = gio::Settings::new(config::APP_ID);
|
||||
let actions = gio::SimpleActionGroup::new();
|
||||
|
||||
Self {
|
||||
settings,
|
||||
has_set_password: Cell::default(), // Synced from the application
|
||||
camera_page: CameraPage::new(actions.clone()),
|
||||
password_page: PasswordPage::new(actions.clone()),
|
||||
|
@ -77,6 +77,8 @@ mod imp {
|
|||
restore_actions: gio::SimpleActionGroup::new(),
|
||||
auto_lock: TemplateChild::default(),
|
||||
dark_mode: TemplateChild::default(),
|
||||
download_favicons: TemplateChild::default(),
|
||||
download_favicons_metered: TemplateChild::default(),
|
||||
lock_timeout: TemplateChild::default(),
|
||||
backup_group: TemplateChild::default(),
|
||||
restore_group: TemplateChild::default(),
|
||||
|
@ -193,13 +195,23 @@ impl PreferencesWindow {
|
|||
imp.dark_mode_group
|
||||
.set_visible(!style_manager.system_supports_color_schemes());
|
||||
|
||||
imp.settings
|
||||
SETTINGS
|
||||
.bind("dark-theme", &*imp.dark_mode, "active")
|
||||
.build();
|
||||
imp.settings
|
||||
SETTINGS
|
||||
.bind("download-favicons", &*imp.download_favicons, "active")
|
||||
.build();
|
||||
SETTINGS
|
||||
.bind(
|
||||
"download-favicons-metered",
|
||||
&*imp.download_favicons_metered,
|
||||
"active",
|
||||
)
|
||||
.build();
|
||||
SETTINGS
|
||||
.bind("auto-lock", &*imp.auto_lock, "active")
|
||||
.build();
|
||||
imp.settings
|
||||
SETTINGS
|
||||
.bind("auto-lock-timeout", &*imp.lock_timeout, "value")
|
||||
.build();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use gtk::{gio, glib, prelude::*, subclass::prelude::*, CompositeTemplate};
|
|||
use gtk_macros::send;
|
||||
use tracing::error;
|
||||
|
||||
use crate::models::{Provider, FAVICONS_PATH, RUNTIME};
|
||||
use crate::models::{Provider, FAVICONS_PATH, RUNTIME, SETTINGS};
|
||||
|
||||
pub enum ImageAction {
|
||||
Ready(String),
|
||||
|
@ -22,6 +22,7 @@ mod imp {
|
|||
#[template(resource = "/com/belmoussaoui/Authenticator/provider_image.ui")]
|
||||
pub struct ProviderImage {
|
||||
pub size: Cell<u32>,
|
||||
pub was_downloaded: Cell<bool>,
|
||||
pub sender: Sender<ImageAction>,
|
||||
pub receiver: RefCell<Option<Receiver<ImageAction>>>,
|
||||
pub provider: RefCell<Option<Provider>>,
|
||||
|
@ -48,6 +49,7 @@ mod imp {
|
|||
sender,
|
||||
receiver,
|
||||
size: Cell::new(96),
|
||||
was_downloaded: Cell::new(false),
|
||||
stack: TemplateChild::default(),
|
||||
image: TemplateChild::default(),
|
||||
spinner: TemplateChild::default(),
|
||||
|
@ -166,6 +168,7 @@ impl ProviderImage {
|
|||
} else {
|
||||
imp.image.set_from_file(large_file.path());
|
||||
}
|
||||
imp.was_downloaded.set(true);
|
||||
imp.stack.set_visible_child_name("image");
|
||||
}
|
||||
_ => {
|
||||
|
@ -176,6 +179,16 @@ impl ProviderImage {
|
|||
|
||||
fn fetch(&self) {
|
||||
let imp = self.imp();
|
||||
let network_monitor = gio::NetworkMonitor::default();
|
||||
if network_monitor.is_network_metered() && !SETTINGS.download_favicons_metered() {
|
||||
imp.image.set_from_icon_name(Some("provider-fallback"));
|
||||
imp.stack.set_visible_child_name("image");
|
||||
return;
|
||||
} else if !SETTINGS.download_favicons() {
|
||||
imp.image.set_from_icon_name(Some("provider-fallback"));
|
||||
imp.stack.set_visible_child_name("image");
|
||||
return;
|
||||
}
|
||||
if let Some(handle) = imp.join_handle.borrow_mut().take() {
|
||||
handle.abort();
|
||||
}
|
||||
|
@ -203,6 +216,7 @@ impl ProviderImage {
|
|||
|
||||
glib::MainContext::default().spawn_local(clone!(@weak self as this => async move {
|
||||
let imp = this.imp();
|
||||
imp.was_downloaded.set(true);
|
||||
match receiver.await {
|
||||
Ok(Some(cache_name)) => {
|
||||
send!(imp.sender.clone(), ImageAction::Ready(cache_name));
|
||||
|
@ -247,6 +261,24 @@ impl ProviderImage {
|
|||
self.bind_property("size", &*imp.image, "pixel-size")
|
||||
.sync_create()
|
||||
.build();
|
||||
SETTINGS.connect_download_favicons_changed(clone!(@weak self as image => move |state| {
|
||||
if state && !image.imp().was_downloaded.get() {
|
||||
image.fetch();
|
||||
}
|
||||
}));
|
||||
|
||||
SETTINGS.connect_download_favicons_metered_changed(
|
||||
clone!(@weak self as image => move |state| {
|
||||
let network_monitor = gio::NetworkMonitor::default();
|
||||
if !image.imp().was_downloaded.get() {
|
||||
if network_monitor.is_network_metered() && state {
|
||||
image.fetch();
|
||||
} else if !network_monitor.is_network_metered() {
|
||||
image.fetch();
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn do_action(&self, action: ImageAction) -> glib::Continue {
|
||||
|
|
|
@ -7,7 +7,7 @@ use once_cell::sync::OnceCell;
|
|||
use crate::{
|
||||
application::Application,
|
||||
config,
|
||||
models::{keyring, Account, OTPUri, ProvidersModel},
|
||||
models::{keyring, Account, OTPUri, ProvidersModel, SETTINGS},
|
||||
utils::spawn_tokio_blocking,
|
||||
widgets::{
|
||||
accounts::AccountDetailsPage,
|
||||
|
@ -29,10 +29,9 @@ mod imp {
|
|||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[template(resource = "/com/belmoussaoui/Authenticator/window.ui")]
|
||||
pub struct Window {
|
||||
pub settings: gio::Settings,
|
||||
pub model: OnceCell<ProvidersModel>,
|
||||
#[template_child]
|
||||
pub main_stack: TemplateChild<gtk::Stack>,
|
||||
|
@ -70,28 +69,6 @@ mod imp {
|
|||
type Type = super::Window;
|
||||
type ParentType = adw::ApplicationWindow;
|
||||
|
||||
fn new() -> Self {
|
||||
let settings = gio::Settings::new(config::APP_ID);
|
||||
Self {
|
||||
settings,
|
||||
providers: TemplateChild::default(),
|
||||
model: OnceCell::default(),
|
||||
account_details: TemplateChild::default(),
|
||||
search_entry: TemplateChild::default(),
|
||||
deck: TemplateChild::default(),
|
||||
error_revealer: TemplateChild::default(),
|
||||
empty_status_page: TemplateChild::default(),
|
||||
search_btn: TemplateChild::default(),
|
||||
password_entry: TemplateChild::default(),
|
||||
accounts_stack: TemplateChild::default(),
|
||||
locked_img: TemplateChild::default(),
|
||||
title_stack: TemplateChild::default(),
|
||||
main_stack: TemplateChild::default(),
|
||||
unlock_button: TemplateChild::default(),
|
||||
toast_overlay: TemplateChild::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
|
@ -267,14 +244,14 @@ impl Window {
|
|||
imp.locked_img.set_from_icon_name(Some(config::APP_ID));
|
||||
|
||||
// load latest window state
|
||||
let width = imp.settings.int("window-width");
|
||||
let height = imp.settings.int("window-height");
|
||||
let width = SETTINGS.int("window-width");
|
||||
let height = SETTINGS.int("window-height");
|
||||
|
||||
if width > -1 && height > -1 {
|
||||
self.set_default_size(width, height);
|
||||
}
|
||||
|
||||
let is_maximized = imp.settings.boolean("is-maximized");
|
||||
let is_maximized = SETTINGS.boolean("is-maximized");
|
||||
if is_maximized {
|
||||
self.maximize();
|
||||
}
|
||||
|
@ -316,12 +293,11 @@ impl Window {
|
|||
}
|
||||
|
||||
fn save_window_state(&self) -> anyhow::Result<()> {
|
||||
let settings = &self.imp().settings;
|
||||
let size = self.default_size();
|
||||
settings.set_int("window-width", size.0)?;
|
||||
settings.set_int("window-height", size.1)?;
|
||||
SETTINGS.set_int("window-width", size.0)?;
|
||||
SETTINGS.set_int("window-height", size.1)?;
|
||||
|
||||
settings.set_boolean("is-maximized", self.is_maximized())?;
|
||||
SETTINGS.set_boolean("is-maximized", self.is_maximized())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user