dappconnect-sdks/packages/status-react/src/protocol/provider.tsx

127 lines
2.9 KiB
TypeScript

import { createContext, useEffect, useReducer } from 'react'
import { createClient } from '@status-im/js'
import { Failed } from '../components/failed'
import { Loading } from '../components/loading'
import type { Account, Client, ClientOptions, Community } from '@status-im/js'
import type React from 'react'
export const Context = createContext<State | undefined>(undefined)
export type State = {
loading: boolean
failed: boolean
client: Client | undefined
community: Community['description'] | undefined
account: Account | undefined
dispatch?: React.Dispatch<Action>
}
export type Action =
| { type: 'INIT'; client: Client }
| { type: 'UPDATE_COMMUNITY'; community: Community['description'] }
| { type: 'SET_ACCOUNT'; account: Account | undefined }
| { type: 'FAIL' }
| { type: 'CONNECT'; connected: boolean }
interface Props {
options: ClientOptions
children: React.ReactNode
}
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'INIT': {
const { client } = action
return {
...state,
loading: false,
client,
account: client.account,
community: client.community.description,
}
}
case 'UPDATE_COMMUNITY': {
return { ...state, community: action.community }
}
case 'SET_ACCOUNT': {
return { ...state, account: action.account }
}
case 'FAIL': {
return { ...state, failed: true, loading: false }
}
case 'CONNECT': {
return { ...state, loading: !action.connected }
}
}
}
export const ProtocolProvider = (props: Props) => {
const { options, children } = props
const [state, dispatch] = useReducer(reducer, {
loading: true,
client: undefined,
community: undefined,
account: undefined,
dispatch: undefined,
failed: false,
})
const { client, loading, failed } = state
useEffect(() => {
const loadClient = async () => {
try {
const client = await createClient(options)
dispatch({ type: 'INIT', client })
} catch (error) {
console.error(error)
dispatch({ type: 'FAIL' })
}
}
loadClient()
// Community public key should not change during the lifetime
}, []) // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
if (client) {
const unsubscribe = [
client.onConnection(connected => {
dispatch({ type: 'CONNECT', connected })
}),
client.onAccountChange(account => {
dispatch({ type: 'SET_ACCOUNT', account })
}),
client.community.onChange(community => {
dispatch({ type: 'UPDATE_COMMUNITY', community })
}),
]
return () => {
unsubscribe.forEach(fn => fn())
}
}
}, [client])
if (failed) {
return <Failed />
}
if (loading) {
return <Loading />
}
return (
<Context.Provider value={{ ...state, dispatch }}>
{children}
</Context.Provider>
)
}