use crate::Loader;
use dioxus::prelude::*;
use freya_elements::elements as dioxus_elements;
use freya_hooks::{use_applied_theme, use_focus, NetworkImageTheme, NetworkImageThemeWith};
use freya_node_state::bytes_to_data;
use reqwest::Url;
#[derive(Props)]
pub struct NetworkImageProps<'a> {
#[props(optional)]
pub theme: Option<NetworkImageThemeWith>,
pub url: Url,
#[props(optional)]
pub fallback: Option<Element<'a>>,
#[props(optional)]
pub loading: Option<Element<'a>>,
#[props(optional, into)]
pub alt: Option<String>,
}
#[derive(PartialEq)]
pub enum ImageStatus {
Loading,
Errored,
Loaded,
}
#[allow(non_snake_case)]
pub fn NetworkImage<'a>(cx: Scope<'a, NetworkImageProps<'a>>) -> Element<'a> {
let focus = use_focus(cx);
let status = use_state(cx, || ImageStatus::Loading);
let image_bytes = use_state::<Option<Vec<u8>>>(cx, || None);
let focus_id = focus.attribute(cx);
let NetworkImageTheme { width, height } =
use_applied_theme!(cx, &cx.props.theme, network_image);
let alt = cx.props.alt.as_deref();
use_effect(cx, &cx.props.url, move |url| {
to_owned![image_bytes, status];
async move {
status.set(ImageStatus::Loading);
let img = fetch_image(url).await;
if let Ok(img) = img {
image_bytes.set(Some(img));
status.set(ImageStatus::Loaded)
} else if let Err(_err) = img {
image_bytes.set(None);
status.set(ImageStatus::Errored)
}
}
});
if *status.get() == ImageStatus::Loading {
if let Some(loading_element) = &cx.props.loading {
render!(loading_element)
} else {
render!(
rect {
height: "{height}",
width: "{width}",
main_align: "center",
cross_align: "center",
Loader {}
}
)
}
} else if *status.get() == ImageStatus::Errored {
if let Some(fallback_element) = &cx.props.fallback {
render!(fallback_element)
} else {
render!(
rect {
height: "{height}",
width: "{width}",
main_align: "center",
cross_align: "center",
label {
text_align: "center",
"Error"
}
}
)
}
} else {
render! {
image_bytes.as_ref().map(|bytes| {
let image_data = bytes_to_data(cx, bytes);
rsx!(
image {
height: "{height}",
width: "{width}",
focus_id: focus_id,
image_data: image_data,
role: "image",
alt: alt
}
)
})
}
}
}
async fn fetch_image(url: Url) -> Result<Vec<u8>, reqwest::Error> {
let res = reqwest::get(url).await?;
let data = res.bytes().await?;
Ok(data.to_vec())
}