Add implementation
This commit is contained in:
54
dist/server/ssr/__vite_rsc_assets_manifest.js
vendored
Normal file
54
dist/server/ssr/__vite_rsc_assets_manifest.js
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
export default {
|
||||
"bootstrapScriptContent": "import(\"/assets/index-I8cR0Dsm.js\")",
|
||||
"clientReferenceDeps": {
|
||||
"847a2b1045ef": {
|
||||
"js": [
|
||||
"/assets/client-CsOmnPdF.js",
|
||||
"/assets/index-I8cR0Dsm.js"
|
||||
],
|
||||
"css": []
|
||||
},
|
||||
"6d786e16fc6b": {
|
||||
"js": [
|
||||
"/assets/client-CsOmnPdF.js",
|
||||
"/assets/index-I8cR0Dsm.js"
|
||||
],
|
||||
"css": []
|
||||
},
|
||||
"5ce7e027532e": {
|
||||
"js": [
|
||||
"/assets/index-BQZM9uZj.js",
|
||||
"/assets/index-I8cR0Dsm.js",
|
||||
"/assets/index-ChbWTil4.js",
|
||||
"/assets/poll-BI_0HvZY.js"
|
||||
],
|
||||
"css": []
|
||||
},
|
||||
"4af94835fa0f": {
|
||||
"js": [
|
||||
"/assets/_id_-cxsEKuSj.js",
|
||||
"/assets/index-I8cR0Dsm.js",
|
||||
"/assets/fullfat_bundler-C8o4MXnP.js",
|
||||
"/assets/index-ChbWTil4.js",
|
||||
"/assets/poll-BI_0HvZY.js"
|
||||
],
|
||||
"css": []
|
||||
},
|
||||
"125820ecd802": {
|
||||
"js": [
|
||||
"/assets/_layout-C9jEQBWP.js",
|
||||
"/assets/index-I8cR0Dsm.js",
|
||||
"/assets/index-ChbWTil4.js"
|
||||
],
|
||||
"css": []
|
||||
}
|
||||
},
|
||||
"serverResources": {
|
||||
"src/pages/_layout.tsx": {
|
||||
"js": [],
|
||||
"css": [
|
||||
"/assets/_layout-ChjUcnq2.css"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
192
dist/server/ssr/assets/_id_-BAEItp57.js
vendored
Normal file
192
dist/server/ssr/assets/_id_-BAEItp57.js
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
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
|
||||
};
|
||||
53
dist/server/ssr/assets/_layout-DwifDpT-.js
vendored
Normal file
53
dist/server/ssr/assets/_layout-DwifDpT-.js
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import { r as reactExports, j as jsxRuntimeExports, __tla as __tla_0 } from "../index.js";
|
||||
import { R as RepoContext } from "./index-BSpyO9eA.js";
|
||||
import "../__vite_rsc_assets_manifest.js";
|
||||
import "node:async_hooks";
|
||||
let export_125820ecd802;
|
||||
let __tla = Promise.all([
|
||||
(() => {
|
||||
try {
|
||||
return __tla_0;
|
||||
} catch {
|
||||
}
|
||||
})()
|
||||
]).then(async () => {
|
||||
function Providers({ children }) {
|
||||
const [repo, setRepo] = reactExports.useState(null);
|
||||
reactExports.useEffect(() => {
|
||||
let cleanup;
|
||||
let handleBeforeUnload;
|
||||
import("./repo-zy9lifAg.js").then(({ getRepo, cleanupRepo }) => {
|
||||
const r = getRepo();
|
||||
setRepo(r);
|
||||
cleanup = cleanupRepo;
|
||||
handleBeforeUnload = () => {
|
||||
r.networkSubsystem.adapters.forEach((adapter) => adapter.disconnect());
|
||||
};
|
||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||
});
|
||||
return () => {
|
||||
if (handleBeforeUnload) {
|
||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||
}
|
||||
cleanup == null ? void 0 : cleanup();
|
||||
};
|
||||
}, []);
|
||||
if (!repo) {
|
||||
return jsxRuntimeExports.jsx("div", {
|
||||
className: "flex min-h-screen items-center justify-center text-gray-400",
|
||||
children: "Loading..."
|
||||
});
|
||||
}
|
||||
return jsxRuntimeExports.jsx(RepoContext.Provider, {
|
||||
value: repo,
|
||||
children
|
||||
});
|
||||
}
|
||||
export_125820ecd802 = {
|
||||
default: Providers
|
||||
};
|
||||
});
|
||||
export {
|
||||
__tla,
|
||||
export_125820ecd802
|
||||
};
|
||||
104
dist/server/ssr/assets/client-CeLGCvkj.js
vendored
Normal file
104
dist/server/ssr/assets/client-CeLGCvkj.js
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
import { j as jsxRuntimeExports, S as Slot, r as reactExports, C as Children } from "../index.js";
|
||||
import "../__vite_rsc_assets_manifest.js";
|
||||
import "node:async_hooks";
|
||||
const RouterContext = /* @__PURE__ */ reactExports.createContext(null);
|
||||
const notAvailableInServer = (name) => () => {
|
||||
throw new Error(`${name} is not in the server`);
|
||||
};
|
||||
function renderError(message) {
|
||||
return /* @__PURE__ */ jsxRuntimeExports.jsxs("html", {
|
||||
children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx("head", {
|
||||
children: /* @__PURE__ */ jsxRuntimeExports.jsx("title", {
|
||||
children: "Unhandled Error"
|
||||
})
|
||||
}),
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsxs("body", {
|
||||
style: {
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
placeContent: "center",
|
||||
placeItems: "center",
|
||||
fontSize: "16px",
|
||||
margin: 0
|
||||
},
|
||||
children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx("h1", {
|
||||
children: "Caught an unexpected error"
|
||||
}),
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", {
|
||||
children: [
|
||||
"Error: ",
|
||||
message
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
class ErrorBoundary extends reactExports.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
static getDerivedStateFromError(error) {
|
||||
return {
|
||||
error
|
||||
};
|
||||
}
|
||||
render() {
|
||||
if ("error" in this.state) {
|
||||
if (this.state.error instanceof Error) {
|
||||
return renderError(this.state.error.message);
|
||||
}
|
||||
return renderError(String(this.state.error));
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
const getRouteSlotId = (path) => "route:" + path;
|
||||
const MOCK_ROUTE_CHANGE_LISTENER = {
|
||||
on: () => notAvailableInServer("routeChange:on"),
|
||||
off: () => notAvailableInServer("routeChange:off")
|
||||
};
|
||||
function INTERNAL_ServerRouter({ route, httpstatus }) {
|
||||
const routeElement = /* @__PURE__ */ jsxRuntimeExports.jsx(Slot, {
|
||||
id: getRouteSlotId(route.path)
|
||||
});
|
||||
const rootElement = /* @__PURE__ */ jsxRuntimeExports.jsxs(Slot, {
|
||||
id: "root",
|
||||
children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx("meta", {
|
||||
name: "httpstatus",
|
||||
content: `${httpstatus}`
|
||||
}),
|
||||
routeElement
|
||||
]
|
||||
});
|
||||
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {
|
||||
children: /* @__PURE__ */ jsxRuntimeExports.jsx(RouterContext, {
|
||||
value: {
|
||||
route,
|
||||
changeRoute: notAvailableInServer("changeRoute"),
|
||||
prefetchRoute: notAvailableInServer("prefetchRoute"),
|
||||
routeChangeEvents: MOCK_ROUTE_CHANGE_LISTENER,
|
||||
fetchingSlices: /* @__PURE__ */ new Set()
|
||||
},
|
||||
children: rootElement
|
||||
})
|
||||
});
|
||||
}
|
||||
const export_847a2b1045ef = {
|
||||
Children,
|
||||
Slot
|
||||
};
|
||||
const export_6d786e16fc6b = {
|
||||
ErrorBoundary,
|
||||
INTERNAL_ServerRouter
|
||||
};
|
||||
export {
|
||||
export_6d786e16fc6b,
|
||||
export_847a2b1045ef
|
||||
};
|
||||
13082
dist/server/ssr/assets/fullfat_node-75TjwUrn.js
vendored
Normal file
13082
dist/server/ssr/assets/fullfat_node-75TjwUrn.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5592
dist/server/ssr/assets/index-BSpyO9eA.js
vendored
Normal file
5592
dist/server/ssr/assets/index-BSpyO9eA.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
82
dist/server/ssr/assets/index-CNRWZdhS.js
vendored
Normal file
82
dist/server/ssr/assets/index-CNRWZdhS.js
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import { r as reactExports, j as jsxRuntimeExports } from "../index.js";
|
||||
import { u as useRepo } from "./index-BSpyO9eA.js";
|
||||
import { c as createPoll } from "./poll-R5-eIJ_b.js";
|
||||
import "../__vite_rsc_assets_manifest.js";
|
||||
import "node:async_hooks";
|
||||
function HomeClient() {
|
||||
const [title, setTitle] = reactExports.useState("");
|
||||
const [joinId, setJoinId] = reactExports.useState("");
|
||||
const repo = useRepo();
|
||||
const handleCreate = () => {
|
||||
if (!title.trim()) return;
|
||||
const handle = repo.create();
|
||||
handle.change((doc) => {
|
||||
const poll = createPoll(title.trim());
|
||||
doc.title = poll.title;
|
||||
doc.options = poll.options;
|
||||
});
|
||||
window.location.href = `/poll/${handle.documentId}`;
|
||||
};
|
||||
const handleJoin = () => {
|
||||
const id = joinId.trim();
|
||||
if (!id) return;
|
||||
window.location.href = `/poll/${id}`;
|
||||
};
|
||||
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-8", children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: "P2P Poll" }),
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "rounded-lg border border-gray-200 bg-white p-6 shadow-sm", children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "mb-4 text-lg font-semibold", children: "Create a New Poll" }),
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
||||
"input",
|
||||
{
|
||||
type: "text",
|
||||
value: title,
|
||||
onChange: (e) => setTitle(e.target.value),
|
||||
onKeyDown: (e) => e.key === "Enter" && handleCreate(),
|
||||
placeholder: "Enter poll title...",
|
||||
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: handleCreate,
|
||||
className: "rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700",
|
||||
children: "Create"
|
||||
}
|
||||
)
|
||||
] })
|
||||
] }),
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "rounded-lg border border-gray-200 bg-white p-6 shadow-sm", children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "mb-4 text-lg font-semibold", children: "Join an Existing Poll" }),
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
||||
"input",
|
||||
{
|
||||
type: "text",
|
||||
value: joinId,
|
||||
onChange: (e) => setJoinId(e.target.value),
|
||||
onKeyDown: (e) => e.key === "Enter" && handleJoin(),
|
||||
placeholder: "Paste poll ID or link...",
|
||||
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: handleJoin,
|
||||
className: "rounded-md bg-gray-600 px-4 py-2 text-sm font-medium text-white hover:bg-gray-700",
|
||||
children: "Join"
|
||||
}
|
||||
)
|
||||
] })
|
||||
] })
|
||||
] });
|
||||
}
|
||||
const export_5ce7e027532e = {
|
||||
default: HomeClient
|
||||
};
|
||||
export {
|
||||
export_5ce7e027532e
|
||||
};
|
||||
39
dist/server/ssr/assets/poll-R5-eIJ_b.js
vendored
Normal file
39
dist/server/ssr/assets/poll-R5-eIJ_b.js
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
function createPoll(title) {
|
||||
return {
|
||||
title,
|
||||
options: []
|
||||
};
|
||||
}
|
||||
function addOption(poll, text) {
|
||||
poll.options.push({
|
||||
id: crypto.randomUUID(),
|
||||
text,
|
||||
votes: []
|
||||
});
|
||||
}
|
||||
function hasVoted(poll, optionId, peerId) {
|
||||
const option = poll.options.find((o) => o.id === optionId);
|
||||
if (!option) return false;
|
||||
return option.votes.includes(peerId);
|
||||
}
|
||||
function vote(poll, optionId, peerId) {
|
||||
const option = poll.options.find((o) => o.id === optionId);
|
||||
if (!option) return;
|
||||
if (option.votes.includes(peerId)) return;
|
||||
option.votes.push(peerId);
|
||||
}
|
||||
function unvote(poll, optionId, peerId) {
|
||||
const option = poll.options.find((o) => o.id === optionId);
|
||||
if (!option) return;
|
||||
const idx = option.votes.indexOf(peerId);
|
||||
if (idx !== -1) {
|
||||
option.votes.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
export {
|
||||
addOption as a,
|
||||
createPoll as c,
|
||||
hasVoted as h,
|
||||
unvote as u,
|
||||
vote as v
|
||||
};
|
||||
5494
dist/server/ssr/assets/repo-zy9lifAg.js
vendored
Normal file
5494
dist/server/ssr/assets/repo-zy9lifAg.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11296
dist/server/ssr/index.js
vendored
Normal file
11296
dist/server/ssr/index.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user