From 39a7c7f68216145c8bb65614db95b32d076a86d2 Mon Sep 17 00:00:00 2001 From: Claas Date: Tue, 18 Jun 2024 22:20:05 +0200 Subject: [PATCH] Wire up chats to display incoming/outgoing messages --- chat/src/context.tsx | 25 ++++++++++++- chat/src/routes/Chat.tsx | 76 +++++++++++++++++++++++++++------------- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/chat/src/context.tsx b/chat/src/context.tsx index b11184a..35ed719 100644 --- a/chat/src/context.tsx +++ b/chat/src/context.tsx @@ -1,5 +1,6 @@ import { JSX, + Signal, createContext, createEffect, createSignal, @@ -12,7 +13,27 @@ const [name, setName] = createSignal( ); const [socket, setSocket] = createSignal(); // Not sure if using a map is better -const messagesByUser = new Map(); +const messagesByUser = new Map>(); + +async function handleMessage(event: MessageEvent) { + if (typeof event.data !== "string") + throw new Error("Message is not a string"); + + console.debug("Received message", event.data); + // For now we just pray it's right 🙂 + const message = JSON.parse(event.data) as ChatMessage; + + let signal = messagesByUser.get(message.sender); + + if (signal === undefined) { + signal = createSignal([]); + messagesByUser.set(message.sender, signal); + } + + const [, setMessages] = signal; + + setMessages((messages) => [...messages, message]); +} const state = { socket, name, setName, messagesByUser }; const Context = createContext(state); @@ -20,9 +41,11 @@ createEffect((previous) => { const id = name(); if (id === null) return previous; + previous?.removeEventListener("message", handleMessage); previous?.close(); const newSocket = new WebSocket(`ws://localhost:3000/messages/${id}`); + newSocket.addEventListener("message", handleMessage); setSocket(newSocket); return newSocket; }, socket()); diff --git a/chat/src/routes/Chat.tsx b/chat/src/routes/Chat.tsx index c3b9612..7ddd17d 100644 --- a/chat/src/routes/Chat.tsx +++ b/chat/src/routes/Chat.tsx @@ -1,6 +1,6 @@ import { useNavigate, useParams } from "@solidjs/router"; import { useAppContext } from "../context"; -import { createEffect } from "solid-js"; +import { For, Show, createEffect, createSignal } from "solid-js"; /** * This type has to be kept in sync with the server-side types @@ -17,50 +17,76 @@ export type ChatMessage = { export default function Chat() { const parameters = useParams(); - const { socket, name } = useAppContext(); + const { socket, name, messagesByUser } = useAppContext(); const navigate = useNavigate(); - const isValidContactName = () => - typeof parameters.name === "string" && parameters.name.length > 0; - const contactName = () => - isValidContactName() ? decodeURI(parameters.name) : null; - - createEffect(() => { - if (!isValidContactName()) { - // Go back to chat list - //TODO inform user what happened - navigate("/"); - return; + if (parameters.name === undefined || parameters.name.length === 0) { + navigate("/"); + return; + } + + const contactName = decodeURI(parameters.name); + + const messagesSignal = () => { + let signal = messagesByUser.get(contactName); + if (signal === undefined) { + signal = createSignal([]); + messagesByUser.set(contactName, signal); } - }); + + return signal; + }; + + const messages = () => { + const messages = messagesSignal(); + if (messages === undefined) return; + return messages[0](); + }; + + const setMessages = () => { + const messages = messagesSignal(); + if (messages === undefined) return; + return messages[1]; + }; function handleSubmit(event: SubmitEvent) { if (!(event.target instanceof HTMLFormElement)) throw new Error("Invalid event target for form submission"); event.preventDefault(); - const messageText: string = event.target.message.value; - console.debug(messageText); + const text: string = event.target.message.value; + console.debug(text); - const from = name(); - const to = contactName(); - if (from === null || to === null) return; + const sender = name(); + const recipient = contactName; + if (sender === null || recipient === null) return; const message = { - recipient: to, - sender: from, - text: messageText, + recipient, + sender, + text: text, time_utc: Date.now(), } satisfies ChatMessage; socket()?.send(JSON.stringify(message)); - event.target.reset(); + + // Add to local messages + const setter = setMessages(); + if (setter === undefined) return; + setter((previous) => [...previous, message]); } + + createEffect(() => console.debug(messages())); + return (
-

Chat with {contactName()}

-
    +

    Chat with {contactName}

    + 0}> +
      + {(message) =>
    1. {message.text}
    2. }
      +
    +