make emotions named instead, add drop prop
This commit is contained in:
parent
989e20757b
commit
ea3b444d71
19 changed files with 1429 additions and 150 deletions
|
|
@ -12,14 +12,14 @@ use leptos_router::hooks::use_params_map;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::components::{
|
||||
ActiveBubble, Card, ChatInput, ChatMessage, MessageLog, RealmHeader, RealmSceneViewer,
|
||||
DEFAULT_BUBBLE_TIMEOUT_MS,
|
||||
ActiveBubble, Card, ChatInput, ChatMessage, InventoryPopup, MessageLog, RealmHeader,
|
||||
RealmSceneViewer, DEFAULT_BUBBLE_TIMEOUT_MS,
|
||||
};
|
||||
#[cfg(feature = "hydrate")]
|
||||
use crate::components::use_channel_websocket;
|
||||
use chattyness_db::models::{
|
||||
AvatarWithPaths, ChannelMemberWithAvatar, EmotionAvailability, RealmRole, RealmWithUserRole,
|
||||
Scene,
|
||||
AvatarWithPaths, ChannelMemberWithAvatar, EmotionAvailability, EmotionState, LooseProp,
|
||||
RealmRole, RealmWithUserRole, Scene,
|
||||
};
|
||||
#[cfg(feature = "hydrate")]
|
||||
use chattyness_db::ws_messages::ClientMessage;
|
||||
|
|
@ -55,6 +55,12 @@ pub fn RealmPage() -> impl IntoView {
|
|||
let (active_bubbles, set_active_bubbles) =
|
||||
signal(HashMap::<(Option<Uuid>, Option<Uuid>), ActiveBubble>::new());
|
||||
|
||||
// Inventory popup state
|
||||
let (inventory_open, set_inventory_open) = signal(false);
|
||||
|
||||
// Loose props state
|
||||
let (loose_props, set_loose_props) = signal(Vec::<LooseProp>::new());
|
||||
|
||||
let realm_data = LocalResource::new(move || {
|
||||
let slug = slug.get();
|
||||
async move {
|
||||
|
|
@ -165,15 +171,40 @@ pub fn RealmPage() -> impl IntoView {
|
|||
});
|
||||
});
|
||||
|
||||
// Loose props callbacks
|
||||
#[cfg(feature = "hydrate")]
|
||||
let on_loose_props_sync = Callback::new(move |props: Vec<LooseProp>| {
|
||||
set_loose_props.set(props);
|
||||
});
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
let on_prop_dropped = Callback::new(move |prop: LooseProp| {
|
||||
set_loose_props.update(|props| {
|
||||
props.push(prop);
|
||||
});
|
||||
});
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
let on_prop_picked_up = Callback::new(move |prop_id: Uuid| {
|
||||
set_loose_props.update(|props| {
|
||||
props.retain(|p| p.id != prop_id);
|
||||
});
|
||||
});
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
let (_ws_state, ws_sender) = use_channel_websocket(
|
||||
slug,
|
||||
Signal::derive(move || channel_id.get()),
|
||||
on_members_update,
|
||||
on_chat_message,
|
||||
on_loose_props_sync,
|
||||
on_prop_dropped,
|
||||
on_prop_picked_up,
|
||||
);
|
||||
|
||||
// Set channel ID when scene loads (triggers WebSocket connection)
|
||||
// Note: Currently using scene.id as the channel_id since channel_members
|
||||
// uses scenes directly. Proper channel infrastructure can be added later.
|
||||
#[cfg(feature = "hydrate")]
|
||||
{
|
||||
Effect::new(move |_| {
|
||||
|
|
@ -212,6 +243,21 @@ pub fn RealmPage() -> impl IntoView {
|
|||
#[cfg(not(feature = "hydrate"))]
|
||||
let on_move = Callback::new(move |(_x, _y): (f64, f64)| {});
|
||||
|
||||
// Handle prop click (pickup) via WebSocket
|
||||
#[cfg(feature = "hydrate")]
|
||||
let on_prop_click = Callback::new(move |prop_id: Uuid| {
|
||||
ws_sender.with_value(|sender| {
|
||||
if let Some(send_fn) = sender {
|
||||
send_fn(ClientMessage::PickUpProp {
|
||||
loose_prop_id: prop_id,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "hydrate"))]
|
||||
let on_prop_click = Callback::new(move |_prop_id: Uuid| {});
|
||||
|
||||
// Handle global keyboard shortcuts (e+0-9 for emotions, : for chat focus)
|
||||
#[cfg(feature = "hydrate")]
|
||||
{
|
||||
|
|
@ -266,6 +312,13 @@ pub fn RealmPage() -> impl IntoView {
|
|||
return;
|
||||
}
|
||||
|
||||
// Handle 'i' to open inventory
|
||||
if key == "i" || key == "I" {
|
||||
set_inventory_open.set(true);
|
||||
ev.prevent_default();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if 'e' key was pressed
|
||||
if key == "e" || key == "E" {
|
||||
*e_pressed_clone.borrow_mut() = true;
|
||||
|
|
@ -276,8 +329,10 @@ pub fn RealmPage() -> impl IntoView {
|
|||
if *e_pressed_clone.borrow() {
|
||||
*e_pressed_clone.borrow_mut() = false; // Reset regardless of outcome
|
||||
if key.len() == 1 {
|
||||
if let Ok(emotion) = key.parse::<u8>() {
|
||||
if emotion <= 9 {
|
||||
if let Ok(digit) = key.parse::<u8>() {
|
||||
// Convert digit to emotion name using EmotionState
|
||||
if let Some(emotion_state) = EmotionState::from_index(digit) {
|
||||
let emotion = emotion_state.to_string();
|
||||
#[cfg(debug_assertions)]
|
||||
web_sys::console::log_1(
|
||||
&format!("[Emotion] Sending emotion {}", emotion).into(),
|
||||
|
|
@ -394,6 +449,7 @@ pub fn RealmPage() -> impl IntoView {
|
|||
}>
|
||||
{move || {
|
||||
let on_move = on_move.clone();
|
||||
let on_prop_click = on_prop_click.clone();
|
||||
let on_chat_focus_change = on_chat_focus_change.clone();
|
||||
let realm_slug_for_viewer = realm_slug_val.clone();
|
||||
#[cfg(feature = "hydrate")]
|
||||
|
|
@ -412,6 +468,7 @@ pub fn RealmPage() -> impl IntoView {
|
|||
#[cfg(not(feature = "hydrate"))]
|
||||
let ws_for_chat: StoredValue<Option<WsSender>, LocalStorage> = StoredValue::new_local(None);
|
||||
let active_bubbles_signal = Signal::derive(move || active_bubbles.get());
|
||||
let loose_props_signal = Signal::derive(move || loose_props.get());
|
||||
view! {
|
||||
<div class="relative w-full">
|
||||
<RealmSceneViewer
|
||||
|
|
@ -419,7 +476,9 @@ pub fn RealmPage() -> impl IntoView {
|
|||
realm_slug=realm_slug_for_viewer.clone()
|
||||
members=members_signal
|
||||
active_bubbles=active_bubbles_signal
|
||||
loose_props=loose_props_signal
|
||||
on_move=on_move.clone()
|
||||
on_prop_click=on_prop_click.clone()
|
||||
/>
|
||||
<div class="absolute bottom-0 left-0 right-0 z-10 pb-4 px-4 pointer-events-none">
|
||||
<ChatInput
|
||||
|
|
@ -451,6 +510,23 @@ pub fn RealmPage() -> impl IntoView {
|
|||
}}
|
||||
</Suspense>
|
||||
</main>
|
||||
|
||||
// Inventory popup
|
||||
{
|
||||
#[cfg(feature = "hydrate")]
|
||||
let ws_sender_for_inv = ws_sender.clone();
|
||||
#[cfg(not(feature = "hydrate"))]
|
||||
let ws_sender_for_inv: StoredValue<Option<WsSender>, LocalStorage> = StoredValue::new_local(None);
|
||||
view! {
|
||||
<InventoryPopup
|
||||
open=Signal::derive(move || inventory_open.get())
|
||||
on_close=Callback::new(move |_: ()| {
|
||||
set_inventory_open.set(false);
|
||||
})
|
||||
ws_sender=ws_sender_for_inv
|
||||
/>
|
||||
}
|
||||
}
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue