import { getUserId } from "./identity"; import { addOption, toggleVote, setDeadline, clearDeadline, getDeadline, } from "./state"; import { initSync } from "./sync"; import { StatusBar } from "./components/StatusBar"; import { PollTitle } from "./components/PollTitle"; import { AddOption } from "./components/AddOption"; import { PollList } from "./components/PollList"; import { ShareSection } from "./components/ShareSection"; import { DeadlineTimer } from "./components/DeadlineTimer"; const ROOM_PARAM = "room"; function createRoomId(): string { if (typeof crypto.randomUUID === "function") { return `poll-${crypto.randomUUID().slice(0, 8)}`; } return `poll-${Math.random().toString(36).slice(2, 10)}`; } function ensureRoomId(): string { const url = new URL(window.location.href); let roomId = url.searchParams.get(ROOM_PARAM)?.trim(); if (!roomId) { roomId = createRoomId(); url.searchParams.set(ROOM_PARAM, roomId); window.history.replaceState({}, "", url); } return roomId; } export function initApp(container: HTMLElement): () => void { const roomId = ensureRoomId(); const userId = getUserId(); const sync = initSync(roomId); const shareUrl = window.location.href; // --- Actions --- const actions = { addOption: (label: string) => { if (isVotingClosed()) return; return addOption(sync.options, label, userId); }, toggleVote: (optionId: string) => { if (isVotingClosed()) return; toggleVote(sync.votes, userId, optionId); }, startDeadline: (durationMs: number) => { setDeadline(sync.deadlineMap, durationMs); }, clearDeadline: () => { clearDeadline(sync.deadlineMap); }, }; function isVotingClosed() { const deadline = getDeadline(sync.deadlineMap); const votingClosed = deadline !== null && Date.now() >= deadline; return votingClosed; } // --- Build UI --- // Header const header = document.createElement("header"); header.className = "app-header"; const wordmark = document.createElement("div"); wordmark.className = "app-wordmark"; wordmark.innerHTML = ` Polly `; const statusBar = StatusBar(sync.provider); header.append(wordmark, statusBar); // Main card const card = document.createElement("main"); card.className = "app-card"; const pollTitle = PollTitle(sync.doc, sync.yTitle); const addOptionComponent = AddOption((label: string) => { const result = actions.addOption(label); if (result && !result.ok) return result.error; return null; }); const pollList = PollList(sync.options, sync.votes, userId, isVotingClosed, actions.toggleVote); const deadlineTimer = DeadlineTimer( sync.deadlineMap, actions.startDeadline, actions.clearDeadline, ); card.append(pollTitle, addOptionComponent, deadlineTimer, pollList); // Footer const footer = document.createElement("footer"); footer.className = "app-footer"; footer.appendChild(ShareSection(roomId)); container.append(header, card, footer); // --- Cleanup --- return () => { sync.destroy(); }; }