193 lines
8.0 KiB
JavaScript
193 lines
8.0 KiB
JavaScript
import { r as reactExports, j as jsxRuntimeExports } from "../index.js";
|
|
import { i as isValidAutomergeUrl } from "./fullfat_node-75TjwUrn.js";
|
|
import { u as useRepo, a as useDocument } from "./index-BSpyO9eA.js";
|
|
import { h as hasVoted, a as addOption, u as unvote, v as vote } from "./poll-R5-eIJ_b.js";
|
|
import "../__vite_rsc_assets_manifest.js";
|
|
import "node:async_hooks";
|
|
import "tty";
|
|
import "util";
|
|
import "os";
|
|
import "node:crypto";
|
|
import "crypto";
|
|
import "module";
|
|
import "fs";
|
|
const PEER_ID_KEY = "p2p-poll-peer-id";
|
|
function generateUUID() {
|
|
return crypto.randomUUID();
|
|
}
|
|
function getPeerId() {
|
|
if (typeof globalThis.localStorage === "undefined") {
|
|
return generateUUID();
|
|
}
|
|
let id = localStorage.getItem(PEER_ID_KEY);
|
|
if (!id) {
|
|
id = generateUUID();
|
|
localStorage.setItem(PEER_ID_KEY, id);
|
|
}
|
|
return id;
|
|
}
|
|
function ConnectionStatus() {
|
|
const repo = useRepo();
|
|
const [connected, setConnected] = reactExports.useState(false);
|
|
const updateStatus = reactExports.useCallback(() => {
|
|
setConnected(repo.peers.length > 0);
|
|
}, [repo]);
|
|
reactExports.useEffect(() => {
|
|
updateStatus();
|
|
const onChange = () => updateStatus();
|
|
repo.networkSubsystem.on("peer", onChange);
|
|
repo.networkSubsystem.on("peer-disconnected", onChange);
|
|
return () => {
|
|
repo.networkSubsystem.off("peer", onChange);
|
|
repo.networkSubsystem.off("peer-disconnected", onChange);
|
|
};
|
|
}, [repo, updateStatus]);
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500", children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
"span",
|
|
{
|
|
className: `inline-block h-2 w-2 rounded-full ${connected ? "bg-green-500" : "bg-yellow-500"}`
|
|
}
|
|
),
|
|
connected ? "Connected" : "Connecting..."
|
|
] });
|
|
}
|
|
function PollView({ docUrl }) {
|
|
const [doc, changeDoc] = useDocument(docUrl);
|
|
const [newOption, setNewOption] = reactExports.useState("");
|
|
const [copied, setCopied] = reactExports.useState(false);
|
|
const peerId = reactExports.useMemo(() => getPeerId(), []);
|
|
if (!doc) {
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-gray-500", children: "Loading poll..." }) });
|
|
}
|
|
const totalVotes = doc.options.reduce((sum, o) => sum + o.votes.length, 0);
|
|
const handleAddOption = () => {
|
|
const text = newOption.trim();
|
|
if (!text) return;
|
|
changeDoc((d) => addOption(d, text));
|
|
setNewOption("");
|
|
};
|
|
const handleVote = (optionId) => {
|
|
if (hasVoted(doc, optionId, peerId)) {
|
|
changeDoc((d) => unvote(d, optionId, peerId));
|
|
} else {
|
|
changeDoc((d) => vote(d, optionId, peerId));
|
|
}
|
|
};
|
|
const handleCopy = () => {
|
|
const url = window.location.href;
|
|
navigator.clipboard.writeText(url).then(() => {
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2e3);
|
|
});
|
|
};
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between", children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl font-bold", children: doc.title }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-1 text-sm text-gray-500", children: [
|
|
totalVotes,
|
|
" vote",
|
|
totalVotes !== 1 ? "s" : "",
|
|
" total"
|
|
] })
|
|
] }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ConnectionStatus, {})
|
|
] }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
|
|
doc.options.map((option) => {
|
|
const voted = hasVoted(doc, option.id, peerId);
|
|
const pct = totalVotes > 0 ? option.votes.length / totalVotes * 100 : 0;
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
"div",
|
|
{
|
|
className: "relative overflow-hidden rounded-lg border border-gray-200 bg-white",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
"div",
|
|
{
|
|
className: "absolute inset-y-0 left-0 bg-blue-50 transition-all duration-300",
|
|
style: { width: `${pct}%` }
|
|
}
|
|
),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex items-center justify-between px-4 py-3", children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
"button",
|
|
{
|
|
onClick: () => handleVote(option.id),
|
|
className: `flex items-center gap-2 text-left text-sm font-medium ${voted ? "text-blue-600" : "text-gray-700 hover:text-blue-600"}`,
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
"span",
|
|
{
|
|
className: `flex h-5 w-5 items-center justify-center rounded border text-xs ${voted ? "border-blue-600 bg-blue-600 text-white" : "border-gray-300"}`,
|
|
children: voted ? "\u2713" : ""
|
|
}
|
|
),
|
|
option.text
|
|
]
|
|
}
|
|
),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm text-gray-500", children: [
|
|
option.votes.length,
|
|
" (",
|
|
pct.toFixed(0),
|
|
"%)"
|
|
] })
|
|
] })
|
|
]
|
|
},
|
|
option.id
|
|
);
|
|
}),
|
|
doc.options.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "py-4 text-center text-sm text-gray-400", children: "No options yet. Add one below!" })
|
|
] }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
"input",
|
|
{
|
|
type: "text",
|
|
value: newOption,
|
|
onChange: (e) => setNewOption(e.target.value),
|
|
onKeyDown: (e) => e.key === "Enter" && handleAddOption(),
|
|
placeholder: "Add an option...",
|
|
className: "flex-1 rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
}
|
|
),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
"button",
|
|
{
|
|
onClick: handleAddOption,
|
|
className: "rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700",
|
|
children: "Add"
|
|
}
|
|
)
|
|
] }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t border-gray-200 pt-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
"button",
|
|
{
|
|
onClick: handleCopy,
|
|
className: "rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-50",
|
|
children: copied ? "Copied!" : "Copy shareable link"
|
|
}
|
|
) })
|
|
] });
|
|
}
|
|
function PollPageClient({ id }) {
|
|
const automergeUrl = `automerge:${id}`;
|
|
if (!isValidAutomergeUrl(automergeUrl)) {
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 p-6 text-center", children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-semibold text-red-800", children: "Invalid Poll ID" }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-red-600", children: "The poll ID in the URL is not valid." }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/", className: "mt-4 inline-block text-sm text-blue-600 hover:underline", children: "Go back home" })
|
|
] });
|
|
}
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-lg border border-gray-200 bg-white p-6 shadow-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx(PollView, { docUrl: automergeUrl }) });
|
|
}
|
|
const export_4af94835fa0f = {
|
|
default: PollPageClient
|
|
};
|
|
export {
|
|
export_4af94835fa0f
|
|
};
|