|
1 | 1 | use crate::api::BACKEND_URL; |
2 | 2 | use crate::{ActionButtonState, ActionButtonStateContext}; |
3 | 3 | use gloo::timers::callback::Interval; |
4 | | -use gloo_net::http::QueryParams; |
| 4 | +use gloo_net::http::{QueryParams, Request}; |
5 | 5 | use std::cell::Cell; |
6 | 6 | use std::rc::Rc; |
| 7 | +use yew::HtmlResult; |
7 | 8 | use yew::prelude::*; |
| 9 | +use yew::suspense::use_future_with; |
| 10 | + |
| 11 | +#[derive(Clone, PartialEq)] |
| 12 | +enum CompileResult { |
| 13 | + Done(String), |
| 14 | + ColdStartTimeout, |
| 15 | +} |
8 | 16 |
|
9 | 17 | #[derive(Properties, PartialEq, Eq)] |
10 | 18 | pub struct OutputContainerProps { |
11 | 19 | pub value: Rc<str>, |
12 | 20 | } |
13 | 21 |
|
14 | 22 | #[component] |
15 | | -pub fn OutputContainer(props: &OutputContainerProps) -> Html { |
| 23 | +pub fn OutputContainer(props: &OutputContainerProps) -> HtmlResult { |
16 | 24 | let action_button_state = use_context::<ActionButtonStateContext>().unwrap(); |
17 | | - let loading = use_state(|| true); |
18 | | - let elapsed = use_state(|| 0u32); |
19 | | - let src = use_state(String::new); |
20 | 25 |
|
21 | | - { |
22 | | - let loading = loading.clone(); |
23 | | - let src = src.clone(); |
24 | | - let elapsed = elapsed.clone(); |
25 | | - use_effect_with(Rc::clone(&props.value), move |value| { |
26 | | - elapsed.set(0); |
27 | | - loading.set(true); |
28 | | - src.set({ |
29 | | - let query = QueryParams::new(); |
30 | | - query.append("code", value); |
31 | | - format!("{}/run?{}", BACKEND_URL, query) |
32 | | - }); |
33 | | - }) |
34 | | - }; |
| 26 | + let result = use_future_with(Rc::clone(&props.value), |value| async move { |
| 27 | + let query = QueryParams::new(); |
| 28 | + query.append("code", &value); |
| 29 | + let url = format!("{}/run?{}", BACKEND_URL, query); |
| 30 | + |
| 31 | + match Request::get(&url).send().await { |
| 32 | + Ok(resp) if resp.status() == 504 => CompileResult::ColdStartTimeout, |
| 33 | + Ok(resp) => CompileResult::Done(resp.text().await.unwrap_or_default()), |
| 34 | + Err(_) => CompileResult::ColdStartTimeout, |
| 35 | + } |
| 36 | + })?; |
35 | 37 |
|
36 | 38 | { |
37 | | - let elapsed = elapsed.clone(); |
38 | | - let is_loading = *loading; |
39 | | - use_effect_with(is_loading, move |is_loading| { |
40 | | - let _interval = is_loading.then(|| { |
41 | | - let counter = Rc::new(Cell::new(0u32)); |
42 | | - Interval::new(1_000, { |
43 | | - let counter = counter.clone(); |
44 | | - let elapsed = elapsed.clone(); |
45 | | - move || { |
46 | | - let next = counter.get() + 1; |
47 | | - counter.set(next); |
48 | | - elapsed.set(next); |
49 | | - } |
50 | | - }) |
51 | | - }); |
52 | | - move || drop(_interval) |
| 39 | + let action_button_state = action_button_state.clone(); |
| 40 | + use_effect(move || { |
| 41 | + action_button_state.dispatch(ActionButtonState::Enabled); |
53 | 42 | }); |
54 | 43 | } |
55 | 44 |
|
56 | | - let onload = { |
57 | | - let loading = loading.clone(); |
58 | | - move |_| { |
59 | | - loading.set(false); |
60 | | - action_button_state.dispatch(ActionButtonState::Enabled); |
| 45 | + Ok(match &*result { |
| 46 | + CompileResult::Done(html_content) => { |
| 47 | + html! { <iframe srcdoc={html_content.clone()} class="w-full h-full" /> } |
61 | 48 | } |
62 | | - }; |
63 | | - |
64 | | - let classes = classes!( |
65 | | - "w-full", |
66 | | - "h-full", |
67 | | - if *loading { "invisible" } else { "visible" } |
68 | | - ); |
69 | | - |
70 | | - html! { |
71 | | - <> |
72 | | - if *loading { |
| 49 | + CompileResult::ColdStartTimeout => { |
| 50 | + html! { |
73 | 51 | <div class="h-full bg-gray-600 flex items-center justify-center"> |
74 | | - <div class="text-gray-200 text-lg flex flex-col items-center gap-2"> |
75 | | - <span class="animate-spin inline-block w-8 h-8 border-[3px] border-gray-200 border-t-transparent rounded-full"></span> |
76 | | - <span>{format!("waiting for the backend service... {}s", *elapsed)}</span> |
| 52 | + <div class="text-gray-200 flex flex-col items-center gap-3 max-w-md text-center"> |
| 53 | + <span class="text-xl font-semibold">{"Backend service is waking up"}</span> |
| 54 | + <span class="text-gray-400">{"The service was asleep and timed out on the first request. It should be ready now."}</span> |
| 55 | + <span class="text-gray-300">{"Hit Run again."}</span> |
77 | 56 | </div> |
78 | 57 | </div> |
79 | 58 | } |
80 | | - <iframe src={AttrValue::from((*src).clone())} {onload} class={classes} /> |
81 | | - </> |
| 59 | + } |
| 60 | + }) |
| 61 | +} |
| 62 | + |
| 63 | +#[component] |
| 64 | +pub fn CompileTimer() -> Html { |
| 65 | + let elapsed = use_state(|| 0u32); |
| 66 | + |
| 67 | + { |
| 68 | + let elapsed = elapsed.clone(); |
| 69 | + use_effect_with((), move |_| { |
| 70 | + let counter = Rc::new(Cell::new(0u32)); |
| 71 | + let interval = Interval::new(1_000, { |
| 72 | + let counter = counter.clone(); |
| 73 | + let elapsed = elapsed.clone(); |
| 74 | + move || { |
| 75 | + let next = counter.get() + 1; |
| 76 | + counter.set(next); |
| 77 | + elapsed.set(next); |
| 78 | + } |
| 79 | + }); |
| 80 | + move || drop(interval) |
| 81 | + }); |
| 82 | + } |
| 83 | + |
| 84 | + html! { |
| 85 | + <div class="h-full bg-gray-600 flex items-center justify-center"> |
| 86 | + <div class="text-gray-200 text-lg flex flex-col items-center gap-2"> |
| 87 | + <span class="animate-spin inline-block w-8 h-8 border-[3px] border-gray-200 border-t-transparent rounded-full"></span> |
| 88 | + <span>{format!("waiting for the backend service... {}s", *elapsed)}</span> |
| 89 | + </div> |
| 90 | + </div> |
82 | 91 | } |
83 | 92 | } |
0 commit comments