A bunch of changes

This commit is contained in:
Alexander Rosenberg 2023-10-30 03:26:08 -07:00
parent 8754520d10
commit dbdf66012d
Signed by: Zander671
GPG Key ID: 5FD0394ADBD72730
5 changed files with 113 additions and 38 deletions

View File

@ -6,6 +6,16 @@
<summary>Api key</summary>
<description>The api key for the current user</description>
</key>
<key name="profile-url" type="ms">
<default>nothing</default>
<summary>Profile picture url</summary>
<description>The url for the users profile picture</description>
</key>
<key name="profile-data" type="may">
<default>nothing</default>
<summary>Profile picture data</summary>
<description>The data for the users profile picture</description>
</key>
<key name="window-size" type="(ii)">
<default>(-1,-1)</default>
</key>

View File

@ -14,17 +14,13 @@ template $SettingsPanel : Box {
row-homogeneous: true;
valign: start;
Button profile_image_button {
has-frame: false;
Image profile_image {
halign: center;
icon-name: "user-info-symbolic";
icon-size: large;
margin-end: 5;
valign: center;
vexpand: true;
}
Image profile_image {
halign: center;
icon-name: "user-info-symbolic";
icon-size: large;
margin-end: 5;
valign: center;
vexpand: true;
layout {
column: "0";
@ -34,27 +30,17 @@ template $SettingsPanel : Box {
}
}
Box {
Button remove_picture_button {
halign: start;
valign: center;
icon-name: "window-close-symbolic";
visible: false;
}
Entry name_label {
editable: false;
can-focus: false;
has-frame: false;
hexpand: true;
input-hints: no_emoji | no_spellcheck;
text: "Testy McTesterface";
styles [
"login-title-label",
]
}
Entry name_label {
editable: false;
can-focus: false;
has-frame: false;
hexpand: true;
input-hints: no_emoji | no_spellcheck;
text: "Testy McTesterface";
styles [
"login-title-label",
]
layout {
column: "1";
column-span: "1";

View File

@ -17,6 +17,7 @@ mod imp {
pub struct Application {
#[property(name = "api-key", type = Option<String>,
get = Self::api_key, set = Self::set_api_key, nullable)]
#[property(get)]
pub settings: RefCell<gio::Settings>,
pub context: RefCell<sl::Context>,
}

View File

@ -1,6 +1,6 @@
use crate::application;
use gtk4 as gtk;
use gtk::{glib, prelude::*, subclass::prelude::*};
use gtk::{glib, gio, gdk, prelude::*, subclass::prelude::*};
mod imp {
use super::*;
@ -8,6 +8,7 @@ mod imp {
use std::cell::RefCell;
use glib::subclass::Signal;
use once_cell::sync::Lazy;
use simplelogin::reqwest;
#[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)]
#[template(file = "data/settings_panel.blp")]
@ -20,12 +21,8 @@ mod imp {
#[template_child]
pub content: TemplateChild<gtk::Grid>,
#[template_child]
pub profile_image_button: TemplateChild<gtk::Button>,
#[template_child]
pub profile_image: TemplateChild<gtk::Image>,
#[template_child]
pub remove_picture_button: TemplateChild<gtk::Button>,
#[template_child]
pub name_label: TemplateChild<gtk::Entry>,
#[template_child]
pub edit_button: TemplateChild<gtk::Button>,
@ -149,6 +146,79 @@ mod imp {
}
}
fn did_profile_picture_change(
new_url: &Option<String>, settings: &gio::Settings
) -> bool {
let old_url: Option<String> = settings.value("profile-url")
.get::<Option<String>>().unwrap();
*new_url != old_url
}
fn has_valid_content_type(resp: &reqwest::Response) -> bool {
match resp.headers().get("Content-Type") {
Some(ct) => match ct.to_str() {
Ok("image/png") => true,
Ok("image/jpeg") => true,
_ => false,
},
None => false,
}
}
async fn process_profile_picture_response_body(
&self, url: &str, resp: reqwest::Response,
settings: &gio::Settings
) {
if let Ok(body) = resp.bytes().await {
let tex_res = gdk::Texture::from_bytes(&glib::Bytes::from(body.as_ref()));
if let Ok(tex) = tex_res {
self.profile_image.get().set_from_paintable(Some(&tex));
_ = settings.set("profile-url", Some(url));
_ = settings.set("profile-data", Some(body.as_ref()));
}
}
}
async fn download_and_set_profile_picture(
&self, url: &str, app: &application::Application
) -> bool {
let ctx = app.context().borrow();
let client = ctx.client();
match client.get(url).send().await {
Ok(resp) if Self::has_valid_content_type(&resp) => {
self.process_profile_picture_response_body(
url, resp, &app.settings()).await;
false
},
Ok(_) => false,
Err(e) => {
self.set_error(Some(&e.to_string()));
true
},
}
}
async fn refresh_user_profile_picture(
&self, user_info: &simplelogin::UserInfo,
) -> bool {
let app = self.application.borrow();
let settings = &app.settings();
if Self::did_profile_picture_change(&user_info.profile_picture_url,
&settings) {
self.profile_image.get()
.set_from_icon_name(Some("user-info-symbolic"));
_ = settings.set("profile-url", Option::<&str>::None);
_ = settings.set("profile-data", Option::<&[u8]>::None);
match &user_info.profile_picture_url {
Some(new_url) => self.download_and_set_profile_picture(
&new_url, &app).await,
None => false,
}
} else {
false
}
}
async fn refresh_user_info(&self) -> bool {
let app = self.application.borrow();
let mut ctx = app.context().borrow_mut();
@ -170,6 +240,8 @@ mod imp {
} else {
"Free"
});
drop(ctx);
self.refresh_user_profile_picture(&user_info).await;
false
},
Err(e) => {

View File

@ -1,4 +1,5 @@
#![allow(dead_code)]
pub use reqwest;
use std::fmt::{self, Display};
use serde::{Serialize, Deserialize};
@ -63,6 +64,7 @@ struct MfaResponse {
struct UserInfoRequest<'a> {
api_key: &'a str,
profile_picture: Option<&'a str>,
name: Option<&'a str>,
}
#[derive(Debug, Deserialize)]
@ -148,6 +150,10 @@ impl Context {
}
}
pub fn client(&self) -> &reqwest::Client {
&self.client
}
pub fn api_key(&self) -> &Option<String> {
&self.api_key
}
@ -304,12 +310,12 @@ impl Context {
}
pub async fn update_user_info(
&mut self, profile_picture: Option<&str>
&mut self, profile_picture: Option<&str>, name: 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})?;
&UserInfoRequest {api_key, profile_picture, name})?;
let res = self.patch_request("api/user_info",
Some(&req_json), None).await?;
Ok(Self::json_to_obj(&res)?)