better Chat, but many bugs
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::prelude::*;
|
||||
use gloo_timers::callback::Interval;
|
||||
use js_sys::Date;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
@@ -10,26 +12,81 @@ extern "C" {
|
||||
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
|
||||
}
|
||||
|
||||
// DAS HIER IST DIE WICHTIGE ÄNDERUNG:
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChatMessage {
|
||||
pub content: String,
|
||||
pub is_incoming: bool,
|
||||
pub created_at: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ChatArgs {
|
||||
content: String,
|
||||
receiver_npub: String, // Wird jetzt als "receiverNpub" gesendet
|
||||
}
|
||||
struct ChatArgs { content: String, receiver_npub: String }
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GetArgs { receiver_npub: String }
|
||||
|
||||
#[function_component(Chat)]
|
||||
pub fn chat() -> Html {
|
||||
let messages = use_state(|| vec![]);
|
||||
let input_ref = use_node_ref();
|
||||
let messages = use_state(|| Vec::<ChatMessage>::new());
|
||||
let relay_connected = use_state(|| false); // Status für den Punkt
|
||||
let recipient_ref = use_node_ref();
|
||||
let input_ref = use_node_ref();
|
||||
let chat_bottom_ref = use_node_ref();
|
||||
|
||||
// Polling Logik & Status Check
|
||||
{
|
||||
let messages = messages.clone();
|
||||
let relay_connected = relay_connected.clone();
|
||||
let recipient_ref = recipient_ref.clone();
|
||||
use_effect(move || {
|
||||
let interval = Interval::new(3000, move || {
|
||||
let messages = messages.clone();
|
||||
let relay_connected = relay_connected.clone();
|
||||
|
||||
if let Some(recipient) = recipient_ref.cast::<HtmlInputElement>() {
|
||||
let npub = recipient.value();
|
||||
let npub_clean = npub.trim().to_string();
|
||||
|
||||
spawn_local(async move {
|
||||
if !npub_clean.is_empty() {
|
||||
let args = serde_wasm_bindgen::to_value(&GetArgs {
|
||||
receiver_npub: npub_clean
|
||||
}).unwrap();
|
||||
|
||||
let fetched: JsValue = invoke("get_nostr_messages", args).await;
|
||||
|
||||
if let Ok(mut new_msgs) = serde_wasm_bindgen::from_value::<Vec<ChatMessage>>(fetched) {
|
||||
new_msgs.sort_by_key(|m| m.created_at);
|
||||
|
||||
// WICHTIG FÜR NEUSTART:
|
||||
// Wenn lokal 0, aber Relay > 0 -> Sofort laden
|
||||
let current_len = (*messages).len();
|
||||
if current_len == 0 && !new_msgs.is_empty() {
|
||||
messages.set(new_msgs);
|
||||
relay_connected.set(true);
|
||||
} else if new_msgs.len() > current_len {
|
||||
messages.set(new_msgs);
|
||||
relay_connected.set(true);
|
||||
} else if !new_msgs.is_empty() {
|
||||
relay_connected.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
move || drop(interval)
|
||||
});
|
||||
}
|
||||
|
||||
// Autoscroll
|
||||
{
|
||||
let chat_bottom_ref = chat_bottom_ref.clone();
|
||||
let messages = messages.clone();
|
||||
use_effect_with(messages, move |_| {
|
||||
let messages_len = messages.len();
|
||||
use_effect_with(messages_len, move |_| {
|
||||
if let Some(element) = chat_bottom_ref.cast::<web_sys::HtmlElement>() {
|
||||
element.scroll_into_view();
|
||||
}
|
||||
@@ -41,32 +98,27 @@ pub fn chat() -> Html {
|
||||
let messages = messages.clone();
|
||||
let input_ref = input_ref.clone();
|
||||
let recipient_ref = recipient_ref.clone();
|
||||
|
||||
Callback::from(move |e: SubmitEvent| {
|
||||
e.prevent_default();
|
||||
let input = input_ref.cast::<HtmlInputElement>().unwrap();
|
||||
let recipient = recipient_ref.cast::<HtmlInputElement>().unwrap();
|
||||
|
||||
|
||||
let content = input.value();
|
||||
let receiver_npub = recipient.value();
|
||||
|
||||
if !content.trim().is_empty() && !receiver_npub.trim().is_empty() {
|
||||
// Optimistic UI Update
|
||||
let mut current = (*messages).clone();
|
||||
current.push(content.clone());
|
||||
current.push(ChatMessage {
|
||||
content: content.clone(),
|
||||
is_incoming: false,
|
||||
created_at: (Date::now() / 1000.0) as u64,
|
||||
});
|
||||
messages.set(current);
|
||||
|
||||
input.set_value("");
|
||||
|
||||
spawn_local(async move {
|
||||
let args = serde_wasm_bindgen::to_value(&ChatArgs {
|
||||
content,
|
||||
receiver_npub,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Der Aufruf wird jetzt funktionieren, da die Keys matchen
|
||||
invoke("send_nostr_message", args).await;
|
||||
let args = serde_wasm_bindgen::to_value(&ChatArgs { content, receiver_npub }).unwrap();
|
||||
let _ = invoke("send_nostr_message", args).await;
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -75,30 +127,42 @@ pub fn chat() -> Html {
|
||||
html! {
|
||||
<div class="chat-wrapper">
|
||||
<header class="feed-header">
|
||||
<h1 class="feed-title">{"💬 Chat"}</h1>
|
||||
<h1 class="feed-title">{"💬 Nostr Chat"}</h1>
|
||||
<div class="relay-status">
|
||||
<span class={if *relay_connected { "status-dot online" } else { "status-dot offline" }}></span>
|
||||
<small>{if *relay_connected { " Relays aktiv" } else { " Verbinde..." }}</small>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="recipient-box">
|
||||
<input
|
||||
ref={recipient_ref}
|
||||
type="text"
|
||||
placeholder="Empfänger npub..."
|
||||
class="recipient-input"
|
||||
<input
|
||||
ref={recipient_ref}
|
||||
type="text"
|
||||
placeholder="Empfänger npub..."
|
||||
class="recipient-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="chat-list">
|
||||
{ for (*messages).iter().map(|msg| html! {
|
||||
<div class="chat-item">
|
||||
<div class="chat-meta">{"Du • Jetzt"}</div>
|
||||
<div class="chat-content">{ msg }</div>
|
||||
<div class={if msg.is_incoming { "chat-item incoming" } else { "chat-item outgoing" }}>
|
||||
<div class="chat-meta">
|
||||
{ if msg.is_incoming { "Empfangen" } else { "Du" } }
|
||||
</div>
|
||||
<div class="chat-content">{ &msg.content }</div>
|
||||
</div>
|
||||
}) }
|
||||
<div ref={chat_bottom_ref}></div>
|
||||
</div>
|
||||
|
||||
<form class="chat-input-container" onsubmit={on_send}>
|
||||
<input ref={input_ref} type="text" placeholder="Nachricht..." class="chat-input" />
|
||||
<input
|
||||
ref={input_ref}
|
||||
type="text"
|
||||
placeholder="Nachricht schreiben..."
|
||||
class="chat-input"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button type="submit" class="chat-send-btn">{"↑"}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user