Merge pull request #911 from nymtech/feature/faucet-page-react
Feature/faucet page react
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
VALIDATOR_ADDRESS=
|
||||
MNEMONIC=
|
||||
TESTNET_URL_1=
|
||||
TESTNET_URL_2=
|
||||
ACCOUNT_ADDRESS=
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
.parcel-cache
|
||||
@@ -0,0 +1,17 @@
|
||||
# Nym Testnet Token Faucet Page
|
||||
|
||||
## Installation
|
||||
|
||||
`yarn install`
|
||||
|
||||
## Environment variables
|
||||
|
||||
Create a file in the root directory called `.env`. Copy everything from the `.env.sample` file into the `.env` file and add the required values`
|
||||
|
||||
## Development environment
|
||||
|
||||
`yarn dev`
|
||||
|
||||
## Build
|
||||
|
||||
`yarn build`
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "testnet-faucet",
|
||||
"version": "1.0.0",
|
||||
"description": "testnet faucet for token aquisition",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "yarn parcel ./src/index.html",
|
||||
"build": "yarn parcel build ./src/index.html"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.6.0",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@hookform/resolvers": "^2.8.3",
|
||||
"@mui/icons-material": "^5.1.1",
|
||||
"@mui/material": "^5.1.1",
|
||||
"@nymproject/nym-validator-client": "^0.18.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-hook-form": "^7.20.1",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^17.0.35",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"parcel": "^2.0.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { AppBar, Container, Toolbar } from '@mui/material'
|
||||
import logo from './images/nym-logo.svg'
|
||||
import { NymThemeProvider } from './theme'
|
||||
import { Form } from './components/form'
|
||||
import { Header } from './components/header'
|
||||
import { GlobalContextProvider } from './context'
|
||||
|
||||
export const App = () => {
|
||||
return (
|
||||
<NymThemeProvider>
|
||||
<GlobalContextProvider>
|
||||
<AppBar
|
||||
position="sticky"
|
||||
sx={{
|
||||
bgcolor: '#070B15',
|
||||
backgroundImage: 'none',
|
||||
boxShadow: 'none',
|
||||
mt: 5,
|
||||
}}
|
||||
>
|
||||
<Container fixed>
|
||||
<Toolbar disableGutters>
|
||||
<img src={logo} />
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
<Container fixed>
|
||||
<Header />
|
||||
<Form />
|
||||
</Container>
|
||||
</GlobalContextProvider>
|
||||
</NymThemeProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useContext } from 'react'
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import RefreshIcon from '@mui/icons-material/Refresh'
|
||||
import { GlobalContext, EnumRequestType } from '../context'
|
||||
|
||||
export const Balance = () => {
|
||||
const { balance, loadingState, getBalance } = useContext(GlobalContext)
|
||||
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
background: 'transparent',
|
||||
border: (theme) => `1px solid ${theme.palette.common.white}`,
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
<CardHeader
|
||||
title={
|
||||
<Typography variant="h6">
|
||||
The current faucet balance is{' '}
|
||||
<Typography
|
||||
component="span"
|
||||
variant="h6"
|
||||
data-testid="punk-balance-message"
|
||||
>
|
||||
{balance} PUNKS
|
||||
</Typography>
|
||||
</Typography>
|
||||
}
|
||||
action={
|
||||
loadingState.isLoading &&
|
||||
loadingState.requestType === EnumRequestType.balance ? (
|
||||
<CircularProgress size={12} />
|
||||
) : (
|
||||
<IconButton onClick={getBalance}>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { useContext } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
CircularProgress,
|
||||
TextField,
|
||||
useMediaQuery,
|
||||
} from '@mui/material'
|
||||
import { Box } from '@mui/system'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
import { useForm, SubmitHandler } from 'react-hook-form'
|
||||
import { validationSchema } from './validationSchema'
|
||||
import { getCoinValue } from '../../utils'
|
||||
import { EnumRequestType, GlobalContext } from '../../context'
|
||||
import { TokenTransfer } from '../token-transfer'
|
||||
|
||||
type TFormData = {
|
||||
address: string
|
||||
amount: string
|
||||
}
|
||||
|
||||
export const Form = () => {
|
||||
const matches = useMediaQuery('(max-width:500px)')
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm({ resolver: yupResolver(validationSchema) })
|
||||
|
||||
console.log(errors)
|
||||
|
||||
const { requestTokens, loadingState, tokenTransfer, error } =
|
||||
useContext(GlobalContext)
|
||||
|
||||
const onSubmit: SubmitHandler<TFormData> = async (data) => {
|
||||
const upunks = getCoinValue(data.amount)
|
||||
await requestTokens({
|
||||
address: data.address,
|
||||
upunks: upunks.toString(),
|
||||
punks: data.amount,
|
||||
})
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
setValue('address', '')
|
||||
setValue('amount', '')
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<TextField
|
||||
label="Address"
|
||||
fullWidth
|
||||
{...register('address')}
|
||||
sx={{ mb: 1 }}
|
||||
helperText={errors?.address?.message}
|
||||
error={!!errors.address}
|
||||
data-testid="address"
|
||||
/>
|
||||
<TextField
|
||||
label="Amount (PUNKS)"
|
||||
fullWidth
|
||||
{...register('amount')}
|
||||
sx={{ mb: 1 }}
|
||||
helperText={errors?.amount?.message}
|
||||
error={!!errors.amount}
|
||||
data-testid={'punk-amounts'}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
mb: 5,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
fullWidth={matches}
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
endIcon={
|
||||
loadingState.requestType === EnumRequestType.tokens && (
|
||||
<CircularProgress size={20} color="inherit" />
|
||||
)
|
||||
}
|
||||
disabled={loadingState.isLoading}
|
||||
data-testid="request-token-button"
|
||||
>
|
||||
Request Tokens
|
||||
</Button>
|
||||
</Box>
|
||||
{error && <Alert severity="error">{error}</Alert>}
|
||||
{tokenTransfer && (
|
||||
<TokenTransfer
|
||||
address={tokenTransfer.address}
|
||||
amount={tokenTransfer.amount}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import * as Yup from 'yup'
|
||||
import { validAmount } from '../../utils'
|
||||
|
||||
export const validationSchema = Yup.object().shape({
|
||||
address: Yup.string().strict().trim('Cannot have leading space').required(),
|
||||
amount: Yup.string()
|
||||
.required()
|
||||
.test('valid-amount', 'A valid amount is required', (amount) => {
|
||||
if (!amount) return false
|
||||
return validAmount(amount)
|
||||
}),
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Grid, Typography, useMediaQuery } from '@mui/material'
|
||||
import { Box } from '@mui/system'
|
||||
import { Balance } from './balance'
|
||||
|
||||
export const Header = () => {
|
||||
const matches = useMediaQuery('(min-width: 500px)')
|
||||
return (
|
||||
<Box sx={{ mb: 3, mt: 3 }}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{ fontWeight: 'light' }}
|
||||
data-testid="token-faucet"
|
||||
>
|
||||
Nym token faucet
|
||||
</Typography>
|
||||
{matches && (
|
||||
<Typography
|
||||
color="primary"
|
||||
variant="h3"
|
||||
sx={{ fontWeight: 'light' }}
|
||||
>
|
||||
Tokens to your address
|
||||
</Typography>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Balance />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Card, CardHeader, Link, Typography } from '@mui/material'
|
||||
import { urls } from '../context/index'
|
||||
|
||||
export const TokenTransfer = ({
|
||||
address,
|
||||
amount,
|
||||
}: {
|
||||
address: string
|
||||
amount: string
|
||||
}) => {
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
background: 'transparent',
|
||||
border: (theme) => `1px solid ${theme.palette.common.white}`,
|
||||
p: 2,
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<CardHeader
|
||||
title={
|
||||
<>
|
||||
<Typography component="span" variant="h5">
|
||||
Successfully transferred {amount} PUNKS to
|
||||
</Typography>{' '}
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href={`${urls.blockExplorer}/account/${address}`}
|
||||
data-testid="success-sent-message"
|
||||
variant="h5"
|
||||
>
|
||||
{address}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { createContext, useEffect, useState } from 'react'
|
||||
import ClientValidator, {
|
||||
nativeToPrintable,
|
||||
} from '@nymproject/nym-validator-client'
|
||||
|
||||
export const urls = {
|
||||
blockExplorer: 'https://testnet-milhon-blocks.nymtech.net',
|
||||
}
|
||||
|
||||
type TGlobalContext = {
|
||||
getBalance: () => void
|
||||
requestTokens: ({
|
||||
address,
|
||||
upunks,
|
||||
}: {
|
||||
address: string
|
||||
upunks: string
|
||||
punks: string
|
||||
}) => void
|
||||
loadingState: TLoadingState
|
||||
balance?: string
|
||||
tokenTransfer?: { address: string; amount: string }
|
||||
error?: string
|
||||
}
|
||||
|
||||
export const GlobalContext = createContext({} as TGlobalContext)
|
||||
|
||||
const { VALIDATOR_ADDRESS, MNEMONIC, TESTNET_URL_1, ACCOUNT_ADDRESS } =
|
||||
process.env
|
||||
|
||||
export enum EnumRequestType {
|
||||
balance = 'balance',
|
||||
tokens = 'tokens',
|
||||
}
|
||||
|
||||
type TLoadingState = {
|
||||
isLoading: boolean
|
||||
requestType?: EnumRequestType
|
||||
}
|
||||
|
||||
export const GlobalContextProvider: React.FC = ({ children }) => {
|
||||
const [validator, setValidator] = useState<ClientValidator>()
|
||||
const [loadingState, setLoadingState] = useState<TLoadingState>({
|
||||
isLoading: false,
|
||||
requestType: undefined,
|
||||
})
|
||||
const [balance, setBalance] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const [tokenTransfer, setTokenTransfer] =
|
||||
useState<{ address: string; amount: string }>()
|
||||
|
||||
const getValidator = async () => {
|
||||
const Validator = await ClientValidator.connect(
|
||||
VALIDATOR_ADDRESS,
|
||||
MNEMONIC,
|
||||
[TESTNET_URL_1],
|
||||
'punk'
|
||||
)
|
||||
setValidator(Validator)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingState.isLoading) {
|
||||
setError(undefined)
|
||||
}
|
||||
}, [loadingState])
|
||||
|
||||
useEffect(() => {
|
||||
getValidator()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (validator || tokenTransfer) getBalance()
|
||||
}, [validator, tokenTransfer])
|
||||
|
||||
const getBalance = async () => {
|
||||
setLoadingState({ isLoading: true, requestType: EnumRequestType.balance })
|
||||
try {
|
||||
const balance = await validator?.getBalance(ACCOUNT_ADDRESS)
|
||||
const punks = nativeToPrintable(balance?.amount || '')
|
||||
setBalance(punks)
|
||||
} catch (e) {
|
||||
setError(`An error occured while getting the balance: ${e}`)
|
||||
} finally {
|
||||
setLoadingState({ isLoading: false, requestType: undefined })
|
||||
}
|
||||
}
|
||||
|
||||
const requestTokens = async ({
|
||||
address,
|
||||
upunks,
|
||||
punks,
|
||||
}: {
|
||||
address: string
|
||||
upunks: string
|
||||
punks: string
|
||||
}) => {
|
||||
setTokenTransfer(undefined)
|
||||
setLoadingState({ isLoading: true, requestType: EnumRequestType.tokens })
|
||||
try {
|
||||
await validator?.send(ACCOUNT_ADDRESS, address, [
|
||||
{ amount: upunks, denom: 'upunk' },
|
||||
])
|
||||
setTokenTransfer({ address, amount: punks })
|
||||
} catch (e) {
|
||||
setError(`An error occured during the transfer request: ${e}`)
|
||||
} finally {
|
||||
setLoadingState({ isLoading: false, requestType: undefined })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<GlobalContext.Provider
|
||||
value={{
|
||||
getBalance,
|
||||
requestTokens,
|
||||
loadingState,
|
||||
balance,
|
||||
tokenTransfer,
|
||||
error,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</GlobalContext.Provider>
|
||||
)
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="100" height="28" viewBox="0 0 100 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.31117 3.0504L15.0523 27.4194H20.6547H26.9659V0H20.6547V24.3347L11.9473 0H6.31117H0V27.4194H6.31117V3.0504Z" fill="white"/>
|
||||
<path d="M87.9513 0L81.9776 24.3347L76.004 0H63.9216V27.4194H70.2328V3.0504L76.2065 27.4194H87.7151L93.6887 3.0504V27.4194H99.9999V0H87.9513Z" fill="white"/>
|
||||
<path d="M45.461 13.6411L37.6986 0H30.4087L42.2885 20.9073V27.4194H48.5997V20.9073L60.4795 0H53.1896L45.461 13.6411Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 531 B |
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="shortcut icon" href="./images/favicon.png" />
|
||||
<title>Nym Testnet Faucet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
import ReactDOM from 'react-dom'
|
||||
import { App } from './App'
|
||||
|
||||
const app = document.getElementById('app')
|
||||
ReactDOM.render(<App />, app)
|
||||
@@ -0,0 +1,14 @@
|
||||
import * as React from 'react'
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles'
|
||||
import { CssBaseline } from '@mui/material'
|
||||
import { getDesignTokens } from './theme'
|
||||
|
||||
export const NymThemeProvider: React.FC = ({ children }) => {
|
||||
const theme = createTheme(getDesignTokens('dark'))
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/* eslint-disable no-shadow,@typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-interface */
|
||||
import {
|
||||
Theme,
|
||||
ThemeOptions,
|
||||
Palette,
|
||||
PaletteOptions,
|
||||
} from '@mui/material/styles'
|
||||
import { PaletteMode } from '@mui/material'
|
||||
|
||||
/**
|
||||
* If you are unfamiliar with Material UI theming, please read the following first:
|
||||
* - https://mui.com/customization/theming/
|
||||
* - https://mui.com/customization/palette/
|
||||
* - https://mui.com/customization/dark-mode/#dark-mode-with-custom-palette
|
||||
*
|
||||
* This file adds typings to the theme using Typescript's module augmentation.
|
||||
*
|
||||
* Read the following if you are unfamiliar with module augmentation and declaration merging. Then
|
||||
* look at the recommendations from Material UI docs for implementation:
|
||||
* - https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
|
||||
* - https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces
|
||||
* - https://mui.com/customization/palette/#adding-new-colors
|
||||
*
|
||||
*
|
||||
* IMPORTANT:
|
||||
*
|
||||
* The type augmentation must match MUI's definitions. So, notice the use of `interface` rather than
|
||||
* `type Foo = { ... }` - this is necessary to merge the definitions.
|
||||
*/
|
||||
|
||||
declare module '@mui/material/styles' {
|
||||
/**
|
||||
* This interface defines a palette used across Nym for branding
|
||||
*/
|
||||
interface NymPalette {
|
||||
highlight: string
|
||||
text: {
|
||||
nav: string
|
||||
footer: string
|
||||
}
|
||||
}
|
||||
|
||||
interface NymPaletteVariant {
|
||||
mode: PaletteMode
|
||||
background: {
|
||||
main: string
|
||||
paper: string
|
||||
}
|
||||
text: {
|
||||
main: string
|
||||
}
|
||||
topNav: {
|
||||
background: string
|
||||
}
|
||||
nav: {
|
||||
background: string
|
||||
hover: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A palette definition only for the Network Explorer that extends the Nym palette
|
||||
*/
|
||||
interface NetworkExplorerPalette {
|
||||
networkExplorer: {
|
||||
map: {
|
||||
stroke: string
|
||||
fills: string[]
|
||||
}
|
||||
background: {
|
||||
tertiary: string
|
||||
}
|
||||
topNav: {
|
||||
background: string
|
||||
socialIcons: string
|
||||
appBar: string
|
||||
}
|
||||
nav: {
|
||||
selected: {
|
||||
main: string
|
||||
nested: string
|
||||
}
|
||||
background: string
|
||||
hover: string
|
||||
text: string
|
||||
}
|
||||
footer: {
|
||||
socialIcons: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface NymPaletteAndNetworkExplorerPalette {
|
||||
nym: NymPalette
|
||||
}
|
||||
|
||||
type NymPaletteAndNetworkExplorerPaletteOptions =
|
||||
Partial<NymPaletteAndNetworkExplorerPalette>
|
||||
|
||||
/**
|
||||
* Add anything not palette related to the theme here
|
||||
*/
|
||||
interface NymTheme {}
|
||||
|
||||
/**
|
||||
* This augments the definitions of the MUI Theme with the Nym theme, as well as
|
||||
* a partial `ThemeOptions` type used by `createTheme`
|
||||
*
|
||||
* IMPORTANT: only add extensions to the interfaces above, do not modify the lines below
|
||||
*/
|
||||
interface Theme extends NymTheme {}
|
||||
interface ThemeOptions extends Partial<NymTheme> {}
|
||||
interface Palette extends NymPaletteAndNetworkExplorerPalette {}
|
||||
interface PaletteOptions extends NymPaletteAndNetworkExplorerPaletteOptions {}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
import { PaletteMode } from '@mui/material'
|
||||
import {
|
||||
PaletteOptions,
|
||||
NymPalette,
|
||||
NetworkExplorerPalette,
|
||||
ThemeOptions,
|
||||
createTheme,
|
||||
NymPaletteVariant,
|
||||
} from '@mui/material/styles'
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
// Nym palette type definitions
|
||||
//
|
||||
|
||||
/**
|
||||
* The Nym palette.
|
||||
*
|
||||
* IMPORTANT: do not export this constant, always use the MUI `useTheme` hook to get the correct
|
||||
* colours for dark/light mode.
|
||||
*/
|
||||
const nymPalette: NymPalette = {
|
||||
/** emphasises important elements */
|
||||
highlight: '#FB6E4E',
|
||||
text: {
|
||||
nav: '#F2F2F2',
|
||||
/** footer text colour */
|
||||
footer: '#666B77',
|
||||
},
|
||||
}
|
||||
|
||||
const darkMode: NymPaletteVariant = {
|
||||
mode: 'dark',
|
||||
background: {
|
||||
main: '#070B15',
|
||||
paper: '#242C3D',
|
||||
},
|
||||
text: {
|
||||
main: '#F2F2F2',
|
||||
},
|
||||
topNav: {
|
||||
background: '#111826',
|
||||
},
|
||||
nav: {
|
||||
background: '#242C3D',
|
||||
hover: '#111826',
|
||||
},
|
||||
}
|
||||
|
||||
const lightMode: NymPaletteVariant = {
|
||||
mode: 'light',
|
||||
background: {
|
||||
main: '#F2F2F2',
|
||||
paper: '#FFFFFF',
|
||||
},
|
||||
text: {
|
||||
main: '#666666',
|
||||
},
|
||||
topNav: {
|
||||
background: '#111826',
|
||||
},
|
||||
nav: {
|
||||
background: '#242C3D',
|
||||
hover: '#111826',
|
||||
},
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
// Nym palettes for light and dark mode
|
||||
//
|
||||
|
||||
/**
|
||||
* Map a Nym palette variant onto the MUI palette
|
||||
*/
|
||||
const variantToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({
|
||||
text: {
|
||||
primary: variant.text.main,
|
||||
},
|
||||
primary: {
|
||||
main: nymPalette.highlight,
|
||||
contrastText: '#fff',
|
||||
},
|
||||
background: {
|
||||
default: variant.background.main,
|
||||
paper: variant.background.paper,
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Returns the Network Explorer palette for light mode.
|
||||
*/
|
||||
const createLightModePalette = (): PaletteOptions => ({
|
||||
nym: {
|
||||
...nymPalette,
|
||||
},
|
||||
...variantToMUIPalette(lightMode),
|
||||
})
|
||||
|
||||
/**
|
||||
* Returns the Network Explorer palette for dark mode.
|
||||
*/
|
||||
const createDarkModePalette = (): PaletteOptions => ({
|
||||
nym: {
|
||||
...nymPalette,
|
||||
},
|
||||
...variantToMUIPalette(darkMode),
|
||||
})
|
||||
|
||||
/**
|
||||
* IMPORANT: if you need to get the default MUI theme, use the following
|
||||
*
|
||||
* import { createTheme as systemCreateTheme } from '@mui/system';
|
||||
*
|
||||
* // get the MUI system defaults for light mode
|
||||
* const systemTheme = systemCreateTheme({ palette: { mode: 'light' } });
|
||||
*
|
||||
*
|
||||
* return {
|
||||
* // change `primary` to default MUI `success`
|
||||
* primary: {
|
||||
* main: systemTheme.palette.success.main,
|
||||
* },
|
||||
* nym: {
|
||||
* ...nymPalette,
|
||||
* },
|
||||
* };
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
// Nym theme overrides
|
||||
//
|
||||
|
||||
/**
|
||||
* Gets the theme options to be passed to `createTheme`.
|
||||
*
|
||||
* Based on pattern from https://mui.com/customization/dark-mode/#dark-mode-with-custom-palette.
|
||||
*
|
||||
* @param mode The theme mode: 'light' or 'dark'
|
||||
*/
|
||||
export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
||||
// first, create the palette from user's choice of light or dark mode
|
||||
const { palette } = createTheme({
|
||||
palette: {
|
||||
mode,
|
||||
...(mode === 'light'
|
||||
? createLightModePalette()
|
||||
: createDarkModePalette()),
|
||||
},
|
||||
})
|
||||
|
||||
// then customise theme and components
|
||||
return {
|
||||
typography: {
|
||||
fontFamily: [
|
||||
'Open Sans',
|
||||
'sans-serif',
|
||||
'BlinkMacSystemFont',
|
||||
'Roboto',
|
||||
'Oxygen',
|
||||
'Ubuntu',
|
||||
'Helvetica Neue',
|
||||
].join(','),
|
||||
fontSize: 14,
|
||||
fontWeightRegular: 600,
|
||||
},
|
||||
transitions: {
|
||||
duration: {
|
||||
shortest: 150,
|
||||
shorter: 200,
|
||||
short: 250,
|
||||
standard: 300,
|
||||
complex: 375,
|
||||
enteringScreen: 225,
|
||||
leavingScreen: 195,
|
||||
},
|
||||
easing: {
|
||||
easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
|
||||
},
|
||||
},
|
||||
palette,
|
||||
}
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
declare module '*.svg' {
|
||||
const content: any
|
||||
export default content
|
||||
}
|
||||
|
||||
namespace NodeJS {
|
||||
export interface Process {
|
||||
env: {
|
||||
VALIDATOR_ADDRESS: string
|
||||
MNEMONIC: string
|
||||
TESTNET_URL_1: string
|
||||
TESTNET_URL_2: string
|
||||
ACCOUNT_ADDRESS: string
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { printableBalanceToNative } from '@nymproject/nym-validator-client/dist/currency'
|
||||
|
||||
export const getCoinValue = (raw: string): number => {
|
||||
let native = printableBalanceToNative(raw)
|
||||
return parseInt(native)
|
||||
}
|
||||
|
||||
export const validAmount = (amount: string): boolean => {
|
||||
if (isNaN(+amount)) return false
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": false,
|
||||
"jsx": "react-jsx",
|
||||
"sourceMap": true,
|
||||
"baseUrl": "."
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user