From 0be78881a7952973ac32e8c917c873c0e98617f4 Mon Sep 17 00:00:00 2001 From: Abdulhade Date: Sun, 16 Nov 2025 09:59:30 +0300 Subject: [PATCH 1/4] co-created html and css for the chat page --- front_files/index.css | 74 ++++++++++++++++++++++++++++++++++++++++++ front_files/index.html | 22 +++++++++++-- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/front_files/index.css b/front_files/index.css index c85ec17..7f1d898 100644 --- a/front_files/index.css +++ b/front_files/index.css @@ -190,6 +190,80 @@ body { flex-direction: column; } +.hidden { + display: none !important; +} + +/* Chat layout */ + +#chat-card { + display: flex; + flex-direction: column; + gap: 10px; +} + +.chat-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 4px; +} + +.chat-session { + font-size: 0.85rem; + color: var(--muted); +} + +.chat-log { + flex: 1; + min-height: 180px; + max-height: 320px; + margin-bottom: 8px; + padding: 12px; + border-radius: 10px; + background: rgba(3, 16, 32, 0.65); + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 8px; +} + +.chat-input-row { + display: flex; + gap: 8px; +} + +.chat-input-row .input { + flex: 1; + margin-bottom: 0; +} + +.chat-message { + padding: 8px 10px; + border-radius: 14px; + font-size: 0.9rem; + max-width: 80%; +} + +.chat-message.me { + align-self: flex-end; + background: var(--primary); + color: #ffffff; +} + +.chat-message.them { + align-self: flex-start; + background: rgba(255, 255, 255, 0.06); +} + +.chat-message.system { + align-self: center; + background: transparent; + color: var(--muted); + font-size: 0.8rem; +} + /* Responsive tweaks */ @media (max-width: 480px) { diff --git a/front_files/index.html b/front_files/index.html index 23d29d4..27b3313 100644 --- a/front_files/index.html +++ b/front_files/index.html @@ -17,7 +17,7 @@
-
+
+ +
- \ No newline at end of file + From cc2baece6b5bd138789c230f10a2933e76b73e04 Mon Sep 17 00:00:00 2001 From: Abdulhade Date: Sun, 16 Nov 2025 10:22:46 +0300 Subject: [PATCH 2/4] Fixed session creation json --- api/http_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/http_handler.go b/api/http_handler.go index 1a2dffe..543aadc 100644 --- a/api/http_handler.go +++ b/api/http_handler.go @@ -51,7 +51,7 @@ func createSession(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, `{ - "sessionId":%s + "sessionId":"%s" }`, session.id) } From 3238623c151d515cc66d963ab45fde6d017df41e Mon Sep 17 00:00:00 2001 From: Abdulhade Date: Sun, 16 Nov 2025 19:16:10 +0300 Subject: [PATCH 3/4] C-Created mock javascript --- front_files/index.js | 248 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 247 insertions(+), 1 deletion(-) diff --git a/front_files/index.js b/front_files/index.js index f7c3da1..13eebc3 100644 --- a/front_files/index.js +++ b/front_files/index.js @@ -1 +1,247 @@ -console.log("I actually wrote this!"); \ No newline at end of file +console.log("Echo mock frontend loaded"); + +(function () { + var createBtn = document.getElementById("create-session-btn"); + var joinBtn = document.getElementById("join-session-btn"); + var sessionIdInput = document.getElementById("session-id"); + + var landingCard = document.getElementById("landing-card"); + var chatCard = document.getElementById("chat-card"); + var chatLog = document.getElementById("chat-log"); + var chatForm = document.getElementById("chat-form"); + var chatInput = document.getElementById("chat-input"); + var chatSendBtn = document.getElementById("chat-send-btn"); + var leaveBtn = document.getElementById("leave-session-btn"); + var sessionInfoEl = document.getElementById("chat-session-info"); + + if ( + !createBtn || + !joinBtn || + !sessionIdInput || + !landingCard || + !chatCard || + !chatLog || + !chatForm || + !chatInput || + !chatSendBtn || + !leaveBtn || + !sessionInfoEl + ) { + console.warn("Echo UI elements not found, aborting mock init"); + return; + } + + var currentSessionId = null; + var currentParty = null; // "A" (offerer) or "B" (answerer) + var mockConnected = false; + + function setSessionInfo(text) { + var partyLabel = + currentParty === "A" + ? "You are party A (offerer side)" + : currentParty === "B" + ? "You are party B (answerer side)" + : ""; + + sessionInfoEl.textContent = partyLabel + ? text + " · " + partyLabel + : text; + } + + function appendMessage(kind, text) { + if (!chatLog) return; + + var item = document.createElement("div"); + item.classList.add("chat-message"); + + if (kind === "me") { + item.classList.add("me"); + } else if (kind === "them") { + item.classList.add("them"); + } else { + item.classList.add("system"); + } + + item.textContent = text; + chatLog.appendChild(item); + chatLog.scrollTop = chatLog.scrollHeight; + } + + function setChatInputEnabled(enabled) { + chatInput.disabled = !enabled; + chatSendBtn.disabled = !enabled; + } + + function resetState() { + currentSessionId = null; + currentParty = null; + mockConnected = false; + setChatInputEnabled(false); + } + + function showLanding() { + landingCard.classList.remove("hidden"); + chatCard.classList.add("hidden"); + sessionIdInput.value = ""; + resetState(); + } + + function showChat() { + landingCard.classList.add("hidden"); + chatCard.classList.remove("hidden"); + chatLog.innerHTML = ""; + setChatInputEnabled(false); + + if (!currentSessionId) { + return; + } + + if (currentParty === "A") { + setSessionInfo( + "Session ID: " + currentSessionId + " (share this with your peer)" + ); + appendMessage( + "system", + "Session created. TODO: open a WebSocket for signaling and send an SDP offer." + ); + } else { + setSessionInfo("Joined session: " + currentSessionId); + appendMessage( + "system", + "Joined session. TODO: connect to the signaling WebSocket and respond with an SDP answer." + ); + } + + appendMessage( + "system", + "Mock mode: no real signaling or WebRTC yet. Everything stays local." + ); + } + + function mockConnectP2P() { + if (mockConnected) { + return; + } + + mockConnected = true; + setChatInputEnabled(true); + appendMessage( + "system", + "Pretend the RTCDataChannel is open now. Replace this with real WebRTC events." + ); + } + + function sendChatMessage(text) { + if (!mockConnected) { + appendMessage( + "system", + "Mock mode: sending locally. Wire this up to RTCDataChannel.send()." + ); + } + + appendMessage("me", text); + + if (!mockConnected) { + appendMessage( + "them", + "(Simulated peer) Replace with your RTCDataChannel onmessage handler." + ); + } + } + + async function createSession() { + if (createBtn.disabled) return; + createBtn.disabled = true; + + try { + var response = await fetch("/api/session", { + method: "POST", + headers: { Accept: "application/json" }, + }); + + if (!response.ok) { + throw new Error( + "Failed to create session (" + response.status + ")" + ); + } + + var data = await response.json(); + if (!data || !data.sessionId) { + throw new Error("Session ID missing in response."); + } + + currentSessionId = String(data.sessionId); + currentParty = "A"; + showChat(); + + appendMessage( + "system", + "TODO: connect to /api/signal/session/" + + currentSessionId + + "/party/A via WebSocket." + ); + mockConnectP2P(); + } catch (err) { + console.error(err); + alert(err && err.message ? err.message : "Could not create session."); + resetState(); + } finally { + createBtn.disabled = false; + } + } + + function joinSession() { + var id = sessionIdInput.value.trim(); + if (!id) { + sessionIdInput.focus(); + return; + } + + currentSessionId = id; + currentParty = "B"; + showChat(); + + appendMessage( + "system", + "TODO: connect to /api/signal/session/" + + currentSessionId + + "/party/B via WebSocket." + ); + mockConnectP2P(); + } + + createBtn.addEventListener("click", function () { + createSession(); + }); + + joinBtn.addEventListener("click", function () { + joinSession(); + }); + + sessionIdInput.addEventListener("keydown", function (event) { + if (event.key === "Enter") { + event.preventDefault(); + joinSession(); + } + }); + + chatForm.addEventListener("submit", function (event) { + event.preventDefault(); + + var text = chatInput.value.trim(); + if (!text) { + return; + } + + sendChatMessage(text); + chatInput.value = ""; + chatInput.focus(); + }); + + leaveBtn.addEventListener("click", function () { + showLanding(); + }); + + // Initial state + setChatInputEnabled(false); +})(); From b0ec147c2de90e8cc9aee024e2e6b9fe5baa1504 Mon Sep 17 00:00:00 2001 From: Abdulhade Date: Sun, 16 Nov 2025 21:22:01 +0300 Subject: [PATCH 4/4] Cleaned up and created connectWebSocket func --- front_files/index.js | 47 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/front_files/index.js b/front_files/index.js index 13eebc3..716065c 100644 --- a/front_files/index.js +++ b/front_files/index.js @@ -33,15 +33,18 @@ console.log("Echo mock frontend loaded"); var currentSessionId = null; var currentParty = null; // "A" (offerer) or "B" (answerer) - var mockConnected = false; + var wsConnected = false; + var ws = null; + var pc = null; + var rc = null; function setSessionInfo(text) { var partyLabel = currentParty === "A" ? "You are party A (offerer side)" : currentParty === "B" - ? "You are party B (answerer side)" - : ""; + ? "You are party B (answerer side)" + : ""; sessionInfoEl.textContent = partyLabel ? text + " · " + partyLabel @@ -75,7 +78,7 @@ console.log("Echo mock frontend loaded"); function resetState() { currentSessionId = null; currentParty = null; - mockConnected = false; + wsConnected = false; setChatInputEnabled(false); } @@ -119,11 +122,11 @@ console.log("Echo mock frontend loaded"); } function mockConnectP2P() { - if (mockConnected) { + if (wsConnected) { return; } - mockConnected = true; + wsConnected = true; setChatInputEnabled(true); appendMessage( "system", @@ -132,7 +135,7 @@ console.log("Echo mock frontend loaded"); } function sendChatMessage(text) { - if (!mockConnected) { + if (!wsConnected) { appendMessage( "system", "Mock mode: sending locally. Wire this up to RTCDataChannel.send()." @@ -141,7 +144,7 @@ console.log("Echo mock frontend loaded"); appendMessage("me", text); - if (!mockConnected) { + if (!wsConnected) { appendMessage( "them", "(Simulated peer) Replace with your RTCDataChannel onmessage handler." @@ -176,11 +179,11 @@ console.log("Echo mock frontend loaded"); appendMessage( "system", - "TODO: connect to /api/signal/session/" + - currentSessionId + - "/party/A via WebSocket." + "Created session" ); - mockConnectP2P(); + + connectWebSocket() + } catch (err) { console.error(err); alert(err && err.message ? err.message : "Could not create session."); @@ -204,10 +207,24 @@ console.log("Echo mock frontend loaded"); appendMessage( "system", "TODO: connect to /api/signal/session/" + - currentSessionId + - "/party/B via WebSocket." + currentSessionId + + "/party/B via WebSocket." ); - mockConnectP2P(); + connectWebSocket() + } + + function connectWebSocket() { + // supporting ws for local testing. + var schema = window.location.protocol === "https" ? "wss://" : "ws://" + + var wsURL = schema + window.location.host + + + "/api/signal/session/" + currentSessionId + "/party/" + currentParty + + ws = new WebSocket(wsURL) + + ws.onopen = () => appendMessage("system", "Connected to WS") + } createBtn.addEventListener("click", function () {