Skip to content

Commit

Permalink
feat: import wallet page
Browse files Browse the repository at this point in the history
  • Loading branch information
satanmourner committed Oct 2, 2024
1 parent 0bdb9f3 commit 63b561f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 113 deletions.
34 changes: 15 additions & 19 deletions src/pages/Recover/index.module.scss
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
.card {
&__container {
width: 32.5rem;
max-width: unset;
height: auto;

@include media-breakpoint-down(md) {
width: 100%;
}
}
.title {
font-size: 24px;
font-weight: bold;
line-height: 32px;
color: $black;
}

&__form {
width: 100%;
@include flex(space-between, center, column);
gap: 0.5rem;
}
.subtitle,
.error {
font-size: 16px;
font-weight: 400;
line-height: 24px;
color: $gray;
}

&__text {
color: $gray;
text-align: center;
}
.error {
color: $color-red-2;
}
65 changes: 23 additions & 42 deletions src/pages/Recover/index.services.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,35 @@
import { useState } from 'react';
import SDK from '@hyperledger/identus-edge-agent-sdk';
import axios from 'src/services/http';
import { config } from 'src/config';
import { recoverDID } from 'src/services/dids';
import { decrypt, restoreIndexedDBs } from 'src/services/backup';
import { ChangeEvent, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAppContext } from 'src/store/context';

const useRecover = () => {
const [mns, setMns] = useState<string[]>([]);
const { t: translate } = useTranslation();
const navigate = useNavigate();
const { state } = useAppContext();
const { did } = state || {};
const inputRef = useRef<HTMLInputElement | null>(null);
const [importedWallet, setImportedWallet] = useState<File | null>(null);
const [errorMessage, setErrorMessage] = useState<string>();
const disabledConfirm = mns.length < 24;
const exampleService = new SDK.Domain.Service('didcomm', ['DIDCommMessaging'], {
uri: 'https://example.com/endpoint',
accept: ['didcomm/v2'],
routingKeys: ['did:example:somemediator#somekey'],
});

const handleMnemonicValue = (value: string) => {
const updatedMnemonics = value.split(' ');
setMns(updatedMnemonics);
const onClickUpload = () => {
setErrorMessage('');
inputRef.current.click();
};

const onConfirm = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (disabledConfirm) return;
try {
const { did, privateKey } = await recoverDID(mns, [exampleService]);
const headers = { 'x-api-key': config.BACKUP_AGENT_API_KEY };
const res = await axios.get(`${config.BACKUP_AGENT}/fetch/${did.methodId}.bin`, { headers });
const strObj = decrypt(privateKey.raw, res.data);
await restoreIndexedDBs(JSON.parse(strObj));
// const db = await connect();
// dispatch({ type: 'SET_PLUTO', payload: db });
// Note: It must be reload and redirect this is why it use window.location instead of react router
window.location.assign('/created#restored');
} catch (err) {
setErrorMessage(err.message);
const onUploadWallet = (event: ChangeEvent<HTMLInputElement>) => {
const { files = [] } = event.target;
const file = files[0];
if (file && file.type === 'text/plain') {
setImportedWallet(file);
//TODO: backup file
navigate('/create-pass');
} else {
setErrorMessage(translate('recover-error'));
}
};

const closeError = () => {
setErrorMessage(undefined);
};

return {
handleMnemonicValue,
onConfirm,
disabledConfirm,
errorMessage,
closeError,
};
return { translate, navigate, did, inputRef, onClickUpload, onUploadWallet, errorMessage };
};

export default useRecover;
66 changes: 14 additions & 52 deletions src/pages/Recover/index.tsx
Original file line number Diff line number Diff line change
@@ -1,79 +1,41 @@
import React from 'react';
import { useNavigate, Navigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAppContext } from 'src/store/context';
import { Form } from 'react-bootstrap';
import ConfirmModal from 'src/components/ConfirmModal';
import { Navigate } from 'react-router-dom';
import Card from 'src/components/Card';
import useRecover from './index.services';
import styles from './index.module.scss';

function Recover() {
const { t: translate } = useTranslation();
const navigate = useNavigate();
const { handleMnemonicValue, onConfirm, disabledConfirm, errorMessage, closeError } = useRecover();
const { state } = useAppContext();
const { translate, navigate, did, inputRef, onClickUpload, onUploadWallet, errorMessage } = useRecover();

if (state.did) return <Navigate to="/" />;
if (did) return <Navigate to="/" />;
return (
<>
<Form onSubmit={onConfirm} className="h-100 d-flex align-items-center justify-content-center">
<div className="h-100 d-flex align-items-center justify-content-center">
<Card
containerClassName={styles['card__container']}
contentClassName="h-100 flex-row"
contentClassName="justify-content-start pt-5 text-center"
buttons={[
{
children: translate('recover-import-button'),
variant: 'primary',
type: 'submit',
disabled: disabledConfirm,
className: 'fw-bold w-100 py-2',
onClick: onClickUpload,
},
{
children: translate('recover-back-button'),
variant: 'inherit',
type: 'button',
onClick: () => navigate('/intro'),
variant: 'light',
className: 'fw-bold w-100 py-2',
onClick: () => navigate('/intro'),
},
]}
>
<div className={styles['card__form']}>
<div className="d-flex flex-column gap-2 align-items-center">
<h4 className="fw-bold">{translate('recover-title')}</h4>
<span className={styles['card__text']}>{translate('recover-subtitle')}</span>
<Form.Group className="my-3 my-md-5 w-100" controlId="recovery">
<Form.Control
as="textarea"
placeholder={translate('recover-title')}
rows={6}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => handleMnemonicValue(e.target.value)}
onPaste={(e: React.ClipboardEvent<HTMLInputElement>) =>
handleMnemonicValue(e.clipboardData.getData('text'))
}
/>
</Form.Group>
</div>
<div className="d-flex flex-column gap-2 mb-2">
<h4 className={styles['title']}>{translate('recover-title')}</h4>
<span className={styles['subtitle']}>{translate('recover-subtitle')}</span>
</div>
<input type="file" ref={inputRef} className="d-none" onChange={onUploadWallet} />
{errorMessage && <span className={styles['error']}>{errorMessage}</span>}
</Card>
</Form>
<ConfirmModal
open={errorMessage !== undefined}
header={translate('recover-confirm.header')}
onClose={closeError}
buttons={[
{
children: translate('recover-confirm.button'),
variant: 'secondary',
onClick: closeError,
className: 'flex-grow-1 border-solid',
},
]}
>
<div className={styles['title']}>
<span className={styles['subtitle']}>{errorMessage}</span>
</div>
</ConfirmModal>
</div>
</>
);
}
Expand Down

0 comments on commit 63b561f

Please sign in to comment.