1use js_sys::JsString;
2use std::borrow::Cow;
3use std::rc::Rc;
4use wasm_bindgen::JsCast;
5use wasm_bindgen_futures::JsFuture;
6use web_sys::{Request, RequestInit, RequestMode, Response};
7
8use yew::{create_portal, function_component, html, Properties, Reducible};
9
10#[doc(hidden)]
11pub use wasm_bindgen_futures;
12
13#[doc(hidden)]
14pub async fn fetch_script(url: Cow<'static, str>) -> String {
15 let mut opts = RequestInit::new();
16 opts.method("GET");
17 opts.mode(RequestMode::Cors);
18
19 let request = Request::new_with_str_and_init(&url, &opts).unwrap();
20
21 let window = web_sys::window().unwrap();
22 let resp_value = JsFuture::from(window.fetch_with_request(&request))
23 .await
24 .unwrap();
25
26 assert!(resp_value.is_instance_of::<Response>());
28 let resp: Response = resp_value.dyn_into().unwrap();
29
30 let text: JsString = JsFuture::from(resp.text().unwrap())
31 .await
32 .unwrap()
33 .unchecked_into();
34 text.into()
35}
36
37#[doc(hidden)]
38pub enum ScriptLoader {
39 NotRequested,
40 Started,
41 Completed(Script),
42}
43
44impl Default for ScriptLoader {
45 fn default() -> Self {
46 Self::NotRequested
47 }
48}
49
50#[doc(hidden)]
51pub enum ScriptLoaderAction {
52 Start,
53 Finish(Rc<String>),
54}
55
56impl Reducible for ScriptLoader {
57 type Action = ScriptLoaderAction;
58
59 fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
60 match action {
61 ScriptLoaderAction::Start => match *self {
62 ScriptLoader::NotRequested => Rc::new(Self::Started),
63 ScriptLoader::Started => unreachable!(
64 "script already started to load, can't receive another start request"
65 ),
66 ScriptLoader::Completed(_) => unreachable!(
67 "script load already completed, can't receive another start request"
68 ),
69 },
70 ScriptLoaderAction::Finish(content) => match *self {
71 ScriptLoader::NotRequested => {
72 unreachable!("script finished load, request should have started")
73 }
74 ScriptLoader::Started => Rc::new(Self::Completed(Script { content })),
75 ScriptLoader::Completed(_) => {
76 unreachable!("script completed load, can't receive another complete request")
77 }
78 },
79 }
80 }
81}
82
83#[derive(Clone, PartialEq)]
85pub struct Script {
86 content: Rc<String>,
87}
88
89#[cfg_attr(documenting, doc(cfg(feature = "script")))]
90#[derive(Properties, PartialEq)]
91pub struct ScriptEffectProps {
92 pub script: Script,
93}
94
95#[cfg_attr(documenting, doc(cfg(feature = "script")))]
96#[function_component(ScriptEffect)]
99pub fn script_effect(props: &ScriptEffectProps) -> Html {
100 create_portal(
101 html! {
102 <script type="text/javascript">
103 {props.script.content.clone()}
104 </script>
105 },
106 web_sys::window()
107 .unwrap()
108 .document()
109 .unwrap()
110 .head()
111 .unwrap()
112 .into(),
113 )
114}