142 lines
6.9 KiB
TypeScript
142 lines
6.9 KiB
TypeScript
import { Box, Flex, PasswordInput, Text, Transition } from "@mantine/core";
|
|
import { useEffect, useState } from "react";
|
|
import Lottie from "lottie-react";
|
|
import animationData from './lottie.json'
|
|
import { useMemory } from "@/app/providers/MemoryProvider/useMemory";
|
|
import { mnemonicToSeed } from "web-bip39";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { modals } from "@mantine/modals";
|
|
import { Buffer } from 'buffer'
|
|
import { encodeWithPassword, generateHashFromPrivateKey, generateKeyPairFromSeed } from "@/app/crypto/crypto";
|
|
import { useAccountProvider } from "@/app/providers/AccountProvider/useAccountProvider";
|
|
import { Account } from "@/app/providers/AccountProvider/AccountProvider";
|
|
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
|
|
import { AnimatedButton } from "@/app/components/AnimatedButton/AnimatedButton";
|
|
import { IconChevronRight } from "@tabler/icons-react";
|
|
import { SIZE_LOGIN_WIDTH_PX } from "@/app/constants";
|
|
|
|
|
|
export function SetPassword() {
|
|
const navigate = useNavigate();
|
|
const [password, setPassword] = useState("");
|
|
const [confirm, setConfirm] = useState("");
|
|
const [phrase, _] = useMemory("seed-phrase", "");
|
|
const [mounted, setMounted] = useState(false);
|
|
const colors = useRosettaColors();
|
|
|
|
const { createAccount, loginAccount, selectAccountToLoginDice } = useAccountProvider();
|
|
|
|
const openInsecurePasswordModal = () => {
|
|
modals.openConfirmModal({
|
|
title: 'Insecure password',
|
|
centered: true,
|
|
children: (
|
|
<Text size="sm">
|
|
Your password is insecure,
|
|
such passwords are easy to guess, come up with a new one, or, which is not recommended, leave this one
|
|
</Text>
|
|
),
|
|
withCloseButton: false,
|
|
labels: { confirm: 'Continue', cancel: "I'll come up with a new one" },
|
|
confirmProps: { color: 'red' },
|
|
onConfirm: doneSetup
|
|
});
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (phrase.trim() == "") {
|
|
navigate("/");
|
|
}
|
|
setTimeout(() => setMounted(true), 100);
|
|
}, []);
|
|
|
|
const doneSetup = async () => {
|
|
let seed = await mnemonicToSeed(phrase);
|
|
let hex = Buffer.from(seed).toString('hex');
|
|
let keypair = await generateKeyPairFromSeed(hex);
|
|
const encrypted = await encodeWithPassword(password, keypair.privateKey);
|
|
const privateKeyHash = await generateHashFromPrivateKey(keypair.privateKey);
|
|
|
|
const account: Account = {
|
|
publicKey: keypair.publicKey,
|
|
privateKey: encrypted,
|
|
privatePlain: keypair.privateKey,
|
|
privateHash: privateKeyHash,
|
|
seedPhraseEncrypted: await encodeWithPassword(password, phrase)
|
|
}
|
|
|
|
createAccount(account);
|
|
loginAccount(account);
|
|
selectAccountToLoginDice(account);
|
|
|
|
navigate("/main");
|
|
}
|
|
|
|
const onDone = async () => {
|
|
if (!password.match(/[A-Z]+/) || !password.match(/[0-9]+/) || !password.match(/[$@#&!]+/)) {
|
|
openInsecurePasswordModal();
|
|
return;
|
|
}
|
|
doneSetup();
|
|
}
|
|
|
|
return (
|
|
<Flex h={520} w={385}>
|
|
<Flex h={'100%'} w={'100%'}>
|
|
<Flex h={'100%'} w={'100%'} pb={'sm'} align={'center'} direction={'column'} justify={'space-between'}>
|
|
<Box mt={'xl'}>
|
|
<Flex gap={'sm'} w={SIZE_LOGIN_WIDTH_PX} direction={'column'} align={'center'}>
|
|
<Lottie animationData={animationData} style={{
|
|
width: 100,
|
|
height: 100
|
|
}} loop={false}></Lottie>
|
|
<Flex w={'100%'} gap={'sm'} direction={'column'} mt={'sm'} pl={'sm'} pr={'sm'} align={'center'}>
|
|
<Text ta={'center'} fw={500} size={'sm'}>
|
|
Protect account
|
|
</Text>
|
|
<Text c={'dimmed'} ta={'center'} size={'xs'}>
|
|
Create a password to protect your account
|
|
</Text>
|
|
</Flex>
|
|
</Flex>
|
|
<Transition mounted={mounted} transition="slide-up" duration={400} timingFunction="ease">
|
|
{(styles) => (
|
|
<Flex w={SIZE_LOGIN_WIDTH_PX} mt={'xl'} pr={'sm'} pl={'sm'} direction={'column'} align={'center'} justify={'center'} gap={'lg'} style={styles}>
|
|
<PasswordInput styles={{
|
|
input: {
|
|
border: '0px solid ' + colors.borderColor,
|
|
backgroundColor: colors.mainColor
|
|
},
|
|
error: {
|
|
color: colors.error
|
|
}
|
|
}} w={'100%'} placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} size="md"></PasswordInput>
|
|
<PasswordInput styles={{
|
|
input: {
|
|
border: '0px solid ' + colors.borderColor,
|
|
backgroundColor: colors.mainColor
|
|
},
|
|
error: {
|
|
color: colors.error
|
|
}
|
|
}} w={'100%'} placeholder="Confirm" value={confirm} onChange={(e) => setConfirm(e.target.value)} size="md"></PasswordInput>
|
|
<AnimatedButton disabled={password.length === 0 || confirm.length === 0 || password !== confirm} rightSection={<IconChevronRight size={16}></IconChevronRight>} animated={['#2DA5FF', '#87DBFF']} fullWidth onClick={onDone} size={'md'}>
|
|
Start
|
|
</AnimatedButton>
|
|
</Flex>
|
|
)}
|
|
</Transition>
|
|
</Box>
|
|
<Transition mounted={mounted} transition="slide-up" duration={400} timingFunction="ease">
|
|
{(styles) => (
|
|
<Flex pr={'sm'} pl={'sm'} w={'100%'} align={'center'} justify={'center'} gap={'lg'} style={styles}>
|
|
<Text c={'dimmed'} ta={'center'} w={'100%'} size={'xs'}>
|
|
Your password <Text c={'blue'} fw={500} span>never leaves</Text> your device and is never stored anywhere.
|
|
</Text>
|
|
</Flex>
|
|
)}
|
|
</Transition>
|
|
</Flex>
|
|
</Flex>
|
|
</Flex>)
|
|
} |