implement auth and create wallet logic

This commit is contained in:
Mahmoud
2022-10-03 00:52:46 +02:00
parent 2a38e29d29
commit 5b9f2617c6
9 changed files with 236 additions and 54 deletions

View File

@@ -0,0 +1,27 @@
import { StoreState } from '@stores/root/reducer';
import { getEncryptedSeed } from '@utils/localStorage';
import { ReactNode } from 'react';
import { useSelector } from 'react-redux';
import { Navigate } from 'react-router-dom';
interface AuthGuardProps {
children: ReactNode,
}
function AuthGuard({ children }: AuthGuardProps) {
const encryptedSeedPhrase = getEncryptedSeed();
const {
isLocked,
} = useSelector((state: StoreState) => ({
...state.walletState,
}));
if (encryptedSeedPhrase && isLocked) return <Navigate to="/login" />;
if (!encryptedSeedPhrase) return <Navigate to="/landing" />;
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
export default AuthGuard;

View File

@@ -1,16 +0,0 @@
import styled from 'styled-components';
import { useTranslation } from 'react-i18next';
const StyledHeader = styled.h1`
font-size: 1.5em;
text-align: center;
font-family: Satoshi-Regular;
color: ${(props) => props.theme.colors.action.classic};
`;
function Header():JSX.Element {
const { t } = useTranslation('translation', { keyPrefix: 'common' });
return <StyledHeader>{t('appName')}</StyledHeader>;
}
export default Header;

View File

@@ -7,6 +7,8 @@ import LegalLinks from '@screens/legalLinks';
import BackupWallet from '@screens/backupWallet';
import CreateWalletSuccess from '@screens/createWalletSuccess';
import CreatePassword from '@screens/createPassword';
import AuthGuard from '@components/guards/auth';
import Login from '@screens/login';
const router = createHashRouter([
{
@@ -14,7 +16,7 @@ const router = createHashRouter([
element: <ScreenContainer />,
children: [
{
index: true,
path: 'landing',
element: <Landing />,
},
{
@@ -22,8 +24,12 @@ const router = createHashRouter([
element: <Onboarding />,
},
{
path: 'home',
element: <Home />,
index: true,
element: (
<AuthGuard>
<Home />
</AuthGuard>
),
},
{
path: 'legal',
@@ -41,6 +47,10 @@ const router = createHashRouter([
path: 'create-wallet-success',
element: <CreateWalletSuccess />,
},
{
path: 'login',
element: <Login />,
},
],
},
]);

View File

@@ -4,10 +4,10 @@ import PasswordIcon from '@assets/img/createPassword/Password.svg';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { getStoreSeedPhraseRequestAction } from '@stores/actions/wallet/actionCreators';
import NewPassword from './newPassword';
import ConfirmPassword from './confirmPassword';
import { StoreState } from '@stores/reducers/root';
import { StoreState } from '@stores/root/reducer';
import { storeWalletSeed } from '@core/wallet';
const Container = styled.div((props) => ({
flex: 1,
@@ -53,9 +53,7 @@ function CreatePassword(): JSX.Element {
const [currentStepIndex, setCurrentStepIndex] = useState<number>(0);
const navigate = useNavigate();
const dispatch = useDispatch();
const {
seedPhrase,
} = useSelector((state: StoreState) => ({
const { seedPhrase } = useSelector((state: StoreState) => ({
...state.walletState,
}));
@@ -63,8 +61,8 @@ function CreatePassword(): JSX.Element {
setCurrentStepIndex(1);
};
const handleConfirmPassword = () => {
dispatch(getStoreSeedPhraseRequestAction(seedPhrase, password));
const handleConfirmPassword = async () => {
await storeWalletSeed(seedPhrase, password);
navigate('/create-wallet-success');
};
@@ -79,9 +77,11 @@ function CreatePassword(): JSX.Element {
return (
<Container>
<StepsContainer>
{Array(2).fill(0).map((view, index) => (
<StepDot active={index === currentStepIndex} key={index.toString() + 1} />
))}
{Array(2)
.fill(0)
.map((view, index) => (
<StepDot active={index === currentStepIndex} key={index.toString() + 1} />
))}
</StepsContainer>
<HeaderContainer>
<img src={PasswordIcon} alt="passoword" />
@@ -89,26 +89,23 @@ function CreatePassword(): JSX.Element {
{currentStepIndex === 0 ? t('CREATE_PASSWORD_TITLE') : t('CONFIRM_PASSWORD_TITLE')}
</HeaderText>
</HeaderContainer>
{
currentStepIndex === 0 ? (
<NewPassword
password={password}
setPassword={setPassword}
handleContinue={handleContinuePasswordCreation}
handleBack={handleNewPasswordBack}
/>
) : (
<ConfirmPassword
password={password}
confirmPassword={confirmPassword}
setConfirmPassword={setConfirmPassword}
handleContinue={handleConfirmPassword}
handleBack={handleConfirmPasswordBack}
/>
)
}
{currentStepIndex === 0 ? (
<NewPassword
password={password}
setPassword={setPassword}
handleContinue={handleContinuePasswordCreation}
handleBack={handleNewPasswordBack}
/>
) : (
<ConfirmPassword
password={password}
confirmPassword={confirmPassword}
setConfirmPassword={setConfirmPassword}
handleContinue={handleConfirmPassword}
handleBack={handleConfirmPasswordBack}
/>
)}
</Container>
);
}

View File

@@ -43,7 +43,7 @@ function CreateWalletSuccess(): JSX.Element {
const navigate = useNavigate();
const handleOpenWallet = () => {
navigate('/home');
navigate('/');
};
return (

View File

@@ -2,9 +2,9 @@ import styled from 'styled-components';
import logo from '@assets/img/full_logo_vertical.svg';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { getGenerateWalletAction } from '@stores/actions/wallet/actionCreators';
import { StoreState } from '@stores/reducers/root';
import { useDispatch } from 'react-redux';
import { setWalletAction } from '@stores/wallet/actions/actionCreators';
import { newWallet } from '@core/wallet';
const TopSectionContainer = styled.div({
display: 'flex',
@@ -69,9 +69,10 @@ function Landing(): JSX.Element {
const navigate = useNavigate();
const dispatch = useDispatch();
const handlePressAction = () => {
const handlePressAction = async () => {
navigate('/onboarding');
dispatch(getGenerateWalletAction());
const wallet = await newWallet();
dispatch(setWalletAction(wallet));
};
return (
<>

View File

@@ -0,0 +1,155 @@
import { useTranslation } from 'react-i18next';
import logo from '@assets/img/full_logo_vertical.svg';
import styled from 'styled-components';
import Eye from '@assets/img/createPassword/Eye.svg';
import EyeSlash from '@assets/img/createPassword/EyeSlash.svg';
import { useState } from 'react';
import { decryptSeedPhrase } from '@utils/encryptionUtils';
import { getEncryptedSeed } from '@utils/localStorage';
import { walletFromSeedPhrase } from '@core/wallet';
import { useDispatch } from 'react-redux';
import { setWalletAction } from '@stores/wallet/actions/actionCreators';
import { useNavigate } from 'react-router-dom';
const ScreenContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'column',
flex: 1,
paddingLeft: props.theme.spacing(9),
paddingRight: props.theme.spacing(9),
}));
const AppVersion = styled.p((props) => ({
...props.theme.body_xs,
color: props.theme.colors.white['0'],
textAlign: 'right',
marginTop: props.theme.spacing(8),
}));
const TopSectionContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
marginTop: props.theme.spacing(50),
marginBottom: props.theme.spacing(15),
}));
const PasswordInputLabel = styled.h2((props) => ({
...props.theme.body_medium_m,
textAlign: 'left',
}));
const PasswordInputContainer = styled.div((props) => ({
display: 'flex',
alignItems: 'center',
width: '100%',
border: '1px solid #303354;',
paddingLeft: props.theme.spacing(8),
paddingRight: props.theme.spacing(8),
borderRadius: props.theme.radius(1),
marginTop: props.theme.spacing(4),
}));
const PasswordInput = styled.input((props) => ({
...props.theme.body_medium_m,
height: 44,
backgroundColor: props.theme.colors.background.elevation0,
color: props.theme.colors.white['0'],
width: '100%',
border: 'none',
}));
const LandingTitle = styled.h1((props) => ({
...props.theme.tile_text,
paddingTop: props.theme.spacing(15),
paddingLeft: props.theme.spacing(34),
paddingRight: props.theme.spacing(34),
color: props.theme.colors.white['200'],
textAlign: 'center',
}));
const VerifyButton = styled.button((props) => ({
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
borderRadius: props.theme.radius(1),
backgroundColor: props.theme.colors.action.classic,
color: props.theme.colors.white['0'],
marginTop: props.theme.spacing(8),
width: '100%',
height: 44,
}));
const ErrorMessage = styled.h2((props) => ({
...props.theme.body_medium_m,
textAlign: 'left',
color: props.theme.colors.feedback.error,
marginTop: props.theme.spacing(4),
}));
function Login(): JSX.Element {
const { t } = useTranslation('translation', { keyPrefix: 'LOGIN_SCREEN' });
const dispatch = useDispatch();
const navigate = useNavigate();
const [isPasswordVisible, setIsPasswordVisible] = useState<boolean>(false);
const [password, setPassword] = useState<string>('');
const [error, setError] = useState<string>('');
const [isVerifying, setIsVerifying] = useState(false);
const handleTogglePasswordView = () => {
setIsPasswordVisible(!isPasswordVisible);
};
const handlePasswordChange = (event: React.FormEvent<HTMLInputElement>) => {
if (error) {
setError('');
}
setPassword(event.currentTarget.value);
};
const handleVerifyPassword = async () => {
setIsVerifying(true);
const seed = getEncryptedSeed();
try {
if (seed) {
const decrypted = await decryptSeedPhrase(seed, password);
const wallet = await walletFromSeedPhrase(decrypted);
dispatch(setWalletAction(wallet));
setIsVerifying(false);
navigate('/');
}
} catch (err) {
setIsVerifying(false);
setError(t('VERIFY_PASSWORD_ERROR'));
}
};
return (
<ScreenContainer>
<AppVersion>V 1.0.0</AppVersion>
<TopSectionContainer>
<img src={logo} width={100} alt="logo" />
<LandingTitle>{t('WELCOME_MESSAGE_FIRST_LOGIN')}</LandingTitle>
</TopSectionContainer>
<PasswordInputLabel>{t('PASSWORD_INPUT_LABEL')}</PasswordInputLabel>
<PasswordInputContainer>
<PasswordInput
type={isPasswordVisible ? 'text' : 'password'}
value={password}
onChange={handlePasswordChange}
placeholder={t('PASSWORD_INPUT_PLACEHOLDER')}
/>
<button type="button" onClick={handleTogglePasswordView} style={{ background: 'none' }}>
<img src={isPasswordVisible ? Eye : EyeSlash} alt="show-password" height={24} />
</button>
</PasswordInputContainer>
{error && <ErrorMessage>{error}</ErrorMessage>}
<VerifyButton onClick={handleVerifyPassword}>
{t('VERIFY_PASSWORD_BUTTON')}
</VerifyButton>
</ScreenContainer>
);
}
export default Login;

View File

@@ -46,5 +46,13 @@
"SCREEN_TITLE": "Wallet created successfully",
"SCREEN_SUBTITLE": "Your new wallet has been created successfully, you can now connect to your wallet.",
"OPEN_WALLET_BUTTON": "Open Wallet"
},
"LOGIN_SCREEN": {
"WELCOME_MESSAGE_FIRST_LOGIN": "Welcome!",
"WELCOME_MESSAGE": "Welcome back!",
"PASSWORD_INPUT_LABEL": "Password",
"PASSWORD_INPUT_PLACEHOLDER": "Enter your password",
"VERIFY_PASSWORD_BUTTON": "Verify",
"VERIFY_PASSWORD_ERROR": "Incorrect Password"
}
}