even more responsive

This commit is contained in:
Adam 2024-09-10 01:59:35 -04:00
parent 31cd79c543
commit fb586bb362
11 changed files with 243 additions and 167 deletions

View file

@ -14,7 +14,7 @@ console_error_panic_hook = "0.1"
console_log = "1" console_log = "1"
log = "0.4" log = "0.4"
leptos-use = "0.14.0-beta" leptos-use = { version = "0.14.0-beta", features = ["use_media_query"] }
# leptos-use = { path = "../../leptos-use" } # leptos-use = { path = "../../leptos-use" }
# leptos-use = { git = "https://github.com/adoyle0/leptos-use.git", branch = "leptos-0.7" } # leptos-use = { git = "https://github.com/adoyle0/leptos-use.git", branch = "leptos-0.7" }
codee = "0.2" codee = "0.2"

View file

@ -1,6 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<style>
@media (prefers-color-scheme: dark) {
body, html {
background-color: #292929;
}
}
</style>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<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 />

View file

@ -12,7 +12,6 @@ pub fn Auth() -> impl IntoView {
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;
let new_username = RwSignal::new(String::new()); let new_username = RwSignal::new(String::new());
let show_auth = RwSignal::new(false);
Effect::new(move |_| { Effect::new(move |_| {
user_context.with(|new_user| { user_context.with(|new_user| {
@ -40,79 +39,71 @@ pub fn Auth() -> impl IntoView {
}); });
view! { view! {
<div class="flex flex-wrap justify-between"> <Popover trigger_type=PopoverTriggerType::Click position=PopoverPosition::BottomEnd>
<p class="my-2">"Welcome " {move || username()}"!"</p> <PopoverTrigger slot>
<Button <Button appearance=ButtonAppearance::Transparent size=ButtonSize::Small>
appearance=ButtonAppearance::Transparent // on_click=move |_| { show_auth.set(!show_auth()) }
size=ButtonSize::Small <Avatar class="drop-shadow-md" name=username />
on_click=move |_| { show_auth.set(!show_auth()) } </Button>
> </PopoverTrigger>
<Avatar name=username /> <Show when=move || { connected() } fallback=|| view! { <p>"Disconnected."</p> }>
</Button> <Popover
</div> trigger_type=PopoverTriggerType::Click
<div class="overflow-hidden"> position=PopoverPosition::BottomStart
<InlineDrawer open=show_auth position=DrawerPosition::Top size=DrawerSize::Small> >
<DrawerBody> <PopoverTrigger slot>
<Show when=move || { connected() } fallback=|| view! { <p>"Disconnected."</p> }> <h2 class="text-2xl">
<Popover "Sign In"
trigger_type=PopoverTriggerType::Click <Button
position=PopoverPosition::BottomStart icon=icondata::TbHelp
> appearance=ButtonAppearance::Transparent
<PopoverTrigger slot> size=ButtonSize::Small
<h2 class="text-2xl"> />
"Sign In" </h2>
<Button </PopoverTrigger>
icon=icondata::TbHelp <p class="indent-1 text-pretty">
appearance=ButtonAppearance::Transparent "You were already given a random name but if you'd like to change it this is the place."
size=ButtonSize::Small </p>
/> <p class="my-2 indent-1 text-pretty">
</h2> "Identities are saved once created so if you leave or get disconnected just enter your old name here to \"log back in\"."
</PopoverTrigger> </p>
<p class="indent-1 text-pretty"> <p class="indent-1 text-pretty">
"You were already given a random name but if you'd like to change it this is the place." "Please don't steal each other's identities (yes, you can)."
</p> </p>
<p class="my-2 indent-1 text-pretty"> </Popover>
"Identities are saved once created so if you leave or get disconnected just enter your old name here to \"log back in\"." <br />
</p>
<p class="indent-1 text-pretty">
"Please don't steal each other's identities (yes, you can)."
</p>
</Popover>
<br />
<form onsubmit="return false" on:submit=send_login.clone() autocomplete="off"> <form onsubmit="return false" on:submit=send_login.clone() autocomplete="off">
<FieldContextProvider> <FieldContextProvider>
<Field label="Username" name="username"> <Field label="Username" name="username">
<Input <Input
placeholder=username placeholder=username
value=new_username value=new_username
disabled=!connected() disabled=!connected()
class="w-80" class="w-80"
rules=vec![InputRule::required(true.into())] rules=vec![InputRule::required(true.into())]
> >
<InputPrefix slot> <InputPrefix slot>
<Icon icon=icondata::AiUserOutlined /> <Icon icon=icondata::AiUserOutlined />
</InputPrefix> </InputPrefix>
</Input> </Input>
</Field> </Field>
<Button <Button
button_type=ButtonType::Submit button_type=ButtonType::Submit
on_click={ on_click={
let field_context = FieldContextInjection::expect_context(); let field_context = FieldContextInjection::expect_context();
move |e: ev::MouseEvent| { move |e: ev::MouseEvent| {
if !field_context.validate() { if !field_context.validate() {
e.prevent_default(); e.prevent_default();
}
}
} }
> }
"Submit" }
</Button> >
</FieldContextProvider> "Submit"
</form> </Button>
</Show> </FieldContextProvider>
</DrawerBody> </form>
</InlineDrawer> </Show>
</div> </Popover>
} }
} }

View file

@ -24,20 +24,29 @@ 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) = signal("".to_string()); let join_id = RwSignal::new("".to_string());
let (delete_id, set_delete_id) = signal("".to_string()); let delete_id = RwSignal::new("".to_string());
let nav_context = expect_context::<RwSignal<String>>();
Effect::new(move |_| { Effect::new(move |_| {
set_websocket_send(to_string(&GameJoinRequest { id: join_id() }).unwrap()); if join_id() != "" {
set_websocket_send(to_string(&GameJoinRequest { id: join_id() }).unwrap());
nav_context.set("game".to_string());
join_id.set("".to_string());
}
}); });
Effect::new(move |_| { Effect::new(move |_| {
set_websocket_send( if delete_id() != "" {
to_string(&GameDeleteRequest { set_websocket_send(
delete_game_id: delete_id(), to_string(&GameDeleteRequest {
}) delete_game_id: delete_id(),
.unwrap(), })
); .unwrap(),
);
delete_id.set("".to_string());
}
}); });
let show_create_game = RwSignal::new(false); let show_create_game = RwSignal::new(false);
@ -51,14 +60,11 @@ pub fn Browser() -> impl IntoView {
position=PopoverPosition::BottomStart position=PopoverPosition::BottomStart
> >
<PopoverTrigger slot> <PopoverTrigger slot>
<h2 class="text-2xl"> <Button
"Game Browser" icon=icondata::TbHelp
<Button appearance=ButtonAppearance::Transparent
icon=icondata::TbHelp size=ButtonSize::Small
appearance=ButtonAppearance::Transparent />
size=ButtonSize::Small
/>
</h2>
</PopoverTrigger> </PopoverTrigger>
<p>"Yes, the delete button works. Please don't abuse it."</p> <p>"Yes, the delete button works. Please don't abuse it."</p>
</Popover> </Popover>
@ -135,7 +141,7 @@ pub fn Browser() -> impl IntoView {
on_click={ on_click={
let game_id = game.uuid.clone(); let game_id = game.uuid.clone();
move |_| { move |_| {
set_join_id(game_id.clone()); join_id.set(game_id.clone());
} }
} }
> >
@ -146,7 +152,7 @@ pub fn Browser() -> impl IntoView {
size=ButtonSize::Small size=ButtonSize::Small
icon=icondata::TiDeleteOutline icon=icondata::TiDeleteOutline
on_click=move |_| { on_click=move |_| {
set_delete_id(game.uuid.clone()); delete_id.set(game.uuid.clone());
} }
> >
"Delete" "Delete"

View file

@ -43,6 +43,8 @@ pub fn CreateGame() -> impl IntoView {
let toggle_show_packs = move |_| show_packs.set(!show_packs()); let toggle_show_packs = move |_| show_packs.set(!show_packs());
let drawer_context = expect_context::<RwSignal<bool>>(); let drawer_context = expect_context::<RwSignal<bool>>();
let nav_context = expect_context::<RwSignal<String>>();
let request_new_game = move |_| { let request_new_game = move |_| {
set_websocket_send( set_websocket_send(
to_string(&NewGameRequest { to_string(&NewGameRequest {
@ -53,6 +55,7 @@ pub fn CreateGame() -> impl IntoView {
); );
input_game_name.set(String::new()); input_game_name.set(String::new());
drawer_context.set(false); drawer_context.set(false);
nav_context.set("game".to_string());
}; };
view! { view! {

View file

@ -79,7 +79,7 @@ pub fn Chat() -> impl IntoView {
<span class="flex justify-between"> <span class="flex justify-between">
<textarea <textarea
node_ref=chat_history_ref node_ref=chat_history_ref
class="w-full h-60 p-1 font-mono rounded-md resize-none bg-neutral-900 text-neutral-200" class="opacity-80 w-full h-60 p-1 font-mono rounded-md resize-none bg-neutral-900 text-neutral-200 drop-shadow-md"
readonly=true readonly=true
wrap="soft" wrap="soft"
> >
@ -95,14 +95,16 @@ pub fn Chat() -> impl IntoView {
<form onsubmit="return false" on:submit=send_message autocomplete="off"> <form onsubmit="return false" on:submit=send_message autocomplete="off">
<FieldContextProvider> <FieldContextProvider>
<Field label="Message" name="message"> <Field label="Message" name="message">
<thaw::Input <Input
rules=vec![InputRule::required(true.into())] rules=vec![InputRule::required(true.into())]
class="drop-shadow-md"
value=chat_input value=chat_input
placeholder="talk shit..." placeholder="talk shit..."
/> />
</Field> </Field>
<Button <Button
button_type=ButtonType::Submit button_type=ButtonType::Submit
class="drop-shadow-md"
on_click={ on_click={
let field_context = FieldContextInjection::expect_context(); let field_context = FieldContextInjection::expect_context();
move |e: ev::MouseEvent| { move |e: ev::MouseEvent| {

View file

@ -39,7 +39,6 @@ pub fn Game() -> impl IntoView {
view! { view! {
<div class="my-2"> <div class="my-2">
<h2 class="text-2xl">Game</h2>
<Show when=move || { connected() } fallback=|| view! { <p>"Disconnected."</p> }> <Show when=move || { connected() } fallback=|| view! { <p>"Disconnected."</p> }>
<Show <Show
when=move || game_meta.get().is_some() && connected() when=move || game_meta.get().is_some() && connected()

View file

@ -3,4 +3,5 @@ pub mod browser;
pub mod chat; pub mod chat;
pub mod debug; pub mod debug;
pub mod game; pub mod game;
pub mod theme_button;
pub mod websocket; pub mod websocket;

View file

@ -0,0 +1,40 @@
use leptos::prelude::*;
use thaw::*;
/// In charge of light/dark modes
#[component]
pub fn ThemeButton() -> impl IntoView {
let theme = expect_context::<RwSignal<Theme>>();
let icon = RwSignal::new(None);
Effect::new(move |_| {
if theme().name == "dark" {
icon.set(Some(icondata::BsMoon));
} else {
icon.set(Some(icondata::BsSun));
}
});
let on_click = move |_| {
icon.update(|icon| {
*icon = match icon {
Some(data) => {
if *data == icondata::BsMoon {
theme.set(Theme::light());
icondata::BsSun
} else {
theme.set(Theme::dark());
icondata::BsMoon
}
}
None => {
theme.set(Theme::dark());
icondata::BsMoon
}
}
.into();
});
};
view! { <Button class="drop-shadow-md" icon on_click appearance=ButtonAppearance::Transparent /> }
}

View file

@ -4,6 +4,7 @@ use leptos_router::{
components::{Route, Router, Routes}, components::{Route, Router, Routes},
StaticSegment, StaticSegment,
}; };
use leptos_use::use_media_query;
use thaw::*; use thaw::*;
// Modules // Modules
@ -16,7 +17,16 @@ use crate::pages::home::Home;
/// 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 {
let theme = RwSignal::new(Theme::dark()); let theme = RwSignal::new(Theme::light());
let prefers_dark = use_media_query("(prefers-color-scheme: dark)");
Effect::new(move |_| {
if prefers_dark() {
theme.set(Theme::dark());
} else {
theme.set(Theme::light());
}
});
provide_context(theme); provide_context(theme);
provide_meta_context(); provide_meta_context();

View file

@ -3,6 +3,7 @@ use crate::components::browser::*;
use crate::components::chat::*; use crate::components::chat::*;
use crate::components::debug::*; use crate::components::debug::*;
use crate::components::game::*; use crate::components::game::*;
use crate::components::theme_button::*;
use crate::components::websocket::*; use crate::components::websocket::*;
use leptos::prelude::*; use leptos::prelude::*;
use thaw::*; use thaw::*;
@ -17,29 +18,8 @@ pub fn Home() -> impl IntoView {
modal.set(true); modal.set(true);
}; };
let theme = expect_context::<RwSignal<Theme>>(); let selected_value = RwSignal::new("home".to_string());
let icon = RwSignal::new(Some(icondata::BsMoon)); provide_context(selected_value);
let on_click = move |_| {
icon.update(|icon| {
*icon = match icon {
Some(data) => {
if *data == icondata::BsMoon {
theme.set(Theme::light());
icondata::BsSun
} else {
theme.set(Theme::dark());
icondata::BsMoon
}
}
None => {
theme.set(Theme::dark());
icondata::BsMoon
}
}
.into();
});
};
view! { view! {
<ErrorBoundary fallback=|errors| { <ErrorBoundary fallback=|errors| {
@ -60,64 +40,101 @@ pub fn Home() -> impl IntoView {
</ul> </ul>
} }
}> }>
<Websocket />
<div class="container m-auto"> <div class="container m-auto relative">
<div <div
class="p-1 sm:p-5 sm:rounded-2xl sm:shadow-black sm:shadow-lg" class="p-1 sm:p-5 sm:rounded-2xl sm:shadow-black sm:shadow-lg min-h-screen sm:min-h-0"
style="background-color: var(--colorNeutralBackground4);" style="background-color: var(--colorNeutralBackground4);"
> >
// Header
<div class="flex flex-wrap justify-between"> <div class="flex flex-wrap justify-between">
<h1 class="text-4xl sm:text-6xl md:text-7xl lg:text-8xl tracking-tighter"> <h1 class="text-4xl sm:text-6xl md:text-7xl lg:text-8xl tracking-tighter">
"Cards For Humanity" "Cards For Humanity"
</h1> </h1>
<Button icon on_click appearance=ButtonAppearance::Transparent /> <ThemeButton />
</div> </div>
<div class="flex justify-between">
<TabList selected_value>
<Tab value="home">
<Button
appearance=ButtonAppearance::Transparent
icon=icondata::BiHomeAlt2Regular
/>
</Tab>
<Tab value="browser">
<Button
appearance=ButtonAppearance::Transparent
icon=icondata::RiMenuSearchSystemLine
/>
</Tab>
<Tab value="game">
<Button
appearance=ButtonAppearance::Transparent
icon=icondata::LuGamepad
/>
</Tab>
<Tab value="chat">
<Button
appearance=ButtonAppearance::Transparent
icon=icondata::BsChatLeftText
/>
</Tab>
<Tab value="debug">
<Button
appearance=ButtonAppearance::Transparent
icon=icondata::AiBugOutlined
/>
</Tab>
</TabList>
<div class="mt-2">
<Auth />
</div>
</div>
<Divider />
<Dialog open=modal> <Show when=move || selected_value() == "home".to_string()>
<DialogSurface> <div class="p-4">
<DialogBody> <h2 class="text-2xl">"Hey!"</h2>
<DialogTitle>"Hey!"</DialogTitle> <p class="indent-4 text-pretty my-3">
<DialogContent> {"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 "}
<p class="indent-2 text-pretty"> <Link href="mailto:adam@doordesk.net">adam@doordesk.net</Link>
{"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 "} {". The server may go down from time to time as bugs are found and as I add updates. If you manage to crash the server or notice it down for a long time please tell me about it."}
<Link href="mailto:adam@doordesk.net"> </p>
adam@doordesk.net <p>Have fun!</p>
</Link> </div>
{". The server may go down from time to time as bugs are found and as I add updates. If you manage to crash the server or notice it down for a long time please tell me about it."} </Show>
</p>
<br />
<p>Have fun!</p>
</DialogContent>
<DialogActions>
<Button
on_click=move |_| modal.set(false)
appearance=ButtonAppearance::Primary
>
"Got it"
</Button>
</DialogActions>
</DialogBody>
</DialogSurface>
</Dialog>
<Websocket /> <Show when=move || selected_value() == "browser".to_string()>
<Auth /> <Browser />
<Divider /> </Show>
<Browser />
<Divider /> <Show when=move || selected_value() == "game".to_string()>
<Game /> <Game />
<Divider /> </Show>
<Chat />
<Divider /> <Show when=move || selected_value() == "chat".to_string()>
<Debug /> <Chat />
<Divider /> </Show>
<div class="m-2">
<Link class="p-2" href="https://git.doordesk.net/adam/cards/"> <Show when=move || selected_value() == "debug".to_string()>
"Git" <Debug />
</Link> </Show>
<Link class="p-2" href="mailto:adam@doordesk.net">
"Email Me" // Footer
</Link> <div
class="fixed sm:static bottom-0 left-0 w-full sm:w-auto"
style="background-color: var(--colorNeutralBackground4);"
>
<Divider />
<div class="m-2">
<Link class="p-2" href="https://git.doordesk.net/adam/cards/">
"About"
</Link>
<Link class="p-2" href="mailto:adam@doordesk.net">
"Contact"
</Link>
</div>
</div> </div>
</div> </div>
</div> </div>