/**
* ConnectDialog — broker connection + session lobby (list / create / join)
*
* The broker connection and the active session are decoupled: this dialog
* connects to the broker, which makes the session list available, and joining
* or creating a session is a separate step that reuses that connection.
*/
import { Component, createSignal, For, onMount, Show } from "solid-js";
import { TbOutlineTrash } from "solid-icons/tb";
import {
connectBroker,
createSession,
joinSession,
resolveBrokerUrl,
sessions,
setMyName,
setMyRole,
useJournalStream,
} from "../stores/journalStream";
import { DeleteSessionDialog } from "./DeleteSessionDialog";
export const ConnectDialog: Component = () => {
const stream = useJournalStream();
const manifest = sessions;
const [playerName, setPlayerName] = createSignal(stream.myName);
const [role, setRole] = createSignal<"gm" | "player" | "observer">(
stream.myRole,
);
const [newSessionName, setNewSessionName] = createSignal("");
const [busy, setBusy] = createSignal(false);
const [error, setError] = createSignal(null);
const [autoJoined, setAutoJoined] = createSignal(false);
const [sessionToDelete, setSessionToDelete] = createSignal<{
id: string;
name: string;
} | null>(null);
const ensureBroker = async () => {
if (stream.connected) return;
await connectBroker(resolveBrokerUrl());
};
const identity = () => ({
name: playerName().trim() || stream.myName,
role: role(),
});
const applyIdentity = () => {
const { name, role: r } = identity();
setMyName(name);
setMyRole(r);
};
const handleJoin = async (sessionId: string) => {
setBusy(true);
setError(null);
try {
applyIdentity();
await ensureBroker();
await joinSession(sessionId, identity());
} catch (e) {
setError(e instanceof Error ? e.message : "Connection failed");
} finally {
setBusy(false);
}
};
const handleCreate = async () => {
const name = newSessionName().trim();
if (!name) return;
setBusy(true);
setError(null);
try {
applyIdentity();
await ensureBroker();
const id = await createSession(name);
if (id) await joinSession(id, identity());
setNewSessionName("");
} catch (e) {
setError(e instanceof Error ? e.message : "Connection failed");
} finally {
setBusy(false);
}
};
// Auto-join when the URL carries session + player + autojoin params;
// otherwise enter the lobby so the session list is available up front.
onMount(() => {
const params = new URL(window.location.href).searchParams;
if (
params.has("session") &&
params.has("player") &&
params.has("autojoin")
) {
setPlayerName(params.get("player") || "");
setRole("player");
setAutoJoined(true);
handleJoin(params.get("session")!);
return;
}
ensureBroker().catch((e) => {
setError(e instanceof Error ? e.message : "Connection failed");
});
});
return (
{busy() ? "正在加入会话…" : "已加入!"}
}
>
输入你的名字和角色,选择或新建一个会话。
连接到 {window.location.host}
setPlayerName(e.currentTarget.value)}
onKeyDown={(e) => e.key === "Enter" && handleCreate()}
placeholder="你的名字"
class="w-full border border-gray-300 rounded px-3 py-1.5 text-sm"
autofocus
/>
{/* Role selector */}
{(["gm", "player", "observer"] as const).map((r) => (
))}
{/* Session list */}
会话
{([id, meta]) => (
)}
{stream.connected ? "还没有会话" : "正在加载会话…"}
{/* Create a new session and join it (GM only) */}
setNewSessionName(e.currentTarget.value)}
onKeyDown={(e) => e.key === "Enter" && handleCreate()}
placeholder="新会话名称"
class="flex-1 border border-gray-300 rounded px-2 py-1.5 text-sm"
/>
{error()}
{(target) => (
setSessionToDelete(null)}
/>
)}
);
};