'init'
This commit is contained in:
27
app/views/SetPassword/SetPassword.module.css
Normal file
27
app/views/SetPassword/SetPassword.module.css
Normal file
@@ -0,0 +1,27 @@
|
||||
.inner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: calc(var(--mantine-spacing-xl));
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
|
||||
font-family:
|
||||
Greycliff CF,
|
||||
var(--mantine-font-family);
|
||||
font-size: 44px;
|
||||
line-height: 1.2;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
position: relative;
|
||||
background-color: var(--mantine-color-blue-light);
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
padding: 4px 12px;
|
||||
}
|
||||
142
app/views/SetPassword/SetPassword.tsx
Normal file
142
app/views/SetPassword/SetPassword.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
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>)
|
||||
}
|
||||
1
app/views/SetPassword/lottie.json
Normal file
1
app/views/SetPassword/lottie.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user