🎨 Put svg image generation in a dedicated datasource
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use tracing::{error, warn};
|
||||
|
||||
use super::button::{ErrorButton, SuccessButton, WarningButton};
|
||||
|
||||
use crate::data::datasources::random_svg_generators::{
|
||||
generate_random_svg_avatar, AvatarConfig, AvatarFeeling,
|
||||
};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/style_vars.rs"));
|
||||
|
||||
use style::{COLOR_CRITICAL_100, COLOR_SUCCESS_100, COLOR_WARNING_100};
|
||||
@@ -20,23 +21,25 @@ pub enum Severity {
|
||||
Warning,
|
||||
Critical,
|
||||
}
|
||||
impl fmt::Display for Severity {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let repr = match self {
|
||||
Self::Ok => "Severity::Ok",
|
||||
Self::Warning => "Severity::Warning",
|
||||
Self::Critical => "Severity::Critical",
|
||||
};
|
||||
write!(f, "{repr}")
|
||||
}
|
||||
}
|
||||
|
||||
struct DicebearConfig<'a> {
|
||||
gesture: &'a str,
|
||||
color: &'a str,
|
||||
browns: Vec<u32>,
|
||||
eyes: Vec<u32>,
|
||||
lips: Vec<u32>,
|
||||
fn avatar_configs() -> &'static HashMap<Severity, AvatarConfig<'static>> {
|
||||
static HASHMAP: OnceLock<HashMap<Severity, AvatarConfig>> = OnceLock::new();
|
||||
HASHMAP.get_or_init(|| {
|
||||
let mut configs = HashMap::new();
|
||||
configs.insert(
|
||||
Severity::Critical,
|
||||
AvatarConfig::new(AvatarFeeling::Alerting, COLOR_CRITICAL_100),
|
||||
);
|
||||
configs.insert(
|
||||
Severity::Warning,
|
||||
AvatarConfig::new(AvatarFeeling::Warning, COLOR_WARNING_100),
|
||||
);
|
||||
configs.insert(
|
||||
Severity::Ok,
|
||||
AvatarConfig::new(AvatarFeeling::Ok, COLOR_SUCCESS_100),
|
||||
);
|
||||
configs
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Props, Clone, PartialEq)]
|
||||
@@ -49,131 +52,20 @@ pub struct ModalProps {
|
||||
pub on_confirm: Option<EventHandler<MouseEvent>>,
|
||||
}
|
||||
|
||||
fn dicebear_variants() -> &'static HashMap<Severity, DicebearConfig<'static>> {
|
||||
static HASHMAP: OnceLock<HashMap<Severity, DicebearConfig>> = OnceLock::new();
|
||||
HASHMAP.get_or_init(|| {
|
||||
let mut variants = HashMap::new();
|
||||
variants.insert(
|
||||
Severity::Critical,
|
||||
DicebearConfig {
|
||||
gesture: "wavePointLongArms",
|
||||
color: COLOR_CRITICAL_100,
|
||||
browns: vec![2, 6, 11, 13],
|
||||
eyes: vec![2, 4],
|
||||
lips: vec![1, 2, 7, 11, 19, 20, 24, 27],
|
||||
},
|
||||
);
|
||||
variants.insert(
|
||||
Severity::Warning,
|
||||
DicebearConfig {
|
||||
gesture: "pointLongArm",
|
||||
color: COLOR_WARNING_100,
|
||||
browns: vec![2, 5, 10, 13],
|
||||
eyes: vec![1, 3],
|
||||
lips: vec![1, 2, 4, 8, 10, 13, 18, 21, 29],
|
||||
},
|
||||
);
|
||||
variants.insert(
|
||||
Severity::Ok,
|
||||
DicebearConfig {
|
||||
gesture: "okLongArm",
|
||||
color: COLOR_SUCCESS_100,
|
||||
browns: vec![1, 3, 4, 7, 8, 9, 12],
|
||||
eyes: vec![5],
|
||||
lips: vec![3, 5, 9, 14, 17, 22, 23, 25, 30],
|
||||
},
|
||||
);
|
||||
|
||||
variants
|
||||
})
|
||||
}
|
||||
|
||||
fn render_dicebear_variants(values: &[u32]) -> String {
|
||||
values
|
||||
.iter()
|
||||
.map(|l| format!("variant{:02}", l))
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
}
|
||||
|
||||
async fn generate_random_figure(url: &str, severity: &Severity) -> Option<String> {
|
||||
let mut res: Option<String> = None;
|
||||
|
||||
let config = match dicebear_variants().get(severity) {
|
||||
Some(config) => config,
|
||||
None => {
|
||||
error!("No dicebear configuration found for \"{severity}\"");
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
let seed = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
|
||||
|
||||
let color = config.color;
|
||||
let gesture = config.gesture;
|
||||
let rendered_browns = render_dicebear_variants(&config.browns);
|
||||
let rendered_eyes = render_dicebear_variants(&config.eyes);
|
||||
let rendered_lips = render_dicebear_variants(&config.lips);
|
||||
|
||||
let req = format!(
|
||||
"https://{url}/7.x/notionists/svg?\
|
||||
seed={seed}&\
|
||||
backgroundColor={color}&\
|
||||
gestureProbability=100&gesture={gesture}&\
|
||||
browsProbability=100&brows={rendered_browns}&\
|
||||
eyesProbability=100&eyes={rendered_eyes}&\
|
||||
lipsProbability=100&lips={rendered_lips}"
|
||||
);
|
||||
|
||||
match reqwest::get(req).await {
|
||||
Ok(result) => {
|
||||
match result.text().await {
|
||||
Ok(svg) => {
|
||||
res = Some(svg);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Error during placeholder loading: {}", err);
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Error during placeholder loading: {}", err);
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Modal(props: ModalProps) -> Element {
|
||||
// TODO: Use configuration file
|
||||
let url = "dicebear.tools.adrien.run";
|
||||
let avatar_config = avatar_configs().get(&props.severity);
|
||||
|
||||
let random_figure_future =
|
||||
use_resource(move || async move { generate_random_figure(url, &props.severity).await });
|
||||
use_resource(move || async move { generate_random_svg_avatar(avatar_config).await });
|
||||
|
||||
let figure = match &*random_figure_future.read_unchecked() {
|
||||
Some(Some(svg)) => Some(rsx! {
|
||||
let icon = match &*random_figure_future.read_unchecked() {
|
||||
Some(svg) => Some(rsx! {
|
||||
div {
|
||||
class: ClassName::MODAL_CONTENT_ICON_PLACEHOLDER,
|
||||
dangerous_inner_html: svg.as_str(),
|
||||
}
|
||||
}),
|
||||
Some(None) => {
|
||||
warn!("No profile image set or generated, display the placeholder");
|
||||
let path = match &props.severity {
|
||||
Severity::Ok => "./images/modal-default-ok-icon.svg",
|
||||
Severity::Warning => "./images/modal-default-warning-icon.svg",
|
||||
Severity::Critical => "./images/modal-default-critical-icon.svg",
|
||||
};
|
||||
Some(rsx! {
|
||||
div {
|
||||
class: ClassName::MODAL_CONTENT_ICON_PLACEHOLDER,
|
||||
img {
|
||||
src: path
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -183,7 +75,7 @@ pub fn Modal(props: ModalProps) -> Element {
|
||||
Severity::Critical => ErrorButton,
|
||||
};
|
||||
|
||||
figure.as_ref()?;
|
||||
icon.as_ref()?;
|
||||
|
||||
rsx! {
|
||||
style { {STYLE_SHEET} },
|
||||
@@ -196,7 +88,7 @@ pub fn Modal(props: ModalProps) -> Element {
|
||||
|
||||
div {
|
||||
class: ClassName::MODAL_CONTENT_ICON,
|
||||
{figure}
|
||||
{icon}
|
||||
},
|
||||
|
||||
div {
|
||||
|
Reference in New Issue
Block a user