fix(users): on setting user icons, shrink icons larger than 1 MB
This commit is contained in:
parent
8bc944aef7
commit
e1d447a08c
1 changed files with 72 additions and 2 deletions
|
|
@ -11,6 +11,7 @@ use cosmic::{
|
||||||
widget::{self, Space, column, icon, row, settings, text},
|
widget::{self, Space, column, icon, row, settings, text},
|
||||||
};
|
};
|
||||||
use cosmic_settings_page::{self as page, Section, section};
|
use cosmic_settings_page::{self as page, Section, section};
|
||||||
|
use image::GenericImageView;
|
||||||
use pwhash::{bcrypt, md5_crypt, sha256_crypt, sha512_crypt};
|
use pwhash::{bcrypt, md5_crypt, sha256_crypt, sha512_crypt};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
@ -20,7 +21,7 @@ use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
future::Future,
|
future::Future,
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
@ -29,6 +30,14 @@ use zbus_polkit::policykit1::CheckAuthorizationFlags;
|
||||||
const DEFAULT_ICON_FILE: &str = "/usr/share/pixmaps/faces/pop-robot.png";
|
const DEFAULT_ICON_FILE: &str = "/usr/share/pixmaps/faces/pop-robot.png";
|
||||||
const USERS_ADMIN_POLKIT_POLICY_ID: &str = "com.system76.CosmicSettings.Users.Admin";
|
const USERS_ADMIN_POLKIT_POLICY_ID: &str = "com.system76.CosmicSettings.Users.Admin";
|
||||||
|
|
||||||
|
// AccountsService has a hard limit of 1MB for icon files
|
||||||
|
// https://gitlab.freedesktop.org/accountsservice/accountsservice/-/blob/main/src/user.c#L3131
|
||||||
|
const MAX_ICON_SIZE_BYTES: u64 = 1_048_576;
|
||||||
|
// Use a smaller threshold to ensure compressed images stay under the limit
|
||||||
|
const ICON_SIZE_THRESHOLD: u64 = 900_000; // 900KB
|
||||||
|
// Target dimensions for resized profile icons
|
||||||
|
const TARGET_ICON_SIZE: u32 = 512;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
id: u64,
|
id: u64,
|
||||||
|
|
@ -124,6 +133,58 @@ impl From<Message> for crate::pages::Message {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_icon_file(path: &Path) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||||
|
let metadata = std::fs::metadata(path)?;
|
||||||
|
let file_size = metadata.len();
|
||||||
|
|
||||||
|
tracing::debug!("Icon file size: {} bytes", file_size);
|
||||||
|
|
||||||
|
if file_size <= ICON_SIZE_THRESHOLD {
|
||||||
|
tracing::debug!("File size is acceptable, using original file");
|
||||||
|
return Ok(path.to_path_buf());
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
"Icon file is {} bytes, resizing to fit under 1MB limit",
|
||||||
|
file_size
|
||||||
|
);
|
||||||
|
|
||||||
|
let img = image::open(path)?;
|
||||||
|
let (width, height) = img.dimensions();
|
||||||
|
|
||||||
|
tracing::debug!("Original image dimensions: {}x{}", width, height);
|
||||||
|
|
||||||
|
let (new_width, new_height) = if width > height {
|
||||||
|
let ratio = TARGET_ICON_SIZE as f32 / width as f32;
|
||||||
|
(TARGET_ICON_SIZE, (height as f32 * ratio) as u32)
|
||||||
|
} else {
|
||||||
|
let ratio = TARGET_ICON_SIZE as f32 / height as f32;
|
||||||
|
((width as f32 * ratio) as u32, TARGET_ICON_SIZE)
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::debug!("Resizing to {}x{}", new_width, new_height);
|
||||||
|
|
||||||
|
let resized = img.resize(new_width, new_height, image::imageops::FilterType::Lanczos3);
|
||||||
|
|
||||||
|
// Create a temporary file for the resized icon
|
||||||
|
let temp_dir = std::env::temp_dir();
|
||||||
|
let temp_filename = format!("cosmic-settings-icon-{}.png", std::process::id());
|
||||||
|
let temp_path = temp_dir.join(temp_filename);
|
||||||
|
|
||||||
|
tracing::debug!("Saving resized icon to: {:?}", temp_path);
|
||||||
|
|
||||||
|
resized.save(&temp_path)?;
|
||||||
|
|
||||||
|
let new_size = std::fs::metadata(&temp_path)?.len();
|
||||||
|
tracing::info!("Resized icon file size: {} bytes", new_size);
|
||||||
|
|
||||||
|
if new_size > MAX_ICON_SIZE_BYTES {
|
||||||
|
tracing::warn!("Resized file is still too large, but attempting anyway");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(temp_path)
|
||||||
|
}
|
||||||
|
|
||||||
impl page::Page<crate::pages::Message> for Page {
|
impl page::Page<crate::pages::Message> for Page {
|
||||||
fn set_id(&mut self, entity: page::Entity) {
|
fn set_id(&mut self, entity: page::Entity) {
|
||||||
self.entity = entity;
|
self.entity = entity;
|
||||||
|
|
@ -466,8 +527,17 @@ impl Page {
|
||||||
return Message::None;
|
return Message::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Prepare the icon file, resizing if necessary to fit within accountsservice's 1MB limit
|
||||||
|
let icon_path = match prepare_icon_file(&path) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(why) => {
|
||||||
|
tracing::error!(?why, "failed to prepare icon file");
|
||||||
|
return Message::None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let result = request_permission_on_denial(&conn, || {
|
let result = request_permission_on_denial(&conn, || {
|
||||||
user.set_icon_file(path.to_str().unwrap())
|
user.set_icon_file(icon_path.to_str().unwrap())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue