v3 : le vote déplié sous l'article, le panier, la signature par le portefeuille Ğ1
- Le vote se déplie sous l'article, façon accordéon, l'URL pour état (?vote=) :
niveau d'eau, totaux, seuil et voix manquantes, ton vote et où il en est
(enregistré, inscrit sur la chaîne, en attente, à reconfirmer, non compté),
inertie et formule. Le reste d'une version (coquilles, traductions,
signalements, historique) passe dans l'onglet « Détails » du panneau.
- Le panier : article par article, « tout pour / tout contre » par section ou
pour tout le document, jamais pour un article à plusieurs versions ; un seul
geste (une écriture, ou une transaction).
- La signature : le portefeuille de la page (injectedWeb3), polkadot-api chargé
au moment de signer ; sans portefeuille, Ğecko par lien et QR ; la page
attend la confirmation. Un bulletin que le portefeuille n'a pas signé est
lâché aussitôt (DELETE /api/ballots/{id}).
- Pastilles d'ambiance : flèches dans le groupe, anneau de focus pointillé à
la couleur d'accent de la page (3:1 au moins), distinct du choix.
- e2e : chaîne simulée (indexeur et nœud), faux portefeuille ; C21–C24, et
C4, C5, C6, C11, C17, C19, C20, M1 mis à jour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// The basket of the reader: the choices not sent yet, kept for the tab (a reload
|
||||
// or a look at another article does not lose them), one basket per document.
|
||||
|
||||
import type { Picks } from './basket';
|
||||
import type { Choice } from './types';
|
||||
|
||||
export class BasketState {
|
||||
picks = $state<Picks>({});
|
||||
#key = '';
|
||||
|
||||
/** Takes back the basket of this document, keeping only the versions still open to vote. */
|
||||
load(docId: string, known: Set<string>) {
|
||||
const key = `ld:basket:${docId}`;
|
||||
if (key === this.#key) {
|
||||
this.#keep(known);
|
||||
return;
|
||||
}
|
||||
this.#key = key;
|
||||
let saved: Picks = {};
|
||||
try {
|
||||
saved = JSON.parse(sessionStorage.getItem(key) ?? '{}') as Picks;
|
||||
} catch {
|
||||
saved = {};
|
||||
}
|
||||
this.picks = {};
|
||||
this.set(saved);
|
||||
this.#keep(known);
|
||||
}
|
||||
|
||||
#keep(known: Set<string>) {
|
||||
const kept = Object.fromEntries(Object.entries(this.picks).filter(([id]) => known.has(id)));
|
||||
if (Object.keys(kept).length !== Object.keys(this.picks).length) this.set(kept);
|
||||
}
|
||||
|
||||
set(picks: Picks) {
|
||||
const clean: Picks = {};
|
||||
for (const [id, c] of Object.entries(picks)) if (c === 'for' || c === 'against' || c === 'none') clean[id] = c;
|
||||
this.picks = clean;
|
||||
try {
|
||||
sessionStorage.setItem(this.#key, JSON.stringify(clean));
|
||||
} catch {
|
||||
/* private mode: the basket lives as long as the page */
|
||||
}
|
||||
}
|
||||
|
||||
/** Picks a choice; the same choice again takes it out of the basket. */
|
||||
pick(id: string, choice: Choice) {
|
||||
const next = { ...this.picks };
|
||||
if (next[id] === choice) delete next[id];
|
||||
else next[id] = choice;
|
||||
this.set(next);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.set({});
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return Object.keys(this.picks).length;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user