yew_hcaptcha/
lib.rs

1use gloo_utils::document;
2use js_sys::Reflect;
3use log::error;
4use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
5use yew::{
6    function_component, html, use_effect_with_deps, use_state, virtual_dom::AttrValue, Callback,
7    Html, Properties,
8};
9
10#[derive(Properties, PartialEq)]
11pub struct Props {
12    pub site_key: AttrValue,
13    pub on_load: Option<Callback<()>>,
14}
15
16#[function_component]
17pub fn HCaptcha(props: &Props) -> Html {
18    let loaded = use_state(|| false);
19    use_effect_with_deps(
20        move |on_load| {
21            if !*loaded {
22                if let Err(e) = inject_script(on_load) {
23                    error!("{:?}", e);
24                }
25            }
26            loaded.set(true);
27            || ()
28        },
29        props.on_load.clone(),
30    );
31    html! {
32        <>
33            <div class="h-captcha" data-sitekey={props.site_key.to_string()} data-theme="dark"></div>
34        </>
35    }
36}
37
38fn inject_script(on_load: &Option<Callback<()>>) -> Result<(), JsValue> {
39    let hcaptcha_loaded = Closure::wrap(Box::new({
40        let on_load = on_load.clone();
41        move || {
42            if let Some(on_load) = &on_load {
43                on_load.emit(());
44            }
45        }
46    }) as Box<dyn FnMut()>);
47    Reflect::set(
48        &JsValue::from(web_sys::window().unwrap()),
49        &JsValue::from("hCaptchaLoaded"),
50        hcaptcha_loaded.as_ref().unchecked_ref(),
51    )?;
52    hcaptcha_loaded.forget();
53    let script = document().create_element("script").unwrap();
54    script.set_attribute("async", "true")?;
55    script.set_attribute("defer", "true")?;
56    script.set_attribute(
57        "src",
58        "https://js.hcaptcha.com/1/api.js?hl=en&onload=hCaptchaLoaded",
59    )?;
60    script.set_attribute("type", "text/javascript")?;
61    let body = document()
62        .body()
63        .ok_or(JsValue::from_str("Can't find body"))?;
64    body.append_child(&script)?;
65    Ok(())
66}