Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
aff136c
feat: remove Mnemonic verification
yanzhihong23 Jan 13, 2022
f7e3f63
feat: add preview dialog
yanzhihong23 Jan 17, 2022
8d965e0
feat: add logo for QR code
yanzhihong23 Jan 18, 2022
11b3c3c
Merge branch 'develop' into feat/simplify-create-persona
yanzhihong23 Jan 18, 2022
f564322
fix: typo
yanzhihong23 Jan 18, 2022
44cf949
fix: eslint
yanzhihong23 Jan 18, 2022
24325c4
fix: eslint
yanzhihong23 Jan 18, 2022
f64e014
chore: watermark file name
yanzhihong23 Jan 18, 2022
5726858
fix: download empty image
yanzhihong23 Jan 18, 2022
2f3bc27
fix: icon size & QRCode level
yanzhihong23 Jan 18, 2022
064e632
fix: remove logo
yanzhihong23 Jan 18, 2022
bce93ec
fix: eslint
yanzhihong23 Jan 18, 2022
672426b
fix: recovery
yanzhihong23 Jan 19, 2022
9a930e9
fix: QR value
yanzhihong23 Jan 19, 2022
d8cd802
fix: no persona
yanzhihong23 Jan 19, 2022
438767e
fix: persona already exists
yanzhihong23 Jan 19, 2022
72a9483
fix: empty file
yanzhihong23 Jan 20, 2022
cd14844
fix: eslint
yanzhihong23 Jan 20, 2022
a5ede84
chore: ui style
hanszhoou Jan 20, 2022
fca9c11
chore: ui
yanzhihong23 Jan 20, 2022
baeb6aa
feat: update QRCode prefix
yanzhihong23 Jan 20, 2022
7f56279
feat: add persona_queryPersonaByPrivateKey api
yanzhihong23 Jan 21, 2022
52aa02f
fix: reset id when refresh words
yanzhihong23 Jan 24, 2022
2e8daaa
fix: eslint
yanzhihong23 Jan 24, 2022
2562dee
chore: nickname existing check
yanzhihong23 Jan 25, 2022
8b4438c
chore: useAsync
yanzhihong23 Jan 25, 2022
3e2ed51
fix: validateMnemonic
yanzhihong23 Jan 25, 2022
9573f04
fix: prettier & eslint
yanzhihong23 Jan 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@
"color": "^4.1.0",
"date-fns": "2.27.0",
"history": "^5.2.0",
"html-to-image": "^1.9.0",
"i18next-browser-languagedetector": "^6.1.2",
"json-stable-stringify": "^1.0.1",
"react-avatar-editor": "^12.0.0",
"react-hook-form": "^7.22.5",
"react-qrcode-logo": "^2.5.0",
"react-router": "6.0.0-beta.0",
"react-router-dom": "6.0.0-beta.0",
"react-to-print": "^2.14.3",
"react-use": "^17.3.1",
"unstated-next": "^1.1.0",
"urlcat": "^2.0.4",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/dashboard/src/assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
export const ABOUT_DIALOG_BACKGROUND = new URL('./images/AboutDialogBackground.png', import.meta.url)
export const PoolTogetherURL = new URL('./images/PoolTogether.png', import.meta.url)
export const SetupTutorialURL = new URL('./images/SetupTutorial.svg', import.meta.url)
export const WatermarkURL = new URL('./images/MaskWatermark.png', import.meta.url)
export const MaskBlueURL = new URL('./images/MaskBlue.png', import.meta.url)
9 changes: 7 additions & 2 deletions packages/dashboard/src/components/Mnemonic/MnemonicReveal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@ const WordCard = styled(Typography)(

export interface MnemonicRevealProps {
words: string[]
indexed?: boolean
wordClass?: string
}

export function MnemonicReveal(props: MnemonicRevealProps) {
const { words } = props
const { words, indexed, wordClass } = props
return (
<Grid container spacing={2}>
{words.map((item, index) => (
<Grid item xs={3} key={index}>
<WordCard>{item}</WordCard>
<WordCard className={wordClass}>
{indexed ? `${index + 1}. ` : ''}
{item}
</WordCard>
</Grid>
))}
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface ButtonGroupProps extends PropsWithChildren<{}> {}

export const ButtonContainer = ({ children }: ButtonGroupProps) => {
return (
<ButtonContainerUI my={7} direction="row" spacing={2} justifyContent="center">
<ButtonContainerUI my={7} direction="row" spacing={2} justifyContent="center" alignItems="center">
{children}
</ButtonContainerUI>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { MaskAlert } from '../MaskAlert'
import { ButtonContainer } from '../RegisterFrame/ButtonContainer'
import { Services } from '../../API'
import { PersonaContext } from '../../pages/Personas/hooks/usePersonaContext'
import { DashboardRoutes } from '@masknet/shared-base'
import { DashboardRoutes, delay } from '@masknet/shared-base'
import { useNavigate } from 'react-router-dom'
import { useState } from 'react'
import { SignUpRoutePath } from '../../pages/SignUp/routePath'
Expand Down Expand Up @@ -36,9 +36,11 @@ export const RestoreFromMnemonic = () => {
const persona = await Services.Identity.queryPersonaByMnemonic(values.join(' '), '')
if (persona) {
await changeCurrentPersona(persona.identifier)
// Waiting persona changed event notify
await delay(100)
navigate(DashboardRoutes.Personas, { replace: true })
} else {
navigate(`${DashboardRoutes.SignUp}/${SignUpRoutePath.PersonaCreate}`, {
navigate(`${DashboardRoutes.SignUp}/${SignUpRoutePath.PersonaRecovery}`, {
replace: false,
state: { mnemonic: values },
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const RestoreFromPrivateKey = memo(() => {
await delay(100)
navigate(DashboardRoutes.Personas)
} else {
navigate(`${DashboardRoutes.SignUp}/${SignUpRoutePath.PersonaCreate}`, {
navigate(`${DashboardRoutes.SignUp}/${SignUpRoutePath.PersonaRecovery}`, {
replace: false,
state: { privateKey: data.privateKey },
})
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/hooks/useMnemonicWordsPuzzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ export function useMnemonicWordsPuzzle() {

const refreshCallback = wordsRetry

return [words, puzzleWords, indexes, answerCallback, resetCallback, refreshCallback] as const
return { words, puzzleWords, indexes, answerCallback, resetCallback, refreshCallback } as const
}
12 changes: 11 additions & 1 deletion packages/dashboard/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
"close": "Close",
"send": "Send",
"resend": "Resend",
"print": "Print",
"download": "Download",
"print_preview": "Print Preview",
"download_preview": "Download Preview",
"confirm_password": "Confirm Password",
"about_dialog_license": "License: ",
"footer_bounty_list": "Bounty List",
Expand All @@ -36,9 +40,15 @@
"setup_page_create_restore_title": "Restoring from Identity or Backups",
"setup_page_create_restore_subtitle": "Restoring from identity & historical backups.",
"setup_page_create_restore_button": "Recovery or Sign In",
"create_account_mask_id": "MASK ID",
"create_account_private_key": "Private Key",
"create_account_identity_id": "Identity ID",
"create_account_identity_title": "Create an Identity for Mask Network",
"create_account_sign_in_button": "Recovery",
"create_account_identity_warning": "Please don’t forget your identity code. Your identity code is the only proof of your user identity and the only basis for you to encrypt or decrypt social activities.",
"create_account_persona_exists": "Persona already exists.",
"create_account_identity_warning": "Download your idendity ID to backup safely.",
"create_account_mnemonic_download_or_print": "I’ve downloaded it or printed it ",
"create_account_preview_tip": "This QR saves your identity code, please keep it safely. You can use Mask APP to scan QR to login persona.",
"create_account_mnemonic_confirm_failed": "Incorrect identity code",
"create_account_connect_social_media_button": "Create",
"create_account_connect_social_media": "Connect to {{type}}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const CreateMnemonic = memo(() => {
const location = useLocation() as { search: Search; state: { password: string } }
const navigate = useNavigate()
const [open, setOpen] = useState(false)
const [words, puzzleWords, indexes, answerCallback, resetCallback, refreshCallback] = useMnemonicWordsPuzzle()
const { words, puzzleWords, indexes, answerCallback, resetCallback, refreshCallback } = useMnemonicWordsPuzzle()
const [searchParams] = useSearchParams()
const { value: hasPassword, loading, retry } = useAsyncRetry(PluginServices.Wallet.hasPassword, [])

Expand Down
1 change: 1 addition & 0 deletions packages/dashboard/src/pages/SignUp/routePath.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum SignUpRoutePath {
MnemonicReveal = 'mnemonic-reveal',
PersonaCreate = 'persona-create',
PersonaRecovery = 'persona-recovery',
ConnectSocialMedia = 'connect-social-media',
}
6 changes: 4 additions & 2 deletions packages/dashboard/src/pages/SignUp/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Route, Routes, useParams } from 'react-router-dom'
import { ConnectSocialMedia, MnemonicRevealForm, PersonaCreate } from './steps'
import { ConnectSocialMedia, MnemonicRevealForm, PersonaCreate, PersonaRecovery } from './steps'
import { SignUpRoutePath } from './routePath'

const Actions = () => {
Expand All @@ -10,6 +10,8 @@ const Actions = () => {
return <MnemonicRevealForm />
case SignUpRoutePath.PersonaCreate:
return <PersonaCreate />
case SignUpRoutePath.PersonaRecovery:
return <PersonaRecovery />
case SignUpRoutePath.ConnectSocialMedia:
return <ConnectSocialMedia />
default:
Expand All @@ -21,7 +23,7 @@ export const SignUpRoutes = () => {
return (
<Routes>
<Route path=":action" element={<Actions />} />
<Route path="*" element={<MnemonicRevealForm />} />
<Route path="*" element={<PersonaCreate />} />
</Routes>
)
}
183 changes: 115 additions & 68 deletions packages/dashboard/src/pages/SignUp/steps/MnemonicRevealForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { memo, useState } from 'react'
import { some } from 'lodash-unified'
import { useNavigate } from 'react-router-dom'
import { Button, Stack, Box } from '@mui/material'
import { memo, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { Button, Stack, Box, IconButton } from '@mui/material'
import RefreshIcon from '@mui/icons-material/Refresh'
import { useCustomSnackbar } from '@masknet/theme'
import { DashboardRoutes } from '@masknet/shared-base'
import { MaskColorVar, useCustomSnackbar } from '@masknet/theme'
import { DashboardRoutes, ECKeyIdentifier } from '@masknet/shared-base'
import { MaskAlert } from '../../../components/MaskAlert'
import { Header } from '../../../components/RegisterFrame/ColumnContentHeader'
import {
Expand All @@ -14,52 +13,102 @@ import {
SignUpAccountLogo,
} from '../../../components/RegisterFrame/ColumnContentLayout'
import { useDashboardI18N } from '../../../locales'
import { DesktopMnemonicConfirm, MnemonicReveal } from '../../../components/Mnemonic'
import { MnemonicReveal } from '../../../components/Mnemonic'
import { SignUpRoutePath } from '../routePath'
import { ButtonContainer } from '../../../components/RegisterFrame/ButtonContainer'
import { useMnemonicWordsPuzzle } from '../../../hooks/useMnemonicWordsPuzzle'

enum CreateWalletStep {
NameAndWords = 0,
Verify = 1,
}
import { useCreatePersonaV2 } from '../../../hooks/useCreatePersonaV2'
import { PersonaContext } from '../../Personas/hooks/usePersonaContext'
import { Services } from '../../../API'
import PrintIcon from '@mui/icons-material/Print'
import { PreviewDialog } from './PreviewDialog'
import { DownloadIcon } from '@masknet/icons'
import { useAsync } from 'react-use'

export const MnemonicRevealForm = memo(() => {
const createPersona = useCreatePersonaV2()
const { changeCurrentPersona } = PersonaContext.useContainer()
const t = useDashboardI18N()
const navigate = useNavigate()
const { state } = useLocation() as { state: { personaName: string } }
const { showSnackbar } = useCustomSnackbar()
const [words, puzzleWords, indexes, answerCallback, resetCallback, refreshCallback] = useMnemonicWordsPuzzle()
const { words, refreshCallback } = useMnemonicWordsPuzzle()
const [preview, setPreview] = useState<{
open: boolean
type: 'print' | 'download'
}>({
open: false,
type: 'print',
})
const [id, setId] = useState<ECKeyIdentifier | null>(null)
const [privateKey, setPrivateKey] = useState('')

const [step, setStep] = useState(CreateWalletStep.NameAndWords)
const create = async () => {
try {
const identifier = await createPersona(words.join(' '), state.personaName)
setId(identifier)
const privateKey = await Services.Identity.exportPersonaPrivateKey(identifier)
setPrivateKey(privateKey)

const onSubmit = async () => {
if (words.join(' ') !== puzzleWords.join(' ')) {
showSnackbar(t.create_account_mnemonic_confirm_failed(), { variant: 'error' })
} else {
navigate(SignUpRoutePath.PersonaCreate, {
replace: true,
state: { mnemonic: words },
})
await changeCurrentPersona(identifier)
} catch (error) {
showSnackbar((error as Error).message, { variant: 'error' })
}
}

const onBack = () => {
setStep(CreateWalletStep.NameAndWords)
resetCallback()
const onConfirm = async () => {
if (!id) {
await create()
}

navigate(`${DashboardRoutes.SignUp}/${SignUpRoutePath.ConnectSocialMedia}`)
}

const onPreview = async (type: 'print' | 'download') => {
if (!id) {
await create()
}

setPreview({
open: true,
type,
})
}

useEffect(() => {
// handle refresh words
if (!id) return

setId(null)
Services.Identity.deletePersona(id, 'delete even with private')
}, [words])

useAsync(async () => {
// handle refresh page after create
const personas = await Services.Identity.queryMyPersonas()
const persona = personas.find((p) => p.nickname === state.personaName)

if (!persona) return

Services.Identity.deletePersona(persona.identifier, 'delete even with private')
}, [])

return (
<ColumnContentLayout>
<Header
title={t.create_account_identity_title()}
action={{ name: t.create_account_sign_in_button(), callback: () => navigate(DashboardRoutes.SignIn) }}
/>
<Body>
<PersonaLogoBox>
<SignUpAccountLogo />
</PersonaLogoBox>
{step === CreateWalletStep.NameAndWords && (
<>
<>
<ColumnContentLayout>
<Header
title={t.create_account_identity_title()}
action={{
name: t.create_account_sign_in_button(),
callback: () => navigate(DashboardRoutes.SignIn),
}}
/>
<Body>
<PersonaLogoBox>
<SignUpAccountLogo />
</PersonaLogoBox>

<Box>
<Stack
direction="row"
justifyContent="flex-end"
Expand All @@ -70,39 +119,37 @@ export const MnemonicRevealForm = memo(() => {
</Stack>
<MnemonicReveal words={words} />
<ButtonContainer>
<Button
size="large"
variant="rounded"
color="primary"
onClick={() => setStep(CreateWalletStep.Verify)}>
{t.verify()}
</Button>
</ButtonContainer>
</>
)}
{step === CreateWalletStep.Verify && (
<>
<DesktopMnemonicConfirm indexes={indexes} puzzleWords={puzzleWords} onChange={answerCallback} />
<ButtonContainer>
<Button variant="rounded" size="large" color="secondary" onClick={onBack}>
{t.back()}
</Button>
<Button
sx={{ width: '224px' }}
variant="rounded"
color="primary"
size="large"
disabled={some(puzzleWords, (word) => !word)}
onClick={onSubmit}>
{t.confirm()}
<Button size="large" variant="rounded" color="primary" onClick={onConfirm}>
{t.create_account_mnemonic_download_or_print()}
</Button>
<IconButton onClick={() => onPreview('print')}>
<PrintIcon
style={{ stroke: MaskColorVar.textLink, fill: MaskColorVar.primaryBackground }}
/>
</IconButton>
<IconButton onClick={() => onPreview('download')}>
<DownloadIcon
color="primary"
style={{ stroke: MaskColorVar.textLink, fill: MaskColorVar.primaryBackground }}
/>
</IconButton>
</ButtonContainer>
</>
)}
<Box sx={{ pt: 4, pb: 2, width: '100%' }}>
<MaskAlert description={t.create_account_identity_warning()} type="error" />
</Box>
</Body>
</ColumnContentLayout>
</Box>

<Box sx={{ pt: 4, pb: 2, width: '100%' }}>
<MaskAlert description={t.create_account_identity_warning()} type="info" />
</Box>
</Body>
</ColumnContentLayout>
<PreviewDialog
type={preview.type}
open={preview.open}
onClose={() => setPreview({ ...preview, open: false })}
personaName={state.personaName}
id={id?.toText()}
words={words}
privateKey={privateKey}
/>
</>
)
})
Loading