1/* ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2 join-party.js -- shared "Join Party" widget logic for instant.html 3 and watch.html. A dedicated file rather than adding this to croom.js 4 because instant.html doesn't load croom.js at all, and croom.js is 5 large enough (11k+ lines, global room-page state, its own 6 DOMContentLoaded assumptions) that pulling it into instant.html just 7 for this would be a much bigger, riskier change than one small, 8 self-contained script both pages can load identically. 9 10 Expects these element IDs to exist on the page (see the Join Party 11 section HTML on each page): 12 #join-party-link - the pasted-link text input 13 #join-party-btn - the initial "Join" button 14 #join-party-code-row - wrapper for the code step, hidden until needed 15 #join-party-code-input - the access-code text input 16 #join-party-code-btn - the code step's "Join" button 17 #join-party-error - inline error message element 18 19 Reuses the exact same backend validation the browser already relies 20 on for private rooms (POST /api/crooms/<id>/access) rather than 21 reimplementing any access logic here -- this file only parses the 22 pasted link and decides which existing endpoint to call. 23 ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 24(function () { 25 "use strict"; 26 27 let _pendingRoomId = null; 28 29 function _parseRoomRef(raw) { 30 const input = (raw || "").trim(); 31 if (!input) return null; 32 33 let embeddedCode = null; 34 const codeMatch = input.match(/[?&]code=([^&\s]+)/i); 35 if (codeMatch) embeddedCode = decodeURIComponent(codeMatch[1]); 36 37 // Pretty URL: /watch/<slug> or /room/<slug> -- the format real share 38 // links actually use whenever the room has a slug at all. 39 const prettyMatch = input.match(/\/(?:watch|room)\/([a-zA-Z0-9_-]+)/i); 40 if (prettyMatch) { 41 return { type: "slug", value: prettyMatch[1].toLowerCase(), embeddedCode }; 42 } 43 44 // Fallback format: crooms-chat.html?room=<id> -- used when a room 45 // has no slug. 46 const idMatch = input.match(/[?&]room=(\d+)/i); 47 if (idMatch) { 48 return { type: "id", value: parseInt(idMatch[1], 10), embeddedCode }; 49 } 50 51 return null; 52 } 53 54 function _showError(msg) { 55 const err = document.getElementById("join-party-error"); 56 if (err) { 57 err.textContent = msg; 58 err.style.display = "block"; 59 } 60 } 61 62 function _clearError() { 63 const err = document.getElementById("join-party-error"); 64 if (err) { 65 err.style.display = "none"; 66 err.textContent = ""; 67 } 68 } 69 70 function _navigateToRoom(room) { 71 const from = room.room_category === "watch" ? "&from=watch" : ""; 72 window.location.href = "/crooms-chat.html?room=" + room.id + from; 73 } 74 75 async function _fetchRoom(ref) { 76 const url = 77 ref.type === "slug" 78 ? "/api/crooms/slug/" + encodeURIComponent(ref.value) 79 : "/api/crooms/" + ref.value + "/preview"; 80 const res = await fetch(url); 81 return res.json(); 82 } 83 84 async function _submitCode(roomId, code) { 85 const res = await fetch("/api/crooms/" + roomId + "/access", { 86 method: "POST", 87 headers: { "Content-Type": "application/json" }, 88 body: JSON.stringify({ code }), 89 }); 90 return res.json(); 91 } 92 93 function _revealCodeStep() { 94 const codeRow = document.getElementById("join-party-code-row"); 95 if (codeRow) codeRow.style.display = "flex"; 96 const codeInput = document.getElementById("join-party-code-input"); 97 if (codeInput) codeInput.focus(); 98 } 99 100 async function _handleJoin() { 101 _clearError(); 102 const linkInput = document.getElementById("join-party-link"); 103 const ref = _parseRoomRef(linkInput && linkInput.value); 104 if (!ref) { 105 _showError("That doesn't look like a valid party link."); 106 return; 107 } 108 109 const btn = document.getElementById("join-party-btn"); 110 if (btn) btn.disabled = true; 111 112 try { 113 const data = await _fetchRoom(ref); 114 if (!data.ok || !data.room) { 115 _showError("This party couldn't be found. It may have ended."); 116 return; 117 } 118 const room = data.room; 119 120 if (room.room_type !== "private") { 121 _navigateToRoom(room); 122 return; 123 } 124 125 _pendingRoomId = room.id; 126 127 // A link copied via Share already carries the code for rooms the 128 // sharer owns -- if it's present and still valid, join immediately 129 // rather than making the person retype what the link already gave. 130 if (ref.embeddedCode) { 131 const accessResult = await _submitCode(room.id, ref.embeddedCode); 132 if (accessResult.ok) { 133 _navigateToRoom(room); 134 return; 135 } 136 // Embedded code was wrong or stale (e.g. regenerated since the 137 // link was shared) -- fall through to the manual prompt below 138 // rather than dead-ending here. 139 } 140 141 _revealCodeStep(); 142 } catch (err) { 143 _showError("Something went wrong. Please try again."); 144 } finally { 145 if (btn) btn.disabled = false;
146 } 147 } 148 149 async function _handleCodeSubmit() { 150 _clearError(); 151 if (!_pendingRoomId) return; 152 153 const codeInput = document.getElementById("join-party-code-input"); 154 const code = ((codeInput && codeInput.value) || "").trim().toUpperCase(); 155 if (!code) { 156 _showError("Please enter the party code."); 157 return; 158 } 159 160 const btn = document.getElementById("join-party-code-btn"); 161 if (btn) btn.disabled = true; 162 163 try { 164 const result = await _submitCode(_pendingRoomId, code); 165 if (result.ok) { 166 window.location.href = "/crooms-chat.html?room=" + _pendingRoomId; 167 return; 168 } 169 if (result.error === "blocked") { 170 _showError("You don't have access to this party."); 171 } else { 172 _showError("That code doesn't match. Please check and try again."); 173 } 174 } catch (err) { 175 _showError("Something went wrong. Please try again."); 176 } finally { 177 if (btn) btn.disabled = false; 178 } 179 } 180 181 document.addEventListener("DOMContentLoaded", function () { 182 const linkInput = document.getElementById("join-party-link"); 183 const btn = document.getElementById("join-party-btn"); 184 const codeInput = document.getElementById("join-party-code-input"); 185 const codeBtn = document.getElementById("join-party-code-btn"); 186 187 if (btn) btn.addEventListener("click", _handleJoin); 188 if (linkInput) { 189 linkInput.addEventListener("keydown", function (e) { 190 if (e.key === "Enter") _handleJoin(); 191 }); 192 } 193 if (codeBtn) codeBtn.addEventListener("click", _handleCodeSubmit); 194 if (codeInput) { 195 codeInput.addEventListener("keydown", function (e) { 196 if (e.key === "Enter") _handleCodeSubmit(); 197 }); 198 } 199 }); 200})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.