190 lines
8.8 KiB
TypeScript
190 lines
8.8 KiB
TypeScript
import { Anchor, AspectRatio, Avatar, Box, Flex, PasswordInput, Skeleton, Text, Transition} from "@mantine/core";
|
|
import classes from './Lockscreen.module.css'
|
|
import { useEffect, useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import useWindow from "@/app/hooks/useWindow";
|
|
import { decodeWithPassword, generateHashFromPrivateKey } from "@/app/crypto/crypto";
|
|
import { useAccountProvider } from "@/app/providers/AccountProvider/useAccountProvider";
|
|
import { Account, AccountBase } from "@/app/providers/AccountProvider/AccountProvider";
|
|
import { useUserCache } from "@/app/providers/InformationProvider/useUserCache";
|
|
import { useRosettaColors } from "@/app/hooks/useRosettaColors";
|
|
import { IconArrowsExchange, IconFingerprint } from "@tabler/icons-react";
|
|
import { SIZE_LOGIN_WIDTH_PX } from "@/app/constants";
|
|
import { modals } from "@mantine/modals";
|
|
import { useAvatars } from "@/app/providers/AvatarProvider/useAvatars";
|
|
import { AnimatedButton } from "@/app/components/AnimatedButton/AnimatedButton";
|
|
import { DiceDropdown } from "@/app/components/DiceDropdown/DiceDropdown";
|
|
import { dotCenterIfNeeded } from "@/app/utils/utils";
|
|
|
|
export function Lockscreen() {
|
|
const [password, setPassword] = useState("");
|
|
const navigate = useNavigate();
|
|
const { setSize, setResizeble } = useWindow();
|
|
const { allAccounts, selectAccountToLoginDice, loginDiceAccount, loginAccount } = useAccountProvider();
|
|
const userInfo = useUserCache(loginDiceAccount.publicKey);
|
|
const [error, setError] = useState(false);
|
|
const colors = useRosettaColors();
|
|
const avatars = useAvatars(loginDiceAccount.publicKey);
|
|
const [mounted, setMounted] = useState(false);
|
|
|
|
|
|
useEffect(() => {
|
|
setSize(385, 555);
|
|
setResizeble(false);
|
|
setTimeout(() => setMounted(true), 100);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (loginDiceAccount.publicKey == "" && allAccounts.length <= 0) {
|
|
navigate("/");
|
|
return;
|
|
}
|
|
}, [loginDiceAccount])
|
|
|
|
const onUnlockPressed = async () => {
|
|
try {
|
|
const decryptedHex = await decodeWithPassword(password, loginDiceAccount.privateKey);
|
|
const privateKeyHash = await generateHashFromPrivateKey(decryptedHex);
|
|
|
|
const account: Account = {
|
|
privateKey: loginDiceAccount.privateKey,
|
|
publicKey: loginDiceAccount.publicKey,
|
|
privateHash: privateKeyHash,
|
|
privatePlain: decryptedHex,
|
|
seedPhraseEncrypted: loginDiceAccount.seedPhraseEncrypted
|
|
};
|
|
|
|
loginAccount(account);
|
|
|
|
navigate("/main");
|
|
} catch (e) {
|
|
setError(true);
|
|
}
|
|
}
|
|
|
|
const createAccount = () => {
|
|
modals.openConfirmModal({
|
|
title: 'Create account',
|
|
centered: true,
|
|
children: (
|
|
<Text size="sm">
|
|
You may be create new account or import existing
|
|
</Text>
|
|
),
|
|
withCloseButton: false,
|
|
labels: { confirm: 'Create new', cancel: "Import" },
|
|
cancelProps: {
|
|
autoFocus: false,
|
|
style: {
|
|
outline: 'none'
|
|
}
|
|
},
|
|
onCancel: () => {
|
|
navigate("/exists-seed");
|
|
},
|
|
onConfirm: () => {
|
|
navigate("/create-seed");
|
|
}
|
|
});
|
|
}
|
|
|
|
const selectAccount = (account: AccountBase) => {
|
|
selectAccountToLoginDice(account);
|
|
}
|
|
|
|
useEffect(() => {
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
if (event.key === "Enter") {
|
|
event.preventDefault();
|
|
onUnlockPressed();
|
|
}
|
|
};
|
|
|
|
window.addEventListener("keydown", handleKeyDown);
|
|
return () => {
|
|
window.removeEventListener("keydown", handleKeyDown);
|
|
};
|
|
}, [onUnlockPressed]);
|
|
|
|
return (
|
|
<Flex style={{
|
|
userSelect: 'none'
|
|
}} h={500} justify={'space-between'} direction={'column'} align={'center'}>
|
|
<div className={classes.inner}>
|
|
<div className={classes.content}>
|
|
<Flex mt={'md'} align={'center'} direction={'column'} justify={'center'}>
|
|
{userInfo && (
|
|
<Box mt={'sm'}>
|
|
<Flex align={'center'} gap={'sm'} direction={'column'}>
|
|
<AspectRatio ratio={2/2} maw={80} mx="auto" pos="relative">
|
|
<Avatar radius={'md'} src={avatars.length > 0 ? avatars[0].avatar : undefined} size={80} color={'initials'} name={userInfo.title}></Avatar>
|
|
</AspectRatio>
|
|
<Flex gap={8} justify={'center'} align={'center'}>
|
|
<Text fz={20} h={25} fw={'bold'}>{dotCenterIfNeeded(userInfo.title, 20, 5)}</Text>
|
|
<DiceDropdown selectedPublicKey={loginDiceAccount.publicKey} onClick={selectAccount}>
|
|
<IconArrowsExchange style={{
|
|
position: 'relative',
|
|
top: 4,
|
|
cursor: 'pointer'
|
|
}} color={colors.brandColor} size={18} />
|
|
</DiceDropdown>
|
|
</Flex>
|
|
<Text c={'dimmed'} mt={'xs'} ta={'center'} w={SIZE_LOGIN_WIDTH_PX} size={'xs'}>For unlock account enter password</Text>
|
|
</Flex>
|
|
</Box>
|
|
)}
|
|
{!userInfo && (
|
|
<Box>
|
|
<Flex align={'center'} gap={'sm'} direction={'column'} justify={'center'}>
|
|
<Skeleton w={80} h={80}></Skeleton>
|
|
<Skeleton h={25} w={150}></Skeleton>
|
|
</Flex>
|
|
</Box>
|
|
)}
|
|
</Flex>
|
|
<Transition mounted={mounted} transition="slide-up" duration={400} timingFunction="ease">
|
|
{(styles) => (
|
|
<Flex style={styles} mt={40} direction={'column'} align={'center'} justify={'center'} gap={'lg'}>
|
|
<PasswordInput
|
|
placeholder="Password"
|
|
value={password}
|
|
onChange={(e) => { setPassword(e.target.value); setError(false) }}
|
|
size="md"
|
|
w={SIZE_LOGIN_WIDTH_PX}
|
|
error={error && "Invalid password"}
|
|
styles={{
|
|
input: {
|
|
border: '0px solid ' + colors.borderColor,
|
|
backgroundColor: colors.mainColor
|
|
},
|
|
error: {
|
|
color: colors.error
|
|
}
|
|
}}
|
|
/>
|
|
<Flex w={SIZE_LOGIN_WIDTH_PX} align={'center'} direction={'column'} justify={'space-between'}>
|
|
<AnimatedButton fullWidth leftSection={
|
|
<IconFingerprint size={16} />
|
|
} animated={['#2DA5FF', '#87DBFF']} size={'md'} onClick={onUnlockPressed}>Enter</AnimatedButton>
|
|
</Flex>
|
|
</Flex>
|
|
)}
|
|
</Transition>
|
|
</div>
|
|
</div>
|
|
<Transition mounted={mounted} transition="slide-up" duration={500} timingFunction="ease">
|
|
{(styles) => (
|
|
<Flex style={styles}>
|
|
<Text c={'dimmed'} ta={'center'} w={SIZE_LOGIN_WIDTH_PX} size={'xs'}>
|
|
You can also <Anchor style={{
|
|
textDecoration: 'none'
|
|
}} fw={500} onClick={() => navigate('/exists-seed')}>recover your password</Anchor> or create a <Anchor style={{
|
|
textDecoration: 'none'
|
|
}} fw={500} onClick={createAccount}>new account.</Anchor>
|
|
</Text>
|
|
</Flex>
|
|
)}
|
|
</Transition>
|
|
</Flex>
|
|
)
|
|
} |