diff --git a/web/src/App.tsx b/web/src/App.tsx index 89e196e..9ed031f 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -76,10 +76,21 @@ export function App() { if (roomId && entered) recordarSala(roomId); }, [roomId, entered]); - // Pantalla 1: sin sala → crear o pegar link. - if (!roomId) return ; + /** + * Sin sala en la URL se entra igual, a la Sala vacía. + * + * Antes había una portada con un botón de "crear una sala", y eso hacía dos + * cosas malas: quien ya tenía salas no las veía (viven en el menú, que solo + * existe dentro), así que creaba otra; y el único camino para llegar a + * cualquier lado era crear una, aunque solo quisieras volver a la de ayer. + * Cuatro de nueve salas acabaron vacías. + * + * Ahora se cae dentro con el menú a mano, y crear es un botón más. + */ + if (!roomId) return ; - // Pantalla 2: hay sala pero falta nombre. + // Hay sala pero falta decir cómo te llamas. Sigue haciendo falta para quien + // llega por un link que le pasaron: la sala necesita saber quién entró. if (!entered) { return ( ; } -// ── Pantalla: crear / entrar a sala ──────────────────────────────────────── - -function Landing() { - const { t } = useTextos(); - const [busy, setBusy] = useState(false); - const [pegando, setPegando] = useState(false); - const [link, setLink] = useState(""); - const [error, setError] = useState(null); - - const nueva = async () => { - setBusy(true); - try { - const id = await createRoom(); - window.location.hash = `#/sala/${id}`; - } catch (e) { - alert(t.noSePudoCrear + String(e)); - setBusy(false); - } - }; - - /** - * Entrar con lo que te pasó tu compa. - * - * Normalmente el link se abre y ya (el id vive en el hash), pero llega por - * WhatsApp o Discord y a veces se copia mal, se parte, o te pasan solo el - * nombre de la sala. Acepta las tres formas en vez de exigir la URL exacta. - */ - const entrar = () => { - const id = extraerRoomId(link); - if (!id) { - setError(t.noEncontreSala); - return; - } - window.location.hash = `#/sala/${id}`; - }; - - return ( -
-
-

MULTI

-

{t.tagline}

- - - {pegando ? ( -
- { - setLink(e.target.value); - setError(null); - }} - onKeyDown={(e) => { - if (e.key === "Enter") entrar(); - if (e.key === "Escape") setPegando(false); - }} - autoFocus - /> - - {error &&

{error}

} -
- ) : ( - - )} -
-
- ); -} - -/** - * Saca el id de la sala de lo que sea que hayan pegado: la URL completa, solo el - * fragmento `#/sala/x`, o el nombre pelón. Los ids son de la forma - * `palabra-palabra-numero`. - */ -function extraerRoomId(raw: string): string | null { - const texto = raw.trim(); - if (!texto) return null; - const enRuta = texto.match(/\/sala\/([\w-]+)/); - if (enRuta) return enRuta[1]; - // Solo el nombre: debe parecer un id de sala, no una frase cualquiera. - if (/^[a-z]+-[a-z]+-\d+$/i.test(texto)) return texto; - return null; -} - -// ── Pantalla: pedir nombre ───────────────────────────────────────────────── function NamePrompt(props: { roomId: string; @@ -233,7 +152,12 @@ function NamePrompt(props: { // ── La Sala ───────────────────────────────────────────────────────────────── -function Sala({ roomId, name }: { roomId: string; name: string }) { +/** + * La Sala. Con `roomId` en null se ve el mismo marco pero vacío: es lo que hay + * al entrar a Multi sin haber elegido sala, con el menú y el botón de crear a + * la mano. + */ +function Sala({ roomId, name }: { roomId: string | null; name: string }) { const { t } = useTextos(); const [members, setMembers] = useState([]); const [messages, setMessages] = useState([]); @@ -247,6 +171,7 @@ function Sala({ roomId, name }: { roomId: string; name: string }) { */ const [nombre, setNombre] = useState(null); const [editandoNombre, setEditandoNombre] = useState(false); + const [creandoSala, setCreandoSala] = useState(false); /** Qué dice el botón de descargar ahora mismo. null = su texto normal. */ const [zipAviso, setZipAviso] = useState(null); /** @@ -333,6 +258,10 @@ function Sala({ roomId, name }: { roomId: string; name: string }) { const proxyOrigin = new URL(SERVER_URL).origin; useEffect(() => { + // Sin sala no hay a qué conectarse: la Sala vacía es solo el marco con el + // menú y el botón de crear. + if (!roomId) return; + const socket = connectSocket(); socketRef.current = socket; @@ -598,6 +527,18 @@ function Sala({ roomId, name }: { roomId: string; name: string }) { const copyLink = () => navigator.clipboard.writeText(window.location.href); + /** Crear otra sala y entrar a ella. El hash es lo que cambia de sala. */ + const crearSala = async () => { + setCreandoSala(true); + try { + window.location.hash = `#/sala/${await createRoom()}`; + } catch (e) { + alert(t.noSePudoCrear + String(e)); + } finally { + setCreandoSala(false); + } + }; + /** * Guardar el nombre nuevo de la sala. * @@ -679,9 +620,13 @@ function Sala({ roomId, name }: { roomId: string; name: string }) { {/* Chat izquierda */}