Skip to content

Commit

Permalink
feature: Custom networks
Browse files Browse the repository at this point in the history
  • Loading branch information
sim committed Jan 10, 2022
1 parent d789246 commit e3519f7
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 7 deletions.
13 changes: 6 additions & 7 deletions src/auth/hooks/useNetwork.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { atom, useRecoilState, useRecoilValue } from "recoil"
import { useWallet } from "@terra-money/wallet-provider"
import { useNetworks } from "app/NetworksProvider"
import { useMergeNetworks } from "data/settings/CustomNetworks"
import { sandbox } from "../scripts/env"
import { getStoredNetwork, storeNetwork } from "../scripts/network"

Expand All @@ -22,20 +21,20 @@ export const useNetworkState = () => {

/* helpers */
export const useNetworkOptions = () => {
const networks = useNetworks()
const networks = useMergeNetworks()

if (!sandbox) return

return Object.values(networks).map(({ name }) => {
return [...Object.values(networks)].map(({ name }) => {
return { value: name, label: name }
})
}

export const useNetwork = () => {
const networks = useNetworks()
const networks = useMergeNetworks()
const network = useRecoilValue(networkState)
const wallet = useWallet()
return sandbox ? networks[network] : wallet.network
const item = networks[network]
return item ?? networks.mainnet
}

export const useNetworkName = () => {
Expand Down
62 changes: 62 additions & 0 deletions src/auth/networks/AddNetworkForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import { useForm } from "react-hook-form"
import { useNetworkState } from "data/wallet"
import { useCustomNetworks } from "data/settings/CustomNetworks"
import { Form, FormItem, Submit, Input } from "components/form"
import { useNetworks } from "app/NetworksProvider"

const AddNetwork = () => {
const { t } = useTranslation()
const navigate = useNavigate()
const { mainnet } = useNetworks()
const [, setNetwork] = useNetworkState()
const { add, validateName } = useCustomNetworks()

/* form */
const form = useForm<TerraNetwork>({ mode: "onChange" })
const { register, handleSubmit, formState } = form
const { errors } = formState

const submit = (values: TerraNetwork) => {
add(values)
setNetwork(values.name)
navigate("/")
}

return (
<Form onSubmit={handleSubmit(submit)}>
<FormItem label={t("Name")} error={errors.name?.message}>
<Input
{...register("name", {
required: true,
validate: {
exists: (value) =>
!validateName(value) ? `${value} already exists` : undefined,
},
})}
placeholder={mainnet.name}
autoFocus
/>
</FormItem>

<FormItem label={t("Chain ID")} error={errors.chainID?.message}>
<Input
{...register("chainID", { required: true })}
placeholder={mainnet.chainID}
/>
</FormItem>

<FormItem label={t("LCD")} error={errors.lcd?.message}>
<Input
{...register("lcd", { required: true })}
placeholder={mainnet.lcd}
/>
</FormItem>

<Submit />
</Form>
)
}

export default AddNetwork
16 changes: 16 additions & 0 deletions src/auth/networks/AddNetworkPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Card, Page } from "components/layout"
import { useTranslation } from "react-i18next"
import AddNetworkForm from "./AddNetworkForm"

const AddNetworkPage = () => {
const { t } = useTranslation()
return (
<Page title={t("Add a network")} small>
<Card>
<AddNetworkForm />
</Card>
</Page>
)
}

export default AddNetworkPage
41 changes: 41 additions & 0 deletions src/auth/networks/ManageNetworksForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useEffect } from "react"
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import { useNetworkState } from "data/wallet"
import { useCustomNetworks } from "data/settings/CustomNetworks"
import { Grid } from "components/layout"
import CustomItem from "txs/AddressBook/CustomItem"

const ManageNetworksForm = () => {
const { t } = useTranslation()
const navigate = useNavigate()
const [, setNetwork] = useNetworkState()
const { list, remove } = useCustomNetworks()
const empty = !list.length

useEffect(() => {
if (empty) navigate("/")
}, [empty, navigate])

return (
<Grid gap={12}>
{list.map((item) => {
const { name, chainID, lcd } = item
return (
<CustomItem
name={name}
contents={[
{ title: t("Chain ID"), desc: chainID },
{ title: t("LCD"), desc: lcd },
]}
onClick={() => setNetwork(name)}
onDelete={() => remove(name)}
key={name}
/>
)
})}
</Grid>
)
}

export default ManageNetworksForm
25 changes: 25 additions & 0 deletions src/auth/networks/ManageNetworksPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import AddIcon from "@mui/icons-material/Add"
import { Card, Page } from "components/layout"
import ManageNetworksForm from "./ManageNetworksForm"

const ManageNetworksPage = () => {
const { t } = useTranslation()

const add = (
<Link to="/network/new">
<AddIcon />
</Link>
)

return (
<Page title={t("Manage networks")} small>
<Card title={t("Custom networks")} extra={add}>
<ManageNetworksForm />
</Card>
</Page>
)
}

export default ManageNetworksPage
42 changes: 42 additions & 0 deletions src/data/settings/CustomNetworks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { atom, useRecoilState } from "recoil"
import { fromPairs } from "ramda"
import { getLocalSetting, setLocalSetting } from "utils/localStorage"
import { SettingKey } from "utils/localStorage"
import { useNetworks } from "app/NetworksProvider"

const customNetworksState = atom({
key: "customNetworks",
default: getLocalSetting<TerraNetwork[]>(SettingKey.CustomNetworks),
})

export const useCustomNetworks = () => {
const [customNetworks, setCustomNetworks] =
useRecoilState(customNetworksState)

const validateName = (name: string) =>
!customNetworks.some((network) => network.name === name)

const updateList = (list: TerraNetwork[]) => {
setCustomNetworks(list)
setLocalSetting(SettingKey.CustomNetworks, list)
}

const add = (newNetwork: TerraNetwork) => {
if (!validateName(newNetwork.name))
throw new Error(`Already exists: ${newNetwork.name}`)

updateList([...customNetworks, newNetwork])
}

const remove = (name: string) =>
updateList(customNetworks.filter((item) => item.name !== name))

return { list: customNetworks, add, remove, validateName }
}

export const useMergeNetworks = () => {
const networks = useNetworks()
const { list } = useCustomNetworks()
const custom = fromPairs(list.map((item) => [item.name, item]))
return { ...networks, ...custom }
}
11 changes: 11 additions & 0 deletions src/types/settings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,14 @@ interface CustomTokenCW20 extends CW20TokenInfoResponse {
interface CustomTokenCW721 extends CW721ContractInfoResponse {
contract: TerraAddress
}

/* Network */
interface TerraNetworks {
[name: string]: TerraNetwork
}

interface TerraNetwork {
name: string
chainID: string
lcd: string
}
2 changes: 2 additions & 0 deletions src/utils/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum SettingKey {
Currency = "Currency",
MinimumValue = "MinimumValue",
CustomTokens = "CustomTokens",
CustomNetworks = "CustomNetworks",
AddressBook = "AddressBook",
}

Expand All @@ -23,6 +24,7 @@ export const DefaultSettings = {
[SettingKey.Currency]: "uusd",
[SettingKey.MinimumValue]: 0,
[SettingKey.CustomTokens]: DefaultCustomTokens as CustomTokens,
[SettingKey.CustomNetworks]: [] as TerraNetwork[],
[SettingKey.AddressBook]: [] as AddressBook[],
}

Expand Down

0 comments on commit e3519f7

Please sign in to comment.