- remove unnecessary / deprecated code
This commit is contained in:
33
src/app.ts
33
src/app.ts
@@ -2,10 +2,9 @@ import { getUserId } from "./identity";
|
|||||||
import {
|
import {
|
||||||
addOption,
|
addOption,
|
||||||
toggleVote,
|
toggleVote,
|
||||||
deleteOption,
|
|
||||||
setDeadline,
|
setDeadline,
|
||||||
clearDeadline,
|
clearDeadline,
|
||||||
createViewModel,
|
getDeadline,
|
||||||
} from "./state";
|
} from "./state";
|
||||||
import { initSync } from "./sync";
|
import { initSync } from "./sync";
|
||||||
import { StatusBar } from "./components/StatusBar";
|
import { StatusBar } from "./components/StatusBar";
|
||||||
@@ -48,18 +47,13 @@ export function initApp(container: HTMLElement): () => void {
|
|||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
addOption: (label: string) => {
|
addOption: (label: string) => {
|
||||||
const vm = createViewModel(getViewModelParams());
|
if (isVotingClosed()) return;
|
||||||
if (vm.votingClosed) return;
|
|
||||||
return addOption(sync.options, label, userId);
|
return addOption(sync.options, label, userId);
|
||||||
},
|
},
|
||||||
toggleVote: (optionId: string) => {
|
toggleVote: (optionId: string) => {
|
||||||
const vm = createViewModel(getViewModelParams());
|
if (isVotingClosed()) return;
|
||||||
if (vm.votingClosed) return;
|
|
||||||
toggleVote(sync.votes, userId, optionId);
|
toggleVote(sync.votes, userId, optionId);
|
||||||
},
|
},
|
||||||
deleteOption: (optionId: string) => {
|
|
||||||
deleteOption(sync.options, sync.votes, optionId);
|
|
||||||
},
|
|
||||||
startDeadline: (durationMs: number) => {
|
startDeadline: (durationMs: number) => {
|
||||||
setDeadline(sync.deadlineMap, durationMs);
|
setDeadline(sync.deadlineMap, durationMs);
|
||||||
},
|
},
|
||||||
@@ -68,18 +62,10 @@ export function initApp(container: HTMLElement): () => void {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function getViewModelParams() {
|
function isVotingClosed() {
|
||||||
return {
|
const deadline = getDeadline(sync.deadlineMap);
|
||||||
yTitle: sync.yTitle,
|
const votingClosed = deadline !== null && Date.now() >= deadline;
|
||||||
options: sync.options,
|
return votingClosed;
|
||||||
votes: sync.votes,
|
|
||||||
deadlineMap: sync.deadlineMap,
|
|
||||||
roomId,
|
|
||||||
shareUrl,
|
|
||||||
connectionStatus: sync.getConnectionStatus(),
|
|
||||||
peerCount: sync.getPeerCount(),
|
|
||||||
userId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Build UI ---
|
// --- Build UI ---
|
||||||
@@ -112,10 +98,7 @@ export function initApp(container: HTMLElement): () => void {
|
|||||||
if (result && !result.ok) return result.error;
|
if (result && !result.ok) return result.error;
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
const pollList = PollList(sync.options, sync.votes, userId, () => {
|
const pollList = PollList(sync.options, sync.votes, userId, isVotingClosed, actions.toggleVote);
|
||||||
const vm = createViewModel(getViewModelParams());
|
|
||||||
return vm.votingClosed;
|
|
||||||
}, actions.toggleVote, actions.deleteOption);
|
|
||||||
const deadlineTimer = DeadlineTimer(
|
const deadlineTimer = DeadlineTimer(
|
||||||
sync.deadlineMap,
|
sync.deadlineMap,
|
||||||
actions.startDeadline,
|
actions.startDeadline,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export function PollList(
|
|||||||
userId: string,
|
userId: string,
|
||||||
isVotingClosed: () => boolean,
|
isVotingClosed: () => boolean,
|
||||||
onVote: (optionId: string) => void,
|
onVote: (optionId: string) => void,
|
||||||
onDelete: (optionId: string) => void,
|
|
||||||
): HTMLElement {
|
): HTMLElement {
|
||||||
|
|
||||||
var currentOptions : { [x: string]: any; } | undefined = undefined
|
var currentOptions : { [x: string]: any; } | undefined = undefined
|
||||||
@@ -112,7 +111,6 @@ export function PollList(
|
|||||||
totalVotes: total,
|
totalVotes: total,
|
||||||
votingClosed,
|
votingClosed,
|
||||||
onVote,
|
onVote,
|
||||||
onDelete,
|
|
||||||
});
|
});
|
||||||
const currentEl = list.children[i] as HTMLElement | undefined;
|
const currentEl = list.children[i] as HTMLElement | undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ export interface PollOptionProps {
|
|||||||
totalVotes: number;
|
totalVotes: number;
|
||||||
votingClosed: boolean;
|
votingClosed: boolean;
|
||||||
onVote: (id: string) => void;
|
onVote: (id: string) => void;
|
||||||
onDelete: (id: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PollOption(props: PollOptionProps): HTMLElement {
|
export function PollOption(props: PollOptionProps): HTMLElement {
|
||||||
const { id, name, votes, voted, totalVotes, votingClosed, onVote, onDelete } = props;
|
const { id, name, votes, voted, totalVotes, votingClosed, onVote } = props;
|
||||||
|
|
||||||
const row = document.createElement("div");
|
const row = document.createElement("div");
|
||||||
row.className = `poll-option${voted ? " poll-option--voted" : ""}`;
|
row.className = `poll-option${voted ? " poll-option--voted" : ""}`;
|
||||||
@@ -30,17 +29,11 @@ export function PollOption(props: PollOptionProps): HTMLElement {
|
|||||||
<button class="poll-option__vote-btn" aria-pressed="${voted}"${votingClosed ? " disabled" : ""}>
|
<button class="poll-option__vote-btn" aria-pressed="${voted}"${votingClosed ? " disabled" : ""}>
|
||||||
${voted ? "Voted" : "Vote"}
|
${voted ? "Voted" : "Vote"}
|
||||||
</button>
|
</button>
|
||||||
<button class="poll-option__delete-btn" aria-label="Remove option">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M2 2l10 10M12 2L2 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
row.querySelector(".poll-option__vote-btn")!.addEventListener("click", () => onVote(id));
|
row.querySelector(".poll-option__vote-btn")!.addEventListener("click", () => onVote(id));
|
||||||
row.querySelector(".poll-option__delete-btn")!.addEventListener("click", () => onDelete(id));
|
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|||||||
103
src/state.ts
103
src/state.ts
@@ -11,25 +11,6 @@ export interface OptionRecord {
|
|||||||
createdBy: string;
|
createdBy: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PollOptionViewModel extends OptionRecord {
|
|
||||||
voteCount: number;
|
|
||||||
isVotedByMe: boolean;
|
|
||||||
percentage: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PollViewModel {
|
|
||||||
title: string;
|
|
||||||
roomId: string;
|
|
||||||
shareUrl: string;
|
|
||||||
connectionStatus: ConnectionStatus;
|
|
||||||
peerCount: number;
|
|
||||||
options: PollOptionViewModel[];
|
|
||||||
totalVotes: number;
|
|
||||||
myVoteOptionId: string | null;
|
|
||||||
deadline: number | null;
|
|
||||||
votingClosed: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Helpers ---
|
// --- Helpers ---
|
||||||
|
|
||||||
export function createOptionId(): string {
|
export function createOptionId(): string {
|
||||||
@@ -104,20 +85,6 @@ export function toggleVote(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteOption(
|
|
||||||
options: Y.Map<OptionRecord>,
|
|
||||||
votes: Y.Map<string>,
|
|
||||||
optionId: string,
|
|
||||||
): void {
|
|
||||||
options.delete(optionId);
|
|
||||||
// Clean up votes pointing to this option
|
|
||||||
for (const [userId, votedOptionId] of votes.entries()) {
|
|
||||||
if (votedOptionId === optionId) {
|
|
||||||
votes.delete(userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Deadline ---
|
// --- Deadline ---
|
||||||
|
|
||||||
export function setDeadline(
|
export function setDeadline(
|
||||||
@@ -135,73 +102,3 @@ export function getDeadline(deadlineMap: Y.Map<unknown>): number | null {
|
|||||||
const val = deadlineMap.get("deadline");
|
const val = deadlineMap.get("deadline");
|
||||||
return typeof val === "number" ? val : null;
|
return typeof val === "number" ? val : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- ViewModel ---
|
|
||||||
|
|
||||||
export function createViewModel(params: {
|
|
||||||
yTitle: Y.Text;
|
|
||||||
options: Y.Map<OptionRecord>;
|
|
||||||
votes: Y.Map<string>;
|
|
||||||
deadlineMap: Y.Map<unknown>;
|
|
||||||
roomId: string;
|
|
||||||
shareUrl: string;
|
|
||||||
connectionStatus: ConnectionStatus;
|
|
||||||
peerCount: number;
|
|
||||||
userId: string;
|
|
||||||
}): PollViewModel {
|
|
||||||
const {
|
|
||||||
yTitle,
|
|
||||||
options,
|
|
||||||
votes,
|
|
||||||
deadlineMap,
|
|
||||||
roomId,
|
|
||||||
shareUrl,
|
|
||||||
connectionStatus,
|
|
||||||
peerCount,
|
|
||||||
userId,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
// Tally votes per option
|
|
||||||
const tally = new Map<string, number>();
|
|
||||||
for (const optionId of votes.values()) {
|
|
||||||
tally.set(optionId, (tally.get(optionId) ?? 0) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let totalVotes = 0;
|
|
||||||
for (const count of tally.values()) {
|
|
||||||
totalVotes += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
const myVoteOptionId = votes.get(userId) ?? null;
|
|
||||||
const deadline = getDeadline(deadlineMap);
|
|
||||||
const votingClosed = deadline !== null && Date.now() >= deadline;
|
|
||||||
|
|
||||||
// Sort by votes desc, then alphabetically
|
|
||||||
const sortedOptions = Array.from(options.values()).sort((a, b) => {
|
|
||||||
const aVotes = tally.get(a.id) ?? 0;
|
|
||||||
const bVotes = tally.get(b.id) ?? 0;
|
|
||||||
if (bVotes !== aVotes) return bVotes - aVotes;
|
|
||||||
return a.label.localeCompare(b.label);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: getPollTitle(yTitle),
|
|
||||||
roomId,
|
|
||||||
shareUrl,
|
|
||||||
connectionStatus,
|
|
||||||
peerCount,
|
|
||||||
myVoteOptionId,
|
|
||||||
totalVotes,
|
|
||||||
deadline,
|
|
||||||
votingClosed,
|
|
||||||
options: sortedOptions.map((option) => ({
|
|
||||||
...option,
|
|
||||||
voteCount: tally.get(option.id) ?? 0,
|
|
||||||
isVotedByMe: myVoteOptionId === option.id,
|
|
||||||
percentage:
|
|
||||||
totalVotes > 0
|
|
||||||
? Math.round(((tally.get(option.id) ?? 0) / totalVotes) * 100)
|
|
||||||
: 0,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user