diff --git a/packages/chakra-components/package.json b/packages/chakra-components/package.json
index f76732cc..203b6885 100644
--- a/packages/chakra-components/package.json
+++ b/packages/chakra-components/package.json
@@ -38,12 +38,13 @@
"@chakra-ui/progress": "^2.1.6",
"@chakra-ui/radio": "^2.0.22",
"@chakra-ui/react-use-disclosure": "^2.1.0",
+ "@chakra-ui/spinner": "^2.1.0",
"@chakra-ui/system": "2.5.5",
"@chakra-ui/table": "^2.0.17",
"@chakra-ui/tag": "^3.0.0",
"@chakra-ui/theme": "^3.0.0",
"@chakra-ui/toast": "^6.1.1",
- "@vocdoni/sdk": "~0.5.1",
+ "@vocdoni/sdk": "~0.6.0",
"react": ">= 16.8.0",
"react-dom": ">= 16.8.0",
"react-markdown": ">= 8.0.0",
@@ -60,6 +61,7 @@
"@chakra-ui/progress": "^2.1.6",
"@chakra-ui/radio": "^2.0.22",
"@chakra-ui/react-use-disclosure": "^2.1.0",
+ "@chakra-ui/spinner": "^2.1.0",
"@chakra-ui/system": "2.5.5",
"@chakra-ui/table": "^2.0.17",
"@chakra-ui/tag": "^3.0.0",
@@ -69,7 +71,7 @@
"@ethersproject/wallet": "^5.7.0",
"@types/react": "^18.0.30",
"@types/react-dom": "^18.0.11",
- "@vocdoni/sdk": "~0.5.1",
+ "@vocdoni/sdk": "~0.6.0",
"clean-package": "^2.2.0",
"date-fns": "^2.29.3",
"eslint": "^8.42.0",
diff --git a/packages/chakra-components/src/components/Election/Questions.tsx b/packages/chakra-components/src/components/Election/Questions.tsx
index bdcc5d76..8890eb0e 100644
--- a/packages/chakra-components/src/components/Election/Questions.tsx
+++ b/packages/chakra-components/src/components/Election/Questions.tsx
@@ -1,6 +1,8 @@
import { Alert, AlertDescription, AlertIcon, AlertTitle } from '@chakra-ui/alert'
+import { Button } from '@chakra-ui/button'
import { FormControl, FormErrorMessage } from '@chakra-ui/form-control'
import { Box, Link, Stack, Text } from '@chakra-ui/layout'
+import { ModalBody, ModalCloseButton, ModalFooter, ModalHeader } from '@chakra-ui/modal'
import { Radio, RadioGroup } from '@chakra-ui/radio'
import { chakra, ChakraProps, omitThemingProps, useMultiStyleConfig } from '@chakra-ui/system'
import { Wallet } from '@ethersproject/wallet'
@@ -91,23 +93,39 @@ export type QuestionsConfirmationProps = {
}
export const QuestionsConfirmation = ({ answers, questions, ...rest }: QuestionsConfirmationProps) => {
+ const mstyles = useMultiStyleConfig('ConfirmModal')
const styles = useMultiStyleConfig('QuestionsConfirmation', rest)
+ const { cancel, proceed } = useConfirm()
const props = omitThemingProps(rest)
const { localize } = useClient()
return (
-
- {localize('vote.confirm')}
- {questions.map((q, k) => {
- const choice = q.choices.find((v) => v.value === parseInt(answers[k.toString()], 10))
- return (
-
- {q.title.default}
- {choice?.title.default}
-
- )
- })}
-
+ <>
+ {localize('confirm.title')}
+
+
+
+ {localize('vote.confirm')}
+ {questions.map((q, k) => {
+ const choice = q.choices.find((v) => v.value === parseInt(answers[k.toString()], 10))
+ return (
+
+ {q.title.default}
+ {choice?.title.default}
+
+ )
+ })}
+
+
+
+
+
+
+ >
)
}
diff --git a/packages/chakra-components/src/components/Election/SpreadsheetAccess.tsx b/packages/chakra-components/src/components/Election/SpreadsheetAccess.tsx
index 31135301..2d3c04e4 100644
--- a/packages/chakra-components/src/components/Election/SpreadsheetAccess.tsx
+++ b/packages/chakra-components/src/components/Election/SpreadsheetAccess.tsx
@@ -24,8 +24,8 @@ export const SpreadsheetAccess = (rest: ChakraProps) => {
const { connected, clearClient } = useElection()
const [loading, setLoading] = useState(false)
const toast = useToast()
- const { env, setSikPassword, setSikSignature } = useClient()
- const { election, setClient, localize, fetchCensus } = useElection()
+ const { env, client: cl } = useClient()
+ const { election, setClient, localize, fetchCensus, sikPassword, sikSignature } = useElection()
const fields: string[] = dotobject(election, 'meta.census.fields')
const {
register,
@@ -45,7 +45,9 @@ export const SpreadsheetAccess = (rest: ChakraProps) => {
}
// create wallet and client
- const wallet = walletFromRow(election!.organizationId, Object.values(vals))
+ const hid = await cl.electionService.getNumericElectionId(election!.id)
+ const salt = await cl.electionService.getElectionSalt(election!.organizationId, hid)
+ const wallet = walletFromRow(salt, Object.values(vals))
const client = new VocdoniSDKClient({
env,
wallet,
@@ -63,8 +65,8 @@ export const SpreadsheetAccess = (rest: ChakraProps) => {
fetchCensus()
// store SIK requirements to client on anon elections
if (election?.electionType.anonymous && sikp) {
- setSikPassword(sikp)
- setSikSignature(await client.anonymousService.signSIKPayload(wallet))
+ sikPassword(sikp)
+ sikSignature(await client.anonymousService.signSIKPayload(wallet))
}
// in case of success, set current client
setClient(client)
diff --git a/packages/chakra-components/src/components/Election/VoteButton.tsx b/packages/chakra-components/src/components/Election/VoteButton.tsx
index db6128c0..90f216fb 100644
--- a/packages/chakra-components/src/components/Election/VoteButton.tsx
+++ b/packages/chakra-components/src/components/Election/VoteButton.tsx
@@ -2,14 +2,11 @@ import { Button, ButtonProps } from '@chakra-ui/button'
import { Signer } from '@ethersproject/abstract-signer'
import { useClient, useElection } from '@vocdoni/react-providers'
import { ArchivedElection, ElectionStatus, InvalidElection } from '@vocdoni/sdk'
+import { useState } from 'react'
import { SpreadsheetAccess } from './SpreadsheetAccess'
export const VoteButton = (props: ButtonProps) => {
- const {
- connected,
- sik: { signature },
- setSikSignature,
- } = useClient()
+ const { connected } = useClient()
const {
client,
loading: { voting },
@@ -19,7 +16,10 @@ export const VoteButton = (props: ButtonProps) => {
election,
voted,
localize,
+ sik: { signature },
+ sikSignature,
} = useElection()
+ const [loading, setLoading] = useState(false)
const isDisabled = !client.wallet || !isAbleToVote || election?.status !== ElectionStatus.ONGOING
if (election instanceof InvalidElection || election instanceof ArchivedElection) return null
@@ -42,10 +42,15 @@ export const VoteButton = (props: ButtonProps) => {
}
if (connected && election?.electionType.anonymous && !signature) {
+ button.isLoading = loading
button.type = 'button'
- button.children = localize('vote.identify')
+ button.children = localize('vote.sign')
button.onClick = async () => {
- setSikSignature(await client.anonymousService.signSIKPayload(client.wallet as Signer))
+ setLoading(true)
+ try {
+ sikSignature(await client.anonymousService.signSIKPayload(client.wallet as Signer))
+ } catch (e) {}
+ setLoading(false)
}
}
diff --git a/packages/chakra-components/src/components/layout/ConfirmModal/ConfirmModal.tsx b/packages/chakra-components/src/components/layout/ConfirmModal/ConfirmModal.tsx
index 7cb9b72a..a7528241 100644
--- a/packages/chakra-components/src/components/layout/ConfirmModal/ConfirmModal.tsx
+++ b/packages/chakra-components/src/components/layout/ConfirmModal/ConfirmModal.tsx
@@ -1,38 +1,15 @@
-import { Button } from '@chakra-ui/button'
-import {
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
-} from '@chakra-ui/modal'
+import { Modal, ModalContent, ModalOverlay } from '@chakra-ui/modal'
import { useMultiStyleConfig } from '@chakra-ui/system'
-import { useClient } from '@vocdoni/react-providers'
import { useConfirm } from './ConfirmProvider'
export const ConfirmModal = () => {
const styles = useMultiStyleConfig('ConfirmModal')
- const { prompt, isOpen, proceed, cancel } = useConfirm()
- const { localize } = useClient()
+ const { prompt, isOpen, cancel } = useConfirm()
return (
-
- {localize('confirm.title')}
-
- {prompt}
-
-
-
-
-
+ {prompt}
)
}
diff --git a/packages/chakra-components/src/i18n/locales.ts b/packages/chakra-components/src/i18n/locales.ts
index 0f35d20b..1ecbdfc3 100644
--- a/packages/chakra-components/src/i18n/locales.ts
+++ b/packages/chakra-components/src/i18n/locales.ts
@@ -22,14 +22,6 @@ export const locales = {
cancel: 'Cancel',
confirm: 'Confirm',
},
- // questions and vote button
- vote: {
- button_update: 'Re-submit vote',
- button: 'Vote',
- confirm: 'Please confirm your choices:',
- voted_description: 'Your vote id is {{ id }}. You can use it to verify your vote.',
- voted_title: 'Your vote was successfully cast!',
- },
empty: 'Apparently this process has no questions 🤔',
errors: {
wrong_data_title: 'Wrong data',
@@ -66,6 +58,15 @@ export const locales = {
required: 'This field is required',
min_length: 'This field must be at least {{ min }} characters long',
},
+ // questions and vote button
+ vote: {
+ button_update: 'Re-submit vote',
+ button: 'Vote',
+ confirm: 'Please confirm your choices:',
+ sign: 'Sign first',
+ voted_description: 'Your vote id is {{ id }}. You can use it to verify your vote.',
+ voted_title: 'Your vote was successfully cast!',
+ },
}
export type Locale = RecursivePartial
diff --git a/packages/chakra-components/src/theme/confirm.ts b/packages/chakra-components/src/theme/confirm.ts
index 0232f5ef..5d863103 100644
--- a/packages/chakra-components/src/theme/confirm.ts
+++ b/packages/chakra-components/src/theme/confirm.ts
@@ -13,6 +13,8 @@ export const confirmAnatomy = [
'close',
]
+export const signModalAnatomy = ['body', 'description', 'footer', 'button']
+
const { defineMultiStyleConfig, definePartsStyle } = createMultiStyleConfigHelpers(confirmAnatomy)
const baseStyle = definePartsStyle({
diff --git a/packages/react-providers/package.json b/packages/react-providers/package.json
index 6675bb9c..29fc5b94 100644
--- a/packages/react-providers/package.json
+++ b/packages/react-providers/package.json
@@ -24,7 +24,7 @@
},
"main": "src/index.ts",
"peerDependencies": {
- "@vocdoni/sdk": "~0.5.3",
+ "@vocdoni/sdk": "~0.6.0",
"react": ">= 16.8.0"
},
"devDependencies": {
@@ -32,7 +32,7 @@
"@ethersproject/wallet": "^5.7.0",
"@types/latinize": "^0.2.15",
"@types/react": "^18.0.30",
- "@vocdoni/sdk": "^0.5.3",
+ "@vocdoni/sdk": "~0.6.0",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-react-app": "^7.0.1",
diff --git a/packages/react-providers/src/client/use-client-provider.ts b/packages/react-providers/src/client/use-client-provider.ts
index 4ca25511..38bd0aaf 100644
--- a/packages/react-providers/src/client/use-client-provider.ts
+++ b/packages/react-providers/src/client/use-client-provider.ts
@@ -1,5 +1,5 @@
import { Wallet } from '@ethersproject/wallet'
-import { Account, AccountData, EnvOptions, VocdoniSDKClient } from '@vocdoni/sdk'
+import { Account, EnvOptions, VocdoniSDKClient } from '@vocdoni/sdk'
import { useEffect } from 'react'
import { useLocalize } from '../i18n/localize'
import { ClientReducerProps, newVocdoniSDKClient, useClientReducer } from './use-client-reducer'
@@ -105,8 +105,6 @@ export const useClientProvider = ({ client: c, env: e, signer: s, options: o }:
clear: actions.clear,
setClient: actions.setClient,
setSigner: actions.setSigner,
- setSikPassword: actions.setSikPassword,
- setSikSignature: actions.setSikSignature,
createAccount,
fetchAccount,
generateSigner,
diff --git a/packages/react-providers/src/client/use-client-reducer.ts b/packages/react-providers/src/client/use-client-reducer.ts
index c6428068..08d21963 100644
--- a/packages/react-providers/src/client/use-client-reducer.ts
+++ b/packages/react-providers/src/client/use-client-reducer.ts
@@ -30,15 +30,12 @@ export const ClientClear = 'client:clear'
export const ClientEnvSet = 'client:env:set'
export const ClientSet = 'client:set'
export const ClientSignerSet = 'client:signer:set'
-export const ClientSikPasswordSet = 'client:sikpassword:set'
-export const ClientSikSignatureSet = 'client:siksignature:set'
export type ClientAccountErrorPayload = ErrorPayload
export type ClientAccountSetPayload = AccountData
export type ClientEnvSetPayload = ClientEnv
export type ClientSetPayload = VocdoniSDKClient
export type ClientSignerSetPayload = Signer | Wallet
-export type ClientSikPayload = string | null
export type ClientActionPayload =
| ClientAccountErrorPayload
@@ -46,7 +43,6 @@ export type ClientActionPayload =
| ClientEnvSetPayload
| ClientSetPayload
| ClientSignerSetPayload
- | ClientSikPayload
export type ClientActionType =
| typeof ClientAccountCreate
@@ -60,8 +56,6 @@ export type ClientActionType =
| typeof ClientEnvSet
| typeof ClientSet
| typeof ClientSignerSet
- | typeof ClientSikPasswordSet
- | typeof ClientSikSignatureSet
export interface ClientAction {
type: ClientActionType
@@ -104,10 +98,6 @@ export interface ClientState {
api_url?: string
faucet_url?: string
}
- sik: {
- password: string | null
- signature: string | null
- }
}
export const clientStateEmpty = (
@@ -139,10 +129,6 @@ export const clientStateEmpty = (
fetch: null,
},
options,
- sik: {
- password: null,
- signature: null,
- },
})
const clientReducer: Reducer = (state: ClientState, action: ClientAction) => {
@@ -212,10 +198,6 @@ const clientReducer: Reducer = (state: ClientState, a
create: null,
fetch: null,
},
- sik: {
- password: null,
- signature: null,
- },
}
}
case ClientClear: {
@@ -254,26 +236,6 @@ const clientReducer: Reducer = (state: ClientState, a
connected: !!client.wallet,
}
}
- case ClientSikPasswordSet: {
- const password = action.payload as ClientSikPayload
- return {
- ...state,
- sik: {
- ...state.sik,
- password,
- },
- }
- }
- case ClientSikSignatureSet: {
- const signature = action.payload as ClientSikPayload
- return {
- ...state,
- sik: {
- ...state.sik,
- signature,
- },
- }
- }
default:
return state
}
@@ -310,8 +272,6 @@ export const useClientReducer = ({ env, client, signer, options }: ClientReducer
const setClient = (client: VocdoniSDKClient) => dispatch({ type: ClientSet, payload: client })
const setEnv = (env: ClientEnvSetPayload) => dispatch({ type: ClientEnvSet, payload: env })
const setSigner = (signer: Wallet | Signer) => dispatch({ type: ClientSignerSet, payload: signer })
- const setSikPassword = (password: string | null) => dispatch({ type: ClientSikPasswordSet, payload: password })
- const setSikSignature = (signature: string | null) => dispatch({ type: ClientSikSignatureSet, payload: signature })
return {
state,
@@ -327,8 +287,6 @@ export const useClientReducer = ({ env, client, signer, options }: ClientReducer
setClient,
setEnv,
setSigner,
- setSikPassword,
- setSikSignature,
},
}
}
diff --git a/packages/react-providers/src/election/use-election-provider.ts b/packages/react-providers/src/election/use-election-provider.ts
index 1768dc0d..5122b89c 100644
--- a/packages/react-providers/src/election/use-election-provider.ts
+++ b/packages/react-providers/src/election/use-election-provider.ts
@@ -30,13 +30,16 @@ export const useElectionProvider = ({
autoUpdate,
...rest
}: ElectionProviderProps) => {
+ const { client: c, localize } = useClient()
+ const { state, actions } = useElectionReducer(c, data)
const {
- client: c,
- localize,
+ client,
+ csp,
+ election,
+ loading,
+ loaded,
sik: { password, signature },
- } = useClient()
- const { state, actions } = useElectionReducer(c, data)
- const { client, csp, election, loading, loaded } = state
+ } = state
const fetchElection = useCallback(
async (id: string) => {
@@ -127,7 +130,7 @@ export const useElectionProvider = ({
const address = await client.wallet?.getAddress()
// The condition is just negated so we can return the code execution.
// A less mental option is to not negate the entire condition and add
- // the try/catch code inside the if
+ // the `await censusFetch()` execution in there
if (
!(
!loaded.census ||
@@ -215,8 +218,8 @@ export const useElectionProvider = ({
try {
let vote: Vote | AnonymousVote = new Vote(values)
- if (election.electionType.anonymous && password) {
- vote = new AnonymousVote(values, password)
+ if (election.electionType.anonymous && signature) {
+ vote = new AnonymousVote(values, signature, password)
}
actions.setVote(vote)
@@ -380,5 +383,7 @@ export const useElectionProvider = ({
fetchCensus: censusFetch,
clearClient: actions.clearClient,
setClient: actions.setClient,
+ sikPassword: actions.sikPassword,
+ sikSignature: actions.sikSignature,
}
}
diff --git a/packages/react-providers/src/election/use-election-reducer.ts b/packages/react-providers/src/election/use-election-reducer.ts
index deec0ac0..a2b7e05b 100644
--- a/packages/react-providers/src/election/use-election-reducer.ts
+++ b/packages/react-providers/src/election/use-election-reducer.ts
@@ -21,6 +21,8 @@ export const ElectionVoteSet = 'election:vote:set'
export const ElectionVotesLeft = 'election:votes_left'
export const ElectionVoting = 'election:voting'
export const ElectionVotingError = 'election:voting:error'
+export const SikPasswordSet = 'sik:password:set'
+export const SikSignatureSet = 'sik:signature:set'
export type ElectionActionType =
| typeof CensusClear
@@ -39,6 +41,8 @@ export type ElectionActionType =
| typeof ElectionVotesLeft
| typeof ElectionVoting
| typeof ElectionVotingError
+ | typeof SikPasswordSet
+ | typeof SikSignatureSet
export type CensusErrorPayload = ErrorPayload
export type CensusIsAbleToVotePayload = undefined | boolean
@@ -54,6 +58,7 @@ export type ElectionVotedPayload = string | null
export type ElectionVoteSetPayload = Vote
export type ElectionVotesLeftPayload = number
export type ElectionVotingErrorPayload = ErrorPayload
+export type SikPayload = string | undefined
export type ElectionActionPayload =
| CensusErrorPayload
@@ -66,6 +71,7 @@ export type ElectionActionPayload =
| ElectionVoteSetPayload
| ElectionVotesLeftPayload
| ElectionVotingErrorPayload
+ | SikPayload
export interface ElectionAction {
type: ElectionActionType
@@ -103,6 +109,10 @@ export interface ElectionReducerState {
token: string | undefined
authToken: string | undefined
}
+ sik: {
+ password: string | undefined
+ signature: string | undefined
+ }
}
export const electionStateEmpty = ({
@@ -142,6 +152,10 @@ export const electionStateEmpty = ({
token: undefined,
authToken: undefined,
},
+ sik: {
+ password: undefined,
+ signature: undefined,
+ },
})
const isAbleToVote = (state: ElectionReducerState, payload?: boolean) =>
@@ -372,14 +386,34 @@ const electionReducer: Reducer = (
},
}
}
+ case SikPasswordSet: {
+ const password = action.payload as SikPayload
+ return {
+ ...state,
+ sik: {
+ ...state.sik,
+ password,
+ },
+ }
+ }
+ case SikSignatureSet: {
+ const signature = action.payload as SikPayload
+ return {
+ ...state,
+ sik: {
+ ...state.sik,
+ signature,
+ },
+ }
+ }
+ default:
+ return state
}
-
- return state
}
export const useElectionReducer = (client: VocdoniSDKClient, election?: PublishedElection) => {
const initial = electionStateEmpty({ client, election })
- const { connected, setSikPassword, setSikSignature } = useClient()
+ const { connected } = useClient()
const [state, dispatch] = useReducer(electionReducer, {
...initial,
election,
@@ -388,6 +422,8 @@ export const useElectionReducer = (client: VocdoniSDKClient, election?: Publishe
const clear = () => dispatch({ type: CensusClear })
const setClient = (client: VocdoniSDKClient) => dispatch({ type: ElectionClientSet, payload: client })
const set = (election: PublishedElection) => dispatch({ type: ElectionSet, payload: election })
+ const sikPassword = (password: SikPayload) => dispatch({ type: SikPasswordSet, payload: password })
+ const sikSignature = (signature: SikPayload) => dispatch({ type: SikSignatureSet, payload: signature })
// update local client in case it's updated
useEffect(() => {
@@ -439,10 +475,12 @@ export const useElectionReducer = (client: VocdoniSDKClient, election?: Publishe
clear,
set,
setClient,
+ sikPassword,
+ sikSignature,
clearClient: () => {
setClient(client)
- setSikPassword(null)
- setSikSignature(null)
+ sikPassword(undefined)
+ sikSignature(undefined)
clear()
},
load: (id?: string) => dispatch({ type: ElectionLoad, payload: id }),
diff --git a/packages/react-providers/src/utils.ts b/packages/react-providers/src/utils.ts
index 0826cec6..862d34a0 100644
--- a/packages/react-providers/src/utils.ts
+++ b/packages/react-providers/src/utils.ts
@@ -1,4 +1,4 @@
-import { VocdoniSDKClient, ensure0x } from '@vocdoni/sdk'
+import { ensure0x, VocdoniSDKClient } from '@vocdoni/sdk'
import latinize from 'latinize'
/**
@@ -50,8 +50,15 @@ export const normalizeText = (text?: string): string => {
return latinize(result)
}
-export const walletFromRow = (organization: string, row: string[]) => {
+/**
+ * Generates a Wallet from a given row of data and a salt. The row of data should be an array of strings
+ *
+ * @param {string} salt A random string to be used as salt, the more random the better
+ * @param {string} row The row to be used to generate the wallet
+ * @returns {Wallet}
+ */
+export const walletFromRow = (salt: string, row: string[]) => {
const normalized = row.map(normalizeText)
- normalized.push(organization)
+ normalized.push(salt)
return VocdoniSDKClient.generateWalletFromData(normalized)
}
diff --git a/templates/chakra/package.json b/templates/chakra/package.json
index 18de7e3a..5f83c9a6 100644
--- a/templates/chakra/package.json
+++ b/templates/chakra/package.json
@@ -16,7 +16,7 @@
"@emotion/styled": "^11.10.6",
"@rainbow-me/rainbowkit": "^1.2.0",
"@vocdoni/chakra-components": "*",
- "@vocdoni/sdk": "~0.5.1",
+ "@vocdoni/sdk": "~0.6.0",
"date-fns": "^2.29.3",
"ethers": "^5.7.2",
"formik": "^2.2.9",