Skip to content

Commit

Permalink
Merge pull request #84 from oasisprotocol/csillag/refactor-simplify
Browse files Browse the repository at this point in the history
Refactor and simplify frontend code
  • Loading branch information
csillag authored Oct 9, 2024
2 parents eefda31 + f0a14b5 commit d726d83
Show file tree
Hide file tree
Showing 19 changed files with 189 additions and 548 deletions.
15 changes: 6 additions & 9 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { FC } from 'react'
import { createHashRouter, Outlet, RouterProvider } from 'react-router-dom'
import { HelmetProvider } from 'react-helmet-async'
import { EIP1193ContextProvider } from './providers/EIP1193Provider'
import { Web3ContextProvider } from './providers/Web3Provider'
import { AppStateContextProvider } from './providers/AppStateProvider'
import { ErrorBoundary } from './components/ErrorBoundary'
import { RouterErrorBoundary } from './components/RouterErrorBoundary'
import { EthereumContextProvider } from './providers/EthereumProvider'
import { PollPage } from './pages/PollPage'
import { DashboardPage } from './pages/DashboardPage'
import { CreatePollPage } from './pages/CreatePollPage'
import { ContractContextProvider } from './providers/ContractProvider'

const router = createHashRouter([
{
Expand Down Expand Up @@ -38,13 +37,11 @@ export const App: FC = () => {
<HelmetProvider>
<ErrorBoundary>
<EthereumContextProvider>
<EIP1193ContextProvider>
<Web3ContextProvider>
<AppStateContextProvider>
<RouterProvider router={router} />
</AppStateContextProvider>
</Web3ContextProvider>
</EIP1193ContextProvider>
<ContractContextProvider>
<AppStateContextProvider>
<RouterProvider router={router} />
</AppStateContextProvider>
</ContractContextProvider>
</EthereumContextProvider>
</ErrorBoundary>
</HelmetProvider>
Expand Down
59 changes: 28 additions & 31 deletions frontend/src/components/ConnectWallet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { FC, useEffect, useState } from 'react'
import { useWeb3 } from '../../hooks/useWeb3'
import { FC, useState } from 'react'
import { METAMASK_HOME_PAGE_URL } from '../../constants/config'
import { Button, ButtonSize } from '../Button'
import { toErrorString, UnknownNetworkError } from '../../utils/errors'
import { UnknownNetworkError } from '../../utils/errors'
import { ConnectedAccount } from '../ConnectedAccount'
import { useAppState } from '../../hooks/useAppState'

import classes from './index.module.css'
import { useEthereum } from '../../hooks/useEthereum'

interface Props {
mobileSticky: boolean
Expand All @@ -14,38 +14,29 @@ interface Props {
}

export const ConnectWallet: FC<Props> = ({ mobileSticky, avoidButtonClasses = false, buttonSize }) => {
const { setAppError } = useAppState()

const [isLoading, setIsLoading] = useState(false)
const [providerAvailable, setProviderAvailable] = useState(true)

const {
state: { isConnected, account, chainId, isUnknownNetwork },
isConnected,
isProviderAvailable,
userAddress,
isHomeChain,
state: { chainId },
connectWallet,
switchNetwork,
isProviderAvailable,
} = useWeb3()
useEffect(() => {
const init = async () => {
setIsLoading(true)
setProviderAvailable(await isProviderAvailable())
setIsLoading(false)
}
} = useEthereum()

init().catch(ex => {
setAppError(toErrorString(ex as Error))
})
}, [])
// const { switchNetwork } = useWeb3()

const handleConnectWallet = async () => {
setIsLoading(true)
try {
await connectWallet()
} catch (ex) {
if (ex instanceof UnknownNetworkError) {
} catch (ex: any) {
if (ex instanceof UnknownNetworkError || ex.code === 4001) {
// Already handled by provider layer
} else {
console.log(ex)
console.log(Object.keys(ex), ex)
alert((ex as Error).message ?? 'Failed to connect')
}
} finally {
Expand All @@ -58,18 +49,24 @@ export const ConnectWallet: FC<Props> = ({ mobileSticky, avoidButtonClasses = fa
try {
await switchNetwork()
await handleConnectWallet()
} catch (ex) {
console.log(ex)
alert((ex as Error).message ?? 'Failed to connect')
} catch (ex: any) {
console.log('Error on switch')
if (ex.code === 'ACTION_REJECTED') {
// User rejection, nothing to do
} else {
console.log(ex)
alert((ex as Error).message ?? 'Failed to switch network')
}
} finally {
setIsLoading(false)
}
}

return (
<>
{!isConnected && !providerAvailable && (
{!isConnected && !isProviderAvailable && (
<a href={METAMASK_HOME_PAGE_URL} target={'_blank'} rel={'noopener noreferrer'}>
CP1
<Button
className={avoidButtonClasses ? undefined : classes.connectWalletBtn}
color={'primary'}
Expand All @@ -80,7 +77,7 @@ export const ConnectWallet: FC<Props> = ({ mobileSticky, avoidButtonClasses = fa
</Button>
</a>
)}
{!isConnected && providerAvailable && isUnknownNetwork && (
{isConnected && isProviderAvailable && !isHomeChain && (
<Button
className={avoidButtonClasses ? undefined : classes.connectWalletBtn}
color={'primary'}
Expand All @@ -91,7 +88,7 @@ export const ConnectWallet: FC<Props> = ({ mobileSticky, avoidButtonClasses = fa
Switch Network
</Button>
)}
{!isConnected && providerAvailable && !isUnknownNetwork && (
{!isConnected && isProviderAvailable && isHomeChain && (
<Button
className={avoidButtonClasses ? undefined : classes.connectWalletBtn}
color={'primary'}
Expand All @@ -102,10 +99,10 @@ export const ConnectWallet: FC<Props> = ({ mobileSticky, avoidButtonClasses = fa
<label className={classes.connectWalletBtnLabel}>Connect wallet</label>
</Button>
)}
{isConnected && account && chainId && (
{isConnected && userAddress && chainId && isHomeChain && (
<ConnectedAccount
className={mobileSticky ? classes.stickyConnectedAccount : undefined}
address={account}
address={userAddress}
chainId={chainId}
/>
)}
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/components/ConnectedAccount/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'
import { JazzIcon } from '../JazzIcon'
import { useWeb3 } from '../../hooks/useWeb3'
import { useEthereum } from '../../hooks/useEthereum'
import { StringUtils } from '../../utils/string.utils'
import classes from './index.module.css'
import { useAppState } from '../../hooks/useAppState'
Expand All @@ -17,9 +17,7 @@ interface Props {
}

export const ConnectedAccount: FC<Props> = ({ className, address, chainId }) => {
const {
state: { explorerBaseUrl },
} = useWeb3()
const { explorerBaseUrl } = useEthereum()
const {
state: { isDesktopScreen },
} = useAppState()
Expand Down
86 changes: 8 additions & 78 deletions frontend/src/hooks/useContracts.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,11 @@
import {
PollManager__factory,
GaslessVoting__factory,
IPollManagerACL__factory,
IPollACL__factory,
} from '@oasisprotocol/blockvote-contracts'
import type { PollManager, GaslessVoting, IPollACL, IPollManagerACL } from '../types'
import { useEffect, useState } from 'react'
import {
VITE_CONTRACT_GASLESSVOTING,
VITE_CONTRACT_POLLMANAGER,
VITE_CONTRACT_POLLMANAGER_ACL,
} from '../constants/config'
import { useEthereum } from './useEthereum'
import { useContext } from 'react'
import { ContractContext } from '../providers/ContractContext'

export const useContracts = (aclAddress?: string | undefined) => {
const eth = useEthereum()
const [pollManager, setPollManager] = useState<PollManager | undefined>()
const [pollManagerAddress, setPollManagerAddress] = useState<string | undefined>()
const [pollManagerWithSigner, setPollManagerWithSigner] = useState<PollManager | undefined>()
const [pollACL, setPollACL] = useState<IPollACL | undefined>()
const [pollManagerACL, setPollManagerACL] = useState<IPollManagerACL | undefined>()
const [gaslessVoting, setGaslessVoting] = useState<GaslessVoting>()

useEffect(() => {
if (!eth.state.provider) {
setPollManager(undefined)
setPollManagerACL(undefined)
setGaslessVoting(undefined)
return
}
const pollManagerAddr = VITE_CONTRACT_POLLMANAGER
// console.log('PollManager at', pollManagerAddr);
setPollManager(PollManager__factory.connect(pollManagerAddr, eth.state.provider))

const pollManagerAclAddr = VITE_CONTRACT_POLLMANAGER_ACL
// console.log('IPollManagerACL at', pollManagerAclAddr);
setPollManagerACL(IPollManagerACL__factory.connect(pollManagerAclAddr, eth.state.provider))

const gaslessVotingAddr = VITE_CONTRACT_GASLESSVOTING
// console.log('GaslessVoting at', gaslessVotingAddr);
setGaslessVoting(GaslessVoting__factory.connect(gaslessVotingAddr, eth.state.provider))
}, [eth.state.provider])

useEffect(() => {
if (pollManager) {
pollManager.getAddress().then(setPollManagerAddress)
} else {
setPollManagerAddress(undefined)
}
}, [pollManager])

useEffect(() => {
if (!eth.state.signer) {
setPollManagerWithSigner(undefined)
return
}
const pollManagerAddr = VITE_CONTRACT_POLLMANAGER
// console.log('PollManager at', pollManagerAddr);
setPollManagerWithSigner(PollManager__factory.connect(pollManagerAddr, eth.state.signer))
}, [eth.state.signer])

useEffect(() => {
if (!eth.state.provider || !aclAddress) {
setPollACL(undefined)
return
}
// console.log('IPollACL at', aclAddress);
setPollACL(IPollACL__factory.connect(aclAddress, eth.state.provider))
}, [aclAddress, eth.state.provider])

return {
eth,
pollManager,
pollManagerAddress,
pollManagerWithSigner,
pollACL,
pollManagerACL,
gaslessVoting,
export const useContracts = () => {
const value = useContext(ContractContext)
if (Object.keys(value).length === 0) {
throw new Error('[useContracts] Component not wrapped within a Provider')
}

return value
}
11 changes: 0 additions & 11 deletions frontend/src/hooks/useEIP1193.ts

This file was deleted.

3 changes: 2 additions & 1 deletion frontend/src/hooks/usePollPermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const usePollPermissions = (poll: ExtendedPoll | undefined, onDashboard:
const aclAddress = poll?.proposal.params.acl
const creator = poll?.proposal.owner

const { eth, pollManagerAddress: daoAddress, pollACL } = useContracts(aclAddress)
const { eth, pollManagerAddress: daoAddress, getPollACL } = useContracts()
const pollACL = getPollACL(aclAddress)

const [isPending, setIsPending] = useState(false)
const [isMine, setIsMine] = useState<boolean | undefined>()
Expand Down
11 changes: 0 additions & 11 deletions frontend/src/hooks/useWeb3.ts

This file was deleted.

5 changes: 2 additions & 3 deletions frontend/src/pages/PollPage/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const usePollData = (pollId: string) => {
} = useExtendedPoll(proposal, { onDashboard: false })

const { now } = useTime()
const { pollManagerWithSigner: signerDao, gaslessVoting } = useContracts(poll?.proposal.params?.acl)
const { pollManagerWithSigner: signerDao, gaslessVoting } = useContracts()

const remainingTime = useMemo(
() => (deadline ? DateUtils.calculateRemainingTimeFrom(deadline, now) : undefined),
Expand Down Expand Up @@ -115,8 +115,7 @@ export const usePollData = (pollId: string) => {
const hasWallet = isDemo || (isHomeChain && userAddress !== ZeroAddress)
const hasWalletOnWrongNetwork = !isDemo && !isHomeChain && userAddress !== ZeroAddress

const canComplete =
!!isMine && (!deadline || isPastDue) && (!isDestroying || !isCompleting)
const canComplete = !!isMine && (!deadline || isPastDue) && (!isDestroying || !isCompleting)
const canDestroy = !!isMine && (!isDestroying || !isCompleting)

// console.log("canAclManage?", canAclManage, "deadline:", deadline, "isPastDue?", isPastDue, "canComplete?", canComplete)
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/pages/RestrictedContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { FC, PropsWithChildren } from 'react'
import { useWeb3 } from '../hooks/useWeb3'
import { useEthereum } from '../hooks/useEthereum'
import { LandingPage } from './LandingPage'

export const RestrictedContent: FC<PropsWithChildren> = props =>
useWeb3().state.isConnected ? props.children : <LandingPage />
export const RestrictedContent: FC<PropsWithChildren> = props => {
const { isConnected, isHomeChain } = useEthereum()
return isHomeChain && isConnected ? props.children : <LandingPage />
}
15 changes: 15 additions & 0 deletions frontend/src/providers/ContractContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { EthereumContext } from './EthereumContext'
import { PollManager, IPollACL, GaslessVoting, IPollManagerACL } from '@oasisprotocol/blockvote-contracts'
import { createContext } from 'react'

export interface ContractContextData {
readonly eth: EthereumContext
readonly pollManager: PollManager | undefined
readonly pollManagerAddress: string | undefined
readonly pollManagerWithSigner: PollManager | undefined
getPollACL: (address: string | undefined) => IPollACL | undefined
readonly pollManagerACL: IPollManagerACL | undefined
readonly gaslessVoting: GaslessVoting | undefined
}

export const ContractContext = createContext<ContractContextData>({} as ContractContextData)
Loading

0 comments on commit d726d83

Please sign in to comment.