Skip to content

Instantly share code, notes, and snippets.

@Enigo
Last active January 13, 2024 11:40
Show Gist options
  • Save Enigo/907643366a7cd9f9a84bab0ecd77682e to your computer and use it in GitHub Desktop.
Save Enigo/907643366a7cd9f9a84bab0ecd77682e to your computer and use it in GitHub Desktop.
use crate::view::loading::LoadingSpinnerGrayNoVh;
use crate::Route;
use gloo_timers::callback::Timeout;
use log::error;
use model::model::search::SearchData;
use web_sys::wasm_bindgen::JsCast;
use web_sys::HtmlInputElement;
use yew::prelude::*;
use yew_router::prelude::*;
#[function_component(Search)]
pub fn search() -> Html {
let search = use_state(|| String::new());
let search_data = use_state(|| None);
let typed_state = use_state(|| false);
{
let search = search.clone();
let search_data = search_data.clone();
use_effect_with((search.clone(), *typed_state), move |_| {
if !search.is_empty() {
let search = search.clone();
wasm_bindgen_futures::spawn_local(async move {
match fetch_single_api_response(
format!("search?search={}", search.as_str()).as_str(),
)
.await
{
Ok(fetched_search) => {
search_data.set(Some(fetched_search));
}
Err(e) => {
error!("{e}")
}
}
});
}
});
}
let timeout_state = use_state(|| None);
let typed_state_clone = typed_state.clone();
let search_data_clone = search_data.clone();
let search_clone = search.clone();
let oninput = Callback::from(move |e: InputEvent| {
if let Some(target) = e.target() {
let input = target.dyn_into::<HtmlInputElement>().ok();
if let Some(input) = input {
let value = input.value();
if value.is_empty() {
search_data_clone.set(None);
typed_state_clone.set(false);
} else {
typed_state_clone.set(true);
search_data_clone.set(None);
let prev_timeout = timeout_state.clone();
if prev_timeout.is_some() {
drop(prev_timeout);
}
let search = search_clone.clone();
let timeout = Timeout::new(1_000, move || {
search.set(value);
});
timeout_state.set(Some(timeout));
}
}
}
});
let onsubmit = Callback::from(|e: SubmitEvent| {
e.prevent_default();
});
let typed_state_clone = typed_state.clone();
let onfocusin = Callback::from(move |e: FocusEvent| {
if let Some(target) = e.target() {
let input = target.dyn_into::<HtmlInputElement>().ok();
if let Some(input) = input {
let value = input.value();
if !value.is_empty() {
typed_state_clone.set(true);
search.set(value);
}
}
}
});
let typed_state_clone = typed_state.clone();
let search_data_clone = search_data.clone();
let onfocusout = Callback::from(move |_| {
let typed_state_clone = typed_state_clone.clone();
let search_data_clone = search_data_clone.clone();
Timeout::new(150, move || {
search_data_clone.set(None);
typed_state_clone.set(false);
})
.forget();
});
let typed_state_clone = typed_state.clone();
let search_data_clone = search_data.clone();
let onclick = Callback::from(move |_| {
search_data_clone.set(None);
typed_state_clone.set(false);
});
let search_result_html = match (*search_data).as_ref() {
Some(search_data) => {
if search_data.asset_content_data.is_empty() {
html! {<li><p class="dropdown-item text-white my-2">{"No results found..."}</p></li>}
} else {
search_data.asset_content_data.iter().map(|asset| {
let onclick = onclick.clone();
html!(
<li>
<div class="align-items-center text-center text-white m-3" {onclick}>
<Link<Route> to={Route::Asset {token_address: asset.token_address.to_string(), token_id: asset.token_id} }>
<img src={asset.image_url.clone()} class="img-fluid" width="50%"/>
</Link<Route>>
<p class="m-0">{asset.name.clone()}</p>
</div>
</li>
)
}).collect::<Html>()
}
}
None => {
html! {
<LoadingSpinnerGrayNoVh />
}
}
};
html! {
<div class="row">
<div class="col-md-8">
<form class="input-group bg-dark border border-white rounded" {onsubmit} {onfocusin} {onfocusout}>
<input id="search" type="search" autocomplete="off" class="form-control bg-dark border-0 text-white shadow-none" placeholder="Token Id or Name" aria-label="Search"
{oninput}/>
<span class="input-group-text bg-dark border-0 rounded text-white"><i class="fas fa-search"></i></span>
<div class="w-100">
<ul class={format!("dropdown-menu bg-gray border border-top-0 border-1 border-white w-100 max-vh-90 p-0 overflow-auto {}", if *typed_state.clone() {"show"} else {"hide"})}>
{ search_result_html }
</ul>
</div>
</form>
</div>
</div>
}
}
async fn fetch_single_api_response(endpoint: &str) -> reqwest::Result<SearchData> {
let result = reqwest::get(format!("http://localhost:8081/api/{}", endpoint))
.await?
.json::<SearchData>()
.await?;
return Ok(result);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment