use gloo_net::http::Request; use web_sys::wasm_bindgen::convert::OptionIntoWasmAbi; use web_sys::wasm_bindgen::JsCast; use web_sys::HtmlElement; use yew::prelude::*; use yew::{classes, function_component, Callback, Html}; use crate::pages::game::GameResult; use crate::CharStatus; static NEW_WORD_URI: &str = "https://wordl.shuttleapp.rs/word"; static MAX_TRIES: usize = 5; fn set_focus(index: usize) { if let Some(w) = web_sys::window() { if let Some(d) = w.document() { if let Some(n) = d .query_selector(&format!("[tabindex='{index}']")) .ok() .flatten() { if let Some(e) = n.dyn_ref::() { e.focus().ok(); } } } } } fn string_to_html(input: &[CharStatus]) -> Html { let classes = classes!( "bg-gray-700", "w-16", "h-16", "text-center", "py-4", "font-bold", "text-lg", ); html! ( ) } #[allow(clippy::too_many_arguments)] fn fetch_new_word( word: &UseStateHandle, loading: &UseStateHandle, submitted_words: &UseStateHandle>>>, input_values: &UseStateHandle>, game_over: &UseStateHandle, length: &UseStateHandle, node_refs: &UseStateHandle>, result: &UseStateHandle, ) { let handle = word.clone(); let loading = loading.clone(); let submitted_words = submitted_words.clone(); let input_values = input_values.clone(); let game_over = game_over.clone(); let length = length.clone(); let node_refs = node_refs.clone(); let result = result.clone(); wasm_bindgen_futures::spawn_local(async move { loading.set(true); let res = Request::get(NEW_WORD_URI).send().await; if let Ok(r) = res { if let Ok(w) = r.text().await { length.set(w.len()); node_refs.set(vec![NodeRef::default(); w.len()]); input_values.set(vec![String::new(); w.len()]); handle.set(w.to_uppercase()); submitted_words.set(Vec::with_capacity(MAX_TRIES)); game_over.set(false); result.set(GameResult::Lose); loading.set(false); } } }); } #[function_component] pub fn Home() -> Html { let word: UseStateHandle = use_state(String::new); let loading: UseStateHandle = use_state(|| true); let curr_index: UseStateHandle = use_state(|| 0usize); // TODO: Remove DEBUG let inp: UseStateHandle = use_state(String::new); let length = use_state(|| 0usize); let submitted_words: UseStateHandle>>> = use_state(|| std::vec::Vec::with_capacity(MAX_TRIES)); let node_refs = use_state(|| vec![NodeRef::default(); 10]); let input_values: UseStateHandle> = use_state(|| vec![String::new(); *length]); let game_over = use_state(|| false); let result = use_state(|| GameResult::Lose); { let handle = word.clone(); let loading = loading.clone(); let submitted_words = submitted_words.clone(); let input_values = input_values.clone(); let game_over = game_over.clone(); let length = length.clone(); let node_refs = node_refs.clone(); let result = result.clone(); use_effect_with((), move |()| { fetch_new_word( &handle, &loading, &submitted_words, &input_values, &game_over, &length, &node_refs, &result, ); }); } let game_over_check = { let word = word.clone(); let submitted_words = submitted_words.clone(); let iv = input_values.clone(); let game_over = game_over.clone(); let length = length.clone(); let result = result.clone(); Callback::from(move |_| { if submitted_words.iter().count() >= *length - 1 || crate::compare_strings(&word, &iv.join("")) .iter() .all(|v| matches!(v, CharStatus::Match(_))) { if crate::compare_strings(&word, &iv.join("")) .iter() .all(|v| matches!(v, CharStatus::Match(_))) { result.set(GameResult::Win); } game_over.set(true); } }) }; let on_submit = { let input_values = input_values.clone(); let submitted_words = submitted_words.clone(); let game_over = game_over.clone(); let length = length.clone(); let word = word.clone(); let node_refs = node_refs.clone(); let loading = loading.clone(); let result = result.clone(); let curr_index = curr_index.clone(); Callback::from(move |_e: MouseEvent| { if *game_over { curr_index.set(0); let input_values = input_values.clone(); let submitted_words = submitted_words.clone(); let game_over = game_over.clone(); let length = length.clone(); let word = word.clone(); let loading = loading.clone(); let node_refs = node_refs.clone(); let result = result.clone(); fetch_new_word( &word, &loading, &submitted_words, &input_values, &game_over, &length, &node_refs, &result, ); return; } let values: Vec<_> = input_values.iter().cloned().collect(); if !values.iter().all(|v| !v.is_empty()) { return; } let mut new_items = (*submitted_words).clone(); new_items.push(crate::compare_strings(&word, &values.join(""))); submitted_words.set(new_items); input_values.set(vec![String::new(); word.len()]); set_focus(0); curr_index.set(0); game_over_check.emit(MouseEvent::none()); }) }; let on_enter = { let curr_index = curr_index.clone(); let length = length.clone(); let on_submit = on_submit.clone(); let input_values = input_values.clone(); let node_refs = node_refs.clone(); Callback::from(move |e: KeyboardEvent| match e.key().as_ref() { "Enter" => { if let Ok(m) = MouseEvent::new("click") { on_submit.emit(m); } } "Backspace" => { e.prevent_default(); let index = *curr_index; let mut values = (*input_values).clone(); values[index] = String::new(); input_values.set(values); if node_refs[index] .cast::() .is_some() && index > 0 { let index = index - 1; curr_index.set(index); set_focus(index); } } k => { let index = *curr_index; let mut values = (*input_values).clone(); if k.len() == 1 && k.chars().all(char::is_alphabetic) { values[index] = k.to_uppercase(); input_values.set(values); if node_refs[index] .cast::() .is_some() && index < *length { let index = index + 1; curr_index.set(index); set_focus(index); } } else { values[index] = String::new(); input_values.set(values); } } }) }; let on_input = { let curr_index = curr_index.clone(); let length = length.clone(); let on_submit = on_submit.clone(); let input_values = input_values.clone(); let node_refs = node_refs.clone(); // TODO: Remove DEBUG let inp = inp.clone(); Callback::from(move |e: InputEvent| { let event = e.dyn_into::().ok(); if let Some(e) = event.as_ref() { match e.key().as_ref() { "Enter" => { if let Ok(m) = MouseEvent::new("click") { on_submit.emit(m); } } "Backspace" => { let index = *curr_index; let mut values = (*input_values).clone(); values[index] = String::new(); input_values.set(values); if node_refs[index] .cast::() .is_some() && index > 0 { let index = index - 1; curr_index.set(index); set_focus(index); } } k => { let index = *curr_index; let mut values = (*input_values).clone(); // TODO: Remove DEBUG inp.set(k.to_owned()); if k.len() == 1 && k.chars().all(char::is_alphabetic) { values[index] = k.to_uppercase(); input_values.set(values); if node_refs[index] .cast::() .is_some() && index < *length { let index = index + 1; curr_index.set(index); set_focus(index); } } else { values[index] = String::new(); input_values.set(values); } } } } }) }; let view = { move || { html! {
// TODO: Remove DEBUG

{(*inp).clone()}

{ if *loading { html!(

{"Loading..."}

) } else if *game_over { let (text, color) = match *result { GameResult::Win => { ("FOUND", "bg-green-600") }, GameResult::Lose => { ("WANTED", "bg-red-600") } }; html! (

{ text }

    { word.chars().map(|e|{ let text = e; html!{
  • {text}
  • }}).collect::() }
) } else if !*game_over { node_refs.iter().enumerate().map(|(index, node_ref)| { let on_focus = { let curr_index = curr_index.clone(); Callback::from(move |e: FocusEvent| { let target = e.target_unchecked_into::(); if let Some(index) = target.get_attribute("tabindex") { if let Ok(i) = index.parse::() { curr_index.set(i); } } }) }; html! { } }).collect::() } else { html!(
) } }
{ for submitted_words.iter().map(|e| {string_to_html(e)})}
} } }; view() }