migrate to leptos 0.7

This commit is contained in:
Adam 2024-09-07 01:17:51 -04:00
parent f22f1a00a9
commit 0de020ac39
19 changed files with 695 additions and 396 deletions

731
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,18 +7,19 @@ authors = ["Adam Doyle <adam@doordesk.net>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
leptos = { version = "0.6", features = ["csr", "nightly"] } leptos = { version = "0.7.0-beta", features = ["csr", "nightly"] }
leptos_meta = { version = "0.6", features = ["csr", "nightly"] } leptos_meta = { version = "0.7.0-beta" }
leptos_router = { version = "0.6", features = ["csr", "nightly"] } leptos_router = { version = "0.7.0-beta", features = ["nightly"] }
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
console_log = "1" console_log = "1"
log = "0.4" log = "0.4"
leptos-use = "0.13" leptos-use = "0.14.0-beta"
codee = "0.2" codee = "0.2"
lib = { workspace = true } lib = { workspace = true }
serde_json = "1.0" serde_json = "1.0"
thaw = { version = "0.4.0-beta2", features = ["csr", "nightly"] }
[dev-dependencies] [dev-dependencies]
wasm-bindgen = "0.2" wasm-bindgen = "0.2"

View file

@ -1,15 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<!-- Add a plain CSS file: see https://trunkrs.dev/assets/#css --> <meta charset="utf-8" />
<!-- If using Tailwind with Leptos CSR, see https://trunkrs.dev/assets/#tailwind instead-->
<link data-trunk rel="tailwind-css" href="public/styles.css" />
<!-- Include favicon in dist output: see https://trunkrs.dev/assets/#icon -->
<link data-trunk rel="icon" href="public/favicon.ico" />
<!-- include support for `wasm-bindgen --weak-refs` - see: https://rustwasm.github.io/docs/wasm-bindgen/reference/weak-references.html -->
<link data-trunk rel="rust" data-target-path="client" data-wasm-opt="z" data-weak-refs /> <link data-trunk rel="rust" data-target-path="client" data-wasm-opt="z" data-weak-refs />
<link data-trunk rel="icon" href="public/favicon.ico" />
<link data-trunk rel="tailwind-css" href="public/styles.css" />
<title>Cards for Humanity</title>
</head> </head>
<body></body> <body></body>
</html> </html>

View file

@ -13,7 +13,7 @@
html, html,
body { body {
@apply text-neutral-200; @apply text-neutral-200 bg-neutral-900;
font-family: "Inter", sans-serif; font-family: "Inter", sans-serif;
font-optical-sizing: auto; font-optical-sizing: auto;
font-weight: 400; font-weight: 400;

View file

@ -1,6 +1,5 @@
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use html::Input; use leptos::{html::Input, prelude::*};
use leptos::*;
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use lib::*; use lib::*;
use serde_json::to_string; use serde_json::to_string;
@ -8,11 +7,11 @@ use serde_json::to_string;
#[component] #[component]
pub fn Auth() -> impl IntoView { pub fn Auth() -> impl IntoView {
let websocket = expect_context::<WebSocketContext>(); let websocket = expect_context::<WebSocketContext>();
let (username, set_username) = create_signal("".to_string()); let (username, set_username) = signal("".to_string());
let user_context = expect_context::<ReadSignal<Option<UserUpdate>>>(); let user_context = expect_context::<ReadSignal<Option<UserUpdate>>>();
let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open;
create_effect(move |_| { Effect::new(move |_| {
user_context.with(|new_user| { user_context.with(|new_user| {
if let Some(user) = new_user { if let Some(user) = new_user {
set_username(user.username.to_string()); set_username(user.username.to_string());
@ -20,7 +19,7 @@ pub fn Auth() -> impl IntoView {
}) })
}); });
let username_input_ref = create_node_ref::<Input>(); let username_input_ref = NodeRef::<Input>::new();
let send_login = move |_| { let send_login = move |_| {
if let Some(input) = username_input_ref.get() { if let Some(input) = username_input_ref.get() {
if input.value() != String::from("") { if input.value() != String::from("") {
@ -37,7 +36,7 @@ pub fn Auth() -> impl IntoView {
}; };
// Clear user name on disconnect // Clear user name on disconnect
create_effect(move |_| { Effect::new(move |_| {
if !connected() { if !connected() {
set_username(String::from("")); set_username(String::from(""));
} }

View file

@ -1,5 +1,5 @@
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::*; use leptos::prelude::*;
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use lib::*; use lib::*;
use serde_json::to_string; use serde_json::to_string;
@ -11,9 +11,9 @@ pub fn Browser() -> impl IntoView {
let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open;
let tx = websocket.clone(); let tx = websocket.clone();
let (websocket_send, set_websocket_send) = create_signal("".to_string()); let (websocket_send, set_websocket_send) = signal("".to_string());
create_effect(move |_| { Effect::new(move |_| {
if websocket_send() != "".to_string() { if websocket_send() != "".to_string() {
tx.send(&websocket_send()); tx.send(&websocket_send());
} }
@ -21,14 +21,14 @@ pub fn Browser() -> impl IntoView {
// Browser stuff // Browser stuff
let game_browser_context = expect_context::<ReadSignal<Vec<GameBrowserMeta>>>(); let game_browser_context = expect_context::<ReadSignal<Vec<GameBrowserMeta>>>();
let (join_id, set_join_id) = create_signal("".to_string()); let (join_id, set_join_id) = signal("".to_string());
let (delete_id, set_delete_id) = create_signal("".to_string()); let (delete_id, set_delete_id) = signal("".to_string());
create_effect(move |_| { Effect::new(move |_| {
set_websocket_send(to_string(&GameJoinRequest { id: join_id() }).unwrap()); set_websocket_send(to_string(&GameJoinRequest { id: join_id() }).unwrap());
}); });
create_effect(move |_| { Effect::new(move |_| {
set_websocket_send( set_websocket_send(
to_string(&GameDeleteRequest { to_string(&GameDeleteRequest {
delete_game_id: delete_id(), delete_game_id: delete_id(),
@ -74,7 +74,7 @@ pub fn Browser() -> impl IntoView {
<td class="text-center border-b"> <td class="text-center border-b">
<button <button
type="button" type="button"
value=&game.uuid value=game.uuid.clone()
on:click=move |e| { on:click=move |e| {
set_join_id(event_target_value(&e)); set_join_id(event_target_value(&e));
} }
@ -83,7 +83,7 @@ pub fn Browser() -> impl IntoView {
</button> </button>
<button <button
type="button" type="button"
value=&game.uuid value=game.uuid.clone()
on:click=move |e| { on:click=move |e| {
set_delete_id(event_target_value(&e)); set_delete_id(event_target_value(&e));
} }

View file

@ -1,6 +1,8 @@
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use html::{Input, Textarea}; use leptos::{
use leptos::*; html::{Input, Textarea},
prelude::*,
};
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use lib::*; use lib::*;
use serde_json::to_string; use serde_json::to_string;
@ -13,18 +15,18 @@ pub fn Chat() -> impl IntoView {
let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open;
// Chat stuff // Chat stuff
let (chat_history, set_chat_history) = create_signal::<Vec<String>>(vec![]); let (chat_history, set_chat_history) = signal::<Vec<String>>(vec![]);
let (users, set_users) = create_signal::<Vec<String>>(vec![]); let (users, set_users) = signal::<Vec<String>>(vec![]);
let (chat_name, set_chat_name) = create_signal::<String>("".to_string()); let (chat_name, set_chat_name) = signal::<String>("".to_string());
let chat_history_ref = create_node_ref::<Textarea>(); let chat_history_ref = NodeRef::<Textarea>::new();
let chat_input_ref = create_node_ref::<Input>(); let chat_input_ref = NodeRef::<Input>::new();
fn update_chat_history(&history: &WriteSignal<Vec<String>>, message: String) { fn update_chat_history(&history: &WriteSignal<Vec<String>>, message: String) {
let _ = &history.update(|history: &mut Vec<_>| history.push(message)); let _ = &history.update(|history: &mut Vec<_>| history.push(message));
} }
// Connection status updates in chat window // Connection status updates in chat window
create_effect(move |_| { Effect::new(move |_| {
websocket.ready_state.with(move |state| match *state { websocket.ready_state.with(move |state| match *state {
ConnectionReadyState::Connecting => { ConnectionReadyState::Connecting => {
update_chat_history( update_chat_history(
@ -45,7 +47,7 @@ pub fn Chat() -> impl IntoView {
}); });
// Handle incoming chat messages // Handle incoming chat messages
create_effect(move |_| { Effect::new(move |_| {
chat_context.with(move |chat_message| { chat_context.with(move |chat_message| {
if let Some(message) = chat_message { if let Some(message) = chat_message {
update_chat_history(&set_chat_history, format!("{}\n", message.text)); update_chat_history(&set_chat_history, format!("{}\n", message.text));
@ -54,7 +56,7 @@ pub fn Chat() -> impl IntoView {
}); });
// Handle users // Handle users
create_effect(move |_| { Effect::new(move |_| {
chat_update_context.with(move |chat_update| { chat_update_context.with(move |chat_update| {
if let Some(update) = chat_update { if let Some(update) = chat_update {
set_users(update.users.clone()); set_users(update.users.clone());
@ -64,7 +66,7 @@ pub fn Chat() -> impl IntoView {
}); });
// Clear user list on disconnect // Clear user list on disconnect
create_effect(move |_| { Effect::new(move |_| {
websocket.ready_state.with(move |status| { websocket.ready_state.with(move |status| {
if *status == ConnectionReadyState::Closed { if *status == ConnectionReadyState::Closed {
set_users(vec![]); set_users(vec![]);
@ -88,7 +90,7 @@ pub fn Chat() -> impl IntoView {
}; };
// Keep chat scrolled to the bottom // Keep chat scrolled to the bottom
create_effect(move |_| { Effect::new(move |_| {
chat_history.with(move |_| { chat_history.with(move |_| {
// Scroll chat textarea to bottom // Scroll chat textarea to bottom
if let Some(hist) = chat_history_ref.get() { if let Some(hist) = chat_history_ref.get() {

View file

@ -1,6 +1,5 @@
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::html::Input; use leptos::{html::Input, prelude::*};
use leptos::*;
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use lib::*; use lib::*;
use serde_json::to_string; use serde_json::to_string;
@ -10,12 +9,12 @@ use std::collections::BTreeSet;
pub fn CreateGame() -> impl IntoView { pub fn CreateGame() -> impl IntoView {
// Websocket stuff // Websocket stuff
let websocket = expect_context::<WebSocketContext>(); let websocket = expect_context::<WebSocketContext>();
let (websocket_send, set_websocket_send) = create_signal("".to_string()); let (websocket_send, set_websocket_send) = signal("".to_string());
let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open;
let tx = websocket.clone(); let tx = websocket.clone();
create_effect(move |_| { Effect::new(move |_| {
if websocket_send() != "".to_string() { if websocket_send() != "".to_string() {
tx.send(&websocket_send()); tx.send(&websocket_send());
} }
@ -24,29 +23,29 @@ pub fn CreateGame() -> impl IntoView {
// New game stuff // New game stuff
let card_packs = expect_context::<ReadSignal<CardPacksMeta>>(); let card_packs = expect_context::<ReadSignal<CardPacksMeta>>();
let (show_packs, set_show_packs) = create_signal(false); let (show_packs, set_show_packs) = signal(false);
let (selected_packs, set_selected_packs) = create_signal::<BTreeSet<u8>>(BTreeSet::new()); let (selected_packs, set_selected_packs) = signal::<BTreeSet<u8>>(BTreeSet::new());
create_effect(move |_| { Effect::new(move |_| {
set_selected_packs set_selected_packs
.update(|set| set.extend(card_packs().official_meta.iter().map(|pack| pack.pack))); .update(|set| set.extend(card_packs().official_meta.iter().map(|pack| pack.pack)));
}); });
create_effect(move |_| { Effect::new(move |_| {
set_selected_packs set_selected_packs
.update(|set| set.extend(card_packs().unofficial_meta.iter().map(|pack| pack.pack))); .update(|set| set.extend(card_packs().unofficial_meta.iter().map(|pack| pack.pack)));
}); });
let new_game_name_ref = create_node_ref::<Input>(); let new_game_name_ref = NodeRef::<Input>::new();
let toggle_show_packs = move |_| set_show_packs(!show_packs()); let toggle_show_packs = move |_| set_show_packs(!show_packs());
let request_new_game = move |_| { let request_new_game = move |_| {
if let Some(input) = new_game_name_ref.get() { if let Some(input) = new_game_name_ref.get() {
if input.value() == *"" { if input.value() == *"" {
logging::error!("New game name is empty!"); println!("New game name is empty!");
} else if selected_packs().is_empty() { } else if selected_packs().is_empty() {
logging::error!("New game selected packs is empty!"); println!("New game selected packs is empty!");
} else { } else {
set_websocket_send( set_websocket_send(
to_string(&NewGameRequest { to_string(&NewGameRequest {

View file

@ -1,5 +1,5 @@
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::*; use leptos::prelude::*;
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use lib::*; use lib::*;
@ -9,8 +9,8 @@ pub fn Debug() -> impl IntoView {
let state_summary = expect_context::<ReadSignal<Option<ServerStateSummary>>>(); let state_summary = expect_context::<ReadSignal<Option<ServerStateSummary>>>();
// Signals // Signals
let (online_users, set_online_users) = create_signal(0); let (online_users, set_online_users) = signal(0);
let (active_games, set_active_games) = create_signal(0); let (active_games, set_active_games) = signal(0);
// Websocket stuff // Websocket stuff
let status = move || websocket.ready_state.get().to_string(); let status = move || websocket.ready_state.get().to_string();
@ -26,7 +26,7 @@ pub fn Debug() -> impl IntoView {
}; };
// Update server info -> move this to a new component // Update server info -> move this to a new component
create_effect(move |_| { Effect::new(move |_| {
state_summary.with(move |state_summary| { state_summary.with(move |state_summary| {
if let Some(state_summary) = state_summary { if let Some(state_summary) = state_summary {
set_online_users(state_summary.online_users); set_online_users(state_summary.online_users);

View file

@ -2,7 +2,7 @@ use crate::components::game::header::*;
use crate::components::game::views::judging::*; use crate::components::game::views::judging::*;
use crate::components::game::views::playing::*; use crate::components::game::views::playing::*;
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::*; use leptos::prelude::*;
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use lib::*; use lib::*;
pub mod cards; pub mod cards;
@ -20,10 +20,10 @@ pub fn Game() -> impl IntoView {
let user_update = expect_context::<ReadSignal<Option<UserUpdate>>>(); let user_update = expect_context::<ReadSignal<Option<UserUpdate>>>();
// Signals // // Signals //
let (judging, set_judging) = create_signal(false); let (judging, set_judging) = signal(false);
// Determine judging // Determine judging
create_effect(move |_| { Effect::new(move |_| {
user_update.with(move |user_meta| { user_update.with(move |user_meta| {
if let Some(user_meta) = user_meta { if let Some(user_meta) = user_meta {
if let Some(game_meta) = game_meta() { if let Some(game_meta) = game_meta() {

View file

@ -1,4 +1,4 @@
use leptos::*; use leptos::prelude::*;
use lib::*; use lib::*;
#[component] #[component]

View file

@ -1,4 +1,4 @@
use leptos::*; use leptos::prelude::*;
use lib::*; use lib::*;
#[component] #[component]

View file

@ -1,6 +1,6 @@
use crate::components::game::cards::*; use crate::components::game::cards::*;
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::*; use leptos::prelude::*;
use lib::*; use lib::*;
use serde_json::to_string; use serde_json::to_string;
@ -13,14 +13,14 @@ pub fn JudgingView() -> impl IntoView {
let set_judge_round = expect_context::<WriteSignal<Option<JudgeRound>>>(); let set_judge_round = expect_context::<WriteSignal<Option<JudgeRound>>>();
// Signals // Signals
let (selected_cards, set_selected_cards) = create_signal::<Vec<WhiteCardMeta>>(vec![]); let (selected_cards, set_selected_cards) = signal::<Vec<WhiteCardMeta>>(vec![]);
let (card_clicked, set_card_clicked) = create_signal::<String>(String::new()); let (card_clicked, set_card_clicked) = signal::<String>(String::new());
// Outoging // Outoging
provide_context::<WriteSignal<String>>(set_card_clicked); provide_context::<WriteSignal<String>>(set_card_clicked);
// On Incoming Judge // On Incoming Judge
create_effect(move |_| { Effect::new(move |_| {
// Clear selected cards // Clear selected cards
if judge_round().is_some() { if judge_round().is_some() {
set_selected_cards.update(|list| { set_selected_cards.update(|list| {
@ -31,7 +31,7 @@ pub fn JudgingView() -> impl IntoView {
// Card selection // // Card selection //
// Toggle selected status of cards // Toggle selected status of cards
create_effect(move |_| { Effect::new(move |_| {
if card_clicked() != "".to_string() { if card_clicked() != "".to_string() {
let identical_cards = selected_cards let identical_cards = selected_cards
.get_untracked() .get_untracked()
@ -86,15 +86,15 @@ pub fn JudgingView() -> impl IntoView {
view! { view! {
<div class="w-full ms-16 inline-flex flex-wrap"> <div class="w-full ms-16 inline-flex flex-wrap">
<BlackCard /> <BlackCard />
// Selected cards // Selected cards
<For {move || {
each=move || selected_cards() selected_cards()
key=move |card| card.uuid.clone() .into_iter()
children=move |card| { .map(|card| {
view! { <WhiteCard card_data=card /> } view! { <WhiteCard card_data=card /> }
} })
/> .collect_view()
}}
</div> </div>
// Submit button // Submit button
@ -114,18 +114,17 @@ pub fn JudgingView() -> impl IntoView {
} }
} }
> >
<For {judge_round()
each=move || judge_round().unwrap().cards_to_judge .unwrap()
key=move |_| 69 .cards_to_judge
children=move |group| { .into_iter()
.map(|group| {
view! { view! {
<div class="m-2 inline-flex flex-wrap justify-center"> <div class="m-2 inline-flex flex-wrap justify-center">
<For {group
each=move || group.clone() .into_iter()
key=move |card| card.uuid.clone() .map(|card| {
children=move |card| {
view! { view! {
// Hide cards from hand view when they exist as selected
<Show when={ <Show when={
let waste_of_memory = card.clone(); let waste_of_memory = card.clone();
move || { !selected_cards().contains(&waste_of_memory) } move || { !selected_cards().contains(&waste_of_memory) }
@ -133,12 +132,12 @@ pub fn JudgingView() -> impl IntoView {
<WhiteCard card_data=card.clone() /> <WhiteCard card_data=card.clone() />
</Show> </Show>
} }
} })
/> .collect_view()}
</div> </div>
} }
} })
/> .collect_view()}
</Show> </Show>
} }
} }

View file

@ -1,6 +1,6 @@
use crate::components::game::cards::*; use crate::components::game::cards::*;
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::*; use leptos::prelude::*;
use lib::*; use lib::*;
use serde_json::to_string; use serde_json::to_string;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -13,13 +13,11 @@ pub fn PlayingView() -> impl IntoView {
let game_state = expect_context::<ReadSignal<Option<GameStateMeta>>>(); let game_state = expect_context::<ReadSignal<Option<GameStateMeta>>>();
// Signals // Signals
let (selected_cards, set_selected_cards) = create_signal::<Vec<WhiteCardMeta>>(vec![]); let (selected_cards, set_selected_cards) = signal::<Vec<WhiteCardMeta>>(vec![]);
let (submitted_cards, set_submitted_cards) = let (submitted_cards, set_submitted_cards) = signal::<HashSet<WhiteCardMeta>>(HashSet::new());
create_signal::<HashSet<WhiteCardMeta>>(HashSet::new()); let (card_clicked, set_card_clicked) = signal::<String>(String::new());
let (card_clicked, set_card_clicked) = create_signal::<String>(String::new()); let (submitted, set_submitted) = signal(false);
let (submitted, set_submitted) = create_signal(false); let (player_hand, set_player_hand) = signal::<HashMap<String, WhiteCardMeta>>(HashMap::new());
let (player_hand, set_player_hand) =
create_signal::<HashMap<String, WhiteCardMeta>>(HashMap::new());
// Outoging // Outoging
provide_context::<WriteSignal<String>>(set_card_clicked); provide_context::<WriteSignal<String>>(set_card_clicked);
@ -27,7 +25,7 @@ pub fn PlayingView() -> impl IntoView {
// On Incoming Meta // // On Incoming Meta //
// Put cards in a map for easier lookup // Put cards in a map for easier lookup
// The server sends a vec to preserve ordering // The server sends a vec to preserve ordering
create_effect(move |_| { Effect::new(move |_| {
if game_state().is_some() { if game_state().is_some() {
for card in game_state().unwrap().white { for card in game_state().unwrap().white {
set_player_hand.update(|map| { set_player_hand.update(|map| {
@ -46,7 +44,7 @@ pub fn PlayingView() -> impl IntoView {
// Card selection // Card selection
// Toggle selected status of cards // Toggle selected status of cards
create_effect(move |_| { Effect::new(move |_| {
if card_clicked() != "".to_string() if card_clicked() != "".to_string()
&& !submitted() && !submitted()
&& submitted_cards().len() < game_state().unwrap().black.1.into() && submitted_cards().len() < game_state().unwrap().black.1.into()
@ -102,15 +100,12 @@ pub fn PlayingView() -> impl IntoView {
<div class="w-full inline-flex flex-wrap"> <div class="w-full inline-flex flex-wrap">
<BlackCard /> <BlackCard />
// Selected cards {move || selected_cards()
<For .into_iter()
each=move || selected_cards() .map(|card| {
key=move |card| card.uuid.clone()
children=move |card| {
view! { <WhiteCard card_data=card /> } view! { <WhiteCard card_data=card /> }
} })
/> .collect_view()}
</div> </div>
// Submit button // Submit button
@ -130,13 +125,15 @@ pub fn PlayingView() -> impl IntoView {
// Player hand // Player hand
<div class="inline-flex flex-wrap justify-center"> <div class="inline-flex flex-wrap justify-center">
<Show when=move || game_state().is_some()> <Show when=move || {
<For game_state().is_some()
each=move || game_state().unwrap().white }>
key=move |card| card.uuid.clone() {game_state()
children=move |card| { .unwrap()
.white
.into_iter()
.map(|card| {
view! { view! {
// Hide cards from hand view when they exist as selected or submitted
<Show when={ <Show when={
let id = card.uuid.clone(); let id = card.uuid.clone();
move || { move || {
@ -151,10 +148,9 @@ pub fn PlayingView() -> impl IntoView {
<WhiteCard card_data=card.clone() /> <WhiteCard card_data=card.clone() />
</Show> </Show>
} }
} })
/> .collect_view()}
</Show> </Show>
</div> </div>
} }
} }

View file

@ -1,26 +1,26 @@
use codee::string::FromToStringCodec; use codee::string::FromToStringCodec;
use leptos::*; use leptos::prelude::*;
use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebSocketReturn}; use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebSocketReturn};
use lib::*; use lib::*;
use serde_json::from_str; use serde_json::from_str;
use std::rc::Rc; use std::sync::Arc;
#[derive(Clone)] #[derive(Clone)]
pub struct WebSocketContext { pub struct WebSocketContext {
pub ready_state: Signal<ConnectionReadyState>, pub ready_state: Signal<ConnectionReadyState>,
// pub message: Signal<Option<String>>, // pub message: Signal<Option<String>>,
send: Rc<dyn Fn(&String)>, send: Arc<dyn Fn(&String) + Send + Sync + 'static>,
pub open: Rc<dyn Fn()>, pub open: Arc<dyn Fn() + Send + Sync + 'static>,
pub close: Rc<dyn Fn()>, pub close: Arc<dyn Fn() + Send + Sync + 'static>,
} }
impl WebSocketContext { impl WebSocketContext {
pub fn new( pub fn new(
ready_state: Signal<ConnectionReadyState>, ready_state: Signal<ConnectionReadyState>,
// message: Signal<Option<String>>, // message: Signal<Option<String>>,
send: Rc<dyn Fn(&String)>, send: Arc<dyn Fn(&String) + Send + Sync + 'static>,
open: Rc<dyn Fn()>, open: Arc<dyn Fn() + Send + Sync + 'static>,
close: Rc<dyn Fn()>, close: Arc<dyn Fn() + Send + Sync + 'static>,
) -> Self { ) -> Self {
Self { Self {
ready_state, ready_state,
@ -59,38 +59,37 @@ pub fn Websocket() -> impl IntoView {
provide_context(WebSocketContext::new( provide_context(WebSocketContext::new(
ready_state, ready_state,
// message, // message,
Rc::new(send.clone()), Arc::new(send.clone()),
Rc::new(open.clone()), Arc::new(open.clone()),
Rc::new(close.clone()), Arc::new(close.clone()),
)); ));
let connected = move || ready_state.get() == ConnectionReadyState::Open; let connected = move || ready_state.get() == ConnectionReadyState::Open;
// Contexts for message handler // Contexts for message handler
// TODO: This context stuff can probably be done better // TODO: This context stuff can probably be done better
let (state_summary, set_state_summary) = let (state_summary, set_state_summary) = signal::<Option<ServerStateSummary>>(Option::None);
create_signal::<Option<ServerStateSummary>>(Option::None); let (active_games, set_active_games) = signal::<Vec<GameBrowserMeta>>(vec![]);
let (active_games, set_active_games) = create_signal::<Vec<GameBrowserMeta>>(vec![]); let (user_update, set_user_update) = signal::<Option<UserUpdate>>(Option::None);
let (user_update, set_user_update) = create_signal::<Option<UserUpdate>>(Option::None); let (chat_update, set_chat_update) = signal::<Option<ChatUpdate>>(Option::None);
let (chat_update, set_chat_update) = create_signal::<Option<ChatUpdate>>(Option::None); let (judge_round, set_judge_round) = signal::<Option<JudgeRound>>(Option::None);
let (judge_round, set_judge_round) = create_signal::<Option<JudgeRound>>(Option::None); let (chat_message, set_chat_message) = signal::<Option<ChatMessage>>(Option::None);
let (chat_message, set_chat_message) = create_signal::<Option<ChatMessage>>(Option::None); let (game_meta, set_game_meta) = signal::<Option<GameMeta>>(Option::None);
let (game_meta, set_game_meta) = create_signal::<Option<GameMeta>>(Option::None); let (game_state, set_game_state) = signal::<Option<GameStateMeta>>(Option::None);
let (game_state, set_game_state) = create_signal::<Option<GameStateMeta>>(Option::None); let (card_packs_meta, set_card_packs_meta) = signal::<CardPacksMeta>(CardPacksMeta {
let (card_packs_meta, set_card_packs_meta) = create_signal::<CardPacksMeta>(CardPacksMeta {
official_meta: vec![], official_meta: vec![],
unofficial_meta: vec![], unofficial_meta: vec![],
}); });
create_effect(move |_| { Effect::new(move |_| {
if !connected() { if !connected() {
set_active_games.set_untracked(vec![]); set_active_games(vec![]);
set_user_update.set_untracked(None); set_user_update(None);
set_chat_update.set_untracked(None); set_chat_update(None);
set_judge_round.set_untracked(None); set_judge_round(None);
set_chat_message.set_untracked(None); set_chat_message(None);
set_game_meta.set_untracked(None); set_game_meta(None);
set_game_state.set_untracked(None); set_game_state(None);
} }
}); });
@ -106,7 +105,7 @@ pub fn Websocket() -> impl IntoView {
provide_context::<ReadSignal<Option<ServerStateSummary>>>(state_summary); provide_context::<ReadSignal<Option<ServerStateSummary>>>(state_summary);
// Message handler // Message handler
create_effect(move |_| { Effect::new(move |_| {
message.with(move |message_raw| { message.with(move |message_raw| {
if let Some(message) = message_raw { if let Some(message) = message_raw {
if let Ok(state_summary) = from_str::<ServerStateSummary>(message) { if let Ok(state_summary) = from_str::<ServerStateSummary>(message) {
@ -128,7 +127,7 @@ pub fn Websocket() -> impl IntoView {
} else if let Ok(judge_update) = from_str::<JudgeRound>(message) { } else if let Ok(judge_update) = from_str::<JudgeRound>(message) {
set_judge_round(Some(judge_update)); set_judge_round(Some(judge_update));
} else { } else {
logging::error!("Unhandled message: {:#?}", message); println!("Unhandled message: {:#?}", message);
} }
} }
}); });

View file

@ -1,6 +1,8 @@
use leptos::*; use leptos::prelude::*;
use leptos_meta::*; use leptos_router::{
use leptos_router::*; components::{Route, Router, Routes},
StaticSegment,
};
// Modules // Modules
mod components; mod components;
@ -8,28 +10,14 @@ mod pages;
// Top-Level pages // Top-Level pages
use crate::pages::home::Home; use crate::pages::home::Home;
use crate::pages::not_found::NotFound;
/// An app router which renders the homepage and handles 404's /// An app router which renders the homepage and handles 404's
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context();
view! { view! {
<Html class="bg-neutral-900" lang="en" dir="ltr" attr:data-theme="dark" />
// sets the document title
<Title text="Cards for Humanity" />
// injects metadata in the <head> of the page
<Meta charset="UTF-8" />
<Meta name="viewport" content="width=device-width, initial-scale=1.0" />
<Router> <Router>
<Routes> <Routes fallback=|| "Not found.".into_view()>
<Route path="/" view=Home /> <Route path=StaticSegment("") view=Home />
<Route path="/*" view=NotFound />
</Routes> </Routes>
</Router> </Router>
} }

View file

@ -1,12 +1,6 @@
use leptos::*;
use client::App; use client::App;
fn main() { fn main() {
// set up logging
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
leptos::mount::mount_to_body(App)
mount_to_body(|| {
view! { <App /> }
})
} }

View file

@ -5,11 +5,15 @@ use crate::components::create_game::*;
use crate::components::debug::*; use crate::components::debug::*;
use crate::components::game::*; use crate::components::game::*;
use crate::components::websocket::*; use crate::components::websocket::*;
use leptos::*; use leptos::prelude::*;
use thaw::*;
/// Default Home Page /// Default Home Page
#[component] #[component]
pub fn Home() -> impl IntoView { pub fn Home() -> impl IntoView {
let open = RwSignal::new(true);
let theme = RwSignal::new(Theme::dark());
view! { view! {
<ErrorBoundary fallback=|errors| { <ErrorBoundary fallback=|errors| {
view! { view! {
@ -33,8 +37,13 @@ pub fn Home() -> impl IntoView {
<div class="container m-auto"> <div class="container m-auto">
<h1 class="mx-4 text-6xl inter-med text-neutral-200">"Cards For Humanity"</h1> <h1 class="mx-4 text-6xl inter-med text-neutral-200">"Cards For Humanity"</h1>
<div class="p-5 bg-neutral-950 rounded-3xl shadow-black shadow-xl"> <div class="p-5 bg-neutral-950 rounded-3xl shadow-black shadow-xl">
<h1 class="text-4xl">Hey!</h1>
<br /> <ConfigProvider theme>
<Dialog open>
<DialogSurface>
<DialogBody>
<DialogTitle>"Hey!"</DialogTitle>
<DialogContent>
<p> <p>
{"Welcome! Thank you for helping me test this. Please let me know about any issues you may come across. Chances are you already know how to contact me but in case you don't you can email me at "} {"Welcome! Thank you for helping me test this. Please let me know about any issues you may come across. Chances are you already know how to contact me but in case you don't you can email me at "}
<a href="mailto:adam@doordesk.net">adam@doordesk.net</a> <a href="mailto:adam@doordesk.net">adam@doordesk.net</a>
@ -42,9 +51,21 @@ pub fn Home() -> impl IntoView {
</p> </p>
<br /> <br />
<p>Have fun!</p> <p>Have fun!</p>
<br /> </DialogContent>
<DialogActions>
<Button
on_click=move |_| open.set(!open())
appearance=ButtonAppearance::Primary
>
"Got it"
</Button>
</DialogActions>
</DialogBody>
</DialogSurface>
</Dialog>
</ConfigProvider>
<Websocket /> <Websocket />
<hr />
<Auth /> <Auth />
<hr /> <hr />
<Browser /> <Browser />

View file

@ -1,4 +1,4 @@
use leptos::*; use leptos::prelude::*;
/// 404 Not Found Page /// 404 Not Found Page
#[component] #[component]