Joining a room
Show the code and the QR on the stage, read it on the phone, handle a failed join.
On this page
On the stage
createRoom is synchronous. The QR is not: its encoder loads on first use. descriptor.qr is
always null. Ask stage.qr() for the matrix.
Room gives you a matrix of booleans, not an image. Draw it however you like.
import { app } from "./stage";
await app.start();
const { code, joinUrl } = app.stage.createRoom();
console.log(code, joinUrl);
const qr = await app.stage.qr();
if (qr) {
const rects = qr.modules.flatMap((dark, index) => {
const x = index % qr.size;
const y = Math.floor(index / qr.size);
return dark ? [`<rect x="${x}" y="${y}" width="1" height="1"/>`] : [];
});
document.body.innerHTML = `<svg viewBox="0 0 ${qr.size} ${qr.size}">${rects.join("")}</svg>`;
}nullwhengenerateQris false or no room is open.
Point the URL at the phone page
The join URL is joinUrlBase plus ?room=CODE. The default base is the stage page's origin.
Set joinUrlBase when the phone page lives elsewhere.
import { createApp, stagePlugin } from "@moku-labs/room";
export const app = createApp({
plugins: [stagePlugin],
pluginConfigs: { session: { joinUrlBase: "https://party.example/pad/" } }
});That app builds https://party.example/pad/?room=H9NSHM.
On the phone
Room does not parse the URL. Read the code and pass it on. joinRoom resolves when the channel to
the stage is open, and throws an Error when it is not.
import { app } from "./phone";
await app.start();
const code = new URLSearchParams(location.search).get("room") ?? "";
try {
await app.controller.joinRoom(code);
await app.controller.requestWakeLock();
} catch (error) {
console.error("Could not join", error);
await app.session.leave();
}falsewhere the browser has no wake lock. Harmless.- Without this, the next
joinRoomfails at once.
What a failed join looks like
| Case | Result |
|---|---|
| Unknown code | Waits reconnectTimeoutMs (10 s), then throws unreachable. Signaling cannot tell a wrong code from an empty room. |
| Signaling join throws | Throws not-found, and room:network-warning fires with rendezvous-unreachable. |
| Already in a room, or a failed join not left | Throws unreachable at once. |
Room over maxControllers | joinRoom resolves. The stage then closes the channel. The phone is not in the roster. |
fullis in the type, but nothing returns it. TheJoinResulttype namesfull. The client code never produces it, on any adapter. Do not build a "room full" screen on it.- Detect a full room yourself. On the stage, check
stage.roster().lengthinroom:peer-joined, and publish a slice the phone can read. - A wrong code takes 10 seconds to fail. Lower
reconnectTimeoutMsfor a faster answer. It also sets the reconnect window. requestWakeLockneeds a secure context. On plainhttp://it resolvesfalse. An iPhone that locks suspends its channel.- Roster entries have no name. Nothing fills
RosterEntry.name. Send a name as an intent and keep it in a slice.
Related
- session plugin: the code, QR and roster API.
- Failures and reloads: after the join.