Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docker-compose.override.local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
services:
keycloak:
ports:
- "8081:8080"
my-db:
ports:
- "5433:5432"
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Link, Typography } from "@mui/material";
import ElectionStateWarning from "./ElectionStateWarning";
import useElection from "../ElectionContextProvider";

// docs/help/preliminary_results.md, as published by the docs site.
const ARTICLE_URL = 'https://docs.bettervoting.com/help/preliminary_results.html';

// Shared by this notice and the line in the submit-confirm dialog, so the two
// cannot drift apart — a voter who is shown one has to be shown the other.
//
// public_results does two jobs. While voting is open it means "live tally
// visible", which is what the notice is about. Once the election closes it means
// "final results published", which carries none of the same inference risk — so
// neither surface can follow the flag alone.
export function useShowsLiveTally() {
const { election } = useElection();
return election.settings.public_results === true
&& (election.state === 'open' || election.state === 'draft');
}

export default function PreliminaryResultsNotice() {
const { t, election } = useElection();
const showsLiveTally = useShowsLiveTally();

if (!showsLiveTally) return <></>;

// Read voter_access directly rather than going through
// getVoterAuthenticationMode(), which throws on a non-canonical settings
// shape — that would take the whole ballot down with it.
const isClosedList = election.settings.voter_access === 'closed';

return <ElectionStateWarning
title='preliminary_results_notice.title'
description='preliminary_results_notice.description'
>
{isClosedList &&
<Typography component='p' sx={{ mt: 1 }}>
{t('preliminary_results_notice.closed_list')}
</Typography>
}
<Typography component='p' sx={{ mt: 1 }}>
{/* Explicit target, rather than a markdown link inside the i18n value:
the shared link renderer defaults anchors to _self, and navigating
away from the ballot discards every score the voter has entered. */}
<Link href={ARTICLE_URL} target='_blank' rel='noreferrer'>
{t('preliminary_results_notice.link_text')}
</Link>
</Typography>
</ElectionStateWarning>
}
14 changes: 14 additions & 0 deletions packages/frontend/src/components/Election/Voting/VotePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useSubstitutedTranslation } from "~/components/util";
import DraftWarning from "../DraftWarning";
import SupportBlurb from "../SupportBlurb";
import ElectionStateWarning from "../ElectionStateWarning"
import PreliminaryResultsNotice, { useShowsLiveTally } from "../PreliminaryResultsNotice"
import WriteInSection from "./WriteInSection"
import { NOTA_ID, makeWriteInCandidateId, isWriteInCandidate } from "@equal-vote/star-vote-shared/utils/makeID";

Expand Down Expand Up @@ -239,6 +240,7 @@ const VotePage = () => {
}

const {t} = useSubstitutedTranslation(election.settings.term_type)
const showsLiveTally = useShowsLiveTally()


if(pages.length == 0){
Expand All @@ -263,6 +265,10 @@ const VotePage = () => {
state="archived"
title="archived_warning.title"
description="archived_warning.description"/>
{/* Sits above BallotPageSelector so it renders once per ballot rather than
once per race, and so it also covers DraggableIRVBallotView, which
bypasses GenericBallotView entirely. */}
<PreliminaryResultsNotice/>
<BallotContext.Provider value={{
instructionsRead: pages[currentPage].instructionsRead,
setInstructionsRead: setInstructionsRead,
Expand Down Expand Up @@ -334,6 +340,14 @@ const VotePage = () => {
<DialogTitle>{t('ballot.dialog_submit_title')}</DialogTitle>
<DialogContent>
<DialogContentText>
{/* The banner above the ballot is scrollable-past, and it is greyed out
behind this dialog. This is the only surface the voter must actively
confirm, so it carries one sentence rather than nothing. */}
{showsLiveTally &&
<Typography variant="body1" sx={{ mb: 2 }}>
{t('ballot.dialog_preliminary_results')}
</Typography>
}

{!receiptEmail &&
<TextField id="receipt-email" label={t('ballot.dialog_email_placeholder')} fullWidth type="text" value={inputEmail} sx={{
Expand Down
15 changes: 15 additions & 0 deletions packages/frontend/src/i18n/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ ballot:
dialog_cancel: Cancel
dialog_submit: Submit
dialog_abstention: Abstained - No preference was expressed
dialog_preliminary_results: >
Results for this {{election}} are public while voting is open.

warnings:
skipped_rank: Do not skip rankings. Rank candidates in order to clearly show preferences. Candidates left blank are ranked last.
duplicate_rank: Do not rank multiple candidates equally. (Ranking candidates equally can void your ballot.)
Expand Down Expand Up @@ -851,6 +854,18 @@ draft_warning:
All ballots will be counted as test votes and
shall be reset prior to the final {{election}}.

preliminary_results_notice:
title: Results are public while voting is open
description: >
Anyone with a link to this {{election}} can see the results before voting closes,
and those results update as {{votes}} are submitted. In a small {{election}} — or
if only a few people vote in a short window — it can be possible to work out how
someone voted.
closed_list: >
This {{election}} uses a voter list. Administrators can see which voters have voted
and when, which alongside live results can narrow that down further.
link_text: What preliminary results reveal

temporary_access_warning:
title: Unlock more with a free bettervoting account!
description: >
Expand Down
Loading