diff --git a/Cargo.lock b/Cargo.lock index db75d31..7b3b057 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -721,6 +721,8 @@ dependencies = [ "actix-files", "actix-web", "console_error_panic_hook", + "futures", + "gloo-net 0.5.0", "http 1.2.0", "leptos", "leptos_actix", @@ -731,6 +733,7 @@ dependencies = [ "tokio", "uuid", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -1123,6 +1126,27 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "gloo-net" version = "0.6.0" @@ -1735,7 +1759,7 @@ checksum = "8d71dea7d42c0d29c40842750232d3425ed1cf10e313a1f898076d20871dad32" dependencies = [ "cached", "cfg-if", - "gloo-net", + "gloo-net 0.6.0", "itertools", "js-sys", "lazy_static", @@ -2662,7 +2686,7 @@ dependencies = [ "const_format", "dashmap", "futures", - "gloo-net", + "gloo-net 0.6.0", "http 1.2.0", "inventory", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 58aba4b..ebc0942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,9 @@ uuid = { version = "1.0", features = ["v4"] } web-sys = { version = "0.3", features = ["Event"] } nostr-sdk = "0.37" tokio = "1" +gloo-net = "0.5" +futures = "0.3" +wasm-bindgen-futures = "0.4" [features] csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"] diff --git a/src/app.rs b/src/app.rs index d12ac5e..210d358 100644 --- a/src/app.rs +++ b/src/app.rs @@ -39,6 +39,7 @@ pub fn App() -> impl IntoView { description, tags, reviews: vec![ReviewWithRating { content: review.clone(), rating }], + wikidata_id: None, }; items.push(item); }); diff --git a/src/components/items_list.rs b/src/components/items_list.rs index 19102c8..025de27 100644 --- a/src/components/items_list.rs +++ b/src/components/items_list.rs @@ -2,12 +2,75 @@ /// Iterates through the items and renders their name, description, tags, and reviews. use leptos::*; use crate::models::item::Item; +use serde::Deserialize; +use futures::future; +use gloo_net::http::Request; +use wasm_bindgen_futures::spawn_local; +use std::sync::Arc; + +// Define the structure for Wikidata API response +#[derive(Deserialize, Clone, Debug)] +struct WikidataResponse { + entities: std::collections::HashMap, +} +#[derive(Deserialize, Clone, Debug)] +struct WikidataEntity { + labels: Option>, + descriptions: Option>, +} + +#[derive(Deserialize, Clone, Debug)] +struct WikidataLabel { + value: String, +} #[component] pub fn ItemsList(items: ReadSignal>) -> impl IntoView { // Create a signal for selected items let (selected_items_signal, set_selected_items) = create_signal(Vec::::new()); + let (wikidata_data, set_wikidata_data) = create_signal(Vec::>::new()); + // Fetch additional data from Wikidata for selected items + let fetch_wikidata = move || { + let selected_indices = selected_items_signal.get(); + let selected_items: Vec = selected_indices + .iter() + .map(|&i| items.get()[i].clone()) + .collect(); + + + // Wrap `selected_items` in an `Arc` so it can be cloned + let selected_items = Arc::new(selected_items); + + // Clone selected_items before the async block to avoid borrowing issues + let selected_items_clone: Arc> = Arc::clone(&selected_items); + + // For each selected item, fetch Wikidata attributes + let futures = selected_items_clone.iter().map(move |item| { + let wikidata_id = item.wikidata_id.clone(); + async move { + if let Some(id) = wikidata_id { + let url = format!("https://www.wikidata.org/wiki/Special:EntityData/{}.json", id); + match Request::get(&url).send().await { + Ok(response) => match response.json::().await { + Ok(parsed_data) => parsed_data.entities.get(&id).cloned(), + Err(_) => None, + }, + Err(_) => None, + } + } else { + None + } + } + }).collect::>(); + + + spawn_local(async move { + let results = future::join_all(futures).await; + set_wikidata_data.set(results); + }); + }; + // Function to toggle selection of an item let toggle_selection = move |i: usize| { set_selected_items.update(|items| { @@ -52,6 +115,9 @@ pub fn ItemsList(items: ReadSignal>) -> impl IntoView { }).collect::>() } + // Button to fetch Wikidata attributes + + // Comparison Table

{ "Comparison Table" }

@@ -61,13 +127,15 @@ pub fn ItemsList(items: ReadSignal>) -> impl IntoView { + {move || { let selected_indices = selected_items_signal.get(); - selected_indices.iter().map(|&i| { + selected_indices.iter().enumerate().map(|(idx, &i)| { let item = &items.get()[i]; + let wikidata_entity = wikidata_data.get().get(idx).cloned().flatten(); view! { @@ -82,6 +150,20 @@ pub fn ItemsList(items: ReadSignal>) -> impl IntoView { { format!("Rating: {}/5 ", review.rating) } }).collect::>()} + } }).collect::>() diff --git a/src/models/item.rs b/src/models/item.rs index 5611adf..efb9d2f 100644 --- a/src/models/item.rs +++ b/src/models/item.rs @@ -9,6 +9,7 @@ pub struct Item { pub description: String, pub tags: Vec<(String, String)>, pub reviews: Vec, + pub wikidata_id: Option, } #[derive(Serialize, Deserialize, Debug, Clone)]
{ "Description" } { "Tags" } { "Reviews" }{ "External Description (Wikidata)" }
{ &item.name } + {move || { + let entity = wikidata_entity.clone(); // Clone the value + match entity { + Some(entity) => entity + .descriptions + .as_ref() + .and_then(|descriptions| descriptions.get("en")) + .map(|label| label.value.clone()) + .unwrap_or_default(), + None => String::from("No description"), + } + }} +