Work on user stats page
This commit is contained in:
parent
04fbed2c3a
commit
8754520d10
@ -205,6 +205,14 @@ template $LoginWindow : ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
label: "Forgot your password? Reset it <a href=\"https://app.simplelogin.io/auth/forgot_password\">here</a>.";
|
||||||
|
margin-bottom: 5;
|
||||||
|
use-markup: true;
|
||||||
|
valign: start;
|
||||||
|
vexpand: true;
|
||||||
|
yalign: 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: "Don\'t have an account? You can register <a href=\"https://app.simplelogin.io/auth/register\">here</a>.";
|
label: "Don\'t have an account? You can register <a href=\"https://app.simplelogin.io/auth/register\">here</a>.";
|
||||||
|
@ -5,6 +5,7 @@ template $SettingsPanel : Box {
|
|||||||
hexpand: true;
|
hexpand: true;
|
||||||
|
|
||||||
child: Box content_wrapper {
|
child: Box content_wrapper {
|
||||||
|
visible: false;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
vexpand: true;
|
vexpand: true;
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ template $SettingsPanel : Box {
|
|||||||
|
|
||||||
Entry name_label {
|
Entry name_label {
|
||||||
editable: false;
|
editable: false;
|
||||||
|
can-focus: false;
|
||||||
has-frame: false;
|
has-frame: false;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
input-hints: no_emoji | no_spellcheck;
|
input-hints: no_emoji | no_spellcheck;
|
||||||
|
@ -88,7 +88,8 @@ mod imp {
|
|||||||
impl Default for Application {
|
impl Default for Application {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let settings = RefCell::new(gio::Settings::new(crate::APP_ID));
|
let settings = RefCell::new(gio::Settings::new(crate::APP_ID));
|
||||||
let api_key: Option<String> = settings.borrow().value("api-key").get();
|
let api_key: Option<String> =
|
||||||
|
settings.borrow().value("api-key").get::<Option<String>>().unwrap();
|
||||||
Self {
|
Self {
|
||||||
settings,
|
settings,
|
||||||
context: RefCell::new(sl::Context::new(api_key.as_deref())),
|
context: RefCell::new(sl::Context::new(api_key.as_deref())),
|
||||||
|
@ -59,6 +59,28 @@ mod imp {
|
|||||||
|
|
||||||
#[gtk::template_callbacks]
|
#[gtk::template_callbacks]
|
||||||
impl SettingsPanel {
|
impl SettingsPanel {
|
||||||
|
fn set_is_loading(&self, loading: bool) {
|
||||||
|
if loading {
|
||||||
|
self.set_error(None);
|
||||||
|
} else if !self.is_error() {
|
||||||
|
self.content_wrapper.get().set_sensitive(true);
|
||||||
|
}
|
||||||
|
self.loading_overlay.get().set_visible(loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_error(&self) -> bool {
|
||||||
|
self.error_overlay.get().is_visible()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_error(&self, error: Option<&str>) {
|
||||||
|
if let Some(e) = error {
|
||||||
|
self.set_is_loading(false);
|
||||||
|
self.error_label.get().set_text(e);
|
||||||
|
}
|
||||||
|
self.content_wrapper.get().set_sensitive(error.is_none());
|
||||||
|
self.error_overlay.get().set_visible(error.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn clear_api_key_clicked(&self, _: >k::Button) {
|
fn clear_api_key_clicked(&self, _: >k::Button) {
|
||||||
let main_loop = glib::MainContext::default();
|
let main_loop = glib::MainContext::default();
|
||||||
@ -79,23 +101,6 @@ mod imp {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_is_loading(&self, loading: bool) {
|
|
||||||
if loading {
|
|
||||||
self.set_error(None);
|
|
||||||
}
|
|
||||||
self.loading_overlay.get().set_visible(loading);
|
|
||||||
self.content_wrapper.get().set_sensitive(!loading);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_error(&self, error: Option<&str>) {
|
|
||||||
if let Some(e) = error {
|
|
||||||
self.set_is_loading(false);
|
|
||||||
self.error_label.get().set_text(e);
|
|
||||||
}
|
|
||||||
self.content_wrapper.get().set_sensitive(error.is_none());
|
|
||||||
self.error_overlay.get().set_visible(error.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn logout_clicked(&self, _: >k::Button) {
|
fn logout_clicked(&self, _: >k::Button) {
|
||||||
let main_loop = glib::MainContext::default();
|
let main_loop = glib::MainContext::default();
|
||||||
@ -125,6 +130,54 @@ mod imp {
|
|||||||
fn close_error_clicked(&self, _: >k::Button) {
|
fn close_error_clicked(&self, _: >k::Button) {
|
||||||
self.set_error(None);
|
self.set_error(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn refresh_stats(&self) -> bool {
|
||||||
|
let app = self.application.borrow();
|
||||||
|
let mut ctx = app.context().borrow_mut();
|
||||||
|
match ctx.stats().await {
|
||||||
|
Ok(stats) => {
|
||||||
|
self.stats_label.get().set_text(&format!(
|
||||||
|
"Aliases: {}, Blocked: {}, Forwarded: {}, Replied: {}",
|
||||||
|
stats.nb_alias, stats.nb_block, stats.nb_forward, stats.nb_reply
|
||||||
|
));
|
||||||
|
false
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
self.set_error(Some(e.to_string().as_str()));
|
||||||
|
true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn refresh_user_info(&self) -> bool {
|
||||||
|
let app = self.application.borrow();
|
||||||
|
let mut ctx = app.context().borrow_mut();
|
||||||
|
match ctx.get_user_info().await {
|
||||||
|
Ok(user_info) => {
|
||||||
|
self.name_label.get().set_text(
|
||||||
|
if user_info.name.is_empty() {
|
||||||
|
"<No name>"
|
||||||
|
} else {
|
||||||
|
&user_info.name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
self.email_label.get().set_text(&user_info.email);
|
||||||
|
self.status_label.get().set_text(
|
||||||
|
if user_info.is_premium {
|
||||||
|
"Premium"
|
||||||
|
} else if user_info.in_trial {
|
||||||
|
"Premium (Trial)"
|
||||||
|
} else {
|
||||||
|
"Free"
|
||||||
|
});
|
||||||
|
false
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
self.set_error(Some(e.to_string().as_str()));
|
||||||
|
true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
@ -160,7 +213,21 @@ mod imp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetImpl for SettingsPanel {}
|
impl WidgetImpl for SettingsPanel {
|
||||||
|
fn map(&self) {
|
||||||
|
self.parent_map();
|
||||||
|
let main_loop = glib::MainContext::default();
|
||||||
|
main_loop.spawn_local(clone!(@weak self as this => async move {
|
||||||
|
this.set_is_loading(true);
|
||||||
|
if !this.refresh_user_info().await {
|
||||||
|
this.refresh_stats().await;
|
||||||
|
}
|
||||||
|
this.set_is_loading(false);
|
||||||
|
this.content_wrapper.get().set_visible(true);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BoxImpl for SettingsPanel {}
|
impl BoxImpl for SettingsPanel {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,30 @@ struct MfaResponse {
|
|||||||
email: String,
|
email: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct UserInfoRequest<'a> {
|
||||||
|
api_key: &'a str,
|
||||||
|
profile_picture: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct UserInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub is_premium: bool,
|
||||||
|
pub email: String,
|
||||||
|
pub in_trial: bool,
|
||||||
|
pub profile_picture_url: Option<String>,
|
||||||
|
pub max_alias_free_plan: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Stats {
|
||||||
|
pub nb_alias: u32,
|
||||||
|
pub nb_block: u32,
|
||||||
|
pub nb_forward: u32,
|
||||||
|
pub nb_reply: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
State,
|
State,
|
||||||
Json,
|
Json,
|
||||||
@ -179,6 +203,23 @@ impl Context {
|
|||||||
.body(body.to_owned())).await
|
.body(body.to_owned())).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn patch_request(
|
||||||
|
&mut self, endpoint: &str, body: Option<&str>,
|
||||||
|
headers: Option<&[(&str, &str)]>
|
||||||
|
) -> Result<String, Error> {
|
||||||
|
let mut builder = self.client.patch(self.url.to_owned() + endpoint);
|
||||||
|
if let Some(body_str) = body {
|
||||||
|
builder = builder.header("Content-Type", "application/json")
|
||||||
|
.body(body_str.to_owned());
|
||||||
|
}
|
||||||
|
if let Some(headers_arr) = headers {
|
||||||
|
for header in headers_arr {
|
||||||
|
builder = builder.header(header.0, header.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::perform_request(builder).await
|
||||||
|
}
|
||||||
|
|
||||||
pub fn obj_to_json<T>(
|
pub fn obj_to_json<T>(
|
||||||
obj: &T
|
obj: &T
|
||||||
) -> Result<String, Error> where T: serde::Serialize {
|
) -> Result<String, Error> where T: serde::Serialize {
|
||||||
@ -250,4 +291,41 @@ impl Context {
|
|||||||
None => Err(Error::new(ErrorKind::State, "Not logged in")),
|
None => Err(Error::new(ErrorKind::State, "Not logged in")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_info(&mut self) -> Result<UserInfo, Error> {
|
||||||
|
match self.api_key.as_ref() {
|
||||||
|
Some(api_key) => {
|
||||||
|
let res = self.get_request("api/user_info",
|
||||||
|
&[("Authentication", api_key.clone().as_str())]).await?;
|
||||||
|
Ok(Self::json_to_obj(&res)?)
|
||||||
|
},
|
||||||
|
None => Err(Error::new(ErrorKind::State, "Not logged in")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_user_info(
|
||||||
|
&mut self, profile_picture: Option<&str>
|
||||||
|
) -> Result<UserInfo, Error> {
|
||||||
|
match self.api_key.as_ref() {
|
||||||
|
Some(api_key) => {
|
||||||
|
let req_json = Self::obj_to_json(
|
||||||
|
&UserInfoRequest {api_key, profile_picture})?;
|
||||||
|
let res = self.patch_request("api/user_info",
|
||||||
|
Some(&req_json), None).await?;
|
||||||
|
Ok(Self::json_to_obj(&res)?)
|
||||||
|
},
|
||||||
|
None => Err(Error::new(ErrorKind::State, "Not logged in")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn stats(&mut self) -> Result<Stats, Error> {
|
||||||
|
match self.api_key.as_ref() {
|
||||||
|
Some(api_key) => {
|
||||||
|
let res = self.get_request("api/stats",
|
||||||
|
&[("Authentication", api_key.clone().as_str())]).await?;
|
||||||
|
Ok(Self::json_to_obj(&res)?)
|
||||||
|
},
|
||||||
|
None => Err(Error::new(ErrorKind::State, "Not logged in")),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user