new styling + new signin and create account pages
This commit is contained in:
@@ -9,6 +9,7 @@ type TClientContext = {
|
||||
clientDetails?: TClientDetails
|
||||
getBalance: TUseGetBalance
|
||||
showAdmin: boolean
|
||||
mode: 'light' | 'dark'
|
||||
handleShowAdmin: () => void
|
||||
logIn: (clientDetails: TSignInWithMnemonic) => void
|
||||
logOut: () => void
|
||||
@@ -23,6 +24,7 @@ export const ClientContextProvider = ({
|
||||
}) => {
|
||||
const [clientDetails, setClientDetails] = useState<TClientDetails>()
|
||||
const [showAdmin, setShowAdmin] = useState(false)
|
||||
const [mode, setMode] = useState<'light' | 'dark'>('dark')
|
||||
|
||||
const history = useHistory()
|
||||
const getBalance = useGetBalance()
|
||||
@@ -44,6 +46,7 @@ export const ClientContextProvider = ({
|
||||
clientDetails,
|
||||
getBalance,
|
||||
showAdmin,
|
||||
mode,
|
||||
handleShowAdmin,
|
||||
logIn,
|
||||
logOut,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { BrowserRouter as Router } from 'react-router-dom'
|
||||
import { Routes } from './routes'
|
||||
import { ClientContext, ClientContextProvider } from './context/main'
|
||||
import { ApplicationLayout } from './layouts'
|
||||
import { SignIn } from './routes/sign-in'
|
||||
import { SignIn } from './routes/sign-in/'
|
||||
import { Admin, ErrorFallback } from './components'
|
||||
import { NymWalletTheme } from './theme'
|
||||
|
||||
|
||||
@@ -6,16 +6,12 @@ import { Bond } from './bond'
|
||||
import { Delegate } from './delegate'
|
||||
import { Receive } from './receive'
|
||||
import { Send } from './send'
|
||||
import { SignIn } from './sign-in'
|
||||
import { Unbond } from './unbond'
|
||||
import { Undelegate } from './undelegate'
|
||||
import { InternalDocs } from './internal-docs'
|
||||
|
||||
export const Routes = () => (
|
||||
<Switch>
|
||||
<Route path="/signin">
|
||||
<SignIn />
|
||||
</Route>
|
||||
<Route path="/balance">
|
||||
<Balance />
|
||||
</Route>
|
||||
|
||||
@@ -199,6 +199,7 @@ const CreateAccountContent = ({ showSignIn }: { showSignIn: () => void }) => {
|
||||
setError(e)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SignInCard>
|
||||
<Typography variant="h4">Create wallet</Typography>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
CardActions,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CircularProgress,
|
||||
Stack,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import { Box } from '@mui/system'
|
||||
import { createAccount } from '../../requests'
|
||||
import { TCreateAccount } from '../../types'
|
||||
import logo from '../../images/logo-background.svg'
|
||||
import { CopyToClipboard } from '../../components'
|
||||
|
||||
export const CreateAccountContent: React.FC<{ showSignIn: () => void }> = ({
|
||||
showSignIn,
|
||||
}) => {
|
||||
const [accountDetails, setAccountDetails] = useState<TCreateAccount>()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<Error>()
|
||||
|
||||
const handleCreateAccount = async () => {
|
||||
setIsLoading(true)
|
||||
setError(undefined)
|
||||
try {
|
||||
const res = await createAccount()
|
||||
setTimeout(() => {
|
||||
setAccountDetails(res)
|
||||
setIsLoading(false)
|
||||
}, 2500)
|
||||
} catch (e: any) {
|
||||
setError(e)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
handleCreateAccount()
|
||||
}, [])
|
||||
|
||||
if (isLoading) return <CircularProgress size={70} />
|
||||
|
||||
return (
|
||||
<Stack spacing={4} alignItems="center">
|
||||
<img src={logo} width={80} />
|
||||
<Typography variant="h4">Congratulations</Typography>
|
||||
<Typography variant="h6">Account setup complete!</Typography>
|
||||
<Alert severity="info" variant="outlined">
|
||||
<Box sx={{ textAlign: 'center' }} data-testid="mnemonic-warning">
|
||||
Please store your mnemonic in a safe place. You'll need it to access
|
||||
your account!
|
||||
</Box>
|
||||
</Alert>
|
||||
<Card variant="outlined" sx={{ bgcolor: 'transparent', p: 2 }}>
|
||||
<CardHeader title="Mnemonic" />
|
||||
<CardContent data-testid="mnemonic-phrase">
|
||||
{accountDetails?.mnemonic}
|
||||
</CardContent>
|
||||
<CardActions sx={{ justifyContent: 'flex-end' }}>
|
||||
<CopyToClipboard text={accountDetails?.mnemonic || ''} />
|
||||
</CardActions>
|
||||
</Card>
|
||||
<Box sx={{ textAlign: 'center' }}>
|
||||
<Typography variant="body2">Account address:</Typography>
|
||||
<Typography data-testid="wallet-address">
|
||||
{accountDetails?.client_address}
|
||||
</Typography>
|
||||
</Box>
|
||||
{error && (
|
||||
<Alert severity="error" variant="outlined">
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={showSignIn}
|
||||
data-testid="sign-in-button"
|
||||
>
|
||||
Back to sign in
|
||||
</Button>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, { useContext, useState } from 'react'
|
||||
import { Box } from '@mui/system'
|
||||
import { SignInContent } from './sign-in'
|
||||
import { CreateAccountContent } from './create-account'
|
||||
|
||||
export const SignIn = () => {
|
||||
const [showCreateAccount, setShowCreateAccount] = useState(false)
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 500,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{showCreateAccount ? (
|
||||
<CreateAccountContent
|
||||
showSignIn={() => setShowCreateAccount(false)}
|
||||
/>
|
||||
) : (
|
||||
<SignInContent showCreateAccount={() => setShowCreateAccount(true)} />
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import React, { useContext, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
CircularProgress,
|
||||
Grid,
|
||||
Stack,
|
||||
Link,
|
||||
TextField,
|
||||
Typography,
|
||||
Alert,
|
||||
} from '@mui/material'
|
||||
import logo from '../../images/logo-background.svg'
|
||||
import { signInWithMnemonic } from '../../requests'
|
||||
import { ClientContext } from '../../context/main'
|
||||
|
||||
export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
||||
showCreateAccount,
|
||||
}) => {
|
||||
const [mnemonic, setMnemonic] = useState<string>('')
|
||||
const [inputError, setInputError] = useState<string>()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const { logIn } = useContext(ClientContext)
|
||||
|
||||
const handleSignIn = async (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault()
|
||||
|
||||
setIsLoading(true)
|
||||
setInputError(undefined)
|
||||
|
||||
try {
|
||||
const res = await signInWithMnemonic(mnemonic || '')
|
||||
setIsLoading(false)
|
||||
logIn(res)
|
||||
} catch (e: any) {
|
||||
setIsLoading(false)
|
||||
setInputError(e)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack spacing={3} alignItems="center" sx={{ width: '100%' }}>
|
||||
<img src={logo} style={{ width: 80 }} />
|
||||
<Typography>Enter Mnemonic and sign in</Typography>
|
||||
<Grid container direction="column" spacing={1}>
|
||||
<Grid item>
|
||||
<TextField
|
||||
value={mnemonic}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setMnemonic(e.target.value)
|
||||
}
|
||||
size="medium"
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
id="mnemonic"
|
||||
label="BIP-39 Mnemonic"
|
||||
name="mnemonic"
|
||||
autoComplete="mnemonic"
|
||||
autoFocus
|
||||
disabled={isLoading}
|
||||
sx={{ color: 'red' }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={isLoading}
|
||||
endIcon={isLoading && <CircularProgress size={20} />}
|
||||
disableElevation
|
||||
size="large"
|
||||
onClick={handleSignIn}
|
||||
>
|
||||
{!isLoading ? 'Sign In' : 'Signing in'}
|
||||
</Button>
|
||||
</Grid>
|
||||
{inputError && (
|
||||
<Grid item sx={{ mt: 1 }}>
|
||||
<Alert severity="error" variant="outlined" data-testid="error">
|
||||
{inputError}
|
||||
</Alert>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<div>
|
||||
<Typography component="span">Don't have an account?</Typography>{' '}
|
||||
<Link href="#" onClick={showCreateAccount}>
|
||||
Create one now
|
||||
</Link>
|
||||
</div>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
import * as React from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles'
|
||||
import { CssBaseline } from '@mui/material'
|
||||
import { getDesignTokens } from './theme'
|
||||
import { ClientContext } from '../context/main'
|
||||
|
||||
/**
|
||||
* Provides the theme for the Network Explorer by reacting to the light/dark mode choice stored in the app context.
|
||||
*/
|
||||
export const NymWalletTheme: React.FC = ({ children }) => {
|
||||
const theme = React.useMemo(
|
||||
() => createTheme(getDesignTokens('light')),
|
||||
['light'],
|
||||
)
|
||||
const { mode } = useContext(ClientContext)
|
||||
const theme = React.useMemo(() => createTheme(getDesignTokens(mode)), [mode])
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
|
||||
@@ -31,7 +31,7 @@ const nymPalette: NymPalette = {
|
||||
const darkMode: NymPaletteVariant = {
|
||||
mode: 'dark',
|
||||
background: {
|
||||
main: '#111826',
|
||||
main: '#121726',
|
||||
paper: '#242C3D',
|
||||
},
|
||||
text: {
|
||||
@@ -192,7 +192,6 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
||||
easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
|
||||
},
|
||||
},
|
||||
|
||||
palette,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user