import van from "./van.js"; const { button, div, span, h1, img, p, input } = van.tags; import markdownit from "https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/+esm"; import hljs from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js"; // Markdown configuration (matching the HTML) const md = markdownit({ html: true, linkify: true, typographer: true, highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { return hljs.highlight(str, { language: lang }).value; } catch (__) {} } return ""; // use external default escaping }, }); export const messages = van.state([]); export const users = van.state({}); // Helper function to render Markdown function renderMarkdown(text) { return md.render(text); } // Component for a single message function Message(message) { const userName = users[message.user_id] || "Unknown User"; // Get username from state or default const content = div({ class: "message-text" }); content.innerHTML = renderMarkdown(message.text); return div( { class: "message" }, img({ class: "user-avatar", src: `cdn/profile/${message.user_id}` }), div( { class: "message-content" }, div( { class: "message-header" }, span({ class: "username" }, userName), span( { class: "timestamp" }, new Date(message.timestamp).toLocaleTimeString("en-GB"), ), ), content, // Render Markdown here ), ); } // Component for the entire chat interface function ChatApp() { return div( { class: "chat-container" }, div( { class: "chat-header" }, div( { class: "chat-title" }, img({ class: "user-avatar", src: "cdn/profile/0" }), h1(null, "Wish.com Discord frfr"), ), ), van.derive(() => { console.log("Deriving messages"); const container = div({ class: "messages-container" }); for (const message of messages.val) { van.add(container, Message(message)); } return container; }), div( { class: "input-container" }, div( { class: "input-wrapper" }, input({ type: "text", placeholder: "Start Typing..." }), button( { class: "send-button", onclick: () => sendMessage() }, img({ src: "cdn/icons/send.svg" }), ), ), ), ); } async function sendMessage() { const messageInput = document.querySelector(".input-wrapper input"); const message = messageInput.value.trim(); if (message) { await fetch("/api/chat/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: user_id, text: message, timestamp: new Date().getTime(), }), }); messageInput.value = ""; } } // Function to fetch user data (replace with your actual API endpoint) async function fetchUsers() { try { const userIds = await fetch("/api/users/").then((r) => r.json()); const userPromises = userIds.map((userId) => fetch(`/api/users/${userId}`) .then((r) => r.text()) .then((username) => ({ userId, username })), ); const userData = await Promise.all(userPromises); userData.forEach(({ userId, username }) => { users.val[userId] = username; }); } catch (error) { console.error("Error fetching users:", error); } } // Function to handle incoming messages from SSE function handleSSEMessage(event) { let message = JSON.parse(event.data); messages.val = [...messages.val, message]; } // Initialize the application async function init() { await fetchUsers(); const eventSource = new EventSource("/api/events"); // Replace with your SSE endpoint eventSource.onopen = () => console.log("SSE connection opened"); eventSource.onmessage = handleSSEMessage; eventSource.onerror = (error) => { console.error("EventSource error:", error); // Attempt to reconnect after a delay setTimeout(() => eventSource.open(), 5000); }; van.add(document.body, ChatApp()); } init();