//! Conversation modal for viewing whisper history with a specific user. use leptos::prelude::*; use leptos::reactive::owner::LocalStorage; #[cfg(feature = "hydrate")] use wasm_bindgen::JsCast; use chattyness_db::ws_messages::ClientMessage; use super::chat_types::ChatMessage; use super::modals::Modal; use super::ws_client::WsSender; /// Conversation modal component for viewing/replying to whispers. #[component] pub fn ConversationModal( #[prop(into)] open: Signal, on_close: Callback<()>, /// The display name of the conversation partner. #[prop(into)] partner_name: Signal, /// All whisper messages involving this partner. #[prop(into)] messages: Signal>, /// Current user's display name for determining message direction. #[prop(into)] current_user_name: Signal, /// WebSocket sender for sending replies. ws_sender: StoredValue, LocalStorage>, ) -> impl IntoView { let (reply_text, set_reply_text) = signal(String::new()); let input_ref = NodeRef::::new(); // Focus input when modal opens #[cfg(feature = "hydrate")] { Effect::new(move |_| { if open.get() { // Small delay to ensure modal is rendered let input_ref = input_ref; let closure = wasm_bindgen::closure::Closure::once(Box::new(move || { if let Some(input) = input_ref.get() { let _ = input.focus(); } }) as Box); if let Some(window) = web_sys::window() { let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0( closure.as_ref().unchecked_ref(), 100, ); } closure.forget(); } }); } let send_reply = { let on_close = on_close.clone(); move || { let text = reply_text.get(); let target = partner_name.get(); if !text.trim().is_empty() && !target.is_empty() { ws_sender.with_value(|sender| { if let Some(send_fn) = sender { send_fn(ClientMessage::SendChatMessage { content: text.trim().to_string(), target_display_name: Some(target.clone()), }); } }); set_reply_text.set(String::new()); on_close.run(()); } } }; #[cfg(feature = "hydrate")] let on_keydown = { let send_reply = send_reply.clone(); move |ev: web_sys::KeyboardEvent| { if ev.key() == "Enter" && !ev.shift_key() { ev.prevent_default(); send_reply(); } } }; #[cfg(not(feature = "hydrate"))] let on_keydown = move |_ev| {}; view! { // Partner name header

"with " {move || partner_name.get()}

// Messages list
"No messages yet"

} >
{sender.to_string()} ": " {msg.content.clone()}
} } /> // Reply input

"Press " "Enter" " to send, " "Esc" " to close"

} }