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:
Yvv
2026-10-30 22:47:26 +01:00
co-authored by Claude Opus 5.5
parent 3541e1b1a6
commit b260596cfd
36 changed files with 4353 additions and 234 deletions
+83 -2
View File
@@ -122,6 +122,8 @@ export const en: Messages = {
'doc.action.vote': 'Voting',
'doc.action.proposals': 'Proposals',
'doc.action.propose': 'Propose a change',
'doc.basket': 'Vote in bulk',
'doc.basket.count': 'Vote in bulk · {n}',
'doc.empty': 'No article is in force. The proposals below move up as soon as they reach their threshold.',
'doc.published': 'Published to the text repository',
'doc.publish.pending': 'Publication pending',
@@ -176,6 +178,8 @@ export const en: Messages = {
'panel.proposals': 'Proposals',
'panel.propose': 'Propose',
'panel.settings': 'Settings',
'panel.basket': 'Basket',
'panel.details': 'Details',
'panel.close': 'Close panel',
'panel.inforce': 'In force',
'panel.proposal': 'Proposal',
@@ -199,7 +203,76 @@ export const en: Messages = {
'vote.entered.remove': '“{title}” has been removed from the text in force.',
'vote.entered.inertia': 'The inertia of “{title}” has changed.',
'vote.closed': 'This version is no longer open for voting.',
'vote.wotOnly': 'Here, only members of the Ğ1 web of trust vote: sign in with your Ğ1 key to vote. You can read, propose and flag a contradiction.',
'vote.fold': 'The vote on “{title}”',
'vote.mine.none': 'You have not voted on this version.',
'vote.mine.for': 'Your vote: for',
'vote.mine.against': 'Your vote: against',
'vote.mine.chain': 'recorded on the Ğ1 chain',
'vote.mine.saved': 'saved',
'vote.mine.pending': 'Waiting for the Ğ1 chain: {choice}.',
'vote.mine.refused': 'Your last vote was not counted: {reason}',
'vote.mine.expired': 'Your last vote was not seen on the Ğ1 chain in time: it does not count.',
'vote.choice.for': 'for',
'vote.choice.against': 'against',
'vote.choice.none': 'withdrawing your vote',
'vote.chain.note': 'Your vote leaves from your Ğ1 wallet: {amount} Ğ1, in a single gesture. The chain shows that you voted, never what you voted.',
'vote.chain.counts': 'Your voice counts for the Ğ1 identity of the wallet that signs, if it is a member of the web of trust.',
'vote.chain.off': 'Voting through the Ğ1 chain is not open yet: the account that receives the votes is still to be created. You can read, propose and flag a contradiction.',
'vote.members': 'Only members of this group vote here.',
'vote.details': 'Typos, translations, flags, history',
'ballot.reason.not_member': 'the wallet that signed is not that of a member of the Ğ1 web of trust.',
'ballot.reason.no_identity': 'the wallet that signed is linked to no Ğ1 identity.',
'ballot.reason.transfer': 'the transfer to the vote account is missing, or its amount is too low.',
'basket.title': 'Voting basket',
'basket.intro': 'Choose article by article, a whole section or the whole document: it all goes in a single gesture.',
'basket.doc': 'The whole document',
'basket.for': 'All for',
'basket.against': 'All against',
'basket.clear': 'Empty',
'basket.pick': 'Your choice for “{title}”',
'basket.pick.retract': 'Withdraw',
'basket.versions': 'Several versions: choose yours.',
'basket.waiting.one': '{n} article has several versions: a grouped gesture does not fill it, choose your version.',
'basket.waiting.other': '{n} articles have several versions: a grouped gesture does not fill them, choose your version.',
'basket.inforce': 'in force',
'basket.count.one': '{n} choice',
'basket.count.other': '{n} choices',
'basket.send.server': 'Vote ({n})',
'basket.send.chain': 'Sign and send ({n})',
'basket.note.server': 'Your votes count as soon as they are sent, all at once.',
'basket.note.chain': 'A single transaction of {amount} Ğ1, signed by your wallet, for all your choices.',
'basket.empty': 'Nothing in the basket yet.',
'basket.sent.one': '{n} vote saved.',
'basket.sent.other': '{n} votes saved.',
'basket.section': 'Vote on this section',
'basket.mine': 'your vote: {choice}',
'sign.title': 'Vote with your Ğ1 wallet',
'sign.summary.one': '{n} vote, in one transaction: {amount} Ğ1 to the vote account, with your sealed ballot.',
'sign.summary.other': '{n} votes, in one transaction: {amount} Ğ1 to the vote account, with your sealed ballot.',
'sign.wallet': 'With which wallet?',
'sign.account': 'With which account?',
'sign.opening': 'Your wallet is opening…',
'sign.signing': 'Confirm in your wallet.',
'sign.waiting': 'The Ğ1 chain is confirming your vote, usually in less than a minute.',
'sign.confirmed': 'Your vote counts: it is recorded on the Ğ1 chain.',
'sign.confirmed.as': 'Counted for the Ğ1 identity “{name}”.',
'sign.refused': 'Your vote was not counted: {reason}',
'sign.expired': 'Your vote was not seen on the Ğ1 chain in time: it does not count. Start again whenever you like.',
'sign.error.cancelled': 'You declined to sign: nothing was sent.',
'sign.error.funds': 'Your wallet does not have enough Ğ1: at least 1 Ğ1 must remain after the vote.',
'sign.error.node': 'The Ğ1 chain is not answering. Try again in a moment.',
'sign.error.account': 'This account is no longer in your wallet.',
'sign.error.wallet': 'Your wallet could not sign. Try again, or use Ğecko.',
'sign.none': 'No wallet is connected to this browser (duniter-connect, for example). On your phone, Ğecko does the job.',
'sign.gecko': 'Use Ğecko',
'sign.gecko.scan': 'Scan this code with Ğecko, or tap the link on your phone. Check the amount and the comment, then confirm.',
'sign.gecko.open': 'Open in Ğecko',
'sign.gecko.amount': 'Amount',
'sign.gecko.comment': 'Comment',
'sign.gecko.qr': 'Code to scan with Ğecko',
'sign.close': 'Close',
'sign.later': 'You can close: the confirmation will arrive on its own, and your vote will appear under the article.',
'sign.retry': 'Try again',
'inertia.title': 'Inertia',
'inertia.explain': 'The higher the inertia, the more votes it takes to change this text when few people vote.',
@@ -394,13 +467,21 @@ export const en: Messages = {
'error.identity_kind': 'A new link can only be for an invited person, never a Ğ1 identity.',
'error.hidden_characters': 'The text contains invisible characters (writing-direction override, zero-width space, soft hyphen, emoji colour selector…): remove them.',
'error.changed': 'The text has changed in the meantime: reload it, then try again.',
'error.wot_members_only': 'Here, only members of the Ğ1 web of trust vote.',
'error.label': 'The name is empty or too long (60 characters at most).',
'error.name': 'Your name has 2 to 60 characters, on one line: letters, figures, spaces, hyphen, apostrophe or dot.',
'error.name_taken': 'This name is already taken. Choose another one, adding an initial for instance.',
'error.team_limits': 'A team link lasts 1 to 90 days, for 1 to 500 people.',
'error.groups': 'Choose at least one group.',
'error.publication_off': 'Publication is not configured on this server.',
'error.chain_vote_off': 'Voting through the Ğ1 chain is not open yet.',
'error.chain_only': 'Here, the vote goes through the Ğ1 chain, with your wallet.',
'error.server_vote': 'Here, voting is direct, without a wallet.',
'error.too_many_ballots': 'You already have many votes waiting for the Ğ1 chain: wait for them to be confirmed.',
'error.lines': 'This vote no longer holds: reload the page, then start again.',
'error.choice': 'This choice does not exist.',
'error.author': 'This account is not a Ğ1 address.',
'error.not_votable': 'This version is no longer open for voting.',
'error.members_only': 'Only members of this group vote here.',
'error.retry': 'Try again',
'common.cancel': 'Cancel',