-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a7c1033
commit c951b68
Showing
6 changed files
with
196 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,7 +35,7 @@ jobs: | |
run: yarn build | ||
env: | ||
VOCDONI_ENVIRONMENT: stg | ||
PROCESS_IDS: '["4ae20a8eb4caa52f5588f7bb9f3c6d6b7cf003a5b03f4589edea100000000290","4ae20a8eb4caa52f5588f7bb9f3c6d6b7cf003a5b03f4589edea10000000027d"]' | ||
PROCESS_IDS: '["4ae20a8eb4ca8bd340fc16a71ae591b88418c42e799705b9807302000000000f"]' | ||
|
||
- name: Deploy to Netlify | ||
uses: nwtgck/[email protected] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import { Box, Progress } from '@chakra-ui/react' | ||
import { ConnectButton } from '@rainbow-me/rainbowkit' | ||
import { ElectionQuestions, SpreadsheetAccess } from '@vocdoni/chakra-components' | ||
import { ElectionProvider, useElection } from '@vocdoni/react-providers' | ||
import { ArchivedElection, InvalidElection, PublishedElection, VocdoniSDKClient } from '@vocdoni/sdk' | ||
import { useEffect } from 'react' | ||
import { Trans } from 'react-i18next' | ||
import { VoteButton } from './Aside' | ||
import { ChainedProvider, useChainedProcesses } from './ChainedContext' | ||
|
||
import '@rainbow-me/rainbowkit/styles.css' | ||
import { ConfirmVoteModal } from './ConfirmVoteModal' | ||
|
||
type ChainedProcessesProps = { | ||
root?: PublishedElection | ArchivedElection | InvalidElection | ||
} | ||
|
||
const ChainedProcessesInner = () => { | ||
const { election, voted, setClient } = useElection() | ||
const { processes, client, current, setProcess, setCurrent, root } = useChainedProcesses() | ||
|
||
// ensure the client is set to the root one | ||
useEffect(() => { | ||
setClient(client) | ||
}, [client, election]) | ||
|
||
// fetch current process and process flow logic | ||
useEffect(() => { | ||
if (!current || processes[current] instanceof InvalidElection || !voted) return | ||
|
||
const currentElection = processes[current] | ||
const meta = currentElection.get('multiprocess') | ||
if (!meta || (!meta.root && !meta.conditions && !meta.default)) return | ||
;(async () => { | ||
// fetch votes info | ||
const next = await getNextProcessInFlow(client, voted, meta) | ||
|
||
if (typeof processes[next] === 'undefined') { | ||
const election = await client.fetchElection(next) | ||
setProcess(next, election) | ||
setCurrent(next) | ||
} | ||
})() | ||
}, [processes, current, voted, client]) | ||
|
||
if (!current || !processes[current]) { | ||
return <Progress w='full' size='xs' isIndeterminate /> | ||
} | ||
|
||
if (processes[current] instanceof InvalidElection) { | ||
return <Trans i18nKey='error.election_is_invalid'>Invalid election</Trans> | ||
} | ||
|
||
return ( | ||
<Box className='md-sizes' mb='100px' pt='25px'> | ||
<ElectionQuestions | ||
confirmContents={(election, answers) => <ConfirmVoteModal election={election} answers={answers} />} | ||
/> | ||
<Box position='sticky' bottom={0} left={0} pb={1} pt={1} display={{ base: 'none', lg2: 'block' }}> | ||
<VoteButton /> | ||
</Box> | ||
</Box> | ||
) | ||
} | ||
|
||
// Note this logic has been thought for single-choice single-question processes. | ||
// The brainfuck required to make it work for other implementations requires a | ||
// thoroughful assessment of the entire feature. | ||
// Also, processes cannot be secret... otherwise we cannot get the vote packages | ||
// and know what the users voted | ||
const getNextProcessInFlow = async (client: VocdoniSDKClient, voted: string, meta: any) => { | ||
if (!meta.conditions) { | ||
return meta.default | ||
} | ||
|
||
const ivote = await client.voteService.info(voted) | ||
|
||
if (!(ivote.package as any).votes) { | ||
throw new Error('vote package is secret, cannot continue with the flow') | ||
} | ||
|
||
const [choice] = (ivote.package as any).votes | ||
|
||
// loop over conditions finding the selected choice | ||
for (const condition of meta.conditions) { | ||
if (choice === condition.choice && condition.question === 0) { | ||
return condition.goto | ||
} | ||
} | ||
|
||
return meta.default | ||
} | ||
|
||
export const ChainedProcesses = ({ root }: ChainedProcessesProps) => { | ||
const { client } = useElection() | ||
if (!root) { | ||
return <Progress w='full' size='xs' isIndeterminate /> | ||
} | ||
|
||
return ( | ||
<ChainedProvider root={root as PublishedElection} client={client}> | ||
<ChainedProcessesWrapper /> | ||
</ChainedProvider> | ||
) | ||
} | ||
|
||
const ChainedProcessesWrapper = () => { | ||
// note election context refers to the root election here, ALWAYS | ||
const { connected, election } = useElection() | ||
const { processes, current, root, setCurrent } = useChainedProcesses() | ||
|
||
// set current to root if login out | ||
useEffect(() => { | ||
if (connected) return | ||
|
||
setCurrent(root.id) | ||
}, [connected]) | ||
|
||
if (!current || !processes[current] || !election || election instanceof InvalidElection) { | ||
return <Progress w='full' size='xs' isIndeterminate /> | ||
} | ||
|
||
return ( | ||
<> | ||
<ElectionProvider key={current} election={processes[current]} ConnectButton={ConnectButton} fetchCensus> | ||
<ChainedProcessesInner /> | ||
</ElectionProvider> | ||
{!connected && election.get('census.type') === 'spreadsheet' && <SpreadsheetAccess />} | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { PublishedElection, VocdoniSDKClient } from '@vocdoni/sdk' | ||
import { createContext, FC, PropsWithChildren, useContext, useState } from 'react' | ||
|
||
type Processes = { | ||
[key: string]: PublishedElection | ||
} | ||
|
||
type ChainedContextState = { | ||
processes: Processes | ||
current: string | null | ||
client: VocdoniSDKClient | ||
root: PublishedElection | ||
setProcess: (id: string, process: PublishedElection) => void | ||
setCurrent: (id: string | null) => void | ||
} | ||
|
||
type ChainedProviderProps = { | ||
client: VocdoniSDKClient | ||
root: PublishedElection | ||
} | ||
|
||
const ChainedContext = createContext<ChainedContextState | undefined>(undefined) | ||
|
||
export const ChainedProvider: FC<PropsWithChildren<ChainedProviderProps>> = ({ children, root, client }) => { | ||
const [processes, setProcesses] = useState<Processes>(root ? { [root.id]: root } : {}) | ||
const [current, setCurrent] = useState<string | null>(root ? root.id : null) | ||
|
||
const setProcess = (id: string, process: PublishedElection) => { | ||
setProcesses((prev) => ({ | ||
...prev, | ||
[id]: process, | ||
})) | ||
} | ||
|
||
return ( | ||
<ChainedContext.Provider value={{ processes, client, current, root, setProcess, setCurrent }}> | ||
{children} | ||
</ChainedContext.Provider> | ||
) | ||
} | ||
|
||
export const useChainedProcesses = () => { | ||
const context = useContext(ChainedContext) | ||
if (!context) { | ||
throw new Error('useProcesses must be used within a ProcessesProvider') | ||
} | ||
return context | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters