-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Onboarding import account via mnemonic (#32)
* Mnemonic phrase import * Import styling * Break out mnemonic into its own component
- Loading branch information
Showing
10 changed files
with
230 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import log from "electron-log"; | ||
import * as z from "zod"; | ||
|
||
import { manager } from "../manager"; | ||
|
||
export const handleImportAccountInputs = z.object({ | ||
name: z.string().optional(), | ||
account: z.string(), | ||
}); | ||
|
||
export async function handleImportAccount({ | ||
name, | ||
account, | ||
}: z.infer<typeof handleImportAccountInputs>) { | ||
const ironfish = await manager.getIronfish(); | ||
const rpcClient = await ironfish.rpcClient(); | ||
|
||
try { | ||
const importResponse = await rpcClient.wallet.importAccount({ | ||
name, | ||
account, | ||
}); | ||
|
||
return importResponse.content; | ||
} catch (error: unknown) { | ||
log.error(error); | ||
|
||
throw new Error( | ||
"Failed to import account, please verify your inputs and try again.", | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 31 additions & 1 deletion
32
renderer/components/OnboardingFlow/ImportAccount/ImportAccount.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,43 @@ | ||
import { Box, Heading } from "@chakra-ui/react"; | ||
import Link from "next/link"; | ||
import { useRouter } from "next/router"; | ||
|
||
import { trpcReact } from "@/providers/TRPCProvider"; | ||
|
||
import { MnemonicImport } from "./MnemonicImport"; | ||
|
||
export function ImportAccount() { | ||
const router = useRouter(); | ||
|
||
const { | ||
mutate: importAccount, | ||
isLoading, | ||
error, | ||
} = trpcReact.importAccount.useMutation(); | ||
|
||
return ( | ||
<Box> | ||
<Link href="/onboarding">Back</Link> | ||
<Heading mt={24} mb={8}> | ||
Import Account WIP | ||
Import Account | ||
</Heading> | ||
<MnemonicImport | ||
isLoading={isLoading} | ||
error={error?.shape?.message} | ||
handleImport={({ name, account }) => { | ||
importAccount( | ||
{ | ||
name, | ||
account, | ||
}, | ||
{ | ||
onSuccess: () => { | ||
router.push("/onboarding/snapshot-download"); | ||
}, | ||
}, | ||
); | ||
}} | ||
/> | ||
</Box> | ||
); | ||
} |
82 changes: 82 additions & 0 deletions
82
renderer/components/OnboardingFlow/ImportAccount/MnemonicImport.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { Box, Text } from "@chakra-ui/react"; | ||
import { useMemo, useState } from "react"; | ||
|
||
import { | ||
EMPTY_PHRASE_ARRAY, | ||
MnemonicPhrase, | ||
} from "@/ui/Forms/MnemonicPhrase/MnemonicPhrase"; | ||
import { TextInput } from "@/ui/Forms/TextInput/TextInput"; | ||
import { PillButton } from "@/ui/PillButton/PillButton"; | ||
import { validateMnemonic } from "@/utils/mnemonic"; | ||
|
||
type Props = { | ||
handleImport: (args: { name: string; account: string }) => void; | ||
isLoading: boolean; | ||
error?: string | null; | ||
}; | ||
|
||
export function MnemonicImport({ handleImport, isLoading, error }: Props) { | ||
const [accountName, setAccountName] = useState(""); | ||
const [isAccountNameDirty, setIsAccountNameDirty] = useState(false); | ||
const [phraseValues, setPhraseValues] = | ||
useState<Array<string>>(EMPTY_PHRASE_ARRAY); | ||
|
||
const errorMessage = useMemo(() => { | ||
return validateMnemonic(phraseValues); | ||
}, [phraseValues]); | ||
|
||
const hasValidName = accountName.length > 0; | ||
const hasNameError = isAccountNameDirty && !hasValidName; | ||
|
||
return ( | ||
<Box> | ||
<TextInput | ||
label="Account Name" | ||
value={accountName} | ||
error={ | ||
isAccountNameDirty && !hasValidName | ||
? "Please enter a name for this account" | ||
: null | ||
} | ||
onChange={(e) => { | ||
setAccountName(e.target.value); | ||
}} | ||
onBlur={() => { | ||
setIsAccountNameDirty(true); | ||
}} | ||
/> | ||
<Text mt={8} mb={4}> | ||
Please enter your mnemonic phrase. If you've copied the full phrase | ||
to your clipboard, you can paste it in any input and it will | ||
automatically be split into the correct number of words. | ||
</Text> | ||
<MnemonicPhrase | ||
defaultVisible | ||
phrase={phraseValues} | ||
error={errorMessage || error} | ||
onChange={(value) => { | ||
setPhraseValues(value); | ||
}} | ||
mb={8} | ||
/> | ||
<PillButton | ||
height="60px" | ||
px={8} | ||
isDisabled={isLoading || !!errorMessage || hasNameError} | ||
onClick={() => { | ||
if (!hasValidName) { | ||
setIsAccountNameDirty(true); | ||
return; | ||
} | ||
|
||
handleImport({ | ||
name: accountName, | ||
account: phraseValues.join(" "), | ||
}); | ||
}} | ||
> | ||
Continue | ||
</PillButton> | ||
</Box> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
export const MNEMONIC_ITEM_COUNT = 24; | ||
export const EMPTY_MNEMONIC_ARRAY = Array.from( | ||
{ length: MNEMONIC_ITEM_COUNT }, | ||
() => "", | ||
); | ||
|
||
export function formatMnemonic(phrase: string | Array<string>) { | ||
const asString = Array.isArray(phrase) ? phrase.join(" ") : phrase; | ||
return asString.trim().replace(/\s+/g, " "); | ||
} | ||
|
||
export function validateMnemonic( | ||
phrase: string | Array<string>, | ||
comparePhrase?: string, | ||
) { | ||
const formatted = formatMnemonic(phrase); | ||
|
||
const hasCorrectLength = | ||
formatted.match(/\s/g)?.length === MNEMONIC_ITEM_COUNT - 1; | ||
|
||
if (!hasCorrectLength) { | ||
return "Please fill out the entire phrase."; | ||
} | ||
|
||
if (typeof comparePhrase === "string" && !validateMnemonic(comparePhrase)) { | ||
throw new Error( | ||
"Invalid compare phrase. Please ensure compare phrase is a 24 word mnemonic phrase.", | ||
); | ||
} | ||
|
||
if (typeof comparePhrase === "string" && formatted !== comparePhrase) { | ||
return "The phrase you entered does not match. Please verify the phrase and try again."; | ||
} | ||
|
||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type MergeProps<T, U> = T & Omit<U, keyof T>; |