fix(reviews): handle reviews on the same form as the other items
This commit is contained in:
parent
36064e6721
commit
66aae845ea
67
src/app.rs
67
src/app.rs
|
@ -1,7 +1,7 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_meta::*;
|
use leptos_meta::*;
|
||||||
use crate::components::{item_form::ItemForm, items_list::ItemsList, review_form::ReviewForm, reviews_list::ReviewsList};
|
use crate::components::{item_form::ItemForm, items_list::ItemsList};
|
||||||
use crate::models::{item::Item, review::Review}; // Ensure Review is imported
|
use crate::models::item::Item;
|
||||||
use crate::nostr::NostrClient;
|
use crate::nostr::NostrClient;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -12,20 +12,15 @@ use nostr_sdk::serde_json;
|
||||||
pub fn App() -> impl IntoView {
|
pub fn App() -> impl IntoView {
|
||||||
provide_meta_context();
|
provide_meta_context();
|
||||||
|
|
||||||
// Signal to store and update the list of items.
|
// Signal to manage the list of items
|
||||||
let (items_signal, set_items) = create_signal(Vec::<Item>::new());
|
let (items_signal, set_items) = create_signal(Vec::<Item>::new());
|
||||||
// Signal to store the ID of the current item for reviews
|
|
||||||
let (current_item_id, set_current_item_id) = create_signal(String::new());
|
|
||||||
// Signal to store reviews for the current item
|
|
||||||
let (reviews_signal, set_reviews) = create_signal(Vec::<Review>::new());
|
|
||||||
let (tx, mut rx) = mpsc::channel::<String>(100);
|
let (tx, mut rx) = mpsc::channel::<String>(100);
|
||||||
|
|
||||||
|
// Nostr client subscription for items
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
// Initialize Nostr client
|
|
||||||
let nostr_client = NostrClient::new("wss://relay.damus.io").await.unwrap();
|
let nostr_client = NostrClient::new("wss://relay.damus.io").await.unwrap();
|
||||||
nostr_client.subscribe_to_items(tx.clone()).await.unwrap();
|
nostr_client.subscribe_to_items(tx.clone()).await.unwrap();
|
||||||
|
|
||||||
// Handle incoming events
|
|
||||||
while let Some(content) = rx.recv().await {
|
while let Some(content) = rx.recv().await {
|
||||||
if let Ok(item) = serde_json::from_str::<Item>(&content) {
|
if let Ok(item) = serde_json::from_str::<Item>(&content) {
|
||||||
set_items.update(|items| items.push(item));
|
set_items.update(|items| items.push(item));
|
||||||
|
@ -33,60 +28,36 @@ pub fn App() -> impl IntoView {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to handle adding a new item to the list.
|
// Add a new item and review using the unified form
|
||||||
let add_item = move |name: String, description: String, tags: Vec<(String, String)>| {
|
let add_item = move |name: String, description: String, tags: Vec<(String, String)>, review: String| {
|
||||||
let new_id = Uuid::new_v4().to_string(); // Generate a new UUID for the item
|
let new_id = Uuid::new_v4().to_string(); // Generate a unique ID
|
||||||
set_current_item_id.set(new_id.clone()); // Update the current item ID
|
|
||||||
|
|
||||||
set_items.update(|items| {
|
set_items.update(|items| {
|
||||||
let item = Item {
|
let item = Item {
|
||||||
id: new_id,
|
id: new_id.clone(),
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
description: description.clone(),
|
description: description.clone(),
|
||||||
tags: tags.clone(),
|
tags: tags.clone(),
|
||||||
reviews: vec![],
|
reviews: vec![review.clone()], // Initialize reviews
|
||||||
};
|
};
|
||||||
items.push(item);
|
items.push(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Publish item to Nostr
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
let nostr_client = NostrClient::new("wss://relay.example.com").await.unwrap();
|
let nostr_client = NostrClient::new("wss://relay.example.com").await.unwrap();
|
||||||
nostr_client.publish_item(name, description, tags).await.unwrap();
|
nostr_client.publish_item(name, description, tags).await.unwrap();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle review submission
|
|
||||||
let submit_review = {
|
|
||||||
let current_item_id = current_item_id.clone(); // Clone the current item ID
|
|
||||||
let user_id = "some_user_id".to_string(); // Replace with actual user ID logic
|
|
||||||
|
|
||||||
move |review_content: String| {
|
|
||||||
// Create a new review and add it to the reviews list
|
|
||||||
let new_review = Review {
|
|
||||||
content: review_content.clone(),
|
|
||||||
item_id: current_item_id.get().clone(), // Use the current item ID
|
|
||||||
user_id: user_id.clone(), // Use the user ID
|
|
||||||
};
|
|
||||||
set_reviews.update(|reviews| reviews.push(new_review));
|
|
||||||
println!("Review submitted: {}", review_content);
|
|
||||||
println!("Current reviews: {:?}", reviews_signal.get());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<>
|
<Stylesheet href="/assets/style.css" />
|
||||||
<Stylesheet href="/assets/style.css" />
|
<div>
|
||||||
<div>
|
<h1>{ "CompareWare" }</h1>
|
||||||
<h1>{ "CompareWare" }</h1>
|
// Unified form for adding an item and its first review
|
||||||
// Form component for adding new items.
|
<ItemForm on_submit=Box::new(add_item) />
|
||||||
<ItemForm on_submit=Box::new(add_item) />
|
// Display all items, including reviews
|
||||||
// Reviews form
|
<ItemsList items=items_signal />
|
||||||
<ReviewForm item_id={current_item_id.get()} on_submit={Box::new(submit_review)} />
|
</div>
|
||||||
// Component to display the list of items.
|
|
||||||
<ItemsList items=items_signal />
|
|
||||||
// Component to display the list of reviews for the current item.
|
|
||||||
<ReviewsList reviews={reviews_signal.get()} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,15 @@ use leptos::*;
|
||||||
use leptos_dom::ev::SubmitEvent;
|
use leptos_dom::ev::SubmitEvent;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ItemForm(on_submit: Box<dyn Fn(String, String, Vec<(String, String)>)>) -> impl IntoView {
|
pub fn ItemForm(
|
||||||
|
on_submit: Box<dyn Fn(String, String, Vec<(String, String)>, String) + 'static>
|
||||||
|
) -> impl IntoView {
|
||||||
let (name, set_name) = create_signal(String::new());
|
let (name, set_name) = create_signal(String::new());
|
||||||
let (description, set_description) = create_signal(String::new());
|
let (description, set_description) = create_signal(String::new());
|
||||||
let (tags, set_tags) = create_signal(Vec::<(String, String)>::new());
|
let (tags, set_tags) = create_signal(Vec::<(String, String)>::new());
|
||||||
let (tag_key, set_tag_key) = create_signal(String::new());
|
let (tag_key, set_tag_key) = create_signal(String::new());
|
||||||
let (tag_value, set_tag_value) = create_signal(String::new());
|
let (tag_value, set_tag_value) = create_signal(String::new());
|
||||||
|
let (review, set_review) = create_signal(String::new());
|
||||||
|
|
||||||
// Handle adding a new tag
|
// Handle adding a new tag
|
||||||
let add_tag = move |_| {
|
let add_tag = move |_| {
|
||||||
|
@ -21,27 +24,38 @@ pub fn ItemForm(on_submit: Box<dyn Fn(String, String, Vec<(String, String)>)>) -
|
||||||
// Handle form submission.
|
// Handle form submission.
|
||||||
let handle_submit = move |ev: SubmitEvent| {
|
let handle_submit = move |ev: SubmitEvent| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
on_submit(name.get(), description.get(), tags.get().clone());
|
on_submit(
|
||||||
|
name.get(), // Item name
|
||||||
|
description.get(), // Item description
|
||||||
|
tags.get().clone(), // Tags
|
||||||
|
review.get(), // Review
|
||||||
|
);
|
||||||
|
|
||||||
// Reset values after submission
|
// Reset values after submission
|
||||||
set_name.update(|n| *n = String::new());
|
set_name.set(String::new());
|
||||||
set_description.update(|d| *d = String::new());
|
set_description.set(String::new());
|
||||||
set_tags.update(|t| t.clear());
|
set_tags.set(vec![]);
|
||||||
|
set_review.set(String::new());
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<form on:submit=handle_submit>
|
<form on:submit=handle_submit>
|
||||||
|
// Item Name Input
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
value={name.get()}
|
value={name.get()}
|
||||||
on:input=move |e| set_name.set(event_target_value(&e))
|
on:input=move |e| set_name.set(event_target_value(&e))
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
// Item Description Input
|
||||||
<textarea
|
<textarea
|
||||||
placeholder="Description"
|
placeholder="Description"
|
||||||
value={description.get()}
|
value={description.get()}
|
||||||
on:input=move |e| set_description.set(event_target_value(&e))
|
on:input=move |e| set_description.set(event_target_value(&e))
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
// Tags Section
|
||||||
<div>
|
<div>
|
||||||
<h3>{ "Add Tags" }</h3>
|
<h3>{ "Add Tags" }</h3>
|
||||||
<input
|
<input
|
||||||
|
@ -61,9 +75,21 @@ pub fn ItemForm(on_submit: Box<dyn Fn(String, String, Vec<(String, String)>)>) -
|
||||||
<ul>
|
<ul>
|
||||||
{tags.get().iter().map(|(key, value)| view! {
|
{tags.get().iter().map(|(key, value)| view! {
|
||||||
<li>{ format!("{}: {}", key, value) }</li>
|
<li>{ format!("{}: {}", key, value) }</li>
|
||||||
}).collect::<Vec<_>>()}
|
}).collect::<Vec<_>>() }
|
||||||
</ul>
|
</ul>
|
||||||
<button type="submit">{ "Add Item" }</button>
|
|
||||||
|
// Review Input
|
||||||
|
<div>
|
||||||
|
<h3>{ "Review" }</h3>
|
||||||
|
<textarea
|
||||||
|
placeholder="Write your review here"
|
||||||
|
value={review.get()}
|
||||||
|
on:input=move |e| set_review.set(event_target_value(&e))
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
// Submit Button
|
||||||
|
<button type="submit">{ "Add Item with Review" }</button>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/// Component to display a list of items.
|
/// Component to display a list of items.
|
||||||
/// Iterates through the items and renders their name, description, and tags.
|
/// Iterates through the items and renders their name, description, tags, and reviews.
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
use crate::models::item::Item;
|
use crate::models::item::Item;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
@ -13,9 +12,16 @@ pub fn ItemsList(items: ReadSignal<Vec<Item>>) -> impl IntoView {
|
||||||
{move || items.get().iter().enumerate().map(|(i, item)| view! {
|
{move || items.get().iter().enumerate().map(|(i, item)| view! {
|
||||||
<li key={i.to_string()}>
|
<li key={i.to_string()}>
|
||||||
<strong>{ item.name.clone() }</strong> - { item.description.clone() }
|
<strong>{ item.name.clone() }</strong> - { item.description.clone() }
|
||||||
|
<h4>{ "Tags:" }</h4>
|
||||||
<ul>
|
<ul>
|
||||||
{item.tags.iter().map(|(key, value)| view! {
|
{item.tags.iter().map(|(key, value)| view! {
|
||||||
<li>{ key.clone() + ": " + value }</li>
|
<li>{ format!("{}: {}", key, value) }</li>
|
||||||
|
}).collect::<Vec<_>>()}
|
||||||
|
</ul>
|
||||||
|
<h4>{ "Reviews:" }</h4>
|
||||||
|
<ul>
|
||||||
|
{item.reviews.iter().map(|review| view! {
|
||||||
|
<li>{ review.clone() }</li>
|
||||||
}).collect::<Vec<_>>()}
|
}).collect::<Vec<_>>()}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
@ -23,4 +29,4 @@ pub fn ItemsList(items: ReadSignal<Vec<Item>>) -> impl IntoView {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
pub mod item_form;
|
pub mod item_form;
|
||||||
pub mod items_list;
|
pub mod items_list;
|
||||||
pub mod review_form;
|
|
||||||
pub mod reviews_list;
|
|
|
@ -1,26 +0,0 @@
|
||||||
use leptos::*;
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub fn ReviewForm(item_id: String, on_submit: Box<dyn Fn(String) + 'static>) -> impl IntoView {
|
|
||||||
let (review_content, set_review_content) = create_signal(String::new());
|
|
||||||
|
|
||||||
let submit_review = move || {
|
|
||||||
on_submit(review_content.get());
|
|
||||||
set_review_content.set(String::new()); // Clear the textarea after submission
|
|
||||||
};
|
|
||||||
|
|
||||||
view! {
|
|
||||||
<div>
|
|
||||||
<h3>"Submit Review"</h3>
|
|
||||||
<textarea
|
|
||||||
placeholder="Write your review here"
|
|
||||||
prop:value=review_content
|
|
||||||
on:input=move |ev| {
|
|
||||||
let input_value = event_target_value(&ev);
|
|
||||||
set_review_content.set(input_value);
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<button on:click=move |_| submit_review()>"Submit Review"</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
use leptos::*;
|
|
||||||
use crate::models::review::Review;
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub fn ReviewsList(reviews: Vec<Review>) -> impl IntoView {
|
|
||||||
view! {
|
|
||||||
<div>
|
|
||||||
<h3>{ "Reviews" }</h3>
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
reviews.into_iter().map(|review| {
|
|
||||||
view! {
|
|
||||||
<li>{ review.content }</li>
|
|
||||||
}
|
|
||||||
}).collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
/// Represents an Item in CompareWare.
|
/// Represents an Item in CompareWare.
|
||||||
/// Each item has metadata and key-value tags for categorization.
|
/// Each item has metadata and key-value tags for categorization.
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::models::review::Review;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
|
@ -9,5 +8,5 @@ pub struct Item {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub tags: Vec<(String, String)>,
|
pub tags: Vec<(String, String)>,
|
||||||
pub reviews: Vec<Review>,
|
pub reviews: Vec<String>,
|
||||||
}
|
}
|
|
@ -1,2 +1 @@
|
||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod review;
|
|
|
@ -1,9 +0,0 @@
|
||||||
// src/models/review.rs
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
pub struct Review {
|
|
||||||
pub item_id: String, // ID of the item the review is associated with
|
|
||||||
pub content: String, // Content of the review
|
|
||||||
pub user_id: String, // ID of the user who submitted the review
|
|
||||||
}
|
|
Loading…
Reference in New Issue