Files
decision/web/src/lib/vote.ts
T

51 lines
2.0 KiB
TypeScript

// What the vote under an article and the basket share: one way to send votes
// to the server (managed groups), and the news of what entered into force.
import { api, ApiError } from './api';
import type { DocState } from './doc.svelte';
import { i18n, t, type Key } from './i18n/index.svelte';
import { toasts } from './toast.svelte';
import { moduleName, sectionLabel } from './labels';
import type { Entered } from './types';
/** A line of a vote: one version, for or against, or null to withdraw one's vote. */
export interface Line {
proposalId: string;
choice: 'for' | 'against' | null;
}
/** Cents of Ğ1, as the person reads them: « 0,1 » in French. */
export function g1Amount(cents: number): string {
return i18n.number(cents / 100);
}
export function errorText(e: unknown): string {
return t(`error.${e instanceof ApiError ? e.code : 'internal'}` as Key);
}
/** Tells, one by one, the texts that just entered into force. */
export function announce(doc: DocState, entered: Entered[]) {
for (const e of entered) {
const u = doc.unit(e.unitId);
// A module is named for what it is (ADR 0009); an article, by its title.
const title = u?.kind ? moduleName(u) : (u?.inForce?.title ?? u?.code ?? '');
const said =
e.kind === 'order'
? t('vote.entered.order', { section: sectionLabel(u?.section) ?? u?.section ?? '' })
: e.kind === 'title'
? t('vote.entered.title', { title: u?.inForce?.title ?? '' })
: t(e.kind === 'remove' ? 'vote.entered.remove' : e.kind === 'inertia' ? 'vote.entered.inertia' : 'vote.entered', { title });
toasts.show(said, 'rise', 6000);
}
}
/** In a managed group: every line in one write on the server. Returns how many were written. */
export async function voteDirect(doc: DocState, lines: Line[]): Promise<number> {
const id = doc.view?.doc.id;
if (!id) return 0;
const r = await api.post<{ count: number; entered: Entered[] }>(`/docs/${id}/votes`, { lines });
await doc.reload();
announce(doc, r.entered);
return r.count;
}