-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #344 from Concordium/add-web3Id-backup
Add web3 id credential backup
- Loading branch information
Showing
17 changed files
with
342 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
...browser-wallet/src/popup/pages/VerifiableCredentialBackup/VerifiableCredentialBackup.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
$header-height: rem(56px); | ||
|
||
.verifiable-credential-import { | ||
display: flex; | ||
flex-direction: column; | ||
height: calc(100% - $header-height); | ||
background-color: $color-bg; | ||
|
||
&__header { | ||
background-color: $color-bg; | ||
} | ||
|
||
&__empty { | ||
text-align: center; | ||
} | ||
|
||
&__list { | ||
overflow-y: auto; | ||
margin-bottom: rem(10px); | ||
} | ||
|
||
&__card { | ||
margin: rem(16px); | ||
} | ||
|
||
&__button { | ||
margin: auto auto 20px; | ||
} | ||
} |
130 changes: 130 additions & 0 deletions
130
.../browser-wallet/src/popup/pages/VerifiableCredentialBackup/VerifiableCredentialImport.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import React, { useContext, useState } from 'react'; | ||
import { | ||
storedVerifiableCredentialSchemasAtom, | ||
storedVerifiableCredentialsAtom, | ||
storedVerifiableCredentialMetadataAtom, | ||
} from '@popup/store/verifiable-credential'; | ||
import { useAtom } from 'jotai'; | ||
import PageHeader from '@popup/shared/PageHeader'; | ||
import { EncryptedData, VerifiableCredential } from '@shared/storage/types'; | ||
import { useTranslation } from 'react-i18next'; | ||
import Button from '@popup/shared/Button'; | ||
import { fullscreenPromptContext } from '@popup/page-layouts/FullscreenPromptLayout'; | ||
import { noOp } from 'wallet-common-helpers'; | ||
import { decrypt } from '@shared/utils/crypto'; | ||
import { useHdWallet } from '@popup/shared/utils/account-helpers'; | ||
import { VerifiableCredentialCardWithStatusFromChain } from '../VerifiableCredential/VerifiableCredentialList'; | ||
import { ExportFormat, VerifiableCredentialExport } from './utils'; | ||
|
||
function DisplayResult({ imported }: { imported: VerifiableCredential[] }) { | ||
const { t } = useTranslation('verifiableCredentialBackup'); | ||
|
||
return ( | ||
<> | ||
{imported.length === 0 && <p className="verifiable-credential-import__empty">{t('noImported')}</p>} | ||
{imported.length > 0 && ( | ||
<div className="verifiable-credential-import__list"> | ||
{imported.map((credential) => { | ||
return ( | ||
<VerifiableCredentialCardWithStatusFromChain | ||
key={credential.id} | ||
className="verifiable-credential-import__card" | ||
credential={credential} | ||
/> | ||
); | ||
})} | ||
</div> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
async function parseExport(data: EncryptedData, encryptionKey: string): Promise<VerifiableCredentialExport> { | ||
// TODO handle bigints | ||
const backup: ExportFormat = JSON.parse(await decrypt(data, encryptionKey)); | ||
// TODO validation | ||
return backup.value; | ||
} | ||
|
||
/** | ||
* Adds items from toAdd that does not exist in stored, using the given update. Returns the items from toAdd that was actually added. | ||
*/ | ||
function updateList<T>(stored: T[], toAdd: T[], isEqual: (a: T, b: T) => boolean, update: (updated: T[]) => void): T[] { | ||
const filtered = toAdd.filter((item) => stored.every((existing) => !isEqual(item, existing))); | ||
update([...stored, ...filtered]); | ||
return filtered; | ||
} | ||
|
||
/** | ||
* Adds items from toAdd that does not exist in stored, using the given update. | ||
*/ | ||
function updateRecord<T>( | ||
stored: Record<string, T>, | ||
toAdd: Record<string, T>, | ||
update: (updated: Record<string, T>) => void | ||
) { | ||
const updated = { ...stored }; | ||
Object.entries(toAdd).forEach(([key, value]) => { | ||
if (!stored[key]) { | ||
updated[key] = value; | ||
} | ||
}); | ||
|
||
update(updated); | ||
} | ||
|
||
export default function VerifiableCredentialImport() { | ||
const [storedVerifiableCredentials, setVerifiableCredentials] = useAtom(storedVerifiableCredentialsAtom); | ||
const [imported, setImported] = useState<VerifiableCredential[]>(); | ||
const [storedSchemas, setSchemas] = useAtom(storedVerifiableCredentialSchemasAtom); | ||
const [storedMetadata, setMetadata] = useAtom(storedVerifiableCredentialMetadataAtom); | ||
const { t } = useTranslation('verifiableCredentialBackup'); | ||
const { withClose } = useContext(fullscreenPromptContext); | ||
const wallet = useHdWallet(); | ||
const [error, setError] = useState<string>(); | ||
|
||
if (storedSchemas.loading || storedMetadata.loading || storedVerifiableCredentials.loading || !wallet) { | ||
return null; | ||
} | ||
|
||
const handleImport = async (event: React.ChangeEvent<HTMLInputElement>) => { | ||
try { | ||
const file = event.target.files?.[0]; | ||
if (file) { | ||
const encryptedBackup: EncryptedData = JSON.parse(await file.text()); | ||
const key = wallet.getVerifiableCredentialBackupEncryptionKey().toString('hex'); | ||
const { verifiableCredentials, schemas, metadata } = await parseExport(encryptedBackup, key); | ||
const filteredCredentials = updateList( | ||
storedVerifiableCredentials.value, | ||
verifiableCredentials, | ||
(a, b) => a.id === b.id, | ||
setVerifiableCredentials | ||
); | ||
updateRecord(storedSchemas.value, schemas, setSchemas); | ||
updateRecord(storedMetadata.value, metadata, setMetadata); | ||
setImported(filteredCredentials); | ||
} | ||
} catch (e) { | ||
setError(t('error')); | ||
} | ||
}; | ||
|
||
// TODO drag and drop | ||
return ( | ||
<> | ||
<PageHeader className="verifiable-credential-import__header">{t('title')}</PageHeader> | ||
<div className="verifiable-credential-import"> | ||
{imported && <DisplayResult imported={imported} />} | ||
{!imported && ( | ||
<> | ||
<input type="file" onChange={handleImport} /> | ||
{error && <p className="m-h-10 form-error-message">{error}</p>} | ||
</> | ||
)} | ||
<Button className="verifiable-credential-import__button" width="wide" onClick={withClose(noOp)}> | ||
{t('close')} | ||
</Button> | ||
</div> | ||
</> | ||
); | ||
} |
10 changes: 10 additions & 0 deletions
10
packages/browser-wallet/src/popup/pages/VerifiableCredentialBackup/i18n/da.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import en from './en'; | ||
|
||
const t: typeof en = { | ||
title: 'Importer Web3 ID Credentials', | ||
noImported: 'Ingen Web3 ID Credentials blev importeret', | ||
error: 'Det var ikke muligt at importere den valgte fil. Filen skal være en backup lavet med en samme seed phrase.', | ||
close: 'Luk', | ||
}; | ||
|
||
export default t; |
6 changes: 6 additions & 0 deletions
6
packages/browser-wallet/src/popup/pages/VerifiableCredentialBackup/i18n/en.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export default { | ||
title: 'Import Web3 ID Credentials', | ||
noImported: 'No Web3 ID Credentials were imported', | ||
error: 'Unable to import the chosen file. The file must be a backup created with the same seed phrase.', | ||
close: 'Close', | ||
}; |
Oops, something went wrong.