Skip to content

Commit

Permalink
Move most actions logic to react-providers
Browse files Browse the repository at this point in the history
closes #109
  • Loading branch information
elboletaire committed Oct 23, 2023
1 parent 7c88865 commit 54f7555
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 67 deletions.
6 changes: 5 additions & 1 deletion packages/chakra-components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ const App = () => {

> Beware that you'll also see a `ClientProvider` also from
> `@vocdoni/react-providers`. You should always be using the one included with
> chakra components in order to have all the features they provide.
> `@vocdoni/chakra-components` in order to have all the features it provides.
>
> Note this also happens with other components and providers. If you are using
> `@vocdoni/chakra-components`, you should always prioritize its exports over
> the ones from `@vocdoni/react-providers`.
Note `env` can be any of the [SDK available environments][sdk environments],
either in string format, or using the SDK `EnvOptions` enum.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ButtonGroup, IconButton } from '@chakra-ui/button'
import { ChakraProps, chakra, useMultiStyleConfig } from '@chakra-ui/system'
import { chakra, ChakraProps, useMultiStyleConfig } from '@chakra-ui/system'
import { useClient, useElection } from '@vocdoni/react-providers'
import { areEqualHexStrings } from '@vocdoni/sdk'
import { FaPause, FaPlay, FaStop } from 'react-icons/fa'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
import { PropsWithChildren, createContext, useContext } from 'react'
import { useActionsProvider } from './use-actions-provider'

export type ActionsState = ReturnType<typeof useActionsProvider>

export const ActionsContext = createContext<ActionsState | undefined>(undefined)

export const useActions = () => {
const ctxt = useContext(ActionsContext)
if (!ctxt) {
throw new Error(
'useActions returned `undefined`, maybe you forgot to wrap the component within <ActionsProvider />?'
)
}

return ctxt
import { ActionsProvider as RActionsProvider } from '@vocdoni/react-providers'
import { PropsWithChildren, ReactElement } from 'react'
import { useActionsToast } from './use-actions-toast'

export const ActionsProvider = (props: PropsWithChildren) => {
return (
<RActionsProvider>
<ChakraInternalActionsProvider {...props} />
</RActionsProvider>
)
}

export type ActionsProviderComponentProps = PropsWithChildren

export const ActionsProvider = ({ children }: ActionsProviderComponentProps) => {
const value = useActionsProvider()
// We need to define an "internal" component in order to be able to use the
// hooks, otherwise they wouldn't have access to the ActionsProvider context
const ChakraInternalActionsProvider = ({ children }: PropsWithChildren) => {
useActionsToast()

return <ActionsContext.Provider value={value}>{children}</ActionsContext.Provider>
return children as ReactElement
}

ActionsProvider.displayName = 'ActionsProvider'
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { IconButtonProps } from '@chakra-ui/button'
import { chakra, forwardRef } from '@chakra-ui/system'
import { useClient, useElection } from '@vocdoni/react-providers'
import { ElectionStatus, areEqualHexStrings } from '@vocdoni/sdk'
import { useActions } from './ActionsProvider'
import { useActions, useClient, useElection } from '@vocdoni/react-providers'
import { areEqualHexStrings, ElectionStatus } from '@vocdoni/sdk'

export const ActionCancel = forwardRef<IconButtonProps, 'button'>((props, ref) => {
const { account, localize } = useClient()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { IconButtonProps } from '@chakra-ui/button'
import { chakra, forwardRef } from '@chakra-ui/system'
import { useClient, useElection } from '@vocdoni/react-providers'
import { ElectionStatus, areEqualHexStrings } from '@vocdoni/sdk'
import { useActions } from './ActionsProvider'
import { useActions, useClient, useElection } from '@vocdoni/react-providers'
import { areEqualHexStrings, ElectionStatus } from '@vocdoni/sdk'

export const ActionContinue = forwardRef<IconButtonProps, 'button'>((props, ref) => {
const { account, localize } = useClient()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { IconButtonProps } from '@chakra-ui/button'
import { chakra, forwardRef } from '@chakra-ui/system'
import { useClient, useElection } from '@vocdoni/react-providers'
import { ElectionStatus, areEqualHexStrings } from '@vocdoni/sdk'
import { useActions } from './ActionsProvider'
import { useActions, useClient, useElection } from '@vocdoni/react-providers'
import { areEqualHexStrings, ElectionStatus } from '@vocdoni/sdk'

export const ActionEnd = forwardRef<IconButtonProps, 'button'>((props, ref) => {
const { account, localize } = useClient()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { IconButtonProps } from '@chakra-ui/button'
import { chakra, forwardRef } from '@chakra-ui/system'
import { useClient, useElection } from '@vocdoni/react-providers'
import { ElectionStatus, areEqualHexStrings } from '@vocdoni/sdk'
import { useActions } from './ActionsProvider'
import { useActions, useClient, useElection } from '@vocdoni/react-providers'
import { areEqualHexStrings, ElectionStatus } from '@vocdoni/sdk'

export const ActionPause = forwardRef<IconButtonProps, 'button'>((props, ref) => {
const { account, localize } = useClient()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ToastId, useToast } from '@chakra-ui/toast'
import { useActions } from '@vocdoni/react-providers'
import { useEffect, useRef } from 'react'

export const useActionsToast = () => {
const tRef = useRef<ToastId>()
const { info, error } = useActions()
const toast = useToast()

// show toasts for info and error
useEffect(() => {
if (toast && info === null && tRef.current) {
toast.close(tRef.current)
}
if (info && toast) {
tRef.current = toast({
title: info.title,
description: info.description,
status: 'info',
duration: null,
isClosable: false,
})
}
if (error && toast) {
toast({
title: error.title,
description: error.description,
status: 'error',
duration: 7000,
isClosable: false,
})
}
}, [info, error, toast])
}
78 changes: 73 additions & 5 deletions packages/react-providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The very first step is to add the `<ClientProvider />` as a wrapper of your
application or, at least, of your election:

~~~tsx
import { ClientProvider } from '@vocdoni/chakra-components'
import { ClientProvider } from '@vocdoni/react-providers'

const App = () => {
const signer = /* any ethers based signer */
Expand All @@ -35,13 +35,81 @@ const App = () => {
`ClientProvider` is a dependency of the other providers, so you'll have to
ensure you initialize that one as the parent.

### ElectionProvider

The `ElectionProvider` is the one that will allow you to interact with the
election and easily execute actions like voting.

You can use it as a wrapper of your elections:

~~~tsx
import { ElectionProvider } from '@vocdoni/react-providers'

const MyElection = () => {
return (
<ElectionProvider id='election-uuid'>
{/* your actual election code */}
</ElectionProvider>
)
}
~~~

You can either specify an id, and the provider will fetch the election for you,
or you can directly pass an `election` object. This is usefull in case you want
to render a list of elections and you already have the data:

~~~tsx
import { ElectionProvider } from '@vocdoni/react-providers'

const MyElectionList = () => {
const elections = /* your elections list */
return (
elections.map((election)=> (
<ElectionProvider election={election}>
<MyElectionListItem />
</ElectionProvider>
))
)
}
~~~

Once you initialized the provider, you can use the `useElection` hook to
interact with the election:


~~~tsx
import { useElection } from '@vocdoni/react-providers'

const MyElectionListItem = () => {
const { election } = useElection()

return <h1>{election.title.default}</h1>
}
~~~

### OrganizationProvider

Works more or less the same as the `ElectionProvider`, but for organizations:

~~~tsx
import { OrganizationProvider } from '@vocdoni/react-providers'

const MyElection = () => {
return (
<OrganizationProvider id='organization-uuid'>
{/* your actual organization code */}
</OrganizationProvider>
)
}
~~~

### hooks

- `useClient` allows you to interact with the `ClientProvider` layer. All the
methods it exports allow you to use the client while interacting with the
context/state:
+ `fetchAccount`
+ `createAccount`
+ `fetchAccount`: fetches connected account information (and balance)
+ `createAccount`: creates a new account using the connected signer
+ `setClient`: allows you to change the client during runtime
+ `localize`: internal method used for localization
+ `setSigner`: allows you to change the signer during runtime
Expand All @@ -53,8 +121,8 @@ ensure you initialize that one as the parent.
(used by flows like the spreadsheet/csv login one)
+ `vote`: A helper method to vote, using the current context info.
- `useOrganization`:
+ `fetch`
+ `update`
+ `fetch`: fetches the organization data
+ `update`: update the organization (only for organization owners)

License
-------
Expand Down
Loading

0 comments on commit 54f7555

Please sign in to comment.