🎨 Put svg image generation in a dedicated datasource
This commit is contained in:
@@ -5,12 +5,12 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use const_format::formatcp;
|
use const_format::formatcp;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use rand::distributions::{Alphanumeric, DistString};
|
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
use validator::{Validate, ValidateArgs, ValidateEmail, ValidationError, ValidationErrors};
|
use validator::{Validate, ValidateArgs, ValidateEmail, ValidationError, ValidationErrors};
|
||||||
use zxcvbn::zxcvbn;
|
use zxcvbn::zxcvbn;
|
||||||
|
|
||||||
use crate::base::{Session, SESSION};
|
use crate::base::{Session, SESSION};
|
||||||
|
use crate::data::datasources::random_svg_generators::{generate_random_svg_shape, ShapeConfig};
|
||||||
|
|
||||||
use super::button::{LoginButton, RegisterButton};
|
use super::button::{LoginButton, RegisterButton};
|
||||||
use super::modal::{Modal, Severity};
|
use super::modal::{Modal, Severity};
|
||||||
@@ -47,36 +47,6 @@ const SHAPE_2_COLORS_STR: &str = formatcp!(
|
|||||||
const SHAPE_3_COLORS_STR: &str = formatcp!(
|
const SHAPE_3_COLORS_STR: &str = formatcp!(
|
||||||
"{COLOR_TERNARY_120},{COLOR_TERNARY_110},{COLOR_TERNARY_100},{COLOR_TERNARY_90},{COLOR_TERNARY_80}");
|
"{COLOR_TERNARY_120},{COLOR_TERNARY_110},{COLOR_TERNARY_100},{COLOR_TERNARY_90},{COLOR_TERNARY_80}");
|
||||||
|
|
||||||
async fn generate_random_avatar(url: String) -> Option<String> {
|
|
||||||
let seed = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
|
|
||||||
let req = format!(
|
|
||||||
"https://{url}/7.x/shapes/svg?\
|
|
||||||
seed={seed}&\
|
|
||||||
backgroundColor={BACKGROUND_COLORS_STR}&\
|
|
||||||
shape1Color={SHAPE_1_COLORS_STR}&\
|
|
||||||
shape2Color={SHAPE_2_COLORS_STR}&\
|
|
||||||
shape3Color={SHAPE_3_COLORS_STR}"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut res: Option<String> = None;
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
const REQUIRED_ERROR_NAME: &str = "required";
|
const REQUIRED_ERROR_NAME: &str = "required";
|
||||||
const REQUIRED_ERROR_HELPER_TEXT: &str = "This field must be completed";
|
const REQUIRED_ERROR_HELPER_TEXT: &str = "This field must be completed";
|
||||||
|
|
||||||
@@ -591,12 +561,7 @@ impl<'a> PasswordSuggestionsModalConfig<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Props, Clone, PartialEq)]
|
pub fn Login() -> Element {
|
||||||
pub struct LoginProps {
|
|
||||||
dicebear_hostname: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn Login(props: LoginProps) -> Element {
|
|
||||||
debug!("Login rendering");
|
debug!("Login rendering");
|
||||||
|
|
||||||
let mut data = use_signal(Data::new);
|
let mut data = use_signal(Data::new);
|
||||||
@@ -632,31 +597,23 @@ pub fn Login(props: LoginProps) -> Element {
|
|||||||
let mut spinner_animated = use_signal(|| false);
|
let mut spinner_animated = use_signal(|| false);
|
||||||
let mut id_placeholder = use_signal(|| LOGIN_ID_PLACEHOLDER);
|
let mut id_placeholder = use_signal(|| LOGIN_ID_PLACEHOLDER);
|
||||||
|
|
||||||
let url = props
|
let mut random_avatar_future = use_resource(move || async move {
|
||||||
.dicebear_hostname
|
let shape_config = ShapeConfig::new(
|
||||||
.unwrap_or("dicebear.tools.adrien.run".to_string());
|
BACKGROUND_COLORS_STR,
|
||||||
|
SHAPE_1_COLORS_STR,
|
||||||
let mut random_avatar_future = use_resource(move || {
|
SHAPE_2_COLORS_STR,
|
||||||
to_owned![url];
|
SHAPE_3_COLORS_STR,
|
||||||
async move { generate_random_avatar(url).await }
|
);
|
||||||
|
generate_random_svg_shape(Some(&shape_config)).await
|
||||||
});
|
});
|
||||||
|
|
||||||
let avatar = match &*random_avatar_future.read_unchecked() {
|
let avatar = match &*random_avatar_future.read_unchecked() {
|
||||||
Some(Some(svg)) => {
|
Some(svg) => Some(rsx! {
|
||||||
rsx!(div {
|
div {
|
||||||
class: ClassName::LOGIN_FORM_PHOTO_CONTENT,
|
class: ClassName::LOGIN_FORM_PHOTO_CONTENT,
|
||||||
dangerous_inner_html: svg.as_str(),
|
dangerous_inner_html: svg.as_str(),
|
||||||
})
|
}
|
||||||
}
|
}),
|
||||||
Some(None) => {
|
|
||||||
warn!("No profile image set or generated, display the placeholder");
|
|
||||||
rsx!(div {
|
|
||||||
class: ClassName::LOGIN_FORM_PHOTO_CONTENT,
|
|
||||||
img {
|
|
||||||
src: "./images/login-profile-placeholder.svg"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use rand::distributions::{Alphanumeric, DistString};
|
|
||||||
use tracing::{error, warn};
|
|
||||||
|
|
||||||
use super::button::{ErrorButton, SuccessButton, WarningButton};
|
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"));
|
include!(concat!(env!("OUT_DIR"), "/style_vars.rs"));
|
||||||
|
|
||||||
use style::{COLOR_CRITICAL_100, COLOR_SUCCESS_100, COLOR_WARNING_100};
|
use style::{COLOR_CRITICAL_100, COLOR_SUCCESS_100, COLOR_WARNING_100};
|
||||||
@@ -20,23 +21,25 @@ pub enum Severity {
|
|||||||
Warning,
|
Warning,
|
||||||
Critical,
|
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> {
|
fn avatar_configs() -> &'static HashMap<Severity, AvatarConfig<'static>> {
|
||||||
gesture: &'a str,
|
static HASHMAP: OnceLock<HashMap<Severity, AvatarConfig>> = OnceLock::new();
|
||||||
color: &'a str,
|
HASHMAP.get_or_init(|| {
|
||||||
browns: Vec<u32>,
|
let mut configs = HashMap::new();
|
||||||
eyes: Vec<u32>,
|
configs.insert(
|
||||||
lips: Vec<u32>,
|
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)]
|
#[derive(Props, Clone, PartialEq)]
|
||||||
@@ -49,131 +52,20 @@ pub struct ModalProps {
|
|||||||
pub on_confirm: Option<EventHandler<MouseEvent>>,
|
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]
|
#[component]
|
||||||
pub fn Modal(props: ModalProps) -> Element {
|
pub fn Modal(props: ModalProps) -> Element {
|
||||||
// TODO: Use configuration file
|
let avatar_config = avatar_configs().get(&props.severity);
|
||||||
let url = "dicebear.tools.adrien.run";
|
|
||||||
|
|
||||||
let random_figure_future =
|
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() {
|
let icon = match &*random_figure_future.read_unchecked() {
|
||||||
Some(Some(svg)) => Some(rsx! {
|
Some(svg) => Some(rsx! {
|
||||||
div {
|
div {
|
||||||
class: ClassName::MODAL_CONTENT_ICON_PLACEHOLDER,
|
class: ClassName::MODAL_CONTENT_ICON_PLACEHOLDER,
|
||||||
dangerous_inner_html: svg.as_str(),
|
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,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -183,7 +75,7 @@ pub fn Modal(props: ModalProps) -> Element {
|
|||||||
Severity::Critical => ErrorButton,
|
Severity::Critical => ErrorButton,
|
||||||
};
|
};
|
||||||
|
|
||||||
figure.as_ref()?;
|
icon.as_ref()?;
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
style { {STYLE_SHEET} },
|
style { {STYLE_SHEET} },
|
||||||
@@ -196,7 +88,7 @@ pub fn Modal(props: ModalProps) -> Element {
|
|||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::MODAL_CONTENT_ICON,
|
class: ClassName::MODAL_CONTENT_ICON,
|
||||||
{figure}
|
{icon}
|
||||||
},
|
},
|
||||||
|
|
||||||
div {
|
div {
|
||||||
|
@@ -70,6 +70,10 @@ $modal-max-height: 55vh;
|
|||||||
width: calc(100% + (2 * $border-normal-width));
|
width: calc(100% + (2 * $border-normal-width));
|
||||||
height: calc(100% - (2 * $border-normal-width));
|
height: calc(100% - (2 * $border-normal-width));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 105%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
|
1
src/data/datasources/mod.rs
Normal file
1
src/data/datasources/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod random_svg_generators;
|
231
src/data/datasources/random_svg_generators.rs
Normal file
231
src/data/datasources/random_svg_generators.rs
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::Result as IoResult;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use rand::distributions::{Alphanumeric, DistString};
|
||||||
|
use reqwest::Result as RequestResult;
|
||||||
|
use tokio::fs::read_to_string;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash)]
|
||||||
|
pub enum AvatarFeeling {
|
||||||
|
Ok,
|
||||||
|
Warning,
|
||||||
|
Alerting,
|
||||||
|
}
|
||||||
|
impl fmt::Display for AvatarFeeling {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let repr = match self {
|
||||||
|
Self::Ok => "Ok",
|
||||||
|
Self::Warning => "Warning",
|
||||||
|
Self::Alerting => "Alerting",
|
||||||
|
};
|
||||||
|
write!(f, "{repr}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AvatarConfig<'a> {
|
||||||
|
feeling: AvatarFeeling,
|
||||||
|
background_color: &'a str,
|
||||||
|
}
|
||||||
|
impl<'a> AvatarConfig<'a> {
|
||||||
|
pub fn new(feeling: AvatarFeeling, background_color: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
feeling,
|
||||||
|
background_color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DicebearType {
|
||||||
|
Notionists,
|
||||||
|
Shapes,
|
||||||
|
}
|
||||||
|
impl fmt::Display for DicebearType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let repr = match self {
|
||||||
|
Self::Notionists => "notionists",
|
||||||
|
Self::Shapes => "shapes",
|
||||||
|
};
|
||||||
|
write!(f, "{repr}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DicebearConfig<'a> {
|
||||||
|
gesture: &'a str,
|
||||||
|
browns: Vec<u32>,
|
||||||
|
eyes: Vec<u32>,
|
||||||
|
lips: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dicebear_variants() -> &'static HashMap<AvatarFeeling, DicebearConfig<'static>> {
|
||||||
|
static HASHMAP: OnceLock<HashMap<AvatarFeeling, DicebearConfig>> = OnceLock::new();
|
||||||
|
HASHMAP.get_or_init(|| {
|
||||||
|
let mut variants = HashMap::new();
|
||||||
|
variants.insert(
|
||||||
|
AvatarFeeling::Alerting,
|
||||||
|
DicebearConfig {
|
||||||
|
gesture: "wavePointLongArms",
|
||||||
|
browns: vec![2, 6, 11, 13],
|
||||||
|
eyes: vec![2, 4],
|
||||||
|
lips: vec![1, 2, 7, 11, 19, 20, 24, 27],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
variants.insert(
|
||||||
|
AvatarFeeling::Warning,
|
||||||
|
DicebearConfig {
|
||||||
|
gesture: "pointLongArm",
|
||||||
|
browns: vec![2, 5, 10, 13],
|
||||||
|
eyes: vec![1, 3],
|
||||||
|
lips: vec![1, 2, 4, 8, 10, 13, 18, 21, 29],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
variants.insert(
|
||||||
|
AvatarFeeling::Ok,
|
||||||
|
DicebearConfig {
|
||||||
|
gesture: "okLongArm",
|
||||||
|
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 fetch_text(req: String) -> RequestResult<String> {
|
||||||
|
reqwest::get(req).await?.text().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_file(path: &str) -> IoResult<String> {
|
||||||
|
read_to_string(path).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_dicebear_svg(
|
||||||
|
r#type: &DicebearType,
|
||||||
|
req_fields: &Vec<String>,
|
||||||
|
placeholder_path: Option<&str>,
|
||||||
|
) -> String {
|
||||||
|
// TODO: Use configuration file
|
||||||
|
let url = "dicebear.tools.adrien.run";
|
||||||
|
|
||||||
|
let seed = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
|
||||||
|
let type_str = r#type.to_string();
|
||||||
|
let url = format!(
|
||||||
|
"https://{url}/7.x/{type_str}/svg?seed={seed}{}{}",
|
||||||
|
if !req_fields.is_empty() { "&" } else { " " },
|
||||||
|
req_fields.join("&")
|
||||||
|
);
|
||||||
|
|
||||||
|
let text = match fetch_text(url).await {
|
||||||
|
Ok(text) => Some(text),
|
||||||
|
Err(err) => {
|
||||||
|
error!("Error during placeholder loading: {}", err);
|
||||||
|
|
||||||
|
match placeholder_path {
|
||||||
|
Some(placeholder_path) => match read_file(placeholder_path).await {
|
||||||
|
Ok(content) => Some(content),
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"Error during to read {placeholder_path} file: {}",
|
||||||
|
err.to_string()
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
text.unwrap_or("".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn generate_random_svg_avatar<'a>(config: Option<&'a AvatarConfig<'a>>) -> String {
|
||||||
|
let (variant, feeling) = match config {
|
||||||
|
Some(config) => (dicebear_variants().get(&config.feeling), &config.feeling),
|
||||||
|
None => (None, &AvatarFeeling::Alerting),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut req_fields = Vec::<String>::new();
|
||||||
|
|
||||||
|
if let Some(config) = config {
|
||||||
|
req_fields.push(format!("backgroundColor={}", config.background_color));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(variant) = variant {
|
||||||
|
req_fields.push(format!(
|
||||||
|
"gestureProbability=100&gesture={}",
|
||||||
|
&variant.gesture
|
||||||
|
));
|
||||||
|
req_fields.push(format!(
|
||||||
|
"&browsProbability=100&brows={}",
|
||||||
|
render_dicebear_variants(&variant.browns)
|
||||||
|
));
|
||||||
|
req_fields.push(format!(
|
||||||
|
"&eyesProbability=100&eyes={}",
|
||||||
|
render_dicebear_variants(&variant.eyes)
|
||||||
|
));
|
||||||
|
req_fields.push(format!(
|
||||||
|
"&lipsProbability=100&lips={}",
|
||||||
|
render_dicebear_variants(&variant.lips)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let placeholder_path = match feeling {
|
||||||
|
AvatarFeeling::Ok => "./images/modal-default-ok-icon.svg",
|
||||||
|
AvatarFeeling::Warning => "./images/modal-default-warning-icon.svg",
|
||||||
|
AvatarFeeling::Alerting => "./images/modal-default-critical-icon.svg",
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch_dicebear_svg(
|
||||||
|
&DicebearType::Notionists,
|
||||||
|
&req_fields,
|
||||||
|
Some(placeholder_path),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShapeConfig<'a> {
|
||||||
|
background_color: &'a str,
|
||||||
|
shape_1_color: &'a str,
|
||||||
|
shape_2_color: &'a str,
|
||||||
|
shape_3_color: &'a str,
|
||||||
|
}
|
||||||
|
impl<'a> ShapeConfig<'a> {
|
||||||
|
pub fn new(
|
||||||
|
background_color: &'a str,
|
||||||
|
shape_1_color: &'a str,
|
||||||
|
shape_2_color: &'a str,
|
||||||
|
shape_3_color: &'a str,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
background_color,
|
||||||
|
shape_1_color,
|
||||||
|
shape_2_color,
|
||||||
|
shape_3_color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn generate_random_svg_shape<'a>(config: Option<&'a ShapeConfig<'a>>) -> String {
|
||||||
|
let mut req_fields = Vec::<String>::new();
|
||||||
|
|
||||||
|
if let Some(config) = config {
|
||||||
|
req_fields.push(format!("backgroundColor={}", config.background_color));
|
||||||
|
req_fields.push(format!("shape1Color={}", config.shape_1_color));
|
||||||
|
req_fields.push(format!("shape2Color={}", config.shape_2_color));
|
||||||
|
req_fields.push(format!("shape3Color={}", config.shape_3_color));
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_dicebear_svg(&DicebearType::Shapes, &req_fields, None).await
|
||||||
|
}
|
1
src/data/mod.rs
Normal file
1
src/data/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod datasources;
|
@@ -1,8 +1,9 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
pub mod components;
|
mod components;
|
||||||
pub mod matrix_interface;
|
mod data;
|
||||||
pub mod utils;
|
mod matrix_interface;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
|
Reference in New Issue
Block a user