forked from quic-issues/427e7578-d7bf-49c8-aee9-2dd999e25316
158 lines
4.0 KiB
TypeScript
158 lines
4.0 KiB
TypeScript
import { loadAllPolls, savePoll, deletePoll as dbDeletePoll, loadPoll } from '$lib/db.js';
|
|
import type { Poll, PollOption, Vote, RoleAssignment } from '$lib/types.js';
|
|
import { getUserId } from '$lib/crypto.js';
|
|
|
|
let polls = $state<Poll[]>([]);
|
|
let loading = $state(true);
|
|
|
|
export function getPolls() {
|
|
return {
|
|
get all() { return polls; },
|
|
get loading() { return loading; },
|
|
get owned() {
|
|
const id = currentUserId;
|
|
return id ? polls.filter((p) => p.ownerId === id) : [];
|
|
},
|
|
get participating() {
|
|
const id = currentUserId;
|
|
return id ? polls.filter((p) => p.ownerId !== id) : [];
|
|
}
|
|
};
|
|
}
|
|
|
|
let currentUserId: string | null = null;
|
|
|
|
export async function initPolls(): Promise<void> {
|
|
loading = true;
|
|
currentUserId = await getUserId();
|
|
polls = await loadAllPolls();
|
|
loading = false;
|
|
}
|
|
|
|
export async function createPoll(data: {
|
|
title: string;
|
|
description: string;
|
|
anonymous: boolean;
|
|
visibility: Poll['visibility'];
|
|
options?: string[];
|
|
}): Promise<Poll> {
|
|
const userId = await getUserId();
|
|
const now = Date.now();
|
|
|
|
const poll: Poll = {
|
|
id: crypto.randomUUID(),
|
|
ownerId: userId,
|
|
title: data.title,
|
|
description: data.description,
|
|
anonymous: data.anonymous,
|
|
status: 'draft',
|
|
visibility: data.visibility,
|
|
createdAt: now,
|
|
options: (data.options ?? []).map((text) => ({
|
|
id: crypto.randomUUID(),
|
|
text,
|
|
addedBy: userId,
|
|
addedAt: now
|
|
})),
|
|
votes: [],
|
|
roles: []
|
|
};
|
|
|
|
await savePoll(poll);
|
|
polls = [...polls, poll];
|
|
return poll;
|
|
}
|
|
|
|
export async function updatePollInStore(poll: Poll): Promise<void> {
|
|
await savePoll(poll);
|
|
polls = polls.map((p) => (p.id === poll.id ? poll : p));
|
|
}
|
|
|
|
export function getPollById(id: string): Poll | undefined {
|
|
return polls.find((p) => p.id === id);
|
|
}
|
|
|
|
export async function refreshPoll(id: string): Promise<Poll | undefined> {
|
|
const poll = await loadPoll(id);
|
|
if (poll) {
|
|
polls = polls.map((p) => (p.id === id ? poll : p));
|
|
if (!polls.find((p) => p.id === id)) {
|
|
polls = [...polls, poll];
|
|
}
|
|
}
|
|
return poll;
|
|
}
|
|
|
|
export async function deletePollFromStore(id: string): Promise<void> {
|
|
await dbDeletePoll(id);
|
|
polls = polls.filter((p) => p.id !== id);
|
|
}
|
|
|
|
export async function addOptionToPoll(pollId: string, text: string): Promise<PollOption | null> {
|
|
const poll = getPollById(pollId);
|
|
if (!poll || poll.status !== 'open') return null;
|
|
|
|
const userId = await getUserId();
|
|
const option: PollOption = {
|
|
id: crypto.randomUUID(),
|
|
text,
|
|
addedBy: userId,
|
|
addedAt: Date.now()
|
|
};
|
|
|
|
const updated = { ...poll, options: [...poll.options, option] };
|
|
await updatePollInStore(updated);
|
|
return option;
|
|
}
|
|
|
|
export async function castVote(pollId: string, optionId: string): Promise<Vote | null> {
|
|
const poll = getPollById(pollId);
|
|
if (!poll || poll.status !== 'open') return null;
|
|
|
|
const userId = await getUserId();
|
|
const vote: Vote = {
|
|
optionId,
|
|
voterId: poll.anonymous ? null : userId,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
// Remove previous vote by this user (for vote changes)
|
|
const filteredVotes = poll.anonymous
|
|
? poll.votes // Can't deduplicate anonymous votes by voterId
|
|
: poll.votes.filter((v) => v.voterId !== userId);
|
|
|
|
const updated = { ...poll, votes: [...filteredVotes, vote] };
|
|
await updatePollInStore(updated);
|
|
return vote;
|
|
}
|
|
|
|
export async function setPollStatus(pollId: string, status: Poll['status']): Promise<void> {
|
|
const poll = getPollById(pollId);
|
|
if (!poll) return;
|
|
|
|
const updated = {
|
|
...poll,
|
|
status,
|
|
...(status === 'closed' ? { closedAt: Date.now() } : {})
|
|
};
|
|
await updatePollInStore(updated);
|
|
}
|
|
|
|
export async function setRole(pollId: string, userId: string, role: RoleAssignment['role']): Promise<void> {
|
|
const poll = getPollById(pollId);
|
|
if (!poll) return;
|
|
|
|
const roles = poll.roles.filter((r) => r.userId !== userId);
|
|
roles.push({ userId, role });
|
|
const updated = { ...poll, roles };
|
|
await updatePollInStore(updated);
|
|
}
|
|
|
|
export async function removeRole(pollId: string, userId: string): Promise<void> {
|
|
const poll = getPollById(pollId);
|
|
if (!poll) return;
|
|
|
|
const updated = { ...poll, roles: poll.roles.filter((r) => r.userId !== userId) };
|
|
await updatePollInStore(updated);
|
|
}
|