Various changes (mostly finish logins)
This commit is contained in:
parent
94801968fa
commit
67440ccca1
@ -7,5 +7,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
gtk4 = { version = "0.7.3", features = ["blueprint", "v4_12"] }
|
||||
once_cell = "1.18.0"
|
||||
simplelogin = { version = "0.1.0", path = "../simplelogin" }
|
||||
tokio = { version = "1.33.0", features = ["full"] }
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{env, io, fs, path::Path, process::Command};
|
||||
|
||||
const SCHEMA_NAME: &'static str = "im.zander.SimpleLogin.gschema.xml";
|
||||
const SCHEMA_NAME: &str = "im.zander.SimpleLogin.gschema.xml";
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
println!("cargo:rerun-if-changed=data/{}", SCHEMA_NAME);
|
||||
|
@ -1,28 +1,16 @@
|
||||
using Gtk 4.0;
|
||||
|
||||
Popover device_entry_popover {
|
||||
child: Label {
|
||||
accessible-role: tooltip;
|
||||
label: "This is the human readable name that will show up in the API key section of the SimpleLogin website.";
|
||||
max-width-chars: 30;
|
||||
wrap: true;
|
||||
}
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
template $LoginWindow : ApplicationWindow {
|
||||
title: "SimpleLogin";
|
||||
|
||||
Box {
|
||||
orientation: vertical;
|
||||
Overlay forms_overlay {
|
||||
valign: center;
|
||||
vexpand: true;
|
||||
|
||||
Overlay forms_overlay {
|
||||
child: forms_wrapper;
|
||||
valign: end;
|
||||
vexpand: true;
|
||||
child: Box forms_wrapper {
|
||||
orientation: vertical;
|
||||
|
||||
Box forms_wrapper {
|
||||
Box {
|
||||
valign: end;
|
||||
vexpand: true;
|
||||
|
||||
@ -55,6 +43,7 @@ template $LoginWindow : ApplicationWindow {
|
||||
Label error_label {
|
||||
label: "ERROR";
|
||||
margin-top: 5;
|
||||
visible: false;
|
||||
|
||||
styles [
|
||||
"error-message-label",
|
||||
@ -73,11 +62,23 @@ template $LoginWindow : ApplicationWindow {
|
||||
secondary-icon-name: "dialog-question-symbolic";
|
||||
truncate-multiline: true;
|
||||
width-chars: 18;
|
||||
icon-release => $device_help_clicked() swapped;
|
||||
changed => $login_form_changed() swapped;
|
||||
activate => $login_form_submit() swapped;
|
||||
|
||||
layout {
|
||||
column: "1";
|
||||
row: "3";
|
||||
}
|
||||
|
||||
Popover device_entry_popover {
|
||||
child: Label {
|
||||
accessible-role: tooltip;
|
||||
label: "This is the human readable name that will show up in the API key section of the SimpleLogin website.";
|
||||
max-width-chars: 30;
|
||||
wrap: true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
@ -116,6 +117,8 @@ template $LoginWindow : ApplicationWindow {
|
||||
margin-start: 6;
|
||||
receives-default: true;
|
||||
truncate-multiline: true;
|
||||
changed => $login_form_changed() swapped;
|
||||
activate => $login_form_submit() swapped;
|
||||
|
||||
layout {
|
||||
column: "1";
|
||||
@ -127,6 +130,8 @@ template $LoginWindow : ApplicationWindow {
|
||||
hexpand: true;
|
||||
margin-start: 6;
|
||||
show-peek-icon: true;
|
||||
changed => $login_form_changed() swapped;
|
||||
activate => $login_form_submit() swapped;
|
||||
|
||||
layout {
|
||||
column: "1";
|
||||
@ -140,6 +145,7 @@ template $LoginWindow : ApplicationWindow {
|
||||
margin-top: 5;
|
||||
sensitive: false;
|
||||
valign: center;
|
||||
clicked => $login_form_submit() swapped;
|
||||
|
||||
layout {
|
||||
column: "0";
|
||||
@ -185,6 +191,8 @@ template $LoginWindow : ApplicationWindow {
|
||||
margin-start: 8;
|
||||
show-peek-icon: true;
|
||||
width-chars: 27;
|
||||
changed => $api_key_form_changed() swapped;
|
||||
activate => $api_key_form_submit() swapped;
|
||||
}
|
||||
|
||||
Button api_key_button {
|
||||
@ -192,46 +200,50 @@ template $LoginWindow : ApplicationWindow {
|
||||
label: "Accept";
|
||||
margin-top: 5;
|
||||
sensitive: false;
|
||||
clicked => $api_key_form_submit() swapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
label: "Don\'t have an account? You can register <a href=\"https://app.simplelogin.io/auth/register\">here</a>.";
|
||||
|
||||
Label {
|
||||
label: "Don\'t have an account? You can register <a href=\"https://app.simplelogin.io/auth/register\">here</a>.";
|
||||
margin-bottom: 10;
|
||||
use-markup: true;
|
||||
valign: start;
|
||||
vexpand: true;
|
||||
yalign: 0.0;
|
||||
use-markup: true;
|
||||
valign: start;
|
||||
vexpand: true;
|
||||
yalign: 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
[overlay]
|
||||
Box loading_overlay {
|
||||
orientation: vertical;
|
||||
visible: false;
|
||||
|
||||
Spinner {
|
||||
spinning: true;
|
||||
valign: end;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
Label {
|
||||
label: "Loading...";
|
||||
valign: start;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box loading_overlay {
|
||||
orientation: vertical;
|
||||
|
||||
Spinner {
|
||||
spinning: true;
|
||||
valign: end;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
Label {
|
||||
label: "Loading...";
|
||||
valign: start;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
@ -5,13 +5,12 @@ template $TotpWindow : Window {
|
||||
title: "TOTP Entry";
|
||||
|
||||
Overlay overlay {
|
||||
child: form_wrapper;
|
||||
halign: center;
|
||||
hexpand: true;
|
||||
valign: center;
|
||||
vexpand: true;
|
||||
|
||||
Grid form_wrapper {
|
||||
child: Grid form_wrapper {
|
||||
column-spacing: 5;
|
||||
halign: center;
|
||||
hexpand: true;
|
||||
@ -49,6 +48,7 @@ template $TotpWindow : Window {
|
||||
|
||||
Button accept_button {
|
||||
label: "Accept";
|
||||
clicked => $accepted() swapped;
|
||||
|
||||
layout {
|
||||
column: "1";
|
||||
@ -58,6 +58,7 @@ template $TotpWindow : Window {
|
||||
|
||||
Button cancel_button {
|
||||
label: "Cancel";
|
||||
clicked => $canceled() swapped;
|
||||
|
||||
layout {
|
||||
column: "0";
|
||||
@ -67,6 +68,7 @@ template $TotpWindow : Window {
|
||||
|
||||
Entry code_entry {
|
||||
placeholder-text: "TOTP Code";
|
||||
changed => $totp_changed() swapped;
|
||||
|
||||
layout {
|
||||
column: "0";
|
||||
@ -77,6 +79,7 @@ template $TotpWindow : Window {
|
||||
|
||||
Label error_label {
|
||||
label: "ERROR";
|
||||
visible: false;
|
||||
|
||||
styles [
|
||||
"error-message-label",
|
||||
@ -88,34 +91,37 @@ template $TotpWindow : Window {
|
||||
row: "2";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
[overlay]
|
||||
Box loading_overlay {
|
||||
orientation: vertical;
|
||||
visible: false;
|
||||
|
||||
Spinner {
|
||||
spinning: true;
|
||||
valign: end;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
Label {
|
||||
label: "Loading...";
|
||||
valign: start;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box loading_overlay {
|
||||
orientation: vertical;
|
||||
|
||||
Spinner {
|
||||
spinning: true;
|
||||
valign: end;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
Label {
|
||||
label: "Loading...";
|
||||
valign: start;
|
||||
vexpand: true;
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
||||
styles [
|
||||
"loading-label",
|
||||
]
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ use crate::login_window::LoginWindow;
|
||||
|
||||
use gtk4 as gtk;
|
||||
use gtk::{glib, gdk, gio, prelude::*, subclass::prelude::*};
|
||||
use std::cell::RefCell;
|
||||
use simplelogin as sl;
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use std::cell::RefCell;
|
||||
use glib::closure_local;
|
||||
|
||||
#[derive(Debug, glib::Properties)]
|
||||
#[properties(wrapper_type = super::Application)]
|
||||
@ -13,6 +15,7 @@ mod imp {
|
||||
#[property(name = "api-key", type = Option<String>,
|
||||
get = Self::api_key, set = Self::set_api_key, nullable)]
|
||||
pub settings: RefCell<gio::Settings>,
|
||||
pub context: RefCell<sl::Context>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
@ -21,14 +24,39 @@ mod imp {
|
||||
}
|
||||
|
||||
fn set_api_key(&self, value: Option<String>) {
|
||||
_ = self.settings.borrow().set("api-key", value).unwrap();
|
||||
_ = self.settings.borrow().set("api-key", value);
|
||||
}
|
||||
|
||||
fn activate_no_api_key(&self) {
|
||||
let login_window = LoginWindow::new(&self.obj());
|
||||
login_window.connect_closure(
|
||||
"logged-in", false,
|
||||
closure_local!(@weak-allow-none self as opt_this
|
||||
=> move |win: LoginWindow| {
|
||||
win.close();
|
||||
if let Some(this) = opt_this {
|
||||
let obj = this.obj();
|
||||
let ctx = obj.context().borrow();
|
||||
let api_key = ctx.api_key();
|
||||
this.set_api_key(api_key.clone());
|
||||
this.activate_has_api_key();
|
||||
}
|
||||
}));
|
||||
login_window.present();
|
||||
}
|
||||
|
||||
fn activate_has_api_key(&self) {
|
||||
println!("Logged in with API Key: {}", self.api_key().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Application {
|
||||
fn default() -> Self {
|
||||
let settings = RefCell::new(gio::Settings::new(crate::APP_ID));
|
||||
let api_key: Option<String> = settings.borrow().value("api-key").get();
|
||||
Self {
|
||||
settings: RefCell::new(gio::Settings::new(crate::APP_ID)),
|
||||
settings,
|
||||
context: RefCell::new(sl::Context::new(api_key.as_deref())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,17 +70,15 @@ mod imp {
|
||||
|
||||
impl ApplicationImpl for Application {
|
||||
fn activate(&self) {
|
||||
let css = gtk::CssProvider::new();
|
||||
gtk::style_context_add_provider_for_display(
|
||||
&gdk::Display::default().unwrap(),
|
||||
&css, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
);
|
||||
css.load_from_string(include_str!("../data/global.css"));
|
||||
if self.api_key().is_none() {
|
||||
let login_window = LoginWindow::new(&self.obj());
|
||||
login_window.present();
|
||||
} else {
|
||||
todo!();
|
||||
let css = gtk::CssProvider::new();
|
||||
gtk::style_context_add_provider_for_display(
|
||||
&gdk::Display::default().unwrap(),
|
||||
&css, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
);
|
||||
css.load_from_string(include_str!("../data/global.css"));
|
||||
match self.api_key() {
|
||||
Some(_) => self.activate_has_api_key(),
|
||||
None => self.activate_no_api_key(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,4 +98,8 @@ impl Application {
|
||||
pub fn new(app_id: &str) -> Self {
|
||||
glib::Object::builder().property("application-id", app_id).build()
|
||||
}
|
||||
|
||||
pub fn context(&self) -> &RefCell<sl::Context> {
|
||||
&self.imp().context
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
use gtk4 as gtk;
|
||||
use gtk::{glib, gio, subclass::prelude::*};
|
||||
use gtk::{glib, gio, prelude::*, subclass::prelude::*};
|
||||
|
||||
mod imp {
|
||||
use crate::{application, totp_window};
|
||||
use super::*;
|
||||
use glib::{subclass::Signal, clone, closure_local};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/login_window.blp")]
|
||||
@ -31,6 +34,106 @@ mod imp {
|
||||
pub loading_overlay: TemplateChild<gtk::Box>,
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl LoginWindow {
|
||||
#[template_callback]
|
||||
fn login_form_changed(&self, _: >k::Widget) {
|
||||
let email = &self.email_entry.get().text();
|
||||
let password = &self.password_entry.get().text();
|
||||
let device = &self.device_entry.get().text();
|
||||
self.login_button.get().set_sensitive(
|
||||
!email.is_empty() && !password.is_empty() && !device.is_empty())
|
||||
}
|
||||
|
||||
fn set_error(&self, error: &str) {
|
||||
self.set_is_loading(false);
|
||||
let error_label = &self.error_label.get();
|
||||
error_label.set_text(error);
|
||||
error_label.set_visible(true);
|
||||
}
|
||||
|
||||
fn set_is_loading(&self, loading: bool) {
|
||||
if loading {
|
||||
self.error_label.get().set_visible(false);
|
||||
}
|
||||
self.loading_overlay.get().set_visible(loading);
|
||||
self.forms_wrapper.get().set_sensitive(!loading);
|
||||
}
|
||||
|
||||
fn handle_need_totp(&self, app: &application::Application) {
|
||||
let totp_window = totp_window::TotpWindow::new(app);
|
||||
totp_window.connect_closure(
|
||||
"totp-success", false,
|
||||
closure_local!(@weak-allow-none self as opt_this =>
|
||||
move |_: totp_window::TotpWindow| {
|
||||
if let Some(this) = opt_this {
|
||||
this.obj().emit_by_name::<()>("logged-in", &[]);
|
||||
}
|
||||
}));
|
||||
totp_window.connect_closure(
|
||||
"totp-failed", false,
|
||||
closure_local!(@weak-allow-none self as opt_this
|
||||
=> move |_: totp_window::TotpWindow, err: &str| {
|
||||
if let Some(this) = opt_this {
|
||||
this.set_error(err);
|
||||
}
|
||||
}));
|
||||
totp_window.present();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn login_form_submit(&self, _: >k::Widget) {
|
||||
let main_loop = glib::MainContext::default();
|
||||
main_loop.spawn_local(clone!(@weak self as this => async move {
|
||||
let email = &this.email_entry.get().text();
|
||||
let password = &this.password_entry.get().text();
|
||||
let device = &this.device_entry.get().text();
|
||||
if !email.is_empty() && !password.is_empty() && !device.is_empty() {
|
||||
this.set_is_loading(true);
|
||||
let app: application::Application =
|
||||
this.obj().application().unwrap().downcast().unwrap();
|
||||
let mut ctx = app.context().borrow_mut();
|
||||
let login_res = ctx.login(email, password, device).await;
|
||||
drop(ctx);
|
||||
match login_res {
|
||||
Ok(need_mfa) if need_mfa => this.handle_need_totp(&app),
|
||||
Ok(_) => this.obj().emit_by_name::<()>("logged-in", &[]),
|
||||
Err(e) => this.set_error(e.to_string().as_str()),
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
fn get_api_key_from_field(&self) -> String {
|
||||
self.api_key_entry.get().text().replace('\n', "")
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn api_key_form_changed(&self, _: >k::Widget) {
|
||||
let api_key = self.get_api_key_from_field();
|
||||
self.api_key_button.get().set_sensitive(!api_key.is_empty());
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn api_key_form_submit(&self, _: >k::Widget) {
|
||||
let api_key = self.get_api_key_from_field();
|
||||
if !api_key.is_empty() {
|
||||
let obj = self.obj();
|
||||
let app: application::Application =
|
||||
obj.application().unwrap().downcast().unwrap();
|
||||
let mut ctx = app.context().borrow_mut();
|
||||
ctx.set_api_key(Some(api_key.as_str()));
|
||||
drop(ctx);
|
||||
obj.emit_by_name::<()>("logged-in", &[]);
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn device_help_clicked(&self, _: gtk::EntryIconPosition) {
|
||||
self.device_entry_popover.get().popup();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for LoginWindow {
|
||||
const NAME: &'static str = "LoginWindow";
|
||||
@ -39,6 +142,7 @@ mod imp {
|
||||
|
||||
fn class_init(class: &mut Self::Class) {
|
||||
class.bind_template();
|
||||
class.bind_template_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
@ -46,7 +150,18 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for LoginWindow {}
|
||||
impl ObjectImpl for LoginWindow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(||
|
||||
vec![Signal::builder("logged-in").build()]);
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for LoginWindow {}
|
||||
impl WindowImpl for LoginWindow {}
|
||||
impl ApplicationWindowImpl for LoginWindow {}
|
||||
|
@ -8,7 +8,8 @@ use gtk::glib::{self, g_info};
|
||||
|
||||
const APP_ID: &str = "im.zander.SimpleLogin";
|
||||
|
||||
fn main() -> glib::ExitCode {
|
||||
#[tokio::main]
|
||||
async fn main() -> glib::ExitCode {
|
||||
#[cfg(debug_assertions)] {
|
||||
std::env::set_var("G_MESSAGES_DEBUG", "SimpleLogin");
|
||||
// allow us to use a test schema for debug runs
|
||||
|
@ -2,12 +2,84 @@ use gtk4 as gtk;
|
||||
use gtk::{glib, gio, prelude::*, subclass::prelude::*};
|
||||
|
||||
mod imp {
|
||||
use crate::application;
|
||||
use super::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use glib::{clone, subclass::Signal};
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/totp_window.blp")]
|
||||
pub struct TotpWindow {
|
||||
#[template_child]
|
||||
pub overlay: gtk::TemplateChild<gtk::Overlay>,
|
||||
#[template_child]
|
||||
pub form_wrapper: gtk::TemplateChild<gtk::Grid>,
|
||||
#[template_child]
|
||||
pub accept_button: gtk::TemplateChild<gtk::Button>,
|
||||
#[template_child]
|
||||
pub cancel_button: gtk::TemplateChild<gtk::Button>,
|
||||
#[template_child]
|
||||
pub code_entry: gtk::TemplateChild<gtk::Entry>,
|
||||
#[template_child]
|
||||
pub error_label: gtk::TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub loading_overlay: gtk::TemplateChild<gtk::Box>,
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl TotpWindow {
|
||||
#[template_callback]
|
||||
fn totp_changed(&self, _: >k::Entry) {
|
||||
let totp = &self.code_entry.get().text();
|
||||
self.accept_button.get().set_sensitive(!totp.is_empty());
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn canceled(&self, _: >k::Button) {
|
||||
self.obj().close();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn accepted(&self, _: >k::Button) {
|
||||
let main_loop = glib::MainContext::default();
|
||||
main_loop.spawn_local(clone!(@weak self as this => async move {
|
||||
let obj = this.obj();
|
||||
this.set_is_loading(true);
|
||||
this.loading_overlay.get().set_visible(true);
|
||||
let app: application::Application =
|
||||
obj.application().unwrap().downcast().unwrap();
|
||||
let mut ctx = app.context().borrow_mut();
|
||||
let totp = &this.code_entry.get().text();
|
||||
let mfa_res = ctx.mfa(totp).await;
|
||||
drop(ctx);
|
||||
match mfa_res {
|
||||
Ok(()) => {
|
||||
obj.emit_by_name::<()>("totp-success", &[]);
|
||||
obj.close();
|
||||
},
|
||||
Err(e) if e.is_json() => this.set_error(e.to_string().as_str()),
|
||||
Err(e) => {
|
||||
obj.emit_by_name::<()>("totp-failed", &[&e.to_string()]);
|
||||
obj.close();
|
||||
},
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
fn set_error(&self, error: &str) {
|
||||
self.set_is_loading(false);
|
||||
let error_label = &self.error_label.get();
|
||||
error_label.set_text(error);
|
||||
error_label.set_visible(true);
|
||||
}
|
||||
|
||||
fn set_is_loading(&self, loading: bool) {
|
||||
if loading {
|
||||
self.error_label.get().set_visible(false);
|
||||
}
|
||||
self.loading_overlay.get().set_visible(loading);
|
||||
self.form_wrapper.get().set_sensitive(!loading);
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -18,6 +90,7 @@ mod imp {
|
||||
|
||||
fn class_init(class: &mut Self::Class) {
|
||||
class.bind_template();
|
||||
class.bind_template_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
@ -25,7 +98,22 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for TotpWindow {}
|
||||
impl ObjectImpl for TotpWindow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(||
|
||||
vec![Signal::builder("totp-success").build(),
|
||||
Signal::builder("totp-failed")
|
||||
.param_types([str::static_type()])
|
||||
.build()]);
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
self.overlay.add_overlay(&self.loading_overlay.get());
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for TotpWindow {}
|
||||
impl WindowImpl for TotpWindow {}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
#![allow(dead_code)]
|
||||
use core::fmt;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
client: reqwest::Client,
|
||||
api_key: Option<String>,
|
||||
@ -13,6 +14,16 @@ pub struct Context {
|
||||
device: Option<String>,
|
||||
}
|
||||
|
||||
impl Display for Context {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
let api_key = match &self.api_key {
|
||||
Some(key) => format!("\"{}\"", key),
|
||||
None => "None".to_owned(),
|
||||
};
|
||||
writeln!(f, "Context(api_key: \"{}\", url: \"{}\")", api_key, self.url)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ErrorResponse {
|
||||
error: String,
|
||||
@ -113,15 +124,15 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn api_key(&self) -> Option<&String> {
|
||||
self.api_key.as_ref()
|
||||
pub fn api_key(&self) -> &Option<String> {
|
||||
&self.api_key
|
||||
}
|
||||
|
||||
pub fn set_api_key(&mut self, api_key: Option<&str>) {
|
||||
self.api_key = api_key.map(|s| s.to_owned());
|
||||
}
|
||||
|
||||
fn parse_error_json(error_body: &String) -> String {
|
||||
fn parse_error_json(error_body: &str) -> String {
|
||||
match serde_json::from_str(error_body) {
|
||||
Ok::<ErrorResponse, _>(obj) => obj.error,
|
||||
Err(_) => "Could not parse error response JSON".to_owned(),
|
||||
@ -136,7 +147,7 @@ impl Context {
|
||||
let status = resp.status();
|
||||
let resp_text = resp.text().await
|
||||
.map_err(|e| Error::new(ErrorKind::Http, e.to_string()))?;
|
||||
return if status.is_success() {
|
||||
if status.is_success() {
|
||||
Ok(resp_text)
|
||||
} else if status.is_client_error() {
|
||||
Err(Error::new(ErrorKind::Auth, Self::parse_error_json(&resp_text)))
|
||||
@ -190,15 +201,13 @@ impl Context {
|
||||
} else {
|
||||
Err(Error::new(ErrorKind::Json, "Error response did not contain mfa key"))
|
||||
}
|
||||
} else if resp_obj.api_key.is_some() {
|
||||
self.api_key = resp_obj.api_key;
|
||||
self.mfa_key = None;
|
||||
self.device = None;
|
||||
Ok(true)
|
||||
} else {
|
||||
if resp_obj.api_key.is_some() {
|
||||
self.api_key = resp_obj.api_key;
|
||||
self.mfa_key = None;
|
||||
self.device = None;
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(Error::new(ErrorKind::Json, "Response did not contain api key"))
|
||||
}
|
||||
Err(Error::new(ErrorKind::Json, "Response did not contain api key"))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user